// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported WorkspaceSwitcherPopup */ const { Clutter, GLib, GObject, Meta, St } = imports.gi; const Mainloop = imports.mainloop; 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' }); 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, flags) { this.set_allocation(box, flags); 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, flags); } } }); 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) Mainloop.source_remove(this._timeoutId); this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, this._onTimeout.bind(this)); GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout'); this._show(); } _onTimeout() { Mainloop.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) Mainloop.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 = []; } });