Skip to content

Commit e3c7f2b

Browse files
committed
[frontend] add context menus to a few elements
1 parent 31ee6d9 commit e3c7f2b

File tree

4 files changed

+128
-56
lines changed

4 files changed

+128
-56
lines changed

frontend/src/Tab.tsx

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from "./history";
99
import { NewTab } from "./pages/NewTab";
1010
import { Playground } from "./pages/Playground";
11+
import { createMenu } from "./components/Menu";
1112

1213
let id = 0;
1314
export class Tab extends StatefulClass {
@@ -54,6 +55,7 @@ export class Tab extends StatefulClass {
5455
addHistoryListeners(frame, this);
5556
frame.addEventListener("contextInit", (ctx) => {
5657
injectHistoryEmulation(ctx.client, this);
58+
injectContextMenu(ctx.client, this);
5759
});
5860

5961
this.frame = frame;
@@ -80,6 +82,54 @@ export class Tab extends StatefulClass {
8082
}
8183
}
8284

85+
function injectContextMenu(client: ScramjetClient, tab: Tab) {
86+
let frame = tab.frame;
87+
client.global.document.addEventListener("contextmenu", (e) => {
88+
// need to calculate the real position of the frame relative to the top
89+
let xoff = 0;
90+
let yoff = 0;
91+
let currentwin = client.global.window;
92+
while (currentwin.parent && currentwin.frameElement) {
93+
// this will return true until the end of the scramjet boundary
94+
let { x, y } = currentwin.frameElement.getBoundingClientRect();
95+
xoff += x;
96+
yoff += y;
97+
currentwin = currentwin.parent.window;
98+
}
99+
// parent is trapped, so it won't calculate the topmost iframe. do that manually
100+
let { x, y } = frame.frame.getBoundingClientRect();
101+
xoff += x;
102+
yoff += y;
103+
createMenu(xoff + e.pageX, yoff + e.pageY, [
104+
{
105+
label: "Back",
106+
action: () => {
107+
frame.back();
108+
},
109+
},
110+
{
111+
label: "Forward",
112+
action: () => {
113+
frame.forward();
114+
},
115+
},
116+
{
117+
label: "Reload",
118+
action: () => {
119+
frame.reload();
120+
},
121+
},
122+
{
123+
label: "Bookmark",
124+
action: () => {
125+
console.log("Bookmarking", tab.title, tab.url);
126+
},
127+
},
128+
]);
129+
e.preventDefault();
130+
});
131+
}
132+
83133
// frame.frame.addEventListener("load", (e) => {
84134
// tab.url = frame.client.url.href;
85135
// });
@@ -105,50 +155,6 @@ export class Tab extends StatefulClass {
105155

106156
// const framedoc = ctx.window.document;
107157

108-
// framedoc.addEventListener("contextmenu", (e) => {
109-
// // need to calculate the real position of the frame relative to the top
110-
// let xoff = 0;
111-
// let yoff = 0;
112-
// let currentwin = ctx.window;
113-
// while (currentwin.parent && currentwin.frameElement) {
114-
// // this will return true until the end of the scramjet boundary
115-
// let { x, y } = currentwin.frameElement.getBoundingClientRect();
116-
// xoff += x;
117-
// yoff += y;
118-
// currentwin = currentwin.parent;
119-
// }
120-
// // parent is trapped, so it won't calculate the topmost iframe. do that manually
121-
// let { x, y } = frame.frame.getBoundingClientRect();
122-
// xoff += x;
123-
// yoff += y;
124-
// createMenu(xoff + e.pageX, yoff + e.pageY, [
125-
// {
126-
// label: "Back",
127-
// action: () => {
128-
// frame.back();
129-
// },
130-
// },
131-
// {
132-
// label: "Forward",
133-
// action: () => {
134-
// frame.forward();
135-
// },
136-
// },
137-
// {
138-
// label: "Reload",
139-
// action: () => {
140-
// frame.reload();
141-
// },
142-
// },
143-
// {
144-
// label: "Bookmark",
145-
// action: () => {
146-
// console.log("Bookmarking", tab.title, tab.url);
147-
// },
148-
// },
149-
// ]);
150-
// e.preventDefault();
151-
// });
152158
// const head = framedoc.querySelector("head")!;
153159
// const observer = new MutationObserver(() => {
154160
// const title = framedoc.querySelector("title");

frontend/src/components/Menu.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ Menu.style = css`
7171

7272
let activeMenu: DLElement<typeof Menu> | null = null;
7373

74+
export function setContextMenu(
75+
elm: HTMLElement,
76+
items: { label: string; action?: () => void }[]
77+
) {
78+
elm.addEventListener("contextmenu", (e) => {
79+
e.preventDefault();
80+
e.stopPropagation();
81+
createMenu(e.clientX, e.clientY, items);
82+
});
83+
}
84+
7485
export function createMenu(
7586
x: number,
7687
y: number,

frontend/src/components/Omnibox.tsx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import iconSettings from "@ktibow/iconset-ion/settings-outline";
77
import iconShield from "@ktibow/iconset-ion/shield-outline";
88
import iconStar from "@ktibow/iconset-ion/star-outline";
99
import iconSearch from "@ktibow/iconset-ion/search";
10-
import { createMenu } from "./Menu";
10+
import { createMenu, setContextMenu } from "./Menu";
1111
import { browser, client } from "../main";
1212
import { IconButton } from "./IconButton";
13+
import { createDelegate, type Delegate } from "dreamland/utils";
1314

1415
export const Spacer: Component = function (cx) {
1516
return <div></div>;
@@ -24,6 +25,7 @@ export const UrlInput: Component<
2425
{
2526
tabUrl: URL;
2627
navigate: (url: string) => void;
28+
selectContent: Delegate<void>;
2729
},
2830
{
2931
value: string;
@@ -67,6 +69,26 @@ export const UrlInput: Component<
6769
fetchSuggestions();
6870
}
6971
});
72+
const activate = () => {
73+
this.active = true;
74+
browser.unfocusframes = true;
75+
document.body.addEventListener("click", (e) => {
76+
this.active = false;
77+
browser.unfocusframes = false;
78+
e.stopPropagation();
79+
});
80+
this.value = this.tabUrl.href;
81+
this.input.focus();
82+
this.input.select();
83+
};
84+
85+
this.selectContent.listen(() => {
86+
this.active = true;
87+
activate();
88+
89+
this.input.select();
90+
});
91+
7092
return (
7193
<div
7294
on:click={(e: MouseEvent) => {
@@ -75,16 +97,7 @@ export const UrlInput: Component<
7597
e.stopPropagation();
7698
return;
7799
}
78-
this.active = true;
79-
browser.unfocusframes = true;
80-
document.body.addEventListener("click", (e) => {
81-
this.active = false;
82-
browser.unfocusframes = false;
83-
e.stopPropagation();
84-
});
85-
this.value = this.tabUrl.href;
86-
this.input.focus();
87-
this.input.select();
100+
activate();
88101
e.stopPropagation();
89102
}}
90103
>
@@ -243,6 +256,17 @@ export const Omnibox: Component<{
243256
canGoBack: boolean;
244257
canGoForwards: boolean;
245258
}> = function (cx) {
259+
const selectContent = createDelegate<void>();
260+
cx.mount = () => {
261+
setContextMenu(cx.root, [
262+
{
263+
label: "Select All",
264+
action: () => {
265+
selectContent();
266+
},
267+
},
268+
]);
269+
};
246270
return (
247271
<div>
248272
<IconButton
@@ -257,7 +281,11 @@ export const Omnibox: Component<{
257281
></IconButton>
258282
<IconButton click={this.refresh} icon={iconRefresh}></IconButton>
259283
<Spacer></Spacer>
260-
<UrlInput tabUrl={use(this.tabUrl)} navigate={this.navigate}></UrlInput>
284+
<UrlInput
285+
selectContent={selectContent}
286+
tabUrl={use(this.tabUrl)}
287+
navigate={this.navigate}
288+
></UrlInput>
261289
<Spacer></Spacer>
262290
<IconButton icon={iconExtension}></IconButton>
263291
<IconButton

frontend/src/components/TabStrip.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { memoize } from "../memoize";
1212
import { IconButton } from "./IconButton";
1313
import type { Tab } from "../Tab";
1414
import html2canvas from "html2canvas";
15+
import { setContextMenu } from "./Menu";
1516

1617
export const DragTab: Component<{
1718
active: boolean;
@@ -21,6 +22,23 @@ export const DragTab: Component<{
2122
destroy: () => void;
2223
transitionend: () => void;
2324
}> = function (cx) {
25+
cx.mount = () => {
26+
setContextMenu(cx.root, [
27+
{
28+
label: "Reload",
29+
action: () => {
30+
this.tab.frame.reload();
31+
},
32+
},
33+
{
34+
label: "Close Tab",
35+
action: () => {
36+
this.destroy();
37+
},
38+
},
39+
// TODO: mute? duplicate?
40+
]);
41+
};
2442
return (
2543
<div
2644
style="z-index: 0;"
@@ -314,6 +332,15 @@ export const Tabs: Component<
314332
cx.mount = () => {
315333
requestAnimationFrame(() => layoutTabs(false));
316334
window.addEventListener("resize", () => layoutTabs(false));
335+
336+
setContextMenu(cx.root, [
337+
{
338+
label: "New Tab",
339+
action: () => {
340+
this.addTab();
341+
},
342+
},
343+
]);
317344
};
318345

319346
const getMaxDragPos = () => {

0 commit comments

Comments
 (0)