Merge branch 'osd' into merge-osd

This commit is contained in:
Maribeth Moffatt
2024-05-23 10:59:53 -07:00
9 changed files with 11433 additions and 136 deletions

View File

@@ -395,7 +395,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
*
* @returns The newly created text input editor.
*/
protected widgetCreate_(): HTMLElement {
protected widgetCreate_(): HTMLInputElement | HTMLTextAreaElement {
const block = this.getSourceBlock();
if (!block) {
throw new UnattachedFieldError();

View File

@@ -514,6 +514,15 @@ export abstract class Flyout
this.autoClose
)
this.hide();
}
/**
* Get the target workspace inside the flyout.
*
* @returns The target workspace inside the flyout.
*/
getTargetWorkspace(): WorkspaceSvg {
return this.targetWorkspace;
}
/**

View File

@@ -66,14 +66,14 @@ export class FlyoutButton implements IASTNodeLocationSvg {
* @param workspace The workspace in which to place this button.
* @param targetWorkspace The flyout's target workspace.
* @param json The JSON specifying the label/button.
* @param isLabel_ Whether this button should be styled as a label.
* @param isFlyoutLabel Whether this button should be styled as a label.
* @internal
*/
constructor(
private readonly workspace: WorkspaceSvg,
private readonly targetWorkspace: WorkspaceSvg,
json: toolbox.ButtonOrLabelInfo,
private readonly isLabel_: boolean,
private readonly isFlyoutLabel: boolean,
) {
this.text = json['text'];
@@ -100,7 +100,9 @@ export class FlyoutButton implements IASTNodeLocationSvg {
* @returns The button's SVG group.
*/
createDom(): SVGElement {
let cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton';
let cssClass = this.isFlyoutLabel
? 'blocklyFlyoutLabel'
: 'blocklyFlyoutButton';
if (this.cssClass) {
cssClass += ' ' + this.cssClass;
}
@@ -112,7 +114,7 @@ export class FlyoutButton implements IASTNodeLocationSvg {
);
let shadow;
if (!this.isLabel_) {
if (!this.isFlyoutLabel) {
// Shadow rectangle (light source does not mirror in RTL).
shadow = dom.createSvgElement(
Svg.RECT,
@@ -130,7 +132,7 @@ export class FlyoutButton implements IASTNodeLocationSvg {
const rect = dom.createSvgElement(
Svg.RECT,
{
'class': this.isLabel_
'class': this.isFlyoutLabel
? 'blocklyFlyoutLabelBackground'
: 'blocklyFlyoutButtonBackground',
'rx': FlyoutButton.BORDER_RADIUS,
@@ -142,7 +144,7 @@ export class FlyoutButton implements IASTNodeLocationSvg {
const svgText = dom.createSvgElement(
Svg.TEXT,
{
'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText',
'class': this.isFlyoutLabel ? 'blocklyFlyoutLabelText' : 'blocklyText',
'x': 0,
'y': 0,
'text-anchor': 'middle',
@@ -155,7 +157,7 @@ export class FlyoutButton implements IASTNodeLocationSvg {
text += '\u200F';
}
svgText.textContent = text;
if (this.isLabel_) {
if (this.isFlyoutLabel) {
this.svgText = svgText;
this.workspace
.getThemeManager()
@@ -179,7 +181,7 @@ export class FlyoutButton implements IASTNodeLocationSvg {
);
this.height = fontMetrics.height;
if (!this.isLabel_) {
if (!this.isFlyoutLabel) {
this.width += 2 * FlyoutButton.TEXT_MARGIN_X;
this.height += 2 * FlyoutButton.TEXT_MARGIN_Y;
shadow?.setAttribute('width', String(this.width));
@@ -235,7 +237,7 @@ export class FlyoutButton implements IASTNodeLocationSvg {
/** @returns Whether or not the button is a label. */
isLabel(): boolean {
return this.isLabel_;
return this.isFlyoutLabel;
}
/**
@@ -321,19 +323,19 @@ export class FlyoutButton implements IASTNodeLocationSvg {
gesture.cancel();
}
if (this.isLabel_ && this.callbackKey) {
if (this.isFlyoutLabel && this.callbackKey) {
console.warn(
'Labels should not have callbacks. Label text: ' + this.text,
);
} else if (
!this.isLabel_ &&
!this.isFlyoutLabel &&
!(
this.callbackKey &&
this.targetWorkspace.getButtonCallback(this.callbackKey)
)
) {
console.warn('Buttons should have callbacks. Button text: ' + this.text);
} else if (!this.isLabel_) {
} else if (!this.isFlyoutLabel) {
const callback = this.targetWorkspace.getButtonCallback(this.callbackKey);
if (callback) {
callback(this);

View File

@@ -267,29 +267,35 @@ export class HorizontalFlyout extends Flyout {
for (let i = 0, item; (item = contents[i]); i++) {
if (item.type === 'block') {
const block = item.block;
const allBlocks = block!.getDescendants(false);
if (block === undefined || block === null) {
continue;
}
const allBlocks = block.getDescendants(false);
for (let j = 0, child; (child = allBlocks[j]); j++) {
// Mark blocks as being inside a flyout. This is used to detect and
// prevent the closure of the flyout if the user right-clicks on such
// a block.
child.isInFlyout = true;
}
const root = block!.getSvgRoot();
const blockHW = block!.getHeightWidth();
const root = block.getSvgRoot();
const blockHW = block.getHeightWidth();
// Figure out where to place the block.
const tab = block!.outputConnection ? this.tabWidth_ : 0;
const tab = block.outputConnection ? this.tabWidth_ : 0;
let moveX;
if (this.RTL) {
moveX = cursorX + blockHW.width;
} else {
moveX = cursorX - tab;
}
block!.moveBy(moveX, cursorY);
block.moveBy(moveX, cursorY);
const rect = this.createRect_(block!, moveX, cursorY, blockHW, i);
const rect = this.createRect_(block, moveX, cursorY, blockHW, i);
cursorX += blockHW.width + gaps[i];
this.addBlockListeners_(root, block!, rect);
this.addBlockListeners_(root, block, rect);
} else if (item.type === 'button') {
const button = item.button as FlyoutButton;
this.initFlyoutButton_(button, cursorX, cursorY);
@@ -306,7 +312,6 @@ export class HorizontalFlyout extends Flyout {
* @param currentDragDeltaXY How far the pointer has moved from the position
* at mouse down, in pixel units.
* @returns True if the drag is toward the workspace.
* @internal
*/
override isDragTowardWorkspace(currentDragDeltaXY: Coordinate): boolean {
const dx = currentDragDeltaXY.x;

View File

@@ -233,29 +233,32 @@ export class VerticalFlyout extends Flyout {
for (let i = 0, item; (item = contents[i]); i++) {
if (item.type === 'block') {
const block = item.block;
const allBlocks = block!.getDescendants(false);
if (!block) {
continue;
}
const allBlocks = block.getDescendants(false);
for (let j = 0, child; (child = allBlocks[j]); j++) {
// Mark blocks as being inside a flyout. This is used to detect and
// prevent the closure of the flyout if the user right-clicks on such
// a block.
child.isInFlyout = true;
}
const root = block!.getSvgRoot();
const blockHW = block!.getHeightWidth();
const moveX = block!.outputConnection
const root = block.getSvgRoot();
const blockHW = block.getHeightWidth();
const moveX = block.outputConnection
? cursorX - this.tabWidth_
: cursorX;
block!.moveBy(moveX, cursorY);
block.moveBy(moveX, cursorY);
const rect = this.createRect_(
block!,
block,
this.RTL ? moveX - blockHW.width : moveX,
cursorY,
blockHW,
i,
);
this.addBlockListeners_(root, block!, rect);
this.addBlockListeners_(root, block, rect);
cursorY += blockHW.height + gaps[i];
} else if (item.type === 'button') {
@@ -274,7 +277,6 @@ export class VerticalFlyout extends Flyout {
* @param currentDragDeltaXY How far the pointer has moved from the position
* at mouse down, in pixel units.
* @returns True if the drag is toward the workspace.
* @internal
*/
override isDragTowardWorkspace(currentDragDeltaXY: Coordinate): boolean {
const dx = currentDragDeltaXY.x;

View File

@@ -13,6 +13,8 @@ import {Gesture} from './gesture.js';
import {ICopyData, isCopyable} from './interfaces/i_copyable.js';
import {isDeletable} from './interfaces/i_deletable.js';
import {KeyboardShortcut, ShortcutRegistry} from './shortcut_registry.js';
import {Rect} from './utils/rect.js';
import {Coordinate} from './utils/coordinate.js';
import {KeyCodes} from './utils/keycodes.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import {isDraggable} from './interfaces/i_draggable.js';
@@ -63,7 +65,8 @@ export function registerDelete() {
!workspace.options.readOnly &&
selected != null &&
isDeletable(selected) &&
selected.isDeletable()
selected.isDeletable() &&
!Gesture.inProgress()
);
},
callback(workspace, e) {
@@ -72,10 +75,6 @@ export function registerDelete() {
// Do this first to prevent an error in the delete code from resulting in
// data loss.
e.preventDefault();
// Don't delete while dragging. Jeez.
if (Gesture.inProgress()) {
return false;
}
const selected = common.getSelected();
if (selected instanceof BlockSvg) {
selected.checkAndDelete();
@@ -93,6 +92,7 @@ export function registerDelete() {
let copyData: ICopyData | null = null;
let copyWorkspace: WorkspaceSvg | null = null;
let copyCoords: Coordinate | null = null;
/**
* Keyboard shortcut to copy a block on ctrl+c, cmd+c, or alt+c.
@@ -131,6 +131,7 @@ export function registerCopy() {
const selected = common.getSelected();
if (!selected || !isCopyable(selected)) return false;
copyData = selected.toCopyData();
copyCoords = selected.getRelativeToSurfaceXY();
copyWorkspace = workspace;
return !!copyData;
},
@@ -214,8 +215,20 @@ export function registerPaste() {
return !workspace.options.readOnly && !Gesture.inProgress();
},
callback() {
if (!copyData || !copyWorkspace) return false;
return !!clipboard.paste(copyData, copyWorkspace);
if (!copyData || !copyCoords || !copyWorkspace) return false;
const {left, top, width, height} = copyWorkspace
.getMetricsManager()
.getViewMetrics(true);
const viewportRect = new Rect(top, top + height, left, left + width);
if (viewportRect.contains(copyCoords.x, copyCoords.y)) {
// Pass the default copy coordinates when they are inside the viewport.
return !!clipboard.paste(copyData, copyWorkspace, copyCoords);
} else {
// Otherwise paste in the middle of the viewport.
const centerCoords = new Coordinate(left + width / 2, top + height / 2);
return !!clipboard.paste(copyData, copyWorkspace, centerCoords);
}
},
keyCodes: [ctrlV, altV, metaV],
};

View File

@@ -2039,7 +2039,6 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg {
*
* @param x Target X to scroll to.
* @param y Target Y to scroll to.
* @internal
*/
scroll(x: number, y: number) {
this.hideChaff(/* opt_onlyClosePopups= */ true);