feat: Blish HUD-style overlay mode - Activity Bar only in EU game window
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
This commit is contained in:
parent
3bf2d9d5b1
commit
0f1c41b58d
11
core/main.py
11
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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue