import json import os import subprocess from fabric.hyprland.widgets import HyprlandActiveWindow as ActiveWindow from fabric.utils import FormattedString from fabric.widgets.box import Box from fabric.widgets.button import Button from fabric.widgets.centerbox import CenterBox from fabric.widgets.label import Label from modules.about import About, AboutApp from utils.roam import modus_service from widgets.dropdown import ModusDropdown, dropdown_divider from widgets.mousecapture import DropDownMouseCapture from utils.app_name_resolver import format_window def show_about_app(): """Show about dialog for current active application""" try: # Use modus_service's Hyprland connection to get current window info wmclass = "" title = "" if ( hasattr(modus_service, "_hyprland_connection") and modus_service._hyprland_connection ): window_data = modus_service._hyprland_connection.send_command( "j/activewindow" ).reply if window_data: window_info = json.loads(window_data.decode("utf-8")) wmclass = window_info.get("class", "") title = window_info.get("title", "") # Don't show about dialog if there's no active window (Finder state) if not wmclass and not title: return app_name = modus_service.current_active_app_name or "Finder" # Don't show about dialog for Finder if app_name == "Finder": return about_window = AboutApp(app_name=app_name, wmclass=wmclass) about_window.toggle(None) except Exception: # Fallback: only show if we have a real app name app_name = modus_service.current_active_app_name or "" if app_name and app_name != "Finder": about_window = AboutApp(app_name=app_name, wmclass="") about_window.toggle(None) def dropdown_option( label: str = "", keybind: str = "", on_click='echo "ModusPanelDropdown Action"', on_clicked=None, ): def on_click_subthread(button): # Execute the action first if on_clicked: on_clicked(button) else: subprocess.Popen( f"nohup {on_click} &", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) # Hide dropdown by finding the current visible dropdown and calling its hide method from widgets.dropdown import dropdowns for dropdown in dropdowns: if dropdown.is_visible() and hasattr(dropdown, "hide_via_mousecapture"): dropdown.hide_via_mousecapture() break return Button( child=CenterBox( start_children=[ Label(label=label, h_align="start", name="dropdown-option-label"), ], end_children=[ Label(label=keybind, h_align="end", name="dropdown-option-keybind") ], orientation="horizontal", h_align="fill", h_expand=True, v_expand=True, ), name="dropdown-option", h_align="fill", on_clicked=on_click_subthread, h_expand=True, v_expand=True, ) class SystemDropdown(ModusDropdown): def __init__(self, parent, **kwargs): super().__init__( dropdown_id="os-menu", parent=parent, dropdown_children=[ dropdown_option( "About this PC", on_clicked=lambda _: About().toggle(_) ), dropdown_divider("---------------------"), dropdown_option( "System Settings...", # TODO: Open Modus own setting # on_click="xdg-open settings", ), dropdown_divider("---------------------"), dropdown_option("Force Quit", "", "hyprctl kill"), dropdown_divider("---------------------"), dropdown_option("Sleep", "", "systemctl suspend"), dropdown_option("Restart...", "", "systemctl reboot"), dropdown_option("Shut Down...", "", "shutdown now"), dropdown_divider("---------------------"), dropdown_option("Lock Screen", "󰘳 L", "hyprlock"), ], **kwargs, ) class MenuBarDropdowns: def __init__(self, parent): self.parent = parent # System dropdown self.system_dropdown = SystemDropdown(parent=parent) self.menu_button_dropdown = DropDownMouseCapture( layer="bottom", child_window=self.system_dropdown ) self.menu_button = Button( label="Modus", name="menu-button", style_classes="button", on_clicked=lambda _: self.menu_button_dropdown.toggle_mousecapture(), ) self.menu_button_dropdown.child_window.set_pointing_to(self.menu_button) # Global menu dropdowns self.global_title_menu_about = dropdown_option( f"About {modus_service.current_active_app_name}", on_clicked=lambda _: show_about_app(), ) self.global_menu_title = DropDownMouseCapture( layer="bottom", child_window=ModusDropdown( dropdown_id="global-menu-title", parent=parent, dropdown_children=[self.global_title_menu_about], ), ) self.global_menu_file = None self.global_menu_edit = None self.global_menu_view = DropDownMouseCapture( layer="bottom", child_window=ModusDropdown( dropdown_id="global-menu-view", parent=parent, dropdown_children=[ dropdown_option( "Enter Full Screen", on_click="hyprctl dispatch fullscreen", ), ], ), ) self.global_menu_go = None self.global_menu_window = DropDownMouseCapture( layer="bottom", child_window=ModusDropdown( dropdown_id="global-menu-window", parent=parent, dropdown_children=[ dropdown_option( "Zoom In", "󰍉 +", on_click="hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor -j | jq '.float * 1.1')", ), dropdown_option( "Zoom Out", "󰍉 -", on_click="hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor -j | jq '(.float * 0.9) | if . < 1 then 1 else . end')", ), dropdown_divider("---------------------"), dropdown_option( "Move Window to Left", on_click="hyprctl dispatch movewindow l", ), dropdown_option( "Move Window to Right", on_click="hyprctl dispatch movewindow r", ), dropdown_option( "Cycle Through Windows", on_click="hyprctl dispatch cyclenext", ), dropdown_divider("---------------------"), dropdown_option( "Float", on_click="hyprctl dispatch togglefloating" ), dropdown_option("Quit", on_click="hyprctl dispatch killactive"), dropdown_option("Pseudo", on_click="hyprctl dispatch pseudo"), dropdown_option( "Toggle Split", on_click="hyprctl dispatch togglesplit" ), dropdown_option("Center", on_click="hyprctl dispatch centerwindow"), dropdown_option("Group", on_click="hyprctl dispatch togglegroup"), dropdown_option( "Pin", on_clicked=lambda _: subprocess.run( "bash ~/.config/scripts/winpin.sh", shell=True ), ), ], ), ) self.global_menu_help = DropDownMouseCapture( layer="bottom", child_window=ModusDropdown( dropdown_id="global-menu-help", parent=parent, dropdown_children=[ dropdown_option( "Modus", on_click="xdg-open https://github.com/S4NKALP/Modus/issues", ), dropdown_divider("---------------------"), dropdown_option( "Hyprland Wiki", on_click="xdg-open https://wiki.hyprland.org/" ), ], ), ) # Create menu buttons modus_service.connect( "current-active-app-name-changed", lambda _, value: self.global_title_menu_about.set_property( "label", f"About {value}" ), ) # Connect to active app name changes to update the title button modus_service.connect("current-active-app-name-changed", self._on_active_app_changed) self.global_menu_button_title = Button( child=ActiveWindow( formatter=FormattedString( "{ format_window(win_title, win_class) }", format_window=format_window, ) ), name="global-title-button", style_classes="button", on_clicked=self._on_title_button_clicked, ) self.global_menu_title.child_window.set_pointing_to( self.global_menu_button_title ) self.global_menu_button_file = Button( label="File", name="global-menu-button-file", style_classes="button" ) self.global_menu_button_edit = Button( label="Edit", name="global-menu-button-edit", style_classes="button" ) self.global_menu_button_view = Button( label="View", name="global-menu-button-view", style_classes="button", on_clicked=lambda _: self.global_menu_view.toggle_mousecapture(), ) self.global_menu_view.child_window.set_pointing_to(self.global_menu_button_view) self.global_menu_button_go = Button( label="Go", name="global-menu-button-go", style_classes="button" ) self.global_menu_button_window = Button( label="Window", name="global-menu-button-window", style_classes="button", on_clicked=lambda _: self.global_menu_window.toggle_mousecapture(), ) self.global_menu_window.child_window.set_pointing_to( self.global_menu_button_window ) self.global_menu_button_help = Button( label="Help", name="global-menu-button-help", style_classes="button", on_clicked=lambda _: self.global_menu_help.toggle_mousecapture(), ) self.global_menu_help.child_window.set_pointing_to(self.global_menu_button_help) modus_service.connect("current-dropdown-changed", self.changed_dropdown) modus_service.connect("dropdowns-hide-changed", self.hide_dropdowns) def _on_title_button_clicked(self, _): """Handle title button click - only show dropdown if there's an active window""" try: # Use modus_service's Hyprland connection to get current window info if ( hasattr(modus_service, "_hyprland_connection") and modus_service._hyprland_connection ): window_data = modus_service._hyprland_connection.send_command( "j/activewindow" ).reply if window_data: window_info = json.loads(window_data.decode("utf-8")) wmclass = window_info.get("class", "") title = window_info.get("title", "") # Only show dropdown if there's an active window (not Finder) if wmclass or title: self.global_menu_title.toggle_mousecapture() return # Fallback: check if current_active_app_name is not "Finder" if ( modus_service.current_active_app_name and modus_service.current_active_app_name != "Finder" ): self.global_menu_title.toggle_mousecapture() except Exception: # If we can't get window info, don't show dropdown pass def _on_active_app_changed(self, _, value): """Handle active app name changes""" # Update the "About" menu item label self.global_title_menu_about.set_property("label", f"About {value}") def hide_dropdowns(self, *_): self.menu_button.remove_style_class("active") self.global_menu_button_edit.remove_style_class("active") self.global_menu_button_file.remove_style_class("active") self.global_menu_button_go.remove_style_class("active") self.global_menu_button_help.remove_style_class("active") self.global_menu_button_title.remove_style_class("active") self.global_menu_button_view.remove_style_class("active") self.global_menu_button_window.remove_style_class("active") def changed_dropdown(self, _, dropdown_id): self.hide_dropdowns(_, True) match dropdown_id: case "os-menu": self.menu_button.add_style_class("active") case "global-menu-edit": self.global_menu_button_edit.add_style_class("active") case "global-menu-file": self.global_menu_button_file.add_style_class("active") case "global-menu-go": self.global_menu_button_go.add_style_class("active") case "global-menu-help": self.global_menu_button_help.add_style_class("active") case "global-menu-title": self.global_menu_button_title.add_style_class("active") case "global-menu-view": self.global_menu_button_view.add_style_class("active") case "global-menu-window": self.global_menu_button_window.add_style_class("active") case _: pass class MenuBar(Box): """Main MenuBar widget that contains all menu buttons""" def __init__(self, parent_window=None, **kwargs): # Extract parent_window from kwargs if not provided as parameter if parent_window is None: parent_window = kwargs.pop("parent_window", None) super().__init__(name="menubar", orientation="horizontal", spacing=0, **kwargs) # Create the dropdown system self.dropdown_system = MenuBarDropdowns(parent=parent_window) # Add all the menu buttons to the menubar self.children = [ self.dropdown_system.global_menu_button_title, self.dropdown_system.global_menu_button_file, self.dropdown_system.global_menu_button_edit, self.dropdown_system.global_menu_button_view, self.dropdown_system.global_menu_button_go, self.dropdown_system.global_menu_button_window, self.dropdown_system.global_menu_button_help, ] def show_system_dropdown(self, imac_button): self.dropdown_system.menu_button_dropdown.child_window.set_pointing_to( imac_button ) mouse_capture = self.dropdown_system.menu_button_dropdown if mouse_capture.is_visible(): mouse_capture.set_child_window_visible(False) else: mouse_capture.set_child_window_visible(True)