[gnome-shell] panel: Add a screen recording indicator



commit 6ec84800524a431d70c7bb5901c2c70f78ebd539
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Tue Feb 8 19:18:01 2022 +0300

    panel: Add a screen recording indicator
    
    The indicator shows the recording duration and lets the user stop it on
    click. It is more discoverable than the stop entry in the aggregate
    menu.
    
    The class extends ButtonBox directly rather than Button because Button
    does nothing that it uses, and actually causes issues with its dummy
    menu (its vfunc_hide() throws an "open-state-changed: Error: incorrect
    pop").
    
    The menu-set signal declaration is required by the panel.
    
    The screencast is stopped upon button press in vfunc_event(), which
    matches PanelMenu.Button's input handling.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2163>

 data/theme/gnome-shell-sass/widgets/_panel.scss | 36 ++++++++++++
 js/ui/panel.js                                  |  1 +
 js/ui/sessionMode.js                            |  2 +-
 js/ui/status/remoteAccess.js                    | 74 ++++++++++++++++++++++++-
 4 files changed, 110 insertions(+), 3 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_panel.scss b/data/theme/gnome-shell-sass/widgets/_panel.scss
index 7e577e828c..feeb1a7a24 100644
--- a/data/theme/gnome-shell-sass/widgets/_panel.scss
+++ b/data/theme/gnome-shell-sass/widgets/_panel.scss
@@ -38,6 +38,18 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
       }
     }
 
+    &.screen-recording-indicator {
+      box-shadow: inset 0 0 0 100px $screenshot_ui_button_red;
+
+      StBoxLayout {
+        spacing: $base_spacing;
+      }
+
+      StIcon {
+        icon-size: $base_icon_size;
+      }
+    }
+
     &:active, &:overview, &:focus, &:checked {
       box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.8);
 
@@ -50,6 +62,10 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
           box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.8);
         }
       }
+
+      &.screen-recording-indicator {
+        box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15);
+      }
     }
 
     &:hover {
@@ -60,6 +76,10 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
           box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.85);
         }
       }
+
+      &.screen-recording-indicator {
+        box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1);
+      }
     }
     
     &:active:hover, &:overview:hover, &:focus:hover, &:checked:hover {
@@ -70,6 +90,10 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
           box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.75);
         }
       }
+
+      &.screen-recording-indicator {
+        box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2);
+      }
     }
 
     // status area icons
@@ -109,6 +133,10 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
             box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.15);
           }
         }
+
+        &.screen-recording-indicator {
+          box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15);
+        }
       }
 
       &:hover {
@@ -119,6 +147,10 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
             box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.10);
           }
         }
+
+        &.screen-recording-indicator {
+          box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1);
+        }
       }
       
       &:active:hover, &:overview:hover, &:focus:hover, &:checked:hover {
@@ -129,6 +161,10 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
             box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.2);
           }
         }
+
+        &.screen-recording-indicator {
+          box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2);
+        }
       }
     }
   }
diff --git a/js/ui/panel.js b/js/ui/panel.js
index ea40998a51..78064f73f3 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -468,6 +468,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
     'a11y': imports.ui.status.accessibility.ATIndicator,
     'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
     'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator,
+    'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator,
 };
 
 var Panel = GObject.registerClass(
diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
index 92d91fc604..7d83ddb6be 100644
--- a/js/ui/sessionMode.js
+++ b/js/ui/sessionMode.js
@@ -91,7 +91,7 @@ const _modes = {
         panel: {
             left: ['activities', 'appMenu'],
             center: ['dateMenu'],
-            right: ['dwellClick', 'a11y', 'keyboard', 'aggregateMenu'],
+            right: ['screenRecording', 'dwellClick', 'a11y', 'keyboard', 'aggregateMenu'],
         },
     },
 };
diff --git a/js/ui/status/remoteAccess.js b/js/ui/status/remoteAccess.js
index d6d500caed..a63b257c50 100644
--- a/js/ui/status/remoteAccess.js
+++ b/js/ui/status/remoteAccess.js
@@ -1,7 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported RemoteAccessApplet */
+/* exported RemoteAccessApplet, ScreenRecordingIndicator */
 
-const { GObject, Meta } = imports.gi;
+const { Atk, Clutter, GLib, GObject, Meta, St } = imports.gi;
 
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
@@ -106,3 +106,73 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
         this._sync();
     }
 });
+
+var ScreenRecordingIndicator = GObject.registerClass({
+    Signals: { 'menu-set': {} },
+}, class ScreenRecordingIndicator extends PanelMenu.ButtonBox {
+    _init() {
+        super._init({
+            reactive: true,
+            can_focus: true,
+            track_hover: true,
+            accessible_name: _('Stop Screencast'),
+            accessible_role: Atk.Role.PUSH_BUTTON,
+        });
+        this.add_style_class_name('screen-recording-indicator');
+
+        this._box = new St.BoxLayout();
+        this.add_child(this._box);
+
+        this._label = new St.Label({
+            text: '0:00',
+            y_align: Clutter.ActorAlign.CENTER,
+        });
+        this._box.add_child(this._label);
+
+        this._icon = new St.Icon({ icon_name: 'stop-symbolic' });
+        this._box.add_child(this._icon);
+
+        this.hide();
+        Main.screenshotUI.connect(
+            'notify::screencast-in-progress',
+            this._onScreencastInProgressChanged.bind(this));
+    }
+
+    vfunc_event(event) {
+        if (event.type() === Clutter.EventType.TOUCH_BEGIN ||
+            event.type() === Clutter.EventType.BUTTON_PRESS)
+            Main.screenshotUI.stopScreencast();
+
+        return Clutter.EVENT_PROPAGATE;
+    }
+
+    _updateLabel() {
+        const minutes = this._secondsPassed / 60;
+        const seconds = this._secondsPassed % 60;
+        this._label.text = '%d:%02d'.format(minutes, seconds);
+    }
+
+    _onScreencastInProgressChanged() {
+        if (Main.screenshotUI.screencast_in_progress) {
+            this.show();
+
+            this._secondsPassed = 0;
+            this._updateLabel();
+
+            this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
+                this._secondsPassed += 1;
+                this._updateLabel();
+                return GLib.SOURCE_CONTINUE;
+            });
+            GLib.Source.set_name_by_id(
+                this._timeoutId, '[gnome-shell] screen recording indicator tick');
+        } else {
+            this.hide();
+
+            GLib.source_remove(this._timeoutId);
+            delete this._timeoutId;
+
+            delete this._secondsPassed;
+        }
+    }
+});


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]