[AGS] Launcher: Prepare
This commit is contained in:
		| @@ -1,22 +1,23 @@ | ||||
| import { Variable } from "astal"; | ||||
| import { App, Astal, Gdk, Gtk } from "astal/gtk4"; | ||||
| import { App, Astal, Gdk, Gtk, hook } from "astal/gtk4"; | ||||
| import AstalApps from "gi://AstalApps"; | ||||
| import AppList from "./modules/Apps"; | ||||
|  | ||||
| const MAX_ITEMS = 8; | ||||
| const prefixes = ['=']; | ||||
|  | ||||
| function hide() { | ||||
|   App.get_window("launcher")!.hide(); | ||||
|     App.get_window("launcher")!.hide(); | ||||
| } | ||||
|  | ||||
| const Launcher = () => { | ||||
|     const { CENTER } = Gtk.Align; | ||||
|     const apps = new AstalApps.Apps(); | ||||
|     const width = Variable(1000); | ||||
|     const height = Variable(1000); | ||||
|  | ||||
|     const text = Variable(""); | ||||
|     const visible = Variable(false); | ||||
|     const list = text((text) => apps.fuzzy_query(text).slice(0, MAX_ITEMS)); | ||||
|     const onEnter = () => { | ||||
|         // TODO handle custom stuff | ||||
|         apps.fuzzy_query(text.get())?.[0].launch(); | ||||
|         hide(); | ||||
|     }; | ||||
| @@ -29,13 +30,58 @@ const Launcher = () => { | ||||
|         application={App} | ||||
|         onShow={(self) => { | ||||
|             width.set(self.get_current_monitor().geometry.width); | ||||
|             height.set(self.get_current_monitor().geometry.height); | ||||
|         }} | ||||
|         onKeyPressed={(self, keyval) => { | ||||
|             if (keyval === Gdk.KEY_Escape) self.hide(); | ||||
|         }} | ||||
|         child={ | ||||
|             <box></box> | ||||
|             <box | ||||
|                 vertical | ||||
|                 cssClasses={["app-launcher-wrapper"]} | ||||
|                 widthRequest={width()} | ||||
|                 heightRequest={height()} | ||||
|                 valign={Gtk.Align.CENTER} | ||||
|             > | ||||
|                 <button onClicked={hide} visible={false} /> | ||||
|                 <box | ||||
|                     vertical | ||||
|                     cssClasses={["app-launcher"]} | ||||
|                     valign={Gtk.Align.CENTER} | ||||
|                     halign={Gtk.Align.CENTER} | ||||
|                     widthRequest={500} | ||||
|                 > | ||||
|                     <button onClicked={hide} visible={false}></button> | ||||
|                     <box cssClasses={["search"]}> | ||||
|                         <image iconName={"system-search-symbolic"}></image> | ||||
|                         <entry | ||||
|                             placeholderText={"Search..."} | ||||
|                             text={text.get()} | ||||
|                             setup={self => { | ||||
|                                 hook(self, App, 'window-toggled', (_, win) => { | ||||
|                                     if (win.name == 'launcher') { | ||||
|                                         self.set_text(''); | ||||
|                                         self.grab_focus(); | ||||
|                                     } | ||||
|                                 }) | ||||
|                             }} | ||||
|                             onNotifyText={self => text.set(self.text)} | ||||
|                             primaryIconSensitive | ||||
|                             onActivate={onEnter} | ||||
|                             hexpand></entry> | ||||
|                     </box> | ||||
|                     <AppList | ||||
|                         hide={hide} | ||||
|                         query={text} | ||||
|                         visible={text(v => { | ||||
|                             return !prefixes.includes(v.slice(0, 1)); | ||||
|                         })} | ||||
|                     ></AppList> | ||||
|                 </box> | ||||
|             </box> | ||||
|         } | ||||
|     > | ||||
|     </window> | ||||
| } | ||||
|  | ||||
| export default Launcher; | ||||
|   | ||||
							
								
								
									
										16
									
								
								config/astal/components/launcher/launcher.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								config/astal/components/launcher/launcher.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| @use '../../util/colours.scss' as *; | ||||
|  | ||||
| window { | ||||
|   background: transparent; | ||||
| } | ||||
|  | ||||
| box.app-launcher-wrapper { | ||||
|   background-color: $shadow-color; | ||||
|  | ||||
|   >box.app-launcher { | ||||
|     background-color: $bg-color; | ||||
|     border-radius: 30px; | ||||
|     padding: 20px; | ||||
|     border: 1px solid $accent-color-2; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										59
									
								
								config/astal/components/launcher/modules/Apps.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								config/astal/components/launcher/modules/Apps.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import { Binding, Variable } from "astal"; | ||||
| import { Gtk } from "astal/gtk4"; | ||||
| import AstalApps from "gi://AstalApps"; | ||||
| import Pango from "gi://Pango?version=1.0"; | ||||
|  | ||||
| const MAX_ITEMS = 8; | ||||
|  | ||||
| const AppList = ({ hide, query, visible }: { hide: () => void, query: Variable<string>, visible: Binding<Boolean> }) => { | ||||
|     const apps = new AstalApps.Apps(); | ||||
|     const list = query((text) => apps.fuzzy_query(text).slice(0, MAX_ITEMS)); | ||||
|     return <box> | ||||
|         <box | ||||
|             spacing={6} | ||||
|             vertical | ||||
|             cssClasses={["app-list"]} | ||||
|             visible={list.as(l => l.length > 0)} | ||||
|         > | ||||
|             {list.as(l => l.map(app => <AppButton app={app} hide={hide}></AppButton>))} | ||||
|         </box> | ||||
|         <box | ||||
|             halign={Gtk.Align.CENTER} | ||||
|             cssClasses={["list-empty"]} | ||||
|             vertical | ||||
|             visible={list.as(l => l.length === 0)} | ||||
|         > | ||||
|             <image iconName={"system-search-symbolic"}></image> | ||||
|             <label label={"No match found"}></label> | ||||
|         </box> | ||||
|     </box> | ||||
| } | ||||
|  | ||||
| const AppButton = ({ app, hide }: { app: AstalApps.Application, hide: () => void }) => { | ||||
|     return <button | ||||
|         onClicked={() => { | ||||
|             hide(); | ||||
|             app.launch(); | ||||
|         }} | ||||
|         child={ | ||||
|             <box> | ||||
|                 <image iconName={app.iconName}></image> | ||||
|                 <box valign={Gtk.Align.CENTER} vertical> | ||||
|                     <label | ||||
|                         cssClasses={["title-2"]} | ||||
|                         ellipsize={Pango.EllipsizeMode.END} | ||||
|                         maxWidthChars={40} | ||||
|                         xalign={0} | ||||
|                         label={app.name} | ||||
|                     ></label> | ||||
|                     <label | ||||
|                         wrap xalign={0} | ||||
|                         label={app.description} | ||||
|                     ></label> | ||||
|                 </box> | ||||
|             </box> | ||||
|         }> | ||||
|     </button> | ||||
| } | ||||
|  | ||||
| export default AppList; | ||||
		Reference in New Issue
	
	Block a user