166 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 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";
 | |
| 
 | |
| const hypr = AstalHyprland.get_default();
 | |
| const SYNC = GObject.BindingFlags.SYNC_CREATE;
 | |
| 
 | |
| const SysTray = () => {
 | |
|     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 icon = new Gtk.Image();
 | |
|         const button = new Gtk.MenuButton({
 | |
|             popover,
 | |
|             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);
 | |
|         });
 | |
| 
 | |
|         trayItems.set(id, button);
 | |
|         trayBox.append(button);
 | |
|     });
 | |
| 
 | |
|     const trayRemovedHandler = tray.connect("item-removed", (_, id) => {
 | |
|         const button = trayItems.get(id);
 | |
|         if (button) {
 | |
|             trayBox.remove(button);
 | |
|             button.run_dispose();
 | |
|             trayItems.delete(id);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     trayBox.connect("destroy", () => {
 | |
|         tray.disconnect(trayAddedHandler);
 | |
|         tray.disconnect(trayRemovedHandler);
 | |
|     });
 | |
| 
 | |
|     return trayBox;
 | |
| };
 | |
| 
 | |
| 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>
 | |
|                     )),
 | |
|             )}
 | |
|         </box>
 | |
|     );
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Displays the name of the currently active window and provides a popover for
 | |
|  * displaying all available clients
 | |
|  */
 | |
| const ActiveWindow = () => {
 | |
|     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);
 | |
|         return popover;
 | |
|     };
 | |
| 
 | |
|     const windowPopover = WindowPopover();
 | |
| 
 | |
|     // ───────────────────────────────────────────────────────────────────
 | |
|     // Return fully assembled HyprlandFocusedClient box
 | |
|     // ───────────────────────────────────────────────────────────────────
 | |
|     return (
 | |
|         <box visible={focused.as(Boolean)}>
 | |
|             <button
 | |
|                 onClicked={() => windowPopover.popup()}
 | |
|                 cssClasses={["bar-button"]}
 | |
|                 child={
 | |
|                     focused.as(
 | |
|                         client =>
 | |
|                             client && (
 | |
|                                 <label
 | |
|                                     maxWidthChars={70}
 | |
|                                     ellipsize={Pango.EllipsizeMode.END}
 | |
|                                     label={bind(client, "title").as(String)} />
 | |
|                             ),
 | |
|                     )
 | |
|                 }></button>
 | |
|             {windowPopover}
 | |
|         </box >
 | |
|     );
 | |
| };
 | |
| 
 | |
| const ModeStatus = () => {
 | |
|     const label = new Gtk.Label();
 | |
|     const map = {
 | |
|         "device": "D",
 | |
|         "launch": "L",
 | |
|         "workspace": "W",
 | |
|         "windowing": "M",
 | |
|         "screenshotting": "S",
 | |
|     }
 | |
|     // TODO: Possibly add popover to it that lists binds
 | |
|     hypr.connect("submap", (name: string) => {
 | |
|         label.label = map[name];
 | |
|         label.cssClasses = ["mode-status", name + '-mode']
 | |
|     })
 | |
|     return 
 | |
| }
 | |
| 
 | |
| const WindowPopoverBox = () => {
 | |
|     return <box vertical>
 | |
|         <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 => {
 | |
|                     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>
 | |
|                         </box>
 | |
|                     }
 | |
|                         onClicked={() => client.focus()}
 | |
|                     ></button>
 | |
|                 })
 | |
|             })}
 | |
|         </box>
 | |
|     </box>
 | |
| }
 | |
| 
 | |
| export default {
 | |
|     Workspace,
 | |
|     ActiveWindow,
 | |
|     SysTray,
 | |
|     ModeStatus
 | |
| };
 |