From d65ff05842272afcaee3ba51b23964bb7cef2260 Mon Sep 17 00:00:00 2001 From: Chandra Kant Date: Tue, 27 Oct 2026 18:17:22 +0530 Subject: [PATCH 1/5] Add customTint property to IconButton --- ryoku/shell/quickshell/ryoshot/IconButton.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ryoku/shell/quickshell/ryoshot/IconButton.qml b/ryoku/shell/quickshell/ryoshot/IconButton.qml index 247b16a413..22d448aed7 100644 --- a/ryoku/shell/quickshell/ryoshot/IconButton.qml +++ b/ryoku/shell/quickshell/ryoshot/IconButton.qml @@ -8,6 +8,7 @@ Rectangle { property bool dim: false property string tooltip: "" property real iconSize: 18 + property color customTint: "transparent" signal clicked() @@ -22,7 +23,7 @@ Rectangle { anchors.centerIn: parent name: btn.icon size: btn.iconSize - tint: btn.active ? Theme.accentInk : (btn.dim ? Theme.inkFaint : Theme.inkDim) + tint: btn.customTint.a > 0 ? btn.customTint : (btn.active ? Theme.accentInk : (btn.dim ? Theme.inkFaint : Theme.inkDim)) } MouseArea { From 78c884edc98be2626b6036372228abdf5279c483 Mon Sep 17 00:00:00 2001 From: Chandra Kant Date: Tue, 27 Oct 2026 18:18:08 +0530 Subject: [PATCH 2/5] Add phone connection properties and signal --- ryoku/shell/quickshell/ryoshot/Toolbar.qml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ryoku/shell/quickshell/ryoshot/Toolbar.qml b/ryoku/shell/quickshell/ryoshot/Toolbar.qml index 57d418e27a..da384ba5d9 100644 --- a/ryoku/shell/quickshell/ryoshot/Toolbar.qml +++ b/ryoku/shell/quickshell/ryoshot/Toolbar.qml @@ -21,6 +21,8 @@ Item { property bool canUndo: false property bool canRedo: false property bool settingsOpen: false + property bool phoneConnected: false + property string phoneDeviceName: "" readonly property real colorCenterX: colorBtn.x + row.x + colorBtn.width / 2 readonly property real widthCenterX: widthBtn.x + row.x + widthBtn.width / 2 @@ -36,6 +38,7 @@ Item { signal copyRequested() signal saveRequested() signal uploadRequested() + signal phoneRequested() signal pinRequested() signal helpRequested() signal settingsRequested() @@ -204,6 +207,14 @@ Item { IconButton { icon: "save"; tooltip: I18n.tr("Save") + " (ctrl+s)"; onClicked: tb.saveRequested() } IconButton { icon: "pin"; tooltip: I18n.tr("Pin to desktop") + " (ctrl+p)"; onClicked: tb.pinRequested() } IconButton { icon: "upload"; tooltip: I18n.tr("Upload") + " (ctrl+u)"; onClicked: tb.uploadRequested() } + IconButton { + icon: "phone" + customTint: tb.phoneConnected ? "transparent" : Theme.danger + tooltip: (tb.phoneConnected + ? (tb.phoneDeviceName.length > 0 ? I18n.tr("Send to %1").arg(tb.phoneDeviceName) : I18n.tr("Send to phone")) + : I18n.tr("Send to phone (no device connected)")) + " (ctrl+k)" + onClicked: tb.phoneRequested() + } Rectangle { Layout.preferredWidth: 1; Layout.preferredHeight: 20; color: Theme.hair; Layout.leftMargin: 3; Layout.rightMargin: 3 } From 987a79b5b185e4f7117e8c92dd3b279bed78c367 Mon Sep 17 00:00:00 2001 From: Chandra Kant Date: Tue, 27 Oct 2026 18:18:51 +0530 Subject: [PATCH 3/5] Add shortcut for 'Send to phone' in ShortcutSheet.qml --- ryoku/shell/quickshell/ryoshot/ShortcutSheet.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ryoku/shell/quickshell/ryoshot/ShortcutSheet.qml b/ryoku/shell/quickshell/ryoshot/ShortcutSheet.qml index 68ae21a516..a8d3db4c4c 100644 --- a/ryoku/shell/quickshell/ryoshot/ShortcutSheet.qml +++ b/ryoku/shell/quickshell/ryoshot/ShortcutSheet.qml @@ -15,6 +15,7 @@ Item { { key: "Ctrl+S", label: I18n.tr("Save") }, { key: "Ctrl+P", label: I18n.tr("Pin") }, { key: "Ctrl+U", label: I18n.tr("Upload") }, + { key: "Ctrl+K", label: I18n.tr("Send to phone") }, { key: "Ctrl+B", label: I18n.tr("Beautify") }, { key: "Enter", label: I18n.tr("Copy and save") }, { key: "Ctrl+Z", label: I18n.tr("Undo") }, @@ -140,3 +141,4 @@ Item { } } } + { key: "Ctrl+C", label: I18n.tr("Copy") }, From 7cb19a3ab1d5231eb96de3946b0bbeda959fb015 Mon Sep 17 00:00:00 2001 From: Chandra Kant Date: Tue, 27 Oct 2026 18:19:29 +0530 Subject: [PATCH 4/5] Add phone connection handling and send function --- ryoku/shell/quickshell/ryoshot/shell.qml | 79 ++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/ryoku/shell/quickshell/ryoshot/shell.qml b/ryoku/shell/quickshell/ryoshot/shell.qml index 21e635b81a..c0205891f3 100644 --- a/ryoku/shell/quickshell/ryoshot/shell.qml +++ b/ryoku/shell/quickshell/ryoshot/shell.qml @@ -38,6 +38,13 @@ ShellRoot { property bool textEditing: false property string openPopover: "" property bool eyedropArmed: false + property bool phoneConnected: false + property string phoneDeviceName: "" + + onPhaseChanged: { + if (phase === "editing" && !kdeStateProc.running) + kdeStateProc.running = true; + } property var selectedIndex: null property var moveOffset: null @@ -1034,6 +1041,14 @@ ShellRoot { }); } + function doSendPhone() { + var tmp = "/tmp/ryoshot-phone-" + Date.now() + ".png"; + grabTo(tmp, function (ok) { + if (ok) { root.shutter(); kdePhoneProc.run(tmp); } + else Qt.quit(); + }); + } + /** * Pins the finished shot: the PNG lands in the pin directory, the poke file * wakes an already running host, and the flock spawn starts one if there is @@ -1261,6 +1276,66 @@ ShellRoot { } } + Process { + id: kdePhoneProc + stdout: StdioCollector { id: kdePhoneOut } + function run(file) { + command = ["sh", "-c", + "dev=$(kdeconnect-cli -a --id-only 2>/dev/null | head -n 1)\n" + + "name=$(kdeconnect-cli -a --name-only 2>/dev/null | head -n 1)\n" + + "if [ -z \"$dev\" ]; then\n" + + " rm -f -- \"$1\"\n" + + " exit 2\n" + + "fi\n" + + "if kdeconnect-cli -d \"$dev\" --share \"$1\" >/dev/null 2>&1; then\n" + + " echo \"$name\"\n" + + " (sleep 15; rm -f -- \"$1\") &\n" + + " exit 0\n" + + "else\n" + + " (sleep 5; rm -f -- \"$1\") &\n" + + " exit 1\n" + + "fi", + "sh", file]; + running = true; + } + onExited: (code) => { + var devName = kdePhoneOut.text.trim(); + console.log("ryoshot: kdeconnect exit " + code + " target=" + JSON.stringify(devName)); + if (code === 0) { + root.phoneConnected = true; + if (devName.length > 0) root.phoneDeviceName = devName; + var msg = devName.length > 0 ? I18n.tr("Sent to %1").arg(devName) : I18n.tr("Sent to phone"); + root.notifySend(I18n.tr("Transferred via KDE Connect"), msg); + root.quitSoon(); + } else if (code === 2) { + root.phoneConnected = false; + root.phoneDeviceName = ""; + root.notifySend(I18n.tr("KDE Connect"), I18n.tr("No reachable device found")); + Qt.quit(); + } else { + root.notifySend(I18n.tr("KDE Connect"), I18n.tr("Transfer failed")); + Qt.quit(); + } + } + } + + Process { + id: kdeStateProc + stdout: StdioCollector { id: kdeStateOut } + command: ["sh", "-c", "kdeconnect-cli -a --name-only 2>/dev/null | head -n 1"] + Component.onCompleted: running = true + onExited: (code) => { + if (code === 0) { + var name = kdeStateOut.text.trim(); + root.phoneConnected = name.length > 0; + root.phoneDeviceName = name; + } else { + root.phoneConnected = false; + root.phoneDeviceName = ""; + } + } + } + // Upload the crop, then open a reverse-image search on the hosted URL. Bing is // the default engine iRiS settled on (Google and Yandex paste endpoints broke). Process { @@ -1345,6 +1420,7 @@ ShellRoot { if (e.key === Qt.Key_S) { root.exportSaveOrStyled(); return true; } if (e.key === Qt.Key_P) { root.doPin(); return true; } if (e.key === Qt.Key_U) { root.doUpload(); return true; } + if (e.key === Qt.Key_K) { root.doSendPhone(); return true; } if (e.key === Qt.Key_B) { root.openBeautify(); return true; } if (e.key === Qt.Key_Z) { if (shift) root.redo(); else root.undo(); return true; } if (e.key === Qt.Key_Y) { root.redo(); return true; } @@ -1523,6 +1599,8 @@ ShellRoot { canUndo: { root.annRevision; return root.model ? root.model.canUndo() : false; } canRedo: { root.annRevision; return root.model ? root.model.canRedo() : false; } settingsOpen: root.settingsOpen + phoneConnected: root.phoneConnected + phoneDeviceName: root.phoneDeviceName x: { if (!win.selLocal) return 0; @@ -1546,6 +1624,7 @@ ShellRoot { onCopyRequested: root.exportCopyOrStyled() onSaveRequested: root.exportSaveOrStyled() onUploadRequested: root.doUpload() + onPhoneRequested: root.doSendPhone() onPinRequested: root.doPin() onHelpRequested: root.shortcutsOpen = !root.shortcutsOpen onSettingsRequested: root.settingsOpen = !root.settingsOpen From 301dfd96637692d3abcdddc5fea0d8950f6bb779 Mon Sep 17 00:00:00 2001 From: Chandra Kant Date: Tue, 27 Oct 2026 18:20:10 +0530 Subject: [PATCH 5/5] Add rectangle SVG path to Icon.qml --- ryoku/shell/quickshell/ryoshot/Icon.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ryoku/shell/quickshell/ryoshot/Icon.qml b/ryoku/shell/quickshell/ryoshot/Icon.qml index bf0c990db2..a09574ef97 100644 --- a/ryoku/shell/quickshell/ryoshot/Icon.qml +++ b/ryoku/shell/quickshell/ryoshot/Icon.qml @@ -47,7 +47,8 @@ Item { "mic": "M9 5a3 3 0 0 1 6 0v7a3 3 0 0 1 -6 0z M6 12a6 6 0 0 0 12 0 M12 18v3 M9 21h6", "keyboard": "M3 6h18v12H3z M6 9h1 M10 9h1 M14 9h1 M18 9h1 M6 12h1 M10 12h1 M14 12h1 M18 12h1 M7 15h10", "webcam": "M5 7h14v10H5z M15 12a3 3 0 1 1 -6 0a3 3 0 0 1 6 0z M12 17v3 M9 20h6", - "folder": "M3 6h7l2 2h9v11H3z" + "folder": "M3 6h7l2 2h9v11H3z", + "phone": "M6 2h12a2 2 0 0 1 2 2v16a2 2 0 0 1 -2 2H6a2 2 0 0 1 -2 -2V4a2 2 0 0 1 2 -2z M11 5h2 M12 18h.01" }) readonly property string d: defs[name] !== undefined ? defs[name] : "" @@ -70,3 +71,4 @@ Item { } } } + "rect": "M5 6h14a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1H5a1 1 0 0 1 -1 -1V7a1 1 0 0 1 1 -1z",