Skip to content

Commit f00a49e

Browse files
committed
feat: side panel widget #16
1 parent bbe20fe commit f00a49e

File tree

3 files changed

+322
-63
lines changed

3 files changed

+322
-63
lines changed

index.js

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ const promptArgs = async (proc) => {
9393
question: "Enter WEB zoom level",
9494
fallback: "1.25",
9595
},
96+
{
97+
key: "web_widget",
98+
question: "Enter WEB widget enabled",
99+
fallback: "true",
100+
},
96101
{
97102
key: "mqtt",
98103
question: "\nConnect to MQTT Broker?",

src/webview.js

+192-63
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const path = require("path");
22
const hardware = require("./hardware");
33
const integration = require("./integration");
4-
const { app, screen, nativeTheme, BaseWindow, WebContentsView } = require("electron");
4+
const { app, screen, nativeTheme, ipcMain, BaseWindow, WebContentsView } = require("electron");
55

66
global.WEBVIEW = global.WEBVIEW || {
77
initialized: false,
@@ -24,28 +24,58 @@ const init = async (args) => {
2424
console.error("Please provide the '--web-url' parameter");
2525
return app.quit();
2626
}
27+
if (!/^https?:\/\//.test(args.web_url)) {
28+
console.error("Please provide the '--web-url' parameter with http(s)");
29+
return app.quit();
30+
}
2731
const url = new URL(args.web_url);
28-
const theme = args.web_theme ? args.web_theme : "dark";
29-
const zoom = args.web_zoom ? parseFloat(args.web_zoom) : 1.25;
32+
const theme = ["light", "dark"].includes(args.web_theme) ? args.web_theme : "dark";
33+
const zoom = !isNaN(parseFloat(args.web_zoom)) ? parseFloat(args.web_zoom) : 1.25;
34+
const widget = args.web_widget ? args.web_widget === "true" : true;
35+
36+
// Init global layout
37+
WEBVIEW.viewZoom = zoom;
38+
WEBVIEW.viewTheme = theme;
39+
WEBVIEW.widgetTheme = theme;
40+
WEBVIEW.widgetEnabled = widget;
41+
nativeTheme.themeSource = WEBVIEW.viewTheme;
3042

3143
// Init global root window
32-
nativeTheme.themeSource = theme;
3344
WEBVIEW.window = new BaseWindow({
3445
title: `TouchKio - ${url.host}`,
3546
icon: path.join(__dirname, "..", "img", "icon.png"),
3647
});
3748
WEBVIEW.window.setMenuBarVisibility(false);
3849
WEBVIEW.window.setFullScreen(true);
3950

40-
// Init global main webview
41-
WEBVIEW.view = new WebContentsView();
51+
// Init global webview
52+
WEBVIEW.view = new WebContentsView({
53+
webPreferences: {
54+
nodeIntegration: false,
55+
contextIsolation: true,
56+
},
57+
});
4258
WEBVIEW.window.contentView.addChildView(WEBVIEW.view);
4359
WEBVIEW.view.webContents.loadURL(url.toString());
60+
WEBVIEW.view.setBackgroundColor("#FFFFFFFF");
61+
62+
// Init global widget
63+
WEBVIEW.widget = new WebContentsView({
64+
transparent: true,
65+
webPreferences: {
66+
nodeIntegration: true,
67+
contextIsolation: false,
68+
},
69+
});
70+
WEBVIEW.window.contentView.addChildView(WEBVIEW.widget);
71+
WEBVIEW.widget.webContents.loadFile(path.join(__dirname, "widget.html"));
72+
WEBVIEW.widget.setBackgroundColor("#00000000");
4473

4574
// Register events
46-
windowEvents(WEBVIEW.window, zoom);
47-
touchEvents(WEBVIEW.window);
48-
viewEvents(WEBVIEW.view);
75+
windowEvents();
76+
widgetEvents();
77+
domEvents();
78+
appEvents();
4979

5080
return true;
5181
};
@@ -69,74 +99,74 @@ const update = () => {
6999
WEBVIEW.status = "Framed";
70100
}
71101

102+
// Update widget status
103+
if (!HARDWARE.support.keyboardVisibility) {
104+
WEBVIEW.widget.webContents.send("button-hide", { id: "keyboard" });
105+
}
106+
72107
// Update integration sensor
73108
console.log("Update Kiosk Status:", WEBVIEW.status);
74109
integration.update();
75110
};
76111

