diff --git a/core/configuration.py b/core/configuration.py index a68f928..2c8795c 100644 --- a/core/configuration.py +++ b/core/configuration.py @@ -15,8 +15,6 @@ class DisplayConstants: KEYVIEWER_OFFSET_Y_TOP: int = 50 KEYVIEWER_OFFSET_Y_BOTTOM: int = 50 FALLBACK_SCREEN_HEIGHT: int = 1080 - FADE_START_Y: int = 800 - FADE_RANGE: int = 200 @dataclass class VisualSettings: @@ -114,6 +112,9 @@ class KeyViewerSettings: show_counts: bool = True height: int = 60 opacity: float = 0.2 + fade_position_y: int = 800 + fade_length_y: int = 200 + fade_trigger: str = "head" def validate(self) -> bool: """Validate KeyViewer settings. @@ -135,6 +136,32 @@ def validate(self) -> bool: self.opacity = max(0.0, min(1.0, self.opacity)) valid = False + # Attempt to get maximum screen height from all connected screens + try: + screens = QApplication.screens() + if screens: + max_screen_y = max(screen.size().height() for screen in screens) + else: + max_screen_y = 5000 + except Exception: + max_screen_y = 5000 + + # Validate fade parameters with respect to maximum screen size + if not (0 <= self.fade_position_y <= max_screen_y): + logger.warning(f"Fade position {self.fade_position_y} out of range [0, {max_screen_y}], clamping") + self.fade_position_y = max(0, min(max_screen_y, self.fade_position_y)) + valid = False + if not (10 <= self.fade_length_y <= max_screen_y): + logger.warning(f"Fade length {self.fade_length_y} out of range [10, {max_screen_y}], clamping") + self.fade_length_y = max(0, min(max_screen_y, self.fade_length_y)) + valid = False + + # Validate fade trigger + if self.fade_trigger not in ("head", "tail"): + logger.warning(f"Fade trigger '{self.fade_trigger}' not in ('head', 'tail'), using 'head'") + self.fade_trigger = "head" + valid = False + # Validate panel_position if self.panel_position not in ("above", "below"): logger.warning(f"Invalid panel_position '{self.panel_position}', using 'below'") diff --git a/core/gui.py b/core/gui.py index 41ed643..0b9a115 100644 --- a/core/gui.py +++ b/core/gui.py @@ -31,7 +31,7 @@ def __init__(self, settings_manager: SettingsManager) -> None: self.settings = settings_manager self.config = self.settings.app_config self.setWindowTitle(f"RainingKeys Config v{self.config.VERSION}") - self.resize(400, 670) + self.resize(410, 722) # Apply Theme self.setStyleSheet(DARK_THEME) diff --git a/core/overlay.py b/core/overlay.py index 44a87f9..9be523b 100644 --- a/core/overlay.py +++ b/core/overlay.py @@ -375,8 +375,9 @@ def _draw_active_bars( kv_offset_x = self.config.key_viewer.panel_offset_x bar_color = self.config.visual.bar_color - fade_start_y = self.config.display.FADE_START_Y - fade_range = self.config.display.FADE_RANGE + fade_position_y = self.config.key_viewer.fade_position_y + fade_length_y = self.config.key_viewer.fade_length_y + fade_trigger = self.config.key_viewer.fade_trigger input_latency = self.config.INPUT_LATENCY_OFFSET # Bar Drawing Loop @@ -422,10 +423,12 @@ def _draw_active_bars( continue # 5. Fade Logic + trigger_dist = dist_head if fade_trigger == "head" else dist_tail + alpha = 1.0 - if dist_head > fade_start_y: - dist_into_fade = dist_head - fade_start_y - factor = 1.0 - (dist_into_fade / fade_range) + if trigger_dist > fade_position_y: + dist_into_fade = trigger_dist - fade_position_y + factor = 1.0 - (dist_into_fade / fade_length_y) alpha = max(0.0, min(1.0, factor)) if alpha <= 0.0: diff --git a/core/settings_manager.py b/core/settings_manager.py index a428fee..a0a4569 100644 --- a/core/settings_manager.py +++ b/core/settings_manager.py @@ -59,6 +59,9 @@ def load(self) -> None: kv.show_counts = self.config_parser.getboolean('keyviewer', 'show_counts', fallback=True) kv.height = self.config_parser.getint('keyviewer', 'height', fallback=60) kv.opacity = self.config_parser.getfloat('keyviewer', 'opacity', fallback=0.2) + kv.fade_position_y = self.config_parser.getint('keyviewer', 'fade_position_y', fallback=800) + kv.fade_length_y = self.config_parser.getint('keyviewer', 'fade_length_y', fallback=200) + kv.fade_trigger = self.config_parser.get('keyviewer', 'fade_trigger', fallback="head") # Lanes if self.config_parser.has_section('lanes'): @@ -142,6 +145,9 @@ def save(self) -> None: self.config_parser.set('keyviewer', 'show_counts', str(kv.show_counts)) self.config_parser.set('keyviewer', 'height', str(kv.height)) self.config_parser.set('keyviewer', 'opacity', str(kv.opacity)) + self.config_parser.set('keyviewer', 'fade_position_y', str(kv.fade_position_y)) + self.config_parser.set('keyviewer', 'fade_length_y', str(kv.fade_length_y)) + self.config_parser.set('keyviewer', 'fade_trigger', kv.fade_trigger) # Lanes if not self.config_parser.has_section('lanes'): self.config_parser.add_section('lanes') diff --git a/core/ui/components.py b/core/ui/components.py index 95df31e..8afeaf7 100644 --- a/core/ui/components.py +++ b/core/ui/components.py @@ -1,6 +1,6 @@ from typing import Optional from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSpinBox, + QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSpinBox, QComboBox, QGroupBox, QPushButton, QCheckBox, QColorDialog ) from PySide6.QtCore import Signal @@ -281,6 +281,44 @@ def init_ui(self) -> None: self.chk_kv_counts.setChecked(self.config.key_viewer.show_counts) self.chk_kv_counts.stateChanged.connect(self.on_change) layout.addWidget(self.chk_kv_counts) + + # Attempt to get maximum screen height from all connected screens + try: + screens = QApplication.screens() + if screens: + max_screen_y = max(screen.size().height() for screen in screens) + else: + max_screen_y = 5000 + except Exception: + max_screen_y = 5000 + + # Fade Controls + fade_layout = QHBoxLayout() + fade_layout.addWidget(QLabel("Fade Y:")) + self.spin_kv_fade_pos = QSpinBox() + self.spin_kv_fade_pos.setRange(0, max_screen_y) + self.spin_kv_fade_pos.setValue(self.config.key_viewer.fade_position_y) + self.spin_kv_fade_pos.valueChanged.connect(self.on_change) + fade_layout.addWidget(self.spin_kv_fade_pos) + + fade_layout.addWidget(QLabel("Length:")) + self.spin_kv_fade_len = QSpinBox() + self.spin_kv_fade_len.setRange(10, max_screen_y) + self.spin_kv_fade_len.setValue(self.config.key_viewer.fade_length_y) + self.spin_kv_fade_len.valueChanged.connect(self.on_change) + fade_layout.addWidget(self.spin_kv_fade_len) + layout.addLayout(fade_layout) + + # Fade Trigger + trigger_layout = QHBoxLayout() + trigger_layout.addWidget(QLabel("Fade Trigger:")) + self.combo_kv_fade_trig = QComboBox() + self.combo_kv_fade_trig.addItems(["head", "tail"]) + self.combo_kv_fade_trig.setCurrentText(self.config.key_viewer.fade_trigger) + self.combo_kv_fade_trig.currentTextChanged.connect(self.on_change) + trigger_layout.addWidget(self.combo_kv_fade_trig) + trigger_layout.addStretch() + layout.addLayout(trigger_layout) self.setLayout(layout) @@ -293,6 +331,9 @@ def on_change(self) -> None: self.config.key_viewer.panel_offset_y = self.spin_kv_off_y.value() self.config.key_viewer.show_counts = self.chk_kv_counts.isChecked() self.config.key_viewer.opacity = self.spin_kv_opacity.value() / 100.0 + self.config.key_viewer.fade_position_y = self.spin_kv_fade_pos.value() + self.config.key_viewer.fade_length_y = self.spin_kv_fade_len.value() + self.config.key_viewer.fade_trigger = self.combo_kv_fade_trig.currentText() self.settings.save() def update_from_config(self) -> None: @@ -304,7 +345,9 @@ def update_from_config(self) -> None: self.spin_kv_off_x, self.spin_kv_off_y, self.spin_kv_opacity, - self.chk_kv_counts + self.chk_kv_counts, + self.spin_kv_fade_pos, + self.spin_kv_fade_len ): self.chk_kv_enabled.setChecked(self.config.key_viewer.enabled) self.spin_kv_height.setValue(self.config.key_viewer.height) @@ -316,3 +359,6 @@ def update_from_config(self) -> None: self.spin_kv_off_y.setValue(self.config.key_viewer.panel_offset_y) self.spin_kv_opacity.setValue(int(self.config.key_viewer.opacity * 100)) self.chk_kv_counts.setChecked(self.config.key_viewer.show_counts) + self.spin_kv_fade_pos.setValue(self.config.key_viewer.fade_position_y) + self.spin_kv_fade_len.setValue(self.config.key_viewer.fade_length_y) + self.combo_kv_fade_trig.setCurrentText(self.config.key_viewer.fade_trigger)