diff --git a/modules/ags/config/scss/binto-widgets/clipboard.scss b/modules/ags/config/scss/binto-widgets/clipboard.scss index 36ab7007..79da8ea6 100644 --- a/modules/ags/config/scss/binto-widgets/clipboard.scss +++ b/modules/ags/config/scss/binto-widgets/clipboard.scss @@ -46,15 +46,17 @@ all: unset; transition: 200ms; border-radius: 9px; - - box { - padding: 10px; - } + padding: 10px; } *:selected .item, - .item:hover, - .item:focus { + *:hover .item, + *:focus .item { background-color: #363449; } + + .down-arrow { + transition: all 500ms; + opacity: 0.5; + } } diff --git a/modules/ags/config/scss/wim-widgets/clipboard.scss b/modules/ags/config/scss/wim-widgets/clipboard.scss index c316c854..88a905fc 100644 --- a/modules/ags/config/scss/wim-widgets/clipboard.scss +++ b/modules/ags/config/scss/wim-widgets/clipboard.scss @@ -47,15 +47,17 @@ all: unset; transition: 200ms; border-radius: 9px; - - box { - padding: 10px; - } + padding: 10px; } *:selected .item, - .item:hover, - .item:focus { + *:hover .item, + *:focus .item { background-color: #363449; } + + .down-arrow { + transition: all 500ms; + opacity: 0.5; + } } diff --git a/modules/ags/config/services/clipboard.ts b/modules/ags/config/services/clipboard.ts index beadbc55..4936086a 100644 --- a/modules/ags/config/services/clipboard.ts +++ b/modules/ags/config/services/clipboard.ts @@ -32,7 +32,7 @@ class Clipboard extends Service { // Public Class Methods public copyOldItem(key: string | number): void { execAsync([ - 'bash', '-c', `cliphist list | grep ${key} | cliphist decode | wl-copy`, + 'bash', '-c', `cliphist list | grep '^${key}' | cliphist decode | wl-copy`, ]); } @@ -63,9 +63,28 @@ class Clipboard extends Service { } } - private _getHistory(n = Clipboard._MAX_CLIPS) { - this._clips = new Map(); + private _addClip(newClip: [number, { clip: string, isImage: boolean }]) { + if ( + ![...this.clips.values()] + .map((c) => c.clip) + .includes(newClip[1].clip) + ) { + this._clips.set(...newClip); + this.emit('clip-added', newClip); + } + else { + const oldClip = [...this.clips.entries()] + .find(([_, { clip }]) => clip === newClip[1].clip); + if (oldClip && oldClip[0] < newClip[0]) { + this.clips.delete(oldClip[0]); + this._clips.set(...newClip); + this.emit('clip-added', newClip); + } + } + } + + private _getHistory(n = Clipboard._MAX_CLIPS) { // FIXME: this is necessary when not putting a cap on clip amount // exec(`prlimit --pid ${exec('pgrep ags')} --nofile=10024:`); @@ -91,8 +110,7 @@ class Clipboard extends Service { }, ]; - this._clips.set(...newClip); - this.emit('clip-added', newClip); + this._addClip(newClip); } else { const decodedClip = await this._decodeItem(clip); @@ -106,8 +124,7 @@ class Clipboard extends Service { }, ]; - this._clips.set(...newClip); - this.emit('clip-added', newClip); + this._addClip(newClip); } } }); diff --git a/modules/ags/config/ts/clipboard/clip.ts b/modules/ags/config/ts/clipboard/clip.ts new file mode 100644 index 00000000..d3e472c2 --- /dev/null +++ b/modules/ags/config/ts/clipboard/clip.ts @@ -0,0 +1,80 @@ +const { Box, Button, Icon, Label, Revealer } = Widget; + + +const ImageClip = (key: number, val: string) => Box({ + class_name: 'item', + name: key.toString(), + + child: Icon({ + icon: val.replace('img:', ''), + size: 100 * 2, + }), +}); + +const TextClip = (key: number, val: string) => { + const lines = val.split('\n'); + + if (lines.length <= 5) { + return Box({ + class_name: 'item', + name: key.toString(), + + child: Label({ + label: val, + truncate: 'end', + max_width_chars: 100, + }), + }); + } + else { + const revText = Revealer({ + hpack: 'start', + child: Label({ + label: lines.slice(2, lines.length).join('\n'), + truncate: 'end', + max_width_chars: 100, + }), + }); + + return Box({ + class_name: 'item', + name: key.toString(), + + vertical: true, + children: [ + Label({ + label: lines.slice(0, 2).join('\n'), + truncate: 'end', + max_width_chars: 100, + hpack: 'start', + }), + + revText, + + Button({ + child: Icon({ + class_name: 'down-arrow', + icon: 'down-large-symbolic', + size: 24, + }), + + on_primary_click_release: (self) => { + const state = !revText.reveal_child; + + revText.reveal_child = state; + + self.child.setCss(` + -gtk-icon-transform: rotate(${state ? '180' : '0'}deg); + `); + }, + }), + ], + }); + } +}; + +export default ({ + key = 0, + isImage = false, + val = '', +}) => isImage ? ImageClip(key, val) : TextClip(key, val); diff --git a/modules/ags/config/ts/clipboard/main.ts b/modules/ags/config/ts/clipboard/main.ts index b2f8dd52..bdb5d12e 100644 --- a/modules/ags/config/ts/clipboard/main.ts +++ b/modules/ags/config/ts/clipboard/main.ts @@ -1,55 +1,17 @@ -const { Box, Icon, Label } = Widget; - import { Fzf, FzfResultItem } from 'fzf'; import Gtk from 'gi://Gtk?version=3.0'; import Clipboard from '../../services/clipboard.ts'; -import CursorBox from '../misc/cursorbox.ts'; import SortedList from '../misc/sorted-list.ts'; +import ClipWidget from './clip.ts'; + + +const getKey = (r: Gtk.ListBoxRow): number => parseInt(r.get_child()?.name ?? '0'); export default () => { - const makeItem = ( - list: Gtk.ListBox, - key: number, - val: string, - isImage: boolean, - ): void => { - const widget = CursorBox({ - class_name: 'item', - name: key.toString(), - - on_primary_click_release: () => { - Clipboard.copyOldItem(key); - App.closeWindow('win-clipboard'); - }, - - child: Box({ - children: [ - isImage ? - Icon({ - icon: val.replace('img:', ''), - size: 100 * 2, - }) : - - Label({ - label: val, - truncate: 'end', - max_width_chars: 100, - }), - ], - }), - }); - - list.add(widget); - widget.show_all(); - }; - let fzfResults = [] as FzfResultItem<[number, { clip: string, isImage: boolean }]>[]; - const getKey = (r: Gtk.ListBoxRow): number => parseInt(r.get_child()?.name ?? '0'); - - return SortedList({ name: 'clipboard', class_name: 'clipboard', @@ -61,18 +23,23 @@ export default () => { }, setup_list: (list, entry) => { + list.cursor = 'pointer'; + const CONNECT_ID = Clipboard.connect('history-searched', () => { // Do every clip that existed before this widget - list.get_children().forEach((row) => { - row.destroy(); - }); Clipboard.clips.forEach((clip, key) => { - makeItem(list, key, clip.clip, clip.isImage); + const widget = ClipWidget({ key, isImage: clip.isImage, val: clip.clip }); + + list.add(widget); + widget.show_all(); }); // Setup connection for new clips Clipboard.connect('clip-added', (_, [key, clip]) => { - makeItem(list, key, clip.clip, clip.isImage); + const widget = ClipWidget({ key, isImage: clip.isImage, val: clip.clip }); + + list.add(widget); + widget.show_all(); }); list.set_sort_func((a, b) => { @@ -93,6 +60,9 @@ export default () => { } }); + // Trigger on_text_change after init + entry.text = '-'; + Clipboard.disconnect(CONNECT_ID); }); },