diff --git a/core/activity_bar.py b/core/activity_bar.py index 0624ae0..b8d0a4b 100644 --- a/core/activity_bar.py +++ b/core/activity_bar.py @@ -1,93 +1,102 @@ """ -EU-Utility - Activity Bar (In-Game Overlay) +EU-Utility - Windows Taskbar Style Activity Bar +=============================================== -macOS-style activity bar/dock for in-game use. -Features: plugin drawer, mini widgets, floating widgets, -customizable layout (vertical/horizontal/grid), size, opacity. +Windows 11-style taskbar for in-game overlay. +Features: Transparent background, search box, pinned plugins, +clean minimal design that expands as needed. """ import json from pathlib import Path from typing import Dict, List, Optional, Callable from dataclasses import dataclass, asdict -from enum import Enum from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, - QFrame, QScrollArea, QGridLayout, QMenu, QDialog, - QSlider, QComboBox, QCheckBox, QSpinBox, QTabWidget + QFrame, QLineEdit, QMenu, QDialog, QSlider, QComboBox, + QCheckBox, QSpinBox, QApplication, QSizePolicy ) -from PyQt6.QtCore import Qt, QPoint, QSize, QTimer, pyqtSignal -from PyQt6.QtGui import QMouseEvent, QPainter, QColor, QIcon, QPixmap - - -class ActivityBarLayout(Enum): - """Activity bar layout modes.""" - HORIZONTAL = "horizontal" - VERTICAL = "vertical" - GRID = "grid" +from PyQt6.QtCore import Qt, QPoint, QSize, QTimer, pyqtSignal, QPropertyAnimation, QEasingCurve +from PyQt6.QtGui import QMouseEvent, QPainter, QColor, QFont, QIcon, QPixmap @dataclass class ActivityBarConfig: """Activity bar configuration.""" enabled: bool = True - layout: ActivityBarLayout = ActivityBarLayout.HORIZONTAL - position: str = "bottom" # top, bottom, left, right - size: int = 48 # Icon size in pixels - opacity: float = 0.9 - always_visible: bool = False + position: str = "bottom" # top, bottom + icon_size: int = 32 auto_hide: bool = True - show_labels: bool = False - grid_columns: int = 4 # For grid layout - grid_rows: int = 2 - pinned_plugins: List[str] = None # Plugin IDs to show in bar + auto_hide_delay: int = 3000 # milliseconds + pinned_plugins: List[str] = None def __post_init__(self): if self.pinned_plugins is None: self.pinned_plugins = [] + + def to_dict(self): + return { + 'enabled': self.enabled, + 'position': self.position, + 'icon_size': self.icon_size, + 'auto_hide': self.auto_hide, + 'auto_hide_delay': self.auto_hide_delay, + 'pinned_plugins': self.pinned_plugins + } + + @classmethod + def from_dict(cls, data): + return cls( + enabled=data.get('enabled', True), + position=data.get('position', 'bottom'), + icon_size=data.get('icon_size', 32), + auto_hide=data.get('auto_hide', True), + auto_hide_delay=data.get('auto_hide_delay', 3000), + pinned_plugins=data.get('pinned_plugins', []) + ) -class ActivityBar(QFrame): +class WindowsTaskbar(QFrame): """ - macOS-style activity bar for in-game overlay. + Windows 11-style taskbar for in-game overlay. Features: - - Plugin drawer (app drawer style) - - Pinned plugins in bar - - Mini widgets - - Configurable layout (horizontal/vertical/grid) - - Draggable, resizable - - Opacity control + - Transparent background (no background visible) + - Windows-style start button + - Search box for quick access + - Pinned plugins expand the bar + - Clean, minimal design """ - widget_requested = pyqtSignal(str) # plugin_id - drawer_toggled = pyqtSignal(bool) # is_open + widget_requested = pyqtSignal(str) + search_requested = pyqtSignal(str) def __init__(self, plugin_manager, parent=None): super().__init__(parent) self.plugin_manager = plugin_manager self.config = self._load_config() - self.drawer_open = False + + # State self.pinned_buttons: Dict[str, QPushButton] = {} - self.mini_widgets: Dict[str, QWidget] = {} + self.is_expanded = False + self.search_text = "" self._setup_window() self._setup_ui() self._apply_config() - # Auto-hide timer + # Auto-hide self.hide_timer = QTimer(self) self.hide_timer.timeout.connect(self._auto_hide) - self.hide_timer.setInterval(3000) # 3 seconds - # Show initially + # Show if enabled if self.config.enabled: self.show() def _setup_window(self): - """Setup window properties for overlay.""" + """Setup frameless overlay window.""" self.setWindowFlags( Qt.WindowType.FramelessWindowHint | Qt.WindowType.WindowStaysOnTopHint | @@ -95,150 +104,152 @@ class ActivityBar(QFrame): Qt.WindowType.WindowDoesNotAcceptFocus ) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) - self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) - # Make draggable + # Draggable state self._dragging = False self._drag_offset = QPoint() def _setup_ui(self): - """Setup the activity bar UI.""" - self.main_layout = QVBoxLayout(self) - self.main_layout.setContentsMargins(4, 4, 4, 4) - self.main_layout.setSpacing(2) - - # Main container - self.container = QFrame() - self.container.setObjectName("activityBarContainer") - self.main_layout.addWidget(self.container) - - # Container layout based on config - self._update_container_layout() - - # Add pinned plugins - self._refresh_pinned_plugins() - - # Drawer button (always last) - self.drawer_btn = self._create_drawer_button() - self._add_to_container(self.drawer_btn) - - # Setup drawer panel - self._setup_drawer() - - def _update_container_layout(self): - """Update container layout based on config.""" - # Clear existing layout - old_layout = self.container.layout() - if old_layout: - while old_layout.count(): - item = old_layout.takeAt(0) - if item.widget(): - item.widget().setParent(None) - import sip - sip.delete(old_layout) - - # Create new layout - if self.config.layout == ActivityBarLayout.HORIZONTAL: - layout = QHBoxLayout(self.container) - elif self.config.layout == ActivityBarLayout.VERTICAL: - layout = QVBoxLayout(self.container) - else: # GRID - layout = QGridLayout(self.container) - - layout.setContentsMargins(6, 6, 6, 6) + """Setup Windows taskbar-style UI.""" + # Main layout with minimal margins + layout = QHBoxLayout(self) + layout.setContentsMargins(8, 4, 8, 4) layout.setSpacing(4) - self.container.setLayout(layout) - # Update container style - self._update_style() - - def _update_style(self): - """Update container styling.""" - opacity = int(self.config.opacity * 255) - border_radius = 12 if self.config.layout == ActivityBarLayout.GRID else 20 - - self.container.setStyleSheet(f""" - QFrame#activityBarContainer {{ - background-color: rgba(35, 40, 55, {opacity}); - border: 1px solid rgba(100, 110, 130, 100); - border-radius: {border_radius}px; - }} + # Style: No background, just floating elements + self.setStyleSheet(""" + WindowsTaskbar { + background: transparent; + border: none; + } """) - - def _create_drawer_button(self) -> QPushButton: - """Create the app drawer button.""" - btn = QPushButton("⋮⋮⋮") # Drawer icon - btn.setFixedSize(self.config.size, self.config.size) - btn.setStyleSheet(f""" - QPushButton {{ - background-color: rgba(255, 140, 66, 200); + + # === START BUTTON (Windows icon style) === + self.start_btn = QPushButton("⊞") # Windows-like icon + self.start_btn.setFixedSize(40, 40) + self.start_btn.setStyleSheet(""" + QPushButton { + background: rgba(255, 255, 255, 0.1); color: white; border: none; - border-radius: {self.config.size // 4}px; - font-size: 16px; + border-radius: 8px; + font-size: 20px; font-weight: bold; - }} - QPushButton:hover {{ - background-color: rgba(255, 140, 66, 255); - }} + } + QPushButton:hover { + background: rgba(255, 255, 255, 0.2); + } + QPushButton:pressed { + background: rgba(255, 255, 255, 0.15); + } """) - btn.setToolTip("Plugin Drawer") - btn.clicked.connect(self._toggle_drawer) - return btn + self.start_btn.setToolTip("Open App Drawer") + self.start_btn.clicked.connect(self._toggle_drawer) + layout.addWidget(self.start_btn) + + # === SEARCH BOX (Windows 11 style) === + self.search_box = QLineEdit() + self.search_box.setFixedSize(200, 36) + self.search_box.setPlaceholderText("Search plugins...") + self.search_box.setStyleSheet(""" + QLineEdit { + background: rgba(255, 255, 255, 0.08); + color: white; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 18px; + padding: 0 16px; + font-size: 13px; + } + QLineEdit:hover { + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.15); + } + QLineEdit:focus { + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 140, 66, 0.5); + } + QLineEdit::placeholder { + color: rgba(255, 255, 255, 0.4); + } + """) + self.search_box.returnPressed.connect(self._on_search) + self.search_box.textChanged.connect(self._on_search_text_changed) + layout.addWidget(self.search_box) + + # Separator + separator = QFrame() + separator.setFixedSize(1, 24) + separator.setStyleSheet("background: rgba(255, 255, 255, 0.1);") + layout.addWidget(separator) + + # === PINNED PLUGINS AREA (expandable) === + self.pinned_container = QWidget() + self.pinned_layout = QHBoxLayout(self.pinned_container) + self.pinned_layout.setContentsMargins(0, 0, 0, 0) + self.pinned_layout.setSpacing(4) + layout.addWidget(self.pinned_container) + + # Spacer to push icons together + layout.addStretch() + + # === SYSTEM TRAY AREA === + # Add a small clock or status indicator + self.clock_label = QLabel("12:00") + self.clock_label.setStyleSheet(""" + color: rgba(255, 255, 255, 0.7); + font-size: 12px; + padding: 0 8px; + """) + self.clock_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(self.clock_label) + + # Start clock update timer + self.clock_timer = QTimer(self) + self.clock_timer.timeout.connect(self._update_clock) + self.clock_timer.start(60000) # Update every minute + self._update_clock() + + # Setup context menu + self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) + self.customContextMenuRequested.connect(self._show_context_menu) + + # Refresh pinned plugins + self._refresh_pinned_plugins() def _create_plugin_button(self, plugin_id: str, plugin_class) -> QPushButton: - """Create a plugin button for the bar.""" + """Create a pinned plugin button (taskbar icon style).""" btn = QPushButton() - btn.setFixedSize(self.config.size, self.config.size) + size = self.config.icon_size + btn.setFixedSize(size + 8, size + 8) - # Get icon - icon_name = getattr(plugin_class, 'icon', 'box') - try: - from core.icon_manager import get_icon_manager - icon_mgr = get_icon_manager() - pixmap = icon_mgr.get_pixmap(icon_name, size=self.config.size - 12) - btn.setIcon(QIcon(pixmap)) - btn.setIconSize(QSize(self.config.size - 12, self.config.size - 12)) - except: - # Fallback to emoji - btn.setText("📦") + # Get plugin icon or use default + icon_text = getattr(plugin_class, 'icon', '◆') + btn.setText(icon_text) - # Style btn.setStyleSheet(f""" QPushButton {{ - background-color: rgba(60, 65, 80, 180); + background: transparent; + color: white; border: none; - border-radius: {self.config.size // 4}px; + border-radius: 6px; + font-size: {size // 2}px; }} QPushButton:hover {{ - background-color: rgba(80, 85, 100, 220); + background: rgba(255, 255, 255, 0.1); + }} + QPushButton:pressed {{ + background: rgba(255, 255, 255, 0.05); }} """) - # Tooltip btn.setToolTip(plugin_class.name) - - # Click handler btn.clicked.connect(lambda: self._on_plugin_clicked(plugin_id)) return btn - def _add_to_container(self, widget: QWidget): - """Add widget to container based on layout.""" - layout = self.container.layout() - - if isinstance(layout, QGridLayout): - # For grid, calculate position - count = layout.count() - col = count % self.config.grid_columns - row = count // self.config.grid_columns - layout.addWidget(widget, row, col) - else: - layout.addWidget(widget) - def _refresh_pinned_plugins(self): - """Refresh pinned plugins in the bar.""" - # Clear existing pinned buttons (except drawer) + """Refresh pinned plugin buttons.""" + # Clear existing for btn in self.pinned_buttons.values(): btn.deleteLater() self.pinned_buttons.clear() @@ -255,10 +266,25 @@ class ActivityBar(QFrame): plugin_class = all_plugins[plugin_id] btn = self._create_plugin_button(plugin_id, plugin_class) self.pinned_buttons[plugin_id] = btn - self._add_to_container(btn) + self.pinned_layout.addWidget(btn) - def _setup_drawer(self): - """Setup the plugin drawer panel.""" + def _toggle_drawer(self): + """Toggle the app drawer (like Windows Start menu).""" + # Create drawer if not exists + if not hasattr(self, 'drawer') or self.drawer is None: + self._create_drawer() + + if self.drawer.isVisible(): + self.drawer.hide() + else: + # Position above the taskbar + bar_pos = self.pos() + self.drawer.move(bar_pos.x(), bar_pos.y() - self.drawer.height()) + self.drawer.show() + self.drawer.raise_() + + def _create_drawer(self): + """Create the app drawer popup.""" self.drawer = QFrame(self.parent()) self.drawer.setWindowFlags( Qt.WindowType.FramelessWindowHint | @@ -266,148 +292,179 @@ class ActivityBar(QFrame): Qt.WindowType.Tool ) self.drawer.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) - self.drawer.hide() + self.drawer.setFixedSize(400, 500) - # Drawer layout - drawer_layout = QVBoxLayout(self.drawer) - drawer_layout.setContentsMargins(12, 12, 12, 12) - drawer_layout.setSpacing(8) - - # Drawer header - header = QLabel("Plugin Drawer") - header.setStyleSheet(""" - color: white; - font-size: 16px; - font-weight: bold; - """) - drawer_layout.addWidget(header) - - # Scroll area for plugins - scroll = QScrollArea() - scroll.setWidgetResizable(True) - scroll.setFrameShape(QFrame.Shape.NoFrame) - scroll.setStyleSheet("background: transparent;") - - self.drawer_content = QWidget() - self.drawer_layout = QGridLayout(self.drawer_content) - self.drawer_layout.setSpacing(8) - - scroll.setWidget(self.drawer_content) - drawer_layout.addWidget(scroll) - - # Refresh drawer content - self._refresh_drawer() - - def _refresh_drawer(self): - """Refresh plugin drawer content.""" - # Clear existing - while self.drawer_layout.count(): - item = self.drawer_layout.takeAt(0) - if item.widget(): - item.widget().deleteLater() - - if not self.plugin_manager: - return - - all_plugins = self.plugin_manager.get_all_discovered_plugins() - - col = 0 - row = 0 - for plugin_id, plugin_class in all_plugins.items(): - # Skip already pinned - if plugin_id in self.config.pinned_plugins: - continue - - # Create plugin item - item = self._create_drawer_item(plugin_id, plugin_class) - self.drawer_layout.addWidget(item, row, col) - - col += 1 - if col >= 4: # 4 columns - col = 0 - row += 1 - - def _create_drawer_item(self, plugin_id: str, plugin_class) -> QFrame: - """Create a drawer item for a plugin.""" - frame = QFrame() - frame.setFixedSize(80, 80) - frame.setStyleSheet(""" + # Drawer style: subtle frosted glass + self.drawer.setStyleSheet(""" QFrame { - background-color: rgba(50, 55, 70, 200); - border-radius: 8px; - } - QFrame:hover { - background-color: rgba(70, 75, 90, 220); + background: rgba(32, 32, 32, 0.95); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; } """) - layout = QVBoxLayout(frame) - layout.setContentsMargins(4, 4, 4, 4) - layout.setSpacing(2) + layout = QVBoxLayout(self.drawer) + layout.setContentsMargins(16, 16, 16, 16) + layout.setSpacing(12) - # Icon - icon_label = QLabel("📦") - icon_label.setStyleSheet("font-size: 24px;") - icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.addWidget(icon_label) + # Header + header = QLabel("All Plugins") + header.setStyleSheet("color: white; font-size: 16px; font-weight: bold;") + layout.addWidget(header) - # Name - name_label = QLabel(plugin_class.name[:8]) - name_label.setStyleSheet("color: white; font-size: 9px;") - name_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.addWidget(name_label) + # Search in drawer + drawer_search = QLineEdit() + drawer_search.setPlaceholderText("Search...") + drawer_search.setStyleSheet(""" + QLineEdit { + background: rgba(255, 255, 255, 0.08); + color: white; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + padding: 8px 12px; + } + """) + layout.addWidget(drawer_search) - # Click to open - frame.mousePressEvent = lambda e, pid=plugin_id: self._on_drawer_item_clicked(pid) + # Plugin grid + plugins_widget = QWidget() + plugins_layout = QVBoxLayout(plugins_widget) + plugins_layout.setSpacing(8) - return frame + if self.plugin_manager: + all_plugins = self.plugin_manager.get_all_discovered_plugins() + for plugin_id, plugin_class in all_plugins.items(): + item = self._create_drawer_item(plugin_id, plugin_class) + plugins_layout.addWidget(item) + + plugins_layout.addStretch() + layout.addWidget(plugins_widget) - def _toggle_drawer(self): - """Toggle the plugin drawer.""" - self.drawer_open = not self.drawer_open - - if self.drawer_open: - # Position drawer near activity bar - pos = self.pos() - if self.config.layout == ActivityBarLayout.HORIZONTAL: - self.drawer.move(pos.x(), pos.y() - 300) - else: - self.drawer.move(pos.x() + self.width(), pos.y()) - - self.drawer.resize(360, 300) - self.drawer.show() - self.drawer_toggled.emit(True) - else: - self.drawer.hide() - self.drawer_toggled.emit(False) + def _create_drawer_item(self, plugin_id: str, plugin_class) -> QPushButton: + """Create a drawer item (like Start menu app).""" + btn = QPushButton(f" {getattr(plugin_class, 'icon', '◆')} {plugin_class.name}") + btn.setFixedHeight(44) + btn.setStyleSheet(""" + QPushButton { + background: transparent; + color: white; + border: none; + border-radius: 8px; + text-align: left; + font-size: 13px; + } + QPushButton:hover { + background: rgba(255, 255, 255, 0.1); + } + """) + btn.clicked.connect(lambda: self._on_drawer_item_clicked(plugin_id)) + return btn def _on_plugin_clicked(self, plugin_id: str): - """Handle plugin button click.""" - print(f"[ActivityBar] Plugin clicked: {plugin_id}") + """Handle pinned plugin click.""" + print(f"[Taskbar] Plugin clicked: {plugin_id}") self.widget_requested.emit(plugin_id) + self._pulse_animation() def _on_drawer_item_clicked(self, plugin_id: str): """Handle drawer item click.""" - print(f"[ActivityBar] Drawer item clicked: {plugin_id}") - self.widget_requested.emit(plugin_id) - self._toggle_drawer() # Close drawer + self.drawer.hide() + self._on_plugin_clicked(plugin_id) + + def _on_search(self): + """Handle search box return.""" + text = self.search_box.text().strip() + if text: + self.search_requested.emit(text) + print(f"[Taskbar] Search: {text}") + + def _on_search_text_changed(self, text: str): + """Handle search text changes.""" + self.search_text = text + # Could implement live filtering here + + def _pulse_animation(self): + """Subtle pulse animation on interaction.""" + anim = QPropertyAnimation(self, b"minimumHeight") + anim.setDuration(150) + anim.setStartValue(self.height()) + anim.setEndValue(self.height() + 2) + anim.setEasingCurve(QEasingCurve.Type.OutQuad) + anim.start() + + def _update_clock(self): + """Update clock display.""" + from datetime import datetime + self.clock_label.setText(datetime.now().strftime("%H:%M")) + + def _show_context_menu(self, position): + """Show right-click context menu.""" + menu = QMenu(self) + menu.setStyleSheet(""" + QMenu { + background: rgba(40, 40, 40, 0.95); + color: white; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + padding: 8px; + } + QMenu::item { + padding: 8px 24px; + border-radius: 4px; + } + QMenu::item:selected { + background: rgba(255, 255, 255, 0.1); + } + """) + + settings_action = menu.addAction("⚙️ Settings") + settings_action.triggered.connect(self._show_settings) + + menu.addSeparator() + + hide_action = menu.addAction("🗕 Hide") + hide_action.triggered.connect(self.hide) + + menu.exec(self.mapToGlobal(position)) + + def _show_settings(self): + """Show settings dialog.""" + dialog = TaskbarSettingsDialog(self.config, self) + if dialog.exec(): + self.config = dialog.get_config() + self._save_config() + self._apply_config() + self._refresh_pinned_plugins() + + def _apply_config(self): + """Apply configuration.""" + # Set auto-hide timer + self.hide_timer.setInterval(self.config.auto_hide_delay) + + # Position bar + screen = QApplication.primaryScreen().geometry() + if self.config.position == "bottom": + self.move((screen.width() - 600) // 2, screen.height() - 60) + else: # top + self.move((screen.width() - 600) // 2, 10) + + # Size + self.setFixedSize(600, 50) def _auto_hide(self): - """Auto-hide the activity bar.""" - if self.config.auto_hide and not self.config.always_visible: - # Check if mouse is over bar or drawer - if not self.underMouse() and not self.drawer.underMouse(): + """Auto-hide when mouse leaves.""" + if self.config.auto_hide and not self.underMouse(): + if not hasattr(self, 'drawer') or not self.drawer.isVisible(): self.hide() def enterEvent(self, event): - """Mouse entered activity bar.""" - if self.config.auto_hide: - self.hide_timer.stop() + """Mouse entered - stop hide timer.""" + self.hide_timer.stop() super().enterEvent(event) def leaveEvent(self, event): - """Mouse left activity bar.""" - if self.config.auto_hide and not self.drawer_open: + """Mouse left - start hide timer.""" + if self.config.auto_hide: self.hide_timer.start() super().leaveEvent(event) @@ -419,7 +476,7 @@ class ActivityBar(QFrame): event.accept() def mouseMoveEvent(self, event: QMouseEvent): - """Drag activity bar.""" + """Drag window.""" if self._dragging: new_pos = event.globalPosition().toPoint() - self._drag_offset self.move(new_pos) @@ -428,66 +485,33 @@ class ActivityBar(QFrame): """Stop dragging.""" if event.button() == Qt.MouseButton.LeftButton: self._dragging = False - self._save_position() - - def _apply_config(self): - """Apply configuration.""" - self.setWindowOpacity(self.config.opacity) - self._update_style() - - # Resize based on layout and size - if self.config.layout == ActivityBarLayout.HORIZONTAL: - self.resize(400, self.config.size + 20) - elif self.config.layout == ActivityBarLayout.VERTICAL: - self.resize(self.config.size + 20, 400) - else: # GRID - self.resize( - self.config.grid_columns * (self.config.size + 8) + 20, - self.config.grid_rows * (self.config.size + 8) + 20 - ) def _load_config(self) -> ActivityBarConfig: - """Load activity bar configuration.""" + """Load configuration.""" config_path = Path("config/activity_bar.json") if config_path.exists(): try: data = json.loads(config_path.read_text()) - return ActivityBarConfig(**data) + return ActivityBarConfig.from_dict(data) except: pass return ActivityBarConfig() def _save_config(self): - """Save activity bar configuration.""" + """Save configuration.""" config_path = Path("config/activity_bar.json") config_path.parent.mkdir(parents=True, exist_ok=True) - config_path.write_text(json.dumps(asdict(self.config), indent=2)) - - def _save_position(self): - """Save bar position.""" - # TODO: Save position to config - pass - - def show_settings_dialog(self): - """Show activity bar settings dialog.""" - dialog = ActivityBarSettingsDialog(self.config, self) - if dialog.exec(): - self.config = dialog.get_config() - self._apply_config() - self._save_config() - self._refresh_pinned_plugins() + config_path.write_text(json.dumps(self.config.to_dict(), indent=2)) -class ActivityBarSettingsDialog(QDialog): - """Settings dialog for activity bar.""" +class TaskbarSettingsDialog(QDialog): + """Settings dialog for Windows Taskbar.""" def __init__(self, config: ActivityBarConfig, parent=None): super().__init__(parent) - self.config = config - self.setWindowTitle("Activity Bar Settings") - self.setMinimumSize(400, 500) - + self.setWindowTitle("Taskbar Settings") + self.setMinimumSize(350, 300) self._setup_ui() def _setup_ui(self): @@ -496,47 +520,24 @@ class ActivityBarSettingsDialog(QDialog): layout = QVBoxLayout(self) - # Form form = QFormLayout() - # Enabled - self.enabled_cb = QCheckBox("Enable Activity Bar") - self.enabled_cb.setChecked(self.config.enabled) - form.addRow(self.enabled_cb) - - # Layout - self.layout_combo = QComboBox() - self.layout_combo.addItems(["Horizontal", "Vertical", "Grid"]) - self.layout_combo.setCurrentText(self.config.layout.value.title()) - form.addRow("Layout:", self.layout_combo) - - # Size - self.size_spin = QSpinBox() - self.size_spin.setRange(32, 96) - self.size_spin.setValue(self.config.size) - form.addRow("Icon Size:", self.size_spin) - - # Opacity - self.opacity_slider = QSlider(Qt.Orientation.Horizontal) - self.opacity_slider.setRange(20, 100) - self.opacity_slider.setValue(int(self.config.opacity * 100)) - form.addRow("Opacity:", self.opacity_slider) - # Auto-hide self.autohide_cb = QCheckBox("Auto-hide when not in use") self.autohide_cb.setChecked(self.config.auto_hide) form.addRow(self.autohide_cb) - # Show labels - self.labels_cb = QCheckBox("Show plugin labels") - self.labels_cb.setChecked(self.config.show_labels) - form.addRow(self.labels_cb) + # Position + self.position_combo = QComboBox() + self.position_combo.addItems(["Bottom", "Top"]) + self.position_combo.setCurrentText(self.config.position.title()) + form.addRow("Position:", self.position_combo) - # Grid config - self.grid_cols = QSpinBox() - self.grid_cols.setRange(2, 8) - self.grid_cols.setValue(self.config.grid_columns) - form.addRow("Grid Columns:", self.grid_cols) + # Icon size + self.icon_size = QSpinBox() + self.icon_size.setRange(24, 48) + self.icon_size.setValue(self.config.icon_size) + form.addRow("Icon Size:", self.icon_size) layout.addLayout(form) @@ -549,32 +550,24 @@ class ActivityBarSettingsDialog(QDialog): layout.addWidget(buttons) def get_config(self) -> ActivityBarConfig: - """Get updated configuration.""" - layout_map = { - "Horizontal": ActivityBarLayout.HORIZONTAL, - "Vertical": ActivityBarLayout.VERTICAL, - "Grid": ActivityBarLayout.GRID - } - + """Get updated config.""" return ActivityBarConfig( - enabled=self.enabled_cb.isChecked(), - layout=layout_map.get(self.layout_combo.currentText(), ActivityBarLayout.HORIZONTAL), - size=self.size_spin.value(), - opacity=self.opacity_slider.value() / 100, + enabled=True, + position=self.position_combo.currentText().lower(), + icon_size=self.icon_size.value(), auto_hide=self.autohide_cb.isChecked(), - show_labels=self.labels_cb.isChecked(), - grid_columns=self.grid_cols.value(), + auto_hide_delay=self.config.auto_hide_delay, pinned_plugins=self.config.pinned_plugins ) # Global instance -_activity_bar: Optional[ActivityBar] = None +_taskbar_instance: Optional[WindowsTaskbar] = None -def get_activity_bar(plugin_manager=None) -> Optional[ActivityBar]: - """Get or create global activity bar instance.""" - global _activity_bar - if _activity_bar is None and plugin_manager: - _activity_bar = ActivityBar(plugin_manager) - return _activity_bar +def get_activity_bar(plugin_manager=None) -> Optional[WindowsTaskbar]: + """Get or create global taskbar instance.""" + global _taskbar_instance + if _taskbar_instance is None and plugin_manager: + _taskbar_instance = WindowsTaskbar(plugin_manager) + return _taskbar_instance