diff options
Diffstat (limited to 'js/ui/workspaceSwitcherPopup.js')
-rw-r--r-- | js/ui/workspaceSwitcherPopup.js | 229 |
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 = []; + } +}); |