diff --git a/nixosModules/ags/v2/style.scss b/nixosModules/ags/v2/style.scss index fbbbb6e0..7717419f 100644 --- a/nixosModules/ags/v2/style.scss +++ b/nixosModules/ags/v2/style.scss @@ -17,6 +17,7 @@ window, viewport { @import 'widgets/date/style.scss'; @import 'widgets/icon-browser/style.scss'; @import 'widgets/lockscreen/style.scss'; +@import 'widgets/misc/style.scss'; @import 'widgets/notifs/style.scss'; @import 'widgets/powermenu/style.scss'; @import 'widgets/screenshot/style.scss'; diff --git a/nixosModules/ags/v2/widgets/applauncher/main.tsx b/nixosModules/ags/v2/widgets/applauncher/main.tsx index 89e7c812..5a6bab62 100644 --- a/nixosModules/ags/v2/widgets/applauncher/main.tsx +++ b/nixosModules/ags/v2/widgets/applauncher/main.tsx @@ -1,66 +1,36 @@ -import { App, Astal, Gtk, Widget } from 'astal/gtk3'; -import { idle } from 'astal'; +import { App } from 'astal/gtk3'; import AstalApps from 'gi://AstalApps'; -import { Fzf, FzfResultItem } from 'fzf'; +import SortedList from '../misc/sorted-list'; -import PopupWindow from '../misc/popup-window'; -import { centerCursor } from '../../lib'; - -import AppItemWidget, { AppItem } from './app-item'; import { launchApp } from './launch'; +import AppItemWidget, { AppItem } from './app-item'; -export default () => { - let Applications: AstalApps.Application[] = []; - let fzfResults = [] as FzfResultItem[]; +export default () => SortedList({ + name: 'applauncher', - const list = new Gtk.ListBox({ - selectionMode: Gtk.SelectionMode.SINGLE, - }); + create_list: () => AstalApps.Apps.new().get_list(), - list.connect('row-activated', (_, row) => { + create_row: (app) => AppItemWidget({ app }), + + fzf_options: { + selector: (app) => app.name + app.executable, + + tiebreakers: [ + (a, b) => b.item.frequency - a.item.frequency, + ], + }, + + on_row_activated: (row) => { const app = (row.get_children()[0] as AppItem).app; launchApp(app); App.get_window('win-applauncher')?.set_visible(false); - }); + }, - const placeholder = ( - - - ) as Widget.Revealer; - - const on_text_change = (text: string) => { - const fzf = new Fzf(Applications, { - selector: (app) => app.name + app.executable, - - tiebreakers: [ - (a, b) => b.item.frequency - a.item.frequency, - ], - }); - - fzfResults = fzf.find(text); - list.invalidate_sort(); - - const visibleApplications = list.get_children().filter((row) => row.visible).length; - - placeholder.reveal_child = visibleApplications <= 0; - }; - - const entry = ( - on_text_change(self.text)} - hexpand - /> - ) as Widget.Entry; - - list.set_sort_func((a, b) => { + sort_func: (a, b, entry, fzfResults) => { const row1 = (a.get_children()[0] as AppItem).app; const row2 = (b.get_children()[0] as AppItem).app; @@ -79,72 +49,5 @@ export default () => { return s2 - s1; } - }); - - const refreshApplications = () => idle(() => { - (list.get_children() as Gtk.ListBoxRow[]) - .forEach((child) => { - child.destroy(); - }); - - Applications = AstalApps.Apps.new().get_list(); - - Applications - .flatMap((app) => AppItemWidget({ app })) - .forEach((child) => { - list.add(child); - }); - - list.show_all(); - on_text_change(''); - }); - - refreshApplications(); - - return ( - { - entry.text = ''; - centerCursor(); - }} - > - - - - - - {entry} - - - - - - - - - {list} - {placeholder} - - - - - - ); -}; + }, +}); diff --git a/nixosModules/ags/v2/widgets/applauncher/style.scss b/nixosModules/ags/v2/widgets/applauncher/style.scss index 2ee89180..cbba15fc 100644 --- a/nixosModules/ags/v2/widgets/applauncher/style.scss +++ b/nixosModules/ags/v2/widgets/applauncher/style.scss @@ -1,32 +1,6 @@ .applauncher { - .app-search { - icon { - font-size: 20px; - min-width: 40px; - min-height: 40px - } - - entry {} - } - - .app-list { - row { - border-radius: 10px; - - &:hover, &:selected { - icon { - -gtk-icon-shadow: 2px 2px $accent_color; - } - } - - .app { - margin: 20px; - font-size: 16px; - } - } - - .placeholder { - font-size: 20px; - } + .app { + margin: 20px; + font-size: 16px; } } diff --git a/nixosModules/ags/v2/widgets/icon-browser/main.tsx b/nixosModules/ags/v2/widgets/icon-browser/main.tsx index 81dcf1c1..4ce12117 100644 --- a/nixosModules/ags/v2/widgets/icon-browser/main.tsx +++ b/nixosModules/ags/v2/widgets/icon-browser/main.tsx @@ -1,54 +1,29 @@ -import { Astal, Gtk, Widget } from 'astal/gtk3'; -import { idle } from 'astal'; +import { Gtk, Widget } from 'astal/gtk3'; -import { Fzf, FzfResultItem } from 'fzf'; - -import PopupWindow from '../misc/popup-window'; -import { centerCursor } from '../../lib'; +import SortedList from '../misc/sorted-list'; -export default () => { - let Icons: string[] = []; - let fzfResults = [] as FzfResultItem[]; +export default () => SortedList({ + name: 'icon-browser', - const list = new Gtk.ListBox({ - selectionMode: Gtk.SelectionMode.SINGLE, - }); + create_list: () => Gtk.IconTheme.get_default().list_icons(null) + .filter((icon) => icon.endsWith('symbolic')) + .sort(), - list.connect('row-activated', (_, row) => { + create_row: (icon) => ( + + + + ), + + on_row_activated: (row) => { const icon = ((row.get_children()[0] as Widget.Box).get_children()[0] as Widget.Icon).icon; console.log(icon); - }); + }, - const placeholder = ( - - - ) as Widget.Revealer; - - const on_text_change = (text: string) => { - const fzf = new Fzf(Icons); - - fzfResults = fzf.find(text); - list.invalidate_sort(); - - const visibleIcons = list.get_children().filter((row) => row.visible).length; - - placeholder.reveal_child = visibleIcons <= 0; - }; - - const entry = ( - on_text_change(self.text)} - hexpand - /> - ) as Widget.Entry; - - list.set_sort_func((a, b) => { + sort_func: (a, b, entry, fzfResults) => { const row1 = ((a.get_children()[0] as Widget.Box).get_children()[0] as Widget.Icon).icon; const row2 = ((b.get_children()[0] as Widget.Box).get_children()[0] as Widget.Icon).icon; @@ -67,76 +42,5 @@ export default () => { return s2 - s1; } - }); - - const refreshIcons = () => idle(() => { - (list.get_children() as Gtk.ListBoxRow[]) - .forEach((child) => { - child.destroy(); - }); - - Icons = Gtk.IconTheme.get_default().list_icons(null) - .filter((icon) => icon.endsWith('symbolic')) - .sort(); - - Icons - .flatMap((icon) => ( - - - - )) - .forEach((child) => { - list.add(child); - }); - - list.show_all(); - on_text_change(''); - }); - - refreshIcons(); - - return ( - { - entry.text = ''; - centerCursor(); - }} - > - - - - - - {entry} - - - - - - - - {list} - {placeholder} - - - - - ); -}; + }, +}); diff --git a/nixosModules/ags/v2/widgets/icon-browser/style.scss b/nixosModules/ags/v2/widgets/icon-browser/style.scss index 167997c7..c5669131 100644 --- a/nixosModules/ags/v2/widgets/icon-browser/style.scss +++ b/nixosModules/ags/v2/widgets/icon-browser/style.scss @@ -1,32 +1,4 @@ -.icon-browser { - .icon-search { - icon { - font-size: 20px; - min-width: 40px; - min-height: 40px - } - - entry {} - } - - .icon-list { - row { - border-radius: 10px; - - &:hover, &:selected { - icon { - -gtk-icon-shadow: 2px 2px $accent_color; - } - } - - box { - margin: 20px; - font-size: 16px; - } - } - - .placeholder { - font-size: 20px; - } - } +.icon-browser .icon-list row box { + margin: 20px; + font-size: 16px; } diff --git a/nixosModules/ags/v2/widgets/misc/popup-window.tsx b/nixosModules/ags/v2/widgets/misc/popup-window.tsx index dd1b4f7e..afceb7f4 100644 --- a/nixosModules/ags/v2/widgets/misc/popup-window.tsx +++ b/nixosModules/ags/v2/widgets/misc/popup-window.tsx @@ -8,9 +8,9 @@ import { get_hyprland_monitor, hyprMessage } from '../../lib'; type CloseType = 'none' | 'stay' | 'released' | 'clicked'; type HyprTransition = 'slide' | 'slide top' | 'slide bottom' | 'slide left' | 'slide right' | 'popin' | 'fade'; -type PopupCallback = (self: PopupWindow) => void; +type PopupCallback = (self?: Widget.Window) => void; -type PopupWindowProps = Widget.WindowProps & { +export type PopupWindowProps = Widget.WindowProps & { transition?: HyprTransition | Binding close_on_unfocus?: CloseType | Binding on_open?: PopupCallback diff --git a/nixosModules/ags/v2/widgets/misc/sorted-list.tsx b/nixosModules/ags/v2/widgets/misc/sorted-list.tsx new file mode 100644 index 00000000..d46397fa --- /dev/null +++ b/nixosModules/ags/v2/widgets/misc/sorted-list.tsx @@ -0,0 +1,174 @@ +// This is definitely not good practice but I couldn't figure out how to extend PopupWindow +// so here we are with a cursed function that returns a prop of the class. + +import { Astal, Gtk, Widget } from 'astal/gtk3'; +import { idle } from 'astal'; + +import { Fzf, FzfOptions, FzfResultItem } from 'fzf'; + +import PopupWindow, { PopupWindow as PopupWindowClass } from '../misc/popup-window'; +import { centerCursor } from '../../lib'; + +export interface SortedListProps { + create_list: () => T[] + create_row: (item: T) => Gtk.Widget + fzf_options?: FzfOptions + on_row_activated: (row: Gtk.ListBoxRow) => void + sort_func: ( + a: Gtk.ListBoxRow, + b: Gtk.ListBoxRow, + entry: Widget.Entry, + fzf: FzfResultItem[], + ) => number + name: string +}; + + +export class SortedList { + private item_list: T[] = []; + private fzf_results: FzfResultItem[] = []; + + readonly window: PopupWindowClass; + + readonly create_list: () => T[]; + readonly create_row: (item: T) => Gtk.Widget; + readonly fzf_options: FzfOptions | undefined; + + readonly on_row_activated: (row: Gtk.ListBoxRow) => void; + + readonly sort_func: ( + a: Gtk.ListBoxRow, + b: Gtk.ListBoxRow, + entry: Widget.Entry, + fzf: FzfResultItem[], + ) => number; + + + constructor({ + create_list, + create_row, + fzf_options, + on_row_activated, + sort_func, + name, + }: SortedListProps) { + const list = new Gtk.ListBox({ + selectionMode: Gtk.SelectionMode.SINGLE, + }); + + list.connect('row-activated', (_, row) => { + this.on_row_activated(row); + }); + + const placeholder = ( + + + ) as Widget.Revealer; + + const on_text_change = (text: string) => { + // @ts-expect-error this should be okay + this.fzf_results = (new Fzf(this.item_list, this.fzf_options)).find(text); + list.invalidate_sort(); + + const visibleApplications = list.get_children().filter((row) => row.visible).length; + + placeholder.reveal_child = visibleApplications <= 0; + }; + + const entry = ( + on_text_change(self.text)} + hexpand + /> + ) as Widget.Entry; + + list.set_sort_func((a, b) => { + return this.sort_func(a, b, entry, this.fzf_results); + }); + + const refreshItems = () => idle(() => { + (list.get_children() as Gtk.ListBoxRow[]) + .forEach((child) => { + child.destroy(); + }); + + this.item_list = this.create_list(); + + this.item_list + .flatMap((prop) => this.create_row(prop)) + .forEach((child) => { + list.add(child); + }); + + list.show_all(); + on_text_change(''); + }); + + this.window = ( + { + entry.text = ''; + centerCursor(); + }} + > + + + + + + {entry} + + + + + + + + + {list} + {placeholder} + + + + + + ) as PopupWindowClass; + + this.create_list = create_list; + this.create_row = create_row; + this.fzf_options = fzf_options; + this.on_row_activated = on_row_activated; + this.sort_func = sort_func; + + refreshItems(); + } +}; + +/** + * @param props props for a SortedList Widget + * @returns the widget + */ +export default function(props: SortedListProps) { + return (new SortedList(props)).window; +} diff --git a/nixosModules/ags/v2/widgets/misc/style.scss b/nixosModules/ags/v2/widgets/misc/style.scss new file mode 100644 index 00000000..89fabde4 --- /dev/null +++ b/nixosModules/ags/v2/widgets/misc/style.scss @@ -0,0 +1,27 @@ +.sorted-list { + .search { + icon { + font-size: 20px; + min-width: 40px; + min-height: 40px + } + + entry {} + } + + .list { + row { + border-radius: 10px; + + &:hover, &:selected { + icon { + -gtk-icon-shadow: 2px 2px $accent_color; + } + } + } + + .placeholder { + font-size: 20px; + } + } +}