diff --git a/negpy/desktop/view/styles/modern_dark.qss b/negpy/desktop/view/styles/modern_dark.qss index 956c4f94..b0e96dae 100644 --- a/negpy/desktop/view/styles/modern_dark.qss +++ b/negpy/desktop/view/styles/modern_dark.qss @@ -124,6 +124,17 @@ QSlider::sub-page:horizontal { border-radius: 3px; } +/* Disabled state — grey out locked/overridden controls. Relies entirely on + Qt's automatic isEnabled()-driven :disabled propagation from a parent + widget.setEnabled(False); no per-callsite changes needed. */ +QSlider::handle:horizontal:disabled { background: #555555; } +QSlider::groove:horizontal:disabled { background: #1A1A1A; } +QSlider::sub-page:horizontal:disabled { background: #2A2A2A; } + +QSlider#cyan_slider::handle:horizontal:disabled, +QSlider#magenta_slider::handle:horizontal:disabled, +QSlider#yellow_slider::handle:horizontal:disabled { background: #555555; } + QListView { background-color: #0D0D0D; border: 1px solid #262626; @@ -183,6 +194,14 @@ QComboBox:focus, QLineEdit:focus, QDoubleSpinBox:focus, QSpinBox:focus, QTextEdi background-color: #0D0D0D; } +QComboBox:disabled, QLineEdit:disabled, QDoubleSpinBox:disabled, QSpinBox:disabled, QTextEdit:disabled { + color: #555555; + border: 1px solid #1A1A1A; +} + +QPushButton:disabled, QToolButton:disabled { color: #555555; } +QCheckBox:disabled { color: #555555; } + QStatusBar { background-color: #B71C1C; color: white; diff --git a/negpy/desktop/view/widgets/sliders.py b/negpy/desktop/view/widgets/sliders.py index 5ec4b1cc..de36e46f 100644 --- a/negpy/desktop/view/widgets/sliders.py +++ b/negpy/desktop/view/widgets/sliders.py @@ -292,7 +292,14 @@ def setValue(self, value: float) -> None: super().setValue(value) self._update_edited_state() + def setEnabled(self, enabled: bool) -> None: + super().setEnabled(enabled) + self._update_edited_state() + def _update_edited_state(self) -> None: + if not self.isEnabled(): + self.label.setStyleSheet(slider_label_qss(THEME.text_muted, edited=False)) + return edited = abs(self.spin.value() - self._default) > 1e-6 self.label.setStyleSheet(slider_label_qss(self._label_color, edited)) @@ -347,10 +354,13 @@ def __init__(self, label: str, default_val: float = 0.0, parent=None): def _apply_hue(self, hue_deg: float) -> None: """Update slider handle to match the current hue; label stays grey (yellow when edited).""" - h = int(hue_deg) % 360 - color = QColor.fromHsv(h, 200, 210) + if self.isEnabled(): + h = int(hue_deg) % 360 + color = QColor.fromHsv(h, 200, 210).name() + else: + color = THEME.text_muted self._update_edited_state() - self.slider.setStyleSheet(slider_handle_qss(color.name())) + self.slider.setStyleSheet(slider_handle_qss(color)) def _on_slider_changed(self, value: int) -> None: super()._on_slider_changed(value) @@ -360,6 +370,10 @@ def setValue(self, value: float) -> None: super().setValue(value) self._apply_hue(value) + def setEnabled(self, enabled: bool) -> None: + super().setEnabled(enabled) + self._apply_hue(self.value()) + def _kelvin_handle_color(kelvin: float) -> QColor: """Blackbody colour (Tanner Helland approximation), softened to the same @@ -394,7 +408,8 @@ def _from_int(self, i: int) -> float: return round(1e6 / (i / 10.0) / 10.0) * 10.0 def _apply_temp(self, kelvin: float) -> None: - self.slider.setStyleSheet(slider_handle_qss(_kelvin_handle_color(kelvin).name())) + color = _kelvin_handle_color(kelvin).name() if self.isEnabled() else THEME.text_muted + self.slider.setStyleSheet(slider_handle_qss(color)) def _on_slider_changed(self, value: int) -> None: super()._on_slider_changed(value) @@ -404,6 +419,10 @@ def setValue(self, value: float) -> None: super().setValue(value) self._apply_temp(self.value()) + def setEnabled(self, enabled: bool) -> None: + super().setEnabled(enabled) + self._apply_temp(self.value()) + class RangeSlider(QWidget): """ diff --git a/tests/test_slider_widgets.py b/tests/test_slider_widgets.py index cad90159..1cd9d075 100644 --- a/tests/test_slider_widgets.py +++ b/tests/test_slider_widgets.py @@ -3,6 +3,7 @@ from PyQt6.QtCore import QEvent, QPointF, Qt from PyQt6.QtGui import QMouseEvent +from negpy.desktop.view.styles.theme import THEME from negpy.desktop.view.widgets.sliders import CompactSlider, KelvinSlider @@ -92,6 +93,31 @@ def test_kelvin_slider_handle_tracks_temperature(qapp): s.timer.stop() +def test_compact_slider_locked_style_overrides_edited(qapp): + slider = CompactSlider("Density", 0.0, 2.0, 1.0) + slider.setValue(1.5) # edited (differs from default) + + slider.setEnabled(False) + assert THEME.text_muted in slider.label.styleSheet() + assert THEME.accent_edited not in slider.label.styleSheet() + + slider.setEnabled(True) + assert THEME.accent_edited in slider.label.styleSheet() + + +def test_kelvin_slider_locked_handle_is_muted(qapp): + s = KelvinSlider("Temperature") + live_qss = s.slider.styleSheet() + + s.setEnabled(False) + assert THEME.text_muted in s.slider.styleSheet() + assert s.slider.styleSheet() != live_qss + + s.setEnabled(True) + assert s.slider.styleSheet() == live_qss + s.timer.stop() + + def test_kelvin_slider_default_roundtrips_exactly(qapp): s = KelvinSlider("Temperature") s.setValue(4300)