summaryrefslogtreecommitdiffstats
path: root/js/ui/workspaceSwitcherPopup.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/ui/workspaceSwitcherPopup.js')
-rw-r--r--js/ui/workspaceSwitcherPopup.js229
1 files changed, 229 insertions, 0 deletions
diff --git a/js/ui/workspaceSwitcherPopup.js b/js/ui/workspaceSwitcherPopup.js
new file mode 100644
index 0000000..4613e96
--- /dev/null
+++ b/js/ui/workspaceSwitcherPopup.js
@@ -0,0 +1,229 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WorkspaceSwitcherPopup */
+
+const { Clutter, GLib, GObject, Meta, St } = imports.gi;
+
+const Main = imports.ui.main;
+
+var ANIMATION_TIME = 100;
+var DISPLAY_TIMEOUT = 600;
+
+var WorkspaceSwitcherPopupList = GObject.registerClass(
+class WorkspaceSwitcherPopupList extends St.Widget {
+ _init() {
+ super._init({
+ style_class: 'workspace-switcher',
+ offscreen_redirect: Clutter.OffscreenRedirect.ALWAYS,
+ });
+
+ this._itemSpacing = 0;
+ this._childHeight = 0;
+ this._childWidth = 0;
+ this._orientation = global.workspace_manager.layout_rows == -1
+ ? Clutter.Orientation.VERTICAL
+ : Clutter.Orientation.HORIZONTAL;
+
+ this.connect('style-changed', () => {
+ this._itemSpacing = this.get_theme_node().get_length('spacing');
+ });
+ }
+
+ _getPreferredSizeForOrientation(_forSize) {
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
+ let themeNode = this.get_theme_node();
+
+ let availSize;
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ availSize = workArea.width - themeNode.get_horizontal_padding();
+ else
+ availSize = workArea.height - themeNode.get_vertical_padding();
+
+ let size = 0;
+ for (let child of this.get_children()) {
+ let [, childNaturalHeight] = child.get_preferred_height(-1);
+ let height = childNaturalHeight * workArea.width / workArea.height;
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ size += height * workArea.width / workArea.height;
+ else
+ size += height;
+ }
+
+ let workspaceManager = global.workspace_manager;
+ let spacing = this._itemSpacing * (workspaceManager.n_workspaces - 1);
+ size += spacing;
+ size = Math.min(size, availSize);
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ this._childWidth = (size - spacing) / workspaceManager.n_workspaces;
+ return themeNode.adjust_preferred_width(size, size);
+ } else {
+ this._childHeight = (size - spacing) / workspaceManager.n_workspaces;
+ return themeNode.adjust_preferred_height(size, size);
+ }
+ }
+
+ _getSizeForOppositeOrientation() {
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ this._childHeight = Math.round(this._childWidth * workArea.height / workArea.width);
+ return [this._childHeight, this._childHeight];
+ } else {
+ this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
+ return [this._childWidth, this._childWidth];
+ }
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ return this._getSizeForOppositeOrientation();
+ else
+ return this._getPreferredSizeForOrientation(forWidth);
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ return this._getPreferredSizeForOrientation(forHeight);
+ else
+ return this._getSizeForOppositeOrientation();
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let themeNode = this.get_theme_node();
+ box = themeNode.get_content_box(box);
+
+ let childBox = new Clutter.ActorBox();
+
+ let rtl = this.text_direction == Clutter.TextDirection.RTL;
+ let x = rtl ? box.x2 - this._childWidth : box.x1;
+ let y = box.y1;
+ for (let child of this.get_children()) {
+ childBox.x1 = Math.round(x);
+ childBox.x2 = Math.round(x + this._childWidth);
+ childBox.y1 = Math.round(y);
+ childBox.y2 = Math.round(y + this._childHeight);
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ if (rtl)
+ x -= this._childWidth + this._itemSpacing;
+ else
+ x += this._childWidth + this._itemSpacing;
+ } else {
+ y += this._childHeight + this._itemSpacing;
+ }
+ child.allocate(childBox);
+ }
+ }
+});
+
+var WorkspaceSwitcherPopup = GObject.registerClass(
+class WorkspaceSwitcherPopup extends St.Widget {
+ _init() {
+ super._init({ x: 0,
+ y: 0,
+ width: global.screen_width,
+ height: global.screen_height,
+ style_class: 'workspace-switcher-group' });
+
+ Main.uiGroup.add_actor(this);
+
+ this._timeoutId = 0;
+
+ this._container = new St.BoxLayout({ style_class: 'workspace-switcher-container' });
+ this.add_child(this._container);
+
+ this._list = new WorkspaceSwitcherPopupList();
+ this._container.add_child(this._list);
+
+ this._redisplay();
+
+ this.hide();
+
+ let workspaceManager = global.workspace_manager;
+ this._workspaceManagerSignals = [];
+ this._workspaceManagerSignals.push(workspaceManager.connect('workspace-added',
+ this._redisplay.bind(this)));
+ this._workspaceManagerSignals.push(workspaceManager.connect('workspace-removed',
+ this._redisplay.bind(this)));
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _redisplay() {
+ let workspaceManager = global.workspace_manager;
+
+ this._list.destroy_all_children();
+
+ for (let i = 0; i < workspaceManager.n_workspaces; i++) {
+ let indicator = null;
+
+ if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.UP)
+ indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
+ else if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.DOWN)
+ indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
+ else if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.LEFT)
+ indicator = new St.Bin({ style_class: 'ws-switcher-active-left' });
+ else if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.RIGHT)
+ indicator = new St.Bin({ style_class: 'ws-switcher-active-right' });
+ else
+ indicator = new St.Bin({ style_class: 'ws-switcher-box' });
+
+ this._list.add_actor(indicator);
+
+ }
+
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
+ let [, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
+ let [, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
+ this._container.x = workArea.x + Math.floor((workArea.width - containerNatWidth) / 2);
+ this._container.y = workArea.y + Math.floor((workArea.height - containerNatHeight) / 2);
+ }
+
+ _show() {
+ this._container.ease({
+ opacity: 255,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ this.show();
+ }
+
+ display(direction, activeWorkspaceIndex) {
+ this._direction = direction;
+ this._activeWorkspaceIndex = activeWorkspaceIndex;
+
+ this._redisplay();
+ if (this._timeoutId != 0)
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DISPLAY_TIMEOUT, this._onTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout');
+ this._show();
+ }
+
+ _onTimeout() {
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+ this._container.ease({
+ opacity: 0.0,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this.destroy(),
+ });
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _onDestroy() {
+ if (this._timeoutId)
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+
+ let workspaceManager = global.workspace_manager;
+ for (let i = 0; i < this._workspaceManagerSignals.length; i++)
+ workspaceManager.disconnect(this._workspaceManagerSignals[i]);
+
+ this._workspaceManagerSignals = [];
+ }
+});