window-list: Add attention indicator

Some X11 clients still rely on the traditional urgent/demand-attention
hints instead of notifications to request the user's attention.

Support these by adding a visual indication to the corresponding
buttons, based on the visual indicator in libadwaita's tabs.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/issues/543
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/366>
This commit is contained in:
Florian Müllner
2024-12-17 01:09:34 +01:00
committed by Marge Bot
parent 0f69d7663b
commit 2a45d8c145
2 changed files with 94 additions and 5 deletions
+85 -5
View File
@@ -37,6 +37,9 @@ const DRAG_PROXIMITY_THRESHOLD = 30;
const SAVED_POSITIONS_KEY = 'window-list-positions';
const ATTENTION_INDICATOR_MAX_SCALE = 0.4;
const ATTENTION_INDICATOR_TRANSITION_DURATION = 300;
const GroupingMode = {
NEVER: 0,
AUTO: 1,
@@ -127,7 +130,7 @@ class WindowContextMenu extends PopupMenu.PopupMenu {
}
}
class TitleWidget extends St.BoxLayout {
class TitleWidget extends St.Widget {
static {
GObject.registerClass({
GTypeFlags: GObject.TypeFlags.ABSTRACT,
@@ -142,20 +145,27 @@ class TitleWidget extends St.BoxLayout {
constructor() {
super({
style_class: 'window-button-box',
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
});
const hbox = new St.BoxLayout({
style_class: 'window-button-box',
x_expand: true,
y_expand: true,
});
this.add_child(hbox);
this._icon = new St.Bin({
style_class: 'window-button-icon',
});
this.add_child(this._icon);
hbox.add_child(this._icon);
this._label = new St.Label({
y_align: Clutter.ActorAlign.CENTER,
});
this.add_child(this._label);
hbox.add_child(this._label);
this.label_actor = this._label;
this.bind_property('abstract-label',
@@ -168,11 +178,28 @@ class TitleWidget extends St.BoxLayout {
x_expand: true,
y_expand: true,
});
this.add_child(this._abstractLabel);
hbox.add_child(this._abstractLabel);
this.bind_property('abstract-label',
this._abstractLabel, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._attentionIndicator = new St.Widget({
style_class: 'window-button-attention-indicator',
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.END,
scale_x: 0,
});
this._attentionIndicator.set_pivot_point(0.5, 0.5);
this.add_child(this._attentionIndicator);
}
setNeedsAttention(enable) {
this._attentionIndicator.ease({
scaleX: enable ? ATTENTION_INDICATOR_MAX_SCALE : 0,
duration: ATTENTION_INDICATOR_TRANSITION_DURATION,
});
}
}
@@ -193,10 +220,13 @@ class WindowTitle extends TitleWidget {
() => this._updateIcon(), GObject.ConnectFlags.AFTER,
'notify::title', () => this._updateTitle(),
'notify::minimized', () => this._minimizedChanged(),
'notify::demands-attention', () => this._updateNeedsAttention(),
'notify::urgent', () => this._updateNeedsAttention(),
this);
this._updateIcon();
this._minimizedChanged();
this._updateNeedsAttention();
}
_minimizedChanged() {
@@ -204,6 +234,11 @@ class WindowTitle extends TitleWidget {
this._updateTitle();
}
_updateNeedsAttention() {
const {urgent, demandsAttention} = this._metaWindow;
this.setNeedsAttention(urgent || demandsAttention);
}
_updateTitle() {
if (!this._metaWindow.title)
return;
@@ -236,9 +271,54 @@ class AppTitle extends TitleWidget {
super();
this._app = app;
this._windows = new Set();
this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE);
this._label.text = app.get_name();
this._app.connectObject(
'windows-changed', () => this._onWindowsChanged(),
this);
this._onWindowsChanged();
this.connect('destroy', () => {
console.debug(`Clearing windows of app ${this._app.id}`);
this._windows.clear();
});
}
_onWindowsChanged() {
const windows = this._app.get_windows();
const removed = [...this._windows].filter(w => !windows.includes(w));
removed.forEach(w => this._untrackWindow(w));
windows.forEach(w => this._trackWindow(w));
this._updateNeedsAttention();
}
_trackWindow(window) {
if (this._windows.has(window))
return;
console.debug(`Tracking window ${window} for app ${this._app.id}`);
window.connectObject(
'notify::urgent', () => this._updateNeedsAttention(),
'notify::demands-attention', () => this._updateNeedsAttention(),
this);
this._windows.add(window);
}
_untrackWindow(window) {
if (!this._windows.delete(window))
return;
console.debug(`Untracking window ${window} for app ${this._app.id}`);
window.disconnectObject(this);
}
_updateNeedsAttention() {
const needsAttention =
[...this._windows].some(w => w.urgent || w.demandsAttention);
this.setNeedsAttention(needsAttention);
}
}
@@ -103,3 +103,12 @@
border-radius: 99px;
margin: 6px;
}
.window-button-attention-indicator {
background-color: -st-accent-color;
height: 2px;
}
.window-button.minimized .window-button-attention-indicator {
background-color: st-transparentize(-st-accent-color, 0.4);
}