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__)