77112
/**
78113
* Register window events and handler.
79-
*
80-
* @param {Object} window - The root window object.
81-
* @param {number} zoom - Zoom level used for the webview.
82114
*/
83-
const windowEvents = (window, zoom) => {
84-
// Handle window resize for webview bounds
115+
const windowEvents = () => {
116+
// Handle window resize events
85117
const resize = () => {
86-
const { width, height } = window.getBounds();
87-
WEBVIEW.view.setBounds({ x: 0, y: 0, width: width, height: height });
88-
WEBVIEW.view.webContents.setZoomFactor(zoom);
89-
};
90-
window.on("ready-to-show", resize);
91-
window.on("resize", resize);
92-
resize();
118+
const window = WEBVIEW.window.getBounds();
119+
const widget = { width: 60, height: 200 };
93120

94-
// Handle window status updates
95-
window.on("minimize", update);
96-
window.on("restore", update);
97-
window.on("maximize", update);
98-
window.on("unmaximize", update);
99-
window.on("enter-full-screen", update);
100-
window.on("leave-full-screen", update);
121+
// Update view size
122+
WEBVIEW.view.setBounds({
123+
x: 0,
124+
y: 0,
125+
width: window.width,
126+
height: window.height,
127+
});
128+
WEBVIEW.view.webContents.setZoomFactor(WEBVIEW.viewZoom);
129+
WEBVIEW.view.webContents.focus();
101130

102-
// Handle signal and exit events
103-
process.on("SIGINT", app.quit);
104-
app.on("before-quit", () => {
105-
WEBVIEW.status = "Terminated";
106-
integration.update();
107-
});
108-
109-
// Handle multiple instances
110-
app.on("second-instance", () => {
111-
if (window.isMinimized()) {
112-
window.restore();
131+
// Update widget size
132+
if (WEBVIEW.widgetEnabled) {
133+
WEBVIEW.widget.setBounds({
134+
x: window.width - 15,
135+
y: parseInt(window.height / 2 - widget.height / 2),
136+
width: widget.width,
137+
height: widget.height,
138+
});
139+
WEBVIEW.widget.webContents.send("data-theme", { theme: "hidden" });
113140
}
114-
window.focus();
115-
});
116-
};
141+
};
142+
WEBVIEW.window.on("ready-to-show", resize);
143+
WEBVIEW.window.on("resize", resize);
144+
resize();
117145

118-
/**
119-
* Register touch events and handler.
120-
*
121-
* @param {Object} window - The root window object.
122-
*/
123-
const touchEvents = (window) => {
124-
// Handle focus change for webview lock
125-
window.on("focus", () => {
146+
// Handle window focus events
147+
WEBVIEW.window.on("focus", () => {
126148
if (WEBVIEW.locked) {
127149
hardware.setDisplayStatus("ON");
128-
window.blur();
150+
WEBVIEW.window.blur();
129151
}
130152
WEBVIEW.locked = false;
131153
});
132154
HARDWARE.display.notifiers.push(() => {
133155
WEBVIEW.locked = hardware.getDisplayStatus() === "OFF";
134156
if (WEBVIEW.locked) {
135-
window.blur();
157+
WEBVIEW.window.blur();
136158
}
137159
});
138160

139-
// Handle touch events for activity tracking
161+
// Handle window status updates
162+
WEBVIEW.window.on("minimize", update);
163+
WEBVIEW.window.on("restore", update);
164+
WEBVIEW.window.on("maximize", update);
165+
WEBVIEW.window.on("unmaximize", update);
166+
WEBVIEW.window.on("enter-full-screen", update);
167+
WEBVIEW.window.on("leave-full-screen", update);
168+
169+
// Handle window touch events for activity tracking
140170
setInterval(() => {
141171
const posOld = WEBVIEW.pointer.position;
142172
const posNew = screen.getCursorScreenPoint();
@@ -156,35 +186,134 @@ const touchEvents = (window) => {
156186
};
157187

158188
/**
159-
* Register webview events and handler.
160-
*
161-
* @param {Object} view - The main webview object.
189+
* Register widget events and handler.
162190
*/
163-
const viewEvents = (view) => {
191+
const widgetEvents = () => {
192+
if (!WEBVIEW.widgetEnabled) {
193+
return;
194+
}
195+
196+
// Handle widget focus events
197+
WEBVIEW.widget.webContents.on("focus", () => {
198+
const window = WEBVIEW.window.getBounds();
199+
const widget = WEBVIEW.widget.getBounds();
200+
201+
// Show widget
202+
WEBVIEW.widget.setBounds({
203+
x: window.width - 60,
204+
y: widget.y,
205+
width: widget.width,
206+
height: widget.height,
207+
});
208+
WEBVIEW.widget.webContents.send("data-theme", { theme: WEBVIEW.widgetTheme });
209+
});
210+
211+
// Handle widget blur events
212+
WEBVIEW.widget.webContents.on("blur", () => {
213+
const window = WEBVIEW.window.getBounds();
214+
const widget = WEBVIEW.widget.getBounds();
215+
216+
// Hide widget
217+
WEBVIEW.widget.setBounds({
218+
x: window.width - 15,
219+
y: widget.y,
220+
width: widget.width,
221+
height: widget.height,
222+
});
223+
WEBVIEW.widget.webContents.send("data-theme", { theme: "hidden" });
224+
});
225+
226+
// Handle widget button click events
227+
ipcMain.on("button-click", (e, button) => {
228+
switch (button.id) {
229+
case "refresh":
230+
WEBVIEW.view.webContents.reloadIgnoringCache();
231+
break;
232+
case "fullscreen":
233+
if (WEBVIEW.window.isFullScreen()) {
234+
WEBVIEW.window.restore();
235+
WEBVIEW.window.unmaximize();
236+
WEBVIEW.window.setFullScreen(false);
237+
} else {
238+
WEBVIEW.window.restore();
239+
WEBVIEW.window.unmaximize();
240+
WEBVIEW.window.setFullScreen(true);
241+
}
242+
break;
243+
case "minimize":
244+
WEBVIEW.window.restore();
245+
WEBVIEW.window.setFullScreen(false);
246+
WEBVIEW.window.minimize();
247+
break;
248+
case "keyboard":
249+
const toggle = hardware.getKeyboardVisibility() === "ON" ? "OFF" : "ON";
250+
hardware.setKeyboardVisibility(toggle);
251+
switch (toggle) {
252+
case "OFF":
253+
WEBVIEW.window.restore();
254+
WEBVIEW.window.unmaximize();
255+
WEBVIEW.window.setFullScreen(true);
256+
break;
257+
case "ON":
258+
WEBVIEW.window.restore();
259+
WEBVIEW.window.setFullScreen(false);
260+
WEBVIEW.window.maximize();
261+
break;
262+
}
263+
break;
264+
}
265+
});
266+
};
267+
268+
/**
269+
* Register dom events and handler.
270+
*/
271+
const domEvents = () => {
164272
// Enable webview touch emulation
165-
view.webContents.debugger.attach("1.1");
166-
view.webContents.debugger.sendCommand("Emulation.setEmitTouchEventsForMouse", {
273+
WEBVIEW.view.webContents.debugger.attach("1.1");
274+
WEBVIEW.view.webContents.debugger.sendCommand("Emulation.setEmitTouchEventsForMouse", {
167275
configuration: "mobile",
168276
enabled: true,
169277
});
170278

171279
// Disable webview hyperlinks
172-
view.webContents.setWindowOpenHandler(() => {
280+
WEBVIEW.view.webContents.setWindowOpenHandler(() => {
173281
return { action: "deny" };
174282
});
175283

176284
// Remove webview scrollbar
177-
view.webContents.on("dom-ready", () => {
178-
view.webContents.insertCSS("::-webkit-scrollbar { display: none; }");
285+
WEBVIEW.view.webContents.on("dom-ready", () => {
286+
WEBVIEW.view.webContents.insertCSS("::-webkit-scrollbar { display: none; }");
179287
});
180288

181289
// Webview fully loaded
182-
view.webContents.on("did-finish-load", () => {
290+
WEBVIEW.view.webContents.on("did-finish-load", () => {
291+
// view.webContents.openDevTools();
183292
WEBVIEW.initialized = true;
184293
update();
185294
});
186295
};
187296

297+
/**
298+
* Register app events and handler.
299+
*/
300+
const appEvents = () => {
301+
// Handle signal and exit events
302+
process.on("SIGINT", app.quit);
303+
app.on("before-quit", () => {
304+
WEBVIEW.status = "Terminated";
305+
integration.update();
306+
});
307+
308+
// Handle multiple instances
309+
app.on("second-instance", () => {
310+
if (WEBVIEW.window.isMinimized()) {
311+
WEBVIEW.window.restore();
312+
}
313+
WEBVIEW.window.focus();
314+
});
315+
};
316+
188317
module.exports = {
189318
init,
190319
update,

0 commit comments

Comments
 (0)