[gnome-shell] status/remote-access: Split out screen sharing indicator



commit 7ab7df739aaee5922bd3d4a980f790fd80062363
Author: Florian Müllner <fmuellner gnome org>
Date:   Wed Jul 27 12:29:26 2022 +0200

    status/remote-access: Split out screen sharing indicator
    
    The latest mockups move the screen sharing indicator into a
    separate control, similar to the existing indicator for built-in
    screen recordings.
    
    As this removes the submenu and only keeps the top bar icon (for
    external screen recordings), this will smooth the transition to
    quick settings.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>

 data/theme/gnome-shell-sass/widgets/_panel.scss |  25 +++
 js/ui/panel.js                                  |   1 +
 js/ui/sessionMode.js                            |   2 +-
 js/ui/status/remoteAccess.js                    | 198 +++++++++++++-----------
 4 files changed, 137 insertions(+), 89 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_panel.scss b/data/theme/gnome-shell-sass/widgets/_panel.scss
index 3fc57eb082..fd3e7d2dab 100644
--- a/data/theme/gnome-shell-sass/widgets/_panel.scss
+++ b/data/theme/gnome-shell-sass/widgets/_panel.scss
@@ -40,7 +40,14 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
 
     &.screen-recording-indicator {
       box-shadow: inset 0 0 0 100px $screenshot_ui_button_red;
+    }
+    &.screen-sharing-indicator {
+      box-shadow: inset 0 0 0 100px $warning_color;
+      StBoxLayout { margin: 0 $base_padding; }
+    }
 
+    &.screen-recording-indicator,
+    &.screen-sharing-indicator {
       StBoxLayout {
         spacing: $base_padding;
       }
@@ -66,6 +73,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
       &.screen-recording-indicator {
         box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15);
       }
+      &.screen-sharing-indicator {
+        box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.15);
+      }
     }
 
     &:hover {
@@ -80,6 +90,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
       &.screen-recording-indicator {
         box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1);
       }
+      &.screen-sharing-indicator {
+        box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.1);
+      }
     }
     
     &:active:hover, &:overview:hover, &:focus:hover, &:checked:hover {
@@ -94,6 +107,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
       &.screen-recording-indicator {
         box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2);
       }
+      &.screen-sharing-indicator {
+        box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.2);
+      }
     }
 
     // status area icons
@@ -137,6 +153,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
         &.screen-recording-indicator {
           box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15);
         }
+        &.screen-sharing-indicator {
+          box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.15);
+        }
       }
 
       &:hover {
@@ -151,6 +170,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
         &.screen-recording-indicator {
           box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1);
         }
+        &.screen-sharing-indicator {
+          box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.1);
+        }
       }
       
       &:active:hover, &:overview:hover, &:focus:hover, &:checked:hover {
@@ -165,6 +187,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
         &.screen-recording-indicator {
           box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2);
         }
+        &.screen-sharing-indicator {
+          box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.2);
+        }
       }
     }
   }
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 5bf469b69c..992cdd8e93 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -441,6 +441,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
     'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
     'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator,
     'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator,
+    'screenSharing': imports.ui.status.remoteAccess.ScreenSharingIndicator,
 };
 
 var Panel = GObject.registerClass(
diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
index 5712b4d248..acf584d8c9 100644
--- a/js/ui/sessionMode.js
+++ b/js/ui/sessionMode.js
@@ -94,7 +94,7 @@ const _modes = {
         panel: {
             left: ['activities', 'appMenu'],
             center: ['dateMenu'],
-            right: ['screenRecording', 'dwellClick', 'a11y', 'keyboard', 'aggregateMenu'],
+            right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'aggregateMenu'],
         },
     },
 };
diff --git a/js/ui/status/remoteAccess.js b/js/ui/status/remoteAccess.js
index a3dfd652c2..08436d7d11 100644
--- a/js/ui/status/remoteAccess.js
+++ b/js/ui/status/remoteAccess.js
@@ -1,11 +1,10 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported RemoteAccessApplet, ScreenRecordingIndicator */
+/* exported RemoteAccessApplet, ScreenRecordingIndicator, ScreenSharingIndicator */
 
 const { Atk, Clutter, GLib, GObject, Meta, St } = imports.gi;
 
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
-const PopupMenu = imports.ui.popupMenu;
 
 // Minimum amount of time the shared indicator is visible (in micro seconds)
 const MIN_SHARED_INDICATOR_VISIBLE_TIME_US = 5 * GLib.TIME_SPAN_SECOND;
