Use Windows high contrast black/white theme with MaterialApp themes (flutter/engine#39206)

* Check high contrast themes

* Formatting

* Test message sending

* Assert channel name

* Formatting

* Calculate luminance

* Refactor brightness check

* Formatting

* Formatting
This commit is contained in:
yaakovschectman 2023-02-01 13:21:37 -05:00 committed by GitHub
parent f9f8c2f67a
commit ce15fbec0e
4 changed files with 66 additions and 14 deletions

View File

@ -668,6 +668,7 @@ void FlutterWindowsEngine::UpdateHighContrastEnabled(bool enabled) {
~FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
}
UpdateAccessibilityFeatures(static_cast<FlutterAccessibilityFeature>(flags));
settings_plugin_->UpdateHighContrastMode(enabled);
}
int FlutterWindowsEngine::EnabledAccessibilityFeatures() const {

View File

@ -26,6 +26,32 @@ constexpr wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
constexpr wchar_t kGetTextScaleFactorRegKey[] =
L"Software\\Microsoft\\Accessibility";
constexpr wchar_t kGetTextScaleFactorRegValue[] = L"TextScaleFactor";
// Return an approximation of the apparent luminance of a given color.
int GetLuminance(DWORD color) {
int r = GetRValue(color);
int g = GetGValue(color);
int b = GetBValue(color);
return (r + r + r + b + (g << 2)) >> 3;
}
// Return kLight if light mode for apps is selected, otherwise return kDark.
SettingsPlugin::PlatformBrightness GetThemeBrightness() {
DWORD use_light_theme;
DWORD use_light_theme_size = sizeof(use_light_theme);
LONG result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD,
nullptr, &use_light_theme, &use_light_theme_size);
if (result == 0) {
return use_light_theme ? SettingsPlugin::PlatformBrightness::kLight
: SettingsPlugin::PlatformBrightness::kDark;
} else {
// The current OS does not support dark mode. (Older Windows 10 or before
// Windows 10)
return SettingsPlugin::PlatformBrightness::kLight;
}
}
} // namespace
SettingsPlugin::SettingsPlugin(BinaryMessenger* messenger,
@ -103,19 +129,13 @@ float SettingsPlugin::GetTextScaleFactor() {
}
SettingsPlugin::PlatformBrightness SettingsPlugin::GetPreferredBrightness() {
DWORD use_light_theme;
DWORD use_light_theme_size = sizeof(use_light_theme);
LONG result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD,
nullptr, &use_light_theme, &use_light_theme_size);
if (result == 0) {
return use_light_theme ? SettingsPlugin::PlatformBrightness::kLight
: SettingsPlugin::PlatformBrightness::kDark;
if (is_high_contrast_) {
DWORD window_color = GetSysColor(COLOR_WINDOW);
int luminance = GetLuminance(window_color);
return luminance >= 127 ? SettingsPlugin::PlatformBrightness::kLight
: SettingsPlugin::PlatformBrightness::kDark;
} else {
// The current OS does not support dark mode. (Older Windows 10 or before
// Windows 10)
return SettingsPlugin::PlatformBrightness::kLight;
return GetThemeBrightness();
}
}
@ -146,4 +166,9 @@ void SettingsPlugin::WatchTextScaleFactorChanged() {
text_scale_factor_changed_watcher_->GetHandle(), TRUE);
}
void SettingsPlugin::UpdateHighContrastMode(bool is_high_contrast) {
is_high_contrast_ = is_high_contrast;
SendSettings();
}
} // namespace flutter

View File

@ -23,6 +23,8 @@ namespace flutter {
// These are typically set in the control panel.
class SettingsPlugin {
public:
enum struct PlatformBrightness { kDark, kLight };
explicit SettingsPlugin(BinaryMessenger* messenger, TaskRunner* task_runner);
virtual ~SettingsPlugin();
@ -37,9 +39,10 @@ class SettingsPlugin {
// this automatically.
virtual void StopWatching();
protected:
enum struct PlatformBrightness { kDark, kLight };
// Update the high contrast status of the system.
virtual void UpdateHighContrastMode(bool is_high_contrast);
protected:
// Returns `true` if the user uses 24 hour time.
virtual bool GetAlwaysUse24HourFormat();
@ -55,6 +58,8 @@ class SettingsPlugin {
// Starts watching text scale factor changes.
virtual void WatchTextScaleFactorChanged();
bool is_high_contrast_ = false;
private:
std::unique_ptr<BasicMessageChannel<rapidjson::Document>> channel_;

View File

@ -21,6 +21,8 @@ class MockSettingsPlugin : public SettingsPlugin {
virtual ~MockSettingsPlugin() = default;
bool is_high_contrast() { return is_high_contrast_; }
// |SettingsPlugin|
MOCK_METHOD0(GetAlwaysUse24HourFormat, bool());
MOCK_METHOD0(GetTextScaleFactor, float());
@ -70,5 +72,24 @@ TEST(SettingsPluginTest, StartWatchingStartsWatchingChanges) {
settings_plugin.StartWatching();
}
TEST(SettingsPluginTest, HighContrastModeHonored) {
int times = 0;
TestBinaryMessenger messenger(
[&times](const std::string& channel, const uint8_t* message,
size_t message_size, BinaryReply reply) {
ASSERT_EQ(channel, "flutter/settings");
times++;
});
::testing::NiceMock<MockSettingsPlugin> settings_plugin(&messenger, nullptr);
settings_plugin.UpdateHighContrastMode(true);
EXPECT_TRUE(settings_plugin.is_high_contrast());
settings_plugin.UpdateHighContrastMode(false);
EXPECT_FALSE(settings_plugin.is_high_contrast());
EXPECT_EQ(times, 2);
}
} // namespace testing
} // namespace flutter