update
This commit is contained in:
@@ -0,0 +1,627 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from fabric.hyprland.service import HyprlandEvent
|
||||
from fabric.hyprland.widgets import HyprlandLanguage as Language
|
||||
from fabric.hyprland.widgets import HyprlandWorkspaces as Workspaces
|
||||
from fabric.hyprland.widgets import WorkspaceButton, 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, GLib, 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.systemprofiles import Systemprofiles
|
||||
from modules.systemtray import SystemTray
|
||||
from modules.weather import Weather
|
||||
from widgets.wayland import WaylandWindow as Window
|
||||
|
||||
CHINESE_NUMERALS = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "〇"]
|
||||
|
||||
# Tooltips
|
||||
tooltip_apps = f"""<b><u>Launcher</u></b>
|
||||
<b>• Apps:</b> Type to search.
|
||||
|
||||
<b>• Calculator [Prefix "="]:</b> Solve a math expression.
|
||||
e.g. "=2+2"
|
||||
|
||||
<b>• Converter [Prefix ";"]:</b> Convert between units.
|
||||
e.g. ";100 USD to EUR", ";10 km to miles"
|
||||
|
||||
<b>• Special Commands [Prefix ":"]:</b>
|
||||
:update - Open {data.APP_NAME_CAP}'s updater.
|
||||
:d - Open Dashboard.
|
||||
:w - Open Wallpapers."""
|
||||
|
||||
tooltip_power = """<b>Power Menu</b>"""
|
||||
tooltip_tools = """<b>Toolbox</b>"""
|
||||
tooltip_overview = """<b>Overview</b>"""
|
||||
|
||||
|
||||
class Bar(Window):
|
||||
def __init__(self, monitor_id: int = 0, **kwargs):
|
||||
self.monitor_id = monitor_id
|
||||
|
||||
super().__init__(
|
||||
name="bar",
|
||||
layer="top",
|
||||
exclusivity="auto",
|
||||
visible=True,
|
||||
all_visible=True,
|
||||
monitor=monitor_id,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
# Calculate workspace range based on monitor_id
|
||||
# Monitor 0: workspaces 1-10, Monitor 1: workspaces 11-20, etc.
|
||||
start_workspace = self.monitor_id * 10 + 1
|
||||
end_workspace = start_workspace + 10
|
||||
workspace_range = range(start_workspace, end_workspace)
|
||||
|
||||
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 workspace_range
|
||||
],
|
||||
buttons_factory=(
|
||||
None
|
||||
if data.BAR_HIDE_SPECIAL_WORKSPACE
|
||||
else Workspaces.default_buttons_factory
|
||||
),
|
||||
)
|
||||
|
||||
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 - start_workspace)]
|
||||
if data.BAR_WORKSPACE_USE_CHINESE_NUMERALS
|
||||
and 0 <= (i - start_workspace) < len(CHINESE_NUMERALS)
|
||||
else str(i)
|
||||
),
|
||||
)
|
||||
for i in workspace_range
|
||||
],
|
||||
buttons_factory=(
|
||||
None
|
||||
if data.BAR_HIDE_SPECIAL_WORKSPACE
|
||||
else Workspaces.default_buttons_factory
|
||||
),
|
||||
)
|
||||
|
||||
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.sysprofiles = Systemprofiles()
|
||||
|
||||
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)
|
||||
|
||||
# Determine date-time format based on the new setting
|
||||
if data.DATETIME_12H_FORMAT:
|
||||
time_format_horizontal = "%I:%M %p"
|
||||
time_format_vertical = "%I\n%M\n%p"
|
||||
else:
|
||||
time_format_horizontal = "%H:%M"
|
||||
time_format_vertical = "%H\n%M"
|
||||
|
||||
self.date_time = DateTime(
|
||||
name="date-time",
|
||||
formatters=(
|
||||
[time_format_horizontal]
|
||||
if not data.VERTICAL
|
||||
else [time_format_vertical]
|
||||
),
|
||||
h_align="center" if not data.VERTICAL else "fill",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
style_classes=["vertical"] if data.VERTICAL else [],
|
||||
)
|
||||
|
||||
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.sysprofiles,
|
||||
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.sysprofiles,
|
||||
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)
|
||||
|
||||
# Create embedded dock when bar is in center position (regardless of DOCK_ENABLED setting)
|
||||
should_embed_dock = (
|
||||
data.BAR_POSITION == "Bottom"
|
||||
or (data.PANEL_THEME == "Panel" and data.BAR_POSITION in ["Top", "Bottom"])
|
||||
)
|
||||
|
||||
if should_embed_dock:
|
||||
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,
|
||||
"sysprofiles": self.sysprofiles,
|
||||
}
|
||||
|
||||
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,
|
||||
"sysprofiles": self.sysprofiles,
|
||||
}
|
||||
|
||||
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):
|
||||
try:
|
||||
lang_data = (
|
||||
event.data[1]
|
||||
if event and event.data and len(event.data) > 1
|
||||
else Language().get_label()
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
lang_data = "UNK" # Fallback to default language 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")
|
||||
# Ensure notch is above bar when bar is shown
|
||||
if self.notch:
|
||||
# Focus the notch window to bring it to front
|
||||
GLib.idle_add(lambda: exec_shell_command_async("hyprctl dispatch focuswindow class:notch") if self.notch else None)
|
||||
|
||||
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")
|
||||
Reference in New Issue
Block a user