Skip to content

Commit a1c54bd

Browse files
Merge pull request #395 from DataLab-Platform/fix/subdued-colors-theme-switch
fix(welcome): follow color mode changes in subdued texts
2 parents abe255b + 43d3d10 commit a1c54bd

3 files changed

Lines changed: 61 additions & 7 deletions

File tree

‎datalab/gui/main.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
from datalab.webapi.actions import WebApiActions
9292
from datalab.widgets import instconfviewer
9393
from datalab.widgets import status as dl_status
94+
from datalab.widgets.expandabletext import refresh_subdued_colors
9495

9596
if TYPE_CHECKING:
9697
from typing import Literal
@@ -2390,9 +2391,10 @@ def _about(self) -> None: # pragma: no cover
23902391
)
23912392

23922393
def _update_extra_color_mode(self) -> None:
2393-
"""Update the macro panel color mode"""
2394+
"""Update the macro panel color mode and the subdued text colors"""
23942395
if self.macropanel is not None:
23952396
self.macropanel.update_color_mode()
2397+
refresh_subdued_colors()
23962398

23972399
# Settings changes are intentionally dispatched in one place because each
23982400
# option may trigger a specific live UI update or panel refresh.

‎datalab/tests/features/common/welcome_unit_test.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from types import SimpleNamespace
1010

1111
import numpy as np
12+
from guidata import qthelpers as guidata_qth
13+
from plotpy import config as plotpy_config
1214
from qtpy import QtCore as QC
1315
from qtpy import QtGui as QG
1416
from qtpy import QtWidgets as QW
@@ -120,6 +122,38 @@ def create_gaussian(menu: QW.QMenu, *_args) -> None:
120122
Conf.welcome_on_startup.set(initial)
121123

122124

125+
def test_welcome_page_follows_color_mode(monkeypatch) -> None:
126+
"""Subdued texts remain legible when the color mode is switched at runtime"""
127+
# Switching the color mode changes these globals: restore them at teardown
128+
monkeypatch.setenv(guidata_qth.ENV_COLOR_MODE, guidata_qth.get_color_mode())
129+
monkeypatch.setattr(guidata_qth, "CURRENT_THEME", guidata_qth.CURRENT_THEME)
130+
initial_mode = Conf.color_mode.get()
131+
description = _("Generate a 1D signal or 2D image from a template.")
132+
with datalab_test_app_context(console=False) as win:
133+
page = win.welcomepanel
134+
labels = [page.start_title] + [
135+
label
136+
for label in page.entries["create"].findChildren(QW.QLabel)
137+
if label.text() == description
138+
]
139+
assert len(labels) == 2
140+
try:
141+
# Light to dark, then dark to light
142+
for mode in ("light", "dark", "light"):
143+
Conf.color_mode.set(mode)
144+
win._update_color_mode() # pylint: disable=protected-access
145+
palette = QW.QApplication.instance().palette()
146+
window = palette.color(QG.QPalette.Window).lightness()
147+
for label in labels:
148+
text = label.palette().color(QG.QPalette.WindowText)
149+
alpha = text.alphaF()
150+
lightness = alpha * text.lightness() + (1 - alpha) * window
151+
assert abs(lightness - window) > 64, (mode, label.text())
152+
finally:
153+
Conf.color_mode.set(initial_mode)
154+
plotpy_config.set_plotpy_color_mode(guidata_qth.LIGHT)
155+
156+
123157
def _application_plugin(
124158
plugin_id: str,
125159
tiles: tuple[WelcomeTile, ...] | Exception,

‎datalab/widgets/expandabletext.py‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
#: Default left indent in pixels
3434
DEFAULT_INDENT: int = 20
3535

36+
#: Dynamic property marking the widgets with a subdued text color
37+
SUBDUED_PROPERTY: str = "datalab_subdued"
38+
3639

3740
# --- Palette helpers (public — reused by other modules) --------------------------
3841

@@ -52,18 +55,33 @@ def apply_palette_color(widget: QW.QWidget, color: QG.QColor) -> None:
5255
def apply_subdued_color(widget: QW.QWidget) -> None:
5356
"""Apply a subdued/secondary text color on *widget*.
5457
55-
Uses the ``QPalette.PlaceholderText`` role (Qt 5.12+) which provides a
56-
theme-native "dimmed" color. Sets both ``WindowText`` (for ``QLabel``)
57-
and ``Text`` (for ``QTextBrowser`` / ``QTextEdit``) roles.
58+
The application ``WindowText`` (for ``QLabel``) and ``Text`` (for
59+
``QTextBrowser`` / ``QTextEdit``) colors are dimmed to half opacity, like Qt
60+
placeholder texts. The widget is marked so that :func:`refresh_subdued_colors`
61+
can follow color mode changes.
5862
5963
Args:
6064
widget: Target widget
6165
"""
62-
subdued = QW.QApplication.instance().palette().color(QG.QPalette.PlaceholderText)
66+
app_palette = QW.QApplication.instance().palette()
6367
palette = widget.palette()
64-
palette.setColor(QG.QPalette.WindowText, subdued)
65-
palette.setColor(QG.QPalette.Text, subdued)
68+
for role in (QG.QPalette.WindowText, QG.QPalette.Text):
69+
color = QG.QColor(app_palette.color(role))
70+
color.setAlpha(128)
71+
palette.setColor(role, color)
6672
widget.setPalette(palette)
73+
widget.setProperty(SUBDUED_PROPERTY, True)
74+
75+
76+
def refresh_subdued_colors() -> None:
77+
"""Update the subdued text colors after an application palette change.
78+
79+
Subdued colors are set explicitly on widgets, so they do not follow the
80+
application palette by themselves.
81+
"""
82+
for widget in QW.QApplication.allWidgets():
83+
if widget.property(SUBDUED_PROPERTY):
84+
apply_subdued_color(widget)
6785

6886

6987
# --- Internal helpers ------------------------------------------------------------

0 commit comments

Comments
 (0)