quick menu

This commit is contained in:
2025-09-30 20:52:03 +02:00
parent 7b0a4a56db
commit 159eafbc73
3 changed files with 359 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ from fabric.widgets.datetime import DateTime
from fabric.widgets.centerbox import CenterBox from fabric.widgets.centerbox import CenterBox
from bar.modules.player import Player from bar.modules.player import Player
from bar.modules.vinyl import VinylButton from bar.modules.vinyl import VinylButton
from bar.modules.quick_menu import QuickMenuOpener
from bar.modules.battery import Battery from bar.modules.battery import Battery
from bar.modules.calendar import CalendarService, CalendarPopup from bar.modules.calendar import CalendarService, CalendarPopup
from bar.modules.notmuch import NotmuchWidget from bar.modules.notmuch import NotmuchWidget
@@ -98,6 +99,12 @@ class StatusBar(Window):
if VINYL["enable"]: if VINYL["enable"]:
self.vinyl = VinylButton() self.vinyl = VinylButton()
# Create quick menu button
self.quick_menu = QuickMenuOpener(icon_name="open-menu-symbolic")
# Setup audio section with vinyl if enabled
if self.vinyl:
self.quick_menu.get_menu().setup_audio_section(vinyl_service=self.vinyl)
self.battery = None self.battery = None
if BATTERY["enable"]: if BATTERY["enable"]:
self.battery = Battery() self.battery = Battery()
@@ -115,9 +122,6 @@ class StatusBar(Window):
end_container_children = [] end_container_children = []
if self.vinyl:
end_container_children.append(self.vinyl)
end_container_children.append(self.status_container) end_container_children.append(self.status_container)
if self.system_tray: if self.system_tray:
end_container_children.append(self.system_tray) end_container_children.append(self.system_tray)
@@ -128,6 +132,8 @@ class StatusBar(Window):
if self.notmuch: if self.notmuch:
end_container_children.append(self.notmuch) end_container_children.append(self.notmuch)
# Add quick menu button next to time
end_container_children.append(self.quick_menu)
end_container_children.append(self.date_time) end_container_children.append(self.date_time)
center_children = [] center_children = []

283
bar/modules/quick_menu.py Normal file
View File

