diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-08 16:02:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-08 16:02:14 +0000 |
commit | 52f118cd4c2fbdd81a6ef463835a20cb3d6a3667 (patch) | |
tree | 0904907fa04c7c1fe36fe8cdefab7e27b477723a /extensions/vertical-workspaces/lib/workspaceThumbnail.js | |
parent | Updating multi-monitors-add-on to version 26 [d8ea040]. (diff) | |
download | gnome-shell-extensions-extra-52f118cd4c2fbdd81a6ef463835a20cb3d6a3667.tar.xz gnome-shell-extensions-extra-52f118cd4c2fbdd81a6ef463835a20cb3d6a3667.zip |
Updating vertical-workspaces to version 28 [891a8df].
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | extensions/vertical-workspaces/lib/workspaceThumbnail.js (renamed from extensions/vertical-workspaces/workspaceThumbnail.js) | 810 |
1 files changed, 599 insertions, 211 deletions
diff --git a/extensions/vertical-workspaces/workspaceThumbnail.js b/extensions/vertical-workspaces/lib/workspaceThumbnail.js index bbf8900..d0bc206 100644 --- a/extensions/vertical-workspaces/workspaceThumbnail.js +++ b/extensions/vertical-workspaces/lib/workspaceThumbnail.js @@ -1,7 +1,7 @@ /** - * Vertical Workspaces + * V-Shell (Vertical Workspaces) * workspaceThumbnail.js - * + * * @author GdH <G-dH@github.com> * @copyright 2022 - 2023 * @license GPL-3.0 @@ -10,7 +10,7 @@ 'use strict'; -const { Clutter, Graphene, Meta, Shell, St } = imports.gi; +const { GLib, Clutter, Graphene, Meta, Shell, St } = imports.gi; const DND = imports.ui.dnd; const Main = imports.ui.main; const Background = imports.ui.background; @@ -22,70 +22,65 @@ const ControlsState = imports.ui.overviewControls.ControlsState; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); -const _Util = Me.imports.util; +// gettext +const _ = Me.imports.lib.settings._; + +const _Util = Me.imports.lib.util; +const shellVersion = _Util.shellVersion; + let _overrides; const WORKSPACE_CUT_SIZE = 10; -const original_MAX_THUMBNAIL_SCALE = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; -let MAX_THUMBNAIL_SCALE; - -var opt = null; +const _originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; +let opt = null; function update(reset = false) { - if (_overrides) { + if (_overrides) _overrides.removeAll(); - } + if (reset) { - if (original_MAX_THUMBNAIL_SCALE) - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = original_MAX_THUMBNAIL_SCALE; + if (_originalMaxThumbnailScale) + WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = _originalMaxThumbnailScale; _overrides = null; opt = null; return; } - opt = Me.imports.settings.opt; - - MAX_THUMBNAIL_SCALE = opt.MAX_THUMBNAIL_SCALE; - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = MAX_THUMBNAIL_SCALE; - + opt = Me.imports.lib.settings.opt; _overrides = new _Util.Overrides(); + // don't limit max thumbnail scale for other clients than overview, for example AATWS. + WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1; + _overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); + _overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { - _overrides.addOverride('ThumbnailsBox', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxVertical); - } else { - _overrides.addOverride('ThumbnailsBox', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxHorizontal); - } + // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation. + Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION; } - -var WorkspaceThumbnailCommon = { - after__init: function () { - - //radius of ws thumbnail background - this.add_style_class_name('ws-tmb'); +const WorkspaceThumbnailCommon = { + // injection to _init() + after__init() { + // layout manager allows aligning widget children + this.layout_manager = new Clutter.BinLayout(); + // adding layout manager to tmb widget breaks wallpaper background aligning and rounded corners + // unless border is removed + if (opt.SHOW_WS_TMB_BG) + this.add_style_class_name('ws-tmb-labeled'); // add workspace thumbnails labels if enabled if (opt.SHOW_WST_LABELS) { // 0 - disable - // layout manager allows aligning widget children - this.layout_manager = new Clutter.BinLayout(); - // adding layout manager to tmb widget breaks wallpaper background aligning and rounded corners - // unless border is removed - if (opt.SHOW_WS_TMB_BG) - this.add_style_class_name('ws-tmb-labeled'); - - const getLabel = function() { + const getLabel = function () { const wsIndex = this.metaWorkspace.index(); let label = `${wsIndex + 1}`; if (opt.SHOW_WST_LABELS === 2) { // 2 - index + workspace name const settings = ExtensionUtils.getSettings('org.gnome.desktop.wm.preferences'); const wsLabels = settings.get_strv('workspace-names'); - if (wsLabels.length > wsIndex && wsLabels[wsIndex]) { + if (wsLabels.length > wsIndex && wsLabels[wsIndex]) label += `: ${wsLabels[wsIndex]}`; - } } else if (opt.SHOW_WST_LABELS === 3) { // 3- index + app name // global.display.get_tab_list offers workspace filtering using the second argument, but... // ... it sometimes includes windows from other workspaces, like minimized VBox machines, after Shell restarts @@ -93,16 +88,16 @@ var WorkspaceThumbnailCommon = { w => w.get_monitor() === this.monitorIndex && w.get_workspace().index() === wsIndex)[0]; if (metaWin) { - let tracker = Shell.WindowTracker.get_default(); - label += `: ${tracker.get_window_app(metaWin).get_name()}`; + const tracker = Shell.WindowTracker.get_default(); + const app = tracker.get_window_app(metaWin); + label += `: ${app ? app.get_name() : ''}`; } } else if (opt.SHOW_WST_LABELS === 4) { const metaWin = global.display.get_tab_list(0, null).filter( w => w.get_monitor() === this.monitorIndex && w.get_workspace().index() === wsIndex)[0]; - if (metaWin) { + if (metaWin) label += `: ${metaWin.title}`; - } } return label; }.bind(this); @@ -124,28 +119,96 @@ var WorkspaceThumbnailCommon = { this.add_child(this._wsLabel); this.set_child_above_sibling(this._wsLabel, null); - this._wsIndexSigId = this.metaWorkspace.connect('notify::workspace-index', () => { + this._wsIndexConId = this.metaWorkspace.connect('notify::workspace-index', () => { const newLabel = getLabel(); this._wsLabel.text = newLabel; + // avoid possibility of accessing non existing ws + if (this._updateLabelTimeout) { + GLib.source_remove(this._updateLabelTimeout); + this._updateLabelTimeout = 0; + } + }); + this._nWindowsConId = this.metaWorkspace.connect('notify::n-windows', () => { + // wait for new information + this._updateLabelTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { + const newLabel = getLabel(); + this._wsLabel.text = newLabel; + this._updateLabelTimeout = 0; + return GLib.SOURCE_REMOVE; + }); }); + } + + if (opt.CLOSE_WS_BUTTON_MODE) { + const closeButton = new St.Icon({ + style_class: 'workspace-close-button', + icon_name: 'window-close-symbolic', + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + reactive: true, + opacity: 0, + }); + + closeButton.connect('button-release-event', () => { + if (opt.CLOSE_WS_BUTTON_MODE) { + this._closeWorkspace(); + return Clutter.EVENT_STOP; + } else { + return Clutter.EVENT_PROPAGATE; + } + }); + + closeButton.connect('button-press-event', () => { + return Clutter.EVENT_STOP; + }); + + closeButton.connect('enter-event', () => { + closeButton.opacity = 255; + if (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())) { + // color the button red if ready to react on clicks + if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && _Util.isCtrlPressed())) + closeButton.add_style_class_name('workspace-close-button-hover'); + } + }); + + closeButton.connect('leave-event', () => { + closeButton.remove_style_class_name('workspace-close-button-hover'); + }); + + this.add_child(closeButton); + this._closeButton = closeButton; + + this.reactive = true; + this._lastCloseClickTime = 0; + } - this.connect('destroy', () => this.metaWorkspace.disconnect(this._wsIndexSigId)); + if (opt.SHOW_WST_LABELS_ON_HOVER) + this._wsLabel.opacity = 0; + this.connect('enter-event', () => { + if (opt.CLOSE_WS_BUTTON_MODE && (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index()))) + this._closeButton.opacity = 200; if (opt.SHOW_WST_LABELS_ON_HOVER) { - this._wsLabel.opacity = 0; - this.reactive = true; - this.connect('enter-event', ()=> this._wsLabel.ease({ + this._wsLabel.ease({ duration: 100, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - opacity: this._wsLabel._maxOpacity - })); - this.connect('leave-event', ()=> this._wsLabel.ease({ + opacity: this._wsLabel._maxOpacity, + }); + } + }); + + this.connect('leave-event', () => { + this._closeButton.opacity = 0; + if (opt.SHOW_WST_LABELS_ON_HOVER) { + this._wsLabel.ease({ duration: 100, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - opacity: 0 - })); + opacity: 0, + }); } - } + }); if (opt.SHOW_WS_TMB_BG) { this._bgManager = new Background.BackgroundManager({ @@ -157,32 +220,58 @@ var WorkspaceThumbnailCommon = { this._viewport.set_child_below_sibling(this._bgManager.backgroundActor, null); - this.connect('destroy', function () { + this.connect('destroy', () => { if (this._bgManager) this._bgManager.destroy(); this._bgManager = null; - }.bind(this)); + }); + // full brightness of the thumbnail bg draws unnecessary attention + // there is a grey bg under the wallpaper this._bgManager.backgroundActor.opacity = 220; + } - // this all is just for the small border radius... - /*const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const cornerRadius = scaleFactor * BACKGROUND_CORNER_RADIUS_PIXELS; - const backgroundContent = this._bgManager.backgroundActor.content; - backgroundContent.rounded_clip_radius = cornerRadius; + this.connect('destroy', () => { + if (this._wsIndexConId) + this.metaWorkspace.disconnect(this._wsIndexConId); + + if (this._nWindowsConId) + this.metaWorkspace.disconnect(this._nWindowsConId); + + if (this._updateLabelTimeout) + GLib.source_remove(this._updateLabelTimeout); + + if (this._bgManager) + this._bgManager.destroy(); + }); + }, + + _closeWorkspace() { + // CLOSE_WS_BUTTON_MODE 1: single click, 2: double-click, 3: Ctrl + + if (opt.CLOSE_WS_BUTTON_MODE === 2) { + const doubleClickTime = Clutter.Settings.get_default().double_click_time; + const clickDelay = Date.now() - this._lastCloseClickTime; + if (clickDelay > doubleClickTime) { + this._lastCloseClickTime = Date.now(); + return; + } + } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !_Util.isCtrlPressed()) { + return; + } - // the original clip has some addition at the bottom - const rect = new Graphene.Rect(); - rect.origin.x = this._viewport.x; - rect.origin.y = this._viewport.y; - rect.size.width = this._viewport.width; - rect.size.height = this._viewport.height; + // close windows on this monitor + const windows = global.display.get_tab_list(0, null).filter( + w => w.get_monitor() === this.monitorIndex && w.get_workspace() === this.metaWorkspace + ); - this._bgManager.backgroundActor.content.set_rounded_clip_bounds(rect);*/ + for (let i = 0; i < windows.length; i++) { + if (!windows[i].is_on_all_workspaces()) + windows[i].delete(global.get_current_time() + i); } }, - activate: function(time) { + activate(time) { if (this.state > ThumbnailState.NORMAL) return; @@ -199,112 +288,214 @@ var WorkspaceThumbnailCommon = { } else { this.metaWorkspace.activate(time); } - } else { - if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && wsIndex < lastWsIndex) { - if (stateAdjustment.value > 1) { - stateAdjustment.value = 1; - } + } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && wsIndex < lastWsIndex) { + if (stateAdjustment.value > 1) + stateAdjustment.value = 1; - // spread windows - // in OVERVIEW MODE 2 windows are not spread and workspace is not scaled - // we need to repeat transition to the overview state 1 (window picker), but with spreading windows animation - if (this.metaWorkspace.active) { - opt.WORKSPACE_MODE = 1; - const stateAdjustment = Main.overview._overview.controls._stateAdjustment - // setting value to 0 would reset WORKSPACE_MODE - stateAdjustment.value = 0.01; - stateAdjustment.ease(1, { - duration: 200, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - /*const adjustment = Main.overview._overview.controls._workspacesDisplay._workspacesViews[0]._workspaces[wsIndex].stateAdjustment; - adjustment.value = 0; - adjustment.ease(1, { - duration: 200, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - });*/ - } else { - // switch ws - this.metaWorkspace.activate(time); - } - // a click on the current workspace should go back to the main view - } else if (this.metaWorkspace.active) { - Main.overview.hide(); + // spread windows + // in OVERVIEW MODE 2 windows are not spread and workspace is not scaled + // we need to repeat transition to the overview state 1 (window picker), but with spreading windows animation + if (this.metaWorkspace.active) { + Main.overview._overview.controls._searchController._setSearchActive(false); + opt.WORKSPACE_MODE = 1; + // setting value to 0 would reset WORKSPACE_MODE + stateAdjustment.value = 0.01; + stateAdjustment.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); } else { + // switch ws this.metaWorkspace.activate(time); } + // a click on the current workspace should go back to the main view + } else if (this.metaWorkspace.active) { + Main.overview.hide(); + } else { + this.metaWorkspace.activate(time); } - } -} + }, -// ThumbnailsBox Vertical + // Draggable target interface used only by ThumbnailsBox + handleDragOverInternal(source, actor, time) { + if (source === Main.xdndHandler) { + this.metaWorkspace.activate(time); + return DND.DragMotionResult.CONTINUE; + } -var ThumbnailsBoxVertical = { - _activateThumbnailAtPoint: function(stageX, stageY, time) { - const [r_, x, y] = this.transform_stage_point(stageX, stageY); + if (this.state > ThumbnailState.NORMAL) + return DND.DragMotionResult.CONTINUE; - const thumbnail = this._thumbnails.find(t => y >= t.y && y <= t.y + t.height); - if (thumbnail) { - thumbnail.activate(time); - } - }, + if (source.metaWindow && + !this._isMyWindow(source.metaWindow.get_compositor_private())) + return DND.DragMotionResult.MOVE_DROP; + if (source.app && source.app.can_open_new_window()) + return DND.DragMotionResult.COPY_DROP; + if (!source.app && source.shellWorkspaceLaunch) + return DND.DragMotionResult.COPY_DROP; - _getPlaceholderTarget: function(index, spacing, rtl) { - const workspace = this._thumbnails[index]; + if (source instanceof imports.ui.appDisplay.FolderIcon) + return DND.DragMotionResult.COPY_DROP; - let targetY1; - let targetY2; - if (rtl) { - const baseY = workspace.y + workspace.height; - targetY1 = baseY - WORKSPACE_CUT_SIZE; - targetY2 = baseY + spacing + WORKSPACE_CUT_SIZE; - } else { - targetY1 = workspace.y - spacing - WORKSPACE_CUT_SIZE; - targetY2 = workspace.y + WORKSPACE_CUT_SIZE; - } + return DND.DragMotionResult.CONTINUE; + }, - if (index === 0) { - if (rtl) - targetY2 -= spacing + WORKSPACE_CUT_SIZE; - else - targetY1 += spacing + WORKSPACE_CUT_SIZE; + acceptDropInternal(source, actor, time) { + if (this.state > ThumbnailState.NORMAL) + return false; + + if (source.metaWindow) { + let win = source.metaWindow.get_compositor_private(); + if (this._isMyWindow(win)) + return false; + + let metaWindow = win.get_meta_window(); + Main.moveWindowToMonitorAndWorkspace(metaWindow, + this.monitorIndex, this.metaWorkspace.index()); + return true; + } else if (source.app && source.app.can_open_new_window()) { + if (source.animateLaunchAtPos) + source.animateLaunchAtPos(actor.x, actor.y); + + source.app.open_new_window(this.metaWorkspace.index()); + return true; + } else if (!source.app && source.shellWorkspaceLaunch) { + // While unused in our own drag sources, shellWorkspaceLaunch allows + // extensions to define custom actions for their drag sources. + source.shellWorkspaceLaunch({ + workspace: this.metaWorkspace.index(), + timestamp: time, + }); + return true; + } else if (source instanceof imports.ui.appDisplay.FolderIcon) { + if (shellVersion >= 44) { + for (let app of source.view._apps) { + // const app = Shell.AppSystem.get_default().lookup_app(id); + app.open_new_window(this.metaWorkspace.index()); + } + } else { + for (let id of source.view._appIds) { + const app = Shell.AppSystem.get_default().lookup_app(id); + app.open_new_window(this.metaWorkspace.index()); + } + } } - if (index === this._dropPlaceholderPos) { - const placeholderHeight = this._dropPlaceholder.get_height() + spacing; - if (rtl) - targetY2 += placeholderHeight; - else - targetY1 -= placeholderHeight; + return false; + }, +}; + +const ThumbnailsBoxCommon = { + after__init(scrollAdjustment, monitorIndex, orientation = opt.ORIENTATION) { + this._boxOrientation = orientation; + }, + + _activateThumbnailAtPoint(stageX, stageY, time, activateCurrent = false) { + if (activateCurrent) { + const thumbnail = this._thumbnails.find(t => t.metaWorkspace.active); + if (thumbnail) + thumbnail.activate(time); + return; } + const [r_, x, y] = this.transform_stage_point(stageX, stageY); - return [targetY1, targetY2]; + let thumbnail; + + if (this._boxOrientation) + thumbnail = this._thumbnails.find(t => y >= t.y && y <= t.y + t.height); + else + thumbnail = this._thumbnails.find(t => x >= t.x && x <= t.x + t.width); + + if (thumbnail) + thumbnail.activate(time); }, - _withinWorkspace: function(y, index, rtl) { - const length = this._thumbnails.length; - const workspace = this._thumbnails[index]; + acceptDrop(source, actor, x, y, time) { + if (this._dropWorkspace !== -1) { + return this._thumbnails[this._dropWorkspace].acceptDropInternal(source, actor, time); + } else if (this._dropPlaceholderPos !== -1) { + if (!source.metaWindow && + (!source.app || !source.app.can_open_new_window()) && + (source.app || !source.shellWorkspaceLaunch) && + !(source instanceof imports.ui.appDisplay.FolderIcon)) + return false; + + + let isWindow = !!source.metaWindow; + + let newWorkspaceIndex; + [newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1]; + this._spliceIndex = newWorkspaceIndex; + + Main.wm.insertWorkspace(newWorkspaceIndex); + + if (isWindow) { + // Move the window to our monitor first if necessary. + let thumbMonitor = this._thumbnails[newWorkspaceIndex].monitorIndex; + Main.moveWindowToMonitorAndWorkspace(source.metaWindow, + thumbMonitor, newWorkspaceIndex, true); + } else if (source.app && source.app.can_open_new_window()) { + if (source.animateLaunchAtPos) + source.animateLaunchAtPos(actor.x, actor.y); + + source.app.open_new_window(newWorkspaceIndex); + } else if (!source.app && source.shellWorkspaceLaunch) { + // While unused in our own drag sources, shellWorkspaceLaunch allows + // extensions to define custom actions for their drag sources. + source.shellWorkspaceLaunch({ + workspace: newWorkspaceIndex, + timestamp: time, + }); + } else if (source instanceof imports.ui.appDisplay.FolderIcon) { + if (shellVersion >= 44) { + for (let app of source.view._apps) { + // const app = Shell.AppSystem.get_default().lookup_app(id); + app.open_new_window(newWorkspaceIndex); + } + } else { + for (let id of source.view._appIds) { + const app = Shell.AppSystem.get_default().lookup_app(id); + app.open_new_window(newWorkspaceIndex); + } + } + } - let workspaceY1 = workspace.y + WORKSPACE_CUT_SIZE; - let workspaceY2 = workspace.y + workspace.height - WORKSPACE_CUT_SIZE; + if (source.app || (!source.app && source.shellWorkspaceLaunch)) { + // This new workspace will be automatically removed if the application fails + // to open its first window within some time, as tracked by Shell.WindowTracker. + // Here, we only add a very brief timeout to avoid the _immediate_ removal of the + // workspace while we wait for the startup sequence to load. + let workspaceManager = global.workspace_manager; + Main.wm.keepWorkspaceAlive(workspaceManager.get_workspace_by_index(newWorkspaceIndex), + WorkspaceThumbnail.WORKSPACE_KEEP_ALIVE_TIME); + } - if (index === length - 1) { - if (rtl) - workspaceY1 -= WORKSPACE_CUT_SIZE; - else - workspaceY2 += WORKSPACE_CUT_SIZE; - } + // Start the animation on the workspace (which is actually + // an old one which just became empty) + let thumbnail = this._thumbnails[newWorkspaceIndex]; + this._setThumbnailState(thumbnail, ThumbnailState.NEW); + thumbnail.slide_position = 1; + thumbnail.collapse_fraction = 1; - return y > workspaceY1 && y <= workspaceY2; + this._queueUpdateStates(); + + return true; + } else { + return false; + } }, - handleDragOver: function(source, actor, x, y, time) { + handleDragOver(source, actor, x, y, time) { + // switch axis for vertical orientation + if (this._boxOrientation) + x = y; + if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - source != Main.xdndHandler) + source !== Main.xdndHandler && !(source instanceof imports.ui.appDisplay.FolderIcon)) return DND.DragMotionResult.CONTINUE; const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; @@ -321,34 +512,132 @@ var ThumbnailsBoxVertical = { const [targetStart, targetEnd] = this._getPlaceholderTarget(index, spacing, rtl); - if (y > targetStart && y <= targetEnd) { + if (x > targetStart && x <= targetEnd) { placeholderPos = index; break; } } - if (this._withinWorkspace(y, index, rtl)) { + if (this._withinWorkspace(x, index, rtl)) { this._dropWorkspace = index; break; } } - if (this._dropPlaceholderPos != placeholderPos) { + if (this._dropPlaceholderPos !== placeholderPos) { this._dropPlaceholderPos = placeholderPos; this.queue_relayout(); } - if (this._dropWorkspace != -1) + if (this._dropWorkspace !== -1) return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, actor, time); - else if (this._dropPlaceholderPos != -1) + else if (this._dropPlaceholderPos !== -1) return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP; else return DND.DragMotionResult.CONTINUE; }, - //vfunc_get_preferred_width: function(forHeight) { + _getPlaceholderTarget(...args) { + if (this._boxOrientation) + return ThumbnailsBoxVertical._getPlaceholderTarget.bind(this)(...args); + else + return ThumbnailsBoxHorizontal._getPlaceholderTarget.bind(this)(...args); + }, + + _withinWorkspace(...args) { + if (this._boxOrientation) + return ThumbnailsBoxVertical._withinWorkspace.bind(this)(...args); + else + return ThumbnailsBoxHorizontal._withinWorkspace.bind(this)(...args); + }, + + get_preferred_custom_width(...args) { + if (this._boxOrientation) + return ThumbnailsBoxVertical.get_preferred_custom_width.bind(this)(...args); + else + return ThumbnailsBoxHorizontal.get_preferred_custom_width.bind(this)(...args); + }, + + get_preferred_custom_height(...args) { + if (this._boxOrientation) + return ThumbnailsBoxVertical.get_preferred_custom_height.bind(this)(...args); + else + return ThumbnailsBoxHorizontal.get_preferred_custom_height.bind(this)(...args); + }, + + vfunc_allocate(...args) { + if (this._boxOrientation) + return ThumbnailsBoxVertical.vfunc_allocate.bind(this)(...args); + else + return ThumbnailsBoxHorizontal.vfunc_allocate.bind(this)(...args); + }, + + _updateShouldShow(...args) { + if (this._boxOrientation) + return ThumbnailsBoxVertical._updateShouldShow.bind(this)(...args); + else + return ThumbnailsBoxHorizontal._updateShouldShow.bind(this)(...args); + }, +}; + +const ThumbnailsBoxVertical = { + _getPlaceholderTarget(index, spacing, rtl) { + this._dropPlaceholder.add_style_class_name('placeholder-vertical'); + const workspace = this._thumbnails[index]; + + let targetY1; + let targetY2; + + if (rtl) { + const baseY = workspace.y + workspace.height; + targetY1 = baseY - WORKSPACE_CUT_SIZE; + targetY2 = baseY + spacing + WORKSPACE_CUT_SIZE; + } else { + targetY1 = workspace.y - spacing - WORKSPACE_CUT_SIZE; + targetY2 = workspace.y + WORKSPACE_CUT_SIZE; + } + + if (index === 0) { + if (rtl) + targetY2 -= spacing + WORKSPACE_CUT_SIZE; + else + targetY1 += spacing + WORKSPACE_CUT_SIZE; + } + + if (index === this._dropPlaceholderPos) { + const placeholderHeight = this._dropPlaceholder.get_height() + spacing; + if (rtl) + targetY2 += placeholderHeight; + else + targetY1 -= placeholderHeight; + } + + return [targetY1, targetY2]; + }, + + _withinWorkspace(y, index, rtl) { + const length = this._thumbnails.length; + const workspace = this._thumbnails[index]; + + let workspaceY1 = workspace.y + WORKSPACE_CUT_SIZE; + let workspaceY2 = workspace.y + workspace.height - WORKSPACE_CUT_SIZE; + + if (index === length - 1) { + if (rtl) + workspaceY1 -= WORKSPACE_CUT_SIZE; + else + workspaceY2 += WORKSPACE_CUT_SIZE; + } + + return y > workspaceY1 && y <= workspaceY2; + }, + + // vfunc_get_preferred_width: function(forHeight) { // override of this vfunc doesn't work for some reason (tested on Ubuntu and Fedora), it's not reachable - get_preferred_custom_width: function(forHeight) { + get_preferred_custom_width(forHeight) { + if (!this.visible) + return [0, 0]; + if (forHeight === -1) return this.get_preferred_custom_height(forHeight); @@ -363,14 +652,16 @@ var ThumbnailsBoxVertical = { const avail = forHeight - totalSpacing; let scale = (avail / nWorkspaces) / this._porthole.height; - scale = Math.min(scale, MAX_THUMBNAIL_SCALE); + // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); const width = Math.round(this._porthole.width * scale); - - return themeNode.adjust_preferred_width(width, width); + return themeNode.adjust_preferred_height(width, width); }, - get_preferred_custom_height: function(_forWidth) { + get_preferred_custom_height(_forWidth) { + if (!this.visible) + return [0, 0]; + // Note that for getPreferredHeight/Width we cheat a bit and skip propagating // the size request to our children because we know how big they are and know // that the actors aren't depending on the virtual functions being called. @@ -379,34 +670,29 @@ var ThumbnailsBoxVertical = { let spacing = themeNode.get_length('spacing'); let nWorkspaces = this._thumbnails.length; - let totalSpacing = (nWorkspaces - 1) * spacing; + // remove also top/bottom box padding + let totalSpacing = (nWorkspaces - 3) * spacing; const ratio = this._porthole.width / this._porthole.height; - const tmbHeight = _forWidth / ratio; - - const naturalheight = this._thumbnails.reduce((accumulator, thumbnail, index) => { - //let workspaceSpacing = 0; + const tmbHeight = themeNode.adjust_for_width(_forWidth) / ratio; + const naturalheight = this._thumbnails.reduce((accumulator, thumbnail/* , index*/) => { const progress = 1 - thumbnail.collapse_fraction; - //const height = (this._porthole.height * MAX_THUMBNAIL_SCALE + workspaceSpacing) * progress; - const height = (tmbHeight) * progress; + const height = tmbHeight * progress; return accumulator + height; }, 0); - //return themeNode.adjust_preferred_height(totalSpacing, naturalheight); - // we need to calculate the height precisely as it need to align with the workspacesDisplay because of transition animation - // This works perfectly for fullHD monitor, for some reason 5:4 aspect ratio monitor adds unnecessary pixels to the final height of the thumbnailsBox - return [totalSpacing, naturalheight]; + return themeNode.adjust_preferred_width(totalSpacing, naturalheight); }, // removes extra space (extraWidth in the original function), we need the box as accurate as possible // for precise app grid transition animation - vfunc_allocate: function(box) { + vfunc_allocate(box) { this.set_allocation(box); - let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; + let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; - if (this._thumbnails.length == 0) // not visible + if (this._thumbnails.length === 0) // not visible return; let themeNode = this.get_theme_node(); @@ -416,16 +702,19 @@ var ThumbnailsBoxVertical = { const portholeHeight = this._porthole.height; const spacing = themeNode.get_length('spacing'); - const nWorkspaces = this._thumbnails.length; + /* const nWorkspaces = this._thumbnails.length;*/ // Compute the scale we'll need once everything is updated, // unless we are currently transitioning if (this._expandFraction === 1) { - const totalSpacing = (nWorkspaces - 1) * spacing; - const availableHeight = (box.get_height() - totalSpacing) / nWorkspaces; + // remove size "breathing" during adding/removing workspaces + + /* const totalSpacing = (nWorkspaces - 1) * spacing; + const availableHeight = (box.get_height() - totalSpacing) / nWorkspaces; */ const hScale = box.get_width() / portholeWidth; - const vScale = availableHeight / portholeHeight; + /* const vScale = availableHeight / portholeHeight;*/ + const vScale = box.get_height() / portholeHeight; const newScale = Math.min(hScale, vScale); if (newScale !== this._targetScale) { @@ -466,13 +755,20 @@ var ThumbnailsBoxVertical = { let y = box.y1; - if (this._dropPlaceholderPos == -1) { + if (this._dropPlaceholderPos === -1) { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); + if (shellVersion >= 44) { + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.hide(); + }); + } else { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.hide(); + }); + } } let childBox = new Clutter.ActorBox(); @@ -486,7 +782,7 @@ var ThumbnailsBoxVertical = { const x2 = x1 + thumbnailWidth; if (i === this._dropPlaceholderPos) { - let [, placeholderHeight] = this._dropPlaceholder.get_preferred_height(-1); + let [, placeholderHeight] = this._dropPlaceholder.get_preferred_width(-1); childBox.x1 = x1; childBox.x2 = x2; @@ -500,9 +796,16 @@ var ThumbnailsBoxVertical = { this._dropPlaceholder.allocate(childBox); - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); + if (shellVersion >= 44) { + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.show(); + }); + } else { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.show(); + }); + } y += placeholderHeight + spacing; } @@ -559,51 +862,119 @@ var ThumbnailsBoxVertical = { this._indicator.allocate(childBox); }, - _updateShouldShow: function() { - // set current workspace indicator border radius - // here just 'cause it's easier than adding to init - this._indicator.add_style_class_name('ws-tmb'); - + _updateShouldShow() { const shouldShow = opt.SHOW_WS_TMB; if (this._shouldShow === shouldShow) return; this._shouldShow = shouldShow; this.notify('should-show'); - } -} + }, +}; // ThumbnailsBox Horizontal -var ThumbnailsBoxHorizontal = { - get_preferred_custom_width: function(_forHeight) { +const ThumbnailsBoxHorizontal = { + _getPlaceholderTarget(index, spacing, rtl) { + const workspace = this._thumbnails[index]; + + let targetX1; + let targetX2; + + if (rtl) { + const baseX = workspace.x + workspace.width; + targetX1 = baseX - WORKSPACE_CUT_SIZE; + targetX2 = baseX + spacing + WORKSPACE_CUT_SIZE; + } else { + targetX1 = workspace.x - spacing - WORKSPACE_CUT_SIZE; + targetX2 = workspace.x + WORKSPACE_CUT_SIZE; + } + + if (index === 0) { + if (rtl) + targetX2 -= spacing + WORKSPACE_CUT_SIZE; + else + targetX1 += spacing + WORKSPACE_CUT_SIZE; + } + + if (index === this._dropPlaceholderPos) { + const placeholderWidth = this._dropPlaceholder.get_width() + spacing; + if (rtl) + targetX2 += placeholderWidth; + else + targetX1 -= placeholderWidth; + } + + return [targetX1, targetX2]; + }, + + _withinWorkspace(x, index, rtl) { + const length = this._thumbnails.length; + const workspace = this._thumbnails[index]; + + let workspaceX1 = workspace.x + WORKSPACE_CUT_SIZE; + let workspaceX2 = workspace.x + workspace.width - WORKSPACE_CUT_SIZE; + + if (index === length - 1) { + if (rtl) + workspaceX1 -= WORKSPACE_CUT_SIZE; + else + workspaceX2 += WORKSPACE_CUT_SIZE; + } + + return x > workspaceX1 && x <= workspaceX2; + }, + + get_preferred_custom_height(forWidth) { + let themeNode = this.get_theme_node(); + + forWidth = themeNode.adjust_for_width(forWidth); + + let spacing = themeNode.get_length('spacing'); + let nWorkspaces = this._thumbnails.length; + let totalSpacing = (nWorkspaces - 1) * spacing; + + const avail = forWidth - totalSpacing; + + let scale = (avail / nWorkspaces) / this._porthole.width; + // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); + + const height = Math.round(this._porthole.height * scale); + return themeNode.adjust_preferred_height(height, height); + }, + + get_preferred_custom_width(_forHeight) { // Note that for getPreferredHeight/Width we cheat a bit and skip propagating // the size request to our children because we know how big they are and know // that the actors aren't depending on the virtual functions being called. + if (!this.visible) + return [0, 0]; + let themeNode = this.get_theme_node(); let spacing = themeNode.get_length('spacing'); let nWorkspaces = this._thumbnails.length; - let totalSpacing = (nWorkspaces - 1) * spacing; + // remove also left/right box padding from the total spacing + let totalSpacing = (nWorkspaces - 3) * spacing; const ratio = this._porthole.height / this._porthole.width; - const tmbWidth = (_forHeight - 2 * spacing) / ratio; - const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail, index) => { + const tmbWidth = themeNode.adjust_for_height(_forHeight) / ratio; + + const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail) => { const progress = 1 - thumbnail.collapse_fraction; const width = tmbWidth * progress; return accumulator + width; }, 0); - return themeNode.adjust_preferred_width(totalSpacing, naturalWidth); }, vfunc_allocate(box) { this.set_allocation(box); - let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; + let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; - if (this._thumbnails.length == 0) // not visible + if (this._thumbnails.length === 0) // not visible return; let themeNode = this.get_theme_node(); @@ -613,15 +984,18 @@ var ThumbnailsBoxHorizontal = { const portholeHeight = this._porthole.height; const spacing = themeNode.get_length('spacing'); - const nWorkspaces = this._thumbnails.length; + /* const nWorkspaces = this._thumbnails.length; */ // Compute the scale we'll need once everything is updated, // unless we are currently transitioning if (this._expandFraction === 1) { - const totalSpacing = (nWorkspaces - 1) * spacing; + // remove size "breathing" during adding/removing workspaces + + /* const totalSpacing = (nWorkspaces - 1) * spacing; const availableWidth = (box.get_width() - totalSpacing) / nWorkspaces; - const hScale = availableWidth / portholeWidth; + const hScale = availableWidth / portholeWidth; */ + const hScale = box.get_width() / portholeWidth; const vScale = box.get_height() / portholeHeight; const newScale = Math.min(hScale, vScale); @@ -663,13 +1037,20 @@ var ThumbnailsBoxHorizontal = { let x = box.x1; - if (this._dropPlaceholderPos == -1) { + if (this._dropPlaceholderPos === -1) { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); + if (shellVersion >= 44) { + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.hide(); + }); + } else { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.hide(); + }); + } } let childBox = new Clutter.ActorBox(); @@ -697,9 +1078,16 @@ var ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate(childBox); - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); + if (shellVersion >= 44) { + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.show(); + }); + } else { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.show(); + }); + } x += placeholderWidth + spacing; } @@ -756,5 +1144,5 @@ var ThumbnailsBoxHorizontal = { this._indicator.allocate(childBox); }, - _updateShouldShow: ThumbnailsBoxVertical._updateShouldShow -} + _updateShouldShow: ThumbnailsBoxVertical._updateShouldShow, +}; |