wasm: Add support for contrastPreference detection to WASM theme

Task-number: QTBUG-133596
Change-Id: I4482fe5a3457579867bd870cb2c8d371dd08e1a0
Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io>
This commit is contained in:
Piotr Wiercinski 2025-03-05 21:09:59 +01:00
parent 9302945ce3
commit 8f65024d84
2 changed files with 101 additions and 41 deletions

View File

@ -5,32 +5,55 @@
#include <QtCore/qvariant.h> #include <QtCore/qvariant.h>
#include <QFontDatabase> #include <QFontDatabase>
#include <QList> #include <QList>
#include <qloggingcategory.h>
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <qpalette.h> #include <qpalette.h>
#include <private/qstdweb_p.h>
#include <emscripten.h> #include <emscripten.h>
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h> #include <emscripten/val.h>
Q_GUI_EXPORT QPalette qt_fusionPalette(); Q_GUI_EXPORT QPalette qt_fusionPalette();
Qt::ColorScheme QWasmTheme::s_autoColorScheme = Qt::ColorScheme::Unknown;
bool QWasmTheme::s_autoPaletteIsDirty = false;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeWasm, "qt.qpa.theme.wasm")
namespace {
bool matchMedia(std::string mediaQueryString)
{
return emscripten::val::global("window")
.call<emscripten::val>("matchMedia", mediaQueryString)["matches"]
.as<bool>();
}
Qt::ColorScheme getColorSchemeFromMedia()
{
if (matchMedia(colorSchemePreferenceDark)) {
return Qt::ColorScheme::Dark;
} else {
return Qt::ColorScheme::Light;
}
}
Qt::ContrastPreference getContrastPreferenceFromMedia()
{
if (matchMedia(contrastPreferenceMore)) {
return Qt::ContrastPreference::HighContrast;
} else {
return Qt::ContrastPreference::NoPreference;
}
}
} // namespace
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
QWasmTheme::QWasmTheme() QWasmTheme::QWasmTheme()
{ {
if (emscripten::val::global("window").call<emscripten::val>( m_colorScheme = getColorSchemeFromMedia();
"matchMedia", qCDebug(lcQpaThemeWasm) << "Initializing Wasm theme. Color scheme: " << m_colorScheme;
std::string("(prefers-color-scheme:dark)"))["matches"].as<bool>()) m_contrastPreference = getContrastPreferenceFromMedia();
s_autoColorScheme = Qt::ColorScheme::Dark; qCDebug(lcQpaThemeWasm) << "Initializing Wasm theme. Contrast preference: " << m_contrastPreference;
else
s_autoColorScheme = Qt::ColorScheme::Light;
for (auto family : QFontDatabase::families()) for (auto family : QFontDatabase::families())
if (QFontDatabase::isFixedPitch(family)) if (QFontDatabase::isFixedPitch(family))
@ -39,15 +62,15 @@ QWasmTheme::QWasmTheme()
m_palette = std::make_unique<QPalette>(); m_palette = std::make_unique<QPalette>();
m_paletteIsDirty = true; // Force update later m_paletteIsDirty = true; // Force update later
const auto callback = [=](emscripten::val event) { QWasmTheme::onColorSchemeChange(event); }; registerCallbacks(
const emscripten::val window = emscripten::val::global("window"); { colorSchemePreferenceDark },
if (!window.isUndefined()) { [this](emscripten::val) { QWasmTheme::onColorSchemeChange(); },
const emscripten::val matchMedia = window.call<emscripten::val>("matchMedia", emscripten::val("(prefers-color-scheme: dark)")); m_colorSchemeChangeCallback);
if (!matchMedia.isUndefined()) { registerCallbacks(
static auto changeEvent = { contrastPreferenceNoPreference, contrastPreferenceMore, contrastPreferenceLess,
std::make_unique<qstdweb::EventCallback>(matchMedia, "change", callback); contrastPreferenceCustom },
} [this](emscripten::val) { QWasmTheme::onContrastPreferenceChange(); },
} m_contrastPreferenceChangeCallbacks);
} }
QWasmTheme::~QWasmTheme() QWasmTheme::~QWasmTheme()
@ -59,9 +82,8 @@ QWasmTheme::~QWasmTheme()
const QPalette *QWasmTheme::palette(Palette type) const const QPalette *QWasmTheme::palette(Palette type) const
{ {
if (type == SystemPalette) { if (type == SystemPalette) {
if (m_paletteIsDirty || s_autoPaletteIsDirty) { if (m_paletteIsDirty) {
m_paletteIsDirty = false; m_paletteIsDirty = false;
s_autoPaletteIsDirty = false;
*m_palette = qt_fusionPalette(); *m_palette = qt_fusionPalette();
} }
return m_palette.get(); return m_palette.get();
@ -71,9 +93,7 @@ const QPalette *QWasmTheme::palette(Palette type) const
Qt::ColorScheme QWasmTheme::colorScheme() const Qt::ColorScheme QWasmTheme::colorScheme() const
{ {
if (m_colorScheme != Qt::ColorScheme::Unknown) return m_colorScheme;
return m_colorScheme;
return s_autoColorScheme;
} }
void QWasmTheme::requestColorScheme(Qt::ColorScheme scheme) void QWasmTheme::requestColorScheme(Qt::ColorScheme scheme)
@ -85,6 +105,11 @@ void QWasmTheme::requestColorScheme(Qt::ColorScheme scheme)
} }
} }
Qt::ContrastPreference QWasmTheme::contrastPreference() const
{
return m_contrastPreference;
}
QVariant QWasmTheme::themeHint(ThemeHint hint) const QVariant QWasmTheme::themeHint(ThemeHint hint) const
{ {
if (hint == QPlatformTheme::StyleNames) if (hint == QPlatformTheme::StyleNames)
@ -102,20 +127,25 @@ const QFont *QWasmTheme::font(Font type) const
return nullptr; return nullptr;
} }
void QWasmTheme::onColorSchemeChange(emscripten::val event) void QWasmTheme::onColorSchemeChange()
{ {
const emscripten::val matches = event["matches"]; auto colorScheme = getColorSchemeFromMedia();
if (!matches.isUndefined()) { if (m_colorScheme != colorScheme) {
const auto oldAutoColorScheme = s_autoColorScheme; qCDebug(lcQpaThemeWasm) << "Color scheme changed to " << colorScheme;
if (matches.as<int>()) m_colorScheme = colorScheme;
s_autoColorScheme = Qt::ColorScheme::Dark; m_paletteIsDirty = true;
else QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
s_autoColorScheme = Qt::ColorScheme::Light; }
}
if (oldAutoColorScheme != s_autoColorScheme) { void QWasmTheme::onContrastPreferenceChange()
s_autoPaletteIsDirty = true; {
QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(); auto contrastPreference = getContrastPreferenceFromMedia();
} if (m_contrastPreference != contrastPreference) {
qCDebug(lcQpaThemeWasm) << "Contrast preference changed to " << contrastPreference;
m_contrastPreference = contrastPreference;
m_paletteIsDirty = true;
QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
} }
} }

