diff --git a/data/mobi.phosh.shell.gschema.xml b/data/mobi.phosh.shell.gschema.xml index e3040811..8f44fd4e 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. + + diff --git a/protocol/meson.build b/protocol/meson.build index 3007a1c1..455e9ae1 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 00000000..77da053d --- /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. + + + + diff --git a/src/layersurface-priv.h b/src/layersurface-priv.h index 61669ce4..7337219e 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 52885b79..acbfb431 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 diff --git a/src/lockscreen.c b/src/lockscreen.c index 065eefa7..0b3d93f3 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); diff --git a/src/phosh-wayland.c b/src/phosh-wayland.c index cba8a12c..5b8cdef6 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 baf52b46..99688c8d 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 diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml index a9b3d64d..f67103ad 100644 --- a/src/phosh.gresources.xml +++ b/src/phosh.gresources.xml @@ -51,8 +51,12 @@ 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/adwaita-hc-light-furios.css + stylesheet/adwaita-light-furios.css stylesheet/common.css + stylesheet/common-furios.css ../data/icons/app-icon-unknown.svg diff --git a/src/style-manager.c b/src/style-manager.c index cba1ea02..483d3e24 100644 --- a/src/style-manager.c +++ b/src/style-manager.c @@ -15,10 +15,36 @@ #include #include +#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 +80,224 @@ 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; +} + + +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, + 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); +} + + +/* + * 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) { 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,41 +338,219 @@ 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)) { + 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); + 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), 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)); } 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); + 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; - if (g_strcmp0 (self->theme_name, name) == 0) - return; - - 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 +560,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 +625,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 +653,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), diff --git a/src/stylesheet/adwaita-dark-furios.css b/src/stylesheet/adwaita-dark-furios.css new file mode 100644 index 00000000..39d7bd8e --- /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/adwaita-hc-light-furios.css b/src/stylesheet/adwaita-hc-light-furios.css new file mode 100644 index 00000000..d7c55cad --- /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; +} diff --git a/src/stylesheet/adwaita-light-furios.css b/src/stylesheet/adwaita-light-furios.css new file mode 100644 index 00000000..940ccc02 --- /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)); +} diff --git a/src/stylesheet/common-furios.css b/src/stylesheet/common-furios.css new file mode 100644 index 00000000..31d09e5b --- /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; +} diff --git a/src/top-panel.c b/src/top-panel.c index 974e8008..ce10322d 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) { diff --git a/src/util.h b/src/util.h index 0cbf2331..0fd8b5e4 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__)