From 85f9bbeea11db35c270fcd3d4d337998652fb3d9 Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:35:51 +1000 Subject: [PATCH 01/12] protocol: add the FuriOS layer shell effects protocol The blur request needs somewhere to live. Adding it to zphoc_layer_shell_effects_v1 would mean editing a protocol phoc owns: our version 4 and any version 4 phoc later defines would be two different things under one name, and a client could not tell them apart. So define zphoc_furios_layer_shell_effects_v1 instead, in a file of our own. Identical to the copy in phoc's tree, which is where the compositor side of it lives; the two must stay in sync. Signed-off-by: wayne --- protocol/meson.build | 1 + ...furios-layer-shell-effects-unstable-v1.xml | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 protocol/phoc-furios-layer-shell-effects-unstable-v1.xml diff --git a/protocol/meson.build b/protocol/meson.build index 3007a1c17..455e9ae17 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -12,6 +12,7 @@ wl_protos = [ [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], ['input-method-unstable-v2.xml'], ['phoc-device-state-unstable-v1.xml'], + ['phoc-furios-layer-shell-effects-unstable-v1.xml'], ['phoc-layer-shell-effects-unstable-v1.xml'], ['phosh-private.xml'], ['virtual-keyboard-unstable-v1.xml'], diff --git a/protocol/phoc-furios-layer-shell-effects-unstable-v1.xml b/protocol/phoc-furios-layer-shell-effects-unstable-v1.xml new file mode 100644 index 000000000..77da053dc --- /dev/null +++ b/protocol/phoc-furios-layer-shell-effects-unstable-v1.xml @@ -0,0 +1,93 @@ + + + + Copyright © 2026 FuriLabs + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Assigns FuriOS specific features to layer surfaces. + + This is deliberately a protocol of its own rather than additions to + zphoc_layer_shell_effects_v1, which phoc owns: extending that interface + here would collide with any version phoc adds upstream, and a client + could not then tell the two apart. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + + + + + This doesn't destroy the objects created via this interface. + + + + + + Requests that the compositor blurs the content rendered beneath + the layer surface so that translucent parts of the surface appear + frosted. + + + + + + + + + This interface gives a layer surface a blurred backdrop. The blur is + only visible through the translucent parts of the surface. + + + + + Set the strength of the blur applied to the content beneath the + surface. A value of 0 disables blur; higher values increase the + blur radius. The compositor maps the value onto its blur + implementation; useful values are in the range 0 to 100. + + Blur is double-buffered, see wl_surface.commit. + + + + + + + This request destroys the blur layer surface and removes the + blur from the underlying surface. + + + + From ea39f7799ff87919da80aca463c48219ab667d6e Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:35:51 +1000 Subject: [PATCH 02/12] phosh-wayland: bind the FuriOS layer shell effects global Binds zphoc_furios_layer_shell_effects_v1 when the compositor offers it and leaves zphoc_layer_shell_effects_v1 bound at version 3, as before. Unlike the phoc globals above it this one is optional and deliberately absent from the "lacks needed globals" check: phosh has to keep running against a compositor without it, just without blur. Signed-off-by: wayne --- src/phosh-wayland.c | 24 ++++++++++++++++++++++++ src/phosh-wayland.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/src/phosh-wayland.c b/src/phosh-wayland.c index cba8a12c5..5b8cdef68 100644 --- a/src/phosh-wayland.c +++ b/src/phosh-wayland.c @@ -56,6 +56,7 @@ struct _PhoshWayland { struct zxdg_output_manager_v1 *zxdg_output_manager_v1; struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1; struct zphoc_layer_shell_effects_v1 *zphoc_layer_shell_effects_v1; + struct zphoc_furios_layer_shell_effects_v1 *zphoc_furios_layer_shell_effects_v1; struct zphoc_device_state_v1 *zphoc_device_state_v1; struct wl_shm *wl_shm; GHashTable *wl_outputs; @@ -86,6 +87,11 @@ registry_handle_global (void *data, name, &zphoc_layer_shell_effects_v1_interface, MIN (3, version)); + } else if (!strcmp (interface, zphoc_furios_layer_shell_effects_v1_interface.name)) { + /* Optional: a compositor without the FuriOS effects simply gets no blur */ + self->zphoc_furios_layer_shell_effects_v1 = + wl_registry_bind (registry, name, &zphoc_furios_layer_shell_effects_v1_interface, + MIN (1, version)); } else if (!strcmp (interface, zphoc_device_state_v1_interface.name)) { self->zphoc_device_state_v1 = wl_registry_bind (registry, name, @@ -325,6 +331,8 @@ phosh_wayland_dispose (GObject *object) g_clear_pointer (&self->zwlr_screencopy_manager_v1, zwlr_screencopy_manager_v1_destroy); g_clear_pointer (&self->zwp_virtual_keyboard_manager_v1, zwp_virtual_keyboard_manager_v1_destroy); g_clear_pointer (&self->zxdg_output_manager_v1, zxdg_output_manager_v1_destroy); + g_clear_pointer (&self->zphoc_furios_layer_shell_effects_v1, + zphoc_furios_layer_shell_effects_v1_destroy); g_clear_pointer (&self->zphoc_layer_shell_effects_v1, zphoc_layer_shell_effects_v1_destroy); g_clear_pointer (&self->zphoc_device_state_v1, zphoc_device_state_v1_destroy); @@ -572,6 +580,22 @@ phosh_wayland_get_seat_capabilities (PhoshWayland *self) } +/** + * phosh_wayland_get_zphoc_furios_layer_shell_effects_v1: + * @self: The #PhoshWayland singleton + * + * Returns: (transfer none) (nullable): the FuriOS layer shell effects global, + * or %NULL when the compositor does not implement it. + */ +struct zphoc_furios_layer_shell_effects_v1 * +phosh_wayland_get_zphoc_furios_layer_shell_effects_v1 (PhoshWayland *self) +{ + g_return_val_if_fail (PHOSH_IS_WAYLAND (self), NULL); + + return self->zphoc_furios_layer_shell_effects_v1; +} + + struct zphoc_layer_shell_effects_v1 * phosh_wayland_get_zphoc_layer_shell_effects_v1 (PhoshWayland *self) { diff --git a/src/phosh-wayland.h b/src/phosh-wayland.h index baf52b463..99688c8d0 100644 --- a/src/phosh-wayland.h +++ b/src/phosh-wayland.h @@ -11,6 +11,7 @@ #include "virtual-keyboard-unstable-v1-client-protocol.h" #include "phoc-device-state-unstable-v1-client-protocol.h" #include "phoc-layer-shell-effects-unstable-v1-client-protocol.h" +#include "phoc-furios-layer-shell-effects-unstable-v1-client-protocol.h" #include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "wlr-gamma-control-unstable-v1-client-protocol.h" @@ -76,5 +77,6 @@ struct zwp_virtual_keyboard_manager_v1 *phosh_wayland_get_zwp_virtual_keyboard_m void phosh_wayland_roundtrip (PhoshWayland *self); PhoshWaylandSeatCapabilities phosh_wayland_get_seat_capabilities (PhoshWayland *self); struct zphoc_layer_shell_effects_v1 *phosh_wayland_get_zphoc_layer_shell_effects_v1 (PhoshWayland *self); +struct zphoc_furios_layer_shell_effects_v1 *phosh_wayland_get_zphoc_furios_layer_shell_effects_v1 (PhoshWayland *self); struct zphoc_device_state_v1 *phosh_wayland_get_zphoc_device_state_v1 (PhoshWayland *self); G_END_DECLS From 1f148ca738de54829587e3cfe9194b64de16d316 Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:35:51 +1000 Subject: [PATCH 03/12] layersurface: ask the compositor to blur the background phosh_layer_surface_set_blur() requests a blur radius for the surface's backdrop. The radius is remembered before map and applied once the surface exists, mirroring how alpha is handled. The request goes to zphoc_furios_layer_shell_effects_v1, our own global, so a compositor that does not offer it simply leaves blur_surface NULL and set_blur() does nothing. Signed-off-by: wayne --- src/layersurface-priv.h | 2 ++ src/layersurface.c | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/layersurface-priv.h b/src/layersurface-priv.h index 61669ce42..7337219e5 100644 --- a/src/layersurface-priv.h +++ b/src/layersurface-priv.h @@ -41,6 +41,8 @@ int phosh_layer_surface_get_configured_width (Pho int phosh_layer_surface_get_configured_height (PhoshLayerSurface *self); void phosh_layer_surface_set_alpha (PhoshLayerSurface *self, double alpha); +void phosh_layer_surface_set_blur (PhoshLayerSurface *self, + guint radius); void phosh_layer_surface_set_stacked_above (PhoshLayerSurface *self, PhoshLayerSurface *target); void phosh_layer_surface_set_stacked_below (PhoshLayerSurface *self, diff --git a/src/layersurface.c b/src/layersurface.c index 52885b795..acbfb4314 100644 --- a/src/layersurface.c +++ b/src/layersurface.c @@ -56,6 +56,7 @@ typedef struct { struct wl_surface *wl_surface; struct zwlr_layer_surface_v1 *layer_surface; struct zphoc_alpha_layer_surface_v1 *alpha_surface; + struct zphoc_furios_blur_layer_surface_v1 *blur_surface; struct zphoc_stacked_layer_surface_v1 *stacked_surface; /* Properties */ @@ -72,6 +73,8 @@ typedef struct { struct wl_output *wl_output; /* alpha_layer_surface_v1 */ double alpha; + /* blur_layer_surface_v1 */ + guint blur_radius; /* stacked_layer_surface_v1 */ PhoshLayerSurface *stack_target; gboolean stack_above; @@ -153,6 +156,21 @@ set_alpha (PhoshLayerSurface *self, double alpha) } +static void +set_blur (PhoshLayerSurface *self, guint radius) +{ + PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); + + priv->blur_radius = radius; + + if (!priv->blur_surface) + return; + + zphoc_furios_blur_layer_surface_v1_set_blur (priv->blur_surface, radius); + wl_surface_commit (priv->wl_surface); +} + + static void phosh_layer_surface_set_stacked (PhoshLayerSurface *self, PhoshLayerSurface *target, gboolean above) { @@ -354,6 +372,7 @@ phosh_layer_surface_map (GtkWidget *widget) PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); PhoshWayland *wl = phosh_wayland_get_default (); struct zphoc_layer_shell_effects_v1 *layer_shell_effects; + struct zphoc_furios_layer_shell_effects_v1 *furios_layer_shell_effects; GTK_WIDGET_CLASS (phosh_layer_surface_parent_class)->map (widget); @@ -396,11 +415,23 @@ phosh_layer_surface_map (GtkWidget *widget) priv->stacked_surface = zphoc_layer_shell_effects_v1_get_stacked_layer_surface (layer_shell_effects, priv->layer_surface); + /* Blur is ours rather than phoc's, so it comes from a global of its own. A + * compositor without it leaves blur_surface NULL and set_blur() does nothing. */ + furios_layer_shell_effects = phosh_wayland_get_zphoc_furios_layer_shell_effects_v1 (wl); + if (furios_layer_shell_effects) { + priv->blur_surface = + zphoc_furios_layer_shell_effects_v1_get_blur_layer_surface (furios_layer_shell_effects, + priv->layer_surface); + } /* Catch up with alpha values set before map */ if (!G_APPROX_VALUE (priv->alpha, 1.0, FLT_EPSILON)) set_alpha (self, priv->alpha); + /* Catch up with blur values set before map */ + if (priv->blur_radius > 0) + set_blur (self, priv->blur_radius); + /* Catch up with stackings set before map */ if (priv->stacked_surface) phosh_layer_surface_set_stacked (self, priv->stack_target, priv->stack_above); @@ -414,6 +445,7 @@ phosh_layer_surface_unmap (GtkWidget *widget) PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); g_clear_pointer (&priv->alpha_surface, zphoc_alpha_layer_surface_v1_destroy); + g_clear_pointer (&priv->blur_surface, zphoc_furios_blur_layer_surface_v1_destroy); g_clear_pointer (&priv->stacked_surface, zphoc_stacked_layer_surface_v1_destroy); g_clear_pointer (&priv->layer_surface, zwlr_layer_surface_v1_destroy); priv->wl_surface = NULL; @@ -437,6 +469,7 @@ phosh_layer_surface_dispose (GObject *object) phosh_layer_surface_set_stacked (self, NULL, FALSE); g_clear_pointer (&priv->alpha_surface, zphoc_alpha_layer_surface_v1_destroy); + g_clear_pointer (&priv->blur_surface, zphoc_furios_blur_layer_surface_v1_destroy); g_clear_pointer (&priv->stacked_surface, zphoc_stacked_layer_surface_v1_destroy); g_clear_pointer (&priv->layer_surface, zwlr_layer_surface_v1_destroy); g_clear_pointer (&priv->namespace, g_free); @@ -968,6 +1001,29 @@ phosh_layer_surface_set_alpha (PhoshLayerSurface *self, double alpha) set_alpha (self, alpha); } +/** + * phosh_layer_surface_set_blur: + * @self: The layer surface + * @radius: The blur radius, 0 disables blur + * + * Requests that the compositor blurs the content behind the surface + * so translucent parts appear frosted. Needs a compositor supporting + * the blur layer surface protocol, otherwise it has no effect. + */ +void +phosh_layer_surface_set_blur (PhoshLayerSurface *self, guint radius) +{ + PhoshLayerSurfacePrivate *priv; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + if (priv->blur_radius == radius) + return; + + set_blur (self, radius); +} + /** * phosh_layer_surface_set_stacked_above: * @self: The surface to be stacked From 2298e5ed86f6e097afc293442f682e684dd1d5e3 Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:36:02 +1000 Subject: [PATCH 04/12] util: add the glass gsettings keys The io.furios.phosh.shell keys the look is built from, shared between the style manager that turns them into colours and the surfaces that ask the compositor for blur. Signed-off-by: wayne --- src/util.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/util.h b/src/util.h index 0cbf23317..0fd8b5e43 100644 --- a/src/util.h +++ b/src/util.h @@ -14,6 +14,25 @@ G_BEGIN_DECLS +/* FuriOS glass look: whether it is used at all, how strong the background + * blur is, how solid and how accent-tinted the glass surfaces are, and an + * optional free-form accent colour that overrides the nine-swatch choice in + * Settings. Shared by the style manager, which turns them into colours, and + * by the surfaces that ask the compositor for blur. */ +#define PHOSH_GLASS_SCHEMA_ID "io.furios.phosh.shell" +#define PHOSH_GLASS_KEY_THEME "glass-theme" +#define PHOSH_GLASS_KEY_BLUR_RADIUS "glass-blur-radius" +#define PHOSH_KEY_LOCKSCREEN_TINT "lockscreen-tint" +#define PHOSH_GLASS_KEY_OPACITY "glass-opacity" +#define PHOSH_GLASS_KEY_LIGHTNESS "glass-lightness" +#define PHOSH_GLASS_KEY_ACCENT_WASH "glass-accent-wash" +#define PHOSH_GLASS_KEY_ACCENT_CUSTOM "accent-color-custom" +#define PHOSH_GLASS_KEY_TEXT_COLOR "glass-text-color" +#define PHOSH_GLASS_KEY_TEXT_OPACITY "glass-text-opacity" +#define PHOSH_GLASS_KEY_ACCENT_TEXT "glass-accent-text-color" +#define PHOSH_GLASS_KEY_TEXT_SHADOW "glass-text-shadow" +#define PHOSH_GLASS_KEY_SHADOW_COLOR "glass-text-shadow-color" + #define phosh_async_error_warn(err, ...) \ phosh_error_warnv (G_LOG_DOMAIN, err, G_IO_ERROR, G_IO_ERROR_CANCELLED, __VA_ARGS__) From 4ea223c50dce2c9336a423739250426840f2a83d Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:36:03 +1000 Subject: [PATCH 05/12] data: add the glass gsettings keys A switch for the glass theme itself, off by default, so phosh keeps its own look until it is asked for. Then blur radius, glass opacity, lightness and accent wash, the text and shadow colours, and a free-form accent that overrides the nine swatches. The look is retuned with gsettings rather than by rebuilding a stylesheet. Signed-off-by: wayne --- data/mobi.phosh.shell.gschema.xml | 149 ++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/data/mobi.phosh.shell.gschema.xml b/data/mobi.phosh.shell.gschema.xml index e3040811e..8f44fd4e1 100644 --- a/data/mobi.phosh.shell.gschema.xml +++ b/data/mobi.phosh.shell.gschema.xml @@ -255,5 +255,154 @@ Apps in this list will not appear in the launcher or app drawer. + + + false + Use the FuriOS glass theme + + Whether to style the shell as frosted glass. Off leaves phosh with + its own stylesheet untouched; on loads the FuriOS variant of it and + gives GTK4 applications a matching one. The keys below only take + effect while this is on. + + + + + '' + Custom accent colour + + A free-form accent colour for the shell, as any colour GDK can parse, + for example '#3584e4'. When set this overrides the accent chosen in + Settings, so the shell is not limited to the nine preset swatches. + Empty means follow that setting. Note that GTK4 applications only + understand the presets, so a custom colour applies to the shell alone. + + + + + '' + Shell text colour + + A free-form colour for the shell's text and icons, as any colour GDK + can parse. Every piece of shell text follows this, including text + drawn on an accent fill. Empty follows the theme, which is near-white + on the dark variant and near-black on the light one. + + + + + '' + Colour of text drawn on the accent + + The colour of labels and icons sitting on top of an accent fill -- a + selected quick setting, a pressed keypad key, a card. This is a + different question from the main text colour only if you want it to + be. Empty follows the main text colour, so all of the shell's text + matches unless this is set deliberately. + + + + + '' + Colour of the drop shadow behind shell text + + The colour of the small drop shadow carried by the shell's text and + icons, most visibly the lock screen clock, date and status labels, + which sit over a wallpaper the shell does not choose. Empty follows + the text colour in use and takes the opposite of it, so light text + gets a dark shadow and dark text a light one. Changing glass-text-color + is therefore enough on its own; this key is for the case where the + wallpaper wants a shadow that is neither. + + + + + 0.7 + + Strength of the drop shadow behind shell text + + How opaque that shadow is. 0 turns it off entirely. The offset stays + small and fixed -- one to three pixels depending on the size of the + text, always more than the blur under it, because a blur wider than + its offset is a glow rather than a shadow. It is there to hold text + legible against an arbitrary wallpaper, not to be seen in its own + right. + + + + + 1.0 + + Shell text opacity + + How opaque the shell's text and icons are. Lower values let the + frosted backdrop show through the text itself. The floor is deliberate: + text that can be turned fully invisible is a way to make the shell + unusable with no obvious way back. + + + + + true + Tint the wallpaper behind the lock screen clock + + Whether the lock screen lays its translucent sheet over the wallpaper + on the page showing the clock and date. On, the wallpaper is dimmed and + the clock stays legible over a bright or busy picture. Off, the picture + is shown as it is. + + The keypad page keeps its sheet either way: the keys and the passcode + entry need a surface to sit on. + + + + + 100 + + Background blur radius + + How strongly the compositor blurs what is behind the shell's glass + surfaces: the top panel, the settings drawer and the lock screen. + 0 turns the blur off. Needs a compositor that supports background + blur; without one this has no effect. + + + + + 0.15 + + Glass opacity + + How solid the shell's glass surfaces are. 0.0 is fully transparent + and 1.0 is opaque, hiding the blur behind it entirely. Other glass + surfaces are scaled from this, keeping their relative weights. + Application windows keep a small minimum whatever this is set to, + since a window with no background at all leaves its text floating on + the wallpaper. + + + + + 0.15 + + Glass lightness + + How much white is mixed into the glass, for a frostier and paler + look. 0.0 keeps the theme's own background colour. + + + + + 0.15 + + Accent wash strength + + How much of the accent colour is washed into the glass surfaces, so + that the drawer, lock screen and overview carry the accent chosen in + Settings. 0.0 leaves them neutral. The light theme uses a lower + proportion of this, since a tint reads more strongly against a pale + background. + + From 46b0cff6ecd5f2c7ac1d02d1bbaca69fb8bcbbc2 Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 17 Aug 2026 22:08:42 +1000 Subject: [PATCH 06/12] stylesheet: add a frosted glass variant Translucent surfaces, soft shadows and inset hairline highlights on the drawer, notifications, media player and OSD, with the radii normalised. The panel is a single glass sheet: stacking translucent layers on top of each other is what made the drawer read as solid. Kept as whole stylesheets of their own -- common-furios.css and adwaita-dark-furios.css -- rather than as edits to phosh's, so nothing about the shell's own look changes until the glass theme is switched on and these are loaded in their place. Signed-off-by: wayne --- src/phosh.gresources.xml | 2 + src/stylesheet/adwaita-dark-furios.css | 56 + src/stylesheet/common-furios.css | 1527 ++++++++++++++++++++++++ 3 files changed, 1585 insertions(+) create mode 100644 src/stylesheet/adwaita-dark-furios.css create mode 100644 src/stylesheet/common-furios.css diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml index a9b3d64d2..f97ed872a 100644 --- a/src/phosh.gresources.xml +++ b/src/phosh.gresources.xml @@ -51,8 +51,10 @@ ui/wifi-network-row.ui ui/wifi-status-page.ui stylesheet/adwaita-dark.css + stylesheet/adwaita-dark-furios.css stylesheet/adwaita-hc-light.css stylesheet/common.css + stylesheet/common-furios.css ../data/icons/app-icon-unknown.svg diff --git a/src/stylesheet/adwaita-dark-furios.css b/src/stylesheet/adwaita-dark-furios.css new file mode 100644 index 000000000..39d7bd8e6 --- /dev/null +++ b/src/stylesheet/adwaita-dark-furios.css @@ -0,0 +1,56 @@ +/* Adwaita dark theme variant, as frosted glass. + * + * A whole stylesheet rather than an overlay on adwaita-dark.css: phosh's own + * sheets are left exactly as they are, and this one is loaded in their place + * when the glass-theme gsetting is on. */ + +/* phosh_fg_color is regenerated by style-manager from glass-text-color and + glass-text-opacity; this base is what it builds on and the fallback when + the generator does not run. */ +@define-color phosh_fg_base_color white; +@define-color phosh_fg_color @phosh_fg_base_color; +@define-color phosh_bg_color #16161a; + +/* Drop shadow behind shell text, regenerated by style-manager from + glass-text-shadow-color and glass-text-shadow. Opposite the text: dark + under this variant's light text, so the shadow gives the glyph an edge + instead of blooming out of one. */ +@define-color phosh_text_shadow_color alpha(black, .6); + +@define-color phosh_borders_color alpha(@phosh_fg_color,.1); + +/* Glass surfaces carry a wash of the user's accent colour, so the panel, + the drawer, the lock screen and the overview all follow whatever is + picked in Settings > Appearance. + + These are FALLBACKS. style-manager regenerates all of them from + io.furios.phosh.shell -- glass-opacity, glass-lightness, glass-accent-wash + -- at a higher provider priority, and these values are what the defaults + there reproduce. They are what a non-Adwaita GTK theme gets, since the + generator only runs for Adwaita and adw-gtk3. */ +@define-color phosh_glass_bg_color mix(@phosh_bg_color, @theme_selected_bg_color, .2); + +@define-color phosh_glass_sheet_color alpha(@phosh_glass_bg_color, .45); +@define-color phosh_glass_tint_color alpha(@phosh_glass_bg_color, .25); +@define-color phosh_glass_lock_color alpha(@phosh_glass_bg_color, .5); +@define-color phosh_glass_field_color alpha(@phosh_glass_bg_color, .6); +@define-color phosh_glass_button_color alpha(@phosh_glass_bg_color, .4); + +/* Card surfaces: notifications, media player, and the lock screen plugins + (calendar, ticket box, upcoming events, ...). Accent at the same strength + as the lock screen keypad discs, so cards and keys read as one material. */ +@define-color phosh_notification_bg_color alpha(@theme_selected_bg_color, .75); +@define-color phosh_action_bg_color #47474e; +@define-color phosh_activity_bg_color alpha(#26262c,.7); +@define-color phosh_splash_bg_color #f6f5f4; +@define-color phosh_splash_fg_color #282828; + +/* Button colors */ +@define-color phosh_button_bg_color alpha(#787888, .28); +@define-color phosh_button_hover_bg_color alpha(#8a8a9a, .36); +@define-color phosh_button_active_bg_color alpha(#9a9aaa, .45); + +@define-color phosh_emergency_button_bg_color #e01b24; +@define-color phosh_emergency_button_fg_color #ffffff; + +@import url("resource:///mobi/phosh/stylesheet/common-furios.css"); diff --git a/src/stylesheet/common-furios.css b/src/stylesheet/common-furios.css new file mode 100644 index 000000000..31d09e5bf --- /dev/null +++ b/src/stylesheet/common-furios.css @@ -0,0 +1,1527 @@ +/* Shared shell geometry and glass surfaces, imported by the -furios theme + * variants. + * + * A whole stylesheet rather than an overlay on common.css: phosh's own sheets + * are left exactly as they are, and this one is imported in their place when + * the glass-theme gsetting is on. */ + +/* + * Top panel and bar + */ +phosh-top-panel { + font-size: 15px; + background: none; + color: @phosh_fg_color; +} + +/* Single glass sheet behind the unfolded panel / settings area. + Kept light since the compositor blurs the content behind it */ +phosh-top-panel-bg { + background: @phosh_glass_sheet_color; +} + +#top-bar-bin { + transition: background-color 200ms ease; +} + +#top-bar-bin:not(.p-solid) #top-bar label { + /* Kept tight: this text is small and already sits on the panel's glass */ + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +#top-bar-bin:not(.p-solid) #top-bar image { + -gtk-icon-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +#top-bar { + padding-top: 4px; + padding-bottom: 4px; +} + +.phosh-wwan-indicator, +phosh-top-panel .indicators { + font-size: 13px; + font-weight: 800; + font-feature-settings: "tnum"; +} + +.phosh-topbar-clock { + font-weight: bold; + font-feature-settings: "tnum"; +} + +phosh-top-panel .phosh-topbar-clock { + font-size: 16px; +} + +/* When moved to the left because of a cutout */ +phosh-top-panel .phosh-topbar-clock.left { + padding-right: 6px; +} + +/* When moved to the right because of a cutout */ +phosh-top-panel .phosh-topbar-clock.right { + padding-left: 6px; +} + +phosh-top-panel .phosh-topbar-date { + font-size: 14px; +} + +.phosh-clock-box { + padding: 16px; +} + +.phosh-topbar-button { + padding: 0 12px; + border-radius: 99px; +} + +/* + * Settings menu + */ + +/* Settings surfaces are a light tint only — phosh-top-panel-bg carries the + glass, so keep these low-alpha to avoid stacking into a solid color */ +widget.phosh-settings-menu > scrolledwindow { + background-color: @phosh_glass_tint_color; + border-radius: 24px; + margin-left: 6px; + margin-right: 6px; + box-shadow: 0 4px 24px rgba(0,0,0,.3), + inset 0 1px 0 alpha(@phosh_fg_color,.06), + inset 0 0 0 1px alpha(@phosh_fg_color,.04); +} + +.phosh-settings-menu { + background-color: @phosh_glass_tint_color; + border-bottom-right-radius: 24px; + border-bottom-left-radius: 24px; + box-shadow: 0 12px 32px rgba(0,0,0,.35); +} + +.phosh-settings-menu button.circular:not(:hover):not(:active) { + border: 2px solid @theme_bg_color; +} + +.phosh-settings-menu > scrolledwindow > viewport { + padding: 0 12px; +} + +.phosh-settings-menu separator { + background: @phosh_borders_color; +} + +phosh-quick-setting { + border-radius: 99px; + border: 0; + font-weight: bold; + font-size: 10pt; + background-color: @phosh_button_bg_color; + box-shadow: inset 0 1px 0 alpha(@phosh_fg_color,.08), + inset 0 0 0 1px alpha(@phosh_fg_color,.06); +} + +phosh-quick-setting button:hover { + background-color: shade(@phosh_button_bg_color, 1.14); +} + +phosh-quick-setting:checked button:hover { + background-color: shade(@theme_selected_bg_color, 1.14); + color: @theme_selected_fg_color; +} + +/* The colour has to be repeated on the children: the GTK theme sets a colour + on `button` itself, which beats inheriting from the widget above, so a + checked quick setting kept the theme's plain foreground -- black on light, + white on dark -- whatever the accent text colour was set to. */ +phosh-quick-setting:checked, +phosh-quick-setting:checked button, +phosh-quick-setting:checked button label, +phosh-quick-setting:checked button image, +phosh-quick-setting:checked label, +phosh-quick-setting:checked image { + background-color: @theme_selected_bg_color; + color: @theme_selected_fg_color; +} + +phosh-quick-setting:checked button, +phosh-quick-setting:checked button label, +phosh-quick-setting:checked button image { + background-color: transparent; +} + +phosh-quick-setting:disabled { + opacity: 0.5; + background-color: @phosh_button_bg_color; +} + +phosh-quick-setting button:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + outline: none; +} + +phosh-quick-setting:checked button:focus { + box-shadow: inset 0 0 0 2px alpha(@theme_fg_color,.3); + outline: none; +} + +phosh-quick-setting button:first-child { + border-radius: 99px 0px 0px 99px; +} + +phosh-quick-setting button:only-child { + border-radius: 99px; +} + +phosh-quick-setting button:last-child:not(:only-child) { + border-radius: 0px 99px 99px 0px; +} + +phosh-quick-setting button { + background: none; + background-color: transparent; + border: none; + box-shadow: none; + -gtk-icon-shadow: none; + padding: 0; + text-shadow: none; + padding: 8px 12px; +} + +phosh-brightness-settings scale { + padding-right: 4px; +} + +/* + * Volume slider in settings + */ +phosh-channel-bar image { + -gtk-icon-style: symbolic; +} + +phosh-channel-bar scale { + padding-right: 4px; +} + +/* Toggle buttions to open details in settings */ +button.phosh-settings-details, +button.phosh-settings-details:focus { + background: none; +} + +/* Listboxes in settings (e.g. audio details) */ +.phosh-settings-list-box { + border-radius: 16px; + padding: 12px; + border: none; + background-color: @phosh_notification_bg_color; + box-shadow: 0 2px 12px rgba(0,0,0,.2), + inset 0 1px 0 alpha(@phosh_fg_color,.06), + inset 0 0 0 1px alpha(@phosh_fg_color,.04); +} + +.phosh-settings-list-box list { + background-color: transparent; +} + +.phosh-settings-list-box list row { + border-radius: 8px; +} + +.phosh-settings-list-box list row:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + background: mix(@phosh_button_bg_color, @theme_selected_bg_color, 0.1); + outline-style: none; + transition: none; +} + +.phosh-settings-list-box list row image { + -gtk-icon-style: symbolic; +} + +/* Status pages of quick settings (e.g. audio wifi or bt status pages) */ +.phosh-status-page { + border-radius: 16px; + padding: 12px 12px 6px; + border: none; + background-color: @phosh_notification_bg_color; + box-shadow: 0 2px 12px rgba(0,0,0,.2), + inset 0 1px 0 alpha(@phosh_fg_color,.06), + inset 0 0 0 1px alpha(@phosh_fg_color,.04); +} + +.phosh-status-page list row:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + background: mix(@phosh_button_bg_color, @theme_selected_bg_color, 0.1); + outline-style: none; + transition: none; +} + +.phosh-status-page list row image { + -gtk-icon-style: symbolic; +} + +phosh-status-page-placeholder > box > label.title { + font-weight: 400; + font-size: 16px; +} + +/* Empty states -- "No Notifications" and its bell, and the same placeholder + reused by the wifi and bluetooth status pages. Both halves were greyed by + the widget rather than by a colour we control: the icon carries .dim-label, + and the title is built with sensitive=0, so Adwaita paints it with + insensitive_fg_color. Neither follows the chosen text colour, and both fade + via opacity, which is applied after the colour resolves. State the colour + and clear the fade so an empty state is simply quiet text in the right + colour. */ +phosh-status-page-placeholder > box > label.title, +phosh-status-page-placeholder > box > label.title:disabled, +phosh-status-page-placeholder > box > image.icon, +phosh-status-page-placeholder .dim-label { + color: @phosh_fg_color; + opacity: 1; +} + +/* The drawer's own empty state is a different widget from the placeholder + above -- a plain box in settings.ui with the .phosh-notifications-empty + class -- and it stacks all three dimming mechanisms: dim-label on the box, + dim-label plus sensitive=0 on the bell, and sensitive=0 on the label. */ +.phosh-notifications-empty, +.phosh-notifications-empty label, +.phosh-notifications-empty label:disabled, +.phosh-notifications-empty image, +.phosh-notifications-empty image:disabled { + color: @phosh_fg_color; + opacity: 1; +} + +/* + * Audio devices listbox + */ +.phosh-gear-button { + border-radius: 99px; +} + +/* + * System menu + */ +#system-menu button { + padding: 8px 16px; + border-radius: 99px; + font-weight: bold; + font-size: 90%; +} + +/* + * Settings gear button + */ +#settings-gear { + background: none; +} + + +/* + * phosh-home + */ + +#powerbar { + background: -gtk-recolor(url('resource:///mobi/phosh/icons/scalable/status/input-powerbar-symbolic.svg')); + background-position: center; + background-size: 150px 15px; + background-repeat: no-repeat; + transition: 400ms ease; +} + +.p-active #powerbar { + opacity: 0.5; +} + +.p-failed #powerbar { + background-size: 150px 15px; + transition: 300ms ease; + animation: error-shake 1.5s ease; + animation-iteration-count: 1; +} + +@keyframes error-shake { + 0% {background-position: center; opacity: .5;} + 10% {background-position: center; opacity: .7;} + 15% {background-position: left; opacity: 1;} + 30% {background-position: right;} + 45% {background-position: left;} + 60% {background-position: right;} + 80% {background-position: center;} + 100% {background-position: center;} +} + +/* + * Overview (app grid with favorites and activities) + */ + +phosh-activity > widget > button { + background: none; + transition: none; + border: none; + border-radius: 6px; + color: inherit; + /* don't add padding here - it will break PhoshActivity's sizing */ + /* use GtkDrawingArea's margin instead */ + padding: 0px; +} + +phosh-activity > widget > button:hover { + background: none; +} + +phosh-activity > widget > button:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + outline: none; +} + +phosh-activity box button { + background: @phosh_bg_color; + border-radius: 0; + /* box-shadow: 0 2px 2px rgba(0,0,0,0.4), 0 3px 16px rgba(0,0,0,0.3); */ +} + +phosh-activity.phosh-empty.light { + color: @phosh_splash_fg_color; +} + +phosh-activity.phosh-empty.light .phosh-drawingarea { + color: @phosh_splash_fg_color; + background-color: @phosh_splash_bg_color; +} + +phosh-activity.phosh-empty .phosh-drawingarea { + border-radius: 12px; + background-color: @theme_bg_color; +} + +phosh-activity.phosh-maximized .phosh-drawingarea { + border-radius: 12px; + background-color: @phosh_activity_bg_color; +} + +phosh-home { + background: none; + color: @phosh_fg_color; +} + +.phosh-activity-close-button { + min-width: 32px; + min-height: 32px; + padding: 8px; + margin: 10px; + border-radius: 50%; +} + +.phosh-overview { + background: none; + color: @phosh_fg_color; +} + +.phosh-favorite { + background: none; + border: none; + padding: 0; +} + +.phosh-favorite:hover { + -gtk-icon-effect: none; +} + +.phosh-favorite:active { + -gtk-icon-effect: highlight; +} + +/* Search Bar */ +.phosh-search-bar-box { + margin: 6px 16px; +} + +.phosh-search-bar { + border-radius: 9999px; + padding: 3px 18px 3px 14px; + border: 1px solid transparent; + background-color: @phosh_glass_field_color; + color: inherit; +} + +.phosh-search-bar > image { + color: inherit; + opacity: 0.7; +} + +.phosh-search-bar:focus { + box-shadow: inset 0 0 0 1px @theme_selected_bg_color; + border: 1px solid @theme_selected_bg_color; +} + +phosh-app-grid { + background: none; + color: @phosh_fg_color; +} + +phosh-app-grid separator { + background: @phosh_borders_color; + min-height: 2px; + border-radius: 1px; + margin: 0 12px; +} + +phosh-app-grid-button button { + font-size: 0.8rem; + border-radius: 12px; + margin: 0; + padding: 0; + background: none; +} + +phosh-home:not(.p-solid) phosh-app-grid label { + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +phosh-app-grid-button button:active, +phosh-app-grid-button button:hover:active, +phosh-app-grid-button button:focus { + background: @phosh_glass_button_color; +} + +phosh-app-grid-folder-button button { + font-size: 0.8rem; + border-radius: 12px; + margin: 0; + padding: 0; + background: @phosh_glass_button_color; +} + +phosh-app-grid-folder-button button:active, +phosh-app-grid-folder-button button:hover, +phosh-app-grid-folder-button button:hover:active, +phosh-app-grid-folder-button button:focus { + background: @phosh_glass_field_color; +} + +.search-active phosh-app-grid-button:first-child button { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + background: @phosh_glass_button_color; +} + +#phosh-filter-adaptive-btn { + border-radius: 9999px; + padding: 6px 24px; + margin: 24px 0; +} + +.phosh-app-grid-folder-page-title-box { + margin: 6px 16px; +} + +/* + * Lock screen + */ + +/* The compositor blurs the wallpaper drawn by phosh-lockscreen-bg underneath; + this sheet sits on that blur and is what keeps the clock legible over a + bright or busy wallpaper. Lower the alpha for more wallpaper, raise it for + more contrast. Without compositor blur it simply dims the wallpaper. */ +phosh-lockscreen { + background-color: @phosh_glass_lock_color; + color: @phosh_fg_color; +} + +/* lockscreen-tint off, on the clock page: no sheet at all, so the wallpaper + is shown exactly as it is. */ +phosh-lockscreen.p-untinted { + background-color: transparent; +} + +phosh-lockscreen-bg { + background-color: @phosh_glass_bg_color; + background-image: linear-gradient(to bottom, + shade(@phosh_glass_bg_color, 1.7), + @phosh_glass_bg_color 45%, + shade(@phosh_glass_bg_color, 0.4)); + color: @phosh_fg_color; +} + +.phosh-lockshield { + background-color: @phosh_bg_color; + color: @phosh_fg_color; +} + +/* Clock */ +#phosh-lockscreen-clock { + font-weight: 300; + font-size: 84px; + font-feature-settings: "tnum"; + letter-spacing: -2px; + margin: 12px 0; + transition: 200ms ease; + padding-top: 40px; + color: @phosh_fg_color; + /* Equal offsets on both axes, so the light reads as coming from one corner + rather than from straight above, and a blur no larger than the offset so + it stays a shadow rather than a halo. Scaled up from the body rule only + because the glyph is 84px. */ + text-shadow: 2px 2px 2px @phosh_text_shadow_color; +} + +#phosh-lockscreen-date { + font-weight: 500; + font-size: 18px; + color: alpha(@phosh_fg_color, .85); + margin: 6px 0; + transition: 200ms ease; + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +.p-small #phosh-lockscreen-clock { + padding-top: 0px; + font-size: 72px; + transition: 200ms ease; +} +.p-small #phosh-lockscreen-date { + font-size: 16px; + margin-top: 0; + transition: 200ms ease; +} + +/* Lockscreen Arrow. Reads as part of the "Slide up to unlock" label directly + beneath it, so it takes the same colour and the same halo -- an accent + coloured glyph over white text looked like one of the two was a mistake. */ +.phosh-lockscreen-arrow { + color: @phosh_fg_color; + -gtk-icon-shadow: 1px 1px 1px @phosh_text_shadow_color; + transition: 400ms linear; + animation: pulsate 1.8s ease-out; + animation-iteration-count: 15; +} + +/* "Slide up to unlock" */ +.phosh-lockscreen-arrow + label { + color: @phosh_fg_color; + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +.phosh-lockscreen-status-icons { + padding: 6px; +} + +.phosh-lockscreen-pin, +.phosh-lockscreen-pin:disabled, +.phosh-lockscreen-pin:focus { + font-size: 20px; + padding: 6px; + border-radius: 12px; + color: @phosh_fg_color; + border: none; + box-shadow:none; + outline: none; + caret-color: transparent; + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +/* "Enter Passcode", and the "Checking…" / wrong-passcode status it is reused for */ +.phosh-lockscreen-unlocker > label { + color: @phosh_fg_color; + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +@keyframes pulsate { + 0% {-gtk-icon-transform: translateY(0); opacity: 0.7;} + 35% {-gtk-icon-transform: translateY(-14px); opacity: 1;} + 100% {-gtk-icon-transform: translateY(0); opacity: 0.7;} +} + +/* Lockscreen keypad */ +phosh-keypad label.digit { + font-size: 200%; + font-weight: bold; + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +/* Accent discs, translucent enough to let the backdrop through; a press takes + them to the full accent colour. Digits ride on @phosh_fg_color, which is + white on dark and near-black on light -- the disc is too pale on the light + variant for white to hold up. */ +phosh-keypad button { + border-radius: 9999px; + font-size: 16px; + font-weight: bold; + padding: 16px; + color: @phosh_fg_color; + background: alpha(@theme_selected_bg_color, 0.75); + box-shadow: inset 0 1px 0 alpha(#ffffff,.14), + inset 0 0 0 1px alpha(#ffffff,.09); +} + +phosh-keypad image { + -gtk-icon-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +phosh-keypad button:active label, +phosh-keypad button:active image { + text-shadow: none; + -gtk-icon-shadow: none; +} + +/* :active only, deliberately. On a touchscreen there is no pointer to leave a + widget, so GTK sets :hover on tap and nothing ever clears it, and :focus + outlives the press entirely. Styling either the same as the press left the + last key tapped latched at full accent, looking held down until some other + key was pressed. */ +phosh-keypad button:active { + background: @theme_selected_bg_color; +} + +/* Keyboard focus still needs to be visible, as a ring rather than a fill so + it cannot be mistaken for a pressed key. */ +phosh-keypad button:focus:not(:active) { + box-shadow: inset 0 1px 0 alpha(#ffffff,.14), + inset 0 0 0 2px alpha(@phosh_fg_color,.35); +} + +/* Hover has to be restated, at a specificity that beats two rules further + down this file: the generic `button:hover`, and an upstream FIXME that sets + `phosh-keypad button:hover { background: none }` to stop the stock flat + keypad double-highlighting. Both outrank the plain `phosh-keypad button` + selector, so together they stripped the disc off a hovered key -- and since + a touchscreen has no pointer to leave the widget, the tapped key stayed + hovered and stayed hollow until another was pressed. That is the key that + looked stuck. Hover simply keeps the resting disc: on a touch device there + is no meaningful hover to show. */ +phosh-keypad button:hover:not(:active) { + background: alpha(@theme_selected_bg_color, 0.75); +} + +/* Unlock, given the keypad disc treatment so it reads as part of the keypad. + The colour is repeated on the label child for the same reason the checked + quick setting needs it: the GTK theme sets a colour on the button's own + nodes, which beats inheriting the one set here. */ +phosh-lockscreen .text-only-button, +phosh-lockscreen .text-only-button label { + color: @phosh_fg_color; + /* Same shadow as a keypad digit, so the two read as one control group. This + was briefly dropped because a white shadow on a saturated disc looked like + a glow; the shadow is dark in both schemes now, which reads as depth. */ + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +phosh-lockscreen .text-only-button { + font-size: 16px; + font-weight: bold; + padding: 16px 36px; + background: alpha(@theme_selected_bg_color, .75); + box-shadow: inset 0 1px 0 alpha(#ffffff,.14), + inset 0 0 0 1px alpha(#ffffff,.09); +} + +/* btn_submit ships insensitive and only wakes up once a digit is entered, so + disabled is the state normally on screen. Adwaita would grey it with + insensitive_fg_color, a mix toward the background that throws away the + chosen text colour entirely. Fading the label instead was the next attempt + and it was still wrong: any fade at all is read as the button ignoring the + colour setting, because that is indistinguishable from what Adwaita was + doing. So the label keeps its colour at full strength and the disc alone + carries the disabled state, which is the part of the button whose colour + nobody chose. */ +phosh-lockscreen .text-only-button:disabled, +phosh-lockscreen .text-only-button:disabled label { + color: @phosh_fg_color; +} + +phosh-lockscreen .text-only-button:disabled { + /* The generic `button:disabled` a few hundred lines down sets opacity 0.5, + which fades the whole widget after its colour is resolved and so survived + every attempt to fix this by setting a colour. That is what kept the + Unlock label washed out. */ + opacity: 1; + background: alpha(@theme_selected_bg_color, .75); + box-shadow: inset 0 1px 0 alpha(#ffffff,.14), + inset 0 0 0 1px alpha(#ffffff,.09); +} + +/* :active only, same touchscreen reason as the keypad */ +phosh-lockscreen .text-only-button:active { + background: @theme_selected_bg_color; +} + +phosh-lockscreen .text-only-button:focus:not(:active):not(:disabled) { + box-shadow: inset 0 1px 0 alpha(#ffffff,.14), + inset 0 0 0 2px alpha(@phosh_fg_color,.35); +} + +/* In-call display on the lock screen: glass over the blur like everything + else there, rather than an opaque neutral bar */ +phosh-lockscreen cui-call-display actionbar { + background-color: @phosh_glass_lock_color; +} + +phosh-lockscreen cui-call-display label { + text-shadow: 1px 1px 1px @phosh_text_shadow_color; +} + +/* Lock screen cards: calendar, ticket box, upcoming events. + These lean on .dim-label and GtkCalendar's own dimmed nodes for hierarchy, + both of which fade text with opacity rather than colour. Opacity is applied + after the colour is resolved, so it survives every setting we have and made + the day names, the "No events" placeholder and the event rows look washed + out no matter what text colour was chosen. Take the fade off and let the + chosen colour through at full strength. */ +phosh-lockscreen .dim-label, +phosh-lockscreen calendar.highlight, +phosh-lockscreen calendar, +phosh-lockscreen calendar label { + opacity: 1; + color: @phosh_fg_color; +} + +/* Days outside the month shown stay quieter -- that is real information, not + decoration -- but as a tint of the chosen colour rather than a fade. */ +phosh-lockscreen calendar:disabled { + color: alpha(@phosh_fg_color, .45); +} + +/* The cards' symbolic icons, most visibly the ticket box's document glyph. + Symbolic icons are painted in the current foreground, so they should have + followed already -- but the row widgets they sit in dim them by their own + means, which is how a black icon came out grey. State it here. */ +phosh-lockscreen phosh-ticket-box image, +phosh-lockscreen phosh-ticket-row image, +phosh-lockscreen phosh-calendar image, +phosh-lockscreen phosh-upcoming-events image, +phosh-lockscreen phosh-launcher-box image, +phosh-lockscreen row image { + color: @phosh_fg_color; + opacity: 1; +} + +phosh-lockscreen .phosh-notification-tray { + margin: 0 6px; + transition: 200ms ease; +} + +phosh-lockscreen phosh-media-player { + margin: 0 8px; +} + +phosh-lockscreen .phosh-notification-tray separator { + background: @phosh_borders_color; +} + +phosh-lockscreen .phosh-notification-tray row:focus { + border-radius: 14px; + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + outline: none; +} + +/* Widget box */ +phosh-lockscreen phosh-widget-box { + margin-left: 6px; + margin-right: 6px; +} + +/* The back button on the widget page, given the keypad disc treatment so it + belongs to the cards it sits above */ +phosh-lockscreen button.circular.image-button { + color: @phosh_fg_color; + background: alpha(@theme_selected_bg_color, .75); + box-shadow: inset 0 1px 0 alpha(#ffffff,.14), + inset 0 0 0 1px alpha(#ffffff,.09); +} + +phosh-lockscreen button.circular.image-button:hover, +phosh-lockscreen button.circular.image-button:active, +phosh-lockscreen button.circular.image-button:focus { + background: @theme_selected_bg_color; +} + +/* + * System modal dialogs (polkit, gcr, ...) + */ + +.phosh-system-modal { + background-color: alpha(@phosh_bg_color, 0.8); +} + +.phosh-system-modal-dialog { + background-color: alpha(@theme_bg_color, .96); + border: 1px solid @phosh_borders_color; + border-radius: 16px; + box-shadow: 0 4px 16px 0 rgba(0,0,0,.3); + padding: 0px; + padding-top: 18px; + margin: 12px; +} + +.phosh-system-modal-dialog-buttons label { + margin: 12px; +} + +.phosh-system-modal-dialog-buttons button { + padding: 0; + margin: 0; + border: 1px solid @phosh_borders_color; + border-left-width: 0; + border-bottom-width: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.phosh-system-modal-dialog-buttons button:first-child { + border-bottom-left-radius: 15px; + border-bottom-right-radius: 0; +} + +.phosh-system-modal-dialog-buttons button:last-child { + border-bottom-left-radius: 0; + border-bottom-right-radius: 15px; + border-right: none; +} + +.phosh-system-modal-dialog-buttons button:only-child { + border-bottom-left-radius: 15px; + border-bottom-right-radius: 15px; +} + +.phosh-system-modal-dialog-content { + padding-left: 18px; + padding-right: 18px; +} + +.phosh-system-modal-dialog-title { + font-weight: 800; + font-size: 120%; +} + +.phosh-end-session-dialog .phosh-end-session-subtitle { + font-feature-settings: "tnum"; +} + +.phosh-end-session-dialog .phosh-end-session-warning { + color: #ba5645; + font-weight: bold; +} + +.phosh-end-session-dialog list { + border-radius: 12px; +} + +.phosh-end-session-dialog list row { + margin-left: 6px; + margin-right: 6px; +} + +.phosh-end-session-dialog list row:first-child { + margin-top: 6px; +} + +.phosh-end-session-dialog list row:last-child { + margin-bottom: 6px; +} + +.phosh-end-session-dialog list row box box.vertical label:first-child { + font-weight: bold; +} + +/* Don't dim list of inhibited applications */ +.phosh-end-session-dialog list * { + color: @theme_text_color; + -gtk-icon-effect: none; +} + +phosh-gtk-mount-prompt .phosh-system-modal-dialog-content > image { + -gtk-icon-style: symbolic; +} + +/* + * Notifications + */ + +phosh-notification-content { + background: transparent; + color: @phosh_fg_color; + padding: 0; + margin: 0; +} + +phosh-notification-content .message-area { + padding: 12px 12px 9px 12px; +} + +.phosh-notification-body:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + outline-style: none; +} + +phosh-notification-content .message-area image { + border-radius: 50%; +} + +/* Action Area (buttons) */ +phosh-notification-content .actions-area { + padding: 0; + border: none; +} + +phosh-notification-content .actions-area button { + background: @phosh_action_bg_color; + border: 1px solid @phosh_borders_color; + border-radius: 0; + font-weight: bold; + padding: 8px 12px; + box-shadow: inset -1px 0 0 0 @phosh_borders_color; +} + +phosh-notification-content .actions-area button:only-child { + border-bottom-left-radius: 14px; + border-bottom-right-radius: 14px; + box-shadow: none; +} + +phosh-notification-content .actions-area button:first-child { + border-bottom-left-radius: 14px; +} +phosh-notification-content .actions-area button:last-child { + border-bottom-right-radius: 14px; + box-shadow: none; +} + +phosh-notification-content .actions-area button:hover { + background-color: rgba(255,255,255,.07); +} + +phosh-notification-content .actions-area button:active { + background-color: rgba(255,255,255,.14); +} + +phosh-notification-content .actions-area button:focus, +phosh-notification-content .actions-area button:only-child:focus, +phosh-notification-content .actions-area button:last-child:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + background-color: alpha(@theme_selected_bg_color, 0.2); +} + +phosh-notification-frame { + margin: 0; + padding: 0; + color: @phosh_fg_color; +} + +phosh-notification-frame list { + background: transparent; +} + +phosh-notification-frame .header-area { + padding: 12px 12px 0 12px; +} + +phosh-notification-frame .header-area image { + -gtk-icon-style: symbolic; +} + +phosh-notification-frame .notification-container { + border-radius: 16px; + border: none; + background-color: @phosh_notification_bg_color; + box-shadow: 0 2px 12px rgba(0,0,0,.25), + inset 0 1px 0 alpha(@phosh_fg_color,.07), + inset 0 0 0 1px alpha(@phosh_fg_color,.05); +} + +/* Banner */ +phosh-notification-banner { + background: none; + border: none; + color: inherit; +} + +phosh-notification-banner > phosh-notification-frame { + padding: 0; +} + +phosh-notification-banner .notification-container { + margin: 6px; + box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.33), + 0px 2px 4px 2px rgba(0, 0, 0, 0.07), + inset 0 1px 0 alpha(@phosh_fg_color,.07), + inset 0 0 0 1px alpha(@phosh_fg_color,.05); +} + +/* Notification Tray */ +.phosh-notification-tray { + padding-bottom: 12px; +} + +.phosh-notification-tray, +.phosh-notification-tray list { + background: transparent; +} + +.notification-container *:focus, +.phosh-notification-tray list row:focus { + outline-style: none; +} + +.phosh-notification-tray list row .notification-container { + margin-top: 12px; +} + +.phosh-notifications-header { + padding:0; +} + +/* Clear notifications button */ +#phosh-notifications-clear-all-btn { + border-radius: 99px; +} + +/* Call notifications */ +phosh-call-notification { + padding: 12px; + color: @phosh_fg_color; + border-radius: 16px; + border: none; + background-color: @phosh_notification_bg_color; + box-shadow: 0 2px 12px rgba(0,0,0,.25), + inset 0 1px 0 alpha(@phosh_fg_color,.07), + inset 0 0 0 1px alpha(@phosh_fg_color,.05); +} + +/* + * Output fader + */ + +@keyframes phosh-fader-default-keyframe { + from {background:rgba(0, 0, 0, 0);} + to {background:rgba(0, 0, 0, 1);} +} + +.phosh-fader-default-fade { + animation-name: phosh-fader-default-keyframe; + animation-duration: 2s; + animation-timing-function: linear; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +/* Fader used for proximity dimming */ +.phosh-fader-proximity-fade { + animation-name: phosh-fader-default-keyframe; + animation-duration: 200ms; + animation-timing-function: linear; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +/* Fader used for screenshot flash */ +@keyframes phosh-fader-flash-keyframe { + from {background:rgba(255, 255, 255, 1);} + to {background:rgba(255, 255, 255, 0);} +} + +.phosh-fader-flash-fade { + animation-name: phosh-fader-flash-keyframe; + animation-duration: 500ms; + animation-timing-function: linear; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +.phosh-fader-screenshot-opaque { + background: rgba(255, 255, 255, 0); +} + +/* Theme change fader */ +@keyframes phosh-fader-theme-to-hc-keyframe { + from {background:rgba(0, 0, 0, 0);} + to {background:rgba(255, 255, 255, 1.0);} +} + +.phosh-fader-theme-to-hc { + animation-name: phosh-fader-theme-to-hc-keyframe; + animation-duration: 100ms; + animation-timing-function: ease-out; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +@keyframes phosh-fader-theme-from-hc-keyframe { + from {background:rgba(255, 255, 255, 0);} + to {background:rgba(0, 0, 0, 1.0);} +} + +.phosh-fader-theme-from-hc { + animation-name: phosh-fader-theme-from-hc-keyframe; + animation-duration: 100ms; + animation-timing-function: ease-out; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +/* + * Media player + */ + +phosh-media-player { + background: @phosh_notification_bg_color; + color: @phosh_fg_color; + border-radius: 16px; + border:none; + box-shadow: 0 2px 12px rgba(0,0,0,.25), + inset 0 1px 0 alpha(@phosh_fg_color,.07), + inset 0 0 0 1px alpha(@phosh_fg_color,.05); + padding: 6px; + padding-bottom: 0; +} + +phosh-media-player button { + background: none; +} + +phosh-media-player button:focus { + background-color: alpha(@theme_selected_bg_color, .2); +} + +phosh-media-player button:hover { + background-color: rgba(255,255,255,.1); +} + +phosh-media-player button:active { + background-color: rgba(255,255,255,.2); +} + +phosh-media-player .media-player-details { + border-radius: 8px; + padding-left: 4px; +} + +/* The details button should not look inactive even when disabled */ +phosh-media-player .media-player-details:disabled { + color: @theme_fg_color; + opacity: 1.0; + -gtk-icon-effect: none; +} + +phosh-media-player .phosh-media-player-art { + margin: 0; + padding: 0; + border-radius: 6px; +} + +phosh-media-player .progress-bar { + font-size: small; +} + +phosh-media-player > box > button { + border-radius: 9999px; + margin: 2px; + padding: 8px; +} + +/* + * OSD + */ + +#osd-bubble { + border-radius: 32px; + box-shadow: 0 2px 2px rgba(0,0,0,0.2), + 0 3px 16px rgba(0,0,0,0.1), + inset 0 1px 0 alpha(@phosh_fg_color,.08), + inset 0 0 0 1px alpha(@phosh_fg_color,.05); + background-color: shade(@phosh_notification_bg_color, 1.5); + color: @phosh_fg_color; + text-shadow: none; + -gtk-icon-shadow: none; +} + +#osd-label { + font-size: 9pt; +} + +phosh-osd-window.phosh-system-modal { + background: none; +} + +phosh-osd-window levelbar block { + min-height:4px; + box-shadow: none; + border: none; + border-radius: 2px; +} + +phosh-osd-window levelbar block.low { + background-color: @phosh_fg_color; +} + +phosh-osd-window levelbar block.high { + background-color: @phosh_fg_color; +} + +phosh-osd-window levelbar block.full { + background-color: @phosh_fg_color; +} + +phosh-osd-window levelbar trough { + border: none; + padding: 0; + box-shadow: none; + background-color: alpha(@phosh_fg_color, 0.2); + border-radius: 2px; +} + +/* + * Splash + */ + +phosh-splash.light { + color: @phosh_splash_fg_color; + background-color: @phosh_splash_bg_color; +} + +phosh-splash.dark { + /* phosh prefers dark, nothing to do */ +} + +.phosh-dropicon-shadow { + -gtk-icon-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 8px rgba(0, 0, 0, 0.2); +} + +phosh-splash spinner { + -gtk-icon-source: -gtk-icontheme('splash-process-working-symbolic'); +} + +/* + * Power button menu + */ + +/* Special style for the dialog */ +phosh-power-menu .phosh-system-modal-dialog { + border-radius:32px; + padding-bottom: 4px; + margin: 24px; +} + +phosh-power-menu grid button { + min-height: 80px; + min-width: 64px; + border-radius: 24px; + font-weight: bold; +} + +phosh-power-menu button image { + opacity: 0.8; +} + +/* Some hardcoded positioning, which is not ideal */ +phosh-power-menu grid { + margin-top: -16px; +} + +phosh-power-menu #close_button { + margin-top: -36px; + margin-right: -36px; +} + +phosh-power-menu #close_button button { + border-radius: 99px; + padding: 8px; +} + +phosh-power-menu .phosh-dialog-close-button { + box-shadow: 0 2px 4px 0 rgba(0,0,0,.2); +} + +/* Emergency button in the power menu */ +.emergency-button { + background: @phosh_emergency_button_bg_color; + color: @phosh_emergency_button_fg_color; +} + +.emergency-button:focus, +.emergency-button:focus:hover, +.emergency-button:hover { + background: shade(@phosh_emergency_button_bg_color, 1.2); +} + +.emergency-button:focus { + box-shadow: inset 0 0 2px 0 shade(@phosh_emergency_button_bg_color, 2); +} + +.emergency-button:active { + background: shade(@phosh_emergency_button_bg_color, 1.5); +} + +/* + * Emergency call menu + */ + +phosh-emergency-menu .emergency-button { + font-size: 12px; + padding: 4px 12px; +} + +/* + * Common style classes + */ + +/* Use a solid background, useful when one needs to toggle between transparent and solid */ +.p-solid { + background-color: alpha(@phosh_bg_color, .85); +} + +/* + * Background + */ +phosh-background { + background: @phosh_bg_color; + border-width: 0px; + border-radius: 0px; +} + +/* + * GTK Overrides + */ + +/* Remove this from all elements */ +* { + text-shadow: none; + -gtk-icon-shadow: none; +} + +/* Buttons */ + +button { + background: @phosh_button_bg_color; + border: none; + box-shadow: none; + color: inherit; +} + +button:hover { + background: @phosh_button_hover_bg_color; +} + +button:disabled { + opacity: 0.5; + box-shadow: none; +} + +button:active, +button:hover:active { + background: @phosh_button_active_bg_color; +} + +button:focus { + box-shadow: inset 0 0 0 2px @theme_selected_bg_color; + background: mix(@phosh_button_bg_color, @theme_selected_bg_color, 0.1); + outline-style: none; + transition: none; +} + +button:focus:hover { + background: mix(@phosh_button_hover_bg_color, @theme_selected_bg_color, 0.1); +} + +button:focus:active { + background: mix(@phosh_button_active_bg_color, @theme_selected_bg_color, 0.1); +} + +button.suggested-action { + background-color: @theme_selected_bg_color; + color: @theme_selected_fg_color; +} + +button.suggested-action:focus { + box-shadow: inset 0 0 0 2px alpha(@theme_selected_fg_color, 0.3); +} + +/* FIXME: an override to prevent double highlighting on some buttons */ +phosh-quick-setting button:focus, +phosh-app-grid-button button:hover, +phosh-keypad button:hover, +phosh-media-player button:hover { + background: none; +} +/* End FIXME */ + +row:selected { + background-color: @theme_selected_bg_color; +} + +/* Scrollbars */ +scrollbar { + background-color: transparent; + border: none; +} + +/* Sliders */ +scale trough { + padding: 1px; + border-radius: 2px; + border: none; + background: alpha(@phosh_fg_color, 0.3); + outline-style: solid; + outline-width: 1px; + outline-color: @theme_selected_bg_color; + outline-offset: 2px; +} + +scale trough > highlight { + padding: 2px; + border: none; +} + +scale trough slider { + border-width: 1px; + border-style: solid; + border-color: shade(@phosh_fg_color, 0.9); + background: shade(@phosh_fg_color, 0.9); +} + +scale trough slider:hover { + border-color: @phosh_fg_color; + background: @phosh_fg_color; +} + +scale trough slider:active { + border-color: shade(@phosh_fg_color, 0.9); + background: shade(@phosh_fg_color, 0.9); +} + +scale highlight { + background-color: @theme_selected_bg_color; +} + +/* Switches */ +switch:not(:disabled):checked { + background-color: @theme_selected_bg_color; +} + +/* Entries */ +entry:focus { + box-shadow: inset 0 0 0 1px @theme_selected_bg_color; +} + +/* Progress bars */ +progressbar progress { + background-color: @theme_selected_bg_color; +} + +/* Levelbars */ +levelbar block.filled { + background-color: @theme_selected_bg_color; + border-color: @theme_selected_bg_color; +} + +levelbar trough { + border-width: 0px; +} + +/* Calendar widgets */ +calendar:selected { + background-color: @theme_selected_bg_color; +} From ea9ca076a3b1e7f4a3f820656df9a1ea64270b86 Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 17 Aug 2026 22:09:18 +1000 Subject: [PATCH 07/12] stylesheet: add a light glass variant phosh has no light stylesheet of its own, so this one has no upstream counterpart to leave alone; it follows the -furios naming of the others and is loaded when the colour scheme is light and the glass theme is on. Signed-off-by: wayne --- src/phosh.gresources.xml | 1 + src/stylesheet/adwaita-light-furios.css | 130 ++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/stylesheet/adwaita-light-furios.css diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml index f97ed872a..32fb9d4b9 100644 --- a/src/phosh.gresources.xml +++ b/src/phosh.gresources.xml @@ -53,6 +53,7 @@ stylesheet/adwaita-dark.css stylesheet/adwaita-dark-furios.css stylesheet/adwaita-hc-light.css + stylesheet/adwaita-light-furios.css stylesheet/common.css stylesheet/common-furios.css diff --git a/src/stylesheet/adwaita-light-furios.css b/src/stylesheet/adwaita-light-furios.css new file mode 100644 index 000000000..940ccc02e --- /dev/null +++ b/src/stylesheet/adwaita-light-furios.css @@ -0,0 +1,130 @@ +/* Light theme variant (frosted light glass) */ + +/* Pure black, not the near-black Adwaita uses. The pair this makes with the + dark variant's pure white is deliberate: text and its shadow are opposites + of each other in both schemes, so the shadow always has the full range to + define the glyph with, whatever wallpaper is behind it. Both are settings + (glass-text-color, glass-text-shadow-color) for the wallpaper that defeats + the default. */ +@define-color phosh_fg_base_color black; +@define-color phosh_fg_color @phosh_fg_base_color; +@define-color phosh_bg_color #ececf2; + +/* Drop shadow behind shell text, regenerated by style-manager. Light here, + opposite the black text. The geometry lives in common-furios.css and is shared + with the dark variant -- only this colour differs between the two. */ +@define-color phosh_text_shadow_color alpha(white, .6); + +@define-color phosh_borders_color alpha(@phosh_fg_color,.1); + +/* Accent wash for the glass surfaces; see adwaita-dark.css. Lighter here + because a tint reads much stronger against a near-white base -- which is + why style-manager scales the configured wash down for this variant. + Fallbacks, as in the dark variant. */ +@define-color phosh_glass_bg_color mix(@phosh_bg_color, @theme_selected_bg_color, .12); + +@define-color phosh_glass_sheet_color alpha(@phosh_glass_bg_color, .45); +@define-color phosh_glass_tint_color alpha(@phosh_glass_bg_color, .25); +@define-color phosh_glass_lock_color alpha(@phosh_glass_bg_color, .5); +@define-color phosh_glass_field_color alpha(@phosh_glass_bg_color, .6); +@define-color phosh_glass_button_color alpha(@phosh_glass_bg_color, .4); + +/* Card surfaces at keypad-disc strength; see adwaita-dark.css */ +@define-color phosh_notification_bg_color alpha(@theme_selected_bg_color, .75); +@define-color phosh_action_bg_color #dcdce4; +@define-color phosh_activity_bg_color alpha(#fafafd,.7); +@define-color phosh_splash_bg_color #f6f5f4; +@define-color phosh_splash_fg_color #282828; + +/* Button colors */ +@define-color phosh_button_bg_color alpha(#5a5a6e, .14); +@define-color phosh_button_hover_bg_color alpha(#5a5a6e, .22); +@define-color phosh_button_active_bg_color alpha(#5a5a6e, .32); + +@define-color phosh_emergency_button_bg_color #e01b24; +@define-color phosh_emergency_button_fg_color #ffffff; + +@import url("resource:///mobi/phosh/stylesheet/common-furios.css"); + +/* No shadow rules of our own here on purpose. + * + * The light and the dark variant carry exactly the same drop shadow: the same + * offsets, on the same elements, out of common-furios.css. Only the colour + * differs, and that comes from phosh_text_shadow_color, which style-manager + * sets to the opposite of the text. + * + * Exempting the elements that sit on one of our own pale surfaces is tempting + * -- black text on pale glass needs little lift -- but it gives the two + * schemes shadows on different sets of elements, which reads as the light + * theme being broken rather than as a considered exemption. A white shadow on + * a pale surface is close to invisible anyway. + */ + +/* White-overlay hovers from common-furios.css are invisible on light surfaces */ +phosh-notification-content .actions-area button:hover, +phosh-media-player button:hover { + background-color: rgba(0,0,0,.05); +} + +phosh-notification-content .actions-area button:active, +phosh-media-player button:active { + background-color: rgba(0,0,0,.1); +} + +/* Elevation for a light scheme. + * + * common-furios.css casts every glass surface with a wide, heavily dark + * rgba(0,0,0,.35) under 32px of blur on the drawer, .2-.25 under 12-24px on + * the cards. Against a near-black base those are invisible and do the job of + * separating one dark surface from another. Against a near-white base, and + * over a bright wallpaper, the same 32px black spread around a pale panel is + * a dark halo, so it is overridden here. + * + * Light-scheme elevation is a tight, faint shadow plus an edge you can see: + * the shadow says "above", the hairline says "separate". The inset highlight + * is white here rather than @phosh_fg_color, which in this scheme is a dark + * line along the top edge of every card -- a lowlight where a highlight was + * intended. */ +widget.phosh-settings-menu > scrolledwindow { + box-shadow: 0 1px 3px rgba(0,0,0,.06), + inset 0 1px 0 alpha(#ffffff,.55), + inset 0 0 0 1px alpha(@phosh_fg_color,.06); +} + +.phosh-settings-menu { + box-shadow: 0 2px 6px rgba(0,0,0,.07); +} + +.phosh-settings-list-box, +.phosh-status-page, +phosh-notification-frame .notification-container, +phosh-call-notification, +phosh-media-player { + box-shadow: 0 1px 3px rgba(0,0,0,.07), + inset 0 1px 0 alpha(#ffffff,.55), + inset 0 0 0 1px alpha(@phosh_fg_color,.06); +} + +phosh-notification-banner .notification-container { + box-shadow: 0 1px 3px rgba(0,0,0,.10), + inset 0 1px 0 alpha(#ffffff,.55), + inset 0 0 0 1px alpha(@phosh_fg_color,.06); +} + +#osd-bubble { + box-shadow: 0 1px 4px rgba(0,0,0,.08), + inset 0 1px 0 alpha(#ffffff,.55), + inset 0 0 0 1px alpha(@phosh_fg_color,.06); +} + +.phosh-system-modal-dialog { + box-shadow: 0 2px 8px rgba(0,0,0,.10); +} + +/* Softer bottom edge for the light lockscreen gradient */ +phosh-lockscreen-bg { + background-image: linear-gradient(to bottom, + #ffffff, + @phosh_glass_bg_color 45%, + shade(@phosh_glass_bg_color, 0.85)); +} From 35bd3360e670993771dffb0295a4730f4a42e2ab Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 17 Aug 2026 22:09:42 +1000 Subject: [PATCH 08/12] stylesheet: keep high contrast solid Glass is the opposite of what the high contrast variant is for, so the -furios copy of it overrides the translucent surfaces back to opaque ones. phosh's own adwaita-hc-light.css is left as it is. Signed-off-by: wayne --- src/phosh.gresources.xml | 1 + src/stylesheet/adwaita-hc-light-furios.css | 121 +++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/stylesheet/adwaita-hc-light-furios.css diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml index 32fb9d4b9..f67103ad1 100644 --- a/src/phosh.gresources.xml +++ b/src/phosh.gresources.xml @@ -53,6 +53,7 @@ stylesheet/adwaita-dark.css stylesheet/adwaita-dark-furios.css stylesheet/adwaita-hc-light.css + stylesheet/adwaita-hc-light-furios.css stylesheet/adwaita-light-furios.css stylesheet/common.css stylesheet/common-furios.css diff --git a/src/stylesheet/adwaita-hc-light-furios.css b/src/stylesheet/adwaita-hc-light-furios.css new file mode 100644 index 000000000..d7c55cad2 --- /dev/null +++ b/src/stylesheet/adwaita-hc-light-furios.css @@ -0,0 +1,121 @@ +/* HighContrast theme variant, kept solid. + * + * Loaded in place of adwaita-hc-light.css when the glass theme is on. It + * imports the glass geometry like the other variants, then puts every + * translucent surface back to opaque. */ + +@define-color phosh_fg_base_color black; +@define-color phosh_fg_color @phosh_fg_base_color; +@define-color phosh_bg_color white; + +/* No shadow in high contrast: crisp glyph edges on a solid background are the + whole point of the variant, and a shadow only softens them. */ +@define-color phosh_text_shadow_color transparent; + +@define-color phosh_borders_color alpha(@phosh_fg_color,.1); + +/* High contrast keeps solid, untinted surfaces and plain foreground text. + style-manager never generates the glass colours for this theme, so these + are the values that apply. */ +@define-color phosh_glass_bg_color @phosh_bg_color; + +@define-color phosh_glass_sheet_color @phosh_bg_color; +@define-color phosh_glass_tint_color @phosh_bg_color; +@define-color phosh_glass_lock_color @phosh_bg_color; +@define-color phosh_glass_field_color @phosh_button_bg_color; +@define-color phosh_glass_button_color @phosh_button_bg_color; + +@define-color phosh_activity_bg_color #e5e5e5; +@define-color phosh_notification_bg_color #e0e0e0; +@define-color phosh_action_bg_color #787878; +@define-color phosh_splash_bg_color @theme_bg_color; +@define-color phosh_splash_fg_color @theme_fg_color; + +/* Button Colors */ +@define-color phosh_button_bg_color #e0e0e0; +@define-color phosh_button_hover_bg_color shade(@phosh_button_bg_color, 1.05); +@define-color phosh_button_active_bg_color shade(@phosh_button_bg_color, 1.1); + +@define-color phosh_emergency_button_bg_color #e01b24; +@define-color phosh_emergency_button_fg_color #ffffff; + +@import url("resource:///mobi/phosh/stylesheet/common-furios.css"); + +/* High contrast wants solid surfaces, no glass */ +phosh-top-panel-bg, +.p-solid, +widget.phosh-settings-menu > scrolledwindow, +.phosh-settings-menu { + background-color: @phosh_bg_color; +} + +phosh-lockscreen-bg { + background-image: none; +} + +/* No glass sheet over the lock screen here; the background is already solid */ +phosh-lockscreen { + background-color: transparent; +} + +#top-bar, #home-bar { + box-shadow: inset 0 0 0 1px @phosh_borders_color; +} + +.phosh-quick-setting, +button { + box-shadow: inset 0 0 0 2px @phosh_borders_color; +} + +button:hover { + box-shadow: inset 0 0 0 2px @phosh_fg_color; +} + +button:disabled { + box-shadow: inset 0 0 0 2px alpha(@phosh_borders_color, 0.5); +} + +.emergency-button:hover { + box-shadow: inset 0 0 2px 0 shade(@phosh_emergency_button_bg_color, .6); +} +.emergency-button:focus { + box-shadow: inset 0 0 2px 0 shade(@phosh_emergency_button_bg_color, .4); +} + +#top-bar-bin:not(.p-solid) #top-bar label, +#phosh-lockscreen-clock, +#phosh-lockscreen-date, +.phosh-lockscreen-arrow + label, +.phosh-lockscreen-pin, +.phosh-lockscreen-pin:disabled, +.phosh-lockscreen-pin:focus, +.phosh-lockscreen-unlocker > label, +phosh-keypad label.digit, +phosh-lockscreen cui-call-display label + { + text-shadow: none; +} + +#top-bar-bin:not(.p-solid) #top-bar image, +phosh-keypad image { + -gtk-icon-shadow: none; +} + +.phosh-search-bar { + background-color: @phosh_button_bg_color; +} + +/* Keep the keypad neutral here; accent-filled discs are the opposite of what + high contrast is for */ +phosh-keypad button { + background: @phosh_button_bg_color; + color: @phosh_fg_color; + box-shadow: inset 0 0 0 2px @phosh_borders_color; +} + +phosh-keypad button:active, +phosh-keypad button:hover, +phosh-keypad button:hover:active, +phosh-keypad button:focus { + background: @phosh_button_active_bg_color; +} From d81d7144fae7de1552db5a343a49d6c4a37330ea Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 17 Aug 2026 22:12:50 +1000 Subject: [PATCH 09/12] style-manager: build the shell's glass from the gsettings keys Loads the -furios stylesheets in place of phosh's own when glass-theme is set, and follows org.gnome.desktop.interface color-scheme while it is -- prefer-dark loads the dark variant, anything else the light one -- so the dark mode quick setting swaps the look live. The glass colours are computed from the keys rather than hard-coded. With the key unset none of this runs: phosh's stylesheet is chosen exactly as it is without us, and only the accent, which is phosh's own setting, is still generated. Text is pure black on light and pure white on dark: what sits behind it is a wallpaper the theme knows nothing about. The shadow takes the opposite of the text colour actually in use rather than of the scheme. Signed-off-by: wayne --- src/style-manager.c | 358 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 343 insertions(+), 15 deletions(-) diff --git a/src/style-manager.c b/src/style-manager.c index cba1ea022..932c378ae 100644 --- a/src/style-manager.c +++ b/src/style-manager.c @@ -17,8 +17,33 @@ #include #define IF_KEY_ACCENT_COLOR "accent-color" +#define IF_KEY_COLOR_SCHEME "color-scheme" #define IF_SCHEMA_NAME "org.gnome.desktop.interface" +/* Schema and key names live in util.h; the surfaces that request blur use + * the same schema. */ + +/* A tint reads far stronger against a pale background, so the light variant + * takes a proportion of the configured wash rather than all of it. Keeps the + * default .2 landing on the .12 the light stylesheet was written around. */ +#define GLASS_LIGHT_WASH_SCALE 0.6 +/* + * A light shadow needs less of itself than a dark one to read the same: white + * against a mid tone wallpaper carries further than black does, so the same + * alpha lands as a heavier, more displaced shadow. Scaling it back is what + * makes the two schemes look like the same shadow; the offsets stay identical. + */ +#define GLASS_LIGHT_SHADOW_SCALE 0.65 + +/* Relative weights of the glass surfaces, against glass-opacity. These are the + * alphas the stylesheets were designed with, expressed as ratios of .45, so + * the default settings reproduce the hand-tuned look exactly. */ +#define GLASS_RATIO_SHEET 1.0 /* top panel / drawer sheet, .45 */ +#define GLASS_RATIO_TINT 0.556 /* settings menu layers, .25 */ +#define GLASS_RATIO_LOCK 1.111 /* lock screen sheet, .50 */ +#define GLASS_RATIO_FIELD 1.333 /* search bar, .60 */ +#define GLASS_RATIO_BUTTON 0.889 /* app grid buttons, .40 */ + /* Accent colors from gnome-shell src/st/st-theme-context.c */ #define ACCENT_COLOR_BLUE "#3584e4" #define ACCENT_COLOR_TEAL "#2190a4" @@ -54,16 +79,69 @@ struct _PhoshStyleManager { GtkCssProvider *accent_css_provider; GSettings *interface_settings; + GSettings *glass_settings; }; G_DEFINE_TYPE (PhoshStyleManager, phosh_style_manager, G_TYPE_OBJECT) +/* First line of the file we generate. Anything without it is the user's own + * and is left alone. */ +#define APP_CSS_MARKER "/* Generated by phosh from io.furios.phosh.shell -- edits are overwritten. */" + +/* Least glass an application window keeps, whatever glass-opacity says */ +#define APP_MIN_OPACITY 0.12 + + +static void +mix_rgba (GdkRGBA *out, const GdkRGBA *a, const GdkRGBA *b, double f) +{ + out->red = a->red + (b->red - a->red) * f; + out->green = a->green + (b->green - a->green) * f; + out->blue = a->blue + (b->blue - a->blue) * f; + out->alpha = 1.0; +} + + +/* The glass colour the shell's CSS arrives at, computed here in numbers so + * the text colour can be reasoned about. Mirrors the mix() chain generated + * for @phosh_glass_bg_color. */ +static void +compute_glass (GdkRGBA *out, + const char *accent, + gboolean prefer_dark, + double lightness, + double wash) +{ + GdkRGBA base, white = { 1.0, 1.0, 1.0, 1.0 }, acc; + + gdk_rgba_parse (&base, prefer_dark ? "#16161a" : "#ececf2"); + if (!gdk_rgba_parse (&acc, accent)) + acc = base; + + mix_rgba (out, &base, &white, lightness); + mix_rgba (out, out, &acc, wash); +} + + static void on_accent_color_changed (PhoshStyleManager *self) { const char *color; g_autofree char *css = NULL; g_autoptr (GtkCssProvider) provider = gtk_css_provider_new (); + g_autofree char *custom = NULL; + g_autofree char *custom_hex = NULL; + g_autofree char *text_custom = NULL; + g_autofree char *text_hex = NULL; + const char *fg_hex, *fg_css, *accent_fg, *accent_fg_css; + g_autofree char *accent_text_custom = NULL; + g_autofree char *accent_text_hex = NULL; + g_autofree char *shadow_custom = NULL; + g_autofree char *shadow_hex = NULL; + const char *shadow_css; + GdkRGBA glass; + double opacity, lightness, wash, text_opacity, text_shadow; + gboolean prefer_dark; if (self->accent_css_provider) { gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), @@ -104,12 +182,172 @@ on_accent_color_changed (PhoshStyleManager *self) color = ACCENT_COLOR_BLUE; } - g_debug ("Setting accent bg color to %s, accent fg color to %s", - color, ACCENT_COLOR_FOREGROUND); + /* A custom colour, if set, wins over the nine presets. Re-serialise it + * rather than pasting the setting straight into the stylesheet: this string + * is interpolated into CSS, so only a colour may come out of it. */ + custom = g_settings_get_string (self->glass_settings, PHOSH_GLASS_KEY_ACCENT_CUSTOM); + if (custom && *custom) { + GdkRGBA rgba; + + if (gdk_rgba_parse (&rgba, custom)) { + custom_hex = g_strdup_printf ("#%02x%02x%02x", + (int) (CLAMP (rgba.red, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.green, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.blue, 0.0, 1.0) * 255.0 + 0.5)); + color = custom_hex; + } else { + g_warning ("Ignoring unparseable custom accent color '%s'", custom); + } + } + + /* Without the glass theme the shell is on phosh's own stylesheet, which + * knows nothing of the glass colours, and redefining its foreground and + * shadow from the glass keys would restyle a look that was not asked for. + * The accent still applies: it is phosh's own, and the custom colour above + * only widens the choice. */ + if (!g_settings_get_boolean (self->glass_settings, PHOSH_GLASS_KEY_THEME)) { + css = g_strdup_printf ("@define-color theme_selected_bg_color %s;\n" + "@define-color theme_selected_fg_color %s;", + color, ACCENT_COLOR_FOREGROUND); + gtk_css_provider_load_from_data (provider, css, -1, NULL); + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1); + g_set_object (&self->accent_css_provider, provider); + return; + } + /* Text colour, same treatment as the accent: parsed and re-serialised + * rather than pasted into the stylesheet. Empty follows the theme, which is + * what @phosh_fg_base_color holds. */ + text_custom = g_settings_get_string (self->glass_settings, PHOSH_GLASS_KEY_TEXT_COLOR); + if (text_custom && *text_custom) { + GdkRGBA rgba; + + if (gdk_rgba_parse (&rgba, text_custom)) { + text_hex = g_strdup_printf ("#%02x%02x%02x", + (int) (CLAMP (rgba.red, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.green, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.blue, 0.0, 1.0) * 255.0 + 0.5)); + } else { + g_warning ("Ignoring unparseable text color '%s'", text_custom); + } + } + + text_opacity = g_settings_get_double (self->glass_settings, PHOSH_GLASS_KEY_TEXT_OPACITY); + opacity = g_settings_get_double (self->glass_settings, PHOSH_GLASS_KEY_OPACITY); + lightness = g_settings_get_double (self->glass_settings, PHOSH_GLASS_KEY_LIGHTNESS); + wash = g_settings_get_double (self->glass_settings, PHOSH_GLASS_KEY_ACCENT_WASH); + + prefer_dark = g_settings_get_enum (self->interface_settings, IF_KEY_COLOR_SCHEME) == + G_DESKTOP_COLOR_SCHEME_PREFER_DARK; + if (!prefer_dark) + wash *= GLASS_LIGHT_WASH_SCALE; + + + /* Resolve the text colour before generating anything: an explicit choice + * wins, otherwise follow the glass, so that pushing the wash and lightness + * to a bright accent flips the text dark instead of leaving white on + * near-white. */ + compute_glass (&glass, color, prefer_dark, lightness, wash); + + /* Every piece of text in the shell follows glass-text-color, including the + * text on an accent fill. Deriving that one from the accent's luminance was + * defensible on contrast grounds and confusing in practice: the rule is + * invisible, it cannot be watched changing while a slider moves, and it + * makes the setting look ignored. Empty falls back to the theme's own + * foreground, and glass-accent-text-color overrides just the accent case. */ + fg_css = text_hex ? text_hex : "@phosh_fg_base_color"; + fg_hex = text_hex ? text_hex : (prefer_dark ? "#ffffff" : "#000000"); + + /* Text sitting *on* the accent is a different question from text on the + * glass: a selected quick setting, a pressed key, a card. White was + * hardcoded, which is wrong for a light accent -- yellow especially. */ + accent_text_custom = g_settings_get_string (self->glass_settings, PHOSH_GLASS_KEY_ACCENT_TEXT); + if (accent_text_custom && *accent_text_custom) { + GdkRGBA rgba; + + if (gdk_rgba_parse (&rgba, accent_text_custom)) + accent_text_hex = g_strdup_printf ("#%02x%02x%02x", + (int) (CLAMP (rgba.red, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.green, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.blue, 0.0, 1.0) * 255.0 + 0.5)); + else + g_warning ("Ignoring unparseable accent text color '%s'", accent_text_custom); + } + + accent_fg_css = accent_text_hex ? accent_text_hex : fg_css; + accent_fg = accent_text_hex ? accent_text_hex : fg_hex; + + /* The drop shadow behind the shell's text. It matters most on the lock + * screen, whose labels sit over a wallpaper the shell did not choose and + * cannot predict. A colour set here wins; empty derives one from the text + * colour actually in use, below. */ + shadow_custom = g_settings_get_string (self->glass_settings, PHOSH_GLASS_KEY_SHADOW_COLOR); + if (shadow_custom && *shadow_custom) { + GdkRGBA rgba; + + if (gdk_rgba_parse (&rgba, shadow_custom)) + shadow_hex = g_strdup_printf ("#%02x%02x%02x", + (int) (CLAMP (rgba.red, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.green, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (rgba.blue, 0.0, 1.0) * 255.0 + 0.5)); + else + g_warning ("Ignoring unparseable text shadow color '%s'", shadow_custom); + } + + /* Opposite the text actually in use, not opposite the colour scheme. With + * nothing set the two agree, since the scheme picks white text or black; + * they diverge the moment glass-text-color is set, which is exactly when it + * matters. Keying it off the text means setting the text alone is enough -- + * white text gets a dark shadow whatever scheme it is in, which is what a + * black wallpaper under the light scheme needs. */ + if (shadow_hex) { + shadow_css = shadow_hex; + } else { + GdkRGBA fg_rgba; + double luminance = prefer_dark ? 1.0 : 0.0; + + if (gdk_rgba_parse (&fg_rgba, fg_hex)) + luminance = 0.2126 * fg_rgba.red + 0.7152 * fg_rgba.green + 0.0722 * fg_rgba.blue; + + shadow_css = luminance > 0.5 ? "#000000" : "#ffffff"; + } + + text_shadow = CLAMP (g_settings_get_double (self->glass_settings, + PHOSH_GLASS_KEY_TEXT_SHADOW), 0.0, 1.0); + if (!prefer_dark) + text_shadow *= GLASS_LIGHT_SHADOW_SCALE; + + g_debug ("Setting accent bg color to %s; glass opacity %.2f, lightness %.2f, wash %.2f, " + "text %s", color, opacity, lightness, wash, fg_hex); + + /* The glass colours are built out of @phosh_bg_color, which the loaded + * stylesheet defines, so this stays correct for both the light and the dark + * variant without repeating either palette here. Each variant also defines + * all of these itself, which is what a non-Adwaita theme falls back on -- + * this function returns early for those. */ css = g_strdup_printf ("@define-color theme_selected_bg_color %s;\n" - "@define-color theme_selected_fg_color %s;", - color, ACCENT_COLOR_FOREGROUND); + "@define-color theme_selected_fg_color alpha(%s, %.3f);\n" + "@define-color phosh_glass_bg_color " + "mix(mix(@phosh_bg_color, #ffffff, %.3f), %s, %.3f);\n" + "@define-color phosh_glass_sheet_color alpha(@phosh_glass_bg_color, %.3f);\n" + "@define-color phosh_glass_tint_color alpha(@phosh_glass_bg_color, %.3f);\n" + "@define-color phosh_glass_lock_color alpha(@phosh_glass_bg_color, %.3f);\n" + "@define-color phosh_glass_field_color alpha(@phosh_glass_bg_color, %.3f);\n" + "@define-color phosh_glass_button_color alpha(@phosh_glass_bg_color, %.3f);\n" + "@define-color phosh_text_shadow_color alpha(%s, %.3f);\n" + "@define-color phosh_fg_color alpha(%s, %.3f);", + color, accent_fg_css, CLAMP (text_opacity, 0.2, 1.0), + lightness, color, wash, + CLAMP (opacity * GLASS_RATIO_SHEET, 0.0, 1.0), + CLAMP (opacity * GLASS_RATIO_TINT, 0.0, 1.0), + CLAMP (opacity * GLASS_RATIO_LOCK, 0.0, 1.0), + CLAMP (opacity * GLASS_RATIO_FIELD, 0.0, 1.0), + CLAMP (opacity * GLASS_RATIO_BUTTON, 0.0, 1.0), + shadow_css, text_shadow, + fg_css, + CLAMP (text_opacity, 0.2, 1.0)); gtk_css_provider_load_from_data (provider, css, -1, NULL); gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), @@ -119,26 +357,38 @@ on_accent_color_changed (PhoshStyleManager *self) static void -on_gtk_theme_name_changed (PhoshStyleManager *self, GParamSpec *pspec, GtkSettings *settings) +update_stylesheet (PhoshStyleManager *self) { const char *style; - g_autofree char *name = NULL; + gboolean glass, prefer_dark; g_autoptr (GtkCssProvider) provider = gtk_css_provider_new (); - g_object_get (settings, "gtk-theme-name", &name, NULL); - - if (g_strcmp0 (self->theme_name, name) == 0) - return; + glass = g_settings_get_boolean (self->glass_settings, PHOSH_GLASS_KEY_THEME); + prefer_dark = g_settings_get_enum (self->interface_settings, IF_KEY_COLOR_SCHEME) == + G_DESKTOP_COLOR_SCHEME_PREFER_DARK; - self->theme_name = g_steal_pointer (&name); - g_debug ("GTK theme: %s", self->theme_name); + /* Only the glass variants come in both schemes, so only they follow the + * colour scheme; without them phosh is left with the single look it has. */ + if (glass) { + g_object_set (gtk_settings_get_default (), + "gtk-application-prefer-dark-theme", prefer_dark, NULL); + } if (self->css_provider) { gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (self->css_provider)); } - style = phosh_style_manager_get_stylesheet (self->theme_name); + if (!glass) + style = phosh_style_manager_get_stylesheet (self->theme_name); + else if (g_strcmp0 (self->theme_name, "HighContrast") == 0) + style = "/mobi/phosh/stylesheet/adwaita-hc-light-furios.css"; + else if (!prefer_dark) + style = "/mobi/phosh/stylesheet/adwaita-light-furios.css"; + else + style = "/mobi/phosh/stylesheet/adwaita-dark-furios.css"; + + g_debug ("Loading stylesheet %s", style); gtk_css_provider_load_from_resource (provider, style); gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), @@ -148,6 +398,37 @@ on_gtk_theme_name_changed (PhoshStyleManager *self, GParamSpec *pspec, GtkSettin /* Refresh accent color */ on_accent_color_changed (self); +} + + +static void +on_color_scheme_changed (PhoshStyleManager *self) +{ + update_stylesheet (self); +} + + +static void +on_glass_theme_changed (PhoshStyleManager *self) +{ + update_stylesheet (self); +} + + +static void +on_gtk_theme_name_changed (PhoshStyleManager *self, GParamSpec *pspec, GtkSettings *settings) +{ + g_autofree char *name = NULL; + + g_object_get (settings, "gtk-theme-name", &name, NULL); + + if (g_strcmp0 (self->theme_name, name) == 0) + return; + + self->theme_name = g_steal_pointer (&name); + g_debug ("GTK theme: %s", self->theme_name); + + update_stylesheet (self); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_THEME_NAME]); } @@ -182,6 +463,7 @@ phosh_style_manager_dispose (GObject *object) g_clear_object (&self->accent_css_provider); g_clear_object (&self->interface_settings); + g_clear_object (&self->glass_settings); G_OBJECT_CLASS (phosh_style_manager_parent_class)->dispose (object); } @@ -209,14 +491,60 @@ phosh_style_manager_init (PhoshStyleManager *self) { GtkSettings *gtk_settings = gtk_settings_get_default (); - g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL); - self->interface_settings = g_settings_new (IF_SCHEMA_NAME); + self->glass_settings = g_settings_new (PHOSH_GLASS_SCHEMA_ID); g_signal_connect_swapped (self->interface_settings, "changed::" IF_KEY_ACCENT_COLOR, G_CALLBACK (on_accent_color_changed), self); + /* Switching the glass theme swaps the stylesheet, which regenerates the + * colours on its way out */ + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_THEME, + G_CALLBACK (on_glass_theme_changed), + self); + /* The glass colours are regenerated by the same path as the accent */ + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_ACCENT_CUSTOM, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_OPACITY, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_LIGHTNESS, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_ACCENT_WASH, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_TEXT_COLOR, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_ACCENT_TEXT, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_TEXT_OPACITY, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_TEXT_SHADOW, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_SHADOW_COLOR, + G_CALLBACK (on_accent_color_changed), + self); + g_signal_connect_swapped (self->interface_settings, + "changed::" IF_KEY_COLOR_SCHEME, + G_CALLBACK (on_color_scheme_changed), + self); g_signal_connect_swapped (gtk_settings, "notify::gtk-theme-name", G_CALLBACK (on_gtk_theme_name_changed), From 15d3bfb5b89b986191716bcd5064d26f087906e1 Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 17 Aug 2026 22:13:28 +1000 Subject: [PATCH 10/12] style-manager: generate a GTK4 stylesheet from the same keys Writes ~/.config/gtk-4.0/gtk.css so applications carry the shell's glass, which makes the theme phone-wide rather than shell-only. It refuses to touch a file it did not write, and applications pick changes up when they next start -- GTK4 reads the user stylesheet once and does not watch it. Switching the glass theme off takes the file away again, so applications do not keep a look that is no longer in use. Signed-off-by: wayne --- src/style-manager.c | 168 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 3 deletions(-) diff --git a/src/style-manager.c b/src/style-manager.c index 932c378ae..483d3e245 100644 --- a/src/style-manager.c +++ b/src/style-manager.c @@ -15,6 +15,7 @@ #include #include +#include #define IF_KEY_ACCENT_COLOR "accent-color" #define IF_KEY_COLOR_SCHEME "color-scheme" @@ -102,9 +103,20 @@ mix_rgba (GdkRGBA *out, const GdkRGBA *a, const GdkRGBA *b, double f) } -/* The glass colour the shell's CSS arrives at, computed here in numbers so - * the text colour can be reasoned about. Mirrors the mix() chain generated - * for @phosh_glass_bg_color. */ +static char * +rgba_css (const GdkRGBA *c, double alpha) +{ + return g_strdup_printf ("rgba(%d,%d,%d,%.3f)", + (int) (CLAMP (c->red, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (c->green, 0.0, 1.0) * 255.0 + 0.5), + (int) (CLAMP (c->blue, 0.0, 1.0) * 255.0 + 0.5), + CLAMP (alpha, 0.0, 1.0)); +} + + +/* The glass colour the shell's CSS arrives at, computed here in numbers so the + * text colour and the app stylesheet can both reason about it. Mirrors the + * mix() chain generated for @phosh_glass_bg_color. */ static void compute_glass (GdkRGBA *out, const char *accent, @@ -123,6 +135,150 @@ compute_glass (GdkRGBA *out, } +/* + * Take the generated stylesheet away again when the glass theme is switched + * off, so applications go back to their own look rather than keeping the + * glass forever. A file we did not write is left alone. + */ +static void +remove_app_stylesheet (void) +{ + g_autofree char *path = NULL; + g_autofree char *existing = NULL; + + path = g_build_filename (g_get_user_config_dir (), "gtk-4.0", "gtk.css", NULL); + + if (!g_file_get_contents (path, &existing, NULL, NULL)) + return; + + if (!g_str_has_prefix (existing, APP_CSS_MARKER)) { + g_debug ("%s was not generated by us, leaving it alone", path); + return; + } + + if (g_unlink (path) != 0) + g_warning ("Failed to remove %s", path); +} + + +/** + * update_app_stylesheet: + * + * Write ~/.config/gtk-4.0/gtk.css so GTK4 applications carry the same glass as + * the shell: same accent, same tint, same translucency, same text colour. + * + * Unlike the shell's own stylesheet this cannot be applied live -- GTK4 reads + * the user stylesheet once at startup and does not watch it -- so apps pick + * changes up the next time they are launched. + * + * The palette has to be repeated here in numbers, because the shell's copy + * lives in CSS where the app has no way to reach it. + */ +static void +update_app_stylesheet (PhoshStyleManager *self, + const char *accent, + const GdkRGBA *glass, + const char *fg_hex, + const char *accent_fg_hex, + double opacity, + double text_opacity) +{ + g_autofree char *path = NULL; + g_autofree char *dir = NULL; + g_autofree char *existing = NULL; + g_autofree char *css = NULL; + g_autofree char *c_window = NULL, *c_header = NULL, *c_view = NULL; + g_autofree char *c_popover = NULL, *c_dialog = NULL, *c_card = NULL; + g_autofree char *c_text = NULL, *c_accent = NULL; + g_autoptr (GError) err = NULL; + GdkRGBA accent_rgba, fg; + + dir = g_build_filename (g_get_user_config_dir (), "gtk-4.0", NULL); + path = g_build_filename (dir, "gtk.css", NULL); + + /* Never clobber a stylesheet we did not write */ + if (g_file_get_contents (path, &existing, NULL, NULL) && + !g_str_has_prefix (existing, APP_CSS_MARKER)) { + g_debug ("%s was not generated by us, leaving it alone", path); + return; + } + + if (!gdk_rgba_parse (&accent_rgba, accent)) + return; + + if (!gdk_rgba_parse (&fg, fg_hex)) + gdk_rgba_parse (&fg, "#ffffff"); + + /* Applications keep a floor of glass. At zero every surface below is fully + * transparent, which makes the other settings look broken -- lightness and + * wash still compute, but they paint a colour with no alpha -- and leaves + * app text floating unreadably on the wallpaper. The shell is left exact, + * where a fully transparent panel over the blur is a legitimate look. */ + opacity = MAX (opacity, APP_MIN_OPACITY); + + c_window = rgba_css (glass, opacity * 1.222); /* .55 at the default .45 */ + c_header = rgba_css (glass, opacity * 0.889); /* .40 */ + c_view = rgba_css (glass, opacity * 0.778); /* .35 */ + c_card = rgba_css (&accent_rgba, opacity * 0.267); /* .12 */ + /* Transient surfaces stay readable whatever the glass is set to */ + c_popover = rgba_css (glass, MAX (opacity * 1.5, 0.9)); + c_dialog = rgba_css (glass, MAX (opacity * 1.5, 0.9)); + c_text = rgba_css (&fg, text_opacity); + c_accent = rgba_css (&accent_rgba, 1.0); + + css = g_strdup_printf ( + APP_CSS_MARKER "\n" + "/* Delete this file to opt out; phosh will not recreate it while it\n" + " contains anything else. GTK4 reads this once at startup, so changes\n" + " appear the next time an application is launched. */\n" + "\n" + "@define-color window_bg_color %s;\n" + "@define-color headerbar_bg_color %s;\n" + "@define-color headerbar_backdrop_color %s;\n" + "@define-color sidebar_bg_color %s;\n" + "@define-color sidebar_backdrop_color %s;\n" + "@define-color secondary_sidebar_bg_color %s;\n" + "@define-color view_bg_color %s;\n" + "@define-color card_bg_color %s;\n" + "@define-color popover_bg_color %s;\n" + "@define-color dialog_bg_color %s;\n" + /* Every foreground libadwaita knows about. Setting only window_fg_color + left the rows inside a boxed list white, since those take + card_fg_color, while the group headings above them changed. The two + theme_* names are GTK's own, for widgets that predate libadwaita. */ + "@define-color window_fg_color %s;\n" + "@define-color view_fg_color %s;\n" + "@define-color headerbar_fg_color %s;\n" + "@define-color card_fg_color %s;\n" + "@define-color dialog_fg_color %s;\n" + "@define-color popover_fg_color %s;\n" + "@define-color sidebar_fg_color %s;\n" + "@define-color secondary_sidebar_fg_color %s;\n" + "@define-color thumbnail_fg_color %s;\n" + "@define-color thumbnail_bg_color %s;\n" + "@define-color theme_fg_color %s;\n" + "@define-color theme_text_color %s;\n" + "@define-color accent_bg_color %s;\n" + "@define-color accent_color %s;\n" + "@define-color accent_fg_color %s;\n", + c_window, c_header, c_window, c_header, c_window, c_header, + c_view, c_card, c_popover, c_dialog, + c_text, c_text, c_text, c_text, c_text, c_text, c_text, c_text, c_text, + c_view, c_text, c_text, + c_accent, c_accent, accent_fg_hex); + + if (g_mkdir_with_parents (dir, 0755) != 0) { + g_warning ("Could not create %s", dir); + return; + } + + if (!g_file_set_contents (path, css, -1, &err)) + g_warning ("Could not write %s: %s", path, err->message); + else + g_debug ("Wrote app stylesheet %s", path); +} + + static void on_accent_color_changed (PhoshStyleManager *self) { @@ -206,6 +362,7 @@ on_accent_color_changed (PhoshStyleManager *self) * The accent still applies: it is phosh's own, and the custom colour above * only widens the choice. */ if (!g_settings_get_boolean (self->glass_settings, PHOSH_GLASS_KEY_THEME)) { + remove_app_stylesheet (); css = g_strdup_printf ("@define-color theme_selected_bg_color %s;\n" "@define-color theme_selected_fg_color %s;", color, ACCENT_COLOR_FOREGROUND); @@ -353,6 +510,11 @@ on_accent_color_changed (PhoshStyleManager *self) GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1); g_set_object (&self->accent_css_provider, provider); + + /* Keep applications in step with the shell. They only pick this up when + * they next start, which is the best GTK4 allows. */ + update_app_stylesheet (self, color, &glass, fg_hex, accent_fg, opacity, + CLAMP (text_opacity, 0.2, 1.0)); } From cb4ffea6040f69e53fb334d9dd5329d06e3b61b3 Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:37:05 +1000 Subject: [PATCH 11/12] top-panel: ask for background blur Only while the glass theme is on: behind an opaque panel the blur would not show, and the compositor would still pay for it. Signed-off-by: wayne --- src/top-panel.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/top-panel.c b/src/top-panel.c index 974e8008b..ce10322de 100644 --- a/src/top-panel.c +++ b/src/top-panel.c @@ -102,6 +102,7 @@ typedef struct _PhoshTopPanel { GnomeXkbInfo *xkbinfo; GSettings *input_settings; GSettings *interface_settings; + GSettings *glass_settings; GdkSeat *seat; GSimpleActionGroup *actions; @@ -658,6 +659,21 @@ static GActionEntry entries[] = { }; +static void +on_blur_radius_changed (PhoshTopPanel *self) +{ + guint radius = 0; + + /* The blur is what makes the glass panel read as glass. Without the theme + * the panel is opaque, so asking for it would buy nothing and still cost + * the compositor a pass over the frame. */ + if (g_settings_get_boolean (self->glass_settings, PHOSH_GLASS_KEY_THEME)) + radius = g_settings_get_uint (self->glass_settings, PHOSH_GLASS_KEY_BLUR_RADIUS); + + phosh_layer_surface_set_blur (PHOSH_LAYER_SURFACE (self), radius); +} + + static void phosh_top_panel_constructed (GObject *object) { @@ -671,6 +687,18 @@ phosh_top_panel_constructed (GObject *object) G_OBJECT_CLASS (phosh_top_panel_parent_class)->constructed (object); + /* Frosted glass: let the compositor blur what's behind the panel */ + self->glass_settings = g_settings_new (PHOSH_GLASS_SCHEMA_ID); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_BLUR_RADIUS, + G_CALLBACK (on_blur_radius_changed), + self); + g_signal_connect_swapped (self->glass_settings, + "changed::" PHOSH_GLASS_KEY_THEME, + G_CALLBACK (on_blur_radius_changed), + self); + on_blur_radius_changed (self); + g_object_bind_property (phosh_shell_get_default (), "locked", self, "on-lockscreen", G_BINDING_SYNC_CREATE); @@ -784,6 +812,7 @@ phosh_top_panel_dispose (GObject *object) g_clear_object (&self->xkbinfo); g_clear_object (&self->input_settings); g_clear_object (&self->interface_settings); + g_clear_object (&self->glass_settings); g_clear_object (&self->actions); g_clear_pointer (&self->action_names, g_strfreev); if (self->seat) { From bf6fdff5f4c02ef915c4fa1d6128ebd7eba3b392 Mon Sep 17 00:00:00 2001 From: wayne Date: Sun, 16 Aug 2026 10:37:06 +1000 Subject: [PATCH 12/12] lockscreen: frost the keypad page Only once the user has swiped up to the keypad, and only with the glass theme on. The info page is a wallpaper with a clock on it and there is nothing behind it worth hiding: the lock screen sits above its own background surface, not above the running apps. Blurring it only makes the picture worse. Signed-off-by: wayne --- src/lockscreen.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/lockscreen.c b/src/lockscreen.c index 065eefa73..0b3d93f3c 100644 --- a/src/lockscreen.c +++ b/src/lockscreen.c @@ -104,6 +104,7 @@ typedef struct { gint64 last_input; PhoshAuth *auth; GSettings *lockscreen_settings; + GSettings *glass_settings; struct { GtkGesture *swipe_gesture; @@ -564,6 +565,8 @@ wall_clock_notify_cb (PhoshLockscreen *self, } +static void update_blur (PhoshLockscreen *self); + static void carousel_position_notified_cb (PhoshLockscreen *self, GParamSpec *pspec, @@ -603,6 +606,8 @@ carousel_page_changed_cb (PhoshLockscreen *self, clear_input (self, TRUE); } + update_blur (self); + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PAGE]); } @@ -843,6 +848,51 @@ phosh_lockscreen_add_background (PhoshLockscreen *self) } +/* + * Frost the lock screen only once the user has swiped up to the keypad. + * + * The info page is a wallpaper with a clock on it, and blurring it just makes + * the picture worse; there is nothing behind it worth hiding, because the lock + * screen sits above its own background surface rather than above the running + * apps. The unlock page is where the frost earns its keep, giving the keypad + * and the entry a surface to sit on. + * + * Called on page changes as well as on the setting changing. The carousel + * emits page-changed once the swipe settles, so the frost appears with the + * keypad rather than tracking the drag. + */ +static void +update_blur (PhoshLockscreen *self) +{ + PhoshLockscreenPrivate *priv = phosh_lockscreen_get_instance_private (self); + PhoshLockscreenPage page; + gboolean glass; + guint radius; + + /* Called from constructed() before the carousel exists, where the page is + * whatever the lock screen is about to open on */ + page = priv->carousel ? phosh_lockscreen_get_page (self) : priv->default_page; + glass = g_settings_get_boolean (priv->glass_settings, PHOSH_GLASS_KEY_THEME); + + if (page != PHOSH_LOCKSCREEN_PAGE_UNLOCK || !glass) + radius = 0; + else + radius = g_settings_get_uint (priv->glass_settings, PHOSH_GLASS_KEY_BLUR_RADIUS); + + phosh_layer_surface_set_blur (PHOSH_LAYER_SURFACE (self), radius); + + /* The same page distinction for the translucent sheet the lock screen lays + * over the wallpaper. With lockscreen-tint off the info page shows the + * picture as it is; the keypad page keeps its sheet either way, because the + * keys and the entry need a surface to sit on. */ + if (page != PHOSH_LOCKSCREEN_PAGE_UNLOCK && + !g_settings_get_boolean (priv->glass_settings, PHOSH_KEY_LOCKSCREEN_TINT)) + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "p-untinted"); + else + gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "p-untinted"); +} + + static void phosh_lockscreen_constructed (GObject *object) { @@ -858,6 +908,25 @@ phosh_lockscreen_constructed (GObject *object) G_OBJECT_CLASS (phosh_lockscreen_parent_class)->constructed (object); + /* Frosted glass. This surface sits above its own background surface, so what + * the compositor blurs is the lock screen wallpaper -- not the apps beneath, + * which would put a recognisable, if smeared, copy of whatever you were + * doing onto the lock screen. */ + priv->glass_settings = g_settings_new (PHOSH_GLASS_SCHEMA_ID); + g_signal_connect_swapped (priv->glass_settings, + "changed::" PHOSH_GLASS_KEY_BLUR_RADIUS, + G_CALLBACK (update_blur), + self); + g_signal_connect_swapped (priv->glass_settings, + "changed::" PHOSH_KEY_LOCKSCREEN_TINT, + G_CALLBACK (update_blur), + self); + g_signal_connect_swapped (priv->glass_settings, + "changed::" PHOSH_GLASS_KEY_THEME, + G_CALLBACK (update_blur), + self); + update_blur (self); + /* window properties */ gtk_window_set_title (GTK_WINDOW (self), "phosh lockscreen"); gtk_window_set_decorated (GTK_WINDOW (self), FALSE); @@ -986,6 +1055,7 @@ phosh_lockscreen_dispose (GObject *object) g_clear_object (&priv->calls_manager); g_clear_pointer (&priv->active, g_free); g_clear_object (&priv->lockscreen_settings); + g_clear_object (&priv->glass_settings); g_clear_pointer (&priv->background, phosh_cp_widget_destroy);