From dbcd000ccbc847527f2ded9b521330a42330aea3 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 5 Jan 2023 18:21:40 +0100 Subject: [PATCH 1/2] Add soft light border in dark theme --- assets/handy-window-dark.css | 7 + assets/handy-window.css | 0 .../flutter/generated_plugin_registrant.cc | 4 + example/linux/flutter/generated_plugins.cmake | 1 + linux/CMakeLists.txt | 5 + linux/handy_window_plugin.cc | 39 ++++ linux/settings/LICENCE | 25 +++ linux/settings/README.md | 4 + linux/settings/handy_gnome_settings.cc | 120 ++++++++++ linux/settings/handy_gnome_settings.h | 26 +++ linux/settings/handy_settings.cc | 50 +++++ linux/settings/handy_settings.h | 67 ++++++ linux/settings/handy_settings_portal.cc | 212 ++++++++++++++++++ linux/settings/handy_settings_portal.h | 55 +++++ pubspec.yaml | 3 + 15 files changed, 618 insertions(+) create mode 100644 assets/handy-window-dark.css create mode 100644 assets/handy-window.css create mode 100644 linux/settings/LICENCE create mode 100644 linux/settings/README.md create mode 100644 linux/settings/handy_gnome_settings.cc create mode 100644 linux/settings/handy_gnome_settings.h create mode 100644 linux/settings/handy_settings.cc create mode 100644 linux/settings/handy_settings.h create mode 100644 linux/settings/handy_settings_portal.cc create mode 100644 linux/settings/handy_settings_portal.h diff --git a/assets/handy-window-dark.css b/assets/handy-window-dark.css new file mode 100644 index 0000000..e3e21c8 --- /dev/null +++ b/assets/handy-window-dark.css @@ -0,0 +1,7 @@ +window.csd.unified:not(.solid-csd):not(.fullscreen):not(.maximized) decoration-overlay { + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07); +} + +window.csd.unified:not(.solid-csd):not(.fullscreen):not(.maximized) decoration-overlay~* headerbar { + box-shadow: none; +} diff --git a/assets/handy-window.css b/assets/handy-window.css new file mode 100644 index 0000000..e69de29 diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc index b2aaa65..c0ca17a 100644 --- a/example/linux/flutter/generated_plugin_registrant.cc +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) handy_window_registrar = @@ -16,4 +17,7 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) yaru_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "YaruPlugin"); + yaru_plugin_register_with_registrar(yaru_registrar); } diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake index 067673a..9e4b433 100644 --- a/example/linux/flutter/generated_plugins.cmake +++ b/example/linux/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST handy_window url_launcher_linux + yaru ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index cc3d31d..478d92c 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -8,12 +8,17 @@ set(PLUGIN_NAME "handy_window_plugin") add_library(${PLUGIN_NAME} SHARED "handy_window_plugin.cc" + "settings/handy_gnome_settings.cc" + "settings/handy_settings_portal.cc" + "settings/handy_settings.cc" ) apply_standard_settings(${PLUGIN_NAME}) set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) target_compile_options(${PLUGIN_NAME} PRIVATE -Wall -Wextra -Werror) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/settings") target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) diff --git a/linux/handy_window_plugin.cc b/linux/handy_window_plugin.cc index fbc7c79..ca6da2e 100644 --- a/linux/handy_window_plugin.cc +++ b/linux/handy_window_plugin.cc @@ -8,6 +8,8 @@ #include +#include "handy_settings.h" + #define _HANDY_INSIDE #include "hdy-window-mixin-private.h" #undef _HANDY_INSIDE @@ -163,6 +165,37 @@ static gboolean use_csd(GtkWidget* window) { return true; } +static void load_css(HandySettings* settings) { + GdkScreen* screen = gdk_screen_get_default(); + gpointer css_provider = + g_object_get_data(G_OBJECT(screen), "_handy_window_css_provider_"); + + if (css_provider == nullptr) { + css_provider = gtk_css_provider_new(); + gtk_style_context_add_provider_for_screen( + screen, GTK_STYLE_PROVIDER(css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_set_data_full(G_OBJECT(screen), "_handy_window_css_provider_", + css_provider, g_object_unref); + } + + HandyColorScheme color_scheme = handy_settings_get_color_scheme(settings); + + g_autoptr(GError) error = nullptr; + g_autofree gchar* exe_path = g_file_read_link("/proc/self/exe", &error); + g_autofree gchar* app_path = g_path_get_dirname(exe_path); + const gchar* asset_path = "data/flutter_assets/packages/handy_window/assets"; + const gchar* css_file = color_scheme == HANDY_COLOR_SCHEME_DARK + ? "handy-window-dark.css" + : "handy-window.css"; + g_autofree gchar* css_path = + g_build_filename(app_path, asset_path, css_file, nullptr); + if (!gtk_css_provider_load_from_path(GTK_CSS_PROVIDER(css_provider), css_path, + &error)) { + g_warning("%s: %s", css_path, error->message); + } +} + static void setup_handy_window(FlView* view) { GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(view)); @@ -242,6 +275,12 @@ static void setup_handy_window(FlView* view) { gtk_widget_show(header_bar); } gtk_widget_show(GTK_WIDGET(view)); + + // css + HandySettings* settings = handy_settings_new(); + load_css(settings); + g_signal_connect_object(settings, "changed", G_CALLBACK(load_css), settings, + G_CONNECT_SWAPPED); } void handy_window_plugin_register_with_registrar(FlPluginRegistrar* registrar) { diff --git a/linux/settings/LICENCE b/linux/settings/LICENCE new file mode 100644 index 0000000..c6823b8 --- /dev/null +++ b/linux/settings/LICENCE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/settings/README.md b/linux/settings/README.md new file mode 100644 index 0000000..36e029e --- /dev/null +++ b/linux/settings/README.md @@ -0,0 +1,4 @@ +# HandySettings + +Based on FlSettings, FlSettingsPortal, and FlGnomeSettings: +https://github.com/flutter/engine/tree/main/shell/platform/linux diff --git a/linux/settings/handy_gnome_settings.cc b/linux/settings/handy_gnome_settings.cc new file mode 100644 index 0000000..adc0ddc --- /dev/null +++ b/linux/settings/handy_gnome_settings.cc @@ -0,0 +1,120 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "handy_gnome_settings.h" + +#include +#include + +static constexpr char kDesktopInterfaceSchema[] = "org.gnome.desktop.interface"; +static constexpr char kDesktopGtkThemeKey[] = "gtk-theme"; + +static constexpr char kGtkThemeDarkSuffix[] = "-dark"; +static constexpr char kInterfaceSettings[] = "interface-settings"; + +struct _HandyGnomeSettings { + GObject parent_instance; + + GSettings* interface_settings; +}; + +enum { kProp0, kPropInterfaceSettings, kPropLast }; + +static void handy_gnome_settings_iface_init(HandySettingsInterface* iface); + +G_DEFINE_TYPE_WITH_CODE(HandyGnomeSettings, handy_gnome_settings, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(handy_settings_get_type(), + handy_gnome_settings_iface_init)) + +static HandyColorScheme handy_gnome_settings_get_color_scheme( + HandySettings* settings) { + HandyGnomeSettings* self = HANDY_GNOME_SETTINGS(settings); + + HandyColorScheme color_scheme = HANDY_COLOR_SCHEME_LIGHT; + + if (self->interface_settings != nullptr) { + // check whether org.gnome.desktop.interface.gtk-theme ends with "-dark" + g_autofree gchar* value = + g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey); + if (g_str_has_suffix(value, kGtkThemeDarkSuffix)) { + color_scheme = HANDY_COLOR_SCHEME_DARK; + } + } + return color_scheme; +} + +static void handy_gnome_settings_set_interface_settings( + HandyGnomeSettings* self, GSettings* settings) { + g_return_if_fail(G_IS_SETTINGS(settings)); + + g_signal_connect_object(settings, "changed::gtk-theme", + G_CALLBACK(handy_settings_emit_changed), self, + G_CONNECT_SWAPPED); + + self->interface_settings = G_SETTINGS(g_object_ref(settings)); +} + +static void handy_gnome_settings_set_property(GObject* object, guint prop_id, + const GValue* value, + GParamSpec* pspec) { + HandyGnomeSettings* self = HANDY_GNOME_SETTINGS(object); + switch (prop_id) { + case kPropInterfaceSettings: + handy_gnome_settings_set_interface_settings( + self, G_SETTINGS(g_value_get_object(value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void handy_gnome_settings_dispose(GObject* object) { + HandyGnomeSettings* self = HANDY_GNOME_SETTINGS(object); + + g_clear_object(&self->interface_settings); + + G_OBJECT_CLASS(handy_gnome_settings_parent_class)->dispose(object); +} + +static void handy_gnome_settings_class_init(HandyGnomeSettingsClass* klass) { + GObjectClass* object_class = G_OBJECT_CLASS(klass); + object_class->dispose = handy_gnome_settings_dispose; + object_class->set_property = handy_gnome_settings_set_property; + + g_object_class_install_property( + object_class, kPropInterfaceSettings, + g_param_spec_object( + kInterfaceSettings, kInterfaceSettings, kDesktopInterfaceSchema, + g_settings_get_type(), + static_cast(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); +} + +static void handy_gnome_settings_iface_init(HandySettingsInterface* iface) { + iface->get_color_scheme = handy_gnome_settings_get_color_scheme; +} + +static void handy_gnome_settings_init(HandyGnomeSettings*) {} + +static GSettings* create_settings(const gchar* schema_id) { + GSettings* settings = nullptr; + GSettingsSchemaSource* source = g_settings_schema_source_get_default(); + if (source != nullptr) { + g_autoptr(GSettingsSchema) schema = + g_settings_schema_source_lookup(source, schema_id, TRUE); + if (schema != nullptr) { + settings = g_settings_new_full(schema, nullptr, nullptr); + } + } + return settings; +} + +HandySettings* handy_gnome_settings_new() { + g_autoptr(GSettings) interface_settings = + create_settings(kDesktopInterfaceSchema); + return HANDY_SETTINGS(g_object_new(handy_gnome_settings_get_type(), + kInterfaceSettings, interface_settings, + nullptr)); +} diff --git a/linux/settings/handy_gnome_settings.h b/linux/settings/handy_gnome_settings.h new file mode 100644 index 0000000..9f0da35 --- /dev/null +++ b/linux/settings/handy_gnome_settings.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef HANDY_GNOME_SETTINGS_H_ +#define HANDY_GNOME_SETTINGS_H_ + +#include "handy_settings.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(HandyGnomeSettings, handy_gnome_settings, HANDY, + GNOME_SETTINGS, GObject); + +/** + * handy_gnome_settings_new: + * + * Creates a new settings instance for GNOME. + * + * Returns: a new #HandySettings. + */ +HandySettings* handy_gnome_settings_new(); + +G_END_DECLS + +#endif // HANDY_GNOME_SETTINGS_H_ diff --git a/linux/settings/handy_settings.cc b/linux/settings/handy_settings.cc new file mode 100644 index 0000000..4816e70 --- /dev/null +++ b/linux/settings/handy_settings.cc @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "handy_settings.h" + +#include "handy_gnome_settings.h" +#include "handy_settings_portal.h" + +G_DEFINE_INTERFACE(HandySettings, handy_settings, G_TYPE_OBJECT) + +enum { + kSignalChanged, + kSignalLastSignal, +}; + +static guint signals[kSignalLastSignal]; + +static void handy_settings_default_init(HandySettingsInterface* iface) { + /** + * HandySettings::changed: + * @settings: an #HandySettings + * + * This signal is emitted after the settings have been changed. + */ + signals[kSignalChanged] = + g_signal_new("changed", G_TYPE_FROM_INTERFACE(iface), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +HandyColorScheme handy_settings_get_color_scheme(HandySettings* self) { + return HANDY_SETTINGS_GET_IFACE(self)->get_color_scheme(self); +} + +void handy_settings_emit_changed(HandySettings* self) { + g_return_if_fail(HANDY_IS_SETTINGS(self)); + g_signal_emit(self, signals[kSignalChanged], 0); +} + +HandySettings* handy_settings_new() { + g_autoptr(HandySettingsPortal) portal = handy_settings_portal_new(); + + g_autoptr(GError) error = nullptr; + if (!handy_settings_portal_start(portal, &error)) { + g_debug("XDG desktop portal settings unavailable: %s", error->message); + return handy_gnome_settings_new(); + } + + return HANDY_SETTINGS(g_object_ref(portal)); +} diff --git a/linux/settings/handy_settings.h b/linux/settings/handy_settings.h new file mode 100644 index 0000000..3ba2644 --- /dev/null +++ b/linux/settings/handy_settings.h @@ -0,0 +1,67 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef HANDY_SETTINGS_H_ +#define HANDY_SETTINGS_H_ + +#include + +G_BEGIN_DECLS + +G_DECLARE_INTERFACE(HandySettings, handy_settings, HANDY, SETTINGS, GObject) + +/** + * HandyColorScheme: + * @HANDY_COLOR_SCHEME_LIGHT: Prefer light theme. + * @HANDY_COLOR_SCHEME_DARK: Prefer dark theme. + * + * Available color schemes. + */ +typedef enum { + HANDY_COLOR_SCHEME_LIGHT, + HANDY_COLOR_SCHEME_DARK, +} HandyColorScheme; + +/** + * HandySettings: + * #HandySettings is and object that provides desktop settings. + */ +struct _HandySettingsInterface { + GTypeInterface parent; + HandyColorScheme (*get_color_scheme)(HandySettings* settings); +}; + +/** + * handy_settings_new: + * + * Creates a new settings instance. + * + * Returns: a new #HandySettings. + */ +HandySettings* handy_settings_new(); + +/** + * handy_settings_get_color_scheme: + * @settings: an #HandySettings. + * + * The preferred color scheme for the user interface. + * + * This corresponds to `org.gnome.desktop.interface.color-scheme` in GNOME. + * + * Returns: an #HandyColorScheme. + */ +HandyColorScheme handy_settings_get_color_scheme(HandySettings* settings); + +/** + * handy_settings_emit_changed: + * @settings: an #HandySettings. + * + * Emits the "changed" signal. Used by HandySettings implementations to notify + * when the desktop settings have changed. + */ +void handy_settings_emit_changed(HandySettings* settings); + +G_END_DECLS + +#endif // HANDY_SETTINGS_H_ diff --git a/linux/settings/handy_settings_portal.cc b/linux/settings/handy_settings_portal.cc new file mode 100644 index 0000000..f54bc8d --- /dev/null +++ b/linux/settings/handy_settings_portal.cc @@ -0,0 +1,212 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "handy_settings_portal.h" + +#include +#include + +static constexpr char kPortalName[] = "org.freedesktop.portal.Desktop"; +static constexpr char kPortalPath[] = "/org/freedesktop/portal/desktop"; +static constexpr char kPortalSettings[] = "org.freedesktop.portal.Settings"; + +struct HandySetting { + const gchar* ns; + const gchar* key; + const GVariantType* type; +}; + +static constexpr char kXdgAppearance[] = "org.freedesktop.appearance"; +static const HandySetting kColorScheme = { + kXdgAppearance, + "color-scheme", + G_VARIANT_TYPE_UINT32, +}; + +static constexpr char kGnomeDesktopInterface[] = "org.gnome.desktop.interface"; +static const HandySetting kGtkTheme = { + kGnomeDesktopInterface, + "gtk-theme", + G_VARIANT_TYPE_STRING, +}; + +static const HandySetting kAllSettings[] = {kColorScheme, kGtkTheme}; + +static constexpr char kGtkThemeDarkSuffix[] = "-dark"; + +typedef enum { kDefault, kPreferDark, kPreferLight } ColorScheme; + +struct _HandySettingsPortal { + GObject parent_instance; + + GDBusProxy* dbus_proxy; + GVariantDict* values; +}; + +static void handy_settings_portal_iface_init(HandySettingsInterface* iface); + +G_DEFINE_TYPE_WITH_CODE(HandySettingsPortal, handy_settings_portal, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(handy_settings_get_type(), + handy_settings_portal_iface_init)) + +static gchar* format_key(const HandySetting* setting) { + return g_strconcat(setting->ns, "::", setting->key, nullptr); +} + +static gboolean get_value(HandySettingsPortal* portal, + const HandySetting* setting, GVariant** value) { + g_autofree const gchar* key = format_key(setting); + *value = g_variant_dict_lookup_value(portal->values, key, setting->type); + return *value != nullptr; +} + +static void set_value(HandySettingsPortal* portal, const HandySetting* setting, + GVariant* value) { + g_autofree const gchar* key = format_key(setting); + + // ignore redundant changes from multiple XDG desktop portal backends + g_autoptr(GVariant) old_value = + g_variant_dict_lookup_value(portal->values, key, nullptr); + if (old_value != nullptr && value != nullptr && + g_variant_equal(old_value, value)) { + return; + } + + g_variant_dict_insert_value(portal->values, key, value); + handy_settings_emit_changed(HANDY_SETTINGS(portal)); +} + +// Based on +// https://gitlab.gnome.org/GNOME/Initiatives/-/wikis/Dark-Style-Preference#other +static gboolean settings_portal_read(GDBusProxy* proxy, const gchar* ns, + const gchar* key, GVariant** out) { + g_autoptr(GError) error = nullptr; + g_autoptr(GVariant) value = + g_dbus_proxy_call_sync(proxy, "Read", g_variant_new("(ss)", ns, key), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, nullptr, &error); + + if (error) { + if (error->domain == G_DBUS_ERROR && + error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) { + g_debug("XDG desktop portal unavailable: %s", error->message); + return false; + } + + if (error->domain == G_DBUS_ERROR && + error->code == G_DBUS_ERROR_UNKNOWN_METHOD) { + g_debug("XDG desktop portal settings unavailable: %s", error->message); + return false; + } + + g_critical("Failed to read XDG desktop portal settings: %s", + error->message); + return false; + } + + g_autoptr(GVariant) child = nullptr; + g_variant_get(value, "(v)", &child); + g_variant_get(child, "v", out); + + return true; +} + +static void settings_portal_changed_cb(GDBusProxy* /*proxy*/, + const char* /*sender_name*/, + const char* signal_name, + GVariant* parameters, + gpointer user_data) { + HandySettingsPortal* portal = HANDY_SETTINGS_PORTAL(user_data); + if (g_strcmp0(signal_name, "SettingChanged")) { + return; + } + + HandySetting setting; + g_autoptr(GVariant) value = nullptr; + g_variant_get(parameters, "(&s&sv)", &setting.ns, &setting.key, &value); + set_value(portal, &setting, value); +} + +static HandyColorScheme handy_settings_portal_get_color_scheme( + HandySettings* settings) { + HandySettingsPortal* self = HANDY_SETTINGS_PORTAL(settings); + + HandyColorScheme color_scheme = HANDY_COLOR_SCHEME_LIGHT; + + g_autoptr(GVariant) value = nullptr; + if (get_value(self, &kColorScheme, &value)) { + if (g_variant_get_uint32(value) == kPreferDark) { + color_scheme = HANDY_COLOR_SCHEME_DARK; + } + } else if (get_value(self, &kGtkTheme, &value)) { + const gchar* gtk_theme_str = g_variant_get_string(value, nullptr); + if (g_str_has_suffix(gtk_theme_str, kGtkThemeDarkSuffix)) { + color_scheme = HANDY_COLOR_SCHEME_DARK; + } + } + + return color_scheme; +} + +static void handy_settings_portal_dispose(GObject* object) { + HandySettingsPortal* self = HANDY_SETTINGS_PORTAL(object); + + g_clear_object(&self->dbus_proxy); + g_clear_pointer(&self->values, g_variant_dict_unref); + + G_OBJECT_CLASS(handy_settings_portal_parent_class)->dispose(object); +} + +static void handy_settings_portal_class_init(HandySettingsPortalClass* klass) { + GObjectClass* object_class = G_OBJECT_CLASS(klass); + object_class->dispose = handy_settings_portal_dispose; +} + +static void handy_settings_portal_iface_init(HandySettingsInterface* iface) { + iface->get_color_scheme = handy_settings_portal_get_color_scheme; +} + +static void handy_settings_portal_init(HandySettingsPortal*) {} + +HandySettingsPortal* handy_settings_portal_new() { + g_autoptr(GVariantDict) values = g_variant_dict_new(nullptr); + return handy_settings_portal_new_with_values(values); +} + +HandySettingsPortal* handy_settings_portal_new_with_values( + GVariantDict* values) { + g_return_val_if_fail(values != nullptr, nullptr); + HandySettingsPortal* portal = HANDY_SETTINGS_PORTAL( + g_object_new(handy_settings_portal_get_type(), nullptr)); + portal->values = g_variant_dict_ref(values); + return portal; +} + +gboolean handy_settings_portal_start(HandySettingsPortal* self, + GError** error) { + g_return_val_if_fail(HANDY_IS_SETTINGS_PORTAL(self), false); + g_return_val_if_fail(self->dbus_proxy == nullptr, false); + + self->dbus_proxy = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr, kPortalName, + kPortalPath, kPortalSettings, nullptr, error); + + if (self->dbus_proxy == nullptr) { + return false; + } + + for (const HandySetting setting : kAllSettings) { + g_autoptr(GVariant) value = nullptr; + if (settings_portal_read(self->dbus_proxy, setting.ns, setting.key, + &value)) { + set_value(self, &setting, value); + } + } + + g_signal_connect_object(self->dbus_proxy, "g-signal", + G_CALLBACK(settings_portal_changed_cb), self, + static_cast(0)); + + return true; +} diff --git a/linux/settings/handy_settings_portal.h b/linux/settings/handy_settings_portal.h new file mode 100644 index 0000000..3b7171f --- /dev/null +++ b/linux/settings/handy_settings_portal.h @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef HANDY_SETTINGS_PORTAL_H_ +#define HANDY_SETTINGS_PORTAL_H_ + +#include "handy_settings.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(HandySettingsPortal, handy_settings_portal, HANDY, + SETTINGS_PORTAL, GObject); + +/** + * HandySettingsPortal: + * #HandySettingsPortal reads settings from the XDG desktop portal. + */ + +/** + * handy_settings_portal_new: + * + * Creates a new settings portal instance. + * + * Returns: a new #HandySettingsPortal. + */ +HandySettingsPortal* handy_settings_portal_new(); + +/** + * handy_settings_portal_new_with_values: + * @values: (nullable): a #GVariantDict. + * + * Creates a new settings portal instance with initial values for testing. + * + * Returns: a new #HandySettingsPortal. + */ +HandySettingsPortal* handy_settings_portal_new_with_values( + GVariantDict* values); + +/** + * handy_settings_portal_start: + * @portal: an #HandySettingsPortal. + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * + * Reads the current settings and starts monitoring for changes in the desktop + * portal settings. + * + * Returns: %TRUE on success, or %FALSE if the portal is not available. + */ +gboolean handy_settings_portal_start(HandySettingsPortal* portal, + GError** error); + +G_END_DECLS + +#endif // HANDY_SETTINGS_PORTAL_H_ diff --git a/pubspec.yaml b/pubspec.yaml index d068db6..56e1e21 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,6 +19,9 @@ dev_dependencies: flutter_lints: ^2.0.0 flutter: + assets: + - assets/handy-window.css + - assets/handy-window-dark.css plugin: platforms: linux: From 471897264e970fdc0df597fb9dc25af9910e0666 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 5 Jan 2023 20:53:11 +0100 Subject: [PATCH 2/2] Update handy-window-dark.css --- assets/handy-window-dark.css | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/assets/handy-window-dark.css b/assets/handy-window-dark.css index e3e21c8..28dbba6 100644 --- a/assets/handy-window-dark.css +++ b/assets/handy-window-dark.css @@ -2,6 +2,15 @@ window.csd.unified:not(.solid-csd):not(.fullscreen):not(.maximized) decoration-o box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07); } -window.csd.unified:not(.solid-csd):not(.fullscreen):not(.maximized) decoration-overlay~* headerbar { +window.csd.unified:not(.solid-csd):not(.fullscreen):not(.maximized) headerbar { box-shadow: none; } + +decoration { + box-shadow: 0 3px 9px 1px rgba(0,0,0,.5); +} + +decoration:backdrop { + box-shadow: 0 3px 9px 1px transparent, + 0 2px 6px 2px rgba(0,0,0,.2); +}