This commit is contained in:
Axenide committed 2025-06-11 02:34:22 -03:00
1 parent a1d1066dd6
commit 1d7ca23a97
4 files changed
+147 -104

No files matched your search

+123 -74
View File
@@ -8,17 +8,20 @@ from pathlib import Path
import gi
import toml
gi.require_version('Gtk', '3.0')
gi.require_version("Gtk", "3.0")
from fabric.utils.helpers import exec_shell_command_async
from gi.repository import GLib
# Importar settings_constants para DEFAULTS
from . import settings_constants
from .data import ( # CONFIG_DIR, HOME_DIR no se usan aquí directamente
APP_NAME, APP_NAME_CAP)
APP_NAME,
APP_NAME_CAP,
)
# Global variable to store binding variables, managed by this module
bind_vars = {} # Se inicializa vacío, load_bind_vars lo poblará
bind_vars = {} # Se inicializa vacío, load_bind_vars lo poblará
def deep_update(target: dict, update: dict) -> dict:
"""
@@ -33,7 +36,8 @@ def deep_update(target: dict, update: dict) -> dict:
else:
# De lo contrario, simplemente establece/sobrescribe el valor.
target[key] = value
return target # Aunque modifica in-place, devolverlo es una convención común
return target # Aunque modifica in-place, devolverlo es una convención común
def ensure_matugen_config():
"""
@@ -41,54 +45,62 @@ def ensure_matugen_config():
with the expected settings.
"""
expected_config = {
'config': {
'reload_apps': True,
'wallpaper': {
'command': 'swww',
'arguments': [
'img', '-t', 'outer',
'--transition-duration', '1.5',
'--transition-step', '255',
'--transition-fps', '60',
'-f', 'Nearest'
"config": {
"reload_apps": True,
"wallpaper": {
"command": "swww",
"arguments": [
"img",
"-t",
"fade",
"--transition-duration",
"0.5",
"--transition-step",
"255",
"--transition-fps",
"60",
"-f",
"Nearest",
],
'set': True
"set": True,
},
"custom_colors": {
"red": {"color": "#FF0000", "blend": True},
"green": {"color": "#00FF00", "blend": True},
"yellow": {"color": "#FFFF00", "blend": True},
"blue": {"color": "#0000FF", "blend": True},
"magenta": {"color": "#FF00FF", "blend": True},
"cyan": {"color": "#00FFFF", "blend": True},
"white": {"color": "#FFFFFF", "blend": True},
},
'custom_colors': {
'red': {'color': "#FF0000", 'blend': True},
'green': {'color': "#00FF00", 'blend': True},
'yellow': {'color': "#FFFF00", 'blend': True},
'blue': {'color': "#0000FF", 'blend': True},
'magenta': {'color': "#FF00FF", 'blend': True},
'cyan': {'color': "#00FFFF", 'blend': True},
'white': {'color': "#FFFFFF", 'blend': True}
}
},
'templates': {
'hyprland': {
'input_path': f'~/.config/{APP_NAME_CAP}/config/matugen/templates/hyprland-colors.conf',
'output_path': f'~/.config/{APP_NAME_CAP}/config/hypr/colors.conf'
"templates": {
"hyprland": {
"input_path": f"~/.config/{APP_NAME_CAP}/config/matugen/templates/hyprland-colors.conf",
"output_path": f"~/.config/{APP_NAME_CAP}/config/hypr/colors.conf",
},
f'{APP_NAME}': {
'input_path': f'~/.config/{APP_NAME_CAP}/config/matugen/templates/{APP_NAME}.css',
'output_path': f'~/.config/{APP_NAME_CAP}/styles/colors.css',
'post_hook': f"fabric-cli exec {APP_NAME} 'app.set_css()' &"
}
}
f"{APP_NAME}": {
"input_path": f"~/.config/{APP_NAME_CAP}/config/matugen/templates/{APP_NAME}.css",
"output_path": f"~/.config/{APP_NAME_CAP}/styles/colors.css",
"post_hook": f"fabric-cli exec {APP_NAME} 'app.set_css()' &",
},
},
}
config_path = os.path.expanduser('~/.config/matugen/config.toml')
config_path = os.path.expanduser("~/.config/matugen/config.toml")
os.makedirs(os.path.dirname(config_path), exist_ok=True)
existing_config = {}
if os.path.exists(config_path):
try:
with open(config_path, 'r') as f:
with open(config_path, "r") as f:
existing_config = toml.load(f)
shutil.copyfile(config_path, config_path + '.bak')
shutil.copyfile(config_path, config_path + ".bak")
except toml.TomlDecodeError:
print(f"Warning: Could not decode TOML from {config_path}. A new default config will be created.")
existing_config = {} # Resetear si está corrupto
print(
f"Warning: Could not decode TOML from {config_path}. A new default config will be created."
)
existing_config = {} # Resetear si está corrupto
except Exception as e:
print(f"Error reading or backing up {config_path}: {e}")
# existing_config podría estar parcialmente cargado o vacío.
@@ -100,39 +112,54 @@ def ensure_matugen_config():
# Para ser más seguros, podemos pasar una copia si existing_config no debe cambiar.
# merged_config = deep_update(existing_config.copy(), expected_config)
# O si existing_config puede ser modificado:
merged_config = deep_update(existing_config, expected_config) # existing_config se modifica in-place
merged_config = deep_update(
existing_config, expected_config
) # existing_config se modifica in-place
try:
with open(config_path, 'w') as f:
with open(config_path, "w") as f:
toml.dump(merged_config, f)
except Exception as e:
print(f"Error writing matugen config to {config_path}: {e}")
current_wall = os.path.expanduser("~/.current.wall")
hypr_colors = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/hypr/colors.conf")
hypr_colors = os.path.expanduser(
f"~/.config/{APP_NAME_CAP}/config/hypr/colors.conf"
)
css_colors = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/styles/colors.css")
if not os.path.exists(current_wall) or not os.path.exists(hypr_colors) or not os.path.exists(css_colors):
if (
not os.path.exists(current_wall)
or not os.path.exists(hypr_colors)
or not os.path.exists(css_colors)
):
os.makedirs(os.path.dirname(hypr_colors), exist_ok=True)
os.makedirs(os.path.dirname(css_colors), exist_ok=True)
image_path = ""
if not os.path.exists(current_wall):
example_wallpaper_path = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/assets/wallpapers_example/example-1.jpg")
example_wallpaper_path = os.path.expanduser(
f"~/.config/{APP_NAME_CAP}/assets/wallpapers_example/example-1.jpg"
)
if os.path.exists(example_wallpaper_path):
try:
# Si ya existe (posiblemente un enlace roto o archivo regular), eliminar y re-enlazar
if os.path.lexists(current_wall): # lexists para no seguir el enlace si es uno
if os.path.lexists(
current_wall
): # lexists para no seguir el enlace si es uno
os.remove(current_wall)
os.symlink(example_wallpaper_path, current_wall)
image_path = example_wallpaper_path
except Exception as e:
print(f"Error creating symlink for wallpaper: {e}")
else:
image_path = os.path.realpath(current_wall) if os.path.islink(current_wall) else current_wall
if image_path and os.path.exists(image_path):
image_path = (
os.path.realpath(current_wall)
if os.path.islink(current_wall)
else current_wall
)
if image_path and os.path.exists(image_path):
print(f"Generating color theme from wallpaper: {image_path}")
try:
matugen_cmd = f"matugen image '{image_path}'"
@@ -143,9 +170,13 @@ def ensure_matugen_config():
except Exception as e:
print(f"Error initiating matugen: {e}")
elif not image_path:
print("Warning: No wallpaper path determined to generate matugen theme from.")
else: # image_path existe pero el archivo no
print(f"Warning: Wallpaper at {image_path} not found. Cannot generate matugen theme.")
print(
"Warning: No wallpaper path determined to generate matugen theme from."
)
else: # image_path existe pero el archivo no
print(
f"Warning: Wallpaper at {image_path} not found. Cannot generate matugen theme."
)
def load_bind_vars():
@@ -153,17 +184,19 @@ def load_bind_vars():
Load saved key binding variables from JSON, if available.
Populates the global `bind_vars` in-place.
"""
global bind_vars # Necesario para modificar el objeto global bind_vars
global bind_vars # Necesario para modificar el objeto global bind_vars
# 1. Limpiar el diccionario bind_vars existente.
bind_vars.clear()
# 2. Actualizarlo con una copia de DEFAULTS.
bind_vars.update(settings_constants.DEFAULTS.copy()) # Usar .copy() para no modificar DEFAULTS accidentalmente
bind_vars.update(
settings_constants.DEFAULTS.copy()
) # Usar .copy() para no modificar DEFAULTS accidentalmente
config_json = os.path.expanduser(f'~/.config/{APP_NAME_CAP}/config/config.json')
config_json = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/config.json")
if os.path.exists(config_json):
try:
with open(config_json, 'r') as f:
with open(config_json, "r") as f:
saved_vars = json.load(f)
# 3. Usar deep_update para fusionar saved_vars en el bind_vars existente.
deep_update(bind_vars, saved_vars)
@@ -171,7 +204,7 @@ def load_bind_vars():
# La lógica para asegurar la estructura de diccionarios anidados
# como 'metrics_visible' y 'metrics_small_visible'
# debe operar sobre el 'bind_vars' ya actualizado.
for vis_key in ['metrics_visible', 'metrics_small_visible']:
for vis_key in ["metrics_visible", "metrics_small_visible"]:
# Asegurar que la clave exista en DEFAULTS como referencia de estructura
if vis_key in settings_constants.DEFAULTS:
default_sub_dict = settings_constants.DEFAULTS[vis_key]
@@ -186,26 +219,30 @@ def load_bind_vars():
if m_key not in current_sub_dict:
current_sub_dict[m_key] = m_val
except json.JSONDecodeError:
print(f"Warning: Could not decode JSON from {config_json}. Using defaults (already initialized).")
print(
f"Warning: Could not decode JSON from {config_json}. Using defaults (already initialized)."
)
# bind_vars ya está poblado con DEFAULTS, no se necesita acción adicional aquí.
except Exception as e:
print(f"Error loading config from {config_json}: {e}. Using defaults (already initialized).")
print(
f"Error loading config from {config_json}: {e}. Using defaults (already initialized)."
)
# bind_vars ya está poblado con DEFAULTS.
# else:
# Si config_json no existe, bind_vars ya está poblado con DEFAULTS.
# print(f"Config file {config_json} not found. Using defaults (already initialized).")
# Si config_json no existe, bind_vars ya está poblado con DEFAULTS.
# print(f"Config file {config_json} not found. Using defaults (already initialized).")
def generate_hyprconf() -> str:
"""
Generate the Hypr configuration string using the current bind_vars.
"""
home = os.path.expanduser('~')
home = os.path.expanduser("~")
# Determine animation type based on bar position
bar_position = bind_vars.get('bar_position', 'Top')
bar_position = bind_vars.get("bar_position", "Top")
is_vertical = bar_position in ["Left", "Right"]
animation_type = "slidefadevert" if is_vertical else "slidefade"
return f"""exec-once = uwsm-app $(python {home}/.config/{APP_NAME_CAP}/main.py)
exec = pgrep -x "hypridle" > /dev/null || uwsm app -- hypridle
exec = uwsm app -- swww-daemon
@@ -283,18 +320,22 @@ animations {{
}}
"""
def ensure_face_icon():
"""
Ensure the face icon exists. If not, copy the default icon.
"""
face_icon_path = os.path.expanduser("~/.face.icon")
default_icon_path = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/assets/default.png")
default_icon_path = os.path.expanduser(
f"~/.config/{APP_NAME_CAP}/assets/default.png"
)
if not os.path.exists(face_icon_path) and os.path.exists(default_icon_path):
try:
shutil.copy(default_icon_path, face_icon_path)
except Exception as e:
print(f"Error copying default face icon: {e}")
def backup_and_replace(src: str, dest: str, config_name: str):
"""
Backup the existing configuration file and replace it with a new one.
@@ -306,7 +347,9 @@ def backup_and_replace(src: str, dest: str, config_name: str):
# os.makedirs(os.path.dirname(backup_path), exist_ok=True)
shutil.copy(dest, backup_path)
print(f"{config_name} config backed up to {backup_path}")
os.makedirs(os.path.dirname(dest), exist_ok=True) # Ensure dest directory exists
os.makedirs(
os.path.dirname(dest), exist_ok=True
) # Ensure dest directory exists
shutil.copy(src, dest)
print(f"{config_name} config replaced from {src}")
except Exception as e:
@@ -338,12 +381,18 @@ def start_config():
print(f"{time.time():.4f}: start_config: Initiating hyprctl reload...")
try:
# subprocess.run(["hyprctl", "reload"], check=True, capture_output=True, text=True)
exec_shell_command_async("hyprctl reload") # Mantener async para no bloquear
print(f"{time.time():.4f}: start_config: Hyprland configuration reload initiated.")
exec_shell_command_async("hyprctl reload") # Mantener async para no bloquear
print(
f"{time.time():.4f}: start_config: Hyprland configuration reload initiated."
)
except FileNotFoundError:
print("Error: hyprctl command not found. Cannot reload Hyprland.")
except subprocess.CalledProcessError as e: # Si usáramos subprocess.run con check=True
print(f"Error reloading Hyprland with hyprctl: {e}\nOutput:\n{e.stdout}\n{e.stderr}")
print("Error: hyprctl command not found. Cannot reload Hyprland.")
except (
subprocess.CalledProcessError
) as e: # Si usáramos subprocess.run con check=True
print(
f"Error reloading Hyprland with hyprctl: {e}\nOutput:\n{e.stdout}\n{e.stderr}"
)
except Exception as e:
print(f"An error occurred initiating hyprctl reload: {e}")
print(f"An error occurred initiating hyprctl reload: {e}")
print(f"{time.time():.4f}: start_config: Finished initiating hyprctl reload.")
+6 -2
View File
@@ -49,13 +49,17 @@ class Calendar(Gtk.Box):
self.current_month = self.current_shown_date.month # Para el header
iso_year, iso_week, _ = self.current_shown_date.isocalendar()
self.previous_key = (iso_year, iso_week)
self.set_halign(Gtk.Align.FILL)
self.set_hexpand(True)
self.set_valign(Gtk.Align.CENTER)
self.set_vexpand(False)
self.cache_threshold = 3 # Umbral para mantener vistas en caché
self.month_views = {} # Reutilizado para vistas de semana también
self.prev_button = Gtk.Button( # Nombre genérico del botón
name="prev-button",
name="prev-month-button",
child=Label(name="month-button-label", markup=icons.chevron_left) # CSS puede ser genérico
)
self.prev_button.connect("clicked", self.on_prev_clicked)
@@ -63,7 +67,7 @@ class Calendar(Gtk.Box):
self.month_label = Gtk.Label(name="month-label") # El nombre es histórico, pero muestra mes/año
self.next_button = Gtk.Button( # Nombre genérico del botón
name="next-button",
name="next-month-button",
child=Label(name="month-button-label", markup=icons.chevron_right) # CSS puede ser genérico
)
self.next_button.connect("clicked", self.on_next_clicked)
+16 -26
View File
@@ -1,6 +1,6 @@
import gi
gi.require_version('Gtk', '3.0')
gi.require_version("Gtk", "3.0")
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.stack import Stack
@@ -29,13 +29,14 @@ class Widgets(Box):
)
vertical_layout = False
if data.PANEL_THEME == "Panel" and (data.BAR_POSITION in ["Left", "Right"] or data.PANEL_POSITION in ["Start", "End"]):
if data.PANEL_THEME == "Panel" and (
data.BAR_POSITION in ["Left", "Right"]
or data.PANEL_POSITION in ["Start", "End"]
):
vertical_layout = True
# Determinar el modo de vista del calendario
calendar_view_mode = "week" if vertical_layout else "month"
# Instanciar Calendar con el view_mode apropiado
self.calendar = Calendar(view_mode=calendar_view_mode)
self.notch = kwargs["notch"]
@@ -62,8 +63,6 @@ class Widgets(Box):
self.controls = ControlSliders()
# self.calendar ya está inicializado arriba con el view_mode correcto
self.player = Player()
self.metrics = Metrics()
@@ -80,7 +79,7 @@ class Widgets(Box):
self.notification_history,
self.network_connections,
self.bluetooth,
]
],
)
self.applet_stack_box = Box(
@@ -90,11 +89,9 @@ class Widgets(Box):
h_align="fill",
children=[
self.applet_stack,
]
],
)
# Modificar la definición de self.children_1 para usar self.calendar
# y ajustar la disposición para el modo vertical
if not vertical_layout:
self.children_1 = [
Box(
@@ -103,23 +100,18 @@ class Widgets(Box):
v_expand=True,
spacing=8,
children=[
self.calendar, # Usar la instancia self.calendar
self.calendar,
self.applet_stack_box,
]
],
),
self.metrics,
]
else: # vertical_layout es True
else:
self.children_1 = [
self.calendar, # Usar la instancia self.calendar (será semanal aquí)
self.applet_stack_box,
self.calendar, # Weekly view when vertical
self.player,
]
# En el diseño vertical, el reproductor se mueve aquí
# y las métricas podrían necesitar una reubicación o ser omitidas
# según el diseño deseado. Por ahora, se omite self.metrics
# si no está explícitamente en la nueva lista.
# Si self.player estaba en children_3, ahora está aquí.
self.container_1 = Box(
name="container-1",
@@ -140,26 +132,24 @@ class Widgets(Box):
self.buttons,
self.controls,
self.container_1,
]
],
)
# Ajustar children_3 según si el reproductor se movió
if not vertical_layout:
self.children_3 = [
self.player,
self.container_2,
]
else: # vertical_layout es True, self.player ya está en self.children_1
else: # vertical_layout
self.children_3 = [
self.container_2,
]
self.container_3 = Box(
name="container-3",
h_expand=True,
v_expand=True,
orientation="h", # Esta orientación podría necesitar ser "v" si todo es vertical
orientation="h",
spacing=8,
children=self.children_3,
)
+2 -2
View File
@@ -1,7 +1,7 @@
{
"version": "0.0.14",
"changelog": [
"<b>fix:</b> Weight conversion not working (<a href='https://github.com/Axenide/Ax-Shell/pull/173'>#173</a>)",
"<b>fix:</b> Integrated dock drag and drop (<a href='https://github.com/Axenide/Ax-Shell/pull/174'>#174</a>)"
"<b>feat:</b> Added weekly calendar on vertical panel layout.",
"<b>tweak:</b> Wallpaper transition set to a fast fade. Feels snappier."
]
}