@@ -21,97 +20,30 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
             return;
 
         this._handles = new Set();
-        this._sharedIndicator = null;
-        this._recordingIndicator = null;
-        this._menuSection = null;
+
+        this._indicator = this._addIndicator();
+        this._indicator.set({
+            style_class: 'screencast-indicator',
+            icon_name: 'media-record-symbolic',
+        });
 
         controller.connect('new-handle', (o, handle) => {
             this._onNewHandle(handle);
         });
-    }
-
-    _ensureControls() {
-        if (this._sharedIndicator && this._recordingIndicator)
-            return;
-
-        this._sharedIndicator = this._addIndicator();
-        this._sharedIndicator.visible = false;
-        this._sharedIndicator.icon_name = 'screen-shared-symbolic';
-        this._sharedIndicator.add_style_class_name('remote-access-indicator');
-
-        this._sharedItem =
-            new PopupMenu.PopupSubMenuMenuItem(_("Screen is Being Shared"),
-                                               true);
-        this._sharedItem.menu.addAction(_("Turn off"),
-            () => {
-                for (let handle of this._handles) {
-                    if (!handle.is_recording)
-                        handle.stop();
-                }
-            });
-        this._sharedItem.icon.icon_name = 'screen-shared-symbolic';
-        this.menu.addMenuItem(this._sharedItem);
-
-        this._recordingIndicator = this._addIndicator();
-        this._recordingIndicator.icon_name = 'media-record-symbolic';
-        this._recordingIndicator.add_style_class_name('screencast-indicator');
-    }
-
-    _isScreenShared() {
-        return [...this._handles].some(handle => !handle.is_recording);
+        this._sync();
     }
 
     _isRecording() {
-        const recordingHandles =
-            [...this._handles].filter(handle => handle.is_recording);
-
         // Screenshot UI screencasts have their own panel, so don't show this
         // indicator if there's only a screenshot UI screencast.
         if (Main.screenshotUI.screencast_in_progress)
-            return recordingHandles.length > 1;
+            return this._handles.size > 1;
 
-        return recordingHandles.length > 0;
-    }
-
-    _hideSharedIndicator() {
-        this._sharedIndicator.visible = false;
-        delete this._hideSharedIndicatorId;
-        return GLib.SOURCE_REMOVE;
+        return this._handles.size > 0;
     }
 
     _sync() {
-        if (this._hideSharedIndicatorId) {
-            GLib.source_remove(this._hideSharedIndicatorId);
-            delete this._hideSharedIndicatorId;
-        }
-
-        if (this._isScreenShared()) {
-            if (!this._sharedIndicator.visible)
-                this._visibleTimeUs = GLib.get_monotonic_time();
-            this._sharedIndicator.visible = true;
-            this._sharedItem.visible = true;
-        } else {
-            if (this._sharedIndicator.visible) {
-                const currentTimeUs = GLib.get_monotonic_time();
-                const timeSinceVisibleUs = currentTimeUs - this._visibleTimeUs;
-
-                if (timeSinceVisibleUs >= MIN_SHARED_INDICATOR_VISIBLE_TIME_US) {
-                    this._hideSharedIndicator();
-                } else {
-                    const timeUntilHideUs =
-                        MIN_SHARED_INDICATOR_VISIBLE_TIME_US - timeSinceVisibleUs;
-                    this._hideSharedIndicatorId =
-                        GLib.timeout_add(
-                            GLib.PRIORITY_DEFAULT,
-                            timeUntilHideUs / GLib.TIME_SPAN_MILLISECOND,
-                            this._hideSharedIndicator.bind(this));
-                }
-            }
-
-            this._sharedItem.visible = false;
-        }
-
-        this._recordingIndicator.visible = this._isRecording();
+        this._indicator.visible = this._isRecording();
     }
 
     _onStopped(handle) {
@@ -120,20 +52,12 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
     }
 
     _onNewHandle(handle) {
-        // We can't possibly know about all types of screen sharing on X11, so
-        // showing these controls on X11 might give a false sense of security.
-        // Thus, only enable these controls when using Wayland, where we are
-        // in control of sharing.
-        //
-        // We still want to show screen recordings though, to indicate when
-        // the built in screen recorder is active, no matter the session type.
-        if (!Meta.is_wayland_compositor() && !handle.is_recording)
+        if (!handle.is_recording)
             return;
 
         this._handles.add(handle);
         handle.connect('stopped', this._onStopped.bind(this));
 
-        this._ensureControls();
         this._sync();
     }
 });
