[v3] Enable default context menu for certain elements. Use CSS to customise.

This commit is contained in:
Lea Anthony 2023-06-20 21:11:37 +10:00
commit 7f6151d573
No known key found for this signature in database
GPG key ID: 33DAF7BB90A58405
11 changed files with 194 additions and 141 deletions

View file

@ -117,7 +117,13 @@ Webview Window Interface Methods
|------------------|---------|-------|-----|-------|
| OpenContextMenu | Y | | Y | |
| On By Default | | | | |
| Control via HTML | | | | |
| Control via HTML | Y | | | |
The default context menu is enabled by default for all elements that are `contentEditable: true`, `<input>` or `<textarea>` tags or have the `--default-contextmenu: true` style set.
The `--default-contextmenu: show` style will always show the context menu
The `--default-contextmenu: hide` style will always hide the context menu
Anything nested under a tag with `--default-contextmenu: hide` style will not show the context menu unless it is explicitly set with `--default-contextmenu: show`.
### Screens

View file

@ -3,31 +3,29 @@
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>body{ text-align: center; color: white; background-color: rgba(0,0,0,0); user-select: none; -ms-user-select: none; -webkit-user-select: none; }</style>
<style>.file{ width: 100px; height: 100px; border: 3px solid black; }</style>
<style>body{ text-align: center; color: white; background-color: rgba(0,0,0,255); user-select: none; -ms-user-select: none; -webkit-user-select: none; }</style>
<style>.region{ width: 100%; border: 3px solid #00a4db; }</style>
</head>
<body>
<h1>Context Menu Demo</h1>
<br/>
<div class="file" id="123abc" data-contextmenu="test" data-contextmenu-data="1">
<div class="region" id="123abc" style="--custom-contextmenu: test; --custom-contextmenu-data: 1">
<h1>1</h1>
</div>
<div class="file" id="234abc" data-contextmenu="test" data-contextmenu-data="2">
<div class="region" id="234abc" style="--custom-contextmenu: test; --custom-contextmenu-data: 2">
<h1>2</h1>
</div>
<div class="file" id="123abcg" style="--default-contextmenu: show">
<div class="region" id="123abcg" style="--default-contextmenu: show">
<h1>Default Context Menu shown here</h1>
<label>
<input type="text" placeholder="context menu hidden here" style="--default-contextmenu: hide"/>
</label>
</div>
<div class="file" id="234abcs" style="--default-contextmenu: hide">
<div class="region" id="234abcs" style="--default-contextmenu: hide">
<h1>Default Context Menu hidden here</h1>
<label>
<input type="text" placeholder="context menu works here" style="--default-contextmenu: show"/>
</label>
</div>
<div id="results"></div>
</body>
<script>
// window.addEventListener("dragstart", (event) =>
// event.dataTransfer.setData("text/plain", "This text may be dragged")
// );
</script>
</html>

View file

@ -26,16 +26,9 @@ func main() {
})
mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Context Menu Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
},
})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Context Menu Demo",
Title: "Context Menu Demo",
Width: 1024,
Height: 800,
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified,
@ -46,7 +39,6 @@ func main() {
contextMenu := app.NewMenu()
contextMenu.Add("Click Me").OnClick(func(data *application.Context) {
fmt.Printf("Context menu data: %+v\n", data.ContextMenuData())
app.Quit()
})
globalContextMenu := app.NewMenu()

View file

@ -6,36 +6,64 @@ function openContextMenu(id, x, y, data) {
void call("OpenContextMenu", {id, x, y, data});
}
export function enableContextMenus(enabled) {
if (enabled) {
window.addEventListener('contextmenu', contextMenuHandler);
} else {
window.removeEventListener('contextmenu', contextMenuHandler);
}
export function setupContextMenus() {
window.addEventListener('contextmenu', contextMenuHandler);
}
function contextMenuHandler(event) {
let processed = processContextMenu(event.target, event);
if (!processed) {
let defaultContextMenuAction = window.getComputedStyle(event.target).getPropertyValue("--default-contextmenu");
defaultContextMenuAction = defaultContextMenuAction ? defaultContextMenuAction.trim() : "";
if (defaultContextMenuAction === 'hide') {
event.preventDefault();
}
// Check for custom context menu
let element = event.target;
let customContextMenu = window.getComputedStyle(element).getPropertyValue("--custom-contextmenu");
customContextMenu = customContextMenu ? customContextMenu.trim() : "";
if (customContextMenu) {
event.preventDefault();
let customContextMenuData = window.getComputedStyle(element).getPropertyValue("--custom-contextmenu-data");
openContextMenu(customContextMenu, event.clientX, event.clientY, customContextMenuData);
return
}
processDefaultContextMenu(event);
}
function processContextMenu(element, event) {
let id = element.getAttribute('data-contextmenu');
if (id) {
event.preventDefault();
openContextMenu(id, event.clientX, event.clientY, element.getAttribute('data-contextmenu-data'));
return true;
} else {
let parent = element.parentElement;
if (parent) {
processContextMenu(parent, event);
}
/*
Default: Show default context menu if contentEditable: true OR text has been selected OR --default-contextmenu: show OR tagname is input or textarea
--default-contextmenu: show will always show the context menu
--default-contextmenu: hide will always hide the context menu
Anything nested under a tag with --default-contextmenu: hide will not show the context menu unless it is explicitly set with --default-contextmenu: show
*/
function processDefaultContextMenu(event) {
// Process default context menu
let element = event.target;
let defaultContextMenuAction = window.getComputedStyle(element).getPropertyValue("--default-contextmenu");
defaultContextMenuAction = defaultContextMenuAction ? defaultContextMenuAction.trim() : "";
switch(defaultContextMenuAction) {
case "show":
return;
case "hide":
event.preventDefault();
return;
default:
// Check if contentEditable is true
let contentEditable = element.getAttribute("contentEditable");
if (contentEditable && contentEditable.toLowerCase() === "true") {
return;
}
// Check if text has been selected
let selection = window.getSelection();
if (selection && selection.toString().length > 0) {
return;
}
// Check if tagname is input or textarea
let tagName = element.tagName.toLowerCase();
if (tagName === "input" || tagName === "textarea") {
return;
}
// hide default context menu
event.preventDefault();
}
return false;
}

View file

@ -18,7 +18,7 @@ import {Plugin, Call, callErrorCallback, callCallback} from "./calls";
import {newWindow} from "./window";
import {dispatchWailsEvent, Emit, Off, OffAll, On, Once, OnMultiple} from "./events";
import {dialogCallback, dialogErrorCallback, Error, Info, OpenFile, Question, SaveFile, Warning,} from "./dialogs";
import {enableContextMenus} from "./contextmenu";
import {setupContextMenus} from "./contextmenu";
import {reloadWML} from "./wml";
import {setupDrag, endDrag} from "./drag";
@ -85,8 +85,7 @@ if (DEBUG) {
console.log("Wails v3.0.0 Debug Mode Enabled");
}
enableContextMenus(true);
setupContextMenus();
setupDrag();
document.addEventListener("DOMContentLoaded", function(event) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long