Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion datalab/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
from datalab.webapi.actions import WebApiActions
from datalab.widgets import instconfviewer
from datalab.widgets import status as dl_status
from datalab.widgets.expandabletext import refresh_subdued_colors

if TYPE_CHECKING:
from typing import Literal
Expand Down Expand Up @@ -2390,9 +2391,10 @@ def _about(self) -> None: # pragma: no cover
)

def _update_extra_color_mode(self) -> None:
"""Update the macro panel color mode"""
"""Update the macro panel color mode and the subdued text colors"""
if self.macropanel is not None:
self.macropanel.update_color_mode()
refresh_subdued_colors()

# Settings changes are intentionally dispatched in one place because each
# option may trigger a specific live UI update or panel refresh.
Expand Down
34 changes: 34 additions & 0 deletions datalab/tests/features/common/welcome_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from types import SimpleNamespace

import numpy as np
from guidata import qthelpers as guidata_qth
from plotpy import config as plotpy_config
from qtpy import QtCore as QC
from qtpy import QtGui as QG
from qtpy import QtWidgets as QW
Expand Down Expand Up @@ -120,6 +122,38 @@ def create_gaussian(menu: QW.QMenu, *_args) -> None:
Conf.welcome_on_startup.set(initial)


def test_welcome_page_follows_color_mode(monkeypatch) -> None:
"""Subdued texts remain legible when the color mode is switched at runtime"""
# Switching the color mode changes these globals: restore them at teardown
monkeypatch.setenv(guidata_qth.ENV_COLOR_MODE, guidata_qth.get_color_mode())
monkeypatch.setattr(guidata_qth, "CURRENT_THEME", guidata_qth.CURRENT_THEME)
initial_mode = Conf.color_mode.get()
description = _("Generate a 1D signal or 2D image from a template.")
with datalab_test_app_context(console=False) as win:
page = win.welcomepanel
labels = [page.start_title] + [
label
for label in page.entries["create"].findChildren(QW.QLabel)
if label.text() == description
]
assert len(labels) == 2
try:
# Light to dark, then dark to light
for mode in ("light", "dark", "light"):
Conf.color_mode.set(mode)
win._update_color_mode() # pylint: disable=protected-access
palette = QW.QApplication.instance().palette()
window = palette.color(QG.QPalette.Window).lightness()
for label in labels:
text = label.palette().color(QG.QPalette.WindowText)
alpha = text.alphaF()
lightness = alpha * text.lightness() + (1 - alpha) * window
assert abs(lightness - window) > 64, (mode, label.text())
finally:
Conf.color_mode.set(initial_mode)
plotpy_config.set_plotpy_color_mode(guidata_qth.LIGHT)


def _application_plugin(
plugin_id: str,
tiles: tuple[WelcomeTile, ...] | Exception,
Expand Down
30 changes: 24 additions & 6 deletions datalab/widgets/expandabletext.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
#: Default left indent in pixels
DEFAULT_INDENT: int = 20

#: Dynamic property marking the widgets with a subdued text color
SUBDUED_PROPERTY: str = "datalab_subdued"


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

Expand All @@ -52,18 +55,33 @@ def apply_palette_color(widget: QW.QWidget, color: QG.QColor) -> None:
def apply_subdued_color(widget: QW.QWidget) -> None:
"""Apply a subdued/secondary text color on *widget*.

Uses the ``QPalette.PlaceholderText`` role (Qt 5.12+) which provides a
theme-native "dimmed" color. Sets both ``WindowText`` (for ``QLabel``)
and ``Text`` (for ``QTextBrowser`` / ``QTextEdit``) roles.
The application ``WindowText`` (for ``QLabel``) and ``Text`` (for
``QTextBrowser`` / ``QTextEdit``) colors are dimmed to half opacity, like Qt
placeholder texts. The widget is marked so that :func:`refresh_subdued_colors`
can follow color mode changes.

Args:
widget: Target widget
"""
subdued = QW.QApplication.instance().palette().color(QG.QPalette.PlaceholderText)
app_palette = QW.QApplication.instance().palette()
palette = widget.palette()
palette.setColor(QG.QPalette.WindowText, subdued)
palette.setColor(QG.QPalette.Text, subdued)
for role in (QG.QPalette.WindowText, QG.QPalette.Text):
color = QG.QColor(app_palette.color(role))
color.setAlpha(128)
palette.setColor(role, color)
widget.setPalette(palette)
widget.setProperty(SUBDUED_PROPERTY, True)


def refresh_subdued_colors() -> None:
"""Update the subdued text colors after an application palette change.

Subdued colors are set explicitly on widgets, so they do not follow the
application palette by themselves.
"""
for widget in QW.QApplication.allWidgets():
if widget.property(SUBDUED_PROPERTY):
apply_subdued_color(widget)


# --- Internal helpers ------------------------------------------------------------
Expand Down
Loading