Activity Bar Improvements:
- Draggable: Grab left handle to move anywhere on screen
- Resizable: Grab right handle to change width (400-1600px)
- Background Opacity: Now actually works (20-100%)
- Search toggle: Right-click menu to show/hide search box
- Clock toggle: Right-click menu to show/hide clock
- Reset Position: Right-click menu to reset to default position
- Settings dialog: Added opacity slider, width, search/clock toggles
Config additions:
- x, y: Custom position
- width: Bar width (400-1600)
- background_opacity: 20-100%
- show_search: Toggle search visibility
- show_clock: Toggle clock visibility
Bug Fixes:
- OverlayController now inherits QObject (fixes 'cannot be converted' error)
- Activity bar saves position when dragged
- Activity bar saves width when resized
Hotkey Changes:
- Ctrl+Shift+U: Now toggles Activity Bar (in-game overlay) instead of dashboard
- Ctrl+Shift+H: Still hides all overlays
- Ctrl+Shift+B: Also toggles Activity Bar (kept for compatibility)
UI Updates:
- Tray menu shows 'Activity Bar (Ctrl+Shift+U)' to indicate hotkey
- Dashboard no longer has a global hotkey (use tray icon or window)
- Updated startup messages to reflect new hotkey setup
- Plugins view now has 'Installed' and 'Store' tabs with real content
- Widgets view shows Clock, System Monitor, Skill Tracker with enable/disable
- Views embedded directly to avoid import issues
- Plugin list shows name, version, description with checkboxes
- Widget cards show name, source plugin, description, preview button
- Added debug output to diagnose why PluginsView isn't loading
- Added PluginsView and WidgetsView to core/ui/__init__.py exports
- Removed plugin_manager check that was causing fallback
- Will show console output to diagnose the issue
1. Sidebar toggle: Now uses arrows (chevron-right when collapsed, chevron-left when expanded)
2. Dashboard: Quick actions filtered to show only installed plugins
3. Plugins page: New tabs - 'Installed' (enable/disable plugins) and 'Store' (browse/install)
4. Widgets page: Now shows registered widgets from plugins with enable/disable
5. Settings page: Removed Plugin Store and My Plugins tabs (moved to Plugins page)
6. Settings: Changed 'Overlay Opacity' to 'Background Opacity' for clarity
7. Status bar: Shows plugin count, EU window status (Not Running/Running/Focused), version
8. New files: plugins_view.py, widgets_view.py with proper functionality
Changes:
- Default mode now OVERLAY_GAME_FOCUSED (Blish HUD style)
- Activity bar ONLY shows when EU window is focused
- Completely hidden on desktop and other windows
- Non-blocking focus detection with QTimer.singleShot
- Auto-disables if window detection is too slow (>500ms)
- Added Overlay Mode dropdown in Settings > General
- Options: Game Focused, Always Visible, Hotkey Toggle, Temporary, Desktop Only
Settings:
- activity_bar.overlay_mode: 'overlay_game' (default)
- Game Focused: Overlay strictly tied to EU window focus
- Import actual views from core.ui (DashboardView, SettingsView, PluginsView)
- Update _create_settings_view() to use SettingsView with full tabs
- Update _create_plugins_view() to use PluginsView with plugin management
- Keep fallback placeholders if views fail to load
- Add settings attribute to PerfectMainWindow for SettingsView compatibility
Views now show:
- Settings: General, Plugin Store, My Plugins, Hotkeys, Data & Backup, Updates tabs
- Plugins: Installed plugin list with enable/disable checkboxes
New OverlayController (core/overlay_controller.py):
- DESKTOP_APP: Activity bar only in desktop app
- OVERLAY_ALWAYS: Always visible as overlay
- OVERLAY_GAME_FOCUSED: Only when EU game window focused
- OVERLAY_HOTKEY_TOGGLE: Toggle with Ctrl+Shift+B (default)
- OVERLAY_TEMPORARY: Show 8 seconds on hotkey, then auto-hide
Changes:
- Activity bar now controlled by OverlayController
- Removed old _start_eu_focus_detection/_check_eu_focus methods
- Updated quit() to stop overlay controller
- Startup messages show current mode
Settings key: activity_bar.overlay_mode
Default: overlay_toggle
NavigationRail now supports expanded/collapsed states:
- Click menu toggle button to expand/collapse
- Expanded: Shows icon + label (200px width)
- Collapsed: Shows icon only (80px width)
- Smooth animated width transition
- Orange accent on active item maintained
NavigationDestination updated:
- Shows QLabel with text when expanded
- Icon-only when collapsed
- Proper styling for both states
Changes:
- Fixed is_focused property (was calling as method)
- EU focus detection now DISABLED by default
- Added auto_show_on_focus setting (default: false)
- Hard disable if focus check takes >1 second
- ActivityBarConfig now includes auto_show_on_focus setting
This prevents the EnumWindows blocking that was causing 4.5s freezes.
Users can manually enable in settings if they want auto-show behavior.
Problem: EnumWindows was iterating through ALL windows on system
when EU wasn't running, causing 4.5s blocking delays every 5s.
Changes:
- _find_window_by_title: 100ms timeout, 500 window limit
- _find_window_by_process: 150ms timeout, 300 window limit
- Skip empty window titles (performance)
- Slow down focus detection to 60s when EU not found 3x
This should eliminate the 2s menu + 7s click delays.
- New debug_logger.py with timing and stack trace capabilities
- Tray icon now logs all interactions with millisecond timing
- Main app startup is fully instrumented
- EU focus detection logs and warns if slow (>100ms)
- Menu interactions use QTimer.singleShot to avoid blocking
- Log file saved to Documents/Entropia Universe/Logs/
This will help identify the source of UI slowness (2s menu delay, 7s click delay)
The tray icon was blocking the main UI thread because:
1. QTimer was updating menu state every second
2. Complex stylesheet on menu may cause blocking
3. Emojis in menu items might cause encoding issues
Simplified:
- Removed update timer (no longer needed)
- Removed complex stylesheet (use default)
- Removed emojis from menu items
- Made TrayIcon inherit QWidget for proper parent
- Simplified signal connections
1. System Tray Icon (replaces floating button):
- Right-click menu with: Dashboard, Activity Bar, Settings, Quit
- Orange 'EU' icon
- Double-click to open dashboard
- Notifications support
2. Removed Floating Icon:
- No more floating button on desktop
- All interaction through tray icon
3. EU Window Focus Detection:
- Activity bar auto-shows when EU is focused
- Activity bar auto-hides when EU loses focus
- Checks every 500ms for focus changes
- Tray icon checkbox reflects activity bar state
New Files:
- core/tray_icon.py: System tray implementation
Modified:
- core/main.py: Use tray instead of floating icon, add focus detection
Usage:
- Start EU-Utility: Tray icon appears in system tray
- Open EU game: Activity bar appears automatically
- Alt-tab away from EU: Activity bar hides
- Right-click tray icon: Access settings, toggle bar, quit
The hide_timer QTimer was being created after _apply_config() was called,
but _apply_config() tries to set the timer interval. This caused:
AttributeError: 'WindowsTaskbar' object has no attribute 'hide_timer'
Fix: Move hide_timer creation to before _apply_config() call.
Complete redesign of the in-game Activity Bar:
- Transparent background (no visible background)
- Windows-style start button (⊞ icon)
- Search box with rounded corners (Windows 11 style)
- Pinned plugins expand the bar dynamically
- Clean minimal design
- System clock display
- Right-click context menu for settings
- Auto-hide functionality
- Draggable positioning
Features:
- Click ⊞ button to open app drawer
- Type in search box to find plugins
- Pin plugins to taskbar for quick access
- Clock shows current time (updates every minute)
- Right-click for settings
The bar now looks like a floating Windows taskbar
perfect for in-game overlay use.
1. Removed box-shadow CSS properties (not supported by Qt)
2. Fixed TypeError: 'QVBoxLayout' object is not callable
- Removed redundant placeholder.layout() call
- Layout was already set up correctly
App should launch without errors now.
Line 351 had malformed docstring with 8 quotes instead of 3:
- Before: """Handle incoming webhooks."""""" (8 quotes)
- After: """Handle incoming webhooks.""" (3 quotes)
This caused Python to report an unterminated string at EOF.
File now parses correctly.
- Created compatibility shim at core/plugin_api.py
- Imports from new core/api/plugin_api.py
- Existing plugins continue to work without changes
- Encourages migration to: from core.api import get_api
NEW: core/api/ directory with comprehensive three-tier API
PluginAPI (core/api/plugin_api.py):
- 12 core service integrations (Log, Window, OCR, Screenshot, Nexus, HTTP, Audio, Notifications, Clipboard, Event Bus, Data Store, Tasks)
- Full docstrings with examples for every method
- Thread-safe design with Qt signal marshaling
- Proper error handling with custom exceptions
- Service availability checking
WidgetAPI (core/api/widget_api.py):
- Widget creation and management
- WidgetConfig dataclass for configuration
- WidgetType enum (MINI, CONTROL, CHART, ALERT, CUSTOM)
- WidgetAnchor enum for positioning
- Event system (moved, resized, closing, closed, update)
- Layout helpers (grid, horizontal, vertical, cascade)
- Persistence (save/load widget states)
- Widget presets for reuse
ExternalAPI (core/api/external_api.py):
- REST API server with aiohttp
- API endpoint registration (decorator and programmatic)
- Incoming webhooks with HMAC verification
- Outgoing webhook POST support
- API key authentication
- IPC (inter-process communication)
- File watcher for config changes
- Server-Sent Events (SSE) support
- CORS configuration
- Webhook history tracking
core/api/__init__.py:
- Unified imports for all three APIs
- Version tracking (2.2.0)
- Clean namespace exports
docs/API_REFERENCE.md:
- Comprehensive 12,000+ word reference
- Quick start examples for each API
- Service-by-service documentation
- Error handling guide
- Integration examples (Discord, custom widget)
Integration:
- Updated core/main.py to import from new API structure
- All three APIs available via: from core.api import get_api, get_widget_api, get_external_api
Benefits:
- Clear separation of concerns (plugins vs widgets vs external)
- Well-documented APIs for developers
- Easy to extend with new services
- Type hints throughout
- Production-ready error handling
- Third-party integration support out of the box
- Added Ctrl+Shift+B hotkey to toggle activity bar visibility
- Added _on_activity_bar_hotkey() handler method
- Updated startup messages to show all hotkeys including activity bar
Hotkeys:
- Ctrl+Shift+U: Toggle main overlay
- Ctrl+Shift+H: Hide all overlays
- Ctrl+Shift+B: Toggle activity bar
NEW: core/activity_bar.py
- ActivityBar class for in-game overlay
- Configurable layouts: Horizontal, Vertical, Grid
- Pinned plugins in bar
- Plugin drawer (app drawer style)
- Draggable, resizable
- Opacity and size controls
- Auto-hide feature
- Settings dialog
Features:
- Horizontal/Vertical/Grid layouts
- Adjustable icon size (32-96px)
- Opacity slider (20-100%)
- Auto-hide when not in use
- Plugin drawer with all enabled plugins
- Click plugin to open mini widget or full UI
- Drag to reposition anywhere on screen
INTEGRATION:
- Added to core/main.py
- Auto-created on startup if enabled
- Toggle with Ctrl+Shift+B (configurable)
- Integrated with plugin manager
Usage:
- Install plugins
- They appear in Activity Bar (if pinned) or Drawer
- Click to open mini widget or full UI
- Right-click for settings
This provides a macOS-style dock experience for in-game use,
while keeping the desktop app for configuration.
ISSUE: Widgets tab was built once at startup. If a plugin was installed
after the app started, the widget wouldn't show up until restart.
FIX:
1. Added _refresh_widgets_tab() method that rebuilds the tab content
2. Called _refresh_widgets_tab() when switching to Widgets tab
3. Added debug logging to show registered widgets count
4. Lists all found widgets in console for debugging
Now when you:
1. Install Clock Widget plugin
2. Click on 🎨 Widgets tab
3. The tab refreshes and shows the Clock Widget
The widget appears immediately without needing to restart!
Widgets have Qt::WindowDoesNotAcceptFocus flag set so they don't
steal focus from the game. Calling activateWindow() on them
produces a warning.
Removed activateWindow() call since:
1. Widgets should not accept focus (they're overlays)
2. raise_() is sufficient to bring them to front
3. WindowDoesNotAcceptFocus is intentional for game overlay widgets
ISSUE: Clock and System Monitor widgets were created but not visible.
The widgets were being garbage collected because they weren't stored.
FIX:
1. Store widget in self._active_widgets list to prevent GC
2. Set parent=self to keep widget alive with overlay
3. Position widget at center of screen (visible location)
4. Call raise_() and activateWindow() to bring to front
5. Added debug output showing widget position
WIDGET CREATION NOW:
- Creates widget with parent
- Positions at center of screen
- Shows, raises, activates
- Stores reference in list
- Prints position for debugging
The issue was that installed plugins (calculator, clock_widget, etc.)
could not import 'plugins.base_plugin' because the plugins package
wasn't properly in Python's module path when loading via
spec_from_file_location.
FIX:
1. Added project root to sys.path so 'plugins' package is findable
2. Added _ensure_plugins_package() method to preload plugins module
3. Modified discover_plugins() to try normal import first
4. Added proper error logging with traceback for debugging
This ensures installed plugins can properly import BasePlugin
from plugins.base_plugin as expected.
Installed plugins (calculator, clock_widget, etc.) import from
plugins.base_plugin, but we moved BasePlugin to core.base_plugin.
This shim re-exports BasePlugin so installed plugins work without
needing to know about core module structure.
FIX:
- plugins/base_plugin.py now re-exports from core.base_plugin
- Installed plugins can continue using: from plugins.base_plugin import BasePlugin
This maintains backward compatibility for all installable plugins.
NEW UI LAYOUT:
- Added tab bar at top of content area with 3 tabs:
* 🔌 Plugins - Shows plugin list from sidebar + plugin content
* 🎨 Widgets - Widget gallery to add overlay widgets
* ⚙️ Settings - All settings in one place
WIDGETS TAB FEATURES:
- Built-in widgets section with Clock and System Monitor
- Each widget has Add button to create overlay
- Plugin widgets section (placeholder for plugin-added widgets)
- Descriptions for each widget type
SETTINGS TAB FEATURES:
- Moved settings from dialog into dedicated tab
- Sub-tabs: Plugins, Plugin Store, Hotkeys, Appearance, About
- Removed Settings button from header (now in tabs)
CHANGES:
- _create_content_area() replaced with _create_content_area_with_tabs()
- Added _switch_tab() method
- Added _create_plugins_tab(), _create_widgets_tab(), _create_settings_tab()
- Added _create_widget_button() helper
- Added _add_clock_widget() and _add_system_monitor_widget()
- Removed Settings button from header bar
- Removed old _open_settings() dialog approach
The UI is now organized with clear navigation between
plugins, widgets, and settings via top tabs.
CHANGES:
1. Fixed plugin download to use raw git files instead of git clone
- Avoids Windows permission issues with .git/objects
- Downloads __init__.py and plugin.py directly
2. Added clickable dependencies button showing full dependency dialog
- Shows Core Services Required
- Shows Plugins Required
- Lists what will be auto-installed
3. Integrated Plugin Store into Settings dialog
- Added 🔌 Store tab to Settings
- Plugin Store is now built-in, not a plugin
4. Removed plugins/settings/ and plugins/plugin_store_ui/
- Settings is now built into overlay_window.py
- Users access via Settings button
5. Added _create_plugin_store_tab() method to OverlayWindow
NOTE: Pull latest EU-Utility-Plugins-Repo to get Clock Widget
The base_plugin.py was removed from plugins/ folder during cleanup
but is still needed by core/plugin_manager.py and built-in plugins.
CHANGES:
- Restored base_plugin.py from plugin repo to core/base_plugin.py
- Updated imports in core/plugin_manager.py
- Updated imports in core/plugin_manager_optimized.py
- Updated imports in plugins/settings/plugin.py
- Updated imports in plugins/plugin_store_ui/plugin.py
This fixes ModuleNotFoundError when starting EU-Utility.