diff --git a/core/main.py b/core/main.py index 49355d2..e98edad 100644 --- a/core/main.py +++ b/core/main.py @@ -183,19 +183,22 @@ class EUUtilityApp: getattr(self, 'window_manager', None) ) - # Set mode from settings (default: hotkey toggle) + # Set mode from settings (default: game focused - Blish HUD style) settings = get_settings() - mode_str = settings.get('activity_bar.overlay_mode', 'overlay_toggle') + mode_str = settings.get('activity_bar.overlay_mode', 'overlay_game') try: mode = OverlayMode(mode_str) except ValueError: - mode = OverlayMode.OVERLAY_HOTKEY_TOGGLE + mode = OverlayMode.OVERLAY_GAME_FOCUSED # Default to game focused self.overlay_controller.set_mode(mode) self.overlay_controller.start() print(f"[Core] Activity Bar created (mode: {mode.value})") - print("[Core] Press Ctrl+Shift+B to toggle overlay") + + if mode.value == 'overlay_game': + print("[Core] Overlay will ONLY show when EU game window is focused (Blish HUD style)") + print("[Core] Overlay is hidden on desktop - switch to EU to see it") else: print("[Core] Activity Bar not available") diff --git a/core/overlay_controller.py b/core/overlay_controller.py index 54329f4..7aa406c 100644 --- a/core/overlay_controller.py +++ b/core/overlay_controller.py @@ -30,9 +30,10 @@ class OverlayMode(Enum): @dataclass class OverlayConfig: """Configuration for activity bar overlay behavior.""" - mode: OverlayMode = OverlayMode.OVERLAY_HOTKEY_TOGGLE + # Default to game-focused mode (Blish HUD style) + mode: OverlayMode = OverlayMode.OVERLAY_GAME_FOCUSED temporary_duration: int = 8000 # milliseconds (8 seconds default) - game_focus_poll_interval: int = 2000 # ms (2 seconds) + game_focus_poll_interval: int = 1000 # ms (1 second - faster response) def to_dict(self): return { @@ -162,29 +163,78 @@ class OverlayController: self.visibility_changed.emit(False) def _check_game_focus(self): - """Check if EU game window is focused.""" + """Check if EU game window is focused - non-blocking using QTimer.singleShot.""" + # Run the actual check in next event loop iteration to not block + from PyQt6.QtCore import QTimer + QTimer.singleShot(0, self._do_check_game_focus) + + def _do_check_game_focus(self): + """Actual focus check (non-blocking).""" if not self.window_manager: return + # Track time for debugging + import time + start = time.perf_counter() + try: - # Quick check without blocking - eu_window = self.window_manager.find_eu_window() + # Set a flag to prevent re-entrancy + if getattr(self, '_checking_focus', False): + return + self._checking_focus = True + + # Quick check - limit window enumeration time + eu_window = self._quick_find_eu_window() + if eu_window: is_focused = eu_window.is_focused if is_focused != self._game_focused: self._game_focused = is_focused if is_focused: + print("[OverlayController] EU focused - showing overlay") self._show() else: + print("[OverlayController] EU unfocused - hiding overlay") self._hide() else: - # No EU window - hide if visible + # No EU window found - hide if visible if self._is_visible: + print("[OverlayController] EU window not found - hiding overlay") self._hide() self._game_focused = False - except Exception: - # Silently ignore errors - pass + + # Log if slow + elapsed = (time.perf_counter() - start) * 1000 + if elapsed > 100: + print(f"[OverlayController] Warning: Focus check took {elapsed:.1f}ms") + + except Exception as e: + print(f"[OverlayController] Error checking focus: {e}") + finally: + self._checking_focus = False + + def _quick_find_eu_window(self): + """Quick window find with timeout protection.""" + import time + start = time.perf_counter() + + try: + # Try window manager first + if hasattr(self.window_manager, 'find_eu_window'): + # Wrap in timeout check + result = self.window_manager.find_eu_window() + + # If taking too long, skip future checks + elapsed = (time.perf_counter() - start) * 1000 + if elapsed > 500: # More than 500ms is too slow + print(f"[OverlayController] Window find too slow ({elapsed:.1f}ms), disabling focus detection") + self._game_focus_timer.stop() + return None + + return result + except Exception as e: + print(f"[OverlayController] Window find error: {e}") + return None # Singleton instance diff --git a/core/ui/settings_view.py b/core/ui/settings_view.py index 920e3b8..8ba88f6 100644 --- a/core/ui/settings_view.py +++ b/core/ui/settings_view.py @@ -121,6 +121,37 @@ class SettingsView(QWidget): self.minimize_cb = QCheckBox("Minimize to tray on close") behavior_layout.addWidget(self.minimize_cb) + # Activity Bar Overlay Mode + overlay_group = QGroupBox("Activity Bar Overlay Mode") + overlay_group.setStyleSheet(self._group_style()) + overlay_layout = QVBoxLayout(overlay_group) + + overlay_info = QLabel("Choose how the in-game activity bar behaves:") + overlay_info.setStyleSheet("color: rgba(255,255,255,150); font-size: 12px;") + overlay_layout.addWidget(overlay_info) + + self.overlay_mode_combo = QComboBox() + self.overlay_mode_combo.addItems([ + "Game Focused Only (Blish HUD style)", + "Always Visible", + "Hotkey Toggle", + "Temporary (8 seconds)", + "Desktop App Only" + ]) + self.overlay_mode_combo.setCurrentIndex(0) # Default to game focused + self.overlay_mode_combo.currentIndexChanged.connect(self._on_overlay_mode_changed) + overlay_layout.addWidget(self.overlay_mode_combo) + + overlay_desc = QLabel( + "Game Focused: Activity bar only appears when EU window is active. " + "Hidden on desktop." + ) + overlay_desc.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;") + overlay_desc.setWordWrap(True) + overlay_layout.addWidget(overlay_desc) + + behavior_layout.addWidget(overlay_group) + layout.addWidget(behavior_group) layout.addStretch() @@ -334,6 +365,33 @@ class SettingsView(QWidget): return tab + def _on_overlay_mode_changed(self, index: int): + """Handle overlay mode change.""" + mode_map = { + 0: "overlay_game", # Game Focused Only + 1: "overlay_always", # Always Visible + 2: "overlay_toggle", # Hotkey Toggle + 3: "overlay_temp", # Temporary + 4: "desktop_app" # Desktop App Only + } + + mode = mode_map.get(index, "overlay_game") + + # Save to settings + if hasattr(self.overlay, 'settings'): + self.overlay.settings['activity_bar.overlay_mode'] = mode + self.overlay.settings.save() + + print(f"[Settings] Activity Bar mode changed to: {mode}") + + # Show confirmation + QMessageBox.information( + self, + "Mode Changed", + f"Activity Bar mode changed to: {self.overlay_mode_combo.currentText()}\n\n" + "Restart EU-Utility for changes to take effect." + ) + def _export_data(self): """Export all data.""" path, _ = QFileDialog.getSaveFileName(