diff --git a/data/theme/gnome-shell-sass/widgets/_panel.scss b/data/theme/gnome-shell-sass/widgets/_panel.scss index 7e577e828..feeb1a7a2 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 ea40998a5..78064f73f 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 92d91fc60..7d83ddb6b 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 d6d500cae..a63b257c5 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; + } + } +});