@@ -0,0 +1,283 @@
from fabric.widgets.button import Button
from fabric.widgets.image import Image
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.wayland import WaylandWindow as Window
from gi.repository import Gtk
from loguru import logger
class QuickMenuItem(Box):
"""Base class for quick menu items"""
def __init__(self, title, icon_name=None, **kwargs):
super().__init__(
orientation="h",
spacing=12,
name="quick-menu-item",
**kwargs
)
self.set_style("padding: 8px 12px; min-width: 280px;")
# Icon and title on the left
left_box = Box(orientation="h", spacing=8)
if icon_name:
icon = Image(icon_name=icon_name, icon_size=16)
left_box.add(icon)
self.title_label = Label(title)
self.title_label.set_style("font-size: 14px;")
left_box.add(self.title_label)
self.add(left_box)
# Derived classes can add controls to the right side
class QuickMenuToggle(QuickMenuItem):
"""A menu item with a toggle switch"""
def __init__(self, title, icon_name=None, active=False, on_toggle=None, **kwargs):
super().__init__(title, icon_name, **kwargs)
# Create a custom toggle using a button with state tracking
self._active = active
self._on_toggle = on_toggle
# Create toggle indicator box
self.toggle_box = Box(
orientation="h",
spacing=0
)
self.toggle_box.set_style("min-width: 44px; min-height: 24px; border-radius: 12px; padding: 2px;")
# Toggle indicator (circle)
self.toggle_indicator = Label("")
self.toggle_indicator.set_style("min-width: 20px; min-height: 20px; border-radius: 10px; background: white;")
self.toggle_box.add(self.toggle_indicator)
# Make it clickable
self.toggle_button = Button(
child=self.toggle_box,
on_clicked=self._on_click
)
self.toggle_button.set_style("background: transparent; border: none; padding: 0;")
# Add spacer to push toggle to the right
spacer = Label("", h_expand=True)
self.add(spacer)
self.add(self.toggle_button)
# Set initial state
self._update_appearance()
def _on_click(self, button):
self._active = not self._active
self._update_appearance()
if self._on_toggle:
self._on_toggle(self._active)
def _update_appearance(self):
if self._active:
self.toggle_box.set_style_classes(["toggle-active"])
self.toggle_box.set_style(
"min-width: 44px; min-height: 24px; border-radius: 12px; padding: 2px; "
"transition: all 0.2s;"
)
self.toggle_indicator.set_style(
"min-width: 20px; min-height: 20px; border-radius: 10px; "
"background: white; margin-left: 20px; transition: all 0.2s;"
)
else:
self.toggle_box.set_style_classes(["toggle-inactive"])
self.toggle_box.set_style(
"min-width: 44px; min-height: 24px; border-radius: 12px; padding: 2px; "
"transition: all 0.2s;"
)
self.toggle_indicator.set_style(
"min-width: 20px; min-height: 20px; border-radius: 10px; "
"background: white; margin-left: 0px; transition: all 0.2s;"
)
def set_active(self, active):
self._active = active
self._update_appearance()
def get_active(self):
return self._active
class QuickMenuButton(QuickMenuItem):
"""A menu item that acts as a button"""
def __init__(self, title, icon_name=None, on_click=None, **kwargs):
super().__init__(title, icon_name, **kwargs)
if on_click:
# Make the entire item clickable
button_overlay = Button(
child=Box(), # Empty box as child
on_clicked=on_click
)
button_overlay.set_style("background: transparent; border: none; padding: 0; margin: 0;")
# Add arrow indicator on the right
arrow = Label("")
arrow.set_style("font-size: 18px; opacity: 0.5;")
spacer = Label("", h_expand=True)
self.add(spacer)
self.add(arrow)
class QuickMenuSection(Box):
"""A section in the quick menu with optional title"""
def __init__(self, title=None, **kwargs):
super().__init__(
orientation="v",
spacing=4,
name="quick-menu-section",
**kwargs
)
if title:
title_label = Label(
title,
name="section-title"
)
title_label.set_style("font-size: 12px; opacity: 0.6; padding: 8px 12px 4px 12px; font-weight: bold;")
self.add(title_label)
self.items_box = Box(orientation="v", spacing=2)
self.add(self.items_box)
def add_item(self, item):
self.items_box.add(item)
class QuickMenu(Window):
def __init__(self, **kwargs):
super().__init__(
name="quick-menu",
layer="top",
anchor="top right",
margin="40px 10px 0px 0px",
exclusivity="none",
visible=False,
all_visible=False,
style_classes=["popup-window"],
**kwargs,
)
# Main container
self.main_box = Box(
orientation="v",
spacing=8,
name="quick-menu-container"
)
# Remove redundant styling since it's handled in stylix.css
pass
# Title
title_box = Box(
orientation="h",
spacing=8
)
title_box.set_style("padding: 12px;")
title = Label("Quick Menu")
title.set_style("font-size: 16px; font-weight: bold;")
title_box.add(title)
self.main_box.add(title_box)
# Add a simple divider line
divider = Label("")
divider.set_style("min-height: 1px; background: rgba(255,255,255,0.1); margin: 0px 12px;")
self.main_box.add(divider)
# Sections container
self.sections_container = Box(
orientation="v",
spacing=8
)
self.sections_container.set_style("padding: 8px 0px;")
self.main_box.add(self.sections_container)
self.children = self.main_box
self.set_size_request(360, -1)
# Store references to dynamic items
self.vinyl_toggle = None
self.sections = {}
def add_section(self, section_id, title=None):
"""Add a new section to the menu"""
section = QuickMenuSection(title=title)
self.sections[section_id] = section
self.sections_container.add(section)
# Add separator before section if not the first
if len(self.sections) > 1:
separator = Label("")
separator.set_style("min-height: 1px; background: rgba(255,255,255,0.1); margin: 4px 12px;")
self.sections_container.add(separator)
return section
def setup_audio_section(self, vinyl_service=None):
"""Setup the audio controls section"""
audio_section = self.add_section("audio", None) # No section title since it's the only section
# Vinyl passthrough toggle
if vinyl_service:
self.vinyl_toggle = QuickMenuToggle(
title="Vinyl Passthrough",
icon_name="folder-music-symbolic",
active=vinyl_service.active,
on_toggle=lambda active: self._on_vinyl_toggle(active, vinyl_service)
)
audio_section.add_item(self.vinyl_toggle)
# Store reference to vinyl service
self.vinyl_service = vinyl_service
def _on_vinyl_toggle(self, active, vinyl_service):
"""Handle vinyl toggle"""
logger.info(f"[QuickMenu] Vinyl toggled: {active}")
vinyl_service.active = active
def setup_system_section(self):
"""Setup system controls section"""
# Removed for now - can add system controls later
pass
def update_vinyl_state(self, active):
"""Update vinyl toggle state from external source"""
if self.vinyl_toggle:
self.vinyl_toggle.set_active(active)
class QuickMenuOpener(Button):
"""Button to open the quick menu"""
def __init__(self, icon_name="open-menu-symbolic", **kwargs):
super().__init__(
name="quick-menu-button",
child=Image(icon_name=icon_name, icon_size=16),
on_clicked=self.toggle_menu,
**kwargs
)
self.menu = QuickMenu()
self.menu_visible = False
def toggle_menu(self, button=None):
"""Toggle the quick menu visibility"""
if self.menu_visible:
logger.info("[QuickMenu] Hiding menu")
self.menu.set_visible(False)
self.menu_visible = False
else:
logger.info("[QuickMenu] Showing menu")
self.menu.set_visible(True)
self.menu.show_all()
self.menu_visible = True
def get_menu(self):
"""Get the menu instance for configuration"""
return self.menu

