Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 0b08b62

Browse files
authored
[Linux] fix and test light vs. dark theme detection (#32618)
1 parent 2694c6a commit 0b08b62

15 files changed

+744
-108
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_event_channel.cc
20882088
FILE: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc
20892089
FILE: ../../../flutter/shell/platform/linux/fl_gl_area.cc
20902090
FILE: ../../../flutter/shell/platform/linux/fl_gl_area.h
2091+
FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.cc
2092+
FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.h
2093+
FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings_test.cc
20912094
FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc
20922095
FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec_test.cc
20932096
FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec.cc
@@ -2137,8 +2140,11 @@ FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.cc
21372140
FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.h
21382141
FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc
21392142
FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.h
2143+
FILE: ../../../flutter/shell/platform/linux/fl_settings.cc
2144+
FILE: ../../../flutter/shell/platform/linux/fl_settings.h
21402145
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.cc
21412146
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.h
2147+
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin_test.cc
21422148
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc
21432149
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h
21442150
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc

shell/platform/linux/BUILD.gn

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ source_set("flutter_linux_sources") {
107107
"fl_engine.cc",
108108
"fl_event_channel.cc",
109109
"fl_gl_area.cc",
110+
"fl_gnome_settings.cc",
110111
"fl_json_message_codec.cc",
111112
"fl_json_method_codec.cc",
112113
"fl_key_channel_responder.cc",
@@ -128,6 +129,7 @@ source_set("flutter_linux_sources") {
128129
"fl_renderer.cc",
129130
"fl_renderer_gl.cc",
130131
"fl_renderer_headless.cc",
132+
"fl_settings.cc",
131133
"fl_settings_plugin.cc",
132134
"fl_standard_message_codec.cc",
133135
"fl_standard_method_codec.cc",
@@ -175,6 +177,13 @@ test_fixtures("flutter_linux_fixtures") {
175177
fixtures = []
176178
}
177179

180+
copy("flutter_linux_gschemas") {
181+
testonly = true
182+
183+
sources = [ "testing/gschemas/ubuntu-20.04.compiled" ]
184+
outputs = [ "$target_gen_dir/assets/{{source_name_part}}/gschemas.compiled" ]
185+
}
186+
178187
executable("flutter_linux_unittests") {
179188
testonly = true
180189

@@ -186,6 +195,7 @@ executable("flutter_linux_unittests") {
186195
"fl_dart_project_test.cc",
187196
"fl_engine_test.cc",
188197
"fl_event_channel_test.cc",
198+
"fl_gnome_settings_test.cc",
189199
"fl_json_message_codec_test.cc",
190200
"fl_json_method_codec_test.cc",
191201
"fl_key_channel_responder_test.cc",
@@ -197,6 +207,7 @@ executable("flutter_linux_unittests") {
197207
"fl_method_response_test.cc",
198208
"fl_pixel_buffer_texture_test.cc",
199209
"fl_plugin_registrar_test.cc",
210+
"fl_settings_plugin_test.cc",
200211
"fl_standard_message_codec_test.cc",
201212
"fl_standard_method_codec_test.cc",
202213
"fl_string_codec_test.cc",
@@ -210,6 +221,7 @@ executable("flutter_linux_unittests") {
210221
"testing/mock_epoxy.cc",
211222
"testing/mock_plugin_registrar.cc",
212223
"testing/mock_renderer.cc",
224+
"testing/mock_settings.cc",
213225
"testing/mock_signal_handler.cc",
214226
"testing/mock_text_input_plugin.cc",
215227
"testing/mock_texture_registrar.cc",
@@ -229,6 +241,7 @@ executable("flutter_linux_unittests") {
229241

230242
deps = [
231243
":flutter_linux_fixtures",
244+
":flutter_linux_gschemas",
232245
":flutter_linux_sources",
233246
"//flutter/runtime:libdart",
234247
"//flutter/shell/platform/embedder:embedder_headers",

shell/platform/linux/fl_engine.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,9 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
516516

517517
setup_locales(self);
518518

519+
g_autoptr(FlSettings) settings = fl_settings_new();
519520
self->settings_plugin = fl_settings_plugin_new(self->binary_messenger);
520-
fl_settings_plugin_start(self->settings_plugin);
521+
fl_settings_plugin_start(self->settings_plugin, settings);
521522

522523
result = self->embedder_api.UpdateSemanticsEnabled(self->engine, TRUE);
523524
if (result != kSuccess) {
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/platform/linux/fl_gnome_settings.h"
6+
7+
#include <gio/gio.h>
8+
#include <glib.h>
9+
10+
static constexpr char kDesktopInterfaceSchema[] = "org.gnome.desktop.interface";
11+
static constexpr char kDesktopTextScalingFactorKey[] = "text-scaling-factor";
12+
static constexpr char kDesktopClockFormatKey[] = "clock-format";
13+
static constexpr char kDesktopGtkThemeKey[] = "gtk-theme";
14+
15+
static constexpr char kClockFormat12Hour[] = "12h";
16+
static constexpr char kGtkThemeDarkSuffix[] = "-dark";
17+
static constexpr char kInterfaceSettings[] = "interface-settings";
18+
19+
struct _FlGnomeSettings {
20+
GObject parent_instance;
21+
22+
GSettings* interface_settings;
23+
};
24+
25+
enum { PROP_0, PROP_INTERFACE_SETTINGS, PROP_LAST };
26+
27+
static void fl_gnome_settings_iface_init(FlSettingsInterface* iface);
28+
29+
G_DEFINE_TYPE_WITH_CODE(FlGnomeSettings,
30+
fl_gnome_settings,
31+
G_TYPE_OBJECT,
32+
G_IMPLEMENT_INTERFACE(fl_settings_get_type(),
33+
fl_gnome_settings_iface_init))
34+
35+
static FlClockFormat fl_gnome_settings_get_clock_format(FlSettings* settings) {
36+
FlGnomeSettings* self = FL_GNOME_SETTINGS(settings);
37+
38+
FlClockFormat clock_format = FL_CLOCK_FORMAT_24H;
39+
40+
if (self->interface_settings != nullptr) {
41+
g_autofree gchar* value =
42+
g_settings_get_string(self->interface_settings, kDesktopClockFormatKey);
43+
if (g_strcmp0(value, kClockFormat12Hour) == 0) {
44+
clock_format = FL_CLOCK_FORMAT_12H;
45+
}
46+
}
47+
return clock_format;
48+
}
49+
50+
static FlColorScheme fl_gnome_settings_get_color_scheme(FlSettings* settings) {
51+
FlGnomeSettings* self = FL_GNOME_SETTINGS(settings);
52+
53+
FlColorScheme color_scheme = FL_COLOR_SCHEME_LIGHT;
54+
55+
if (self->interface_settings != nullptr) {
56+
// check whether org.gnome.desktop.interface.gtk-theme ends with "-dark"
57+
g_autofree gchar* value =
58+
g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey);
59+
if (g_str_has_suffix(value, kGtkThemeDarkSuffix)) {
60+
color_scheme = FL_COLOR_SCHEME_DARK;
61+
}
62+
}
63+
return color_scheme;
64+
}
65+
66+
static gdouble fl_gnome_settings_get_text_scaling_factor(FlSettings* settings) {
67+
FlGnomeSettings* self = FL_GNOME_SETTINGS(settings);
68+
69+
gdouble scaling_factor = 1.0;
70+
71+
if (self->interface_settings != nullptr) {
72+
scaling_factor = g_settings_get_double(self->interface_settings,
73+
kDesktopTextScalingFactorKey);
74+
}
75+
return scaling_factor;
76+
}
77+
78+
static void fl_gnome_settings_set_interface_settings(FlGnomeSettings* self,
79+
GSettings* settings) {
80+
g_return_if_fail(G_IS_SETTINGS(settings));
81+
82+
g_signal_connect_object(settings, "changed::clock-format",
83+
G_CALLBACK(fl_settings_emit_changed), self,
84+
G_CONNECT_SWAPPED);
85+
g_signal_connect_object(settings, "changed::gtk-theme",
86+
G_CALLBACK(fl_settings_emit_changed), self,
87+
G_CONNECT_SWAPPED);
88+
g_signal_connect_object(settings, "changed::text-scaling-factor",
89+
G_CALLBACK(fl_settings_emit_changed), self,
90+
G_CONNECT_SWAPPED);
91+
92+
self->interface_settings = G_SETTINGS(g_object_ref(settings));
93+
}
94+
95+
static void fl_gnome_settings_set_property(GObject* object,
96+
guint prop_id,
97+
const GValue* value,
98+
GParamSpec* pspec) {
99+
FlGnomeSettings* self = FL_GNOME_SETTINGS(object);
100+
switch (prop_id) {
101+
case PROP_INTERFACE_SETTINGS:
102+
fl_gnome_settings_set_interface_settings(
103+
self, G_SETTINGS(g_value_get_object(value)));
104+
break;
105+
default:
106+
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
107+
break;
108+
}
109+
}
110+
111+
static void fl_gnome_settings_dispose(GObject* object) {
112+
FlGnomeSettings* self = FL_GNOME_SETTINGS(object);
113+
114+
g_clear_object(&self->interface_settings);
115+
116+
G_OBJECT_CLASS(fl_gnome_settings_parent_class)->dispose(object);
117+
}
118+
119+
static void fl_gnome_settings_class_init(FlGnomeSettingsClass* klass) {
120+
GObjectClass* object_class = G_OBJECT_CLASS(klass);
121+
object_class->dispose = fl_gnome_settings_dispose;
122+
object_class->set_property = fl_gnome_settings_set_property;
123+
124+
g_object_class_install_property(
125+
object_class, PROP_INTERFACE_SETTINGS,
126+
g_param_spec_object(
127+
kInterfaceSettings, kInterfaceSettings, kDesktopInterfaceSchema,
128+
g_settings_get_type(),
129+
static_cast<GParamFlags>(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
130+
G_PARAM_STATIC_STRINGS)));
131+
}
132+
133+
static void fl_gnome_settings_iface_init(FlSettingsInterface* iface) {
134+
iface->get_clock_format = fl_gnome_settings_get_clock_format;
135+
iface->get_color_scheme = fl_gnome_settings_get_color_scheme;
136+
iface->get_text_scaling_factor = fl_gnome_settings_get_text_scaling_factor;
137+
}
138+
139+
static void fl_gnome_settings_init(FlGnomeSettings* self) {}
140+
141+
static GSettings* create_settings(const gchar* schema_id) {
142+
GSettings* settings = nullptr;
143+
GSettingsSchemaSource* source = g_settings_schema_source_get_default();
144+
if (source != nullptr) {
145+
g_autoptr(GSettingsSchema) schema =
146+
g_settings_schema_source_lookup(source, schema_id, TRUE);
147+
if (schema != nullptr) {
148+
settings = g_settings_new_full(schema, nullptr, nullptr);
149+
}
150+
}
151+
return settings;
152+
}
153+
154+
FlSettings* fl_gnome_settings_new() {
155+
g_autoptr(GSettings) interface_settings =
156+
create_settings(kDesktopInterfaceSchema);
157+
return FL_SETTINGS(g_object_new(fl_gnome_settings_get_type(),
158+
kInterfaceSettings, interface_settings,
159+
nullptr));
160+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_
6+
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_
7+
8+
#include "flutter/shell/platform/linux/fl_settings.h"
9+
10+
G_BEGIN_DECLS
11+
12+
G_DECLARE_FINAL_TYPE(FlGnomeSettings,
13+
fl_gnome_settings,
14+
FL,
15+
GNOME_SETTINGS,
16+
GObject);
17+
18+
/**
19+
* fl_gnome_settings_new:
20+
*
21+
* Creates a new settings instance for GNOME.
22+
*
23+
* Returns: a new #FlSettings.
24+
*/
25+
FlSettings* fl_gnome_settings_new();
26+
27+
G_END_DECLS
28+
29+
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/platform/linux/fl_gnome_settings.h"
6+
#include "flutter/shell/platform/linux/testing/fl_test.h"
7+
#include "flutter/shell/platform/linux/testing/mock_settings.h"
8+
#include "flutter/shell/platform/linux/testing/mock_signal_handler.h"
9+
#include "flutter/testing/testing.h"
10+
11+
#include <gio/gio.h>
12+
#define G_SETTINGS_ENABLE_BACKEND
13+
#include <gio/gsettingsbackend.h>
14+
15+
#include "gmock/gmock.h"
16+
#include "gtest/gtest.h"
17+
18+
class FlGnomeSettingsTest : public ::testing::Test {
19+
protected:
20+
void SetUp() override {
21+
// force _g_io_modules_ensure_extension_points_registered() to get called
22+
g_settings_backend_get_default();
23+
}
24+
};
25+
26+
static GSettings* create_settings(const gchar* name, const gchar* schema_id) {
27+
g_autofree gchar* path =
28+
g_build_filename(flutter::testing::GetFixturesPath(), name, nullptr);
29+
g_autoptr(GSettingsSchemaSource) source =
30+
g_settings_schema_source_new_from_directory(path, nullptr, false,
31+
nullptr);
32+
g_autoptr(GSettingsSchema) schema =
33+
g_settings_schema_source_lookup(source, schema_id, false);
34+
g_autoptr(GSettingsBackend) backend = g_memory_settings_backend_new();
35+
return g_settings_new_full(schema, backend, nullptr);
36+
}
37+
38+
TEST_F(FlGnomeSettingsTest, ClockFormat) {
39+
g_autoptr(GSettings) interface_settings =
40+
create_settings("ubuntu-20.04", "org.gnome.desktop.interface");
41+
g_settings_set_string(interface_settings, "clock-format", "24h");
42+
43+
g_autoptr(FlSettings) settings = FL_SETTINGS(
44+
g_object_new(fl_gnome_settings_get_type(), "interface_settings",
45+
interface_settings, nullptr));
46+
EXPECT_EQ(fl_settings_get_clock_format(settings), FL_CLOCK_FORMAT_24H);
47+
48+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
49+
EXPECT_SIGNAL(settings_changed).Times(1);
50+
51+
g_settings_set_string(interface_settings, "clock-format", "12h");
52+
EXPECT_EQ(fl_settings_get_clock_format(settings), FL_CLOCK_FORMAT_12H);
53+
}
54+
55+
TEST_F(FlGnomeSettingsTest, GtkTheme) {
56+
g_autoptr(GSettings) interface_settings =
57+
create_settings("ubuntu-20.04", "org.gnome.desktop.interface");
58+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru");
59+
60+
g_autoptr(FlSettings) settings = FL_SETTINGS(
61+
g_object_new(fl_gnome_settings_get_type(), "interface_settings",
62+
interface_settings, nullptr));
63+
EXPECT_EQ(fl_settings_get_color_scheme(settings), FL_COLOR_SCHEME_LIGHT);
64+
65+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
66+
EXPECT_SIGNAL(settings_changed).Times(1);
67+
68+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru-dark");
69+
EXPECT_EQ(fl_settings_get_color_scheme(settings), FL_COLOR_SCHEME_DARK);
70+
}
71+
72+
TEST_F(FlGnomeSettingsTest, TextScalingFactor) {
73+
g_autoptr(GSettings) interface_settings =
74+
create_settings("ubuntu-20.04", "org.gnome.desktop.interface");
75+
g_settings_set_double(interface_settings, "text-scaling-factor", 1.0);
76+
77+
g_autoptr(FlSettings) settings = FL_SETTINGS(
78+
g_object_new(fl_gnome_settings_get_type(), "interface_settings",
79+
interface_settings, nullptr));
80+
EXPECT_EQ(fl_settings_get_text_scaling_factor(settings), 1.0);
81+
82+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
83+
EXPECT_SIGNAL(settings_changed).Times(1);
84+
85+
g_settings_set_double(interface_settings, "text-scaling-factor", 1.5);
86+
EXPECT_EQ(fl_settings_get_text_scaling_factor(settings), 1.5);
87+
}
88+
89+
TEST_F(FlGnomeSettingsTest, SignalHandlers) {
90+
g_autoptr(GSettings) interface_settings =
91+
create_settings("ubuntu-20.04", "org.gnome.desktop.interface");
92+
93+
g_autoptr(FlSettings) settings = FL_SETTINGS(
94+
g_object_new(fl_gnome_settings_get_type(), "interface_settings",
95+
interface_settings, nullptr));
96+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
97+
98+
EXPECT_SIGNAL(settings_changed).Times(3);
99+
100+
g_settings_set_string(interface_settings, "clock-format", "12h");
101+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru-dark");
102+
g_settings_set_double(interface_settings, "text-scaling-factor", 1.5);
103+
104+
EXPECT_SIGNAL(settings_changed).Times(0);
105+
106+
g_clear_object(&settings);
107+
108+
// destroyed FlSettings object must have disconnected its signal handlers
109+
g_settings_set_string(interface_settings, "clock-format", "24h");
110+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru");
111+
g_settings_set_double(interface_settings, "text-scaling-factor", 2.0);
112+
}

0 commit comments

Comments
 (0)