mirror of
https://github.com/google/blockly.git
synced 2026-01-08 01:20:12 +01:00
Merge branch 'osd' into merge-osd
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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],
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user