Skip to content

Commit ebf0119

Browse files
committed
chore: Bump version to 1.3.9
1 parent 883fe6b commit ebf0119

1 file changed

Lines changed: 104 additions & 27 deletions

File tree

core/configuration.py

Lines changed: 104 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
from dataclasses import dataclass, field
22
from typing import Dict, List, Optional
33
from PySide6.QtGui import QColor
4+
from PySide6.QtWidgets import QApplication
45
from .logging_config import get_logger
56

67
logger = get_logger(__name__)
78

9+
10+
@dataclass
11+
class DisplayConstants:
12+
"""Display and layout constants for the overlay."""
13+
LANE_START_X: int = 50
14+
EXTRA_PADDING: int = 50
15+
KEYVIEWER_OFFSET_Y_TOP: int = 50
16+
KEYVIEWER_OFFSET_Y_BOTTOM: int = 50
17+
FALLBACK_SCREEN_HEIGHT: int = 1080
18+
FADE_START_Y: int = 800
19+
FADE_RANGE: int = 200
20+
821
@dataclass
922
class VisualSettings:
23+
"""Visual settings for bar appearance and behavior."""
1024
scroll_speed: int = 800
1125
lane_width: int = 70
1226
bar_width: int = 70
1327
bar_height: int = 20
1428
bar_color_str: str = "0,255,255,200"
1529
fall_direction: str = "up"
1630

17-
# Display constants
18-
LANE_START_X: int = 50
19-
EXTRA_PADDING: int = 50
20-
KEYVIEWER_OFFSET_Y_TOP: int = 50
21-
KEYVIEWER_OFFSET_Y_BOTTOM: int = 50
22-
FALLBACK_SCREEN_HEIGHT: int = 1080
23-
2431
@property
2532
def bar_color(self) -> QColor:
26-
"""Parse and validate color string, returning QColor."""
33+
"""Parse and validate color string, returning QColor.
34+
35+
Returns:
36+
QColor object parsed from bar_color_str.
37+
38+
Raises:
39+
ValueError: If color string format is invalid.
40+
"""
2741
try:
2842
parts = self.bar_color_str.split(',')
2943
if len(parts) != 4:
@@ -36,38 +50,62 @@ def bar_color(self) -> QColor:
3650

3751
return QColor(r, g, b, a)
3852
except (ValueError, AttributeError) as e:
39-
logger.warning(f"Invalid color string '{self.bar_color_str}': {e}. Using default.")
53+
logger.error(f"Invalid color string '{self.bar_color_str}': {e}. Using default.")
4054
return QColor(0, 255, 255, 200)
4155

4256
@dataclass
4357
class PositionSettings:
58+
"""Overlay window position settings."""
4459
x: int = 0
4560
y: int = 0
4661

