From 5a9bd3c4e5cee54484a85d3f363b4c6246a71990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 23 Apr 2025 10:27:24 +0200 Subject: [PATCH] Indicate urgency-hint in dash-to-panel Resolves: https://issues.redhat.com/browse/RHEL-76834 --- dash-to-panel-attention-indicator.patch | 272 ++++++++++++++++++++++++ gnome-shell-extensions.spec | 7 +- 2 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 dash-to-panel-attention-indicator.patch diff --git a/dash-to-panel-attention-indicator.patch b/dash-to-panel-attention-indicator.patch new file mode 100644 index 0000000..94aac5b --- /dev/null +++ b/dash-to-panel-attention-indicator.patch @@ -0,0 +1,272 @@ +From f6ccd2e00f5568ee3140cd7aaa72b19466eae39f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 23 Apr 2025 08:43:56 +0200 +Subject: [PATCH 1/2] windowPreview: Add attention indicator + +Some X11 clients still rely on the traditional urgent/demands-attention +hints instead of notifications to request the user's attention. + +Support these by adding a visual indication to the corresponding +previews, based on the visual indicator in libadwaita's tabs. +--- + extensions/dash-to-panel/stylesheet.css | 5 +++ + extensions/dash-to-panel/windowPreview.js | 42 ++++++++++++++++++++++- + 2 files changed, 46 insertions(+), 1 deletion(-) + +diff --git a/extensions/dash-to-panel/stylesheet.css b/extensions/dash-to-panel/stylesheet.css +index 6917e24d..1dcd3ae1 100644 +--- a/extensions/dash-to-panel/stylesheet.css ++++ b/extensions/dash-to-panel/stylesheet.css +@@ -149,3 +149,8 @@ + -progress-bar-border: rgba(0.9, 0.9, 0.9, 1); + */ + } ++ ++.window-preview-attention-indicator { ++ background-color: rgba(27, 106, 203, 1.0); ++ height: 2px; ++} +diff --git a/extensions/dash-to-panel/windowPreview.js b/extensions/dash-to-panel/windowPreview.js +index 45d08a04..12fe18bb 100644 +--- a/extensions/dash-to-panel/windowPreview.js ++++ b/extensions/dash-to-panel/windowPreview.js +@@ -50,6 +50,8 @@ const FOCUSED_COLOR_OFFSET = 24; + const HEADER_COLOR_OFFSET = -12; + const FADE_SIZE = 36; + const PEEK_INDEX_PROP = '_dtpPeekInitialIndex'; ++const ATTENTION_INDICATOR_MAX_SCALE = 0.4; ++const ATTENTION_INDICATOR_TRANSITION_DURATION = 0.3; + + let headerHeight = 0; + let alphaBg = 0; +@@ -740,14 +742,29 @@ var Preview = Utils.defineClass({ + + setStyle(headerBox, this._getBackgroundColor(HEADER_COLOR_OFFSET, 1)); + this._workspaceIndicator = new St.Label({ y_align: Clutter.ActorAlign.CENTER }); ++ let titleBox = new St.Widget({ ++ layout_manager: new Clutter.BinLayout(), ++ x_expand: true, ++ y_expand: true, ++ }); + this._windowTitle = new St.Label({ y_align: Clutter.ActorAlign.CENTER, x_expand: true }); ++ this._attentionIndicator = new St.Widget({ ++ style_class: 'window-preview-attention-indicator', ++ x_expand: true, ++ y_expand: true, ++ y_align: Clutter.ActorAlign.END, ++ scale_x: 0, ++ }); ++ ++ titleBox.add_child(this._windowTitle); ++ titleBox.add_child(this._attentionIndicator); + + this._iconBin = new St.Widget({ layout_manager: new Clutter.BinLayout() }); + this._iconBin.set_size(headerHeight, headerHeight); + + headerBox.add_child(this._iconBin); + headerBox.insert_child_at_index(this._workspaceIndicator, isLeftButtons ? 0 : 1); +- headerBox.insert_child_at_index(this._windowTitle, isLeftButtons ? 1 : 2); ++ headerBox.insert_child_at_index(titleBox, isLeftButtons ? 1 : 2); + + box.insert_child_at_index(headerBox, isTopHeader ? 0 : 1); + } +@@ -942,6 +959,14 @@ var Preview = Utils.defineClass({ + this.window.disconnect(this._titleWindowChangeId); + this._titleWindowChangeId = 0; + } ++ if (this._attentionHintChangeId) { ++ this.window.disconnect(this._attentionHintChangeId) ++ this._attentionHintChangeId = 0 ++ } ++ if (this._urgentHintChangeId) { ++ this.window.disconnect(this._urgentHintChangeId) ++ this._urgentHintChangeId = 0 ++ } + }, + + _updateHeader: function() { +@@ -970,8 +995,11 @@ var Preview = Utils.defineClass({ + setStyle(this._workspaceIndicator, workspaceStyle); + + this._titleWindowChangeId = this.window.connect('notify::title', () => this._updateWindowTitle()); ++ this._attentionHintChangeId = this.window.connect('notify::demands-attention', () => this._updateNeedsAttention()); ++ this._urgentHintChangeId = this.window.connect('notify::urgent', () => this._updateNeedsAttention()); + setStyle(this._windowTitle, 'max-width: 0px; padding-right: 4px;' + commonTitleStyles); + this._updateWindowTitle(); ++ this._updateNeedsAttention(); + } + }, + +@@ -979,6 +1007,18 @@ var Preview = Utils.defineClass({ + this._windowTitle.text = this.window.title; + }, + ++ _updateNeedsAttention: function() { ++ const urgent = this.window.urgent; ++ const demandsAttention = this.window.demands_attention; ++ const needsAttention = urgent || demandsAttention; ++ Utils.animate( ++ this._attentionIndicator, ++ getTweenOpts({ ++ scale_x: needsAttention ? ATTENTION_INDICATOR_MAX_SCALE : 0, ++ time: ATTENTION_INDICATOR_TRANSITION_DURATION, ++ })); ++ }, ++ + _hideOrShowCloseButton: function(hide) { + if (this._needsCloseButton) { + Utils.animate(this._closeButtonBin, getTweenOpts({ opacity: hide ? 0 : 255 })); +-- +2.49.0 + + +From 497df374038b1389204bed3bd89983c6fed20836 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 23 Apr 2025 09:38:56 +0200 +Subject: [PATCH 2/2] appIcons: Add attention indicator + +Some X11 clients still rely on the traditional urgent/demands-attention +hints instead of notifications to request the user's attention. + +Tabs in libadwaita use an underline for that purpose, but unfortunately +that indication is already taken by the running indicator; so instead, +add a subtle flash effect to icons with urgent windows. +--- + extensions/dash-to-panel/appIcons.js | 86 ++++++++++++++++++++++++++++ + 1 file changed, 86 insertions(+) + +diff --git a/extensions/dash-to-panel/appIcons.js b/extensions/dash-to-panel/appIcons.js +index 5cfb1350..46c9030d 100644 +--- a/extensions/dash-to-panel/appIcons.js ++++ b/extensions/dash-to-panel/appIcons.js +@@ -25,6 +25,7 @@ + const Clutter = imports.gi.Clutter; + const Gio = imports.gi.Gio; + const GLib = imports.gi.GLib; ++const GObject = imports.gi.GObject; + const Gtk = imports.gi.Gtk; + const Signals = imports.signals; + const Lang = imports.lang; +@@ -114,6 +115,8 @@ var taskbarAppIcon = Utils.defineClass({ + _init: function(appInfo, panel, iconParams, previewMenu, iconAnimator) { + this.dtpPanel = panel; + this._nWindows = 0; ++ this._windows = new Set(); ++ this._windowSignals = new Map(); + this.window = appInfo.window; + this.isLauncher = appInfo.isLauncher; + this._previewMenu = previewMenu; +@@ -187,6 +190,13 @@ var taskbarAppIcon = Utils.defineClass({ + this.actor.set_width(panel.geom.w); + } + ++ this._flashEffect = new Clutter.BrightnessContrastEffect({ ++ name: 'attention-flash', ++ enabled: false, ++ }); ++ this.icon.add_effect(this._flashEffect); ++ this._updateTrackedWindows(); ++ + // Monitor windows-changes instead of app state. + // Keep using the same Id and function callback (that is extended) + if(this._stateChangedId > 0) { +@@ -324,6 +334,8 @@ var taskbarAppIcon = Utils.defineClass({ + this._destroyed = true; + + this._timeoutsHandler.destroy(); ++ this._windowSignals.forEach((ids, w) => ids.forEach(id => w.disconnect(id))); ++ this._windowSignals.clear(); + + this._previewMenu.close(true); + +@@ -367,6 +379,7 @@ var taskbarAppIcon = Utils.defineClass({ + }, + + onWindowsChanged: function() { ++ this._updateTrackedWindows(); + this._updateWindows(); + this.updateIcon(); + }, +@@ -1039,6 +1052,79 @@ var taskbarAppIcon = Utils.defineClass({ + this._previewMenu.update(this, windows); + }, + ++ _updateTrackedWindows: function() { ++ const windows = this.window ? [this.window] : 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: function(window) { ++ if (this._windows.has(window)) ++ return; ++ ++ this._windowSignals.set(window, [ ++ window.connect('notify::urgent', () => this._updateNeedsAttention()), ++ window.connect('notify::demands-attention', () => this._updateNeedsAttention()), ++ ]); ++ this._windows.add(window); ++ }, ++ ++ _untrackWindow: function(window) { ++ if (!this._windows.delete(window)) ++ return; ++ ++ for (let id of this._windowSignals.get(window)) ++ window.disconnect(id); ++ this._windowSignals.delete(window); ++ }, ++ ++ _updateNeedsAttention: function() { ++ const needsAttention = ++ [...this._windows].some(w => w.urgent || w.demands_attention); ++ ++ if (this._flashEffect.enabled === needsAttention) ++ return; ++ ++ this._flashEffect.enabled = needsAttention; ++ ++ if (needsAttention) { ++ const flashColor = new Clutter.Color({ ++ red: 177, ++ green: 177, ++ blue: 228, ++ }); ++ this._flashEffect.set_brightness(0) ++ const flashTransition = new Clutter.PropertyTransition({ ++ property_name: '@effects.attention-flash.brightness', ++ interval: new Clutter.Interval({value_type: Clutter.Color}), ++ duration: 1500, ++ repeat_count: -1, ++ auto_reverse: true, ++ progress_mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, ++ }); ++ this.icon.add_transition('@effects.attention-flash.brightness', flashTransition); ++ flashTransition.set_to(flashColor); ++ ++ this.icon.translation_y = 0; ++ const bumpTransition = new Clutter.PropertyTransition({ ++ property_name: 'translation-y', ++ interval: new Clutter.Interval({value_type: GObject.TYPE_DOUBLE}), ++ duration: 500, ++ repeat_count: -1, ++ auto_reverse: true, ++ progress_mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, ++ }); ++ this.icon.add_transition('translation-y', bumpTransition); ++ bumpTransition.set_to(-3); ++ } else { ++ this.icon.remove_all_transitions(); ++ this.icon.translation_y = 0; ++ } ++ }, ++ + _getRunningIndicatorCount: function() { + return Math.min(this._nWindows, MAX_INDICATORS); + }, +-- +2.49.0 + diff --git a/gnome-shell-extensions.spec b/gnome-shell-extensions.spec index a01cd1c..6416204 100644 --- a/gnome-shell-extensions.spec +++ b/gnome-shell-extensions.spec @@ -6,7 +6,7 @@ Name: gnome-shell-extensions Version: 3.32.1 -Release: 41%{?dist} +Release: 42%{?dist} Summary: Modify and extend GNOME Shell functionality and behavior Group: User Interface/Desktops @@ -62,6 +62,7 @@ Patch0033: 0001-classification-banner-Hide-from-picks.patch Patch0034: 0001-desktop-icons-Fix-k-in-.desktop-files.patch Patch0035: window-list-attention-indicator.patch Patch0036: apps-menu-custom-layout-manager.patch +Patch0037: dash-to-panel-attention-indicator.patch %description GNOME Shell Extensions is a collection of extensions providing additional and @@ -576,6 +577,10 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions %changelog +* Tue May 06 2025 Florian Müllner - 3.32.1-42 +- Indicate urgency-hint in dash-to-panel + Resolves: RHEL-76834 + * Tue May 06 2025 Florian Müllner - 3.32.1-41 - Use custom layout manager in apps menu Resolves: RHEL-14936