View File

@@ -143,8 +143,10 @@ def generate_stylix_css():
box-shadow: none; box-shadow: none;
}} }}
/* Calendar popup */ /* Generic popup styling */
#calendar-popup {{ .popup-window,
#calendar-popup,
#quick-menu {{
background-color: #{colors["base00"]}; background-color: #{colors["base00"]};
border: solid 2px #{colors["base02"]}; border: solid 2px #{colors["base02"]};
border-radius: 12px; border-radius: 12px;
@@ -284,6 +286,69 @@ tooltip>* {{
#workspaces>button.urgent {{ #workspaces>button.urgent {{
background-color: #{colors["base08"]}; background-color: #{colors["base08"]};
}} }}
/* Quick Menu styling */
#quick-menu-container {{
background-color: #{colors["base00"]};
border-radius: 8px;
}}
#quick-menu-button {{
background: transparent;
border: none;
padding: 4px;
margin: 0;
box-shadow: none;
color: #{colors["base05"]};
}}
#quick-menu-button:hover {{
background-color: #{colors["base01"]};
border-radius: 8px;
}}
.quick-menu-item {{
border-radius: 6px;
transition: background-color 0.15s ease;
}}
.quick-menu-item:hover {{
background-color: #{colors["base01"]};
}}
.section-title {{
color: #{colors["base04"]};
font-weight: bold;
font-size: {small_font}px;
}}
/* Vinyl button styling */
#vinyl-button {{
background-color: transparent;
color: #{colors["base05"]};
border: none;
padding: 4px;
margin: 0px;
border-radius: 8px;
}}
#vinyl-button.active {{
background-color: #{colors["base0B"]};
color: #{colors["base00"]};
}}
#vinyl-icon {{
color: inherit;
}}
/* Toggle switch styling for quick menu */
.toggle-active {{
background-color: #{colors["base0B"]};
}}
.toggle-inactive {{
background-color: #{colors["base02"]};
}}
""" """
# Write to temporary file # Write to temporary file