4762
def validate(self) -> bool:
48-
"""Validate position values are within reasonable bounds.
63+
"""Validate position values are within screen bounds.
64+
65+
Uses actual screen size when available, falls back to reasonable defaults.
4966
5067
Returns:
5168
True if all values were valid, False if any were clamped.
5269
"""
53-
MIN_POS = -10000
54-
MAX_POS = 10000
70+
# Get screen bounds for validation
71+
try:
72+
screen = QApplication.primaryScreen()
73+
if screen:
74+
size = screen.size()
75+
MIN_X = -size.width() // 2
76+
MIN_Y = -size.height() // 2
77+
MAX_X = size.width() * 2
78+
MAX_Y = size.height() * 2
79+
else:
80+
# Fallback bounds for headless or error cases
81+
MIN_X = -10000
82+
MIN_Y = -10000
83+
MAX_X = 10000
84+
MAX_Y = 10000
85+
except Exception:
86+
# If screen detection fails, use safe defaults
87+
MIN_X = -10000
88+
MIN_Y = -10000
89+
MAX_X = 10000
90+
MAX_Y = 10000
91+
5592
valid = True
5693

57-
if not (MIN_POS <= self.x <= MAX_POS):
58-
logger.warning(f"X position {self.x} out of range, clamping to [{MIN_POS}, {MAX_POS}]")
59-
self.x = max(MIN_POS, min(MAX_POS, self.x))
94+
if not (MIN_X <= self.x <= MAX_X):
95+
logger.warning(f"X position {self.x} out of range [{MIN_X}, {MAX_X}], clamping")
96+
self.x = max(MIN_X, min(MAX_X, self.x))
6097
valid = False
6198

62-
if not (MIN_POS <= self.y <= MAX_POS):
63-
logger.warning(f"Y position {self.y} out of range, clamping to [{MIN_POS}, {MAX_POS}]")
64-
self.y = max(MIN_POS, min(MAX_POS, self.y))
99+
if not (MIN_Y <= self.y <= MAX_Y):
100+
logger.warning(f"Y position {self.y} out of range [{MIN_Y}, {MAX_Y}], clamping")
101+
self.y = max(MIN_Y, min(MAX_Y, self.y))
65102
valid = False
66103

67104
return valid
68105

69106
@dataclass
70107
class KeyViewerSettings:
108+
"""KeyViewer panel display settings."""
71109
enabled: bool = True
72110
layout: str = "horizontal"
73111
panel_position: str = "below"
@@ -107,30 +145,69 @@ def validate(self) -> bool:
107145

108146
@dataclass
109147
class AppConfig:
148+
"""Main application configuration container.
149+
150+
Attributes:
151+
visual: Visual display settings
152+
position: Window position settings
153+
key_viewer: KeyViewer panel settings
154+
lane_map: Mapping from key strings to lane indices
155+
MAX_BARS: Maximum number of concurrent bars to render
156+
INPUT_LATENCY_OFFSET: Timing offset for input latency compensation
157+
DEBUG_MODE: Enable debug rendering and logging
158+
VERSION: Application version string
159+
CONFIG_VERSION: Config schema version for migrations
160+
display: Display and layout constants
161+
"""
110162
visual: VisualSettings = field(default_factory=VisualSettings)
111163
position: PositionSettings = field(default_factory=PositionSettings)
112164
key_viewer: KeyViewerSettings = field(default_factory=KeyViewerSettings)
113-
lane_map: Dict[str, int] = field(default_factory=dict)
114-
165+
lane_map: Dict[str, int] = field(default_factory=lambda: {'d': 0, 'f': 1, 'j': 2, 'k': 3})
166+
115167
# Constants
116168
MAX_BARS: int = 300
117169
INPUT_LATENCY_OFFSET: float = 0.0
118-
FADE_START_Y: int = 800
119-
FADE_RANGE: int = 200
120170
DEBUG_MODE: bool = False
121-
VERSION: str = "1.3.7"
122-
123-
def __post_init__(self):
124-
if not self.lane_map:
125-
self.lane_map = {'d': 0, 'f': 1, 'j': 2, 'k': 3}
171+
VERSION: str = "1.3.9"
172+
CONFIG_VERSION: int = 1 # Config schema version for migrations
173+
display: DisplayConstants = field(default_factory=DisplayConstants)
126174

127175
def set_lane_keys(self, keys: List[str]) -> None:
128176
"""Set lane key mapping.
129177
130178
Args:
131179
keys: List of key strings to map to lanes (by index).
180+
181+
Example:
182+
set_lane_keys(['d', 'f', 'j', 'k']) maps 'd' to lane 0, 'f' to lane 1, etc.
132183
"""
133184
new_map = {}
134185
for idx, key in enumerate(keys):
135186
new_map[key] = idx
136187
self.lane_map = new_map
188+
189+
def migrate_config(self, from_version: int) -> None:
190+
"""Migrate configuration from an older version.
191+
192+
Args:
193+
from_version: The config version to migrate from.
194+
195+
Example:
196+
migrate_config(0) # Migrate from version 0 to current version
197+
"""
198+
if from_version >= self.CONFIG_VERSION:
199+
return # Already at or past current version
200+
201+
# Migration from version 0 to 1
202+
if from_version == 0:
203+
# Add any new settings introduced in version 1
204+
logger.info(f"Migrating config from version {from_version} to {self.CONFIG_VERSION}")
205+
# No changes needed for v0->v1 migration
206+
207+
# Future migrations would go here
208+
# Example:
209+
# if from_version == 1:
210+
# # Migrate v1 to v2
211+
# pass
212+
213+
logger.info(f"Config migration complete, now at version {self.CONFIG_VERSION}")

0 commit comments

Comments
 (0)