View File

@ -7,6 +7,10 @@
#include <qpa/qplatformtheme.h> #include <qpa/qplatformtheme.h>
#include <QtGui/QFont> #include <QtGui/QFont>
#include <memory>
#include <private/qstdweb_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QWasmEventTranslator; class QWasmEventTranslator;
@ -17,6 +21,30 @@ class QWasmScreen;
class QWasmCompositor; class QWasmCompositor;
class QWasmBackingStore; class QWasmBackingStore;
// this reflects @media/prefers-contrast
constexpr auto colorSchemePreferenceDark = "(prefers-color-scheme: dark)";
constexpr auto contrastPreferenceNoPreference = "(prefers-contrast: no-preference)";
constexpr auto contrastPreferenceMore = "(prefers-contrast: more)";
constexpr auto contrastPreferenceLess = "(prefers-contrast: less)";
constexpr auto contrastPreferenceCustom = "(prefers-contrast: custom)";
template <typename MediaName, typename CallbackFn, typename Container>
void registerCallbacks(std::initializer_list<MediaName> mediaNames, CallbackFn callback, Container &callbacksContainer)
{
emscripten::val window = emscripten::val::global("window");
if (!window.isUndefined()) {
for (auto &&mediaName : mediaNames) {
auto media = window.call<emscripten::val>("matchMedia", emscripten::val(mediaName));
if constexpr (std::is_same_v<Container, std::vector<QWasmEventHandler>>) {
callbacksContainer.emplace_back(media, "change", callback);
} else {
Q_ASSERT(mediaNames.size() == 1);
callbacksContainer = QWasmEventHandler(media, "change", callback);
}
}
}
}
class QWasmTheme : public QPlatformTheme class QWasmTheme : public QPlatformTheme
{ {
public: public:
@ -26,19 +54,21 @@ public:
const QPalette *palette(Palette type = SystemPalette) const override; const QPalette *palette(Palette type = SystemPalette) const override;
Qt::ColorScheme colorScheme() const override; Qt::ColorScheme colorScheme() const override;
void requestColorScheme(Qt::ColorScheme scheme) override; void requestColorScheme(Qt::ColorScheme scheme) override;
Qt::ContrastPreference contrastPreference() const override;
QVariant themeHint(ThemeHint hint) const override; QVariant themeHint(ThemeHint hint) const override;
const QFont *font(Font type) const override; const QFont *font(Font type) const override;
QFont *fixedFont = nullptr; QFont *fixedFont = nullptr;
static void onColorSchemeChange(emscripten::val event); void onColorSchemeChange();
void onContrastPreferenceChange();
private: private:
Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
QWasmEventHandler m_colorSchemeChangeCallback;
std::unique_ptr<QPalette> m_palette; std::unique_ptr<QPalette> m_palette;
mutable bool m_paletteIsDirty = false; mutable bool m_paletteIsDirty = false;
Qt::ContrastPreference m_contrastPreference;
static Qt::ColorScheme s_autoColorScheme; std::vector<QWasmEventHandler> m_contrastPreferenceChangeCallbacks;
static bool s_autoPaletteIsDirty;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE