From 7bec8c6fedcb18883b279c54ae145fa19db3538b Mon Sep 17 00:00:00 2001 From: Eugene Platonov Date: Sat, 19 Jul 2025 14:41:13 +0300 Subject: [PATCH 1/3] Disable file features on WebAssembly --- README.md | 13 +++++++++++++ src/Components/ExternalDrop.qml | 11 +++++++---- src/Components/Preferences.qml | 1 + src/Components/Sequence/Footer.qml | 6 ++++-- src/FileDialogue.qml | 14 ++++++++++++-- src/FileDialogueStub.qml | 9 +++++++++ src/NotificationSystem.qml | 7 ++++--- src/TrayIconStub.qml | 7 +++++++ src/main.cpp | 6 ++++-- src/main.qml | 9 +++++---- src/qml.qrc | 2 ++ 11 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 src/FileDialogueStub.qml create mode 100644 src/TrayIconStub.qml diff --git a/README.md b/README.md index ff072682..78b144d8 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,19 @@ Building from source: $ make -j $ ./Pilorama +### WebAssembly + +Qt for WebAssembly build requires the Emscripten toolchain and a Qt installation +compiled with WebAssembly support. After installing those dependencies, the app +can be built with: + + $ emcmake cmake ../src -DQT_HOST_PATH=/path/to/qt + $ make -j + +The resulting `Pilorama.html` can be served from any HTTP server. Note that the +WebAssembly build disables the system tray icon and preset file import/export +functionality. + ## Development / Code of Conduct #### Release Process diff --git a/src/Components/ExternalDrop.qml b/src/Components/ExternalDrop.qml index 350612eb..6da50ba2 100644 --- a/src/Components/ExternalDrop.qml +++ b/src/Components/ExternalDrop.qml @@ -45,12 +45,15 @@ Item{ onExited: { externalDrop.validFile = false } - onDropped: if (drop.hasText) { + onDropped: if (Qt.platform.os !== "wasm" && drop.hasText) { if (drop.proposedAction == Qt.MoveAction || drop.proposedAction == Qt.CopyAction) { - masterModel.data = fileDialogue.openFile(drop.text).data - masterModel.title = fileDialogue.openFile(drop.text).title - masterModel.load() + var file = fileDialogue.item ? fileDialogue.item.openFile(drop.text) : null + if (file) { + masterModel.data = file.data + masterModel.title = file.title + masterModel.load() + } drop.acceptProposedAction() externalDrop.validFile = false diff --git a/src/Components/Preferences.qml b/src/Components/Preferences.qml index dc057a0a..225eb559 100644 --- a/src/Components/Preferences.qml +++ b/src/Components/Preferences.qml @@ -54,6 +54,7 @@ Item { PreferenceItem { id: closeOnQuit + visible: Qt.platform.os !== "wasm" cellHeight: preferences.cellHeight fontSize: preferences.fontSize checked: !window.quitOnClose diff --git a/src/Components/Sequence/Footer.qml b/src/Components/Sequence/Footer.qml index 970e6a64..92ffc885 100644 --- a/src/Components/Sequence/Footer.qml +++ b/src/Components/Sequence/Footer.qml @@ -98,21 +98,23 @@ Rectangle { Icon { id: loadButton + visible: Qt.platform.os !== "wasm" glyph: "\uea0b" anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - onReleased: { fileDialogue.openDialogue() } + onReleased: { if (fileDialogue.item) fileDialogue.item.openDialogue() } } Icon { id: saveButton + visible: Qt.platform.os !== "wasm" glyph: "\uea07" anchors.left: loadButton.right anchors.verticalCenter: parent.verticalCenter - onReleased: { fileDialogue.saveDialogue() } + onReleased: { if (fileDialogue.item) fileDialogue.item.saveDialogue() } } } diff --git a/src/FileDialogue.qml b/src/FileDialogue.qml index b5296651..c21738ef 100644 --- a/src/FileDialogue.qml +++ b/src/FileDialogue.qml @@ -11,11 +11,13 @@ Item{ property var title: masterModel.title function openDialogue(){ - openFileDialog.open() + if (Qt.platform.os !== "wasm") + openFileDialog.open() } function saveDialogue(){ - saveFileDialog.open() + if (Qt.platform.os !== "wasm") + saveFileDialog.open() } function getTitle(url){ @@ -24,6 +26,9 @@ Item{ } function openFile(url) { + if (Qt.platform.os === "wasm") + return { title: "", data: "" } + var request = new XMLHttpRequest(); request.open("GET", url, false); request.send(null) @@ -31,6 +36,9 @@ Item{ } function saveFile(url) { + if (Qt.platform.os === "wasm") + return "" + var request = new XMLHttpRequest(); request.open("PUT", url, true); // async false created empty files on macos request.send(masterModel.data); @@ -39,6 +47,7 @@ Item{ FileDialog { id: openFileDialog + visible: Qt.platform.os !== "wasm" nameFilters: ["JSON files (*.json)"] currentFolder: StandardPaths.writableLocation(StandardPaths.DesktopLocation) @@ -51,6 +60,7 @@ Item{ FileDialog { id: saveFileDialog + visible: Qt.platform.os !== "wasm" fileMode: FileDialog.SaveFile nameFilters: ["JSON files (*.json)"] defaultSuffix : 'json' diff --git a/src/FileDialogueStub.qml b/src/FileDialogueStub.qml new file mode 100644 index 00000000..9b0a1f37 --- /dev/null +++ b/src/FileDialogueStub.qml @@ -0,0 +1,9 @@ +import QtQuick + +QtObject { + // Stub for platforms without file system access + function openDialogue() {} + function saveDialogue() {} + function openFile(url) { return { title: "", data: "" } } + function saveFile(url) { return "" } +} diff --git a/src/NotificationSystem.qml b/src/NotificationSystem.qml index 09c1122c..71eef875 100644 --- a/src/NotificationSystem.qml +++ b/src/NotificationSystem.qml @@ -33,12 +33,13 @@ QtObject { function sendWithSound(name) { soundNotification.play(); - tray.send(name) + if (tray.item && tray.item.send) + tray.item.send(name) } function sendFromItem(item) { sendWithSound(masterModel.get(item.id).name) - if (appSettings.showOnSegmentStart) - tray.popUp() + if (appSettings.showOnSegmentStart && tray.item && tray.item.popUp) + tray.item.popUp() } } diff --git a/src/TrayIconStub.qml b/src/TrayIconStub.qml new file mode 100644 index 00000000..4da6bcb3 --- /dev/null +++ b/src/TrayIconStub.qml @@ -0,0 +1,7 @@ +import QtQuick + +QtObject { + // Stub for platforms without system tray support + function send(name) {} + function popUp() {} +} diff --git a/src/main.cpp b/src/main.cpp index 76d3ca88..e04da9b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,8 +45,10 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("MacOSController", &macOSController); - qputenv("QML_XHR_ALLOW_FILE_WRITE", QByteArray("1")); - qputenv("QML_XHR_ALLOW_FILE_READ", QByteArray("1")); +#ifndef Q_OS_WASM + qputenv("QML_XHR_ALLOW_FILE_WRITE", QByteArray("1")); + qputenv("QML_XHR_ALLOW_FILE_READ", QByteArray("1")); +#endif engine.load(url); diff --git a/src/main.qml b/src/main.qml index 411411a2..b680573e 100644 --- a/src/main.qml +++ b/src/main.qml @@ -82,8 +82,7 @@ ApplicationWindow { if (appSettings.showInDock) { MacOSController.hideFromDock() } - } - else { + } else if (Qt.platform.os !== "wasm") { window.visibility = ApplicationWindow.Minimized; } } @@ -183,8 +182,9 @@ ApplicationWindow { durationSettings: durationSettings } - TrayIcon { + Loader { id: tray + source: Qt.platform.os === "wasm" ? "TrayIconStub.qml" : "TrayIcon.qml" } NotificationSystem { @@ -199,8 +199,9 @@ ApplicationWindow { id: clock } - FileDialogue { + Loader { id: fileDialogue + source: Qt.platform.os === "wasm" ? "FileDialogueStub.qml" : "FileDialogue.qml" } QtObject { diff --git a/src/qml.qrc b/src/qml.qrc index 85bbc09c..b4ed10be 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -27,7 +27,9 @@ PiloramaTimer.qml MouseTracker.qml TrayIcon.qml + TrayIconStub.qml FileDialogue.qml + FileDialogueStub.qml MasterModel.qml ModelBurner.qml Components/Colors.qml From 2c89fb05c268f6505a5e5586da2b0063532c5696 Mon Sep 17 00:00:00 2001 From: Eugene Platonov Date: Sat, 19 Jul 2025 14:51:23 +0300 Subject: [PATCH 2/3] Disable preset buttons on WebAssembly --- README.md | 5 +++-- src/Components/Icon.qml | 3 +++ src/Components/Sequence/Footer.qml | 11 +++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 78b144d8..85e42bab 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,9 @@ can be built with: $ make -j The resulting `Pilorama.html` can be served from any HTTP server. Note that the -WebAssembly build disables the system tray icon and preset file import/export -functionality. +WebAssembly build disables the system tray icon. The preset import/export +buttons remain visible but are disabled with tooltips until filesystem access +is implemented. ## Development / Code of Conduct diff --git a/src/Components/Icon.qml b/src/Components/Icon.qml index 3c8f820d..87f49365 100644 --- a/src/Components/Icon.qml +++ b/src/Components/Icon.qml @@ -7,6 +7,7 @@ Text { property int size: 24 property string source property bool propagateComposedEvents: true + property bool enabled: true signal pressed() signal released() @@ -18,12 +19,14 @@ Text { font.pixelSize: size renderType: Text.NativeRendering color: colors.getColor('light') + opacity: enabled ? 1.0 : 0.3 Behavior on color { ColorAnimation { duration: 80 } } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + enabled: icon.enabled propagateComposedEvents: parent.propagateComposedEvents onReleased: parent.released() onPressed: parent.pressed() diff --git a/src/Components/Sequence/Footer.qml b/src/Components/Sequence/Footer.qml index 92ffc885..2f63051c 100644 --- a/src/Components/Sequence/Footer.qml +++ b/src/Components/Sequence/Footer.qml @@ -1,4 +1,5 @@ import QtQuick +import QtQuick.Controls import ".." Rectangle { @@ -98,10 +99,13 @@ Rectangle { Icon { id: loadButton - visible: Qt.platform.os !== "wasm" glyph: "\uea0b" anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter + enabled: Qt.platform.os !== "wasm" + + ToolTip.visible: !loadButton.enabled && loadButton.containsMouse + ToolTip.text: qsTr("Import not supported in WebAssembly build") onReleased: { if (fileDialogue.item) fileDialogue.item.openDialogue() } @@ -109,10 +113,13 @@ Rectangle { Icon { id: saveButton - visible: Qt.platform.os !== "wasm" glyph: "\uea07" anchors.left: loadButton.right anchors.verticalCenter: parent.verticalCenter + enabled: Qt.platform.os !== "wasm" + + ToolTip.visible: !saveButton.enabled && saveButton.containsMouse + ToolTip.text: qsTr("Export not supported in WebAssembly build") onReleased: { if (fileDialogue.item) fileDialogue.item.saveDialogue() } From a22b47be64159c53ba83b81f1486aeb88ef7cb7f Mon Sep 17 00:00:00 2001 From: Eugene Platonov Date: Sat, 19 Jul 2025 15:06:24 +0300 Subject: [PATCH 3/3] Fix tooltip activation --- src/Components/Icon.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Components/Icon.qml b/src/Components/Icon.qml index 87f49365..2e2249fb 100644 --- a/src/Components/Icon.qml +++ b/src/Components/Icon.qml @@ -8,6 +8,7 @@ Text { property string source property bool propagateComposedEvents: true property bool enabled: true + property alias containsMouse: iconMouse.containsMouse signal pressed() signal released() @@ -20,13 +21,16 @@ Text { renderType: Text.NativeRendering color: colors.getColor('light') opacity: enabled ? 1.0 : 0.3 + hoverEnabled: true Behavior on color { ColorAnimation { duration: 80 } } MouseArea { + id: iconMouse anchors.fill: parent cursorShape: Qt.PointingHandCursor enabled: icon.enabled + hoverEnabled: true propagateComposedEvents: parent.propagateComposedEvents onReleased: parent.released() onPressed: parent.pressed()