diff --git a/Config/ClipboardConfig.qml b/Config/ClipboardConfig.qml new file mode 100644 index 0000000..5d52ef1 --- /dev/null +++ b/Config/ClipboardConfig.qml @@ -0,0 +1,13 @@ +import Quickshell.Io + +JsonObject { + property bool enabled: true + property int maxEntriesShown: 10 + property Sizes sizes: Sizes { + } + + component Sizes: JsonObject { + property int itemHeight: 60 + property int width: 500 + } +} diff --git a/Config/Config.qml b/Config/Config.qml index 272e6b2..b10be07 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -13,6 +13,7 @@ Singleton { property alias appearance: adapter.appearance property alias background: adapter.background property alias barConfig: adapter.barConfig + property alias clipboard: adapter.clipboard property alias colors: adapter.colors property alias dashboard: adapter.dashboard property alias dock: adapter.dock @@ -116,6 +117,17 @@ Singleton { }; } + function serializeClipboard(): var { + return { + enabled: clipboard.enabled, + maxEntriesShown: clipboard.maxEntriesShown, + sizes: { + width: clipboard.sizes.width, + itemHeight: clipboard.sizes.itemHeight + } + }; + } + function serializeColors(): var { return { schemeType: colors.schemeType, @@ -143,7 +155,8 @@ Singleton { launcher: serializeLauncher(), colors: serializeColors(), dock: serializeDock(), - screenshot: serializeScreenshot() + screenshot: serializeScreenshot(), + clipboard: serializeClipboard() }; } @@ -447,6 +460,8 @@ Singleton { } property BarConfig barConfig: BarConfig { } + property ClipboardConfig clipboard: ClipboardConfig { + } property Colors colors: Colors { } property DashboardConfig dashboard: DashboardConfig { diff --git a/Helpers/ClipHistory.qml b/Helpers/ClipHistory.qml index a847f9e..d409af9 100644 --- a/Helpers/ClipHistory.qml +++ b/Helpers/ClipHistory.qml @@ -17,16 +17,10 @@ Singleton { name: Fuzzy.prepare(`${a.replace(/^\s*\S+\s+/, "")}`), entry: a })) - property string pressPasteCommand: "ydotool key -d 1 29:1 47:1 47:0 29:0" property real scoreThreshold: 0.2 function copy(entry) { - if (root.cliphistBinary.includes("cliphist")) - Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy`]); - else { - const entryNumber = entry.split("\t")[0]; - Quickshell.execDetached(["bash", "-c", `${root.cliphistBinary} decode ${entryNumber} | wl-copy`]); - } + Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy`]); } function deleteEntry(entry) { @@ -50,12 +44,7 @@ Singleton { } function paste(entry) { - if (root.cliphistBinary.includes("cliphist")) - Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy && wl-paste`]); - else { - const entryNumber = entry.split("\t")[0]; - Quickshell.execDetached(["bash", "-c", `${root.cliphistBinary} decode ${entryNumber} | wl-copy; ${root.pressPasteCommand}`]); - } + Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy && wl-paste`]); } function refresh() { diff --git a/Modules/Clipboard/Content.qml b/Modules/Clipboard/Content.qml index f4685eb..5d9dcbe 100644 --- a/Modules/Clipboard/Content.qml +++ b/Modules/Clipboard/Content.qml @@ -10,11 +10,12 @@ import qs.Config Item { id: root + readonly property int itemHeight: Config.clipboard.sizes.itemHeight required property ShellScreen screen required property PersistentProperties visibilities - implicitHeight: screen.height / 2 - implicitWidth: 500 + implicitHeight: search.implicitHeight + entries.anchors.topMargin + ((view.spacing + itemHeight) * Config.clipboard.maxEntriesShown) - view.spacing + implicitWidth: Config.clipboard.sizes.width Component.onCompleted: { if (!ClipHistory.entries.length > 0) @@ -51,6 +52,13 @@ Item { anchors.top: parent.top color: DynamicColors.palette.m3onSurface placeholderText: "Search clipboard history..." + + Keys.onDownPressed: view.incrementCurrentIndex() + Keys.onUpPressed: view.decrementCurrentIndex() + onAccepted: { + ClipHistory.copy(view.currentItem.modelData); + root.visibilities.clipboard = false; + } } } @@ -62,20 +70,28 @@ Item { anchors.right: parent.right anchors.top: search.bottom anchors.topMargin: Appearance.spacing.normal - radius: Appearance.rounding.normal + radius: Appearance.rounding.small - ListView { + CustomListView { id: view anchors.fill: parent + highlightFollowsCurrentItem: false + highlightRangeMode: ListView.ApplyRange + preferredHighlightBegin: 0 + preferredHighlightEnd: height spacing: Appearance.spacing.normal + CustomScrollBar.vertical: CustomScrollBar { + flickable: view + } delegate: RowLayout { id: clipItem required property string modelData - height: 50 + height: root.itemHeight + spacing: Appearance.spacing.small width: view.width CustomClippingRect { @@ -83,8 +99,6 @@ Item { Layout.fillHeight: true Layout.fillWidth: true - // Layout.preferredWidth: implicitWidth + (textLayer.pressed ? 18 : 0) - // implicitWidth: 250 radius: textLayer.pressed ? (Appearance.rounding.small / 2) : Appearance.rounding.small Behavior on Layout.preferredWidth { @@ -98,14 +112,47 @@ Item { } } - CustomText { - id: text + Item { + id: textWrapper - anchors.left: parent.left - anchors.margins: Appearance.padding.normal - anchors.verticalCenter: parent.verticalCenter - elide: Text.ElideRight - text: clipItem.modelData + anchors.fill: parent + layer.enabled: true + + layer.effect: OpacityMask { + maskSource: fadeMask + } + + CustomText { + id: text + + anchors.left: parent.left + anchors.margins: Appearance.padding.normal + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + text: clipItem.modelData + } + } + + CustomRect { + id: fadeMask + + anchors.fill: parent + layer.enabled: true + visible: false + + gradient: Gradient { + orientation: Gradient.Horizontal + + GradientStop { + color: Qt.rgba(1, 1, 1, 1.0) + position: 0.85 + } + + GradientStop { + color: Qt.rgba(1, 1, 1, 0) + position: 1.0 + } + } } StateLayer { @@ -116,17 +163,17 @@ Item { } IconButton { + Layout.bottomMargin: Appearance.padding.normal Layout.fillHeight: true - Layout.margins: Appearance.padding.smallest Layout.preferredWidth: height + Layout.topMargin: Appearance.padding.normal icon: "content_copy" isToggle: false - // implicitWidth: 30 } IconButton { Layout.fillHeight: true - Layout.margins: Appearance.padding.smallest + Layout.margins: Appearance.padding.normal Layout.preferredWidth: height icon: "delete" inactiveColor: Qt.alpha(DynamicColors.palette.m3error, 0.8) @@ -134,15 +181,24 @@ Item { isToggle: false } } - model: ScriptModel { - values: { - const entries = ClipHistory.entries; - const search = searchField.text; - var regex = new RegExp(search, "i"); + highlight: CustomRect { + color: DynamicColors.palette.m3onSurface + implicitHeight: view.currentItem?.height ?? 0 + implicitWidth: view.width + opacity: 0.08 + radius: Appearance.rounding.small + y: view.currentItem?.y ?? 0 - return entries.filter(n => regex.test(n)); + Behavior on y { + Anim { + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } } } + model: ScriptModel { + values: ClipHistory.fuzzyQuery(searchField.text) + } } } }