diff --git a/modules/controlcenter/main.py b/modules/controlcenter/main.py index bf372a7..7360b01 100644 --- a/modules/controlcenter/main.py +++ b/modules/controlcenter/main.py @@ -73,7 +73,11 @@ class ModusControlCenter(Window): ) self.wlan_label = Label( - label=wlan, name="wifi-widget-label", max_chars_width=20, h_align="start" + label=wlan, + name="wifi-widget-label", + max_chars_width=15, + h_align="start", + ellipsization="end", ) if bluetooth != "disabled": if bluetooth.startswith("connected:"): @@ -87,7 +91,8 @@ class ModusControlCenter(Window): self.bluetooth_label = Label( label=bluetooth_display, name="bluetooth-widget-label", - max_chars_width=20, + max_chars_width=15, + ellipsization="end", h_align="start", ) self.volume_scale = Scale( diff --git a/modules/controlcenter/per_app_volume.py b/modules/controlcenter/per_app_volume.py index 18a87fb..ef45fb0 100644 --- a/modules/controlcenter/per_app_volume.py +++ b/modules/controlcenter/per_app_volume.py @@ -7,6 +7,8 @@ from fabric.widgets.button import Button from fabric.widgets.label import Label from fabric.widgets.scale import Scale from fabric.widgets.scrolledwindow import ScrolledWindow +from fabric.widgets.image import Image +from fabric.widgets.separator import Separator # Local imports from utils.roam import audio_service @@ -19,7 +21,6 @@ class PerAppVolumeControl(Box): super().__init__( orientation="vertical", name="per-app-volume-control", - style_classes="menu", spacing=5, **kwargs, ) @@ -35,7 +36,7 @@ class PerAppVolumeControl(Box): self.apps_container = Box( orientation="vertical", name="apps-scrolled-container", - spacing=5, + spacing=2, ) self.scrolled_window = ScrolledWindow( @@ -77,6 +78,57 @@ class PerAppVolumeControl(Box): """Return to main control center view""" self.control_center.close_per_app_volume() + def _get_app_icon(self, app): + """Get icon for application""" + # Don't use fabric's generic audio icon, use description/name for better detection + fabric_icon = getattr(app, "icon_name", "") + if fabric_icon and fabric_icon != "audio": + return fabric_icon + + # Use description first (more accurate), then name + app_description = getattr(app, "description", "").lower() + app_name = app.name.lower() + + # Check description first as it's more reliable + search_text = app_description if app_description else app_name + + icon_mapping = { + "spotify": "spotify", + "firefox": "firefox", + "chromium": "chromium-browser", + "chrome": "google-chrome", + "vlc": "vlc", + "discord": "discord", + "steam": "steam", + "zen": "zen-browser", + "code": "vscode", + "visual studio code": "vscode", + "telegram": "telegram-desktop", + "pulse": "audio-card", + "zen": "zen-browser", + "pipewire": "audio-card", + "alsa": "audio-card", + "sink": "audio-speakers", + "source": "audio-input-microphone", + "youtube": "youtube", + "music": "rhythmbox", + "media": "multimedia-player", + } + + # Check if any key in mapping is contained in search text + for key, icon in icon_mapping.items(): + if key in search_text: + return icon + + # Also check app name as fallback + if search_text != app_name: + for key, icon in icon_mapping.items(): + if key in app_name: + return icon + + # Default audio icon for unknown apps + return "audio-volume-high" + def _format_app_name(self, name): """Format application name with proper capitalization""" if not name: @@ -115,14 +167,25 @@ class PerAppVolumeControl(Box): return applications = getattr(audio_service, "applications", []) - + if applications: - for app in applications: + for i, app in enumerate(applications): if hasattr(app, "name") and hasattr(app, "volume"): app_widget = self._create_app_control(app) self.apps_container.children = list( self.apps_container.children ) + [app_widget] + + # Add separator between apps (except for last one) + if i < len(applications) - 1: + separator = Separator( + orientation="horizontal", + style_classes="app-volume-separator", + ) + self.apps_container.children = list( + self.apps_container.children + ) + [separator] + self._app_widgets[app.name] = (app_widget, app) else: self._show_no_apps_message() @@ -138,28 +201,43 @@ class PerAppVolumeControl(Box): self.apps_container.children = [message] def _create_app_control(self, app): - """Create volume control for a single application""" - # Format and truncate app name + """Create compact volume control for a single application""" + # Format app name (shorter for compact layout) app_name = self._format_app_name(app.name) - if len(app_name) > 20: - app_name = app_name[:17] + "..." - # Get current volume from fabric audio service - # Fabric returns volume as a float, typically 0.0 to max_volume (default 100) current_volume = getattr(app, "volume", 0.0) max_vol = getattr(audio_service, "max_volume", 100) if audio_service else 100 - + # Ensure volume is in valid range volume_percent = max(0, min(current_volume, max_vol)) - # Volume scale - use fabric's max_volume as range + # App icon - use the same approach as expanded player + icon_name = self._get_app_icon(app) + app_icon = Image( + icon_name=icon_name, + name="app-icon", + icon_size=16, + style_classes="app-icon", + ) + + # App name label + name_label = Label( + label=app_name, + style_classes="app-name-compact", + justification="left", + h_align="start", + max_chars_width=10, + ellipsization="end", + ) + + # Volume scale (smaller and horizontal) volume_scale = Scale( value=volume_percent, min_value=0, max_value=max_vol, increments=(1, 5), - name="apple-volume-slider", - size=28, + name="compact-volume-slider", + size=20, h_expand=True, ) @@ -169,20 +247,19 @@ class PerAppVolumeControl(Box): lambda scale, scroll_type, value, app=app: self._set_app_volume(app, value), ) - # Create the app control widget + # Create horizontal compact layout app_control = Box( - orientation="vertical", - spacing=8, - style_classes="apple-app-volume-item", + orientation="horizontal", + spacing=5, + h_expand=True, + style_classes="compact-app-volume-item", children=[ - Label( - label=app_name, - style_classes="apple-app-name", - h_align="start", - ), + app_icon, + name_label, volume_scale, ], ) + # Add separator after the control return app_control @@ -195,14 +272,16 @@ class PerAppVolumeControl(Box): try: # Get max volume from audio service - max_vol = getattr(audio_service, "max_volume", 100) if audio_service else 100 - + max_vol = ( + getattr(audio_service, "max_volume", 100) if audio_service else 100 + ) + # Ensure volume is within bounds volume_value = max(0, min(volume_value, max_vol)) - + # Set volume directly - fabric expects the actual volume value, not percentage app.volume = volume_value - + except Exception as e: print(f"Error setting volume for {app.name}: {e}") finally: @@ -234,4 +313,3 @@ class PerAppVolumeControl(Box): self._updating_volumes.clear() super().destroy() - diff --git a/styles/controlcenter.css b/styles/controlcenter.css index 610f1ba..96e620e 100644 --- a/styles/controlcenter.css +++ b/styles/controlcenter.css @@ -284,14 +284,31 @@ } #control-center-menu slider { - background-image: none; - background-color: transparent; - padding: 2px; - min-width: 4px; - min-height: 20px; - border-radius: 20px; + background: linear-gradient(135deg, #ffffff 0%, #f0f1f2 100%); + border-color: #000; + min-width: 28px; + min-height: 28px; + margin: -2px; + border-radius: 50%; + /* background-image: none; */ + /* background-color: transparent; */ + /* min-width: 4px; */ + /* min-height: 40px; */ + /* margin: -9px; */ } +#control-center-menu slider:hover { + border-color: #999; + min-width: 30px; + min-height: 30px; + margin: -3px; + border-radius: 50%; + /* background-image: none; */ + /* background-color: transparent; */ + /* min-width: 4px; */ + /* min-height: 40px; */ + /* margin: -9px; */ +} #control-center-menu scale { background-color: transparent; margin-top: 10px; @@ -324,17 +341,14 @@ /* Per-app volume control styles */ #per-app-volume-control { - margin: 0px; - - background-color: alpha(#999, 0.1); - min-height: 140px; - - min-width: 140px; - + min-width: 350px; + /* background-color: alpha(#000, 0.3); */ + background-color: alpha(#999, 0.1); + /* box-shadow: inset 0 0 200px 0 alpha(#111, 0.3); */ border: 0.5px solid alpha(#000, 0.4); - border-radius: 8px; + padding: 1rem; /* padding: 1rem; */ } @@ -353,7 +367,6 @@ font-weight: 500; transition: background-color 0.2s ease; } - #back-button:hover { background-color: alpha(#fff, 0.18); } @@ -750,3 +763,24 @@ background: #ffffff; box-shadow: 0 0 0 1px alpha(#fff, 0.3); /* Smaller glow */ } + +/* Per app-volume-item */ + +.compact-app-volume-item { + padding-bottom: 5px; + padding-right: 14px; + border-bottom: 1px solid alpha(#fff, 0.1); +} +#app-icon { + margin-top: 8px; +} +.app-name-compact { + font-size: 14px; + font-weight: bold; + margin-top: 8px; +} + +#compact-app-volume-slider scale { + margin-right: 8px; + padding-right: 8px; +}