diff --git a/ags/style.scss b/ags/style.scss index 4dd37a3..6c66762 100644 --- a/ags/style.scss +++ b/ags/style.scss @@ -153,7 +153,7 @@ scrollbar trough { background: colors.$bg-translucent; border-top-left-radius: 8px; border-bottom-left-radius: 8px; - padding: 0 2px; + padding: 2px; & slider { @include mixins.reset-props; diff --git a/ags/style/_runner.scss b/ags/style/_runner.scss index 04c0849..8de0e54 100644 --- a/ags/style/_runner.scss +++ b/ags/style/_runner.scss @@ -32,15 +32,14 @@ } & list { - & eventbox:focus > box, - & eventbox:hover > box, - & listboxchild:selected eventbox > box, - & listboxchild:active eventbox > box { + & > *:selected > .result, + & > *:active > .result, + & > *:hover > .result { background: colors.$bg-secondary; } } - & list eventbox > .result { + & list .result { padding: 10px; background: colors.$bg-primary; margin: 2px 0; diff --git a/ags/widget/runner/ResultWidget.ts b/ags/widget/runner/ResultWidget.ts index 5a98edb..d2d1962 100644 --- a/ags/widget/runner/ResultWidget.ts +++ b/ags/widget/runner/ResultWidget.ts @@ -14,9 +14,8 @@ type ResultWidgetProps = { }; @register({ GTypeName: "ResultWidget" }) -class ResultWidget extends Widget.EventBox { - private readonly connections: Array; - public readonly onClick: ((() => void)|undefined); +class ResultWidget extends Widget.Box { + public readonly onClick: (() => void); public readonly icon: (string|undefined); public readonly setup: ((() => void)|undefined); public readonly closeOnClick: boolean = true; @@ -26,50 +25,41 @@ class ResultWidget extends Widget.EventBox { super(); if(props.icon) this.icon = props.icon; - if(props.onClick) - this.onClick = props.onClick; if(props.setup) this.setup = props.setup; if(props.closeOnClick !== undefined) this.closeOnClick = props.closeOnClick; - this.connections = [ - this.connect("click", () => { - this.onClick && this.onClick(); - this.closeOnClick && closeRunner(); - }), + this.onClick = () => { + props.onClick && props.onClick(); + this.closeOnClick && closeRunner(); + }; - this.connect("destroy-event", () => this.connections.map((id: number) => - this.disconnect(id))) - ]; + this.set_class_name("result"); + this.set_hexpand(true); + + this.add(new Widget.Icon({ + visible: Boolean(props.icon), + icon: props.icon || "image-missing" + } as Widget.IconProps)); this.add(new Widget.Box({ - className: "result", - hexpand: true, + orientation: Gtk.Orientation.VERTICAL, + valign: Gtk.Align.CENTER, children: [ - new Widget.Icon({ - visible: Boolean(props.icon), - icon: props.icon || "image-missing" - } as Widget.IconProps), - new Widget.Box({ - orientation: Gtk.Orientation.VERTICAL, - valign: Gtk.Align.CENTER, - children: [ - new Widget.Label({ - className: "title", - xalign: 0, - truncate: true, - label: props.title - } as Widget.LabelProps), - new Widget.Label({ - className: "description", - visible: Boolean(props.description), - truncate: true, - xalign: 0, - label: props.description || "" - } as Widget.LabelProps) - ] - } as Widget.BoxProps), + new Widget.Label({ + className: "title", + xalign: 0, + truncate: true, + label: props.title + } as Widget.LabelProps), + new Widget.Label({ + className: "description", + visible: Boolean(props.description), + truncate: true, + xalign: 0, + label: props.description || "" + } as Widget.LabelProps) ] } as Widget.BoxProps)); } diff --git a/ags/window/Runner.ts b/ags/window/Runner.ts index 1623734..6699b81 100644 --- a/ags/window/Runner.ts +++ b/ags/window/Runner.ts @@ -1,4 +1,4 @@ -import { Variable } from "astal"; +import { AstalIO, timeout, Variable } from "astal"; import { Gdk, Gtk, Widget } from "astal/gtk3"; import { PopupWindow, PopupWindowProps } from "../widget/PopupWindow"; import { updateApps } from "../scripts/apps"; @@ -8,6 +8,7 @@ import { handleApplications } from "../scripts/runner/apps"; import { ResultWidget, ResultWidgetProps } from "../widget/runner/ResultWidget"; export let runnerInstance: (Widget.Window|null) = null; +let onClickTimeout: (AstalIO.Time|undefined); export function closeRunner(gtkWindow?: Widget.Window) { const window = gtkWindow ? gtkWindow : runnerInstance; @@ -64,6 +65,13 @@ export namespace Runner { className: "search", onChanged: (entry) => entryText.set(entry.text), placeholderText: props?.entryPlaceHolder || "", + onActivate: (_) => { + const resultWidget = resultsList.get_selected_row()?.get_child(); + if(resultWidget instanceof ResultWidget) { + resultWidget.onClick(); + _.isFocus = false; + } + }, primary_icon_name: "system-search" } as Widget.EntryProps); @@ -82,33 +90,6 @@ export namespace Runner { : pluginResult) : null; - [ - new Widget.Box({ - className: "not-found", - orientation: Gtk.Orientation.VERTICAL, - visible: entryText((text: string) => text.trim().length > 0), - expand: true, - children: [ - new Widget.Icon({ - icon: "software-update-urgent-symbolic" - } as Widget.IconProps), - new Widget.Label({ - label: "Couldn't find any results with this search. Maybe try pressing F5 and searching again?", - truncate: false, - wrap: true - } as Widget.LabelProps) - ] - } as Widget.BoxProps), - new Widget.Box({ - className: "placeholder", - orientation: Gtk.Orientation.VERTICAL, - expand: true, - visible: Boolean(props?.resultsPlaceholder), - children: props?.resultsPlaceholder && - props?.resultsPlaceholder() - } as Widget.BoxProps) - ]; - if(resultsList.get_children().length > 0) { resultsList.get_children().map((listItem: Gtk.Widget) => { resultsList.remove(listItem); @@ -116,10 +97,34 @@ export namespace Runner { }); } - if(results && results.length > 0) + if(results && results.length > 0 && searchEntry.text.trim().length > 0) { results.map((resultWidget: ResultWidget) => { resultsList.insert(resultWidget, -1); + + const listBoxChild = resultsList.get_row_at_index(resultsList.get_children().length - 1)!; + const resWidget = listBoxChild.get_child(); + if(listBoxChild && resWidget instanceof ResultWidget) { + resultsList.connect("row-activated", (_, row: Gtk.ListBoxRow) => { + const rWidget = row.get_child()!; + if(rWidget instanceof ResultWidget) { + if(!onClickTimeout) { + rWidget.onClick(); + // Timeout, so it doesn't fire the executable a hundred times :skull: + onClickTimeout = timeout(500, () => onClickTimeout = undefined); + } + } + }); + } }); + } else { + if(props?.resultsPlaceholder) { + const widgets = props.resultsPlaceholder(); + resultsList.get_children().map((res) => + resultsList.remove(res)); + + widgets.map((widget) => resultsList.insert(widget, -1)); + } + } selectedResultIndex = 0; resultsList.select_row(resultsList.get_row_at_index(selectedResultIndex)); @@ -133,17 +138,17 @@ export namespace Runner { widthRequest: props?.width || 750, heightRequest: props?.height || 450, onKeyPressEvent: (_, event: Gdk.Event) => { + const keyVal = event.get_keyval()[1]; + if(!searchEntry.has_focus && keyVal !== Gdk.KEY_F5 + && keyVal !== Gdk.KEY_Down && keyVal !== Gdk.KEY_Up + && keyVal !== Gdk.KEY_KP_Enter && keyVal !== Gdk.KEY_ISO_Enter) { + searchEntry.grab_focus_without_selecting(); + } + + event.get_keyval()[1] === Gdk.KEY_F5 && updateApps(); - - if(event.get_keyval()[1] === Gdk.KEY_Down) { - resultsList.get_children().length > 0 && - resultsList.select_row(resultsList.get_row_at_index( - (selectedResultIndex + 1) > (resultsList.get_children().length - 1) ? - 0 - : selectedResultIndex + 1 - )); - } + }, closeAction: (_) => closeRunner(_), onClose: () => subs.map(sub => sub()),