summaryrefslogtreecommitdiffstats
path: root/extensions/vertical-workspaces/lib/workspaceThumbnail.js
diff options
context:
space:
mode:
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,
+};