-
Notifications
You must be signed in to change notification settings - Fork 28k
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
Closes #54285 adds webview/context contribution #154524
Conversation
@mjbvz I've just started on this PR to address #54285 please let me know what you think. I'm not sure if plumbing the With these changes I was able to get modified version of GitLens to add custom menu items to a specific webview (e.g. the welcome view) with the following contribution:
When executed, the command callback gets an arg which looks like With this contribution, extensions could then use their own context keys to hide and show the desired commands. The challenges left here are:
|
👍 this is a requirement on my end |
@Tyriar do you think this could just be an all or nothing thing? Basically a flag on the webview that says that the extension will control the context menu completely, so cut/copy/paste won't be added/shown for that webview? If the extension needs it, they can manually add those commands themselves. |
I'd be fine with all or nothing, I agree it should be easy to add themselves. |
I'm going to rename I'm also going to start implementing:
Any preference on the data attribute name? My current plan is for |
@mjbvz @Tyriar I think this PR is basically complete outside of any desired naming changes. It works for normal webviews, webview views, and custom editors -- other webviews should be uneffected except for menu contributions that are always available (since they have no provided context) I've added a new I've also provided 3 context keys for the context menu
Here's an example using the GitLens Rebase Editor: "webview/context": [
{
"command": "gitlens.rebase.help",
"when": "webview == gitlens.rebase",
"group": "9_gitlens@1"
},
{
"command": "gitlens.rebase.copySha",
"when": "webview == gitlens.rebase && webviewItem == commit && webviewItemElement == sha",
"group": "1_gitlens@1"
},
{
"command": "gitlens.rebase.copyMessage",
"when": "webview == gitlens.rebase && webviewItem == commit && webviewItemElement == message",
"group": "1_gitlens@1"
},
{
"command": "gitlens.rebase.showCommitDetails",
"when": "webview == gitlens.rebase && webviewItem == commit",
"group": "2_gitlens@1"
}
], Here's the DOM -- I have |
Adds `webViewItem` read from nearest `data-vscode-context-menu-item` Adds `webViewItemElement` read from `data-vscode-context-menu-item-element`
This allows webviews to opt-out of the default (cut/copy/paste) context menu items
I also added an additional |
@mjbvz any comments on the direction/naming/etc? 😉 |
Hey @eamodio! Checking back in on this PR (thank you again for contributing this 🙏) and I have two questions after catching up on everything. Render context menus on click? This would enable the UI/UX scenario where clicking an icon button will render a menu (anchored to the button). This has been a highly requested bit of UI for the webview toolkit and it would be awesome if we could provide this to extension authors. Sub question: While writing this I just realized the above UI pattern will also probably require that extension authors are provided a reference to the context menu DOM in order to correctly anchor it to an icon button (a la something like Popper). Would exposing that be possible (or maybe it's already possible to get a reference to a context menu DOM)? Render native macOS context menus? |
Yes they should as this is just building on the cut/copy/paste context menu that @mjbvz already added to the webviews
For this, while I would love to have it, I would do that in a separate PR/contribution, since this PR doesn't really add any new API or API surface-area (other than the additional Although because the webview is in charge of the DOM, I believe you could just simulate a right-click (e.g. generate a |
Nice :) Thanks for clarifying
Totally understandable! I'd be happy to open an issue to track this if desired. But I suppose I'll double check with @mjbvz that this is an API surface you would be open to adding in the first place?
Great thought/good to know there may be a way to (in theory) still implement this behavior in the meantime. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into this! This is promising but I want to make sure we have great support for showing different context menus in different parts of the webview.
While it seems that this is kind of possible with the current approach, what if instead of just setting the key for a single item/element, we were to traverse up from the event target to find all ancestor contexts keys in the webview dom and set those? This is more like how VS Code handles contexts for the editor itself
For example, for the below html:
<div class="menu" data-vscode-context='{"webviewSection": "menu", "countOfThing": 123}'>
...
<div class="nav" data-vscode-context='{"webviewSection: "nav", "vscodeSupressDefaultContextMenuItems": true}'>
...
</div>
</div>
When right clicking inside nav
, the resulting context keys would be:
webviewSection
="nav"
(overrides the value from the. menu
element)countOfThing
=123
(inherited from the parent.menu
element)vscodeSupressDefaultContextMenuItems
=true
I'm not sure if a json format the above example is best but this approach does seem promising. It also would let us remove preventDefaultContextMenuItems
from vscode.d.ts
and instead let the webview specify this itself by setting vscodeSupressDefaultContextMenuItems
somewhere in the ancestors of the clicked element (potentially even at the root of the document)
What do you think?
Aggregates all ancestor context
@mjbvz I think that sounds like a good idea -- initially I was a little concerned since it would a) be a little more overhead and b) there would be no standardization for how to deal with webview context, and c) packing JSON into the DOM felt odd. But a) shouldn't be much of a big deal at all and b) I'm not sure if that is good or bad -- this way is certainly more flexible and allows for much better control over the context data for both the I've pushed changes which implement the suggested change to use JSON in a single I also added a |
@hawkticehurst I tested out the I setup a private onClicked(e: MouseEvent) {
const evt = new MouseEvent('contextmenu', {
bubbles: true,
clientX: e.clientX,
clientY: e.clientY,
});
e.target?.dispatchEvent(evt);
e.stopImmediatePropagation();
} And that opened the desired context menu in the correct placement. |
This reverts commit 71f0180. Also renames proposal from webviewContextMenus to contribWebviewContext to align with other empty contribution point proposals
@@ -329,12 +341,18 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD | |||
const elementBox = this.element.getBoundingClientRect(); | |||
contextMenuService.showContextMenu({ | |||
getActions: () => { | |||
const contextKeyService = this._contextKeyService!.createOverlay([ | |||
...Object.entries(data.context), | |||
['webview', this.providedId], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look into adding a generic activeWebviewId
context key that is set whenever a webview is focused. This matches the activeCustomEditorId
context key we already have
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that different than webview
? That is currently the "id" of the webview that the context menu is being shown for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or are you saying to usages outside of the context menu -- a more global context key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Will make a few tweaks to the context names and then discuss this further in the August API syncs
@mjbvz Woo! Thanks! What are your thoughts on: #154524 (comment) Should we maybe add something into the webview to wrap that up and make it more accessible? To be clear I'm not suggesting to wrap it up in this proposal, since I want/need this to land as soon as possible -- more if you feel it warrants another proposal/exploration |
@eamodio Maybe this could live in the webview UI toolkit? Not currently convinced its a common enough need to put into core VS COde |
Yeah, if we offer a button that shows a context menu the component could provide that behavior. |
Oh yeah, to be clear, I was thinking the same thing when I posted my original comment! We already have an icon button implemented in the webview UI toolkit that can be used and I'd be totally happy to create a context menu button type component that abstracts away a lot of the behavior of this new component. My hope is that VS Code core would provide some slightly cleaner APIs for handling click events and the ability to anchor the menu to another component. I think the code snippet that @eamodio provided above works well enough for synthetically creating a click event (but I would of course happily take a cleaner way of doing this). The big problem (as far as I can tell) is that the above code will render the context menu at the exact X/Y coordinates of the mouse click: clientX: e.clientX,
clientY: e.clientY, This could result in context menus being rendered in a way that overlaps the button depending on where it's been clicked and would likely be unacceptable for the toolkit since one of its primary goals is to provide a set of components that follow the design language of VS Code core UI (which in this case always renders menus to the bottom left of icon buttons). Using Popper as an easy reference for possible syntax, it would be great if there was some ability to do this: import { createPopper } from '@popperjs/core';
const vscode = acquireVSCodeAPI();
const button = document.querySelector('#toolkit-icon-button');
const menu = vscode.getReferenceToContextMenuAsDOMNode("my-context-menu");
createPopper(button, menu, {
placement: 'bottom-start',
}); Specific APIs/syntax/implementation details aside, the main objective would be some way of being able to get the context menu to always render at the bottom left of an icon button. |
Yeah to get the placement correct you'd have to just provide the right x/y coordinates, which you can get by getting the bounding box of the target element |
Ahhh fantastic (thank you). In that case, I'm totally happy to take a go at implementing all of this as a component in the toolkit once this API lands in core! |
This PR fixes #54285