2011-09-28 13:16:26 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2023-07-10 09:53:00 +00:00
|
|
|
|
|
|
|
import Atk from 'gi://Atk';
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import GObject from 'gi://GObject';
|
panel: Add workspaces indicators in activities button
After removing the app name and icon, the next natural step that
was requested from the design team is to add workspaces indicators
to the top bar, where currently the Activities button is placed.
In addition to that, this is desired because there are known issues
with using "Activities" as a label for the overview. A more
comprehensive rationale for that can be found at [1].
Add an workspaces indicator replacing the Activities label in the
activities button.
The WorkspaceIndicators class controls how many workspaces dots
exists, their expansion, and the width multiplier. The WorkspaceDot
class takes the expansion and the multiplier, and applies it
internally so that we can get perfectly rounded dots at all
times without using CSS hacks.
The width multipliers are hardcoded, and defined by the design
team. We can revisit them later if necessary. Special care is
taken to not let these width multipliers result in fractional
widths.
When the number of workspaces changes, WorkspaceIndicators adds
new dot to the end, and animate them. When removing, scale the dot
out, then destroy it.
This does not work with workspace grids, but that's not supported
by GNOME Shell anyway, so no effort is made to cover this use case.
The button continues to have "Activities" as its accessible name,
but the label actor is removed.
Also adjust the padding of the activities pill, so it better wraps
the new indicators.
[1] https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/227
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2902>
2023-08-10 13:27:13 +00:00
|
|
|
import Graphene from 'gi://Graphene';
|
2023-07-10 09:53:00 +00:00
|
|
|
import Meta from 'gi://Meta';
|
|
|
|
import Shell from 'gi://Shell';
|
|
|
|
import St from 'gi://St';
|
|
|
|
|
|
|
|
import * as Animation from './animation.js';
|
|
|
|
import {AppMenu} from './appMenu.js';
|
2023-08-07 15:30:22 +00:00
|
|
|
import * as Config from '../misc/config.js';
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as CtrlAltTab from './ctrlAltTab.js';
|
|
|
|
import * as DND from './dnd.js';
|
|
|
|
import * as Overview from './overview.js';
|
|
|
|
import * as PopupMenu from './popupMenu.js';
|
|
|
|
import * as PanelMenu from './panelMenu.js';
|
|
|
|
import {QuickSettingsMenu, SystemIndicator} from './quickSettings.js';
|
|
|
|
import * as Main from './main.js';
|
panel: Add workspaces indicators in activities button
After removing the app name and icon, the next natural step that
was requested from the design team is to add workspaces indicators
to the top bar, where currently the Activities button is placed.
In addition to that, this is desired because there are known issues
with using "Activities" as a label for the overview. A more
comprehensive rationale for that can be found at [1].
Add an workspaces indicator replacing the Activities label in the
activities button.
The WorkspaceIndicators class controls how many workspaces dots
exists, their expansion, and the width multiplier. The WorkspaceDot
class takes the expansion and the multiplier, and applies it
internally so that we can get perfectly rounded dots at all
times without using CSS hacks.
The width multipliers are hardcoded, and defined by the design
team. We can revisit them later if necessary. Special care is
taken to not let these width multipliers result in fractional
widths.
When the number of workspaces changes, WorkspaceIndicators adds
new dot to the end, and animate them. When removing, scale the dot
out, then destroy it.
This does not work with workspace grids, but that's not supported
by GNOME Shell anyway, so no effort is made to cover this use case.
The button continues to have "Activities" as its accessible name,
but the label actor is removed.
Also adjust the padding of the activities pill, so it better wraps
the new indicators.
[1] https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/227
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2902>
2023-08-10 13:27:13 +00:00
|
|
|
import * as Util from '../misc/util.js';
|
2023-07-10 09:53:00 +00:00
|
|
|
|
|
|
|
import * as RemoteAccessStatus from './status/remoteAccess.js';
|
|
|
|
import * as PowerProfileStatus from './status/powerProfiles.js';
|
|
|
|
import * as RFKillStatus from './status/rfkill.js';
|
|
|
|
import * as CameraStatus from './status/camera.js';
|
|
|
|
import * as VolumeStatus from './status/volume.js';
|
|
|
|
import * as BrightnessStatus from './status/brightness.js';
|
|
|
|
import * as SystemStatus from './status/system.js';
|
|
|
|
import * as LocationStatus from './status/location.js';
|
|
|
|
import * as NightLightStatus from './status/nightLight.js';
|
|
|
|
import * as DarkModeStatus from './status/darkMode.js';
|
|
|
|
import * as BacklightStatus from './status/backlight.js';
|
|
|
|
import * as ThunderboltStatus from './status/thunderbolt.js';
|
|
|
|
import * as AutoRotateStatus from './status/autoRotate.js';
|
|
|
|
import * as BackgroundAppsStatus from './status/backgroundApps.js';
|
|
|
|
|
|
|
|
import {DateMenuButton} from './dateMenu.js';
|
|
|
|
import {ATIndicator} from './status/accessibility.js';
|
|
|
|
import {InputSourceIndicator} from './status/keyboard.js';
|
|
|
|
import {DwellClickIndicator} from './status/dwellClick.js';
|
|
|
|
import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js';
|
|
|
|
|
|
|
|
const PANEL_ICON_SIZE = 16;
|
|
|
|
const APP_MENU_ICON_MARGIN = 0;
|
|
|
|
|
|
|
|
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
2011-01-05 14:47:27 +00:00
|
|
|
|
2022-07-24 17:49:58 +00:00
|
|
|
const N_QUICK_SETTINGS_COLUMNS = 2;
|
|
|
|
|
panel: Add workspaces indicators in activities button
After removing the app name and icon, the next natural step that
was requested from the design team is to add workspaces indicators
to the top bar, where currently the Activities button is placed.
In addition to that, this is desired because there are known issues
with using "Activities" as a label for the overview. A more
comprehensive rationale for that can be found at [1].
Add an workspaces indicator replacing the Activities label in the
activities button.
The WorkspaceIndicators class controls how many workspaces dots
exists, their expansion, and the width multiplier. The WorkspaceDot
class takes the expansion and the multiplier, and applies it
internally so that we can get perfectly rounded dots at all
times without using CSS hacks.
The width multipliers are hardcoded, and defined by the design
team. We can revisit them later if necessary. Special care is
taken to not let these width multipliers result in fractional
widths.
When the number of workspaces changes, WorkspaceIndicators adds
new dot to the end, and animate them. When removing, scale the dot
out, then destroy it.
This does not work with workspace grids, but that's not supported
by GNOME Shell anyway, so no effort is made to cover this use case.
The button continues to have "Activities" as its accessible name,
but the label actor is removed.
Also adjust the padding of the activities pill, so it better wraps
the new indicators.
[1] https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/227
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2902>
2023-08-10 13:27:13 +00:00
|
|
|
const INACTIVE_WORKSPACE_DOT_SCALE = 0.75;
|
|
|
|
|
2009-12-26 17:00:36 +00:00
|
|
|
/**
|
2010-05-05 14:03:48 +00:00
|
|
|
* AppMenuButton:
|
2009-12-26 17:00:36 +00:00
|
|
|
*
|
|
|
|
* This class manages the "application menu" component. It tracks the
|
|
|
|
* currently focused application. However, when an app is launched,
|
|
|
|
* this menu also handles startup notification for it. So when we
|
|
|
|
* have an active startup notification, we switch modes to display that.
|
|
|
|
*/
|
2023-07-10 09:53:00 +00:00
|
|
|
const AppMenuButton = GObject.registerClass({
|
|
|
|
Signals: {'changed': {}},
|
2017-10-31 01:23:39 +00:00
|
|
|
}, class AppMenuButton extends PanelMenu.Button {
|
2017-10-31 00:03:21 +00:00
|
|
|
_init(panel) {
|
2017-10-31 01:23:39 +00:00
|
|
|
super._init(0.0, null, true);
|
2011-05-15 16:55:23 +00:00
|
|
|
|
2019-04-09 23:17:51 +00:00
|
|
|
this.accessible_role = Atk.Role.MENU;
|
2012-02-27 16:31:10 +00:00
|
|
|
|
2010-10-06 21:31:22 +00:00
|
|
|
this._startingApps = [];
|
2009-08-11 15:32:58 +00:00
|
|
|
|
2012-08-28 21:10:44 +00:00
|
|
|
this._menuManager = panel.menuManager;
|
2010-10-06 21:31:22 +00:00
|
|
|
this._targetApp = null;
|
2009-08-11 15:32:58 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
let bin = new St.Bin({name: 'appMenu'});
|
2019-04-09 23:17:51 +00:00
|
|
|
this.add_actor(bin);
|
2011-03-08 17:46:43 +00:00
|
|
|
|
2023-08-06 22:34:20 +00:00
|
|
|
this.bind_property('reactive', this, 'can-focus', 0);
|
2019-04-09 23:17:51 +00:00
|
|
|
this.reactive = false;
|
2011-03-08 17:46:43 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._container = new St.BoxLayout({style_class: 'panel-status-menu-box'});
|
2010-05-05 14:03:48 +00:00
|
|
|
bin.set_child(this._container);
|
2010-03-12 20:57:01 +00:00
|
|
|
|
2012-10-30 17:30:21 +00:00
|
|
|
let textureCache = St.TextureCache.get_default();
|
|
|
|
textureCache.connect('icon-theme-changed',
|
2023-08-06 23:45:22 +00:00
|
|
|
this._onIconThemeChanged.bind(this));
|
2012-10-30 17:30:21 +00:00
|
|
|
|
2019-02-04 23:30:41 +00:00
|
|
|
let iconEffect = new Clutter.DesaturateEffect();
|
2020-05-07 12:25:13 +00:00
|
|
|
this._iconBox = new St.Bin({
|
|
|
|
style_class: 'app-menu-icon',
|
|
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2019-02-04 23:30:41 +00:00
|
|
|
this._iconBox.add_effect(iconEffect);
|
2010-02-28 20:36:13 +00:00
|
|
|
this._container.add_actor(this._iconBox);
|
2013-08-12 12:36:38 +00:00
|
|
|
|
2019-02-04 23:30:41 +00:00
|
|
|
this._iconBox.connect('style-changed', () => {
|
|
|
|
let themeNode = this._iconBox.get_theme_node();
|
2023-08-07 00:51:19 +00:00
|
|
|
iconEffect.enabled = themeNode.get_icon_style() === St.IconStyle.SYMBOLIC;
|
2019-02-04 23:30:41 +00:00
|
|
|
});
|
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
this._label = new St.Label({
|
|
|
|
y_expand: true,
|
|
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2015-02-18 20:39:33 +00:00
|
|
|
this._container.add_actor(this._label);
|
2011-03-29 09:49:50 +00:00
|
|
|
|
2018-10-04 14:52:34 +00:00
|
|
|
this._visible = !Main.overview.visible;
|
2010-05-12 21:30:14 +00:00
|
|
|
if (!this._visible)
|
2018-07-06 08:48:15 +00:00
|
|
|
this.hide();
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.connectObject(
|
|
|
|
'hiding', this._sync.bind(this),
|
|
|
|
'showing', this._sync.bind(this), this);
|
2009-08-11 15:32:58 +00:00
|
|
|
|
2019-11-18 20:18:29 +00:00
|
|
|
this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, {
|
|
|
|
animate: true,
|
2019-11-18 20:25:59 +00:00
|
|
|
hideOnStop: true,
|
2019-11-18 20:18:29 +00:00
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this._container.add_actor(this._spinner);
|
2010-06-10 12:07:33 +00:00
|
|
|
|
2018-10-04 14:52:34 +00:00
|
|
|
let menu = new AppMenu(this);
|
|
|
|
this.setMenu(menu);
|
|
|
|
this._menuManager.addMenu(menu);
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Shell.WindowTracker.get_default().connectObject('notify::focus-app',
|
|
|
|
this._focusAppChanged.bind(this), this);
|
|
|
|
Shell.AppSystem.get_default().connectObject('app-state-changed',
|
|
|
|
this._onAppStateChanged.bind(this), this);
|
|
|
|
global.window_manager.connectObject('switch-workspace',
|
|
|
|
this._sync.bind(this), this);
|
2010-10-06 21:31:22 +00:00
|
|
|
|
2009-08-11 15:32:58 +00:00
|
|
|
this._sync();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2010-05-12 21:30:14 +00:00
|
|
|
|
2018-08-21 09:48:19 +00:00
|
|
|
fadeIn() {
|
2013-01-27 14:45:04 +00:00
|
|
|
if (this._visible)
|
2010-05-12 21:30:14 +00:00
|
|
|
return;
|
|
|
|
|
2011-02-16 20:17:46 +00:00
|
|
|
this._visible = true;
|
2019-04-09 23:17:51 +00:00
|
|
|
this.reactive = true;
|
2018-07-20 19:46:19 +00:00
|
|
|
this.remove_all_transitions();
|
|
|
|
this.ease({
|
|
|
|
opacity: 255,
|
|
|
|
duration: Overview.ANIMATION_TIME,
|
2019-08-20 21:43:54 +00:00
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
2018-07-20 19:46:19 +00:00
|
|
|
});
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2010-05-12 21:30:14 +00:00
|
|
|
|
2018-08-21 09:48:19 +00:00
|
|
|
fadeOut() {
|
2010-05-12 21:30:14 +00:00
|
|
|
if (!this._visible)
|
|
|
|
return;
|
|
|
|
|
2011-02-16 20:17:46 +00:00
|
|
|
this._visible = false;
|
2019-04-09 23:17:51 +00:00
|
|
|
this.reactive = false;
|
2018-07-20 19:46:19 +00:00
|
|
|
this.remove_all_transitions();
|
|
|
|
this.ease({
|
|
|
|
opacity: 0,
|
2020-04-15 09:16:32 +00:00
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
2018-07-20 19:46:19 +00:00
|
|
|
duration: Overview.ANIMATION_TIME,
|
|
|
|
});
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2009-08-11 15:32:58 +00:00
|
|
|
|
2021-03-01 09:07:27 +00:00
|
|
|
_syncIcon(app) {
|
|
|
|
const icon = app.create_icon_texture(PANEL_ICON_SIZE - APP_MENU_ICON_MARGIN);
|
2013-07-19 19:39:48 +00:00
|
|
|
this._iconBox.set_child(icon);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2013-07-19 19:39:48 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onIconThemeChanged() {
|
2012-10-30 17:30:21 +00:00
|
|
|
if (this._iconBox.child == null)
|
|
|
|
return;
|
|
|
|
|
2021-03-01 09:07:27 +00:00
|
|
|
if (this._targetApp)
|
|
|
|
this._syncIcon(this._targetApp);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-10-30 17:30:21 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
stopAnimation() {
|
2019-07-22 14:46:40 +00:00
|
|
|
this._spinner.stop();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2010-06-10 12:07:33 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
startAnimation() {
|
2012-11-05 22:11:27 +00:00
|
|
|
this._spinner.play();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2010-06-10 12:07:33 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onAppStateChanged(appSys, app) {
|
2010-06-10 12:07:33 +00:00
|
|
|
let state = app.state;
|
2023-08-07 00:51:19 +00:00
|
|
|
if (state !== Shell.AppState.STARTING)
|
|
|
|
this._startingApps = this._startingApps.filter(a => a !== app);
|
|
|
|
else if (state === Shell.AppState.STARTING)
|
2010-10-06 21:31:22 +00:00
|
|
|
this._startingApps.push(app);
|
2010-06-10 12:07:33 +00:00
|
|
|
// For now just resync on all running state changes; this is mainly to handle
|
|
|
|
// cases where the focused window's application changes without the focus
|
|
|
|
// changing. An example case is how we map OpenOffice.org based on the window
|
|
|
|
// title which is a dynamic property.
|
|
|
|
this._sync();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2010-06-10 12:07:33 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_focusAppChanged() {
|
2009-10-15 23:28:29 +00:00
|
|
|
let tracker = Shell.WindowTracker.get_default();
|
|
|
|
let focusedApp = tracker.focus_app;
|
2010-07-02 19:36:56 +00:00
|
|
|
if (!focusedApp) {
|
|
|
|
// If the app has just lost focus to the panel, pretend
|
|
|
|
// nothing happened; otherwise you can't keynav to the
|
|
|
|
// app menu.
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
if (global.stage.key_focus != null)
|
2010-07-02 19:36:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-03-13 18:20:37 +00:00
|
|
|
this._sync();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-03-13 18:20:37 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_findTargetApp() {
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let workspace = workspaceManager.get_active_workspace();
|
2012-03-13 18:20:37 +00:00
|
|
|
let tracker = Shell.WindowTracker.get_default();
|
|
|
|
let focusedApp = tracker.focus_app;
|
2013-08-13 11:41:44 +00:00
|
|
|
if (focusedApp && focusedApp.is_on_workspace(workspace))
|
|
|
|
return focusedApp;
|
|
|
|
|
2019-08-20 00:51:42 +00:00
|
|
|
for (let i = 0; i < this._startingApps.length; i++) {
|
2012-03-13 18:20:37 +00:00
|
|
|
if (this._startingApps[i].is_on_workspace(workspace))
|
2013-08-13 11:41:44 +00:00
|
|
|
return this._startingApps[i];
|
2019-08-20 00:51:42 +00:00
|
|
|
}
|
2010-07-02 19:36:56 +00:00
|
|
|
|
2013-08-13 11:41:44 +00:00
|
|
|
return null;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-03-08 17:46:43 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_sync() {
|
2013-08-13 11:41:44 +00:00
|
|
|
let targetApp = this._findTargetApp();
|
2011-11-16 00:12:58 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._targetApp !== targetApp) {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._targetApp?.disconnectObject(this);
|
2011-03-08 17:46:43 +00:00
|
|
|
|
2013-08-13 11:41:44 +00:00
|
|
|
this._targetApp = targetApp;
|
2011-03-08 17:46:43 +00:00
|
|
|
|
2013-08-13 11:41:44 +00:00
|
|
|
if (this._targetApp) {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._targetApp.connectObject('notify::busy', this._sync.bind(this), this);
|
2015-02-18 20:39:33 +00:00
|
|
|
this._label.set_text(this._targetApp.get_name());
|
2019-04-09 23:17:51 +00:00
|
|
|
this.set_accessible_name(this._targetApp.get_name());
|
2021-03-01 09:07:27 +00:00
|
|
|
|
|
|
|
this._syncIcon(this._targetApp);
|
2011-05-15 16:55:23 +00:00
|
|
|
}
|
2011-03-08 17:46:43 +00:00
|
|
|
}
|
|
|
|
|
2019-08-19 19:38:51 +00:00
|
|
|
let visible = this._targetApp != null && !Main.overview.visibleTarget;
|
2013-08-13 11:41:44 +00:00
|
|
|
if (visible)
|
2018-08-21 09:48:19 +00:00
|
|
|
this.fadeIn();
|
2013-08-13 11:41:44 +00:00
|
|
|
else
|
2018-08-21 09:48:19 +00:00
|
|
|
this.fadeOut();
|
2010-06-10 12:07:33 +00:00
|
|
|
|
2019-08-19 19:38:51 +00:00
|
|
|
let isBusy = this._targetApp != null &&
|
2023-08-07 00:51:19 +00:00
|
|
|
(this._targetApp.get_state() === Shell.AppState.STARTING ||
|
2019-08-19 19:38:51 +00:00
|
|
|
this._targetApp.get_busy());
|
2013-08-13 11:41:44 +00:00
|
|
|
if (isBusy)
|
2011-03-08 17:46:43 +00:00
|
|
|
this.startAnimation();
|
2011-05-15 16:55:23 +00:00
|
|
|
else
|
2013-08-13 11:41:44 +00:00
|
|
|
this.stopAnimation();
|
2009-08-11 15:32:58 +00:00
|
|
|
|
2019-08-19 19:38:51 +00:00
|
|
|
this.reactive = visible && !isBusy;
|
2013-08-13 11:41:44 +00:00
|
|
|
|
2018-10-04 14:52:34 +00:00
|
|
|
this.menu.setApp(this._targetApp);
|
2009-08-11 15:32:58 +00:00
|
|
|
this.emit('changed');
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-20 14:38:48 +00:00
|
|
|
});
|
2009-08-11 15:32:58 +00:00
|
|
|
|
panel: Add workspaces indicators in activities button
After removing the app name and icon, the next natural step that
was requested from the design team is to add workspaces indicators
to the top bar, where currently the Activities button is placed.
In addition to that, this is desired because there are known issues
with using "Activities" as a label for the overview. A more
comprehensive rationale for that can be found at [1].
Add an workspaces indicator replacing the Activities label in the
activities button.
The WorkspaceIndicators class controls how many workspaces dots
exists, their expansion, and the width multiplier. The WorkspaceDot
class takes the expansion and the multiplier, and applies it
internally so that we can get perfectly rounded dots at all
times without using CSS hacks.
The width multipliers are hardcoded, and defined by the design
team. We can revisit them later if necessary. Special care is
taken to not let these width multipliers result in fractional
widths.
When the number of workspaces changes, WorkspaceIndicators adds
new dot to the end, and animate them. When removing, scale the dot
out, then destroy it.
This does not work with workspace grids, but that's not supported
by GNOME Shell anyway, so no effort is made to cover this use case.
The button continues to have "Activities" as its accessible name,
but the label actor is removed.
Also adjust the padding of the activities pill, so it better wraps
the new indicators.
[1] https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/227
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2902>
2023-08-10 13:27:13 +00:00
|
|
|
const WorkspaceDot = GObject.registerClass({
|
|
|
|
Properties: {
|
|
|
|
'expansion': GObject.ParamSpec.double('expansion', '', '',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
|
|
|
0.0, 1.0, 0.0),
|
|
|
|
'width-multiplier': GObject.ParamSpec.double(
|
|
|
|
'width-multiplier', '', '',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
|
|
|
1.0, 10.0, 1.0),
|
|
|
|
},
|
|
|
|
}, class WorkspaceDot extends Clutter.Actor {
|
|
|
|
constructor(params = {}) {
|
|
|
|
super({
|
|
|
|
pivot_point: new Graphene.Point({x: 0.5, y: 0.5}),
|
|
|
|
...params,
|
|
|
|
});
|
|
|
|
|
|
|
|
this._dot = new St.Widget({
|
|
|
|
style_class: 'workspace-dot',
|
|
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
|
|
pivot_point: new Graphene.Point({x: 0.5, y: 0.5}),
|
|
|
|
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
|
|
|
|
});
|
|
|
|
this.add_child(this._dot);
|
|
|
|
|
|
|
|
this.connect('notify::width-multiplier', () => this.queue_relayout());
|
|
|
|
this.connect('notify::expansion', () => {
|
|
|
|
this._updateVisuals();
|
|
|
|
this.queue_relayout();
|
|
|
|
});
|
|
|
|
this._updateVisuals();
|
|
|
|
|
|
|
|
this._destroying = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateVisuals() {
|
|
|
|
const {expansion} = this;
|
|
|
|
|
|
|
|
this._dot.set({
|
|
|
|
opacity: Util.lerp(0.50, 1.0, expansion) * 255,
|
|
|
|
scaleX: Util.lerp(INACTIVE_WORKSPACE_DOT_SCALE, 1.0, expansion),
|
|
|
|
scaleY: Util.lerp(INACTIVE_WORKSPACE_DOT_SCALE, 1.0, expansion),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_get_preferred_width(forHeight) {
|
|
|
|
const factor = Util.lerp(1.0, this.widthMultiplier, this.expansion);
|
|
|
|
return this._dot.get_preferred_width(forHeight).map(v => Math.round(v * factor));
|
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_get_preferred_height(forWidth) {
|
|
|
|
return this._dot.get_preferred_height(forWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
|
|
|
|
|
|
|
box.set_origin(0, 0);
|
|
|
|
this._dot.allocate(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
scaleIn() {
|
|
|
|
this.set({
|
|
|
|
scale_x: 0,
|
|
|
|
scale_y: 0,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.ease({
|
|
|
|
duration: 500,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
|
|
scale_x: 1.0,
|
|
|
|
scale_y: 1.0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
scaleOutAndDestroy() {
|
|
|
|
this._destroying = true;
|
|
|
|
|
|
|
|
this.ease({
|
|
|
|
duration: 500,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
|
|
scale_x: 0.0,
|
|
|
|
scale_y: 0.0,
|
|
|
|
onComplete: () => this.destroy(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
get destroying() {
|
|
|
|
return this._destroying;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const WorkspaceIndicators = GObject.registerClass(
|
|
|
|
class WorkspaceIndicators extends St.BoxLayout {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
|
|
|
|
this._workspacesAdjustment = Main.createWorkspacesAdjustment(this);
|
|
|
|
this._workspacesAdjustment.connectObject(
|
|
|
|
'notify::value', () => this._updateExpansion(),
|
|
|
|
'notify::upper', () => this._recalculateDots(),
|
|
|
|
this);
|
|
|
|
|
|
|
|
for (let i = 0; i < this._workspacesAdjustment.upper; i++)
|
|
|
|
this.insert_child_at_index(new WorkspaceDot(), i);
|
|
|
|
this._updateExpansion();
|
|
|
|
}
|
|
|
|
|
|
|
|
_getActiveIndicators() {
|
|
|
|
return [...this].filter(i => !i.destroying);
|
|
|
|
}
|
|
|
|
|
|
|
|
_recalculateDots() {
|
|
|
|
const activeIndicators = this._getActiveIndicators();
|
|
|
|
const nIndicators = activeIndicators.length;
|
|
|
|
const targetIndicators = this._workspacesAdjustment.upper;
|
|
|
|
|
|
|
|
let remaining = Math.abs(nIndicators - targetIndicators);
|
|
|
|
while (remaining--) {
|
|
|
|
if (nIndicators < targetIndicators) {
|
|
|
|
const indicator = new WorkspaceDot();
|
|
|
|
this.add_child(indicator);
|
|
|
|
indicator.scaleIn();
|
|
|
|
} else {
|
|
|
|
const indicator = activeIndicators[nIndicators - remaining - 1];
|
|
|
|
indicator.scaleOutAndDestroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this._updateExpansion();
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateExpansion() {
|
|
|
|
const nIndicators = this._getActiveIndicators().length;
|
|
|
|
const activeWorkspace = this._workspacesAdjustment.value;
|
|
|
|
|
|
|
|
let widthMultiplier;
|
|
|
|
if (nIndicators <= 2)
|
|
|
|
widthMultiplier = 3.625;
|
|
|
|
else if (nIndicators <= 5)
|
|
|
|
widthMultiplier = 3.25;
|
|
|
|
else
|
|
|
|
widthMultiplier = 2.75;
|
|
|
|
|
|
|
|
this.get_children().forEach((indicator, index) => {
|
|
|
|
const distance = Math.abs(index - activeWorkspace);
|
|
|
|
indicator.expansion = Math.clamp(1 - distance, 0, 1);
|
|
|
|
indicator.widthMultiplier = widthMultiplier;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const ActivitiesButton = GObject.registerClass(
|
2017-10-31 01:23:39 +00:00
|
|
|
class ActivitiesButton extends PanelMenu.Button {
|
2017-10-31 00:03:21 +00:00
|
|
|
_init() {
|
2017-10-31 01:23:39 +00:00
|
|
|
super._init(0.0, null, true);
|
2011-07-14 12:56:14 +00:00
|
|
|
|
panel: Add workspaces indicators in activities button
After removing the app name and icon, the next natural step that
was requested from the design team is to add workspaces indicators
to the top bar, where currently the Activities button is placed.
In addition to that, this is desired because there are known issues
with using "Activities" as a label for the overview. A more
comprehensive rationale for that can be found at [1].
Add an workspaces indicator replacing the Activities label in the
activities button.
The WorkspaceIndicators class controls how many workspaces dots
exists, their expansion, and the width multiplier. The WorkspaceDot
class takes the expansion and the multiplier, and applies it
internally so that we can get perfectly rounded dots at all
times without using CSS hacks.
The width multipliers are hardcoded, and defined by the design
team. We can revisit them later if necessary. Special care is
taken to not let these width multipliers result in fractional
widths.
When the number of workspaces changes, WorkspaceIndicators adds
new dot to the end, and animate them. When removing, scale the dot
out, then destroy it.
This does not work with workspace grids, but that's not supported
by GNOME Shell anyway, so no effort is made to cover this use case.
The button continues to have "Activities" as its accessible name,
but the label actor is removed.
Also adjust the padding of the activities pill, so it better wraps
the new indicators.
[1] https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/227
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2902>
2023-08-10 13:27:13 +00:00
|
|
|
this.set({
|
|
|
|
name: 'panelActivities',
|
|
|
|
accessible_role: Atk.Role.TOGGLE_BUTTON,
|
|
|
|
/* Translators: If there is no suitable word for "Activities"
|
|
|
|
in your language, you can use the word for "Overview". */
|
|
|
|
accessible_name: _('Activities'),
|
2020-03-29 21:51:13 +00:00
|
|
|
});
|
2011-07-14 12:35:55 +00:00
|
|
|
|
panel: Add workspaces indicators in activities button
After removing the app name and icon, the next natural step that
was requested from the design team is to add workspaces indicators
to the top bar, where currently the Activities button is placed.
In addition to that, this is desired because there are known issues
with using "Activities" as a label for the overview. A more
comprehensive rationale for that can be found at [1].
Add an workspaces indicator replacing the Activities label in the
activities button.
The WorkspaceIndicators class controls how many workspaces dots
exists, their expansion, and the width multiplier. The WorkspaceDot
class takes the expansion and the multiplier, and applies it
internally so that we can get perfectly rounded dots at all
times without using CSS hacks.
The width multipliers are hardcoded, and defined by the design
team. We can revisit them later if necessary. Special care is
taken to not let these width multipliers result in fractional
widths.
When the number of workspaces changes, WorkspaceIndicators adds
new dot to the end, and animate them. When removing, scale the dot
out, then destroy it.
This does not work with workspace grids, but that's not supported
by GNOME Shell anyway, so no effort is made to cover this use case.
The button continues to have "Activities" as its accessible name,
but the label actor is removed.
Also adjust the padding of the activities pill, so it better wraps
the new indicators.
[1] https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/227
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2902>
2023-08-10 13:27:13 +00:00
|
|
|
this.add_child(new WorkspaceIndicators());
|
2012-02-17 17:16:53 +00:00
|
|
|
|
2017-10-31 00:38:18 +00:00
|
|
|
Main.overview.connect('showing', () => {
|
2023-07-05 15:00:14 +00:00
|
|
|
this.add_style_pseudo_class('checked');
|
2019-08-19 17:55:49 +00:00
|
|
|
this.add_accessible_state(Atk.StateType.CHECKED);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
|
|
|
Main.overview.connect('hiding', () => {
|
2023-07-05 15:00:14 +00:00
|
|
|
this.remove_style_pseudo_class('checked');
|
2019-08-19 17:55:49 +00:00
|
|
|
this.remove_accessible_state(Atk.StateType.CHECKED);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2011-07-14 12:53:44 +00:00
|
|
|
|
|
|
|
this._xdndTimeOut = 0;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-07-14 12:53:44 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
handleDragOver(source, _actor, _x, _y, _time) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (source !== Main.xdndHandler)
|
2012-02-11 10:14:43 +00:00
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2011-07-14 12:53:44 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._xdndTimeOut !== 0)
|
2019-08-19 18:50:33 +00:00
|
|
|
GLib.source_remove(this._xdndTimeOut);
|
|
|
|
this._xdndTimeOut = GLib.timeout_add(GLib.PRIORITY_DEFAULT, BUTTON_DND_ACTIVATION_TIMEOUT, () => {
|
2019-01-31 14:08:10 +00:00
|
|
|
this._xdndToggleOverview();
|
2017-12-02 00:27:35 +00:00
|
|
|
});
|
2014-04-10 17:26:52 +00:00
|
|
|
GLib.Source.set_name_by_id(this._xdndTimeOut, '[gnome-shell] this._xdndToggleOverview');
|
2012-02-11 10:14:43 +00:00
|
|
|
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-07-14 12:53:44 +00:00
|
|
|
|
2019-09-10 05:42:48 +00:00
|
|
|
vfunc_event(event) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (event.type() === Clutter.EventType.TOUCH_END ||
|
|
|
|
event.type() === Clutter.EventType.BUTTON_RELEASE) {
|
2015-10-22 16:12:28 +00:00
|
|
|
if (Main.overview.shouldToggleByCornerOrButton())
|
|
|
|
Main.overview.toggle();
|
2019-08-20 00:51:42 +00:00
|
|
|
}
|
2014-07-31 15:26:47 +00:00
|
|
|
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-07-14 12:56:14 +00:00
|
|
|
|
2023-08-08 16:14:04 +00:00
|
|
|
vfunc_key_release_event(event) {
|
|
|
|
let symbol = event.get_key_symbol();
|
2023-08-07 00:51:19 +00:00
|
|
|
if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_space) {
|
2019-11-04 10:18:25 +00:00
|
|
|
if (Main.overview.shouldToggleByCornerOrButton()) {
|
|
|
|
Main.overview.toggle();
|
|
|
|
return Clutter.EVENT_STOP;
|
2019-09-10 05:42:48 +00:00
|
|
|
}
|
2011-07-14 12:56:14 +00:00
|
|
|
}
|
2019-11-04 10:18:25 +00:00
|
|
|
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-07-14 12:56:14 +00:00
|
|
|
|
2019-02-04 11:30:53 +00:00
|
|
|
_xdndToggleOverview() {
|
2019-02-01 13:41:55 +00:00
|
|
|
let [x, y] = global.get_pointer();
|
2011-07-14 12:53:44 +00:00
|
|
|
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (pickedActor === this && Main.overview.shouldToggleByCornerOrButton())
|
2013-03-04 22:02:02 +00:00
|
|
|
Main.overview.toggle();
|
2011-07-14 12:53:44 +00:00
|
|
|
|
2019-08-19 18:50:33 +00:00
|
|
|
GLib.source_remove(this._xdndTimeOut);
|
2011-07-14 12:53:44 +00:00
|
|
|
this._xdndTimeOut = 0;
|
2013-11-29 00:45:39 +00:00
|
|
|
return GLib.SOURCE_REMOVE;
|
2011-07-14 12:53:44 +00:00
|
|
|
}
|
2011-11-20 17:56:27 +00:00
|
|
|
});
|
2011-02-09 02:12:10 +00:00
|
|
|
|
2021-09-02 15:15:36 +00:00
|
|
|
const UnsafeModeIndicator = GObject.registerClass(
|
2022-07-27 02:46:42 +00:00
|
|
|
class UnsafeModeIndicator extends SystemIndicator {
|
2021-09-02 15:15:36 +00:00
|
|
|
_init() {
|
|
|
|
super._init();
|
|
|
|
|
|
|
|
this._indicator = this._addIndicator();
|
|
|
|
this._indicator.icon_name = 'channel-insecure-symbolic';
|
|
|
|
|
|
|
|
global.context.bind_property('unsafe-mode',
|
|
|
|
this._indicator, 'visible',
|
|
|
|
GObject.BindingFlags.SYNC_CREATE);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const QuickSettings = GObject.registerClass(
|
2022-07-24 17:49:58 +00:00
|
|
|
class QuickSettings extends PanelMenu.Button {
|
2023-06-07 15:30:29 +00:00
|
|
|
constructor() {
|
|
|
|
super(0.0, C_('System menu in the top bar', 'System'), true);
|
2022-07-24 17:49:58 +00:00
|
|
|
|
|
|
|
this._indicators = new St.BoxLayout({
|
|
|
|
style_class: 'panel-status-indicators-box',
|
|
|
|
});
|
|
|
|
this.add_child(this._indicators);
|
|
|
|
|
|
|
|
this.setMenu(new QuickSettingsMenu(this, N_QUICK_SETTINGS_COLUMNS));
|
2022-07-27 02:46:42 +00:00
|
|
|
|
2023-06-07 15:30:29 +00:00
|
|
|
this._setupIndicators().catch(error =>
|
|
|
|
logError(error, 'Failed to setup quick settings'));
|
|
|
|
}
|
|
|
|
|
|
|
|
async _setupIndicators() {
|
|
|
|
if (Config.HAVE_NETWORKMANAGER) {
|
2023-07-10 09:53:00 +00:00
|
|
|
/** @type {import('./status/network.js')} */
|
|
|
|
const NetworkStatus = await import('./status/network.js');
|
2023-06-07 15:30:29 +00:00
|
|
|
|
|
|
|
this._network = new NetworkStatus.Indicator();
|
|
|
|
} else {
|
2022-08-03 04:03:40 +00:00
|
|
|
this._network = null;
|
2023-06-07 15:30:29 +00:00
|
|
|
}
|
2022-08-03 04:03:40 +00:00
|
|
|
|
2023-06-07 15:30:29 +00:00
|
|
|
if (Config.HAVE_BLUETOOTH) {
|
2023-07-10 09:53:00 +00:00
|
|
|
/** @type {import('./status/bluetooth.js')} */
|
|
|
|
const BluetoothStatus = await import('./status/bluetooth.js');
|
2023-06-07 15:30:29 +00:00
|
|
|
|
|
|
|
this._bluetooth = new BluetoothStatus.Indicator();
|
|
|
|
} else {
|
2022-07-26 08:45:35 +00:00
|
|
|
this._bluetooth = null;
|
2023-06-07 15:30:29 +00:00
|
|
|
}
|
2022-07-26 08:45:35 +00:00
|
|
|
|
2023-06-07 15:30:29 +00:00
|
|
|
this._system = new SystemStatus.Indicator();
|
2023-07-13 21:34:30 +00:00
|
|
|
this._camera = new CameraStatus.Indicator();
|
2023-07-28 13:21:23 +00:00
|
|
|
this._volumeOutput = new VolumeStatus.OutputIndicator();
|
|
|
|
this._volumeInput = new VolumeStatus.InputIndicator();
|
2023-06-07 15:30:29 +00:00
|
|
|
this._brightness = new BrightnessStatus.Indicator();
|
|
|
|
this._remoteAccess = new RemoteAccessStatus.RemoteAccessApplet();
|
|
|
|
this._location = new LocationStatus.Indicator();
|
|
|
|
this._thunderbolt = new ThunderboltStatus.Indicator();
|
|
|
|
this._nightLight = new NightLightStatus.Indicator();
|
|
|
|
this._darkMode = new DarkModeStatus.Indicator();
|
2023-06-13 19:36:20 +00:00
|
|
|
this._backlight = new BacklightStatus.Indicator();
|
2023-06-07 15:30:29 +00:00
|
|
|
this._powerProfiles = new PowerProfileStatus.Indicator();
|
|
|
|
this._rfkill = new RFKillStatus.Indicator();
|
|
|
|
this._autoRotate = new AutoRotateStatus.Indicator();
|
2022-07-27 02:46:42 +00:00
|
|
|
this._unsafeMode = new UnsafeModeIndicator();
|
2023-06-07 15:30:29 +00:00
|
|
|
this._backgroundApps = new BackgroundAppsStatus.Indicator();
|
2022-07-27 02:46:42 +00:00
|
|
|
|
2023-08-10 15:38:38 +00:00
|
|
|
// add privacy-related indicators before any external indicators
|
|
|
|
let pos = 0;
|
|
|
|
this._indicators.insert_child_at_index(this._remoteAccess, pos++);
|
|
|
|
this._indicators.insert_child_at_index(this._camera, pos++);
|
|
|
|
this._indicators.insert_child_at_index(this._volumeInput, pos++);
|
|
|
|
this._indicators.insert_child_at_index(this._location, pos++);
|
|
|
|
|
|
|
|
// append all other indicators
|
2023-07-28 13:21:23 +00:00
|
|
|
this._indicators.add_child(this._brightness);
|
|
|
|
this._indicators.add_child(this._thunderbolt);
|
2022-07-24 18:18:55 +00:00
|
|
|
this._indicators.add_child(this._nightLight);
|
2022-08-03 04:03:40 +00:00
|
|
|
if (this._network)
|
|
|
|
this._indicators.add_child(this._network);
|
2022-07-27 01:38:28 +00:00
|
|
|
this._indicators.add_child(this._darkMode);
|
2023-06-13 19:36:20 +00:00
|
|
|
this._indicators.add_child(this._backlight);
|
2022-07-29 16:50:57 +00:00
|
|
|
this._indicators.add_child(this._powerProfiles);
|
2022-07-26 08:45:35 +00:00
|
|
|
if (this._bluetooth)
|
|
|
|
this._indicators.add_child(this._bluetooth);
|
2022-07-24 18:45:45 +00:00
|
|
|
this._indicators.add_child(this._rfkill);
|
2022-07-27 01:46:43 +00:00
|
|
|
this._indicators.add_child(this._autoRotate);
|
2023-07-28 13:21:23 +00:00
|
|
|
this._indicators.add_child(this._volumeOutput);
|
2022-07-27 02:46:42 +00:00
|
|
|
this._indicators.add_child(this._unsafeMode);
|
2022-07-30 00:17:15 +00:00
|
|
|
this._indicators.add_child(this._system);
|
2022-07-27 02:46:42 +00:00
|
|
|
|
2023-08-10 15:38:38 +00:00
|
|
|
// add our quick settings items before any external ones
|
|
|
|
const sibling = this.menu.getFirstItem();
|
|
|
|
this._addItemsBefore(this._system.quickSettingsItems,
|
|
|
|
sibling, N_QUICK_SETTINGS_COLUMNS);
|
|
|
|
this._addItemsBefore(this._volumeOutput.quickSettingsItems,
|
|
|
|
sibling, N_QUICK_SETTINGS_COLUMNS);
|
|
|
|
this._addItemsBefore(this._volumeInput.quickSettingsItems,
|
|
|
|
sibling, N_QUICK_SETTINGS_COLUMNS);
|
|
|
|
this._addItemsBefore(this._brightness.quickSettingsItems,
|
|
|
|
sibling, N_QUICK_SETTINGS_COLUMNS);
|
|
|
|
|
|
|
|
this._addItemsBefore(this._camera.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._remoteAccess.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._thunderbolt.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._location.quickSettingsItems, sibling);
|
2022-08-03 04:03:40 +00:00
|
|
|
if (this._network)
|
2023-08-10 15:38:38 +00:00
|
|
|
this._addItemsBefore(this._network.quickSettingsItems, sibling);
|
2022-07-26 08:45:35 +00:00
|
|
|
if (this._bluetooth)
|
2023-08-10 15:38:38 +00:00
|
|
|
this._addItemsBefore(this._bluetooth.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._powerProfiles.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._nightLight.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._darkMode.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._backlight.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._rfkill.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._autoRotate.quickSettingsItems, sibling);
|
|
|
|
this._addItemsBefore(this._unsafeMode.quickSettingsItems, sibling);
|
|
|
|
|
|
|
|
// append background apps
|
|
|
|
this._backgroundApps.quickSettingsItems.forEach(
|
|
|
|
item => this.menu.addItem(item, N_QUICK_SETTINGS_COLUMNS));
|
|
|
|
}
|
|
|
|
|
|
|
|
_addItemsBefore(items, sibling, colSpan = 1) {
|
|
|
|
items.forEach(item => this.menu.insertItemBefore(item, sibling, colSpan));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert indicator and quick settings items at
|
|
|
|
* appropriate positions
|
|
|
|
*
|
|
|
|
* @param {PanelMenu.Button} indicator
|
|
|
|
* @param {number=} colSpan
|
|
|
|
*/
|
|
|
|
addExternalIndicator(indicator, colSpan = 1) {
|
|
|
|
// Insert before first non-privacy indicator if it exists
|
|
|
|
let sibling = this._brightness ?? null;
|
|
|
|
this._indicators.insert_child_below(indicator, sibling);
|
|
|
|
|
|
|
|
// Insert before background apps if it exists
|
|
|
|
sibling = this._backgroundApps?.quickSettingsItems?.at(-1) ?? null;
|
|
|
|
this._addItemsBefore(indicator.quickSettingsItems, sibling, colSpan);
|
2022-07-24 17:49:58 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-08-28 21:10:44 +00:00
|
|
|
const PANEL_ITEM_IMPLEMENTATIONS = {
|
|
|
|
'activities': ActivitiesButton,
|
|
|
|
'appMenu': AppMenuButton,
|
2022-07-24 17:49:58 +00:00
|
|
|
'quickSettings': QuickSettings,
|
2023-06-07 15:30:29 +00:00
|
|
|
'dateMenu': DateMenuButton,
|
|
|
|
'a11y': ATIndicator,
|
|
|
|
'keyboard': InputSourceIndicator,
|
|
|
|
'dwellClick': DwellClickIndicator,
|
|
|
|
'screenRecording': ScreenRecordingIndicator,
|
|
|
|
'screenSharing': ScreenSharingIndicator,
|
2012-08-28 21:10:44 +00:00
|
|
|
};
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const Panel = GObject.registerClass(
|
2017-10-31 01:23:39 +00:00
|
|
|
class Panel extends St.Widget {
|
2017-10-31 00:03:21 +00:00
|
|
|
_init() {
|
2020-03-29 21:51:13 +00:00
|
|
|
super._init({
|
|
|
|
name: 'panel',
|
|
|
|
reactive: true,
|
|
|
|
});
|
2018-06-27 19:02:15 +00:00
|
|
|
|
2020-02-26 03:53:40 +00:00
|
|
|
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
2009-11-10 17:13:58 +00:00
|
|
|
|
2012-09-21 20:19:27 +00:00
|
|
|
this._sessionStyle = null;
|
|
|
|
|
2012-08-26 14:05:46 +00:00
|
|
|
this.statusArea = {};
|
2011-04-06 13:26:15 +00:00
|
|
|
|
2015-02-23 19:07:04 +00:00
|
|
|
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
2010-05-20 15:18:46 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._leftBox = new St.BoxLayout({name: 'panelLeft'});
|
2018-06-27 19:02:15 +00:00
|
|
|
this.add_child(this._leftBox);
|
2023-08-06 22:40:20 +00:00
|
|
|
this._centerBox = new St.BoxLayout({name: 'panelCenter'});
|
2018-06-27 19:02:15 +00:00
|
|
|
this.add_child(this._centerBox);
|
2023-08-06 22:40:20 +00:00
|
|
|
this._rightBox = new St.BoxLayout({name: 'panelRight'});
|
2018-06-27 19:02:15 +00:00
|
|
|
this.add_child(this._rightBox);
|
2009-08-11 15:16:25 +00:00
|
|
|
|
2022-03-05 21:47:54 +00:00
|
|
|
this.connect('button-press-event', this._onButtonPress.bind(this));
|
|
|
|
this.connect('touch-event', this._onTouchEvent.bind(this));
|
|
|
|
|
2017-10-31 00:38:18 +00:00
|
|
|
Main.overview.connect('showing', () => {
|
2018-06-27 19:02:15 +00:00
|
|
|
this.add_style_pseudo_class('overview');
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
|
|
|
Main.overview.connect('hiding', () => {
|
2018-06-27 19:02:15 +00:00
|
|
|
this.remove_style_pseudo_class('overview');
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2013-02-05 14:24:46 +00:00
|
|
|
|
2018-06-27 19:02:15 +00:00
|
|
|
Main.layoutManager.panelBox.add(this);
|
2023-08-06 23:45:22 +00:00
|
|
|
Main.ctrlAltTabManager.addGroup(this,
|
|
|
|
_('Top Bar'), 'focus-top-bar-symbolic',
|
|
|
|
{sortGroup: CtrlAltTab.SortGroup.TOP});
|
2012-09-01 12:42:53 +00:00
|
|
|
|
2017-12-02 00:27:35 +00:00
|
|
|
Main.sessionMode.connect('updated', this._updatePanel.bind(this));
|
2016-09-23 19:08:11 +00:00
|
|
|
|
2019-01-28 00:42:00 +00:00
|
|
|
global.display.connect('workareas-changed', () => this.queue_relayout());
|
2012-09-01 12:42:53 +00:00
|
|
|
this._updatePanel();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2008-12-01 19:51:43 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
vfunc_get_preferred_width(_forHeight) {
|
2017-04-12 06:46:54 +00:00
|
|
|
let primaryMonitor = Main.layoutManager.primaryMonitor;
|
|
|
|
|
|
|
|
if (primaryMonitor)
|
2018-06-27 19:02:15 +00:00
|
|
|
return [0, primaryMonitor.width];
|
2011-07-21 14:49:04 +00:00
|
|
|
|
2018-06-27 19:02:15 +00:00
|
|
|
return [0, 0];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-07-21 14:49:04 +00:00
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
2018-06-27 19:02:15 +00:00
|
|
|
|
2011-07-21 14:49:04 +00:00
|
|
|
let allocWidth = box.x2 - box.x1;
|
|
|
|
let allocHeight = box.y2 - box.y1;
|
|
|
|
|
2019-02-01 13:41:55 +00:00
|
|
|
let [, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
|
|
|
|
let [, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
|
|
|
|
let [, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
|
2011-07-21 14:49:04 +00:00
|
|
|
|
|
|
|
let sideWidth, centerWidth;
|
|
|
|
centerWidth = centerNaturalWidth;
|
2018-01-09 08:39:06 +00:00
|
|
|
|
|
|
|
// get workspace area and center date entry relative to it
|
2018-06-27 19:02:15 +00:00
|
|
|
let monitor = Main.layoutManager.findMonitorForActor(this);
|
2018-01-09 08:39:06 +00:00
|
|
|
let centerOffset = 0;
|
|
|
|
if (monitor) {
|
|
|
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
|
|
|
|
centerOffset = 2 * (workArea.x - monitor.x) + workArea.width - monitor.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
sideWidth = Math.max(0, (allocWidth - centerWidth + centerOffset) / 2);
|
2011-07-21 14:49:04 +00:00
|
|
|
|
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
|
|
|
|
childBox.y1 = 0;
|
|
|
|
childBox.y2 = allocHeight;
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this.get_text_direction() === Clutter.TextDirection.RTL) {
|
2023-08-06 23:45:22 +00:00
|
|
|
childBox.x1 = Math.max(
|
|
|
|
allocWidth - Math.min(Math.floor(sideWidth), leftNaturalWidth),
|
|
|
|
0);
|
2011-07-21 14:49:04 +00:00
|
|
|
childBox.x2 = allocWidth;
|
|
|
|
} else {
|
|
|
|
childBox.x1 = 0;
|
2023-08-06 23:45:22 +00:00
|
|
|
childBox.x2 = Math.min(Math.floor(sideWidth), leftNaturalWidth);
|
2011-07-21 14:49:04 +00:00
|
|
|
}
|
2020-05-09 19:30:26 +00:00
|
|
|
this._leftBox.allocate(childBox);
|
2011-07-21 14:49:04 +00:00
|
|
|
|
|
|
|
childBox.x1 = Math.ceil(sideWidth);
|
|
|
|
childBox.y1 = 0;
|
|
|
|
childBox.x2 = childBox.x1 + centerWidth;
|
|
|
|
childBox.y2 = allocHeight;
|
2020-05-09 19:30:26 +00:00
|
|
|
this._centerBox.allocate(childBox);
|
2011-07-21 14:49:04 +00:00
|
|
|
|
|
|
|
childBox.y1 = 0;
|
|
|
|
childBox.y2 = allocHeight;
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this.get_text_direction() === Clutter.TextDirection.RTL) {
|
2011-07-21 14:49:04 +00:00
|
|
|
childBox.x1 = 0;
|
2023-08-06 23:45:22 +00:00
|
|
|
childBox.x2 = Math.min(Math.floor(sideWidth), rightNaturalWidth);
|
2011-07-21 14:49:04 +00:00
|
|
|
} else {
|
2023-08-06 23:45:22 +00:00
|
|
|
childBox.x1 = Math.max(
|
|
|
|
allocWidth - Math.min(Math.floor(sideWidth), rightNaturalWidth),
|
|
|
|
0);
|
2011-07-21 14:49:04 +00:00
|
|
|
childBox.x2 = allocWidth;
|
|
|
|
}
|
2020-05-09 19:30:26 +00:00
|
|
|
this._rightBox.allocate(childBox);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-07-21 14:49:04 +00:00
|
|
|
|
2019-09-10 05:42:48 +00:00
|
|
|
_tryDragWindow(event) {
|
2013-02-20 01:27:11 +00:00
|
|
|
if (Main.modalCount > 0)
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2013-02-20 01:27:11 +00:00
|
|
|
|
2022-03-05 21:47:54 +00:00
|
|
|
const targetActor = global.stage.get_event_actor(event);
|
|
|
|
if (targetActor !== this)
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2011-10-04 15:14:29 +00:00
|
|
|
|
2022-09-29 16:53:45 +00:00
|
|
|
const [x, y_] = event.get_coords();
|
2019-09-10 05:42:48 +00:00
|
|
|
let dragWindow = this._getDraggableWindowForPosition(x);
|
2018-05-24 15:28:36 +00:00
|
|
|
|
2019-09-10 05:42:48 +00:00
|
|
|
if (!dragWindow)
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2011-10-04 15:14:29 +00:00
|
|
|
|
2022-09-29 16:53:45 +00:00
|
|
|
return dragWindow.begin_grab_op(
|
2019-09-10 05:42:48 +00:00
|
|
|
Meta.GrabOp.MOVING,
|
2022-10-21 18:21:13 +00:00
|
|
|
event.get_device(),
|
|
|
|
event.get_event_sequence(),
|
2022-09-29 16:53:45 +00:00
|
|
|
event.get_time()) ? Clutter.EVENT_STOP : Clutter.EVENT_PROPAGATE;
|
2019-09-10 05:42:48 +00:00
|
|
|
}
|
2011-10-04 15:14:29 +00:00
|
|
|
|
2022-03-05 21:47:54 +00:00
|
|
|
_onButtonPress(actor, event) {
|
|
|
|
if (event.get_button() !== Clutter.BUTTON_PRIMARY)
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2011-10-04 15:14:29 +00:00
|
|
|
|
2022-03-05 21:47:54 +00:00
|
|
|
return this._tryDragWindow(event);
|
2019-09-10 05:42:48 +00:00
|
|
|
}
|
|
|
|
|
2022-03-05 21:47:54 +00:00
|
|
|
_onTouchEvent(actor, event) {
|
|
|
|
if (event.type() !== Clutter.EventType.TOUCH_BEGIN)
|
2019-09-10 05:42:48 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2011-10-04 15:14:29 +00:00
|
|
|
|
2022-03-05 21:47:54 +00:00
|
|
|
return this._tryDragWindow(event);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-10-04 15:14:29 +00:00
|
|
|
|
2023-08-08 16:14:04 +00:00
|
|
|
vfunc_key_press_event(event) {
|
|
|
|
let symbol = event.get_key_symbol();
|
2023-08-07 00:51:19 +00:00
|
|
|
if (symbol === Clutter.KEY_Escape) {
|
2023-08-08 16:14:04 +00:00
|
|
|
global.display.focus_default_window(event.get_time());
|
2017-11-01 18:43:11 +00:00
|
|
|
return Clutter.EVENT_STOP;
|
|
|
|
}
|
|
|
|
|
2023-08-08 16:14:04 +00:00
|
|
|
return super.vfunc_key_press_event(event);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2017-11-01 18:43:11 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_toggleMenu(indicator) {
|
2019-12-17 00:00:40 +00:00
|
|
|
if (!indicator || !indicator.mapped)
|
2019-01-23 22:55:12 +00:00
|
|
|
return; // menu not supported by current session mode
|
2012-08-28 21:10:44 +00:00
|
|
|
|
|
|
|
let menu = indicator.menu;
|
2019-04-09 23:17:51 +00:00
|
|
|
if (!indicator.reactive)
|
2012-05-18 14:04:47 +00:00
|
|
|
return;
|
2012-03-27 13:26:20 +00:00
|
|
|
|
2013-04-26 14:14:55 +00:00
|
|
|
menu.toggle();
|
|
|
|
if (menu.isOpen)
|
2018-11-27 12:58:25 +00:00
|
|
|
menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-03-27 13:26:20 +00:00
|
|
|
|
2022-07-30 00:24:17 +00:00
|
|
|
_closeMenu(indicator) {
|
|
|
|
if (!indicator || !indicator.mapped)
|
|
|
|
return; // menu not supported by current session mode
|
|
|
|
|
|
|
|
if (!indicator.reactive)
|
|
|
|
return;
|
|
|
|
|
|
|
|
indicator.menu.close();
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
toggleCalendar() {
|
2015-02-18 01:52:07 +00:00
|
|
|
this._toggleMenu(this.statusArea.dateMenu);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2015-02-18 01:52:07 +00:00
|
|
|
|
2023-03-21 16:07:19 +00:00
|
|
|
toggleQuickSettings() {
|
|
|
|
this._toggleMenu(this.statusArea.quickSettings);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
closeCalendar() {
|
2022-07-30 00:24:17 +00:00
|
|
|
this._closeMenu(this.statusArea.dateMenu);
|
|
|
|
}
|
2015-02-13 09:44:13 +00:00
|
|
|
|
2022-07-30 00:24:17 +00:00
|
|
|
closeQuickSettings() {
|
|
|
|
this._closeMenu(this.statusArea.quickSettings);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2015-02-13 09:44:13 +00:00
|
|
|
|
2012-09-01 12:42:53 +00:00
|
|
|
set boxOpacity(value) {
|
|
|
|
let isReactive = value > 0;
|
|
|
|
|
|
|
|
this._leftBox.opacity = value;
|
|
|
|
this._leftBox.reactive = isReactive;
|
|
|
|
this._centerBox.opacity = value;
|
|
|
|
this._centerBox.reactive = isReactive;
|
|
|
|
this._rightBox.opacity = value;
|
|
|
|
this._rightBox.reactive = isReactive;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-09-01 12:42:53 +00:00
|
|
|
|
|
|
|
get boxOpacity() {
|
|
|
|
return this._leftBox.opacity;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-09-01 12:42:53 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updatePanel() {
|
2012-08-28 21:10:44 +00:00
|
|
|
let panel = Main.sessionMode.panel;
|
2012-09-01 12:42:53 +00:00
|
|
|
this._hideIndicators();
|
|
|
|
this._updateBox(panel.left, this._leftBox);
|
|
|
|
this._updateBox(panel.center, this._centerBox);
|
|
|
|
this._updateBox(panel.right, this._rightBox);
|
2012-09-21 20:19:27 +00:00
|
|
|
|
2018-07-14 20:56:22 +00:00
|
|
|
if (panel.left.includes('dateMenu'))
|
2015-04-23 10:43:03 +00:00
|
|
|
Main.messageTray.bannerAlignment = Clutter.ActorAlign.START;
|
2018-07-14 20:56:22 +00:00
|
|
|
else if (panel.right.includes('dateMenu'))
|
2015-04-23 10:43:03 +00:00
|
|
|
Main.messageTray.bannerAlignment = Clutter.ActorAlign.END;
|
|
|
|
// Default to center if there is no dateMenu
|
|
|
|
else
|
|
|
|
Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER;
|
|
|
|
|
2012-09-21 20:19:27 +00:00
|
|
|
if (this._sessionStyle)
|
2022-02-02 22:19:12 +00:00
|
|
|
this.remove_style_class_name(this._sessionStyle);
|
2012-09-21 20:19:27 +00:00
|
|
|
|
|
|
|
this._sessionStyle = Main.sessionMode.panelStyle;
|
|
|
|
if (this._sessionStyle)
|
2022-02-02 22:19:12 +00:00
|
|
|
this.add_style_class_name(this._sessionStyle);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-08-28 21:10:44 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_hideIndicators() {
|
2012-09-01 12:42:53 +00:00
|
|
|
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
|
|
|
|
let indicator = this.statusArea[role];
|
|
|
|
if (!indicator)
|
|
|
|
continue;
|
2012-09-04 16:27:50 +00:00
|
|
|
indicator.container.hide();
|
2012-09-01 12:42:53 +00:00
|
|
|
}
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-09-01 12:42:53 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_ensureIndicator(role) {
|
2012-09-01 12:42:53 +00:00
|
|
|
let indicator = this.statusArea[role];
|
|
|
|
if (!indicator) {
|
|
|
|
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
|
|
|
if (!constructor) {
|
|
|
|
// This icon is not implemented (this is a bug)
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
indicator = new constructor(this);
|
|
|
|
this.statusArea[role] = indicator;
|
|
|
|
}
|
|
|
|
return indicator;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-09-01 12:42:53 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateBox(elements, box) {
|
2012-09-01 12:42:53 +00:00
|
|
|
let nChildren = box.get_n_children();
|
|
|
|
|
|
|
|
for (let i = 0; i < elements.length; i++) {
|
|
|
|
let role = elements[i];
|
|
|
|
let indicator = this._ensureIndicator(role);
|
|
|
|
if (indicator == null)
|
|
|
|
continue;
|
2011-04-06 13:26:15 +00:00
|
|
|
|
2012-09-01 12:42:53 +00:00
|
|
|
this._addToPanelBox(role, indicator, i + nChildren, box);
|
2010-07-24 11:57:53 +00:00
|
|
|
}
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2010-07-24 11:57:53 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_addToPanelBox(role, indicator, position, box) {
|
2012-09-04 16:27:50 +00:00
|
|
|
let container = indicator.container;
|
2012-09-01 12:42:53 +00:00
|
|
|
container.show();
|
|
|
|
|
|
|
|
let parent = container.get_parent();
|
2012-09-04 16:27:50 +00:00
|
|
|
if (parent)
|
2012-09-01 12:42:53 +00:00
|
|
|
parent.remove_actor(container);
|
|
|
|
|
2015-04-23 16:43:58 +00:00
|
|
|
|
2012-09-01 12:42:53 +00:00
|
|
|
box.insert_child_at_index(container, position);
|
2012-08-28 21:10:44 +00:00
|
|
|
this.statusArea[role] = indicator;
|
2017-10-31 00:38:18 +00:00
|
|
|
let destroyId = indicator.connect('destroy', emitter => {
|
2012-08-28 21:10:44 +00:00
|
|
|
delete this.statusArea[role];
|
|
|
|
emitter.disconnect(destroyId);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2017-12-02 00:27:35 +00:00
|
|
|
indicator.connect('menu-set', this._onMenuSet.bind(this));
|
2015-04-28 09:05:58 +00:00
|
|
|
this._onMenuSet(indicator);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-08-25 15:46:25 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
addToStatusArea(role, indicator, position, box) {
|
2012-08-26 14:05:46 +00:00
|
|
|
if (this.statusArea[role])
|
2022-02-07 14:14:06 +00:00
|
|
|
throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`);
|
2011-08-22 21:19:13 +00:00
|
|
|
|
|
|
|
if (!(indicator instanceof PanelMenu.Button))
|
|
|
|
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
|
|
|
|
2022-01-18 20:02:04 +00:00
|
|
|
position ??= 0;
|
2012-08-28 21:10:44 +00:00
|
|
|
let boxes = {
|
|
|
|
left: this._leftBox,
|
|
|
|
center: this._centerBox,
|
2019-08-20 21:43:54 +00:00
|
|
|
right: this._rightBox,
|
2012-08-28 21:10:44 +00:00
|
|
|
};
|
|
|
|
let boxContainer = boxes[box] || this._rightBox;
|
2012-09-01 12:42:53 +00:00
|
|
|
this.statusArea[role] = indicator;
|
2012-08-28 21:10:44 +00:00
|
|
|
this._addToPanelBox(role, indicator, position, boxContainer);
|
2011-08-22 21:19:13 +00:00
|
|
|
return indicator;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-09-21 20:19:27 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onMenuSet(indicator) {
|
2019-06-29 23:04:32 +00:00
|
|
|
if (!indicator.menu || indicator.menu._openChangedId)
|
2015-04-28 09:05:58 +00:00
|
|
|
return;
|
|
|
|
|
2022-09-05 00:21:29 +00:00
|
|
|
this.menuManager.addMenu(indicator.menu);
|
|
|
|
|
2015-04-28 09:05:58 +00:00
|
|
|
indicator.menu._openChangedId = indicator.menu.connect('open-state-changed',
|
2017-10-31 00:38:18 +00:00
|
|
|
(menu, isOpen) => {
|
2015-04-28 09:05:58 +00:00
|
|
|
let boxAlignment;
|
|
|
|
if (this._leftBox.contains(indicator.container))
|
|
|
|
boxAlignment = Clutter.ActorAlign.START;
|
|
|
|
else if (this._centerBox.contains(indicator.container))
|
|
|
|
boxAlignment = Clutter.ActorAlign.CENTER;
|
|
|
|
else if (this._rightBox.contains(indicator.container))
|
|
|
|
boxAlignment = Clutter.ActorAlign.END;
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (boxAlignment === Main.messageTray.bannerAlignment)
|
2015-04-28 09:05:58 +00:00
|
|
|
Main.messageTray.bannerBlocked = isOpen;
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2012-09-01 12:42:53 +00:00
|
|
|
}
|
2018-02-05 18:46:22 +00:00
|
|
|
|
|
|
|
_getDraggableWindowForPosition(stageX) {
|
|
|
|
let workspaceManager = global.workspace_manager;
|
2020-04-03 23:52:29 +00:00
|
|
|
const windows = workspaceManager.get_active_workspace().list_windows();
|
|
|
|
const allWindowsByStacking =
|
|
|
|
global.display.sort_windows_by_stacking(windows).reverse();
|
2018-02-05 18:46:22 +00:00
|
|
|
|
|
|
|
return allWindowsByStacking.find(metaWindow => {
|
|
|
|
let rect = metaWindow.get_frame_rect();
|
|
|
|
return metaWindow.is_on_primary_monitor() &&
|
|
|
|
metaWindow.showing_on_its_workspace() &&
|
2023-08-07 00:51:19 +00:00
|
|
|
metaWindow.get_window_type() !== Meta.WindowType.DESKTOP &&
|
2018-02-05 18:46:22 +00:00
|
|
|
metaWindow.maximized_vertically &&
|
2019-01-29 01:18:52 +00:00
|
|
|
stageX > rect.x && stageX < rect.x + rect.width;
|
2018-02-05 18:46:22 +00:00
|
|
|
});
|
|
|
|
}
|
2011-11-20 17:56:27 +00:00
|
|
|
});
|