Commit a4851831 authored by verdre's avatar verdre

panel: Use transparent panel depeding on background characteristics

Allow making the panel completely transparent if the background
provides enough contrast for either black or white text.

The following background characteristics are used to determine the
text-friendliness of the wallpaper:

 - Normal: The background in the area around the panel does not have any
notable properties (default case, no particular CSS class)

 - Bright / Dark: The panel background is either notably bright or dark
("background-bright" or "background-dark" CSS class)

 - Noisy: The panel background is noisy and might not provide sufficient
contrast for text, this case is also used if the color analysis failed
("background-noisy" CSS class)

A background image can have both the Noisy and the Bright / Dark
characteristics set at the same time.

We use the opaque panel by default and only switch to the transparent
panel if the background is either bright or dark. If the background is
noisy, the opaque panel is always used. In the overview, we always use
the transparent panel with white text, except when there's no window
near the panel and the background is neither bright or dark, to avoid
the unnatural transition of the background getting darker while the
panel is getting brighter.

The "window-created" signal of MetaDisplay is used instead of connecting
to the "actor-added/removed" signals of the window_group because the
workspace switching animation reparents all windows to its own container
during the animation, which causes a lot of unnecessary calls to
_updateOpaqueState.

Fixes #408
parent 72626f56
......@@ -730,16 +730,11 @@ StScrollBar {
#panel {
background-color: black;
transition-duration: 300ms;
font-weight: bold;
height: 1.86em;
font-feature-settings: "tnum";
&.unlock-screen,
&.login-screen,
&.lock-screen {
background-color: transparent;
}
#panelLeft, #panelCenter { // spacing between activities<>app menu and such
spacing: 4px;
}
......@@ -749,16 +744,6 @@ StScrollBar {
-panel-corner-background-color: black;
-panel-corner-border-width: 2px;
-panel-corner-border-color: transparent;
&:active, &:overview, &:focus {
-panel-corner-border-color: lighten($selected_bg_color,5%);
}
&.lock-screen, &.login-screen, &.unlock-screen {
-panel-corner-radius: 0;
-panel-corner-background-color: transparent;
-panel-corner-border-color: transparent;
}
}
.panel-button {
......@@ -766,6 +751,7 @@ StScrollBar {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100ms;
.app-menu-icon {
-st-icon-style: symbolic;
......@@ -774,25 +760,7 @@ StScrollBar {
//dimensions of the icon are hardcoded
}
&:hover {
color: lighten($fg_color, 10%);
}
&:active, &:overview, &:focus, &:checked {
// Trick due to St limitations. It needs a background to draw
// a box-shadow
background-color: rgba(0, 0, 0, 0.01);
box-shadow: inset 0 -2px 0px lighten($selected_bg_color,5%);
color: lighten($fg_color,10%);
}
.system-status-icon { icon-size: 1.09em; padding: 0 5px; }
.unlock-screen &,
.login-screen &,
.lock-screen & {
color: lighten($fg_color, 10%);
&:focus, &:hover, &:active { color: lighten($fg_color, 10%); }
}
}
.panel-status-indicators-box,
......@@ -808,6 +776,144 @@ StScrollBar {
.screencast-indicator { color: $warning_color; }
.remote-access-indicator { color: $warning_color; }
&.background-dark, &.background-bright {
background-color: transparent;
.panel-corner {
-panel-corner-radius: 0;
-panel-corner-background-color: transparent;
}
}
&.background-dark .panel-button {
color: white;
}
&.background-bright .panel-button {
color: black;
}
&.background-noisy, &.window-near {
background-color: black;
.panel-corner {
-panel-corner-radius: $panel-corner-radius;
-panel-corner-background-color: black;
}
.panel-button {
color: #ccc;
}
}
&:overview {
background-color: black;
.panel-corner {
-panel-corner-radius: $panel-corner-radius;
-panel-corner-background-color: black;
}
.panel-button {
color: #ccc;
}
&.background-dark, &.background-bright, &.window-near {
background-color: transparent;
.panel-corner {
-panel-corner-radius: 0;
-panel-corner-background-color: transparent;
}
.panel-button {
color: white;
}
}
&.background-noisy {
background-color: black;
.panel-corner {
-panel-corner-radius: $panel-corner-radius;
-panel-corner-background-color: black;
}
.panel-button {
color: #ccc;
}
&.window-near {
background-color: transparent;
.panel-corner {
-panel-corner-radius: 0;
-panel-corner-background-color: transparent;
}
.panel-button {
color: white;
}
}
}
}
&.lock-screen, &.login-screen, &.unlock-screen {
background-color: transparent;
.panel-corner {
-panel-corner-radius: 0;
-panel-corner-background-color: transparent;
}
.panel-button {
color: white;
}
&.screen-shield {
background-color: black;
.panel-corner {
-panel-corner-radius: $panel-corner-radius;
-panel-corner-background-color: black;
}
.panel-button {
color: #ccc;
}
&.background-bright, &.background-dark {
background-color: transparent;
.panel-corner {
-panel-corner-radius: 0;
-panel-corner-background-color: transparent;
}
}
&.background-bright .panel-button {
color: black;
}
&.background-dark .panel-button {
color: white;
}
&.background-noisy {
background-color: black;
.panel-corner {
-panel-corner-radius: $panel-corner-radius;
-panel-corner-background-color: black;
}
.panel-button {
color: #ccc;
}
}
}
}
}
// calendar popover
......@@ -1934,8 +2040,6 @@ StScrollBar {
.screen-shield-notification-count-text { padding: 0px 0px 0px 12px; }
#panel.lock-screen { background-color: transparentize($_bubble_bg_color, 0.5); }
.screen-shield-background { //just the shadow, really
background: black;
box-shadow: 0px 2px 4px transparentize(black,0.6);
......
......@@ -853,9 +853,46 @@ class Panel extends St.Widget {
Main.overview.connect('showing', () => {
this.add_style_pseudo_class('overview');
this._updateOpaqueState();
});
Main.overview.connect('hiding', () => {
this.remove_style_pseudo_class('overview');
this._updateOpaqueState();
});
if (Main.screenShield) {
Main.screenShield.connect('shield-visible', () => {
this._addStyleClassName('screen-shield');
if (Main.layoutManager.primaryMonitor) {
let bgManager = Main.screenShield.getBgManagerForMonitor(Main.layoutManager.primaryMonitor);
this._onBackgroundChanged(bgManager);
}
});
Main.screenShield.connect('shield-hidden', () => {
this._removeStyleClassName('screen-shield');
if (Main.layoutManager.primaryMonitor) {
let bgManager = Main.layoutManager._bgManagers[Main.layoutManager.primaryIndex];
this._onBackgroundChanged(bgManager);
}
});
}
Main.layoutManager.connect('monitors-changed', () => {
if (Main.layoutManager.primaryMonitor) {
let bgManager = Main.layoutManager._bgManagers[Main.layoutManager.primaryIndex];
bgManager.connect('changed', () => this._onBackgroundChanged(bgManager));
if (bgManager.isLoaded)
this._onBackgroundChanged(bgManager);
}
});
Main.layoutManager.connect('startup-complete', () => {
if (Main.layoutManager.primaryMonitor) {
let bgManager = Main.layoutManager._bgManagers[Main.layoutManager.primaryIndex];
bgManager.connect('changed', () => this._onBackgroundChanged(bgManager));
if (bgManager.isLoaded)
this._onBackgroundChanged(bgManager);
}
});
Main.layoutManager.panelBox.add(this);
......@@ -864,10 +901,45 @@ class Panel extends St.Widget {
Main.sessionMode.connect('updated', this._updatePanel.bind(this));
this._trackedWindows = new Map();
global.display.connect('window-created', this._onWindowAdded.bind(this));
global.window_manager.connect('switch-workspace', () => this._updateOpaqueState());
global.display.connect('workareas-changed', () => { this.queue_relayout(); });
this._updatePanel();
}
_onWindowAdded(metaDisplay, metaWindow) {
let signalIds = [];
let metaWindowActor = metaWindow.get_compositor_private();
// Workaround for https://gitlab.gnome.org/GNOME/mutter/issues/156
if (!metaWindowActor) {
let id = Mainloop.idle_add(() => {
if (metaWindow.get_compositor_private())
this._onWindowAdded(metaDisplay, metaWindow);
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] this._onWindowAdded');
return;
}
['allocation-changed', 'notify::visible'].forEach(s => {
signalIds.push(metaWindowActor.connect(s, this._updateOpaqueState.bind(this)));
});
this._trackedWindows.set(metaWindowActor, signalIds);
metaWindowActor.connect('destroy', this._onWindowRemoved.bind(this));
}
_onWindowRemoved(metaWindowActor) {
this._trackedWindows.get(metaWindowActor).forEach(id => {
metaWindowActor.disconnect(id);
});
this._trackedWindows.delete(metaWindowActor);
this._updateOpaqueState();
}
vfunc_get_preferred_width(forHeight) {
let primaryMonitor = Main.layoutManager.primaryMonitor;
......@@ -1075,6 +1147,8 @@ class Panel extends St.Widget {
else
Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER;
this._updateOpaqueState();
if (this._sessionStyle)
this._removeStyleClassName(this._sessionStyle);
......@@ -1091,6 +1165,64 @@ class Panel extends St.Widget {
}
}
_updateOpaqueState() {
/* Get all the windows in the active workspace that are in the primary monitor and visible */
let workspaceManager = global.workspace_manager;
let activeWorkspace = workspaceManager.get_active_workspace();
let windows = activeWorkspace.list_windows().filter(metaWindow => {
return metaWindow.is_on_primary_monitor() &&
metaWindow.showing_on_its_workspace() &&
metaWindow.get_window_type() != Meta.WindowType.DESKTOP;
});
/* Check if at least one window is near enough to the panel */
let [, panelTop] = this.get_transformed_position();
let panelBottom = panelTop + this.get_height();
let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let windowNearPanel = windows.some(metaWindow => {
let verticalPosition = metaWindow.get_frame_rect().y;
return verticalPosition < panelBottom + 5 * scale;
});
if (windowNearPanel)
this._addStyleClassName('window-near');
else
this._removeStyleClassName('window-near');
}
_onBackgroundChanged(bgManager) {
let allStyleClassNames = ['background-noisy', 'background-dark', 'background-bright'];
// We use a slightly bigger height to make sure the algorithm
// can factor in sudden color changes right beneath the panel.
let width = this.get_width();
let height = parseInt(this.get_height() * 1.5);
let [success, bgIsNoisy, bgIsDark, bgIsBright] =
bgManager.getCharacteristicsForArea(this.x, this.y, width, height);
let styleClassNames = [];
if (success) {
if (bgIsNoisy)
styleClassNames.push('background-noisy');
if (bgIsDark)
styleClassNames.push('background-dark');
if (bgIsBright)
styleClassNames.push('background-bright');
} else {
// Always assume the background is noisy if the color check failed
styleClassNames.push('background-noisy');
}
allStyleClassNames.forEach(name => {
if (styleClassNames.includes(name))
this._addStyleClassName(name);
else
this._removeStyleClassName(name);
});
}
_hideIndicators() {
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
let indicator = this.statusArea[role];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment