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:
parent
4f155d3757
commit
7ab7df739a
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user