💄 runner: better look and feel, internal improvements
This commit is contained in:
+63
-34
@@ -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<ResultWidget>): Widget.Window {
|
||||
export function openRunner(props: RunnerProps, placeholder?: () => Array<ResultWidget>): Widget.Window {
|
||||
let onClickTimeout: (AstalIO.Time|undefined);
|
||||
const connections: Map<GObject.Object, number> = 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<Result
|
||||
primary_icon_name: "system-search"
|
||||
} as Widget.EntryProps);
|
||||
|
||||
const defaultHeight = 420;
|
||||
|
||||
const resultsList: Gtk.ListBox = new Gtk.ListBox({
|
||||
const mainBox = new Widget.Box({
|
||||
className: `runner main ${props.showResultsPlaceHolderOnStartup ? "empty" : ""}`,
|
||||
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,
|
||||
visible: props.showResultsPlaceHolderOnStartup ?? false,
|
||||
propagateNaturalHeight: true,
|
||||
maxContentHeight: props.height,
|
||||
child: new Gtk.ListBox({
|
||||
visible: true,
|
||||
expand: true
|
||||
} as Gtk.ListBox.ConstructorProps);
|
||||
expand: true,
|
||||
} as Gtk.ListBox.ConstructorProps)
|
||||
})
|
||||
]
|
||||
} as Widget.BoxProps);
|
||||
|
||||
const scrollable = mainBox.get_children()[1] as Widget.Scrollable;
|
||||
const resultsList = scrollable.get_child() as Gtk.ListBox;
|
||||
|
||||
if(props?.showResultsPlaceHolderOnStartup && placeholder) {
|
||||
const placeholderWidgets = placeholder();
|
||||
@@ -211,6 +241,9 @@ export function openRunner(props?: RunnerProps, placeholder?: () => Array<Result
|
||||
widgets.map((resultWidget: ResultWidget) => {
|
||||
resultsList.insert(resultWidget, -1);
|
||||
|
||||
const conns: Array<number> = [];
|
||||
|
||||
conns.push(
|
||||
resultsList.connect("row-activated", (_, row: Gtk.ListBoxRow) => {
|
||||
const rWidget = row.get_child();
|
||||
if(rWidget instanceof ResultWidget) {
|
||||
@@ -221,17 +254,25 @@ export function openRunner(props?: RunnerProps, placeholder?: () => Array<Result
|
||||
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<Result
|
||||
updateApps();
|
||||
},
|
||||
onDestroy: () => {
|
||||
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!;
|
||||
|
||||
+20
-10
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user