@@ -207,3 +131,101 @@ var ScreenRecordingIndicator = GObject.registerClass({
         }
     }
 });
+
+var ScreenSharingIndicator = GObject.registerClass({
+    Signals: {'menu-set': {}},
+}, class ScreenSharingIndicator extends PanelMenu.ButtonBox {
+    _init() {
+        super._init({
+            reactive: true,
+            can_focus: true,
+            track_hover: true,
+            accessible_name: _('Stop Screen Sharing'),
+            accessible_role: Atk.Role.PUSH_BUTTON,
+        });
+        this.add_style_class_name('screen-sharing-indicator');
+
+        this._box = new St.BoxLayout();
+        this.add_child(this._box);
+
+        let icon = new St.Icon({icon_name: 'screen-shared-symbolic'});
+        this._box.add_child(icon);
+
+        icon = new St.Icon({icon_name: 'window-close-symbolic'});
+        this._box.add_child(icon);
+
+        this._controller = global.backend.get_remote_access_controller();
+
+        this._handles = new Set();
+
+        this._controller?.connect('new-handle',
+            (o, handle) => this._onNewHandle(handle));
+
+        this._sync();
+    }
+
+    _onNewHandle(handle) {
+        // We can't possibly know about all types of screen sharing on X11, so
+        // showing these controls on X11 might give a false sense of security.
+        // Thus, only enable these controls when using Wayland, where we are
+        // in control of sharing.
+        if (!Meta.is_wayland_compositor())
+            return;
+
+        if (handle.isRecording)
+            return;
+
+        this._handles.add(handle);
+        handle.connect('stopped', () => {
+            this._handles.delete(handle);
+            this._sync();
+        });
+        this._sync();
+    }
+
+    vfunc_event(event) {
+        if (event.type() === Clutter.EventType.TOUCH_BEGIN ||
+            event.type() === Clutter.EventType.BUTTON_PRESS)
+            this._stopSharing();
+
+        return Clutter.EVENT_PROPAGATE;
+    }
+
+    _stopSharing() {
+        for (const handle of this._handles)
+            handle.stop();
+    }
+
+    _hideIndicator() {
+        this.hide();
+        delete this._hideIndicatorId;
+        return GLib.SOURCE_REMOVE;
+    }
+
+    _sync() {
+        if (this._hideIndicatorId) {
+            GLib.source_remove(this._hideIndicatorId);
+            delete this._hideIndicatorId;
+        }
+
+        if (this._handles.size > 0) {
+            if (!this.visible)
+                this._visibleTimeUs = GLib.get_monotonic_time();
+            this.show();
+        } else if (this.visible) {
+            const currentTimeUs = GLib.get_monotonic_time();
+            const timeSinceVisibleUs = currentTimeUs - this._visibleTimeUs;
+
+            if (timeSinceVisibleUs >= MIN_SHARED_INDICATOR_VISIBLE_TIME_US) {
+                this._hideIndicator();
+            } else {
+                const timeUntilHideUs =
+                    MIN_SHARED_INDICATOR_VISIBLE_TIME_US - timeSinceVisibleUs;
+                this._hideIndicatorId =
+                    GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+                        timeUntilHideUs / GLib.TIME_SPAN_MILLISECOND,
+                        () => this._hideIndicator());
+            }
+        }
+    }
+});


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