From f81ed09c3cdcef562fe6c61c2bd35b2185c45f0b Mon Sep 17 00:00:00 2001 From: retrozinndev Date: Fri, 13 Jun 2025 18:52:30 -0300 Subject: [PATCH] :lipstick: runner: better look and feel, internal improvements --- ags/runner/Runner.ts | 115 ++++++++++++++++++++++++++--------------- ags/style/_runner.scss | 30 +++++++---- 2 files changed, 92 insertions(+), 53 deletions(-) diff --git a/ags/runner/Runner.ts b/ags/runner/Runner.ts index f06fdd4..92cad32 100644 --- a/ags/runner/Runner.ts +++ b/ags/runner/Runner.ts @@ -1,4 +1,4 @@ -import { AstalIO, timeout } from "astal"; +import { AstalIO, GObject, timeout } from "astal"; import { Astal, Gdk, Gtk, Widget } from "astal/gtk3"; import { PopupWindow, PopupWindowProps } from "../widget/PopupWindow"; import { updateApps } from "../scripts/apps"; @@ -44,11 +44,11 @@ export function regExMatch(search: string, item: (string|number)): boolean { if(typeof item === "number") return new RegExp(`${search.split('').map(c => - `.*${c}.*`).join('')}`, + `${c}`).join('')}`, "g").test(item.toString()); return new RegExp(`${search.split('').map(c => - `.*${c}.*`).join('')}`, + `${c}`).join('')}`, "gi").test(item); } @@ -82,7 +82,6 @@ export function setEntryText(text: string): void { export function openDefault(initialText?: string) { return Runner.openRunner({ entryPlaceHolder: "Start typing...", - showResultsPlaceHolderOnStartup: false, initialText, resultsLimit: 24 } as Runner.RunnerProps, @@ -132,16 +131,28 @@ export function openDefault(initialText?: string) { ]); } -export function openRunner(props?: RunnerProps, placeholder?: () => Array): Widget.Window { +export function openRunner(props: RunnerProps, placeholder?: () => Array): Widget.Window { let onClickTimeout: (AstalIO.Time|undefined); + const connections: Map = new Map(); + + props.width ??= 780; + props.height ??= 420; gtkEntry = new Widget.Entry({ className: "search", placeholderText: props?.entryPlaceHolder || "", - onChanged: (self) => { + onChanged: async (self) => { updateResultsList(self.text); resultsList.get_row_at_index(0) && resultsList.select_row(resultsList.get_row_at_index(0)); + + if(self.text.trim().length < 1 && !mainBox.get_style_context().has_class("empty-input")) { + mainBox.get_style_context().add_class("empty-input"); + return; + } + + mainBox.get_style_context().has_class("empty-input") && + mainBox.get_style_context().remove_class("empty-input"); }, onActivate: (entry) => { const resultWidget = resultsList.get_selected_row()?.get_child(); @@ -154,12 +165,31 @@ export function openRunner(props?: RunnerProps, placeholder?: () => Array Array { resultsList.insert(resultWidget, -1); - resultsList.connect("row-activated", (_, row: Gtk.ListBoxRow) => { - const rWidget = row.get_child(); - if(rWidget instanceof ResultWidget) { - if(onClickTimeout) return; + const conns: Array = []; - // Timeout, so it doesn't fire the event a hundred times :skull: - onClickTimeout = timeout(500, () => onClickTimeout = undefined); - rWidget.onClick(); - rWidget.closeOnClick && Runner.close(); - } - }); + conns.push( + resultsList.connect("row-activated", (_, row: Gtk.ListBoxRow) => { + const rWidget = row.get_child(); + if(rWidget instanceof ResultWidget) { + if(onClickTimeout) return; + + // Timeout, so it doesn't fire the event a hundred times :skull: + onClickTimeout = timeout(500, () => onClickTimeout = undefined); + rWidget.onClick(); + rWidget.closeOnClick && Runner.close(); + } + }), + resultsList.connect("destroy", () => + conns.forEach((id) => resultsList.disconnect(id)) + ) + ); }); + + widgets.length > 0 ? + (!scrollable.visible && scrollable.show()) + : scrollable.hide(); } if(!instance) instance = Windows.createWindowForFocusedMonitor((mon: number): (Widget.Window) => PopupWindow({ namespace: "runner", monitor: mon, - widthRequest: props?.width ?? 780, - heightRequest: props?.height ?? defaultHeight, - marginTop: (AstalHyprland.get_default().get_monitor(mon)?.height / 2) - 220, + widthRequest: props.width, + heightRequest: props.height, + marginTop: (AstalHyprland.get_default().get_monitor(mon)?.height / 2) - (props.height! / 2), exclusivity: Astal.Exclusivity.IGNORE, halign: Gtk.Align.CENTER, valign: Gtk.Align.START, @@ -256,29 +297,17 @@ export function openRunner(props?: RunnerProps, placeholder?: () => Array { + connections.forEach((id, obj) => GObject.signal_handler_is_connected(obj, id) && + obj.disconnect(id)); + gtkEntry = null; - [...plugins.values()].map(plugin => + + [...plugins.values()].forEach(plugin => plugin && plugin.onClose && plugin.onClose()); + instance = null; }, - child: new Widget.Box({ - className: "runner main", - orientation: Gtk.Orientation.VERTICAL, - hexpand: true, - valign: Gtk.Align.START, - children: [ - gtkEntry, - new Widget.Scrollable({ - className: "results-scrollable", - vscroll: Gtk.PolicyType.AUTOMATIC, - hscroll: Gtk.PolicyType.NEVER, - expand: true, - propagateNaturalHeight: true, - maxContentHeight: props?.height ?? defaultHeight, - child: resultsList - }) - ] - } as Widget.BoxProps) + child: mainBox } as PopupWindowProps))(); return instance!; diff --git a/ags/style/_runner.scss b/ags/style/_runner.scss index 4615b78..1860085 100644 --- a/ags/style/_runner.scss +++ b/ags/style/_runner.scss @@ -1,19 +1,24 @@ @use "./colors"; .runner.main { - background: colors.$bg-translucent; - padding: 10px; - border-radius: 22px; + $radius: 24px; + + background: rgba($color: colors.$bg-primary, $alpha: .8); + border-radius: $radius; + box-shadow: inset 0 0 0 1px colors.$bg-secondary, + 0 0 8px 1px colors.$bg-translucent; + + padding: 4px; & entry { - background: colors.$bg-primary; - padding: 10px 9px; - border-radius: 12px; - margin-bottom: 1px; - min-height: 1.4em; + transition: 80ms ease-in; + min-height: 1.6em; + padding: 14px; + border-radius: inherit; + background: none; &:focus { - box-shadow: inset 0 0 0 2px colors.$bg-secondary; + box-shadow: none; } & image.left { @@ -22,7 +27,8 @@ } & list { - all: unset; + padding: 6px; + padding-top: 0; & > *:selected > .result, & > *:active > .result, @@ -39,6 +45,10 @@ } } + & trough { + margin-bottom: 10px; + } + & list .result { padding: 10px; background: colors.$bg-primary;