[Astal] Add mode indicator
This commit is contained in:
		| @@ -1,50 +1,59 @@ | ||||
| import AstalTray from "gi://AstalTray"; | ||||
| import { bind, GObject } from "astal"; | ||||
| import AstalHyprland from "gi://AstalHyprland"; | ||||
| import { Gtk } from "astal/gtk4"; | ||||
| import Pango from "gi://Pango?version=1.0"; | ||||
| import { | ||||
|     GObject, bind, | ||||
|     exec, | ||||
|     readFile | ||||
| } from 'astal'; | ||||
| import AstalHyprland from 'gi://AstalHyprland'; | ||||
| import AstalTray from 'gi://AstalTray'; | ||||
| import { | ||||
|     Gtk | ||||
| } from 'astal/gtk4'; | ||||
| import Pango from 'gi://Pango?version=1.0'; | ||||
|  | ||||
| const hypr = AstalHyprland.get_default(); | ||||
| const SYNC = GObject.BindingFlags.SYNC_CREATE; | ||||
|  | ||||
| const SysTray = () => { | ||||
|     const trayBox = new Gtk.Box({ cssClasses: ["bar-button"] }); | ||||
|     const trayBox = new Gtk.Box( { | ||||
|         'cssClasses': [ 'bar-button' ] | ||||
|     } ); | ||||
|     const tray = AstalTray.get_default(); | ||||
|  | ||||
|     const trayItems = new Map<string, Gtk.MenuButton>(); | ||||
|     const trayAddedHandler = tray.connect("item-added", (_, id) => { | ||||
|         const item = tray.get_item(id); | ||||
|         const popover = Gtk.PopoverMenu.new_from_model(item.menu_model); | ||||
|     const trayAddedHandler = tray.connect( 'item-added', ( _, id ) => { | ||||
|         const item = tray.get_item( id ); | ||||
|         const popover = Gtk.PopoverMenu.new_from_model( item.menu_model ); | ||||
|         const icon = new Gtk.Image(); | ||||
|         const button = new Gtk.MenuButton({ | ||||
|         const button = new Gtk.MenuButton( { | ||||
|             popover, | ||||
|             child: icon, | ||||
|             cssClasses: ["tray-item"], | ||||
|         }); | ||||
|             'child': icon, | ||||
|             'cssClasses': [ 'tray-item' ], | ||||
|         } ); | ||||
|  | ||||
|         item.bind_property("gicon", icon, "gicon", SYNC); | ||||
|         popover.insert_action_group("dbusmenu", item.action_group); | ||||
|         item.connect("notify::action-group", () => { | ||||
|             popover.insert_action_group("dbusmenu", item.action_group); | ||||
|         }); | ||||
|         item.bind_property( | ||||
|             'gicon', icon, 'gicon', SYNC | ||||
|         ); | ||||
|         popover.insert_action_group( 'dbusmenu', item.action_group ); | ||||
|         item.connect( 'notify::action-group', () => { | ||||
|             popover.insert_action_group( 'dbusmenu', item.action_group ); | ||||
|         } ); | ||||
|  | ||||
|         trayItems.set(id, button); | ||||
|         trayBox.append(button); | ||||
|     }); | ||||
|         trayItems.set( id, button ); | ||||
|         trayBox.append( button ); | ||||
|     } ); | ||||
|     const trayRemovedHandler = tray.connect( 'item-removed', ( _, id ) => { | ||||
|         const button = trayItems.get( id ); | ||||
|  | ||||
|     const trayRemovedHandler = tray.connect("item-removed", (_, id) => { | ||||
|         const button = trayItems.get(id); | ||||
|         if (button) { | ||||
|             trayBox.remove(button); | ||||
|         if ( button ) { | ||||
|             trayBox.remove( button ); | ||||
|             button.run_dispose(); | ||||
|             trayItems.delete(id); | ||||
|             trayItems.delete( id ); | ||||
|         } | ||||
|     }); | ||||
|     } ); | ||||
|  | ||||
|     trayBox.connect("destroy", () => { | ||||
|         tray.disconnect(trayAddedHandler); | ||||
|         tray.disconnect(trayRemovedHandler); | ||||
|     }); | ||||
|     trayBox.connect( 'destroy', () => { | ||||
|         tray.disconnect( trayAddedHandler ); | ||||
|         tray.disconnect( trayRemovedHandler ); | ||||
|     } ); | ||||
|  | ||||
|     return trayBox; | ||||
| }; | ||||
| @@ -52,25 +61,21 @@ const SysTray = () => { | ||||
| const Workspace = () => { | ||||
|     return ( | ||||
|         <box> | ||||
|             {bind(hypr, "workspaces").as(wss => | ||||
|                 wss | ||||
|                     .filter(ws => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces | ||||
|                     .sort((a, b) => a.id - b.id) | ||||
|                     .map(ws => ( | ||||
|                         <button | ||||
|                             cssClasses={bind(hypr, "focusedWorkspace").as(fw => | ||||
|                                 ws === fw | ||||
|                                     ? [ | ||||
|                                         "focused-workspace-button", | ||||
|                                         "workspace-button", | ||||
|                                     ] | ||||
|                                     : ["workspace-button"], | ||||
|                             )} | ||||
|                             onButtonPressed={() => ws.focus()} | ||||
|                             child={<label label={String(ws.id)}></label>} | ||||
|                         ></button> | ||||
|                     )), | ||||
|             )} | ||||
|             {bind( hypr, 'workspaces' ).as( wss => wss | ||||
|                 .filter( ws => !( ws.id >= -99 && ws.id <= -2 ) ) // filter out special workspaces | ||||
|                 .sort( ( a, b ) => a.id - b.id ) | ||||
|                 .map( ws => ( | ||||
|                     <button | ||||
|                         cssClasses={bind( hypr, 'focusedWorkspace' ).as( fw => ws === fw | ||||
|                             ? [ | ||||
|                                 'focused-workspace-button', | ||||
|                                 'workspace-button', | ||||
|                             ] | ||||
|                             : [ 'workspace-button' ], )} | ||||
|                         onButtonPressed={() => ws.focus()} | ||||
|                         child={<label label={String( ws.id )}></label>} | ||||
|                     ></button> | ||||
|                 ) ), )} | ||||
|         </box> | ||||
|     ); | ||||
| }; | ||||
| @@ -80,15 +85,15 @@ const Workspace = () => { | ||||
|  * displaying all available clients | ||||
|  */ | ||||
| const ActiveWindow = () => { | ||||
|     const focused = bind(hypr, "focusedClient"); | ||||
|     const focused = bind( hypr, 'focusedClient' ); | ||||
|  | ||||
|     const WindowPopover = (): Gtk.Popover => { | ||||
|         // Set up boxes + Popover | ||||
|         const popover = new Gtk.Popover(); | ||||
|  | ||||
|         const popoverBox = WindowPopoverBox(); | ||||
|  | ||||
|         popover.set_child(popoverBox); | ||||
|         popover.set_child( popoverBox ); | ||||
|  | ||||
|         return popover; | ||||
|     }; | ||||
|  | ||||
| @@ -98,65 +103,88 @@ const ActiveWindow = () => { | ||||
|     // Return fully assembled HyprlandFocusedClient box | ||||
|     // ─────────────────────────────────────────────────────────────────── | ||||
|     return ( | ||||
|         <box visible={focused.as(Boolean)}> | ||||
|         <box visible={focused.as( Boolean )}> | ||||
|             <button | ||||
|                 onClicked={() => windowPopover.popup()} | ||||
|                 cssClasses={["bar-button"]} | ||||
|                 cssClasses={[ 'bar-button' ]} | ||||
|                 child={ | ||||
|                     focused.as( | ||||
|                         client => | ||||
|                             client && ( | ||||
|                                 <label | ||||
|                                     maxWidthChars={70} | ||||
|                                     ellipsize={Pango.EllipsizeMode.END} | ||||
|                                     label={bind(client, "title").as(String)} /> | ||||
|                             ), | ||||
|                     ) | ||||
|                     focused.as( client => client && ( | ||||
|                         <label | ||||
|                             maxWidthChars={70} | ||||
|                             ellipsize={Pango.EllipsizeMode.END} | ||||
|                             label={bind( client, 'title' ).as( String )} /> | ||||
|                     ), ) | ||||
|                 }></button> | ||||
|             {windowPopover} | ||||
|         </box > | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| type submaps = 'device' | 'launch' | 'workspace' | 'windowing' | 'screenshotting' | 'notifications' | ''; | ||||
|  | ||||
| const ModeStatus = () => { | ||||
|     const label = new Gtk.Label(); | ||||
|     const map = { | ||||
|         "device": "D", | ||||
|         "launch": "L", | ||||
|         "workspace": "W", | ||||
|         "windowing": "M", | ||||
|         "screenshotting": "S", | ||||
|         "notifications": "N", | ||||
|     let isUsingHyprmode = false; | ||||
|  | ||||
|     try { | ||||
|         const path = exec( 'bash -c "cd ~ && pwd"' ) + '/.config/hyprmode'; | ||||
|  | ||||
|         isUsingHyprmode = readFile( path ).trim() === 'y'; | ||||
|     } catch ( e ) { | ||||
|         printerr( 'Failed to read hyprmode config', e ); | ||||
|     } | ||||
|  | ||||
|     const label = new Gtk.Label(); | ||||
|  | ||||
|     if ( !isUsingHyprmode ) return label; | ||||
|  | ||||
|     print( '==> Using hyprmode config' ); | ||||
|  | ||||
|     const map = { | ||||
|         'device': 'D', | ||||
|         'launch': 'L', | ||||
|         'workspace': 'W', | ||||
|         'windowing': 'M', | ||||
|         'screenshotting': 'S', | ||||
|         'notifications': 'N', | ||||
|         '': 'N' | ||||
|     }; | ||||
|  | ||||
|     label.label = 'N'; | ||||
|     label.cssClasses = [ 'mode-status' ]; | ||||
|  | ||||
|     // TODO: Possibly add popover to it that lists binds | ||||
|     hypr.connect("submap", (name: string) => { | ||||
|     hypr.connect( 'submap', ( _, name: submaps ) => { | ||||
|         label.label = map[name]; | ||||
|         label.cssClasses = ["mode-status", name + '-mode'] | ||||
|     }) | ||||
|     return  | ||||
| } | ||||
|         label.cssClasses = [ | ||||
|             'mode-status', | ||||
|             name + '-mode' | ||||
|         ]; | ||||
|     } ); | ||||
|  | ||||
|     return label; | ||||
| }; | ||||
|  | ||||
| const WindowPopoverBox = () => { | ||||
|     return <box vertical> | ||||
|         <label label={"Available Windows"} cssClasses={['title-2']}></label> | ||||
|         <label label={'Available Windows'} cssClasses={[ 'title-2' ]}></label> | ||||
|         <Gtk.Separator marginTop={5} marginBottom={5}></Gtk.Separator> | ||||
|         <box vertical> | ||||
|             {bind(hypr, 'clients').as(clients => { | ||||
|                 return clients.map(client => { | ||||
|             {bind( hypr, 'clients' ).as( clients => { | ||||
|                 return clients.map( client => { | ||||
|                     return <button child={ | ||||
|                         <box> | ||||
|                             <label label={bind(client, 'workspace').as(w => `(WS ${w.name})`)}></label> | ||||
|                             <label label={bind(client, 'initialClass').as(c => `[${c}]`)}></label> | ||||
|                             <label label={bind(client, 'title')}></label> | ||||
|                             <label label={bind( client, 'workspace' ).as( w => `(WS ${ w.name })` )}></label> | ||||
|                             <label label={bind( client, 'initialClass' ).as( c => `[${ c }]` )}></label> | ||||
|                             <label label={bind( client, 'title' )}></label> | ||||
|                         </box> | ||||
|                     } | ||||
|                         onClicked={() => client.focus()} | ||||
|                     ></button> | ||||
|                 }) | ||||
|             })} | ||||
|                     onClicked={() => client.focus()} | ||||
|                     ></button>; | ||||
|                 } ); | ||||
|             } )} | ||||
|         </box> | ||||
|     </box> | ||||
| } | ||||
|     </box>; | ||||
| }; | ||||
|  | ||||
| export default { | ||||
|     Workspace, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user