From a6f47cc5483d0048e05d19308dad74ce063ed3ea Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Sat, 7 Jun 2025 16:11:30 +0545 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20chore(dock):=20refactor=20DockSy?= =?UTF-8?q?stemTray=20to=20DockComponents=20and=20update=20related=20chang?= =?UTF-8?q?es=20for=20consistent=20naming=20and=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar.py | 564 ---------------------------- config/config.json | 15 +- config/data.py | 15 +- config/dock.json | 13 +- config/settings/gui.py | 734 +++++++++++++++++++++++++------------ modules/dock/components.py | 33 +- modules/dock/main.py | 39 +- 7 files changed, 550 insertions(+), 863 deletions(-) delete mode 100644 bar.py diff --git a/bar.py b/bar.py deleted file mode 100644 index 7ef36a1..0000000 --- a/bar.py +++ /dev/null @@ -1,564 +0,0 @@ -import json -import os - -from fabric.hyprland.service import HyprlandEvent -from fabric.hyprland.widgets import ( - Language, - WorkspaceButton, - Workspaces, - get_hyprland_connection, -) -from fabric.utils.helpers import exec_shell_command_async -from fabric.widgets.box import Box -from fabric.widgets.button import Button -from fabric.widgets.centerbox import CenterBox -from fabric.widgets.datetime import DateTime -from fabric.widgets.label import Label -from fabric.widgets.revealer import Revealer -from gi.repository import Gdk, Gtk - -import config.data as data -import modules.icons as icons -from modules.controls import ControlSmall -from modules.dock import Dock -from modules.metrics import Battery, MetricsSmall, NetworkApplet -from modules.systemtray import SystemTray -from modules.weather import Weather -from widgets.wayland import WaylandWindow as Window - -CHINESE_NUMERALS = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "〇"] - -# Tooltips -tooltip_apps = f"""Launcher -• Apps: Type to search. - -• Calculator [Prefix "="]: Solve a math expression. - e.g. "=2+2" - -• Converter [Prefix ";"]: Convert between units. - e.g. ";100 USD to EUR", ";10 km to miles" - -• Special Commands [Prefix ":"]: - :update - Open {data.APP_NAME_CAP}'s updater. - :d - Open Dashboard. - :w - Open Wallpapers.""" - -tooltip_power = """Power Menu""" -tooltip_tools = """Toolbox""" -tooltip_overview = """Overview""" - - -class Bar(Window): - def __init__(self, **kwargs): - super().__init__( - name="bar", - layer="top", - exclusivity="auto", - visible=True, - all_visible=True, - ) - - self.anchor_var = "" - self.margin_var = "" - - match data.BAR_POSITION: - case "Top": - self.anchor_var = "left top right" - case "Bottom": - self.anchor_var = "left bottom right" - case "Left": - self.anchor_var = "left" if data.CENTERED_BAR else "left top bottom" - case "Right": - self.anchor_var = "right" if data.CENTERED_BAR else "top right bottom" - case _: - self.anchor_var = "left top right" - - if data.VERTICAL: - match data.BAR_THEME: - case "Edge": - self.margin_var = "-8px -8px -8px -8px" - case _: - self.margin_var = "-4px -8px -4px -4px" - else: - match data.BAR_THEME: - case "Edge": - self.margin_var = "-8px -8px -8px -8px" - case _: - if data.BAR_POSITION == "Bottom": - self.margin_var = "-8px -4px -4px -4px" - else: - self.margin_var = "-4px -4px -8px -4px" - - self.set_anchor(self.anchor_var) - self.set_margin(self.margin_var) - - self.notch = kwargs.get("notch", None) - self.component_visibility = data.BAR_COMPONENTS_VISIBILITY - - self.dock_instance = None - self.integrated_dock_widget = None - - self.workspaces = Workspaces( - name="workspaces", - invert_scroll=True, - empty_scroll=True, - v_align="fill", - orientation="h" if not data.VERTICAL else "v", - spacing=8, - buttons=[ - WorkspaceButton( - h_expand=False, - v_expand=False, - h_align="center", - v_align="center", - id=i, - label=None, - style_classes=["vertical"] if data.VERTICAL else None, - ) - for i in range(1, 11) - ], - ) - - self.workspaces_num = Workspaces( - name="workspaces-num", - invert_scroll=True, - empty_scroll=True, - v_align="fill", - orientation="h" if not data.VERTICAL else "v", - spacing=0 if not data.BAR_WORKSPACE_USE_CHINESE_NUMERALS else 4, - buttons=[ - WorkspaceButton( - h_expand=False, - v_expand=False, - h_align="center", - v_align="center", - id=i, - label=CHINESE_NUMERALS[i - 1] - if data.BAR_WORKSPACE_USE_CHINESE_NUMERALS - and 1 <= i <= len(CHINESE_NUMERALS) - else str(i), - ) - for i in range(1, 11) - ], - ) - - self.ws_container = Box( - name="workspaces-container", - children=self.workspaces - if not data.BAR_WORKSPACE_SHOW_NUMBER - else self.workspaces_num, - ) - - self.button_tools = Button( - name="button-bar", - tooltip_markup=tooltip_tools, - on_clicked=lambda *_: self.tools_menu(), - child=Label(name="button-bar-label", markup=icons.toolbox), - ) - - self.connection = get_hyprland_connection() - self.button_tools.connect("enter_notify_event", self.on_button_enter) - self.button_tools.connect("leave_notify_event", self.on_button_leave) - - self.systray = SystemTray() - self.weather = Weather() - self.network = NetworkApplet() - - self.lang_label = Label(name="lang-label") - self.language = Button( - name="language", h_align="center", v_align="center", child=self.lang_label - ) - self.on_language_switch() - self.connection.connect("event::activelayout", self.on_language_switch) - - self.date_time = DateTime( - name="date-time", - formatters=["%H:%M"] if not data.VERTICAL else ["%H\n%M"], - h_align="center" if not data.VERTICAL else "fill", - v_align="center", - h_expand=True, - v_expand=True, - ) - - self.button_apps = Button( - name="button-bar", - tooltip_markup=tooltip_apps, - on_clicked=lambda *_: self.search_apps(), - child=Label(name="button-bar-label", markup=icons.apps), - ) - self.button_apps.connect("enter_notify_event", self.on_button_enter) - self.button_apps.connect("leave_notify_event", self.on_button_leave) - - self.button_power = Button( - name="button-bar", - tooltip_markup=tooltip_power, - on_clicked=lambda *_: self.power_menu(), - child=Label(name="button-bar-label", markup=icons.shutdown), - ) - self.button_power.connect("enter_notify_event", self.on_button_enter) - self.button_power.connect("leave_notify_event", self.on_button_leave) - - self.button_overview = Button( - name="button-bar", - tooltip_markup=tooltip_overview, - on_clicked=lambda *_: self.overview(), - child=Label(name="button-bar-label", markup=icons.windows), - ) - self.button_overview.connect("enter_notify_event", self.on_button_enter) - self.button_overview.connect("leave_notify_event", self.on_button_leave) - - self.control = ControlSmall() - self.metrics = MetricsSmall() - self.battery = Battery() - - self.apply_component_props() - - self.rev_right = [ - self.metrics, - self.control, - ] - - self.revealer_right = Revealer( - name="bar-revealer", - transition_type="slide-left", - child_revealed=True, - child=Box( - name="bar-revealer-box", - orientation="h", - spacing=4, - children=self.rev_right if not data.VERTICAL else None, - ), - ) - - self.boxed_revealer_right = Box( - name="boxed-revealer", - children=[ - self.revealer_right, - ], - ) - - self.rev_left = [ - self.weather, - self.network, - ] - - self.revealer_left = Revealer( - name="bar-revealer", - transition_type="slide-right", - child_revealed=True, - child=Box( - name="bar-revealer-box", - orientation="h", - spacing=4, - children=self.rev_left if not data.VERTICAL else None, - ), - ) - - self.boxed_revealer_left = Box( - name="boxed-revealer", - children=[ - self.revealer_left, - ], - ) - - self.h_start_children = [ - self.button_apps, - self.ws_container, - self.button_overview, - self.boxed_revealer_left, - ] - - self.h_end_children = [ - self.boxed_revealer_right, - self.battery, - self.systray, - self.button_tools, - self.language, - self.date_time, - self.button_power, - ] - - self.v_start_children = [ - self.button_apps, - self.systray, - self.control, - self.network, - self.button_tools, - ] - - self.v_center_children = [ - self.button_overview, - self.ws_container, - self.weather, - ] - - self.v_end_children = [ - self.battery, - self.metrics, - self.language, - self.date_time, - self.button_power, - ] - - self.v_all_children = [] - self.v_all_children.extend(self.v_start_children) - self.v_all_children.extend(self.v_center_children) - self.v_all_children.extend(self.v_end_children) - - if ( - data.DOCK_ENABLED - and data.BAR_POSITION == "Bottom" - or data.PANEL_THEME == "Panel" - and data.BAR_POSITION in ["Top", "Bottom"] - ): - if not data.VERTICAL: - self.dock_instance = Dock(integrated_mode=True) - self.integrated_dock_widget = self.dock_instance.wrapper - - is_centered_bar = data.VERTICAL and getattr(data, "CENTERED_BAR", False) - - bar_center_actual_children = None - - if self.integrated_dock_widget is not None: - bar_center_actual_children = self.integrated_dock_widget - elif data.VERTICAL: - bar_center_actual_children = Box( - orientation=Gtk.Orientation.VERTICAL, - spacing=4, - children=self.v_all_children - if is_centered_bar - else self.v_center_children, - ) - - self.bar_inner = CenterBox( - name="bar-inner", - orientation=Gtk.Orientation.HORIZONTAL - if not data.VERTICAL - else Gtk.Orientation.VERTICAL, - h_align="fill", - v_align="fill", - start_children=None - if is_centered_bar - else Box( - name="start-container", - spacing=4, - orientation=Gtk.Orientation.HORIZONTAL - if not data.VERTICAL - else Gtk.Orientation.VERTICAL, - children=self.h_start_children - if not data.VERTICAL - else self.v_start_children, - ), - center_children=bar_center_actual_children, - end_children=None - if is_centered_bar - else Box( - name="end-container", - spacing=4, - orientation=Gtk.Orientation.HORIZONTAL - if not data.VERTICAL - else Gtk.Orientation.VERTICAL, - children=self.h_end_children - if not data.VERTICAL - else self.v_end_children, - ), - ) - - self.children = self.bar_inner - - self.hidden = False - - self.themed_children = [ - self.button_apps, - self.button_overview, - self.button_power, - self.button_tools, - self.language, - self.date_time, - self.ws_container, - self.weather, - self.network, - self.battery, - self.metrics, - self.systray, - self.control, - ] - if self.integrated_dock_widget: - self.themed_children.append(self.integrated_dock_widget) - - current_theme = data.BAR_THEME - theme_classes = ["pills", "dense", "edge", "edgecenter"] - for tc in theme_classes: - self.bar_inner.remove_style_class(tc) - - self.style = None - match current_theme: - case "Pills": - self.style = "pills" - case "Dense": - self.style = "dense" - case "Edge": - if data.VERTICAL and data.CENTERED_BAR: - self.style = "edgecenter" - else: - self.style = "edge" - case _: - self.style = "pills" - - self.bar_inner.add_style_class(self.style) - - if self.integrated_dock_widget and hasattr( - self.integrated_dock_widget, "add_style_class" - ): - for theme_class_to_remove in ["pills", "dense", "edge"]: - style_context = self.integrated_dock_widget.get_style_context() - if style_context.has_class(theme_class_to_remove): - self.integrated_dock_widget.remove_style_class( - theme_class_to_remove - ) - self.integrated_dock_widget.add_style_class(self.style) - - if data.BAR_THEME == "Dense" or data.BAR_THEME == "Edge": - for child in self.themed_children: - if hasattr(child, "add_style_class"): - child.add_style_class("invert") - - match data.BAR_POSITION: - case "Top": - self.bar_inner.add_style_class("top") - case "Bottom": - self.bar_inner.add_style_class("bottom") - case "Left": - self.bar_inner.add_style_class("left") - case "Right": - self.bar_inner.add_style_class("right") - case _: - self.bar_inner.add_style_class("top") - - if data.VERTICAL: - self.bar_inner.add_style_class("vertical") - - self.systray._update_visibility() - self.chinese_numbers() - - def apply_component_props(self): - components = { - "button_apps": self.button_apps, - "systray": self.systray, - "control": self.control, - "network": self.network, - "button_tools": self.button_tools, - "button_overview": self.button_overview, - "ws_container": self.ws_container, - "weather": self.weather, - "battery": self.battery, - "metrics": self.metrics, - "language": self.language, - "date_time": self.date_time, - "button_power": self.button_power, - } - - for component_name, widget in components.items(): - if component_name in self.component_visibility: - widget.set_visible(self.component_visibility[component_name]) - - def toggle_component_visibility(self, component_name): - components = { - "button_apps": self.button_apps, - "systray": self.systray, - "control": self.control, - "network": self.network, - "button_tools": self.button_tools, - "button_overview": self.button_overview, - "ws_container": self.ws_container, - "weather": self.weather, - "battery": self.battery, - "metrics": self.metrics, - "language": self.language, - "date_time": self.date_time, - "button_power": self.button_power, - } - - if component_name in components and component_name in self.component_visibility: - self.component_visibility[component_name] = not self.component_visibility[ - component_name - ] - components[component_name].set_visible( - self.component_visibility[component_name] - ) - - config_file = os.path.expanduser( - f"~/.config/{data.APP_NAME}/config/config.json" - ) - if os.path.exists(config_file): - try: - with open(config_file, "r") as f: - config = json.load(f) - - config[f"bar_{component_name}_visible"] = self.component_visibility[ - component_name - ] - - with open(config_file, "w") as f: - json.dump(config, f, indent=4) - except Exception as e: - print(f"Error updating config file: {e}") - - return self.component_visibility[component_name] - - return None - - def on_button_enter(self, widget, event): - window = widget.get_window() - if window: - window.set_cursor(Gdk.Cursor.new_from_name(widget.get_display(), "hand2")) - - def on_button_leave(self, widget, event): - window = widget.get_window() - if window: - window.set_cursor(None) - - def on_button_clicked(self, *args): - exec_shell_command_async("notify-send 'Botón presionado' '¡Funciona!'") - - def search_apps(self): - if self.notch: - self.notch.open_notch("launcher") - - def overview(self): - if self.notch: - self.notch.open_notch("overview") - - def power_menu(self): - if self.notch: - self.notch.open_notch("power") - - def tools_menu(self): - if self.notch: - self.notch.open_notch("tools") - - def on_language_switch(self, _=None, event: HyprlandEvent = None): - lang_data = ( - event.data[1] - if event and event.data and len(event.data) > 1 - else Language().get_label() - ) - self.language.set_tooltip_text(lang_data) - if not data.VERTICAL: - self.lang_label.set_label(lang_data[:3].upper()) - else: - self.lang_label.add_style_class("icon") - self.lang_label.set_markup(icons.keyboard) - - def toggle_hidden(self): - self.hidden = not self.hidden - if self.hidden: - self.bar_inner.add_style_class("hidden") - else: - self.bar_inner.remove_style_class("hidden") - - def chinese_numbers(self): - if data.BAR_WORKSPACE_USE_CHINESE_NUMERALS: - self.workspaces_num.add_style_class("chinese") - else: - self.workspaces_num.remove_style_class("chinese") diff --git a/config/config.json b/config/config.json index 95a331b..0d60a4f 100644 --- a/config/config.json +++ b/config/config.json @@ -45,13 +45,12 @@ "corners_visible": false, "dock_theme": "Pills", "notif_pos": "Top", - "bar_workspace_visible": true, - "bar_metrics_visible": true, - "bar_date_time_visible": true, - "bar_battery_visible": true, - "bar_controls_visible": true, - "bar_indicators_visible": true, - "bar_systray_visible": true, + "dock_workspace_visible": true, + "dock_metrics_visible": true, + "dock_date_time_visible": true, + "dock_battery_visible": true, + "dock_controls_visible": true, + "dock_indicators_visible": true, "metrics_visible": { "cpu": true, "ram": true, @@ -60,4 +59,4 @@ "gpu": false }, "metrics_disks": [] -} \ No newline at end of file +} diff --git a/config/data.py b/config/data.py index abd0f52..91af475 100644 --- a/config/data.py +++ b/config/data.py @@ -34,7 +34,6 @@ MATUGEN_STATE_FILE = os.path.join(CONFIG_DIR, "matugen") DOCK_THEME = "Pills" - def load_config(): """Load the configuration from config.json""" config_path = os.path.expanduser(f"~/{APP_NAME_CAP}/config/config.json") @@ -74,7 +73,10 @@ if os.path.exists(CONFIG_FILE): } METRICS_DISKS = config.get("metrics_disks", ["/"]) - METRICS_VISIBLE = config.get("metrics_visible", {"cpu": True, "ram": True, "disk": True, "swap": True, "gpu": False}) + METRICS_VISIBLE = config.get( + "metrics_visible", + {"cpu": True, "ram": True, "disk": True, "swap": True, "gpu": False}, + ) else: WALLPAPERS_DIR = WALLPAPERS_DIR_DEFAULT DOCK_POSITION = "Bottom" @@ -87,7 +89,6 @@ else: NOTIF_POS = NOTIF_POS_DEFAULT - DOCK_COMPONENTS_VISIBILITY = { "workspaces": True, "metrics": True, @@ -98,4 +99,10 @@ else: } METRICS_DISKS = ["/"] - METRICS_VISIBLE = {"cpu": True, "ram": True, "disk": True, "swap": True, "gpu": False} + METRICS_VISIBLE = { + "cpu": True, + "ram": True, + "disk": True, + "swap": True, + "gpu": False, + } diff --git a/config/dock.json b/config/dock.json index d4fbc6a..7564bbf 100644 --- a/config/dock.json +++ b/config/dock.json @@ -1,12 +1,3 @@ { - "pinned_apps": [ - { - "name": "Brave", - "display_name": "Brave", - "window_class": "brave-browser", - "executable": "brave", - "command_line": "brave %U", - "icon_name": "brave-desktop" - } - ] -} + "pinned_apps": [] +} \ No newline at end of file diff --git a/config/settings/gui.py b/config/settings/gui.py index 9e3d209..435fc72 100644 --- a/config/settings/gui.py +++ b/config/settings/gui.py @@ -1,3 +1,16 @@ +from config.settings.utils import backup_and_replace, bind_vars, start_config +from config.data import APP_NAME, APP_NAME_CAP, NOTIF_POS_DEFAULT, NOTIF_POS_KEY +from PIL import Image +from gi.repository import GdkPixbuf, GLib, Gtk +from fabric.widgets.window import Window +from fabric.widgets.stack import Stack +from fabric.widgets.scrolledwindow import ScrolledWindow +from fabric.widgets.scale import Scale +from fabric.widgets.label import Label +from fabric.widgets.image import Image as FabricImage +from fabric.widgets.entry import Entry +from fabric.widgets.button import Button +from fabric.widgets.box import Box import json import os import shutil @@ -8,21 +21,7 @@ from pathlib import Path import gi -gi.require_version('Gtk', '3.0') -from fabric.widgets.box import Box -from fabric.widgets.button import Button -from fabric.widgets.entry import Entry -from fabric.widgets.image import Image as FabricImage -from fabric.widgets.label import Label -from fabric.widgets.scale import Scale -from fabric.widgets.scrolledwindow import ScrolledWindow -from fabric.widgets.stack import Stack -from fabric.widgets.window import Window -from gi.repository import GdkPixbuf, GLib, Gtk -from PIL import Image - -from config.data import (APP_NAME, APP_NAME_CAP, NOTIF_POS_DEFAULT, NOTIF_POS_KEY) -from config.settings.utils import backup_and_replace, bind_vars, start_config +gi.require_version("Gtk", "3.0") class HyprConfGUI(Window): @@ -47,9 +46,10 @@ class HyprConfGUI(Window): root_box.add(main_content_box) self.tab_stack = Stack( - transition_type="slide-up-down", - transition_duration=250, - v_expand=True, h_expand=True + transition_type="slide-up-down", + transition_duration=250, + v_expand=True, + h_expand=True, ) self.key_bindings_tab_content = self.create_key_bindings_tab() @@ -57,8 +57,12 @@ class HyprConfGUI(Window): self.system_tab_content = self.create_system_tab() self.about_tab_content = self.create_about_tab() - self.tab_stack.add_titled(self.key_bindings_tab_content, "key_bindings", "Key Bindings") - self.tab_stack.add_titled(self.appearance_tab_content, "appearance", "Appearance") + self.tab_stack.add_titled( + self.key_bindings_tab_content, "key_bindings", "Key Bindings" + ) + self.tab_stack.add_titled( + self.appearance_tab_content, "appearance", "Appearance" + ) self.tab_stack.add_titled(self.system_tab_content, "system", "System") self.tab_stack.add_titled(self.about_tab_content, "about", "About") @@ -79,12 +83,12 @@ class HyprConfGUI(Window): def create_key_bindings_tab(self): scrolled_window = ScrolledWindow( - h_scrollbar_policy="never", + h_scrollbar_policy="never", v_scrollbar_policy="automatic", h_expand=True, v_expand=True, propagate_width=False, - propagate_height=False + propagate_height=False, ) main_vbox = Box(orientation="v", spacing=10, style="margin: 15px;") @@ -97,11 +101,19 @@ class HyprConfGUI(Window): keybind_grid.set_margin_end(5) keybind_grid.set_margin_top(5) keybind_grid.set_margin_bottom(5) - - action_label = Label(markup="Action", h_align="start", style="margin-bottom: 5px;") - modifier_label = Label(markup="Modifier", h_align="start", style="margin-bottom: 5px;") - separator_label = Label(label="+", h_align="center", style="margin-bottom: 5px;") - key_label = Label(markup="Key", h_align="start", style="margin-bottom: 5px;") + + action_label = Label( + markup="Action", h_align="start", style="margin-bottom: 5px;" + ) + modifier_label = Label( + markup="Modifier", h_align="start", style="margin-bottom: 5px;" + ) + separator_label = Label( + label="+", h_align="center", style="margin-bottom: 5px;" + ) + key_label = Label( + markup="Key", h_align="start", style="margin-bottom: 5px;" + ) keybind_grid.attach(action_label, 0, 0, 1, 1) keybind_grid.attach(modifier_label, 1, 0, 1, 1) @@ -110,35 +122,39 @@ class HyprConfGUI(Window): self.entries = [] bindings = [ - (f"Reload {APP_NAME_CAP}", 'prefix_restart', 'suffix_restart'), - ("Message", 'prefix_axmsg', 'suffix_axmsg'), - ("Dashboard", 'prefix_dash', 'suffix_dash'), - ("Bluetooth", 'prefix_bluetooth', 'suffix_bluetooth'), - ("Pins", 'prefix_pins', 'suffix_pins'), - ("Kanban", 'prefix_kanban', 'suffix_kanban'), - ("App Launcher", 'prefix_launcher', 'suffix_launcher'), - ("Tmux", 'prefix_tmux', 'suffix_tmux'), - ("Clipboard History", 'prefix_cliphist', 'suffix_cliphist'), - ("Toolbox", 'prefix_toolbox', 'suffix_toolbox'), - ("Overview", 'prefix_overview', 'suffix_overview'), - ("Wallpapers", 'prefix_wallpapers', 'suffix_wallpapers'), - ("Random Wallpaper", 'prefix_randwall', 'suffix_randwall'), - ("Emoji Picker", 'prefix_emoji', 'suffix_emoji'), - ("Power Menu", 'prefix_power', 'suffix_power'), - ("Toggle Caffeine", 'prefix_caffeine', 'suffix_caffeine'), - ("Reload CSS", 'prefix_css', 'suffix_css'), - ("Restart with inspector", 'prefix_restart_inspector', 'suffix_restart_inspector'), + (f"Reload {APP_NAME_CAP}", "prefix_restart", "suffix_restart"), + ("Message", "prefix_axmsg", "suffix_axmsg"), + ("Dashboard", "prefix_dash", "suffix_dash"), + ("Bluetooth", "prefix_bluetooth", "suffix_bluetooth"), + ("Pins", "prefix_pins", "suffix_pins"), + ("Kanban", "prefix_kanban", "suffix_kanban"), + ("App Launcher", "prefix_launcher", "suffix_launcher"), + ("Tmux", "prefix_tmux", "suffix_tmux"), + ("Clipboard History", "prefix_cliphist", "suffix_cliphist"), + ("Toolbox", "prefix_toolbox", "suffix_toolbox"), + ("Overview", "prefix_overview", "suffix_overview"), + ("Wallpapers", "prefix_wallpapers", "suffix_wallpapers"), + ("Random Wallpaper", "prefix_randwall", "suffix_randwall"), + ("Emoji Picker", "prefix_emoji", "suffix_emoji"), + ("Power Menu", "prefix_power", "suffix_power"), + ("Toggle Caffeine", "prefix_caffeine", "suffix_caffeine"), + ("Reload CSS", "prefix_css", "suffix_css"), + ( + "Restart with inspector", + "prefix_restart_inspector", + "suffix_restart_inspector", + ), ] for i, (label_text, prefix_key, suffix_key) in enumerate(bindings): row = i + 1 binding_label = Label(label=label_text, h_align="start") keybind_grid.attach(binding_label, 0, row, 1, 1) - prefix_entry = Entry(text=bind_vars.get(prefix_key, '')) + prefix_entry = Entry(text=bind_vars.get(prefix_key, "")) keybind_grid.attach(prefix_entry, 1, row, 1, 1) plus_label = Label(label="+", h_align="center") keybind_grid.attach(plus_label, 2, row, 1, 1) - suffix_entry = Entry(text=bind_vars.get(suffix_key, '')) + suffix_entry = Entry(text=bind_vars.get(suffix_key, "")) keybind_grid.attach(suffix_entry, 3, row, 1, 1) self.entries.append((prefix_key, suffix_key, prefix_entry, suffix_entry)) @@ -147,12 +163,12 @@ class HyprConfGUI(Window): def create_appearance_tab(self): scrolled_window = ScrolledWindow( - h_scrollbar_policy="never", + h_scrollbar_policy="never", v_scrollbar_policy="automatic", h_expand=True, v_expand=True, propagate_width=False, - propagate_height=False + propagate_height=False, ) vbox = Box(orientation="v", spacing=15, style="margin: 15px;") @@ -168,15 +184,17 @@ class HyprConfGUI(Window): top_grid.attach(wall_header, 0, 0, 1, 1) wall_label = Label(label="Directory:", h_align="start", v_align="center") top_grid.attach(wall_label, 0, 1, 1, 1) - + chooser_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) chooser_container.set_halign(Gtk.Align.START) chooser_container.set_valign(Gtk.Align.CENTER) self.wall_dir_chooser = Gtk.FileChooserButton( title="Select a folder", action=Gtk.FileChooserAction.SELECT_FOLDER ) - self.wall_dir_chooser.set_tooltip_text("Select the directory containing your wallpaper images") - self.wall_dir_chooser.set_filename(bind_vars.get('wallpapers_dir','')) + self.wall_dir_chooser.set_tooltip_text( + "Select the directory containing your wallpaper images" + ) + self.wall_dir_chooser.set_filename(bind_vars.get("wallpapers_dir", "")) self.wall_dir_chooser.set_size_request(180, -1) chooser_container.add(self.wall_dir_chooser) top_grid.attach(chooser_container, 1, 1, 1, 1) @@ -184,32 +202,39 @@ class HyprConfGUI(Window): face_header = Label(markup="Profile Icon", h_align="start") top_grid.attach(face_header, 2, 0, 2, 1) current_face = os.path.expanduser("~/.face.icon") - face_image_container = Box(style_classes=["image-frame"], h_align="center", v_align="center") + face_image_container = Box( + style_classes=["image-frame"], h_align="center", v_align="center" + ) self.face_image = FabricImage(size=64) try: if os.path.exists(current_face): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(current_face, 64, 64) self.face_image.set_from_pixbuf(pixbuf) else: - self.face_image.set_from_icon_name("user-info", Gtk.IconSize.DIALOG) + self.face_image.set_from_icon_name("user-info", Gtk.IconSize.DIALOG) except Exception as e: print(f"Error loading face icon: {e}") self.face_image.set_from_icon_name("image-missing", Gtk.IconSize.DIALOG) face_image_container.add(self.face_image) top_grid.attach(face_image_container, 2, 1, 1, 1) - + browse_btn_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) browse_btn_container.set_halign(Gtk.Align.START) browse_btn_container.set_valign(Gtk.Align.CENTER) - face_btn = Button(label="Browse...", - tooltip_text="Select a square image for your profile icon", - on_clicked=self.on_select_face_icon) + face_btn = Button( + label="Browse...", + tooltip_text="Select a square image for your profile icon", + on_clicked=self.on_select_face_icon, + ) browse_btn_container.add(face_btn) top_grid.attach(browse_btn_container, 3, 1, 1, 1) self.face_status_label = Label(label="", h_align="start") top_grid.attach(self.face_status_label, 2, 2, 2, 1) - separator1 = Box(style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;", h_expand=True) + separator1 = Box( + style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;", + h_expand=True, + ) vbox.add(separator1) layout_header = Label(markup="Layout Options", h_align="start") @@ -223,13 +248,17 @@ class HyprConfGUI(Window): position_label = Label(label="Dock Position", h_align="start", v_align="center") layout_grid.attach(position_label, 0, 0, 1, 1) - position_combo_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) + position_combo_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) self.position_combo = Gtk.ComboBoxText() self.position_combo.set_tooltip_text("Select the position of the dock") positions = ["Bottom", "Left", "Right"] - for pos in positions: + for pos in positions: self.position_combo.append_text(pos) - current_position = bind_vars.get('dock_position', "Bottom") + current_position = bind_vars.get("dock_position", "Bottom") try: self.position_combo.set_active(positions.index(current_position)) except ValueError: @@ -240,12 +269,16 @@ class HyprConfGUI(Window): dock_theme_label = Label(label="Dock Theme", h_align="start", v_align="center") layout_grid.attach(dock_theme_label, 2, 0, 1, 1) - dock_theme_combo_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) + dock_theme_combo_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) self.dock_theme_combo = Gtk.ComboBoxText() self.dock_theme_combo.set_tooltip_text("Select the visual theme for the dock") for theme in self.themes: self.dock_theme_combo.append_text(theme) - current_dock_theme = bind_vars.get('dock_theme', "Pills") + current_dock_theme = bind_vars.get("dock_theme", "Pills") try: self.dock_theme_combo.set_active(self.themes.index(current_dock_theme)) except ValueError: @@ -255,50 +288,87 @@ class HyprConfGUI(Window): dock_label = Label(label="Show Dock", h_align="start", v_align="center") layout_grid.attach(dock_label, 0, 1, 1, 1) - dock_switch_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - self.dock_switch = Gtk.Switch(active=bind_vars.get('dock_enabled', True)) + dock_switch_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + self.dock_switch = Gtk.Switch(active=bind_vars.get("dock_enabled", True)) self.dock_switch.connect("notify::active", self.on_dock_enabled_changed) dock_switch_container.add(self.dock_switch) layout_grid.attach(dock_switch_container, 1, 1, 1, 1) - dock_auto_hide_label = Label(label="Auto-hide Dock", h_align="start", v_align="center") + dock_auto_hide_label = Label( + label="Auto-hide Dock", h_align="start", v_align="center" + ) layout_grid.attach(dock_auto_hide_label, 0, 2, 1, 1) - dock_auto_hide_switch_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - self.dock_auto_hide_switch = Gtk.Switch(active=bind_vars.get('dock_auto_hide', False), sensitive=self.dock_switch.get_active()) + dock_auto_hide_switch_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + self.dock_auto_hide_switch = Gtk.Switch( + active=bind_vars.get("dock_auto_hide", False), + sensitive=self.dock_switch.get_active(), + ) dock_auto_hide_switch_container.add(self.dock_auto_hide_switch) layout_grid.attach(dock_auto_hide_switch_container, 1, 2, 1, 1) - dock_hover_label = Label(label="Show Dock Only on Hover", h_align="start", v_align="center") + dock_hover_label = Label( + label="Show Dock Only on Hover", h_align="start", v_align="center" + ) layout_grid.attach(dock_hover_label, 2, 1, 1, 1) - dock_hover_switch_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - self.dock_hover_switch = Gtk.Switch(active=bind_vars.get('dock_always_occluded', False), sensitive=self.dock_switch.get_active()) + dock_hover_switch_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + self.dock_hover_switch = Gtk.Switch( + active=bind_vars.get("dock_always_occluded", False), + sensitive=self.dock_switch.get_active(), + ) dock_hover_switch_container.add(self.dock_hover_switch) layout_grid.attach(dock_hover_switch_container, 3, 1, 1, 1) - - notification_pos_label = Label(label="Notification Position", h_align="start", v_align="center") + + notification_pos_label = Label( + label="Notification Position", h_align="start", v_align="center" + ) layout_grid.attach(notification_pos_label, 0, 7, 1, 1) - notification_pos_combo_container = Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - + notification_pos_combo_container = Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + self.notification_pos_combo = Gtk.ComboBoxText() - self.notification_pos_combo.set_tooltip_text("Select where notifications appear on the screen.") - + self.notification_pos_combo.set_tooltip_text( + "Select where notifications appear on the screen." + ) + notification_positions_list = ["Top", "Bottom"] for pos in notification_positions_list: self.notification_pos_combo.append_text(pos) - + current_notif_pos = bind_vars.get(NOTIF_POS_KEY, NOTIF_POS_DEFAULT) try: - self.notification_pos_combo.set_active(notification_positions_list.index(current_notif_pos)) + self.notification_pos_combo.set_active( + notification_positions_list.index(current_notif_pos) + ) except ValueError: self.notification_pos_combo.set_active(0) - self.notification_pos_combo.connect("changed", self.on_notification_position_changed) - + self.notification_pos_combo.connect( + "changed", self.on_notification_position_changed + ) + notification_pos_combo_container.add(self.notification_pos_combo) layout_grid.attach(notification_pos_combo_container, 1, 7, 3, 1) - separator2 = Box(style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;", h_expand=True) + separator2 = Box( + style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;", + h_expand=True, + ) vbox.add(separator2) components_header = Label(markup="Modules", h_align="start") @@ -312,65 +382,77 @@ class HyprConfGUI(Window): self.component_switches = {} component_display_names = { - 'workspace':"Workspaces", - 'metrics':"System Metrics", - 'date_time':"Date & Time", - 'battery':"Battery Indicator", - 'controls':"Control Panel", - 'indicators':"Indicators", - 'systray':"System Tray", + "workspace": "Workspaces", + "metrics": "System Metrics", + "date_time": "Date & Time", + "battery": "Battery Indicator", + "controls": "Control Panel", + "indicators": "Indicators", } - self.corners_switch = Gtk.Switch(active=bind_vars.get('corners_visible', True)) + self.corners_switch = Gtk.Switch(active=bind_vars.get("corners_visible", True)) num_components = len(component_display_names) + 1 rows_per_column = (num_components + 1) // 2 - - corners_label = Label(label="Rounded Corners", h_align="start", v_align="center") + + corners_label = Label( + label="Rounded Corners", h_align="start", v_align="center" + ) components_grid.attach(corners_label, 0, 0, 1, 1) - switch_container_corners = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) + switch_container_corners = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) switch_container_corners.add(self.corners_switch) components_grid.attach(switch_container_corners, 1, 0, 1, 1) - + current_row = 0 current_col = 0 item_idx = 0 for i, (name, display) in enumerate(component_display_names.items()): - if item_idx < (rows_per_column -1) : - row = item_idx + 1 + if item_idx < (rows_per_column - 1): + row = item_idx + 1 col = 0 else: - row = (item_idx - (rows_per_column -1)) + row = item_idx - (rows_per_column - 1) col = 2 - + component_label = Label(label=display, h_align="start", v_align="center") components_grid.attach(component_label, col, row, 1, 1) - - switch_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - component_switch = Gtk.Switch(active=bind_vars.get(f'bar_{name}_visible', True)) + + switch_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + component_switch = Gtk.Switch( + active=bind_vars.get(f"dock_{name}_visible", True) + ) switch_container.add(component_switch) components_grid.attach(switch_container, col + 1, row, 1, 1) self.component_switches[name] = component_switch - item_idx +=1 - + item_idx += 1 return scrolled_window - - def on_notification_position_changed(self, combo: Gtk.ComboBoxText): selected_text = combo.get_active_text() if selected_text: bind_vars[NOTIF_POS_KEY] = selected_text - print(f"Notification position updated in bind_vars: {bind_vars[NOTIF_POS_KEY]}") + print( + f"Notification position updated in bind_vars: { + bind_vars[NOTIF_POS_KEY] + }" + ) def create_system_tab(self): scrolled_window = ScrolledWindow( - h_scrollbar_policy="never", + h_scrollbar_policy="never", v_scrollbar_policy="automatic", h_expand=True, v_expand=True, propagate_width=False, - propagate_height=False + propagate_height=False, ) vbox = Box(orientation="v", spacing=15, style="margin: 15px;") @@ -387,12 +469,15 @@ class HyprConfGUI(Window): terminal_label = Label(label="Command:", h_align="start", v_align="center") system_grid.attach(terminal_label, 0, 1, 1, 1) self.terminal_entry = Entry( - text=bind_vars.get('terminal_command', "kitty -e"), + text=bind_vars.get("terminal_command", "kitty -e"), tooltip_text="Command used to launch terminal apps (e.g., 'kitty -e')", - h_expand=True + h_expand=True, ) system_grid.attach(self.terminal_entry, 1, 1, 1, 1) - hint_label = Label(markup="Examples: 'kitty -e', 'alacritty -e', 'foot -e'", h_align="start") + hint_label = Label( + markup="Examples: 'kitty -e', 'alacritty -e', 'foot -e'", + h_align="start", + ) system_grid.attach(hint_label, 0, 2, 2, 1) hypr_header = Label(markup="Hyprland Integration", h_align="start") @@ -400,63 +485,108 @@ class HyprConfGUI(Window): row = 1 self.lock_switch = None if self.show_lock_checkbox: - lock_label = Label(label="Replace Hyprlock config", h_align="start", v_align="center") + lock_label = Label( + label="Replace Hyprlock config", h_align="start", v_align="center" + ) system_grid.attach(lock_label, 2, row, 1, 1) - lock_switch_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - self.lock_switch = Gtk.Switch(tooltip_text="Replace Hyprlock configuration with Ax-Shell's custom config") + lock_switch_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + self.lock_switch = Gtk.Switch( + tooltip_text="Replace Hyprlock configuration with Ax-Shell's custom config" + ) lock_switch_container.add(self.lock_switch) system_grid.attach(lock_switch_container, 3, row, 1, 1) row += 1 self.idle_switch = None if self.show_idle_checkbox: - idle_label = Label(label="Replace Hypridle config", h_align="start", v_align="center") + idle_label = Label( + label="Replace Hypridle config", h_align="start", v_align="center" + ) system_grid.attach(idle_label, 2, row, 1, 1) - idle_switch_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - self.idle_switch = Gtk.Switch(tooltip_text="Replace Hypridle configuration with Ax-Shell's custom config") + idle_switch_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + self.idle_switch = Gtk.Switch( + tooltip_text="Replace Hypridle configuration with Ax-Shell's custom config" + ) idle_switch_container.add(self.idle_switch) system_grid.attach(idle_switch_container, 3, row, 1, 1) row += 1 if self.show_lock_checkbox or self.show_idle_checkbox: - note_label = Label(markup="Existing configs will be backed up", h_align="start") + note_label = Label( + markup="Existing configs will be backed up", + h_align="start", + ) system_grid.attach(note_label, 2, row, 2, 1) metrics_header = Label(markup="System Metrics Options", h_align="start") vbox.add(metrics_header) - metrics_grid = Gtk.Grid(column_spacing=15, row_spacing=8, margin_start=10, margin_top=5) + metrics_grid = Gtk.Grid( + column_spacing=15, row_spacing=8, margin_start=10, margin_top=5 + ) vbox.add(metrics_grid) self.metrics_switches = {} - metric_names = {'cpu': "CPU", 'ram': "RAM", 'disk': "Disk", 'swap': "Swap", 'gpu': "GPU"} + metric_names = { + "cpu": "CPU", + "ram": "RAM", + "disk": "Disk", + "swap": "Swap", + "gpu": "GPU", + } metrics_grid.attach(Label(label="Show in Metrics", h_align="start"), 0, 0, 1, 1) for i, (key, label_text) in enumerate(metric_names.items()): - switch = Gtk.Switch(active=bind_vars.get('metrics_visible', {}).get(key, True)) + switch = Gtk.Switch( + active=bind_vars.get("metrics_visible", {}).get(key, True) + ) self.metrics_switches[key] = switch - metrics_grid.attach(Label(label=label_text, h_align="start"), 0, i + 1, 1, 1) + metrics_grid.attach( + Label(label=label_text, h_align="start"), 0, i + 1, 1, 1 + ) metrics_grid.attach(switch, 1, i + 1, 1, 1) - def enforce_minimum_metrics(switch_dict): enabled_switches = [s for s in switch_dict.values() if s.get_active()] can_disable = len(enabled_switches) > 1 for s in switch_dict.values(): s.set_sensitive(True if can_disable or not s.get_active() else False) - def on_metric_toggle(switch, gparam, switch_dict): enforce_minimum_metrics(switch_dict) - for k_s, s_s in self.metrics_switches.items(): s_s.connect("notify::active", on_metric_toggle, self.metrics_switches) + def on_metric_toggle(switch, gparam, switch_dict): + enforce_minimum_metrics(switch_dict) + + for k_s, s_s in self.metrics_switches.items(): + s_s.connect("notify::active", on_metric_toggle, self.metrics_switches) enforce_minimum_metrics(self.metrics_switches) - - disks_label = Label(label="Disk directories for Metrics", h_align="start", v_align="center") + + disks_label = Label( + label="Disk directories for Metrics", h_align="start", v_align="center" + ) vbox.add(disks_label) self.disk_entries = Box(orientation="v", spacing=8, h_align="start") - - self._create_disk_edit_entry_func = lambda path: self._add_disk_entry_widget(path) - for p in bind_vars.get('bar_metrics_disks', ["/"]): self._create_disk_edit_entry_func(p) + self._create_disk_edit_entry_func = lambda path: self._add_disk_entry_widget( + path + ) + + for p in bind_vars.get("metrics_disks", ["/"]): + self._create_disk_edit_entry_func(p) vbox.add(self.disk_entries) - - add_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.START, valign=Gtk.Align.CENTER) - add_btn = Button(label="Add new disk", on_clicked=lambda _: self._create_disk_edit_entry_func("/")) + + add_container = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + halign=Gtk.Align.START, + valign=Gtk.Align.CENTER, + ) + add_btn = Button( + label="Add new disk", + on_clicked=lambda _: self._create_disk_edit_entry_func("/"), + ) add_container.add(add_btn) vbox.add(add_container) @@ -468,23 +598,52 @@ class HyprConfGUI(Window): entry = Entry(text=path, h_expand=True) bar.add(entry) x_btn = Button(label="X") - x_btn.connect("clicked", lambda _, current_bar_to_remove=bar: self.disk_entries.remove(current_bar_to_remove)) + x_btn.connect( + "clicked", + lambda _, current_bar_to_remove=bar: self.disk_entries.remove( + current_bar_to_remove + ), + ) bar.add(x_btn) self.disk_entries.add(bar) self.disk_entries.show_all() - def create_about_tab(self): vbox = Box(orientation="v", spacing=18, style="margin: 30px;") - vbox.add(Label(markup=f"{APP_NAME_CAP}", h_align="start", style="font-size: 1.5em; margin-bottom: 8px;")) - vbox.add(Label(label="A hackable shell for Hyprland, powered by Fabric.", h_align="start", style="margin-bottom: 12px;")) + vbox.add( + Label( + markup=f"{APP_NAME_CAP}", + h_align="start", + style="font-size: 1.5em; margin-bottom: 8px;", + ) + ) + vbox.add( + Label( + label="A hackable shell for Hyprland, powered by Fabric.", + h_align="start", + style="margin-bottom: 12px;", + ) + ) repo_box = Box(orientation="h", spacing=6, h_align="start") repo_label = Label(label="GitHub:", h_align="start") - repo_link = Label(markup='https://github.com/S4NKALP/Modus') - repo_box.add(repo_label); repo_box.add(repo_link) + repo_link = Label( + markup='https://github.com/S4NKALP/Modus' + ) + repo_box.add(repo_label) + repo_box.add(repo_link) vbox.add(repo_box) - def on_kofi_clicked(_): import webbrowser; webbrowser.open("https://ko-fi.com/S4NKALP") - kofi_btn = Button(label="Support on Ko-Fi ❤️", on_clicked=on_kofi_clicked, tooltip_text="Support S4NKALP on Ko-Fi", style="margin-top: 18px; min-width: 160px;") + + def on_kofi_clicked(_): + import webbrowser + + webbrowser.open("https://ko-fi.com/S4NKALP") + + kofi_btn = Button( + label="Support on Ko-Fi ❤️", + on_clicked=on_kofi_clicked, + tooltip_text="Support S4NKALP on Ko-Fi", + style="margin-top: 18px; min-width: 160px;", + ) vbox.add(kofi_btn) vbox.add(Box(v_expand=True)) return vbox @@ -492,7 +651,8 @@ class HyprConfGUI(Window): def on_ws_num_changed(self, switch, gparam): is_active = switch.get_active() self.ws_chinese_switch.set_sensitive(is_active) - if not is_active: self.ws_chinese_switch.set_active(False) + if not is_active: + self.ws_chinese_switch.set_active(False) def on_position_changed(self, combo): position = combo.get_active_text() @@ -511,43 +671,67 @@ class HyprConfGUI(Window): def on_select_face_icon(self, widget): dialog = Gtk.FileChooserDialog( - title="Select Face Icon", transient_for=self.get_toplevel(), action=Gtk.FileChooserAction.OPEN + title="Select Face Icon", + transient_for=self.get_toplevel(), + action=Gtk.FileChooserAction.OPEN, + ) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, ) - dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) image_filter = Gtk.FileFilter() image_filter.set_name("Image files") - for mime in ["image/png", "image/jpeg"]: image_filter.add_mime_type(mime) - for pattern in ["*.png", "*.jpg", "*.jpeg"]: image_filter.add_pattern(pattern) + for mime in ["image/png", "image/jpeg"]: + image_filter.add_mime_type(mime) + for pattern in ["*.png", "*.jpg", "*.jpeg"]: + image_filter.add_pattern(pattern) dialog.add_filter(image_filter) if dialog.run() == Gtk.ResponseType.OK: self.selected_face_icon = dialog.get_filename() - self.face_status_label.label = f"Selected: {os.path.basename(self.selected_face_icon)}" + self.face_status_label.label = ( + f"Selected: {os.path.basename(self.selected_face_icon)}" + ) try: - pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(self.selected_face_icon, 64, 64) + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( + self.selected_face_icon, 64, 64 + ) self.face_image.set_from_pixbuf(pixbuf) except Exception as e: - print(f"Error loading selected face icon preview: {e}") - self.face_image.set_from_icon_name("image-missing", Gtk.IconSize.DIALOG) + print(f"Error loading selected face icon preview: {e}") + self.face_image.set_from_icon_name("image-missing", Gtk.IconSize.DIALOG) dialog.destroy() def on_accept(self, widget): - - current_bind_vars_snapshot = {} + current_bind_vars_snapshot = {} for prefix_key, suffix_key, prefix_entry, suffix_entry in self.entries: current_bind_vars_snapshot[prefix_key] = prefix_entry.get_text() current_bind_vars_snapshot[suffix_key] = suffix_entry.get_text() - current_bind_vars_snapshot['wallpapers_dir'] = self.wall_dir_chooser.get_filename() - - current_bind_vars_snapshot['dock_position'] = self.position_combo.get_active_text() - current_bind_vars_snapshot['vertical'] = current_bind_vars_snapshot['dock_position'] in ["Left", "Right"] - - current_bind_vars_snapshot['dock_enabled'] = self.dock_switch.get_active() - current_bind_vars_snapshot['dock_auto_hide'] = self.dock_auto_hide_switch.get_active() - current_bind_vars_snapshot['dock_always_occluded'] = self.dock_hover_switch.get_active() - current_bind_vars_snapshot['terminal_command'] = self.terminal_entry.get_text() - current_bind_vars_snapshot['corners_visible'] = self.corners_switch.get_active() - current_bind_vars_snapshot['dock_theme'] = self.dock_theme_combo.get_active_text() + current_bind_vars_snapshot["wallpapers_dir"] = ( + self.wall_dir_chooser.get_filename() + ) + + current_bind_vars_snapshot["dock_position"] = ( + self.position_combo.get_active_text() + ) + current_bind_vars_snapshot["vertical"] = current_bind_vars_snapshot[ + "dock_position" + ] in ["Left", "Right"] + + current_bind_vars_snapshot["dock_enabled"] = self.dock_switch.get_active() + current_bind_vars_snapshot["dock_auto_hide"] = ( + self.dock_auto_hide_switch.get_active() + ) + current_bind_vars_snapshot["dock_always_occluded"] = ( + self.dock_hover_switch.get_active() + ) + current_bind_vars_snapshot["terminal_command"] = self.terminal_entry.get_text() + current_bind_vars_snapshot["corners_visible"] = self.corners_switch.get_active() + current_bind_vars_snapshot["dock_theme"] = ( + self.dock_theme_combo.get_active_text() + ) selected_notif_pos_text = self.notification_pos_combo.get_active_text() if selected_notif_pos_text: @@ -556,88 +740,112 @@ class HyprConfGUI(Window): current_bind_vars_snapshot[NOTIF_POS_KEY] = NOTIF_POS_DEFAULT for component_name, switch in self.component_switches.items(): - current_bind_vars_snapshot[f'bar_{component_name}_visible'] = switch.get_active() + current_bind_vars_snapshot[f"dock_{component_name}_visible"] = ( + switch.get_active() + ) - current_bind_vars_snapshot['metrics_visible'] = {k: s.get_active() for k, s in self.metrics_switches.items()} - current_bind_vars_snapshot['metrics_disks'] = [ - child.get_children()[0].get_text() for child in self.disk_entries.get_children() if isinstance(child, Gtk.Box) and child.get_children() and isinstance(child.get_children()[0], Entry) + current_bind_vars_snapshot["metrics_visible"] = { + k: s.get_active() for k, s in self.metrics_switches.items() + } + current_bind_vars_snapshot["metrics_disks"] = [ + child.get_children()[0].get_text() + for child in self.disk_entries.get_children() + if isinstance(child, Gtk.Box) + and child.get_children() + and isinstance(child.get_children()[0], Entry) ] - selected_icon_path = self.selected_face_icon replace_lock = self.lock_switch and self.lock_switch.get_active() replace_idle = self.idle_switch and self.idle_switch.get_active() if self.selected_face_icon: - self.selected_face_icon = None - self.face_status_label.label = "" + self.selected_face_icon = None + self.face_status_label.label = "" def _apply_and_reload_task_thread(): - nonlocal current_bind_vars_snapshot - + nonlocal current_bind_vars_snapshot + from . import utils + utils.bind_vars.clear() utils.bind_vars.update(current_bind_vars_snapshot) - start_time = time.time() print(f"{start_time:.4f}: Background task started.") - config_json = os.path.expanduser(f'~/{APP_NAME_CAP}/config/config.json') + config_json = os.path.expanduser(f"~/{APP_NAME_CAP}/config/config.json") os.makedirs(os.path.dirname(config_json), exist_ok=True) try: - with open(config_json, 'w') as f: - json.dump(utils.bind_vars, f, indent=4) + with open(config_json, "w") as f: + json.dump(utils.bind_vars, f, indent=4) print(f"{time.time():.4f}: Saved config.json.") - except Exception as e: print(f"Error saving config.json: {e}") + except Exception as e: + print(f"Error saving config.json: {e}") if selected_icon_path: print(f"{time.time():.4f}: Processing face icon...") try: img = Image.open(selected_icon_path) - side = min(img.size); left = (img.width - side)//2; top = (img.height - side)//2 + side = min(img.size) + left = (img.width - side) // 2 + top = (img.height - side) // 2 cropped_img = img.crop((left, top, left + side, top + side)) face_icon_dest = os.path.expanduser("~/.face.icon") - cropped_img.save(face_icon_dest, format='PNG') + cropped_img.save(face_icon_dest, format="PNG") print(f"{time.time():.4f}: Face icon saved to {face_icon_dest}") GLib.idle_add(self._update_face_image_widget, face_icon_dest) - except Exception as e: print(f"Error processing face icon: {e}") + except Exception as e: + print(f"Error processing face icon: {e}") print(f"{time.time():.4f}: Finished processing face icon.") if replace_lock: print(f"{time.time():.4f}: Replacing hyprlock config...") src = os.path.expanduser(f"~/{APP_NAME_CAP}/config/hypr/hyprlock.conf") dest = os.path.expanduser("~/.config/hypr/hyprlock.conf") - if os.path.exists(src): backup_and_replace(src, dest, "Hyprlock") - else: print(f"Warning: Source hyprlock config not found at {src}") + if os.path.exists(src): + backup_and_replace(src, dest, "Hyprlock") + else: + print(f"Warning: Source hyprlock config not found at {src}") print(f"{time.time():.4f}: Finished replacing hyprlock config.") if replace_idle: print(f"{time.time():.4f}: Replacing hypridle config...") src = os.path.expanduser(f"~/{APP_NAME_CAP}/config/hypr/hypridle.conf") dest = os.path.expanduser("~/.config/hypr/hypridle.conf") - if os.path.exists(src): backup_and_replace(src, dest, "Hypridle") - else: print(f"Warning: Source hypridle config not found at {src}") + if os.path.exists(src): + backup_and_replace(src, dest, "Hypridle") + else: + print(f"Warning: Source hypridle config not found at {src}") print(f"{time.time():.4f}: Finished replacing hypridle config.") - print(f"{time.time():.4f}: Checking/Appending hyprland.conf source string...") + print( + f"{time.time():.4f}: Checking/Appending hyprland.conf source string..." + ) hypr_path = os.path.expanduser("~/.config/hypr/hyprland.conf") try: from .constants import SOURCE_STRING + needs_append = True if os.path.exists(hypr_path): with open(hypr_path, "r") as f: - if SOURCE_STRING.strip() in f.read(): needs_append = False - else: os.makedirs(os.path.dirname(hypr_path), exist_ok=True) - + if SOURCE_STRING.strip() in f.read(): + needs_append = False + else: + os.makedirs(os.path.dirname(hypr_path), exist_ok=True) + if needs_append: - with open(hypr_path, "a") as f: f.write("\n" + SOURCE_STRING) + with open(hypr_path, "a") as f: + f.write("\n" + SOURCE_STRING) print(f"Appended source string to {hypr_path}") - except Exception as e: print(f"Error updating {hypr_path}: {e}") - print(f"{time.time():.4f}: Finished checking/appending hyprland.conf source string.") + except Exception as e: + print(f"Error updating {hypr_path}: {e}") + print( + f"{time.time():.4f}: Finished checking/appending hyprland.conf source string." + ) print(f"{time.time():.4f}: Running start_config()...") - start_config() + start_config() print(f"{time.time():.4f}: Finished start_config().") print(f"{time.time():.4f}: Initiating Ax-Shell restart using Popen...") @@ -645,21 +853,39 @@ class HyprConfGUI(Window): kill_cmd = f"killall {APP_NAME}" start_cmd = ["uwsm", "app", "--", "python", main_py] try: - kill_proc = subprocess.Popen(kill_cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + kill_proc = subprocess.Popen( + kill_cmd, + shell=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) kill_proc.wait(timeout=2) print(f"{time.time():.4f}: killall process finished (o timed out).") - except subprocess.TimeoutExpired: print("Warning: killall command timed out.") - except Exception as e: print(f"Error running killall: {e}") - + except subprocess.TimeoutExpired: + print("Warning: killall command timed out.") + except Exception as e: + print(f"Error running killall: {e}") + try: - subprocess.Popen(start_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True) + subprocess.Popen( + start_cmd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + ) print(f"{APP_NAME_CAP} restart initiated via Popen.") - except FileNotFoundError as e: print(f"Error restarting {APP_NAME_CAP}: Command not found ({e})") - except Exception as e: print(f"Error restarting {APP_NAME_CAP} via Popen: {e}") - + except FileNotFoundError as e: + print(f"Error restarting {APP_NAME_CAP}: Command not found ({e})") + except Exception as e: + print(f"Error restarting {APP_NAME_CAP} via Popen: {e}") + print(f"{time.time():.4f}: Ax-Shell restart commands issued via Popen.") end_time = time.time() - print(f"{end_time:.4f}: Background task finished (Total: {end_time - start_time:.4f}s).") + print( + f"{end_time:.4f}: Background task finished (Total: { + end_time - start_time:.4f + }s)." + ) thread = threading.Thread(target=_apply_and_reload_task_thread) thread.daemon = True @@ -668,7 +894,7 @@ class HyprConfGUI(Window): def _update_face_image_widget(self, icon_path): try: - if self.face_image and self.face_image.get_window(): + if self.face_image and self.face_image.get_window(): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, 64, 64) self.face_image.set_from_pixbuf(pixbuf) except Exception as e: @@ -679,21 +905,26 @@ class HyprConfGUI(Window): def on_reset(self, widget): dialog = Gtk.MessageDialog( - transient_for=self.get_toplevel(), flags=0, message_type=Gtk.MessageType.QUESTION, - buttons=Gtk.ButtonsType.YES_NO, text="Reset all settings to defaults?" + transient_for=self.get_toplevel(), + flags=0, + message_type=Gtk.MessageType.QUESTION, + buttons=Gtk.ButtonsType.YES_NO, + text="Reset all settings to defaults?", + ) + dialog.format_secondary_text( + "This will reset all keybindings and appearance settings to their default values." ) - dialog.format_secondary_text("This will reset all keybindings and appearance settings to their default values.") response = dialog.run() dialog.hide() # Hide the dialog first dialog.destroy() # Then destroy it - + # Process any pending events while Gtk.events_pending(): Gtk.main_iteration() - + if response == Gtk.ResponseType.YES: from . import utils - from .constants import DEFAULTS + from .constants import DEFAULTS utils.bind_vars.clear() utils.bind_vars.update(DEFAULTS.copy()) @@ -702,72 +933,93 @@ class HyprConfGUI(Window): prefix_entry.set_text(utils.bind_vars[prefix_key]) suffix_entry.set_text(utils.bind_vars[suffix_key]) - self.wall_dir_chooser.set_filename(utils.bind_vars['wallpapers_dir']) - + self.wall_dir_chooser.set_filename(utils.bind_vars["wallpapers_dir"]) + positions = ["Bottom", "Left", "Right"] - default_position = DEFAULTS.get('dock_position', "Top") + default_position = DEFAULTS.get("dock_position", "Top") try: self.position_combo.set_active(positions.index(default_position)) except ValueError: self.position_combo.set_active(0) - - - self.dock_switch.set_active(utils.bind_vars.get('dock_enabled', True)) - self.dock_auto_hide_switch.set_active(utils.bind_vars.get('dock_auto_hide', False)) - self.dock_hover_switch.set_active(utils.bind_vars.get('dock_always_occluded', False)) + + self.dock_switch.set_active(utils.bind_vars.get("dock_enabled", True)) + self.dock_auto_hide_switch.set_active( + utils.bind_vars.get("dock_auto_hide", False) + ) + self.dock_hover_switch.set_active( + utils.bind_vars.get("dock_always_occluded", False) + ) self.dock_hover_switch.set_sensitive(self.dock_switch.get_active()) self.dock_auto_hide_switch.set_sensitive(self.dock_switch.get_active()) - self.terminal_entry.set_text(utils.bind_vars['terminal_command']) - - default_dock_theme_val = DEFAULTS.get('dock_theme', "Pills") + self.terminal_entry.set_text(utils.bind_vars["terminal_command"]) + + default_dock_theme_val = DEFAULTS.get("dock_theme", "Pills") try: - self.dock_theme_combo.set_active(self.themes.index(default_dock_theme_val)) + self.dock_theme_combo.set_active( + self.themes.index(default_dock_theme_val) + ) except ValueError: self.dock_theme_combo.set_active(0) default_notif_pos_val = DEFAULTS.get(NOTIF_POS_KEY, NOTIF_POS_DEFAULT) notification_positions_list = ["Top", "Bottom"] try: - self.notification_pos_combo.set_active(notification_positions_list.index(default_notif_pos_val)) + self.notification_pos_combo.set_active( + notification_positions_list.index(default_notif_pos_val) + ) except ValueError: self.notification_pos_combo.set_active(0) for name, switch in self.component_switches.items(): - switch.set_active(utils.bind_vars.get(f'bar_{name}_visible', True)) - self.corners_switch.set_active(utils.bind_vars.get('corners_visible', True)) + switch.set_active(utils.bind_vars.get(f"dock_{name}_visible", True)) + self.corners_switch.set_active(utils.bind_vars.get("corners_visible", True)) + + metrics_vis_defaults = DEFAULTS.get("metrics_visible", {}) + for k, s_widget in self.metrics_switches.items(): + s_widget.set_active(metrics_vis_defaults.get(k, True)) - metrics_vis_defaults = DEFAULTS.get('metrics_visible', {}) - for k, s_widget in self.metrics_switches.items(): s_widget.set_active(metrics_vis_defaults.get(k, True)) - - def enforce_minimum_metrics(switch_dict): enabled_switches = [s for s in switch_dict.values() if s.get_active()] can_disable = len(enabled_switches) > 1 for s in switch_dict.values(): - s.set_sensitive(True if can_disable or not s.get_active() else False) + s.set_sensitive( + True if can_disable or not s.get_active() else False + ) + enforce_minimum_metrics(self.metrics_switches) + for child in list(self.disk_entries.get_children()): + self.disk_entries.remove(child) - for child in list(self.disk_entries.get_children()): self.disk_entries.remove(child) - - for p in DEFAULTS.get('metrics_disks', ["/"]): + for p in DEFAULTS.get("metrics_disks", ["/"]): self._add_disk_edit_entry_func(p) self._update_panel_position_sensitivity() self.selected_face_icon = None self.face_status_label.label = "" - current_face = os.path.expanduser("~/.face.icon") + current_face = os.path.expanduser("~/.face.icon") try: - pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(current_face, 64, 64) if os.path.exists(current_face) else None - if pixbuf: self.face_image.set_from_pixbuf(pixbuf) - else: self.face_image.set_from_icon_name("user-info", Gtk.IconSize.DIALOG) - except Exception: self.face_image.set_from_icon_name("image-missing", Gtk.IconSize.DIALOG) + pixbuf = ( + GdkPixbuf.Pixbuf.new_from_file_at_size(current_face, 64, 64) + if os.path.exists(current_face) + else None + ) + if pixbuf: + self.face_image.set_from_pixbuf(pixbuf) + else: + self.face_image.set_from_icon_name("user-info", Gtk.IconSize.DIALOG) + except Exception: + self.face_image.set_from_icon_name("image-missing", Gtk.IconSize.DIALOG) - if self.lock_switch: self.lock_switch.set_active(False) - if self.idle_switch: self.idle_switch.set_active(False) + if self.lock_switch: + self.lock_switch.set_active(False) + if self.idle_switch: + self.idle_switch.set_active(False) print("Settings reset to defaults.") def on_close(self, widget): - if self.application: self.application.quit() - else: self.destroy() + if self.application: + self.application.quit() + else: + self.destroy() diff --git a/modules/dock/components.py b/modules/dock/components.py index 5ad55ca..0a423ee 100644 --- a/modules/dock/components.py +++ b/modules/dock/components.py @@ -1,23 +1,21 @@ -import os import json +import os + import config.data as data from fabric.widgets.box import Box from fabric.widgets.button import Button from fabric.widgets.datetime import DateTime from modules.dock.battery import Battery -from modules.dock.metrics import Metrics from modules.dock.controls import Controls -from modules.dock.workspaces import create_workspace_widget from modules.dock.indicators import Indicators +from modules.dock.metrics import Metrics +from modules.dock.workspaces import create_workspace_widget -class DockSystemTray(Box): +class DockComponents(Box): def __init__(self, orientation_val="h", **kwargs): super().__init__( - name="system-tray", - orientation=orientation_val, - spacing=4, - **kwargs + name="dock-components", orientation=orientation_val, spacing=4, **kwargs ) # Initialize component visibility from data @@ -57,7 +55,6 @@ class DockSystemTray(Box): self.add(self.battery) self.add(self.date_time) - # Apply initial visibility self.apply_component_props() @@ -86,16 +83,24 @@ class DockSystemTray(Box): } if component_name in components and component_name in self.component_visibility: - self.component_visibility[component_name] = not self.component_visibility[component_name] - components[component_name].set_visible(self.component_visibility[component_name]) + self.component_visibility[component_name] = not self.component_visibility[ + component_name + ] + components[component_name].set_visible( + self.component_visibility[component_name] + ) - config_file = os.path.expanduser(f"~/.config/{data.APP_NAME}/config/config.json") + config_file = os.path.expanduser( + f"~/.config/{data.APP_NAME}/config/config.json" + ) if os.path.exists(config_file): try: with open(config_file, "r") as f: config = json.load(f) - config[f"dock_{component_name}_visible"] = self.component_visibility[component_name] + config[f"dock_{component_name}_visible"] = ( + self.component_visibility[component_name] + ) with open(config_file, "w") as f: json.dump(config, f, indent=4) @@ -104,4 +109,4 @@ class DockSystemTray(Box): return self.component_visibility[component_name] - return None \ No newline at end of file + return None diff --git a/modules/dock/main.py b/modules/dock/main.py index f18926e..6560549 100644 --- a/modules/dock/main.py +++ b/modules/dock/main.py @@ -19,7 +19,7 @@ from fabric.widgets.image import Image from fabric.widgets.revealer import Revealer from gi.repository import Gdk, GLib, Gtk from modules.corners import MyCorner -from modules.dock.components import DockSystemTray +from modules.dock.components import DockComponents from utils.icon_resolver import IconResolver from utils.occlusion import check_occlusion from utils.wayland import WaylandWindow as Window @@ -146,24 +146,20 @@ class Dock(Window): self.dock_eventbox.connect("enter-notify-event", self._on_dock_enter) self.dock_eventbox.connect("leave-notify-event", self._on_dock_leave) - # Create system tray section + # Create components section separator_orientation = ( Gtk.Orientation.VERTICAL if self.view.get_orientation() == Gtk.Orientation.HORIZONTAL else Gtk.Orientation.HORIZONTAL ) - - # Initialize system tray with proper orientation - self.system_tray = DockSystemTray( - orientation_val=dock_wrapper_orientation_val - ) - # Initialize system tray with proper orientation - self.system_tray = DockSystemTray( - orientation_val=dock_wrapper_orientation_val - ) + # Initialize components with proper orientation + self.components = DockComponents(orientation_val=dock_wrapper_orientation_val) - # Add system tray to dock + # Initialize components with proper orientation + self.components = DockComponents(orientation_val=dock_wrapper_orientation_val) + + # Add components to dock if data.DOCK_POSITION == "Right": self.view.add( Box( @@ -175,9 +171,9 @@ class Dock(Window): name="dock-separator", ) ) - self.view.add(self.system_tray) + self.view.add(self.components) else: - self.view.pack_start(self.system_tray, False, False, 0) + self.view.pack_start(self.components, False, False, 0) self.view.add( Box( orientation=separator_orientation, @@ -765,9 +761,9 @@ class Dock(Window): name="dock-separator", ) ) - children.append(self.system_tray) + children.append(self.components) else: # Bottom or Left position - children.append(self.system_tray) + children.append(self.components) if pinned_buttons or open_buttons: children.append( Box( @@ -917,16 +913,15 @@ class Dock(Window): seat = display.get_default_seat() pointer = seat.get_pointer() window = self.get_window() - + if window: screen, x, y = pointer.get_position() # Fix: Unpack all three values win_x, win_y = window.get_position() win_w = window.get_width() win_h = window.get_height() - + # If pointer is outside dock window bounds - if (x < win_x or x > win_x + win_w or - y < win_y or y > win_y + win_h): + if x < win_x or x > win_x + win_w or y < win_y or y > win_y + win_h: app_id_dragged = widget.app_identifier instances_dragged = widget.instances @@ -935,7 +930,9 @@ class Dock(Window): if isinstance(app_id_dragged, dict) and isinstance( pinned_app_item, dict ): - if app_id_dragged.get("name") == pinned_app_item.get("name"): + if app_id_dragged.get("name") == pinned_app_item.get( + "name" + ): app_index_dragged = i break elif app_id_dragged == pinned_app_item: