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>
This commit is contained in:
Florian Müllner 2022-07-27 12:29:26 +02:00 committed by Marge Bot
parent 4f155d3757
commit 7ab7df739a
4 changed files with 137 additions and 89 deletions

View File

@ -40,7 +40,14 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
&.screen-recording-indicator { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px $screenshot_ui_button_red; 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 { StBoxLayout {
spacing: $base_padding; spacing: $base_padding;
} }
@ -66,6 +73,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
&.screen-recording-indicator { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15); 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 { &:hover {
@ -80,6 +90,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
&.screen-recording-indicator { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1); 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 { &: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 { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2); 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 // status area icons
@ -137,6 +153,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
&.screen-recording-indicator { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15); 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 { &:hover {
@ -151,6 +170,9 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
&.screen-recording-indicator { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1); 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 { &: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 { &.screen-recording-indicator {
box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2); 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);
}
} }
} }
} }

View File

@ -441,6 +441,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'keyboard': imports.ui.status.keyboard.InputSourceIndicator, 'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator, 'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator,
'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator, 'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator,
'screenSharing': imports.ui.status.remoteAccess.ScreenSharingIndicator,
}; };
var Panel = GObject.registerClass( var Panel = GObject.registerClass(

View File

@ -94,7 +94,7 @@ const _modes = {
panel: { panel: {
left: ['activities', 'appMenu'], left: ['activities', 'appMenu'],
center: ['dateMenu'], center: ['dateMenu'],
right: ['screenRecording', 'dwellClick', 'a11y', 'keyboard', 'aggregateMenu'], right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'aggregateMenu'],
}, },
}, },
}; };

View File

@ -1,11 +1,10 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- 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 { Atk, Clutter, GLib, GObject, Meta, St } = imports.gi;
const Main = imports.ui.main; const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
// Minimum amount of time the shared indicator is visible (in micro seconds) // Minimum amount of time the shared indicator is visible (in micro seconds)
const MIN_SHARED_INDICATOR_VISIBLE_TIME_US = 5 * GLib.TIME_SPAN_SECOND; const MIN_SHARED_INDICATOR_VISIBLE_TIME_US = 5 * GLib.TIME_SPAN_SECOND;
@ -21,97 +20,30 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
return; return;
this._handles = new Set(); this._handles = new Set();
this._sharedIndicator = null;
this._recordingIndicator = null; this._indicator = this._addIndicator();
this._menuSection = null; this._indicator.set({
style_class: 'screencast-indicator',
icon_name: 'media-record-symbolic',
});
controller.connect('new-handle', (o, handle) => { controller.connect('new-handle', (o, handle) => {
this._onNewHandle(handle); this._onNewHandle(handle);
}); });
} this._sync();
_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);
} }
_isRecording() { _isRecording() {
const recordingHandles =
[...this._handles].filter(handle => handle.is_recording);
// Screenshot UI screencasts have their own panel, so don't show this // Screenshot UI screencasts have their own panel, so don't show this
// indicator if there's only a screenshot UI screencast. // indicator if there's only a screenshot UI screencast.
if (Main.screenshotUI.screencast_in_progress) if (Main.screenshotUI.screencast_in_progress)
return recordingHandles.length > 1; return this._handles.size > 1;
return recordingHandles.length > 0; return this._handles.size > 0;
}
_hideSharedIndicator() {
this._sharedIndicator.visible = false;
delete this._hideSharedIndicatorId;
return GLib.SOURCE_REMOVE;
} }
_sync() { _sync() {
if (this._hideSharedIndicatorId) { this._indicator.visible = this._isRecording();
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();
} }
_onStopped(handle) { _onStopped(handle) {
@ -120,20 +52,12 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
} }
_onNewHandle(handle) { _onNewHandle(handle) {
// We can't possibly know about all types of screen sharing on X11, so if (!handle.is_recording)
// 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)
return; return;
this._handles.add(handle); this._handles.add(handle);
handle.connect('stopped', this._onStopped.bind(this)); handle.connect('stopped', this._onStopped.bind(this));
this._ensureControls();
this._sync(); 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());
}
}
}
});