fix: Only hide WidgetDiv if it is associated with the affected workspace. (#8150)

* Associate a workspace with WidgetDiv.

* Minor fixes after merging.

* Hide widget if owner is in an unknown workspace.
This commit is contained in:
John Nesky
2024-05-20 12:48:05 -07:00
committed by GitHub
parent 3ac2fb9f82
commit 9b2ab79e73
8 changed files with 53 additions and 16 deletions

View File

@@ -599,7 +599,7 @@ export class BlockSvg
const menuOptions = this.generateContextMenu();
if (menuOptions && menuOptions.length) {
ContextMenu.show(e, menuOptions, this.RTL);
ContextMenu.show(e, menuOptions, this.RTL, this.workspace);
ContextMenu.setCurrentBlock(this);
}
}

View File

@@ -274,7 +274,7 @@ export class RenderedWorkspaceComment
ContextMenuRegistry.ScopeType.COMMENT,
{comment: this},
);
contextMenu.show(e, menuOptions, this.workspace.RTL);
contextMenu.show(e, menuOptions, this.workspace.RTL, this.workspace);
}
/** Snap this comment to the nearest grid point. */

View File

@@ -23,6 +23,7 @@ import {Rect} from './utils/rect.js';
import * as serializationBlocks from './serialization/blocks.js';
import * as svgMath from './utils/svg_math.js';
import * as WidgetDiv from './widgetdiv.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as Xml from './xml.js';
import * as common from './common.js';
@@ -62,13 +63,15 @@ let menu_: Menu | null = null;
* @param e Mouse event.
* @param options Array of menu options.
* @param rtl True if RTL, false if LTR.
* @param workspace The workspace associated with the context menu, if any.
*/
export function show(
e: PointerEvent,
options: (ContextMenuOption | LegacyContextMenuOption)[],
rtl: boolean,
workspace?: WorkspaceSvg,
) {
WidgetDiv.show(dummyOwner, rtl, dispose);
WidgetDiv.show(dummyOwner, rtl, dispose, workspace);
if (!options.length) {
hide();
return;

View File

@@ -372,7 +372,12 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
if (!block) {
throw new UnattachedFieldError();
}
WidgetDiv.show(this, block.RTL, this.widgetDispose_.bind(this));
WidgetDiv.show(
this,
block.RTL,
this.widgetDispose_.bind(this),
this.workspace_,
);
this.htmlInput_ = this.widgetCreate_() as HTMLInputElement;
this.isBeingEdited_ = true;
this.valueWhenEditorWasOpened_ = this.value_;
@@ -546,17 +551,17 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
*/
protected onHtmlInputKeyDown_(e: KeyboardEvent) {
if (e.key === 'Enter') {
WidgetDiv.hide();
WidgetDiv.hideIfOwner(this);
dropDownDiv.hideWithoutAnimation();
} else if (e.key === 'Escape') {
this.setValue(
this.htmlInput_!.getAttribute('data-untyped-default-value'),
false,
);
WidgetDiv.hide();
WidgetDiv.hideIfOwner(this);
dropDownDiv.hideWithoutAnimation();
} else if (e.key === 'Tab') {
WidgetDiv.hide();
WidgetDiv.hideIfOwner(this);
dropDownDiv.hideWithoutAnimation();
(this.sourceBlock_ as BlockSvg).tab(this, !e.shiftKey);
e.preventDefault();

View File

@@ -240,7 +240,7 @@ export class HorizontalFlyout extends Flyout {
this.workspace_.scrollbar?.setX(pos);
// When the flyout moves from a wheel event, hide WidgetDiv and
// dropDownDiv.
WidgetDiv.hide();
WidgetDiv.hideIfOwnerIsInWorkspace(this.workspace_);
dropDownDiv.hideWithoutAnimation();
}
// Don't scroll the page.

View File

@@ -209,7 +209,7 @@ export class VerticalFlyout extends Flyout {
this.workspace_.scrollbar?.setY(pos);
// When the flyout moves from a wheel event, hide WidgetDiv and
// dropDownDiv.
WidgetDiv.hide();
WidgetDiv.hideIfOwnerIsInWorkspace(this.workspace_);
dropDownDiv.hideWithoutAnimation();
}
// Don't scroll the page.

View File

@@ -8,7 +8,7 @@
import * as common from './common.js';
import * as dom from './utils/dom.js';
import type {Field} from './field.js';
import {Field} from './field.js';
import type {Rect} from './utils/rect.js';
import type {Size} from './utils/size.js';
import type {WorkspaceSvg} from './workspace_svg.js';
@@ -16,6 +16,9 @@ import type {WorkspaceSvg} from './workspace_svg.js';
/** The object currently using this container. */
let owner: unknown = null;
/** The workspace associated with the owner currently using this container. */
let ownerWorkspace: WorkspaceSvg | null = null;
/** Optional cleanup function set by whichever object uses the widget. */
let dispose: (() => void) | null = null;
@@ -76,8 +79,14 @@ export function createDom() {
* @param rtl Right-to-left (true) or left-to-right (false).
* @param newDispose Optional cleanup function to be run when the widget is
* closed.
* @param workspace The workspace associated with the widget owner.
*/
export function show(newOwner: unknown, rtl: boolean, newDispose: () => void) {
export function show(
newOwner: unknown,
rtl: boolean,
newDispose: () => void,
workspace?: WorkspaceSvg | null,
) {
hide();
owner = newOwner;
dispose = newDispose;
@@ -85,9 +94,16 @@ export function show(newOwner: unknown, rtl: boolean, newDispose: () => void) {
if (!div) return;
div.style.direction = rtl ? 'rtl' : 'ltr';
div.style.display = 'block';
const mainWorkspace = common.getMainWorkspace() as WorkspaceSvg;
rendererClassName = mainWorkspace.getRenderer().getClassName();
themeClassName = mainWorkspace.getTheme().getClassName();
if (!workspace && newOwner instanceof Field) {
// For backward compatibility with plugin fields that do not provide a
// workspace to this function, attempt to derive it from the field.
workspace = (newOwner as Field).getSourceBlock()?.workspace as WorkspaceSvg;
}
ownerWorkspace = workspace ?? null;
const rendererWorkspace =
workspace ?? (common.getMainWorkspace() as WorkspaceSvg);
rendererClassName = rendererWorkspace.getRenderer().getClassName();
themeClassName = rendererWorkspace.getTheme().getClassName();
if (rendererClassName) {
dom.addClass(div, rendererClassName);
}
@@ -145,6 +161,19 @@ export function hideIfOwner(oldOwner: unknown) {
hide();
}
}
/**
* Destroy the widget and hide the div if it is being used by an object in the
* specified workspace, or if it is used by an unknown workspace.
*
* @param oldOwnerWorkspace The workspace that was using this container.
*/
export function hideIfOwnerIsInWorkspace(oldOwnerWorkspace: WorkspaceSvg) {
if (ownerWorkspace === null || ownerWorkspace === oldOwnerWorkspace) {
hide();
}
}
/**
* Set the widget div's position and height. This function does nothing clever:
* it will not ensure that your widget div ends up in the visible window.

View File

@@ -1686,7 +1686,7 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg {
this.configureContextMenu(menuOptions, e);
}
ContextMenu.show(e, menuOptions, this.RTL);
ContextMenu.show(e, menuOptions, this.RTL, this);
}
/**
@@ -2376,7 +2376,7 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg {
*/
hideChaff(onlyClosePopups = false) {
Tooltip.hide();
WidgetDiv.hide();
WidgetDiv.hideIfOwnerIsInWorkspace(this);
dropDownDiv.hideWithoutAnimation();
this.hideComponents(onlyClosePopups);