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>
This commit is contained in:
Ivan Molodetskikh 2022-02-08 19:18:01 +03:00 committed by Marge Bot
parent 33cf163f95
commit 6ec8480052
4 changed files with 110 additions and 3 deletions

View File

@ -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 { &:active, &:overview, &:focus, &:checked {
box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.8); 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); 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 { &: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); 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 { &: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); 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 // 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); 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 { &: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); 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 { &: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); 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);
}
} }
} }
} }

View File

@ -468,6 +468,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'a11y': imports.ui.status.accessibility.ATIndicator, 'a11y': imports.ui.status.accessibility.ATIndicator,
'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,
}; };
var Panel = GObject.registerClass( var Panel = GObject.registerClass(

View File

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

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- 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 Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
@ -106,3 +106,73 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
this._sync(); 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;
}
}
});