Skip to content
174 linesCodeBlameRaw
1// The title bar's work: draw what the page under it reports, and send its
2// buttons to the app (src/bridge.rs: title_bar_current, title_bar_act,
3// window_control, window_menu). A click here hands the keyboard back to
4// the page, so typing goes on where it was.
5(function () {
6 var core = window.__TAURI__ && window.__TAURI__.core;
7 var events = window.__TAURI__ && window.__TAURI__.event;
8 function invoke(command, args) {
9 if (core && typeof core.invoke === "function") return core.invoke(command, args || {});
10 return Promise.reject(new Error("the title bar cannot reach the app"));
11 }
12 function quietly(promise) {
13 promise.catch(function () {});
14 }
15 function act(action) {
16 quietly(invoke("title_bar_act", { action: action }));
17 }
18 function control(action) {
19 return invoke("window_control", { action: action });
20 }
21
22 var ua = navigator.userAgent;
23 var MAC = /Macintosh|Mac OS X/.test(ua) && !/Windows/.test(ua);
24 if (MAC) document.documentElement.classList.add("mac");
25
26 function svg(body) {
27 return (
28 '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
29 body +
30 "</svg>"
31 );
32 }
33 // Drawn here, from lines and boxes, as each system draws its own.
34 var ICONS = {
35 menu: svg('<line x1="3" y1="4.5" x2="13" y2="4.5"/><line x1="3" y1="8" x2="13" y2="8"/><line x1="3" y1="11.5" x2="13" y2="11.5"/>'),
36 back: svg('<polyline points="9.5,3.5 5,8 9.5,12.5"/><line x1="5" y1="8" x2="13" y2="8"/>'),
37 forward: svg('<polyline points="6.5,3.5 11,8 6.5,12.5"/><line x1="3" y1="8" x2="11" y2="8"/>'),
38 search: svg('<circle cx="7" cy="7" r="4"/><line x1="10" y1="10" x2="13" y2="13"/>'),
39 minimize: svg('<line x1="3.5" y1="8" x2="12.5" y2="8"/>'),
40 maximize: svg('<rect x="3.5" y="3.5" width="9" height="9" rx="1"/>'),
41 restore: svg('<rect x="3.5" y="5.5" width="7" height="7" rx="1"/><path d="M5.5 5.5V4.5a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-1"/>'),
42 close: svg('<line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/>'),
43 };
44
45 function button(kind, label, icon, onClick) {
46 var b = document.createElement("button");
47 b.type = "button";
48 b.tabIndex = -1;
49 b.className = kind;
50 b.setAttribute("aria-label", label);
51 b.title = label;
52 if (icon) b.innerHTML = ICONS[icon];
53 b.addEventListener("click", function (event) {
54 event.stopPropagation();
55 onClick(b);
56 });
57 return b;
58 }
59
60 var bar = document.getElementById("bar");
61 var left = document.getElementById("left");
62 var middle = document.getElementById("middle");
63 var right = document.getElementById("right");
64
65 if (!MAC) {
66 left.append(
67 button("icon", "Menu", "menu", function (b) {
68 var box = b.getBoundingClientRect();
69 quietly(invoke("window_menu", { x: box.left, y: box.bottom }));
70 }),
71 );
72 }
73 var back = button("icon", "Back", "back", function () {
74 act("back");
75 });
76 var forward = button("icon", "Forward", "forward", function () {
77 act("forward");
78 });
79 left.append(back, forward);
80
81 var maximize = null;
82 function drawMaximized(maximized) {
83 if (!maximize) return;
84 maximize.innerHTML = maximized ? ICONS.restore : ICONS.maximize;
85 maximize.setAttribute("aria-label", maximized ? "Restore" : "Maximize");
86 maximize.title = maximized ? "Restore" : "Maximize";
87 }
88 // The window's buttons, at the right as Windows and Linux put them; macOS has its own.
89 if (!MAC) {
90 maximize = button("caption", "Maximize", "maximize", function () {
91 control("maximize").then(drawMaximized, function () {});
92 });
93 right.append(
94 button("caption", "Minimize", "minimize", function () {
95 quietly(control("minimize"));
96 }),
97 maximize,
98 button("caption close", "Close", "close", function () {
99 quietly(control("close"));
100 }),
101 );
102 var resizing = 0;
103 window.addEventListener("resize", function () {
104 clearTimeout(resizing);
105 resizing = setTimeout(function () {
106 control("maximized").then(drawMaximized, function () {});
107 }, 120);
108 });
109 control("maximized").then(drawMaximized, function () {});
110 }
111
112 // Dragging the bar moves the window; a double click maximizes it, as a system's title bar does.
113 bar.addEventListener("mousedown", function (event) {
114 if (event.button !== 0 || event.target.closest("button")) return;
115 if (event.detail === 2) {
116 control("maximize").then(drawMaximized, function () {});
117 return;
118 }
119 quietly(control("drag"));
120 });
121 // The keyboard belongs to the page: a click here gives it back.
122 window.addEventListener("mouseup", function () {
123 act("focus");
124 });
125
126 var title = document.createElement("span");
127 title.className = "title";
128 var pill = button("search", "Search", "search", function () {
129 act("search");
130 });
131 var pillText = document.createElement("span");
132 pill.append(pillText);
133
134 function setColors(colors) {
135 var root = document.documentElement.style;
136 if (!colors) return;
137 var shade = colors.dark ? "237, 237, 239" : "15, 15, 17";
138 root.setProperty("--bg", colors.background);
139 root.setProperty("--fg", colors.foreground);
140 root.setProperty("--muted", colors.muted);
141 root.setProperty("--accent", colors.accent);
142 root.setProperty("--hover", "rgba(" + shade + ", " + (colors.dark ? 0.1 : 0.07) + ")");
143 root.setProperty("--pill", "rgba(" + shade + ", " + (colors.dark ? 0.09 : 0.05) + ")");
144 root.setProperty("--pill-hover", "rgba(" + shade + ", " + (colors.dark ? 0.14 : 0.09) + ")");
145 root.setProperty("--pill-edge", "rgba(" + shade + ", " + (colors.dark ? 0.08 : 0.1) + ")");
146 document.documentElement.style.colorScheme = colors.dark ? "dark" : "light";
147 }
148
149 function draw(page) {
150 if (!page) return;
151 middle.textContent = "";
152 if (page.search) {
153 pillText.textContent = page.search;
154 pill.setAttribute("aria-label", page.search);
155 pill.removeAttribute("title");
156 middle.append(pill);
157 } else {
158 title.textContent = page.title || "g1t";
159 middle.append(title);
160 }
161 back.disabled = page.canGoBack === false;
162 forward.disabled = page.canGoForward === false;
163 setColors(page.colors);
164 }
165
166 if (events && typeof events.listen === "function") {
167 events
168 .listen("g1t-title-bar", function (event) {
169 draw(event.payload);
170 })
171 .catch(function () {});
172 }
173 invoke("title_bar_current").then(draw, function () {});
174})();