From 17b0230ea1fb6cb36b75ef4544fd09222d689e81 Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Sun, 17 Aug 2025 15:36:54 +0800 Subject: feat(Clip): preview --- launcher/components/Clip.qml | 149 +++++++++++++++++++++++++++---------------- services/Clip.qml | 46 +++++++++++-- 2 files changed, 135 insertions(+), 60 deletions(-) diff --git a/launcher/components/Clip.qml b/launcher/components/Clip.qml index 3217646..23e48a9 100644 --- a/launcher/components/Clip.qml +++ b/launcher/components/Clip.qml @@ -73,70 +73,111 @@ IComponent { width: parent.width height: Math.min(listView.contentHeight + titleBar.height, Launcher.widgetHeight * 1.5) - ListView { - id: listView + RowLayout { Layout.fillWidth: true Layout.fillHeight: true - clip: true - model: clipHist - spacing: 0 - - delegate: Item { - required property var modelData - required property int index - width: item.implicitWidth + Launcher.innerMargin * 4 - height: item.height + Launcher.innerMargin * 4 - - Row { - id: item - anchors.left: parent.left - anchors.top: parent.top - anchors.margins: Launcher.innerMargin * 2 - spacing: Launcher.innerMargin * 2 - - IconImage { - source: modelData?.appIcon - implicitSize: parent.height - } - - Column { - Loader { - sourceComponent: modelData?.type === "image" ? img : text - readonly property Component text: IText { - text: modelData?.data || "" - font.pixelSize: Style.font.size.largerr - } - readonly property Component img: Image { - source: modelData?.data || "" - } + ListView { + id: listView + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: clipHist + spacing: 0 + + delegate: Item { + required property var modelData + required property int index + width: item.implicitWidth + Launcher.innerMargin * 4 + height: item.height + Launcher.innerMargin * 4 + + Row { + id: item + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: Launcher.innerMargin * 2 + spacing: Launcher.innerMargin * 2 + + IconImage { + source: modelData?.appIcon + implicitSize: parent.height } - Row { - spacing: Launcher.innerMargin - IText { - text: modelData.type - color: Colors.foreground3 - font.pixelSize: Style.font.size.normal + + Column { + Loader { + sourceComponent: modelData?.type === "image" ? img : text + readonly property Component text: IText { + text: modelData?.data || "" + font.pixelSize: Style.font.size.largerr + } + readonly property Component img: Image { + source: modelData?.data || "" + } } - IText { - text: TimeDate.sinceWhen(modelData?.timestamp) || "" - color: Colors.foreground3 - font.pixelSize: Style.font.size.normal + Row { + spacing: Launcher.innerMargin + property string sinceWhen: TimeDate.sinceWhen(modelData?.timestamp) || "" + IText { + text: modelData.type + color: Colors.foreground3 + font.pixelSize: Style.font.size.normal + } + IText { + visible: parent.sinceWhen + text: 'ยท' + color: Colors.foreground3 + font.pixelSize: Style.font.size.normal + } + IText { + text: parent.sinceWhen || "" + color: Colors.foreground3 + font.pixelSize: Style.font.size.normal + } } } } - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onPositionChanged: { - root.selectedIndex = index; + MouseArea { + anchors.fill: parent + hoverEnabled: true + onPositionChanged: { + root.selectedIndex = index; + } + onExited: { + root.selectedIndex = 0; + } + onPressed: { + root.exec(); + } } - onExited: { - root.selectedIndex = 0; + } + } + Flickable { + id: preview + clip: true + Layout.fillWidth: true + Layout.fillHeight: true + + contentWidth: loader.item.width + contentHeight: loader.item.height + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + + Loader { + id: loader + sourceComponent: clipHist[selectedIndex]?.type === "image" ? img : text + property Component text: TextEdit { + id: textEdit + width: preview.width + color: Colors.foreground1 + selectionColor: Colors.background3 + text: clipHist[selectedIndex]?.decoded } - onPressed: { - root.exec(); + property Component img: Image { + id: image + width: preview.width + height: preview.height + source: clipHist[selectedIndex]?.image || "" + fillMode: Image.PreserveAspectFit } } } diff --git a/services/Clip.qml b/services/Clip.qml index 4b289a0..734e2f2 100644 --- a/services/Clip.qml +++ b/services/Clip.qml @@ -18,8 +18,8 @@ Searchable { property list _clipHist: [] property list _clipImg: [] property list _clipImgIds: _clipImg.map(p => p.split("/").pop().split(".").shift()) - property var _clipMetadata: ({}) + property var _clipDecoded: ({}) function getType(text: string): string { if (!text) @@ -42,7 +42,7 @@ Searchable { return map; }, {}); - return _clipHist.filter(raw => raw).map(raw => { + return _clipHist.filter(raw => raw).map((raw, idx) => { const tab = raw.indexOf("\t"); let idNum = -1; let payload = raw; @@ -89,9 +89,6 @@ Searchable { } root._clipMetadata = meta; } - // fileView.onAdapterUpdated: { - // root._clipMetadata = adapter.metadata; - // } } on_ClipMetadataChanged: { @@ -112,6 +109,7 @@ Searchable { function _update() { clipProc.running = true; + clipDecodeProc.running = true; imgProc.running = true; } @@ -130,7 +128,7 @@ Searchable { const idStr = firstLine.slice(0, tab); const n = parseInt(idStr, 10); if (!Number.isNaN(n)) { - var newMetadata = {}; + let newMetadata = {}; for (var k in _clipMetadata) { if (_clipMetadata.hasOwnProperty(k)) newMetadata[k] = _clipMetadata[k]; @@ -172,6 +170,42 @@ Searchable { } } + Process { + id: clipDecodeProc + property var tmp: ({}) + property int index: 0 + running: true + command: ["sh", "-c", 'cliphist list | while IFS= read -r line; do\n' + ' cliphist decode "$line"; ' + 'printf "THIS IS A SEPERATOR 123 -----";\n' + 'done'] + onStarted: { + clipDecodeProc.tmp = []; + clipDecodeProc.index = 0; + } + onExited: { + if (clipDecodeProc.tmp) + root._clipDecoded = clipDecodeProc.tmp; + } + stdout: SplitParser { + onRead: data => { + data = data += '\n'; + if (data.includes("THIS IS A SEPERATOR 123 -----")) { + const split = data.split("THIS IS A SEPERATOR 123 -----"); + const first = split[0] || ""; + const second = split[1] || ""; + if (first) + clipDecodeProc.tmp[clipDecodeProc.index] += first; + clipDecodeProc.index++; + if (second) + clipDecodeProc.tmp[clipDecodeProc.index] = second; + return; + } + if (!clipDecodeProc.tmp[clipDecodeProc.index]) + clipDecodeProc.tmp[clipDecodeProc.index] = data; + else + clipDecodeProc.tmp[clipDecodeProc.index] += data; + } + } + } + Process { id: imgProc running: true -- cgit v1.2.3