Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Add context menu options for workspace comments #8035

Merged
merged 5 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ export class BlockSvg
* @param e Mouse event.
* @internal
*/
showContextMenu(e: Event) {
showContextMenu(e: PointerEvent) {
const menuOptions = this.generateContextMenu();

if (menuOptions && menuOptions.length) {
Expand Down
17 changes: 0 additions & 17 deletions core/blockly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,23 +399,6 @@ WorkspaceSvg.newTrashcan = function (workspace: WorkspaceSvg): Trashcan {
return new Trashcan(workspace);
};

WorkspaceCommentSvg.prototype.showContextMenu = function (
this: WorkspaceCommentSvg,
e: Event,
) {
if (this.workspace.options.readOnly) {
return;
}
const menuOptions = [];

if (this.isDeletable() && this.isMovable()) {
menuOptions.push(ContextMenu.commentDuplicateOption(this));
menuOptions.push(ContextMenu.commentDeleteOption(this));
}

ContextMenu.show(e, menuOptions, this.RTL);
};

MiniWorkspaceBubble.prototype.newWorkspaceSvg = function (
options: Options,
): WorkspaceSvg {
Expand Down
20 changes: 14 additions & 6 deletions core/comments/rendered_workspace_comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import {
WorkspaceCommentPaster,
WorkspaceCommentCopyData,
} from '../clipboard/workspace_comment_paster.js';
import {IContextMenu} from '../interfaces/i_contextmenu.js';
import * as contextMenu from '../contextmenu.js';
import {ContextMenuRegistry} from '../contextmenu_registry.js';

export class RenderedWorkspaceComment
extends WorkspaceComment
Expand All @@ -34,7 +37,8 @@ export class RenderedWorkspaceComment
IDraggable,
ISelectable,
IDeletable,
ICopyable<WorkspaceCommentCopyData>
ICopyable<WorkspaceCommentCopyData>,
IContextMenu
{
/** The class encompassing the svg elements making up the workspace comment. */
private view: CommentView;
Expand Down Expand Up @@ -178,11 +182,6 @@ export class RenderedWorkspaceComment
}
}

/** Returns whether this comment is deletable or not. */
isDeletable(): boolean {
return !this.workspace.options.readOnly;
}

BeksOmega marked this conversation as resolved.
Show resolved Hide resolved
/** Visually indicates that this comment would be deleted if dropped. */
setDeleteStyle(wouldDelete: boolean): void {
if (wouldDelete) {
Expand Down Expand Up @@ -239,4 +238,13 @@ export class RenderedWorkspaceComment
}),
};
}

/** Show a context menu for this comment. */
showContextMenu(e: PointerEvent): void {
const menuOptions = ContextMenuRegistry.registry.getContextMenuOptions(
ContextMenuRegistry.ScopeType.COMMENT,
{comment: this},
);
contextMenu.show(e, menuOptions, this.workspace.RTL);
}
}
135 changes: 5 additions & 130 deletions core/contextmenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import type {Block} from './block.js';
import type {BlockSvg} from './block_svg.js';
import * as browserEvents from './browser_events.js';
import * as clipboard from './clipboard.js';
import {config} from './config.js';
import * as dom from './utils/dom.js';
import type {
Expand All @@ -19,15 +18,11 @@ import type {
import * as eventUtils from './events/utils.js';
import {Menu} from './menu.js';
import {MenuItem} from './menuitem.js';
import {Msg} from './msg.js';
import * as aria from './utils/aria.js';
import {Coordinate} from './utils/coordinate.js';
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 {WorkspaceCommentSvg} from './workspace_comment_svg.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as Xml from './xml.js';
import * as common from './common.js';

Expand Down Expand Up @@ -69,7 +64,7 @@ let menu_: Menu | null = null;
* @param rtl True if RTL, false if LTR.
*/
export function show(
e: Event,
e: PointerEvent,
options: (ContextMenuOption | LegacyContextMenuOption)[],
rtl: boolean,
) {
Expand All @@ -78,7 +73,7 @@ export function show(
hide();
return;
}
const menu = populate_(options, rtl);
const menu = populate_(options, rtl, e);
menu_ = menu;

position_(menu, e, rtl);
Expand All @@ -95,11 +90,13 @@ export function show(
*
* @param options Array of menu options.
* @param rtl True if RTL, false if LTR.
* @param e The event that triggered the context menu to open.
* @returns The menu that will be shown on right click.
*/
function populate_(
options: (ContextMenuOption | LegacyContextMenuOption)[],
rtl: boolean,
e: PointerEvent,
): Menu {
/* Here's what one option object looks like:
{text: 'Make It So',
Expand All @@ -124,7 +121,7 @@ function populate_(
// will not be expecting a scope parameter, so there should be
// no problems. Just assume it is a ContextMenuOption and we'll
// pass undefined if it's not.
option.callback((option as ContextMenuOption).scope);
option.callback((option as ContextMenuOption).scope, e);
}, 0);
});
};
Expand Down Expand Up @@ -266,125 +263,3 @@ export function callbackFactory(
return newBlock;
};
}

// Helper functions for creating context menu options.

/**
* Make a context menu option for deleting the current workspace comment.
*
* @param comment The workspace comment where the
* right-click originated.
* @returns A menu option,
* containing text, enabled, and a callback.
* @internal
*/
export function commentDeleteOption(
comment: WorkspaceCommentSvg,
): LegacyContextMenuOption {
const deleteOption = {
text: Msg['REMOVE_COMMENT'],
enabled: true,
callback: function () {
eventUtils.setGroup(true);
comment.dispose();
eventUtils.setGroup(false);
},
};
return deleteOption;
}

/**
* Make a context menu option for duplicating the current workspace comment.
*
* @param comment The workspace comment where the
* right-click originated.
* @returns A menu option,
* containing text, enabled, and a callback.
* @internal
*/
export function commentDuplicateOption(
comment: WorkspaceCommentSvg,
): LegacyContextMenuOption {
const duplicateOption = {
text: Msg['DUPLICATE_COMMENT'],
enabled: true,
callback: function () {
const data = comment.toCopyData();
if (!data) return;
clipboard.paste(data, comment.workspace);
},
};
return duplicateOption;
}

/**
* Make a context menu option for adding a comment on the workspace.
*
* @param ws The workspace where the right-click
* originated.
* @param e The right-click mouse event.
* @returns A menu option, containing text, enabled, and a callback.
* comments are not bundled in.
* @internal
*/
export function workspaceCommentOption(
ws: WorkspaceSvg,
e: Event,
): ContextMenuOption {
/**
* Helper function to create and position a comment correctly based on the
* location of the mouse event.
*/
function addWsComment() {
const comment = new WorkspaceCommentSvg(
ws,
Msg['WORKSPACE_COMMENT_DEFAULT_TEXT'],
WorkspaceCommentSvg.DEFAULT_SIZE,
WorkspaceCommentSvg.DEFAULT_SIZE,
);

const injectionDiv = ws.getInjectionDiv();
// Bounding rect coordinates are in client coordinates, meaning that they
// are in pixels relative to the upper left corner of the visible browser
// window. These coordinates change when you scroll the browser window.
const boundingRect = injectionDiv.getBoundingClientRect();

// The client coordinates offset by the injection div's upper left corner.
const mouseEvent = e as MouseEvent;
const clientOffsetPixels = new Coordinate(
mouseEvent.clientX - boundingRect.left,
mouseEvent.clientY - boundingRect.top,
);

// The offset in pixels between the main workspace's origin and the upper
// left corner of the injection div.
const mainOffsetPixels = ws.getOriginOffsetInPixels();

// The position of the new comment in pixels relative to the origin of the
// main workspace.
const finalOffset = Coordinate.difference(
clientOffsetPixels,
mainOffsetPixels,
);
// The position of the new comment in main workspace coordinates.
finalOffset.scale(1 / ws.scale);

const commentX = finalOffset.x;
const commentY = finalOffset.y;
comment.moveBy(commentX, commentY);
if (ws.rendered) {
comment.initSvg();
comment.render();
common.setSelected(comment);
}
}

const wsCommentOption = {
enabled: true,
} as ContextMenuOption;
wsCommentOption.text = Msg['ADD_COMMENT'];
wsCommentOption.callback = function () {
addWsComment();
};
return wsCommentOption;
}
Loading
Loading