✨ feat(config): add settings GUI tabs for appearance, keybindings, system, and about
This commit is contained in:
5 files changed
+1266
-1085
No files matched your search
@@ -0,0 +1,40 @@
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.label import Label
|
||||
|
||||
from config.data import APP_NAME_CAP
|
||||
|
||||
|
||||
class AboutTab:
|
||||
"""About information tab for settings"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def create_about_tab(self):
|
||||
"""Create the About tab content"""
|
||||
vbox = Box(orientation="v", spacing=18, style="margin: 30px;")
|
||||
vbox.add(
|
||||
Label(
|
||||
markup=f"<b>{APP_NAME_CAP}</b>",
|
||||
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='<a href="https://github.com/S4NKALP/Modus">https://github.com/S4NKALP/Modus</a>'
|
||||
)
|
||||
repo_box.add(repo_label)
|
||||
repo_box.add(repo_link)
|
||||
vbox.add(repo_box)
|
||||
|
||||
vbox.add(Box(v_expand=True))
|
||||
return vbox
|
||||
@@ -0,0 +1,689 @@
|
||||
import os
|
||||
import gi
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.image import Image as FabricImage
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from gi.repository import Gdk, GdkPixbuf, Gtk
|
||||
|
||||
from config.data import NOTIF_POS_DEFAULT, NOTIF_POS_KEY
|
||||
from config.settings.utils import bind_vars
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
|
||||
class AppearanceTab:
|
||||
"""Appearance and layout management tab for settings"""
|
||||
|
||||
def __init__(self, themes, parent_window=None):
|
||||
self.themes = themes
|
||||
self.parent_window = parent_window
|
||||
self.selected_face_icon = None
|
||||
|
||||
# Widget references
|
||||
self.wall_dir_chooser = None
|
||||
self.face_image = None
|
||||
self.face_status_label = None
|
||||
self.ws_mode_combo = None
|
||||
self.ws_chinese_switch = None
|
||||
self.position_combo = None
|
||||
self.dock_theme_combo = None
|
||||
self.icon_size_scale = None
|
||||
self.application_switcher_scale = None
|
||||
self.dock_switch = None
|
||||
self.dock_auto_hide_switch = None
|
||||
self.dock_hover_switch = None
|
||||
self.dock_hide_special_switch = None
|
||||
self.dock_hide_special_apps_switch = None
|
||||
self.notification_pos_combo = None
|
||||
self.component_switches = {}
|
||||
self.corners_switch = None
|
||||
|
||||
def create_appearance_tab(self):
|
||||
"""Create the Appearance tab content"""
|
||||
scrolled_window = ScrolledWindow(
|
||||
h_scrollbar_policy="never",
|
||||
v_scrollbar_policy="automatic",
|
||||
h_expand=False,
|
||||
v_expand=False,
|
||||
propagate_width=False,
|
||||
propagate_height=False,
|
||||
)
|
||||
# Set fixed size to match tab stack dimensions
|
||||
scrolled_window.set_size_request(580, 580)
|
||||
|
||||
vbox = Box(orientation="v", spacing=15, style="margin: 15px;")
|
||||
scrolled_window.add(vbox)
|
||||
|
||||
# Create wallpaper and profile icon section
|
||||
self._create_wallpaper_profile_section(vbox)
|
||||
|
||||
# Add separator
|
||||
separator1 = Box(
|
||||
style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;",
|
||||
h_expand=True,
|
||||
)
|
||||
vbox.add(separator1)
|
||||
|
||||
# Create layout options section
|
||||
self._create_layout_options_section(vbox)
|
||||
|
||||
# Add separator
|
||||
separator2 = Box(
|
||||
style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;",
|
||||
h_expand=True,
|
||||
)
|
||||
vbox.add(separator2)
|
||||
|
||||
# Create modules section
|
||||
self._create_modules_section(vbox)
|
||||
|
||||
return scrolled_window
|
||||
|
||||
def _create_wallpaper_profile_section(self, vbox):
|
||||
"""Create wallpaper and profile icon section"""
|
||||
top_grid = Gtk.Grid()
|
||||
top_grid.set_column_spacing(20)
|
||||
top_grid.set_row_spacing(5)
|
||||
top_grid.set_margin_bottom(10)
|
||||
vbox.add(top_grid)
|
||||
|
||||
# Wallpaper section
|
||||
wall_header = Label(markup="<b>Wallpapers</b>", h_align="start")
|
||||
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_size_request(180, -1)
|
||||
chooser_container.add(self.wall_dir_chooser)
|
||||
top_grid.attach(chooser_container, 1, 1, 1, 1)
|
||||
|
||||
# Profile icon section
|
||||
face_header = Label(markup="<b>Profile Icon</b>", 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"
|
||||
)
|
||||
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)
|
||||
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,
|
||||
)
|
||||
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)
|
||||
|
||||
def _create_layout_options_section(self, vbox):
|
||||
"""Create layout options section"""
|
||||
layout_header = Label(markup="<b>Layout Options</b>", h_align="start")
|
||||
vbox.add(layout_header)
|
||||
layout_grid = Gtk.Grid()
|
||||
layout_grid.set_column_spacing(20)
|
||||
layout_grid.set_row_spacing(10)
|
||||
layout_grid.set_margin_start(10)
|
||||
layout_grid.set_margin_top(5)
|
||||
vbox.add(layout_grid)
|
||||
|
||||
# Workspace mode
|
||||
ws_mode_label = Label(label="Workspace Mode", h_align="start", v_align="center")
|
||||
layout_grid.attach(ws_mode_label, 0, 0, 1, 1)
|
||||
|
||||
ws_mode_combo_container = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
halign=Gtk.Align.START,
|
||||
valign=Gtk.Align.CENTER,
|
||||
)
|
||||
self.ws_mode_combo = Gtk.ComboBoxText()
|
||||
self.ws_mode_combo.set_tooltip_text("Select how workspaces are displayed")
|
||||
ws_modes = ["Dots", "Numbers", "Single Button"]
|
||||
for mode in ws_modes:
|
||||
self.ws_mode_combo.append_text(mode)
|
||||
|
||||
# Set current mode based on config
|
||||
current_dots = bind_vars.get("workspace_dots", True)
|
||||
current_nums = bind_vars.get("workspace_nums", False)
|
||||
if current_dots:
|
||||
current_mode = "Dots"
|
||||
elif current_nums:
|
||||
current_mode = "Numbers"
|
||||
else:
|
||||
current_mode = "Single Button"
|
||||
|
||||
try:
|
||||
self.ws_mode_combo.set_active(ws_modes.index(current_mode))
|
||||
except ValueError:
|
||||
self.ws_mode_combo.set_active(0)
|
||||
|
||||
self.ws_mode_combo.connect("changed", self.on_ws_mode_changed)
|
||||
ws_mode_combo_container.add(self.ws_mode_combo)
|
||||
layout_grid.attach(ws_mode_combo_container, 1, 0, 1, 1)
|
||||
|
||||
# Chinese numerals option
|
||||
ws_chinese_label = Label(
|
||||
label="Use Chinese Numerals", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(ws_chinese_label, 2, 0, 1, 1)
|
||||
ws_chinese_switch_container = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
halign=Gtk.Align.START,
|
||||
valign=Gtk.Align.CENTER,
|
||||
)
|
||||
self.ws_chinese_switch = Gtk.Switch(
|
||||
active=bind_vars.get("workspace_use_chinese_numerals", False),
|
||||
sensitive=current_nums, # Only enabled when numbers mode is selected
|
||||
)
|
||||
self.ws_chinese_switch.set_tooltip_text(
|
||||
"Use Chinese numerals (一, 二, 三...) instead of Arabic numbers"
|
||||
)
|
||||
ws_chinese_switch_container.add(self.ws_chinese_switch)
|
||||
layout_grid.attach(ws_chinese_switch_container, 3, 0, 1, 1)
|
||||
|
||||
# Dock position
|
||||
position_label = Label(label="Dock Position", h_align="start", v_align="center")
|
||||
layout_grid.attach(position_label, 0, 1, 1, 1)
|
||||
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", "Top", "Left", "Right"]
|
||||
for pos in positions:
|
||||
self.position_combo.append_text(pos)
|
||||
current_position = bind_vars.get("dock_position", "Bottom")
|
||||
try:
|
||||
self.position_combo.set_active(positions.index(current_position))
|
||||
except ValueError:
|
||||
self.position_combo.set_active(0)
|
||||
self.position_combo.connect("changed", self.on_position_changed)
|
||||
position_combo_container.add(self.position_combo)
|
||||
layout_grid.attach(position_combo_container, 1, 1, 1, 1)
|
||||
|
||||
# Dock theme
|
||||
dock_theme_label = Label(label="Dock Theme", h_align="start", v_align="center")
|
||||
layout_grid.attach(dock_theme_label, 2, 1, 1, 1)
|
||||
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")
|
||||
try:
|
||||
self.dock_theme_combo.set_active(self.themes.index(current_dock_theme))
|
||||
except ValueError:
|
||||
self.dock_theme_combo.set_active(0)
|
||||
dock_theme_combo_container.add(self.dock_theme_combo)
|
||||
layout_grid.attach(dock_theme_combo_container, 3, 1, 1, 1)
|
||||
|
||||
# Show Dock switch
|
||||
dock_label = Label(label="Show Dock", h_align="start", v_align="center")
|
||||
layout_grid.attach(dock_label, 0, 2, 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))
|
||||
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, 2, 1, 1)
|
||||
|
||||
# Show Dock Only on Hover
|
||||
dock_hover_label = Label(
|
||||
label="Show Dock Only on Hover", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(dock_hover_label, 2, 2, 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.add(self.dock_hover_switch)
|
||||
layout_grid.attach(dock_hover_switch_container, 3, 2, 1, 1)
|
||||
|
||||
# Auto-hide Dock
|
||||
dock_auto_hide_label = Label(
|
||||
label="Auto-hide Dock", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(dock_auto_hide_label, 0, 3, 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.add(self.dock_auto_hide_switch)
|
||||
layout_grid.attach(dock_auto_hide_switch_container, 1, 3, 1, 1)
|
||||
|
||||
# Hide Special Workspace
|
||||
dock_hide_special_label = Label(
|
||||
label="Hide Special Workspace", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(dock_hide_special_label, 2, 3, 1, 1)
|
||||
dock_hide_special_switch_container = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
halign=Gtk.Align.START,
|
||||
valign=Gtk.Align.CENTER,
|
||||
)
|
||||
self.dock_hide_special_switch = Gtk.Switch(
|
||||
active=bind_vars.get("dock_hide_special_workspace", True),
|
||||
sensitive=self.dock_switch.get_active(),
|
||||
)
|
||||
self.dock_hide_special_switch.set_tooltip_text(
|
||||
"Hide the dock when on special workspaces (scratchpad)"
|
||||
)
|
||||
dock_hide_special_switch_container.add(self.dock_hide_special_switch)
|
||||
layout_grid.attach(dock_hide_special_switch_container, 3, 3, 1, 1)
|
||||
|
||||
# Icon size scale
|
||||
icon_size_label = Label(
|
||||
label="Taskbar Icon Size", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(icon_size_label, 0, 4, 1, 1)
|
||||
|
||||
icon_size_scale_container = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
halign=Gtk.Align.START,
|
||||
valign=Gtk.Align.CENTER,
|
||||
hexpand=True,
|
||||
)
|
||||
|
||||
self.icon_size_scale = Gtk.Scale.new_with_range(
|
||||
Gtk.Orientation.HORIZONTAL, 20, 30, 2
|
||||
)
|
||||
self.icon_size_scale.set_value(bind_vars.get("dock_icon_size", 20))
|
||||
self.icon_size_scale.set_size_request(150, -1)
|
||||
self.icon_size_scale.set_draw_value(True)
|
||||
self.icon_size_scale.set_value_pos(Gtk.PositionType.RIGHT)
|
||||
self.icon_size_scale.set_tooltip_text("Adjust the size of dock icons")
|
||||
|
||||
icon_size_scale_container.add(self.icon_size_scale)
|
||||
layout_grid.attach(icon_size_scale_container, 1, 4, 1, 1)
|
||||
|
||||
# Application switcher items scale
|
||||
application_switcher_label = Label(
|
||||
label="Application Switcher Items", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(application_switcher_label, 2, 4, 1, 1)
|
||||
|
||||
application_switcher_scale_container = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
halign=Gtk.Align.START,
|
||||
valign=Gtk.Align.CENTER,
|
||||
hexpand=True,
|
||||
)
|
||||
|
||||
self.application_switcher_scale = Gtk.Scale.new_with_range(
|
||||
Gtk.Orientation.HORIZONTAL, 5, 20, 1
|
||||
)
|
||||
self.application_switcher_scale.set_value(
|
||||
bind_vars.get("window_switcher_items_per_row", 13)
|
||||
)
|
||||
self.application_switcher_scale.set_size_request(150, -1)
|
||||
self.application_switcher_scale.set_draw_value(True)
|
||||
self.application_switcher_scale.set_value_pos(Gtk.PositionType.RIGHT)
|
||||
self.application_switcher_scale.set_tooltip_text(
|
||||
"Adjust the number of items per row in the application switcher"
|
||||
)
|
||||
|
||||
application_switcher_scale_container.add(self.application_switcher_scale)
|
||||
layout_grid.attach(application_switcher_scale_container, 3, 4, 1, 1)
|
||||
|
||||
# Hide Special Workspace Apps
|
||||
dock_hide_special_apps_label = Label(
|
||||
label="Hide Special Workspace Apps", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(dock_hide_special_apps_label, 2, 5, 1, 1)
|
||||
dock_hide_special_apps_switch_container = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
halign=Gtk.Align.START,
|
||||
valign=Gtk.Align.CENTER,
|
||||
)
|
||||
self.dock_hide_special_apps_switch = Gtk.Switch(
|
||||
active=bind_vars.get("dock_hide_special_workspace_apps", True),
|
||||
sensitive=self.dock_switch.get_active(),
|
||||
)
|
||||
self.dock_hide_special_apps_switch.set_tooltip_text(
|
||||
"Hide applications that are in special workspaces (scratchpad) from the dock"
|
||||
)
|
||||
dock_hide_special_apps_switch_container.add(self.dock_hide_special_apps_switch)
|
||||
layout_grid.attach(dock_hide_special_apps_switch_container, 3, 5, 1, 1)
|
||||
|
||||
# Notification position
|
||||
notification_pos_label = Label(
|
||||
label="Notification Position", h_align="start", v_align="center"
|
||||
)
|
||||
layout_grid.attach(notification_pos_label, 0, 8, 1, 1)
|
||||
|
||||
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."
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
except ValueError:
|
||||
self.notification_pos_combo.set_active(0)
|
||||
|
||||
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, 8, 3, 1)
|
||||
|
||||
def _create_modules_section(self, vbox):
|
||||
"""Create modules section"""
|
||||
components_header = Label(markup="<b>Modules</b>", h_align="start")
|
||||
vbox.add(components_header)
|
||||
components_grid = Gtk.Grid()
|
||||
components_grid.set_column_spacing(15)
|
||||
components_grid.set_row_spacing(8)
|
||||
components_grid.set_margin_start(10)
|
||||
components_grid.set_margin_top(5)
|
||||
vbox.add(components_grid)
|
||||
|
||||
self.component_switches = {}
|
||||
component_display_names = {
|
||||
"workspace": "Workspaces",
|
||||
"metrics": "System Metrics",
|
||||
"date_time": "Date & Time",
|
||||
"battery": "Battery Indicator",
|
||||
"controls": "Control Panel",
|
||||
"indicators": "Indicators",
|
||||
"music_player": "Music Player",
|
||||
"applications": "Taskbar",
|
||||
"language": "Language Indicator",
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
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.add(self.corners_switch)
|
||||
components_grid.attach(switch_container_corners, 1, 0, 1, 1)
|
||||
|
||||
item_idx = 0
|
||||
for name, display in component_display_names.items():
|
||||
if item_idx < (rows_per_column - 1):
|
||||
row = item_idx + 1
|
||||
col = 0
|
||||
else:
|
||||
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"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
|
||||
|
||||
# Event handlers
|
||||
def on_ws_mode_changed(self, combo):
|
||||
"""Handle workspace mode change"""
|
||||
mode = combo.get_active_text()
|
||||
is_numbers_mode = mode == "Numbers"
|
||||
self.ws_chinese_switch.set_sensitive(is_numbers_mode)
|
||||
if not is_numbers_mode:
|
||||
self.ws_chinese_switch.set_active(False)
|
||||
|
||||
def on_position_changed(self, _combo):
|
||||
"""Handle dock position change"""
|
||||
# Note: centered_switch is not used in appearance tab,
|
||||
# this method is kept for compatibility
|
||||
pass
|
||||
|
||||
def on_dock_enabled_changed(self, switch, _gparam):
|
||||
"""Handle dock enabled/disabled change"""
|
||||
is_active = switch.get_active()
|
||||
self.dock_hover_switch.set_sensitive(is_active)
|
||||
self.dock_auto_hide_switch.set_sensitive(is_active)
|
||||
self.dock_hide_special_switch.set_sensitive(is_active)
|
||||
self.dock_hide_special_apps_switch.set_sensitive(is_active)
|
||||
if not is_active:
|
||||
self.dock_hover_switch.set_active(False)
|
||||
self.dock_auto_hide_switch.set_active(False)
|
||||
self.dock_hide_special_switch.set_active(False)
|
||||
self.dock_hide_special_apps_switch.set_active(False)
|
||||
|
||||
def on_notification_position_changed(self, combo):
|
||||
"""Handle notification position change"""
|
||||
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]
|
||||
}"
|
||||
)
|
||||
|
||||
def on_select_face_icon(self, _widget):
|
||||
"""Handle face icon selection"""
|
||||
dialog = Gtk.FileChooserDialog(
|
||||
title="Select Face Icon",
|
||||
transient_for=self.parent_window,
|
||||
action=Gtk.FileChooserAction.OPEN,
|
||||
)
|
||||
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)
|
||||
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)}"
|
||||
)
|
||||
try:
|
||||
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)
|
||||
dialog.destroy()
|
||||
|
||||
def get_appearance_values(self):
|
||||
"""Get current appearance values from widgets"""
|
||||
values = {}
|
||||
|
||||
values["wallpapers_dir"] = self.wall_dir_chooser.get_filename()
|
||||
values["dock_position"] = self.position_combo.get_active_text()
|
||||
values["vertical"] = values["dock_position"] in ["Left", "Right"]
|
||||
values["dock_enabled"] = self.dock_switch.get_active()
|
||||
values["dock_auto_hide"] = self.dock_auto_hide_switch.get_active()
|
||||
values["dock_always_occluded"] = self.dock_hover_switch.get_active()
|
||||
values["dock_hide_special_workspace"] = self.dock_hide_special_switch.get_active()
|
||||
values["dock_hide_special_workspace_apps"] = self.dock_hide_special_apps_switch.get_active()
|
||||
values["dock_icon_size"] = int(self.icon_size_scale.get_value())
|
||||
values["window_switcher_items_per_row"] = int(self.application_switcher_scale.get_value())
|
||||
values["corners_visible"] = self.corners_switch.get_active()
|
||||
values["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:
|
||||
values[NOTIF_POS_KEY] = selected_notif_pos_text
|
||||
else:
|
||||
values[NOTIF_POS_KEY] = NOTIF_POS_DEFAULT
|
||||
|
||||
for component_name, switch in self.component_switches.items():
|
||||
values[f"dock_{component_name}_visible"] = switch.get_active()
|
||||
|
||||
# Save workspace settings
|
||||
ws_mode = self.ws_mode_combo.get_active_text()
|
||||
values["workspace_dots"] = ws_mode == "Dots"
|
||||
values["workspace_nums"] = ws_mode == "Numbers"
|
||||
values["workspace_use_chinese_numerals"] = self.ws_chinese_switch.get_active()
|
||||
|
||||
return values
|
||||
|
||||
def get_selected_face_icon(self):
|
||||
"""Get the selected face icon path"""
|
||||
return self.selected_face_icon
|
||||
|
||||
def clear_selected_face_icon(self):
|
||||
"""Clear the selected face icon"""
|
||||
self.selected_face_icon = None
|
||||
self.face_status_label.label = ""
|
||||
|
||||
def reset_to_defaults(self, defaults):
|
||||
"""Reset appearance settings to default values"""
|
||||
self.wall_dir_chooser.set_filename(defaults.get("wallpapers_dir", ""))
|
||||
|
||||
positions = ["Bottom", "Top", "Left", "Right"]
|
||||
default_position = defaults.get("dock_position", "Bottom")
|
||||
try:
|
||||
self.position_combo.set_active(positions.index(default_position))
|
||||
except ValueError:
|
||||
self.position_combo.set_active(0)
|
||||
|
||||
self.dock_switch.set_active(defaults.get("dock_enabled", True))
|
||||
self.dock_auto_hide_switch.set_active(defaults.get("dock_auto_hide", False))
|
||||
self.dock_hover_switch.set_active(defaults.get("dock_always_occluded", False))
|
||||
self.dock_hide_special_switch.set_active(defaults.get("dock_hide_special_workspace", True))
|
||||
self.dock_hide_special_apps_switch.set_active(defaults.get("dock_hide_special_workspace_apps", True))
|
||||
|
||||
# Update sensitivity based on dock enabled state
|
||||
dock_enabled = self.dock_switch.get_active()
|
||||
self.dock_hover_switch.set_sensitive(dock_enabled)
|
||||
self.dock_auto_hide_switch.set_sensitive(dock_enabled)
|
||||
self.dock_hide_special_switch.set_sensitive(dock_enabled)
|
||||
self.dock_hide_special_apps_switch.set_sensitive(dock_enabled)
|
||||
|
||||
default_dock_theme_val = defaults.get("dock_theme", "Pills")
|
||||
try:
|
||||
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)
|
||||
)
|
||||
except ValueError:
|
||||
self.notification_pos_combo.set_active(0)
|
||||
|
||||
for name, switch in self.component_switches.items():
|
||||
switch.set_active(defaults.get(f"dock_{name}_visible", True))
|
||||
|
||||
# Reset workspace settings
|
||||
default_dots = defaults.get("workspace_dots", True)
|
||||
default_nums = defaults.get("workspace_nums", False)
|
||||
if default_dots:
|
||||
default_mode = "Dots"
|
||||
elif default_nums:
|
||||
default_mode = "Numbers"
|
||||
else:
|
||||
default_mode = "Single Button"
|
||||
|
||||
ws_modes = ["Dots", "Numbers", "Single Button"]
|
||||
try:
|
||||
self.ws_mode_combo.set_active(ws_modes.index(default_mode))
|
||||
except ValueError:
|
||||
self.ws_mode_combo.set_active(0)
|
||||
|
||||
self.ws_chinese_switch.set_active(defaults.get("workspace_use_chinese_numerals", False))
|
||||
self.ws_chinese_switch.set_sensitive(default_nums)
|
||||
self.corners_switch.set_active(defaults.get("corners_visible", True))
|
||||
|
||||
# Reset face icon
|
||||
self.selected_face_icon = None
|
||||
self.face_status_label.label = ""
|
||||
current_face = os.path.expanduser("~/.face.icon")
|
||||
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)
|
||||
except Exception as e:
|
||||
print(f"Error loading face icon: {e}")
|
||||
self.face_image.set_from_icon_name("image-missing", Gtk.IconSize.DIALOG)
|
||||
|
||||
# Reset scales
|
||||
self.icon_size_scale.set_value(defaults.get("dock_icon_size", 20))
|
||||
self.application_switcher_scale.set_value(defaults.get("window_switcher_items_per_row", 13))
|
||||
+40
-1085
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,115 @@
|
||||
import gi
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from gi.repository import Gtk
|
||||
|
||||
from config.data import APP_NAME_CAP
|
||||
from config.settings.utils import bind_vars
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
|
||||
class KeyBindingsTab:
|
||||
"""Key bindings management tab for settings"""
|
||||
|
||||
def __init__(self):
|
||||
self.entries = []
|
||||
|
||||
def create_key_bindings_tab(self):
|
||||
"""Create the Key Bindings tab content"""
|
||||
scrolled_window = ScrolledWindow(
|
||||
h_scrollbar_policy="never",
|
||||
v_scrollbar_policy="automatic",
|
||||
h_expand=False,
|
||||
v_expand=False,
|
||||
propagate_width=False,
|
||||
propagate_height=False,
|
||||
)
|
||||
# Set fixed size to match tab stack dimensions
|
||||
scrolled_window.set_size_request(580, 580)
|
||||
|
||||
main_vbox = Box(orientation="v", spacing=10, style="margin: 15px;")
|
||||
scrolled_window.add(main_vbox)
|
||||
|
||||
keybind_grid = Gtk.Grid()
|
||||
keybind_grid.set_column_spacing(10)
|
||||
keybind_grid.set_row_spacing(8)
|
||||
keybind_grid.set_margin_start(5)
|
||||
keybind_grid.set_margin_end(5)
|
||||
keybind_grid.set_margin_top(5)
|
||||
keybind_grid.set_margin_bottom(5)
|
||||
|
||||
action_label = Label(
|
||||
markup="<b>Action</b>", h_align="start", style="margin-bottom: 5px;"
|
||||
)
|
||||
modifier_label = Label(
|
||||
markup="<b>Modifier</b>", h_align="start", style="margin-bottom: 5px;"
|
||||
)
|
||||
separator_label = Label(
|
||||
label="+", h_align="center", style="margin-bottom: 5px;"
|
||||
)
|
||||
key_label = Label(
|
||||
markup="<b>Key</b>", 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)
|
||||
keybind_grid.attach(separator_label, 2, 0, 1, 1)
|
||||
keybind_grid.attach(key_label, 3, 0, 1, 1)
|
||||
|
||||
self.entries = []
|
||||
bindings = [
|
||||
(f"Reload {APP_NAME_CAP}", "prefix_restart", "suffix_restart"),
|
||||
("Message", "prefix_msg", "suffix_msg"),
|
||||
(
|
||||
"Application Switcher",
|
||||
"prefix_application_switcher",
|
||||
"suffix_application_switcher",
|
||||
),
|
||||
("Launcher", "prefix_launcher", "suffix_launcher"),
|
||||
("App Launcher", "prefix_app_launcher", "suffix_app_launcher"),
|
||||
("Clipboard History", "prefix_cliphist", "suffix_cliphist"),
|
||||
("Wallpapers", "prefix_wallpapers", "suffix_wallpapers"),
|
||||
("Random Wallpaper", "prefix_randwall", "suffix_randwall"),
|
||||
("Emoji Picker", "prefix_emoji", "suffix_emoji"),
|
||||
("Kanban", "prefix_kanban", "suffix_kanban"),
|
||||
("Power Menu", "prefix_power", "suffix_power"),
|
||||
("Toggle Caffeine", "prefix_caffeine", "suffix_caffeine"),
|
||||
("Settings", "prefix_settings", "suffix_settings"),
|
||||
(
|
||||
"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, ""))
|
||||
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, ""))
|
||||
keybind_grid.attach(suffix_entry, 3, row, 1, 1)
|
||||
self.entries.append((prefix_key, suffix_key, prefix_entry, suffix_entry))
|
||||
|
||||
main_vbox.add(keybind_grid)
|
||||
return scrolled_window
|
||||
|
||||
def get_key_binding_values(self):
|
||||
"""Get current key binding values from entries"""
|
||||
values = {}
|
||||
for prefix_key, suffix_key, prefix_entry, suffix_entry in self.entries:
|
||||
values[prefix_key] = prefix_entry.get_text()
|
||||
values[suffix_key] = suffix_entry.get_text()
|
||||
return values
|
||||
|
||||
def reset_to_defaults(self, defaults):
|
||||
"""Reset key bindings to default values"""
|
||||
for prefix_key, suffix_key, prefix_entry, suffix_entry in self.entries:
|
||||
prefix_entry.set_text(defaults.get(prefix_key, ""))
|
||||
suffix_entry.set_text(defaults.get(suffix_key, ""))
|
||||
@@ -0,0 +1,382 @@
|
||||
import gi
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from gi.repository import GLib, Gtk
|
||||
|
||||
from config.settings.utils import bind_vars
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
|
||||
class SystemTab:
|
||||
"""System settings and metrics management tab for settings"""
|
||||
|
||||
def __init__(self, show_lock_checkbox=False, show_idle_checkbox=False, window_size_enforcer=None):
|
||||
self.show_lock_checkbox = show_lock_checkbox
|
||||
self.show_idle_checkbox = show_idle_checkbox
|
||||
self.window_size_enforcer = window_size_enforcer
|
||||
|
||||
# Widget references
|
||||
self.terminal_entry = None
|
||||
self.lock_switch = None
|
||||
self.idle_switch = None
|
||||
self.limited_apps_entry = None
|
||||
self.ignored_apps_entry = None
|
||||
self.metrics_switches = {}
|
||||
self.disk_entries = None
|
||||
self._create_disk_edit_entry_func = None
|
||||
|
||||
def create_system_tab(self):
|
||||
"""Create the System tab content"""
|
||||
scrolled_window = ScrolledWindow(
|
||||
h_scrollbar_policy="never",
|
||||
v_scrollbar_policy="automatic",
|
||||
h_expand=False,
|
||||
v_expand=False,
|
||||
propagate_width=False,
|
||||
propagate_height=False,
|
||||
)
|
||||
# Set fixed size to match tab stack dimensions
|
||||
scrolled_window.set_size_request(580, 580)
|
||||
|
||||
vbox = Box(orientation="v", spacing=15, style="margin: 15px;")
|
||||
scrolled_window.add(vbox)
|
||||
|
||||
# Create terminal and hyprland settings section
|
||||
self._create_terminal_hyprland_section(vbox)
|
||||
|
||||
# Create notification settings section
|
||||
self._create_notification_settings_section(vbox)
|
||||
|
||||
# Create system metrics section
|
||||
self._create_system_metrics_section(vbox)
|
||||
|
||||
return scrolled_window
|
||||
|
||||
def _create_terminal_hyprland_section(self, vbox):
|
||||
"""Create terminal and Hyprland integration section"""
|
||||
system_grid = Gtk.Grid()
|
||||
system_grid.set_column_spacing(20)
|
||||
system_grid.set_row_spacing(10)
|
||||
system_grid.set_margin_bottom(15)
|
||||
vbox.add(system_grid)
|
||||
|
||||
# Terminal settings
|
||||
terminal_header = Label(markup="<b>Terminal Settings</b>", h_align="start")
|
||||
system_grid.attach(terminal_header, 0, 0, 2, 1)
|
||||
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"),
|
||||
tooltip_text="Command used to launch terminal apps (e.g., 'kitty -e')",
|
||||
h_expand=True,
|
||||
)
|
||||
system_grid.attach(self.terminal_entry, 1, 1, 1, 1)
|
||||
hint_label = Label(
|
||||
markup="<small>Examples: 'kitty -e', 'alacritty -e', 'foot -e'</small>",
|
||||
h_align="start",
|
||||
)
|
||||
system_grid.attach(hint_label, 0, 2, 2, 1)
|
||||
|
||||
# Hyprland integration
|
||||
hypr_header = Label(markup="<b>Hyprland Integration</b>", h_align="start")
|
||||
system_grid.attach(hypr_header, 2, 0, 2, 1)
|
||||
row = 1
|
||||
self.lock_switch = None
|
||||
if self.show_lock_checkbox:
|
||||
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 Modus'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"
|
||||
)
|
||||
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 Modus'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="<small>Existing configs will be backed up</small>",
|
||||
h_align="start",
|
||||
)
|
||||
system_grid.attach(note_label, 2, row, 2, 1)
|
||||
|
||||
def _create_notification_settings_section(self, vbox):
|
||||
"""Create notification settings section"""
|
||||
notifications_header = Label(
|
||||
markup="<b>Notification Settings</b>", h_align="start"
|
||||
)
|
||||
vbox.add(notifications_header)
|
||||
|
||||
notif_grid = Gtk.Grid()
|
||||
notif_grid.set_column_spacing(20)
|
||||
notif_grid.set_row_spacing(10)
|
||||
notif_grid.set_margin_start(10)
|
||||
notif_grid.set_margin_top(5)
|
||||
notif_grid.set_margin_bottom(15)
|
||||
vbox.add(notif_grid)
|
||||
|
||||
# Limited Apps History
|
||||
limited_apps_label = Label(
|
||||
label="Limited Apps History:", h_align="start", v_align="center"
|
||||
)
|
||||
notif_grid.attach(limited_apps_label, 0, 0, 1, 1)
|
||||
|
||||
limited_apps_list = bind_vars.get("limited_apps_history", ["Spotify"])
|
||||
limited_apps_text = ", ".join(f'"{app}"' for app in limited_apps_list)
|
||||
self.limited_apps_entry = Entry(
|
||||
text=limited_apps_text,
|
||||
tooltip_text='Enter app names separated by commas, e.g: "Spotify", "Discord"',
|
||||
h_expand=True,
|
||||
)
|
||||
notif_grid.attach(self.limited_apps_entry, 1, 0, 1, 1)
|
||||
|
||||
limited_apps_hint = Label(
|
||||
markup='<small>Apps with limited notification history (format: "App1", "App2")</small>',
|
||||
h_align="start",
|
||||
)
|
||||
notif_grid.attach(limited_apps_hint, 0, 1, 2, 1)
|
||||
|
||||
# History Ignored Apps
|
||||
ignored_apps_label = Label(
|
||||
label="History Ignored Apps:", h_align="start", v_align="center"
|
||||
)
|
||||
notif_grid.attach(ignored_apps_label, 0, 2, 1, 1)
|
||||
|
||||
ignored_apps_list = bind_vars.get("history_ignored_apps", ["Modus"])
|
||||
ignored_apps_text = ", ".join(f'"{app}"' for app in ignored_apps_list)
|
||||
self.ignored_apps_entry = Entry(
|
||||
text=ignored_apps_text,
|
||||
tooltip_text='Enter app names separated by commas, e.g: "Modus", "Screenshot"',
|
||||
h_expand=True,
|
||||
)
|
||||
notif_grid.attach(self.ignored_apps_entry, 1, 2, 1, 1)
|
||||
|
||||
ignored_apps_hint = Label(
|
||||
markup='<small>Apps whose notifications are ignored in history (format: "App1", "App2")</small>',
|
||||
h_align="start",
|
||||
)
|
||||
notif_grid.attach(ignored_apps_hint, 0, 3, 2, 1)
|
||||
|
||||
def _create_system_metrics_section(self, vbox):
|
||||
"""Create system metrics section"""
|
||||
metrics_header = Label(markup="<b>System Metrics Options</b>", h_align="start")
|
||||
vbox.add(metrics_header)
|
||||
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",
|
||||
"swap": "Swap",
|
||||
"disk": "Disk",
|
||||
"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)
|
||||
)
|
||||
self.metrics_switches[key] = switch
|
||||
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 s_s in self.metrics_switches.values():
|
||||
s_s.connect("notify::active", on_metric_toggle, self.metrics_switches)
|
||||
enforce_minimum_metrics(self.metrics_switches)
|
||||
|
||||
# Disk directories section
|
||||
disks_label = Label(
|
||||
label="Disk directories for Metrics", h_align="start", v_align="center"
|
||||
)
|
||||
vbox.add(disks_label)
|
||||
|
||||
# Create a scrolled container for disk entries to prevent overflow
|
||||
disk_entries_scrolled = ScrolledWindow(
|
||||
h_scrollbar_policy="never",
|
||||
v_scrollbar_policy="automatic",
|
||||
h_expand=False,
|
||||
v_expand=False,
|
||||
propagate_width=False,
|
||||
propagate_height=False,
|
||||
)
|
||||
# Set fixed size for disk entries container
|
||||
disk_entries_scrolled.set_size_request(550, 120)
|
||||
|
||||
self.disk_entries = Box(orientation="v", spacing=8, h_align="start")
|
||||
# Set size constraints for the disk entries box
|
||||
self.disk_entries.set_size_request(530, -1)
|
||||
disk_entries_scrolled.add(self.disk_entries)
|
||||
|
||||
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(disk_entries_scrolled)
|
||||
|
||||
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)
|
||||
|
||||
def _add_disk_entry_widget(self, path):
|
||||
"""Helper to add a disk entry row to the disk_entries Box."""
|
||||
bar = Box(orientation="h", spacing=10, h_align="start")
|
||||
# Set fixed height for disk entry rows
|
||||
bar.set_size_request(-1, 30)
|
||||
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._remove_disk_entry(current_bar_to_remove),
|
||||
)
|
||||
bar.add(x_btn)
|
||||
self.disk_entries.add(bar)
|
||||
self.disk_entries.show_all()
|
||||
# Enforce window size after adding content
|
||||
if self.window_size_enforcer:
|
||||
GLib.idle_add(self.window_size_enforcer)
|
||||
|
||||
def _remove_disk_entry(self, bar_to_remove):
|
||||
"""Helper to remove a disk entry and enforce window size"""
|
||||
self.disk_entries.remove(bar_to_remove)
|
||||
# Enforce window size after removing content
|
||||
if self.window_size_enforcer:
|
||||
GLib.idle_add(self.window_size_enforcer)
|
||||
|
||||
def get_system_values(self):
|
||||
"""Get current system values from widgets"""
|
||||
values = {}
|
||||
|
||||
values["terminal_command"] = self.terminal_entry.get_text()
|
||||
|
||||
values["metrics_visible"] = {
|
||||
k: s.get_active() for k, s in self.metrics_switches.items()
|
||||
}
|
||||
values["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)
|
||||
]
|
||||
|
||||
# Parse notification app lists
|
||||
def parse_app_list(text):
|
||||
"""Parse comma-separated app names with quotes"""
|
||||
if not text.strip():
|
||||
return []
|
||||
apps = []
|
||||
for app in text.split(","):
|
||||
app = app.strip()
|
||||
if app.startswith('"') and app.endswith('"'):
|
||||
app = app[1:-1]
|
||||
elif app.startswith("'") and app.endswith("'"):
|
||||
app = app[1:-1]
|
||||
if app:
|
||||
apps.append(app)
|
||||
return apps
|
||||
|
||||
values["limited_apps_history"] = parse_app_list(
|
||||
self.limited_apps_entry.get_text()
|
||||
)
|
||||
values["history_ignored_apps"] = parse_app_list(
|
||||
self.ignored_apps_entry.get_text()
|
||||
)
|
||||
|
||||
return values
|
||||
|
||||
def get_hyprland_switches(self):
|
||||
"""Get the state of Hyprland integration switches"""
|
||||
return {
|
||||
"replace_lock": self.lock_switch and self.lock_switch.get_active(),
|
||||
"replace_idle": self.idle_switch and self.idle_switch.get_active()
|
||||
}
|
||||
|
||||
def reset_to_defaults(self, defaults):
|
||||
"""Reset system settings to default values"""
|
||||
self.terminal_entry.set_text(defaults.get("terminal_command", "kitty -e"))
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
enforce_minimum_metrics(self.metrics_switches)
|
||||
|
||||
# Reset disk entries
|
||||
for child in list(self.disk_entries.get_children()):
|
||||
self.disk_entries.remove(child)
|
||||
|
||||
for p in defaults.get("metrics_disks", ["/"]):
|
||||
self._create_disk_edit_entry_func(p)
|
||||
|
||||
# Reset notification app lists
|
||||
limited_apps_list = defaults.get("limited_apps_history", ["Spotify"])
|
||||
limited_apps_text = ", ".join(f'"{app}"' for app in limited_apps_list)
|
||||
self.limited_apps_entry.set_text(limited_apps_text)
|
||||
|
||||
ignored_apps_list = defaults.get("history_ignored_apps", ["Modus"])
|
||||
ignored_apps_text = ", ".join(f'"{app}"' for app in ignored_apps_list)
|
||||
self.ignored_apps_entry.set_text(ignored_apps_text)
|
||||
|
||||
# Reset Hyprland switches
|
||||
if self.lock_switch:
|
||||
self.lock_switch.set_active(False)
|
||||
if self.idle_switch:
|
||||
self.idle_switch.set_active(False)
|
||||
Reference in new issue
Block a user