2011-09-28 13:16:26 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2023-07-10 09:53:00 +00:00
|
|
|
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import GObject from 'gi://GObject';
|
|
|
|
import Meta from 'gi://Meta';
|
|
|
|
import Shell from 'gi://Shell';
|
|
|
|
import St from 'gi://St';
|
|
|
|
import Graphene from 'gi://Graphene';
|
|
|
|
|
|
|
|
import * as DND from './dnd.js';
|
|
|
|
import * as Main from './main.js';
|
|
|
|
import {TransientSignalHolder} from '../misc/signalTracker.js';
|
|
|
|
import * as Util from '../misc/util.js';
|
|
|
|
import * as Workspace from './workspace.js';
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2021-02-16 19:15:21 +00:00
|
|
|
const NUM_WORKSPACES_THRESHOLD = 2;
|
|
|
|
|
2021-01-26 00:09:53 +00:00
|
|
|
// The maximum size of a thumbnail is 5% the width and height of the screen
|
2023-07-10 09:53:00 +00:00
|
|
|
export const MAX_THUMBNAIL_SCALE = 0.05;
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const RESCALE_ANIMATION_TIME = 200;
|
|
|
|
const SLIDE_ANIMATION_TIME = 200;
|
2011-02-11 22:05:35 +00:00
|
|
|
|
2011-05-05 02:47:07 +00:00
|
|
|
// When we create workspaces by dragging, we add a "cut" into the top and
|
|
|
|
// bottom of each workspace so that the user doesn't have to hit the
|
|
|
|
// placeholder exactly.
|
2023-07-10 09:53:00 +00:00
|
|
|
const WORKSPACE_CUT_SIZE = 10;
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const WORKSPACE_KEEP_ALIVE_TIME = 100;
|
2012-02-17 14:57:27 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const MUTTER_SCHEMA = 'org.gnome.mutter';
|
2012-07-24 15:45:07 +00:00
|
|
|
|
2012-11-24 17:49:59 +00:00
|
|
|
/* A layout manager that requests size only for primary_actor, but then allocates
|
|
|
|
all using a fixed layout */
|
2023-07-10 09:53:00 +00:00
|
|
|
const PrimaryActorLayout = GObject.registerClass(
|
2017-10-31 01:23:39 +00:00
|
|
|
class PrimaryActorLayout extends Clutter.FixedLayout {
|
2017-10-31 00:03:21 +00:00
|
|
|
_init(primaryActor) {
|
2017-10-31 01:23:39 +00:00
|
|
|
super._init();
|
2012-11-24 17:49:59 +00:00
|
|
|
|
|
|
|
this.primaryActor = primaryActor;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
vfunc_get_preferred_width(container, forHeight) {
|
2012-11-24 17:49:59 +00:00
|
|
|
return this.primaryActor.get_preferred_width(forHeight);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
vfunc_get_preferred_height(container, forWidth) {
|
2012-11-24 17:49:59 +00:00
|
|
|
return this.primaryActor.get_preferred_height(forWidth);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-11-24 17:49:59 +00:00
|
|
|
});
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const WindowClone = GObject.registerClass({
|
2019-07-16 09:24:13 +00:00
|
|
|
Signals: {
|
|
|
|
'drag-begin': {},
|
|
|
|
'drag-cancelled': {},
|
|
|
|
'drag-end': {},
|
2023-08-06 22:40:20 +00:00
|
|
|
'selected': {param_types: [GObject.TYPE_UINT]},
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class WindowClone extends Clutter.Actor {
|
|
|
|
_init(realWindow) {
|
2023-08-06 22:40:20 +00:00
|
|
|
let clone = new Clutter.Clone({source: realWindow});
|
2019-07-16 09:24:13 +00:00
|
|
|
super._init({
|
|
|
|
layout_manager: new PrimaryActorLayout(clone),
|
2019-08-20 21:43:54 +00:00
|
|
|
reactive: true,
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
|
|
|
this._delegate = this;
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_child(clone);
|
2011-01-31 02:18:12 +00:00
|
|
|
this.realWindow = realWindow;
|
|
|
|
this.metaWindow = realWindow.meta_window;
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
this.realWindow.connectObject(
|
|
|
|
'notify::position', this._onPositionChanged.bind(this),
|
|
|
|
'destroy', () => {
|
|
|
|
// First destroy the clone and then destroy everything
|
|
|
|
// This will ensure that we never see it in the _disconnectSignals loop
|
|
|
|
clone.destroy();
|
|
|
|
this.destroy();
|
|
|
|
}, this);
|
2011-02-03 03:54:33 +00:00
|
|
|
this._onPositionChanged();
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
this._draggable = DND.makeDraggable(this, {
|
|
|
|
restoreOnSuccess: true,
|
|
|
|
dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
|
|
|
|
dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY,
|
|
|
|
});
|
2017-12-02 00:27:35 +00:00
|
|
|
this._draggable.connect('drag-begin', this._onDragBegin.bind(this));
|
|
|
|
this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this));
|
|
|
|
this._draggable.connect('drag-end', this._onDragEnd.bind(this));
|
2011-01-31 02:18:12 +00:00
|
|
|
this.inDrag = false;
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2023-04-20 01:30:04 +00:00
|
|
|
const clickAction = new Clutter.ClickAction();
|
|
|
|
clickAction.connect('clicked',
|
|
|
|
() => this.emit('selected', Clutter.get_current_event_time()));
|
|
|
|
this._draggable.addClickAction(clickAction);
|
|
|
|
|
2017-10-31 00:38:18 +00:00
|
|
|
let iter = win => {
|
2012-11-24 17:49:59 +00:00
|
|
|
let actor = win.get_compositor_private();
|
|
|
|
|
|
|
|
if (!actor)
|
|
|
|
return false;
|
|
|
|
if (!win.is_attached_dialog())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
this._doAddAttachedDialog(win, actor);
|
|
|
|
win.foreach_transient(iter);
|
|
|
|
|
|
|
|
return true;
|
2017-10-31 00:38:18 +00:00
|
|
|
};
|
2012-11-24 17:49:59 +00:00
|
|
|
this.metaWindow.foreach_transient(iter);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2013-01-19 01:22:15 +00:00
|
|
|
// Find the actor just below us, respecting reparenting done
|
|
|
|
// by DND code
|
2017-10-31 00:03:21 +00:00
|
|
|
getActualStackAbove() {
|
2013-01-19 01:22:15 +00:00
|
|
|
if (this._stackAbove == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
if (this.inDrag) {
|
|
|
|
if (this._stackAbove._delegate)
|
|
|
|
return this._stackAbove._delegate.getActualStackAbove();
|
|
|
|
else
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return this._stackAbove;
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-01-19 01:22:15 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setStackAbove(actor) {
|
2011-01-31 02:18:12 +00:00
|
|
|
this._stackAbove = actor;
|
2013-01-19 01:22:15 +00:00
|
|
|
|
|
|
|
// Don't apply the new stacking now, it will be applied
|
|
|
|
// when dragging ends and window are stacked again
|
|
|
|
if (actor.inDrag)
|
|
|
|
return;
|
|
|
|
|
2019-11-05 19:17:19 +00:00
|
|
|
let parent = this.get_parent();
|
2013-01-19 01:22:15 +00:00
|
|
|
let actualAbove = this.getActualStackAbove();
|
|
|
|
if (actualAbove == null)
|
2019-11-05 19:17:19 +00:00
|
|
|
parent.set_child_below_sibling(this, null);
|
2011-01-31 02:18:12 +00:00
|
|
|
else
|
2019-11-05 19:17:19 +00:00
|
|
|
parent.set_child_above_sibling(this, actualAbove);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
addAttachedDialog(win) {
|
2012-11-24 17:49:59 +00:00
|
|
|
this._doAddAttachedDialog(win, win.get_compositor_private());
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_doAddAttachedDialog(metaDialog, realDialog) {
|
2023-08-06 22:40:20 +00:00
|
|
|
let clone = new Clutter.Clone({source: realDialog});
|
2012-11-24 17:49:59 +00:00
|
|
|
this._updateDialogPosition(realDialog, clone);
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
realDialog.connectObject(
|
|
|
|
'notify::position', dialog => this._updateDialogPosition(dialog, clone),
|
|
|
|
'destroy', () => clone.destroy(), this);
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_child(clone);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateDialogPosition(realDialog, cloneDialog) {
|
2012-11-24 17:49:59 +00:00
|
|
|
let metaDialog = realDialog.meta_window;
|
2014-09-18 21:02:45 +00:00
|
|
|
let dialogRect = metaDialog.get_frame_rect();
|
|
|
|
let rect = this.metaWindow.get_frame_rect();
|
2012-11-24 17:49:59 +00:00
|
|
|
|
|
|
|
cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onPositionChanged() {
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_position(this.realWindow.x, this.realWindow.y);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-03 03:54:33 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDestroy() {
|
2019-07-16 09:24:13 +00:00
|
|
|
this._delegate = null;
|
2011-01-31 02:18:12 +00:00
|
|
|
|
|
|
|
if (this.inDrag) {
|
|
|
|
this.emit('drag-end');
|
|
|
|
this.inDrag = false;
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
_onDragBegin(_draggable, _time) {
|
2011-01-31 02:18:12 +00:00
|
|
|
this.inDrag = true;
|
|
|
|
this.emit('drag-begin');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
_onDragCancelled(_draggable, _time) {
|
2011-12-02 15:54:27 +00:00
|
|
|
this.emit('drag-cancelled');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-12-02 15:54:27 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
_onDragEnd(_draggable, _time, _snapback) {
|
2011-01-31 02:18:12 +00:00
|
|
|
this.inDrag = false;
|
|
|
|
|
|
|
|
// We may not have a parent if DnD completed successfully, in
|
|
|
|
// which case our clone will shortly be destroyed and replaced
|
|
|
|
// with a new one on the target workspace.
|
2019-11-05 19:17:19 +00:00
|
|
|
let parent = this.get_parent();
|
|
|
|
if (parent !== null) {
|
2011-01-31 02:18:12 +00:00
|
|
|
if (this._stackAbove == null)
|
2019-11-05 19:17:19 +00:00
|
|
|
parent.set_child_below_sibling(this, null);
|
2011-01-31 02:18:12 +00:00
|
|
|
else
|
2019-11-05 19:17:19 +00:00
|
|
|
parent.set_child_above_sibling(this, this._stackAbove);
|
2011-01-31 02:18:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.emit('drag-end');
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2011-01-31 02:18:12 +00:00
|
|
|
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const ThumbnailState = {
|
2019-01-29 01:27:05 +00:00
|
|
|
NEW: 0,
|
2021-01-29 15:25:28 +00:00
|
|
|
EXPANDING: 1,
|
|
|
|
EXPANDED: 2,
|
|
|
|
ANIMATING_IN: 3,
|
|
|
|
NORMAL: 4,
|
|
|
|
REMOVING: 5,
|
|
|
|
ANIMATING_OUT: 6,
|
|
|
|
ANIMATED_OUT: 7,
|
|
|
|
COLLAPSING: 8,
|
|
|
|
DESTROYED: 9,
|
2011-02-12 19:40:49 +00:00
|
|
|
};
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const WorkspaceThumbnail = GObject.registerClass({
|
2019-07-25 22:13:19 +00:00
|
|
|
Properties: {
|
|
|
|
'collapse-fraction': GObject.ParamSpec.double(
|
|
|
|
'collapse-fraction', 'collapse-fraction', 'collapse-fraction',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
|
|
|
0, 1, 0),
|
|
|
|
'slide-position': GObject.ParamSpec.double(
|
|
|
|
'slide-position', 'slide-position', 'slide-position',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
|
|
|
0, 1, 0),
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-25 22:13:19 +00:00
|
|
|
}, class WorkspaceThumbnail extends St.Widget {
|
2023-08-07 01:15:51 +00:00
|
|
|
/**
|
|
|
|
* @param {Meta.Workspace} metaWorkspace
|
|
|
|
* @param {number} monitorIndex
|
|
|
|
*/
|
2021-02-26 13:09:15 +00:00
|
|
|
_init(metaWorkspace, monitorIndex) {
|
2019-07-25 16:53:00 +00:00
|
|
|
super._init({
|
|
|
|
clip_to_allocation: true,
|
2019-08-20 21:43:54 +00:00
|
|
|
style_class: 'workspace-thumbnail',
|
2023-08-06 22:40:20 +00:00
|
|
|
pivot_point: new Graphene.Point({x: 0.5, y: 0.5}),
|
2019-07-25 16:53:00 +00:00
|
|
|
});
|
|
|
|
this._delegate = this;
|
|
|
|
|
2011-01-31 02:18:12 +00:00
|
|
|
this.metaWorkspace = metaWorkspace;
|
2021-02-26 13:09:15 +00:00
|
|
|
this.monitorIndex = monitorIndex;
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2012-01-11 22:24:42 +00:00
|
|
|
this._removed = false;
|
|
|
|
|
2021-02-26 12:36:26 +00:00
|
|
|
this._viewport = new Clutter.Actor();
|
|
|
|
this.add_child(this._viewport);
|
|
|
|
|
2013-02-22 11:23:56 +00:00
|
|
|
this._contents = new Clutter.Actor();
|
2021-02-26 12:36:26 +00:00
|
|
|
this._viewport.add_child(this._contents);
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2019-07-25 16:53:00 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2018-01-19 16:37:20 +00:00
|
|
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex);
|
|
|
|
this.setPorthole(workArea.x, workArea.y, workArea.width, workArea.height);
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2017-10-31 00:38:18 +00:00
|
|
|
let windows = global.get_window_actors().filter(actor => {
|
2013-02-07 07:26:56 +00:00
|
|
|
let win = actor.meta_window;
|
2013-01-14 17:38:02 +00:00
|
|
|
return win.located_on_workspace(metaWorkspace);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2011-01-31 02:18:12 +00:00
|
|
|
|
|
|
|
// Create clones for windows that should be visible in the Overview
|
|
|
|
this._windows = [];
|
2012-01-11 22:59:17 +00:00
|
|
|
this._allWindows = [];
|
2011-01-31 02:18:12 +00:00
|
|
|
for (let i = 0; i < windows.length; i++) {
|
2021-08-15 22:36:59 +00:00
|
|
|
windows[i].meta_window.connectObject('notify::minimized',
|
|
|
|
this._updateMinimized.bind(this), this);
|
2012-01-11 22:59:17 +00:00
|
|
|
this._allWindows.push(windows[i].meta_window);
|
2011-05-31 16:49:36 +00:00
|
|
|
|
2019-08-20 00:51:42 +00:00
|
|
|
if (this._isMyWindow(windows[i]) && this._isOverviewWindow(windows[i]))
|
2011-01-31 02:18:12 +00:00
|
|
|
this._addWindowClone(windows[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Track window changes
|
2021-08-15 22:36:59 +00:00
|
|
|
this.metaWorkspace.connectObject(
|
|
|
|
'window-added', this._windowAdded.bind(this),
|
|
|
|
'window-removed', this._windowRemoved.bind(this), this);
|
|
|
|
global.display.connectObject(
|
|
|
|
'window-entered-monitor', this._windowEnteredMonitor.bind(this),
|
|
|
|
'window-left-monitor', this._windowLeftMonitor.bind(this), this);
|
2011-02-12 19:40:49 +00:00
|
|
|
|
|
|
|
this.state = ThumbnailState.NORMAL;
|
|
|
|
this._slidePosition = 0; // Fully slid in
|
|
|
|
this._collapseFraction = 0; // Not collapsed
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setPorthole(x, y, width, height) {
|
2021-02-26 12:36:26 +00:00
|
|
|
this._viewport.set_size(width, height);
|
|
|
|
this._contents.set_position(-x, -y);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-13 21:53:15 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_lookupIndex(metaWindow) {
|
2023-08-07 00:51:19 +00:00
|
|
|
return this._windows.findIndex(w => w.metaWindow === metaWindow);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
syncStacking(stackIndices) {
|
2017-10-31 00:38:18 +00:00
|
|
|
this._windows.sort((a, b) => {
|
|
|
|
let indexA = stackIndices[a.metaWindow.get_stable_sequence()];
|
|
|
|
let indexB = stackIndices[b.metaWindow.get_stable_sequence()];
|
|
|
|
return indexA - indexB;
|
|
|
|
});
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2021-01-26 00:10:44 +00:00
|
|
|
for (let i = 1; i < this._windows.length; i++) {
|
2011-01-31 02:18:12 +00:00
|
|
|
let clone = this._windows[i];
|
2021-01-26 00:10:44 +00:00
|
|
|
const previousClone = this._windows[i - 1];
|
|
|
|
clone.setStackAbove(previousClone);
|
2011-01-31 02:18:12 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2021-01-30 01:03:07 +00:00
|
|
|
set slidePosition(slidePosition) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._slidePosition === slidePosition)
|
2019-07-25 22:13:19 +00:00
|
|
|
return;
|
2021-01-27 16:28:36 +00:00
|
|
|
|
|
|
|
const scale = Util.lerp(1, 0.75, slidePosition);
|
|
|
|
this.set_scale(scale, scale);
|
|
|
|
this.opacity = Util.lerp(255, 0, slidePosition);
|
|
|
|
|
2011-02-12 19:40:49 +00:00
|
|
|
this._slidePosition = slidePosition;
|
2019-07-25 22:13:19 +00:00
|
|
|
this.notify('slide-position');
|
2019-07-25 16:53:00 +00:00
|
|
|
this.queue_relayout();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-01-30 01:03:07 +00:00
|
|
|
get slidePosition() {
|
2011-02-12 19:40:49 +00:00
|
|
|
return this._slidePosition;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-01-30 01:03:07 +00:00
|
|
|
set collapseFraction(collapseFraction) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._collapseFraction === collapseFraction)
|
2019-07-25 22:13:19 +00:00
|
|
|
return;
|
2011-02-12 19:40:49 +00:00
|
|
|
this._collapseFraction = collapseFraction;
|
2019-07-25 22:13:19 +00:00
|
|
|
this.notify('collapse-fraction');
|
2019-07-25 16:53:00 +00:00
|
|
|
this.queue_relayout();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-01-30 01:03:07 +00:00
|
|
|
get collapseFraction() {
|
2011-02-12 19:40:49 +00:00
|
|
|
return this._collapseFraction;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_doRemoveWindow(metaWin) {
|
2018-07-09 10:19:00 +00:00
|
|
|
let clone = this._removeWindowClone(metaWin);
|
|
|
|
if (clone)
|
|
|
|
clone.destroy();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_doAddWindow(metaWin) {
|
2012-01-11 22:24:42 +00:00
|
|
|
if (this._removed)
|
2011-01-31 02:18:12 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
let win = metaWin.get_compositor_private();
|
|
|
|
|
|
|
|
if (!win) {
|
|
|
|
// Newly-created windows are added to a workspace before
|
|
|
|
// the compositor finds out about them...
|
2019-08-19 18:50:33 +00:00
|
|
|
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
2017-10-31 00:38:18 +00:00
|
|
|
if (!this._removed &&
|
|
|
|
metaWin.get_compositor_private() &&
|
2023-08-07 00:51:19 +00:00
|
|
|
metaWin.get_workspace() === this.metaWorkspace)
|
2017-10-31 00:38:18 +00:00
|
|
|
this._doAddWindow(metaWin);
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
2014-04-10 17:26:52 +00:00
|
|
|
GLib.Source.set_name_by_id(id, '[gnome-shell] this._doAddWindow');
|
2011-01-31 02:18:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-14 20:56:22 +00:00
|
|
|
if (!this._allWindows.includes(metaWin)) {
|
2021-08-15 22:36:59 +00:00
|
|
|
metaWin.connectObject('notify::minimized',
|
|
|
|
this._updateMinimized.bind(this), this);
|
2012-01-17 22:50:31 +00:00
|
|
|
this._allWindows.push(metaWin);
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:19:30 +00:00
|
|
|
// We might have the window in our list already if it was on all workspaces and
|
|
|
|
// now was moved to this workspace
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._lookupIndex(metaWin) !== -1)
|
2011-02-28 19:19:30 +00:00
|
|
|
return;
|
|
|
|
|
2012-11-24 17:49:59 +00:00
|
|
|
if (!this._isMyWindow(win))
|
2011-01-31 02:18:12 +00:00
|
|
|
return;
|
|
|
|
|
2012-11-24 17:49:59 +00:00
|
|
|
if (this._isOverviewWindow(win)) {
|
|
|
|
this._addWindowClone(win);
|
|
|
|
} else if (metaWin.is_attached_dialog()) {
|
|
|
|
let parent = metaWin.get_transient_for();
|
|
|
|
while (parent.is_attached_dialog())
|
2018-07-23 12:22:34 +00:00
|
|
|
parent = parent.get_transient_for();
|
2012-11-24 17:49:59 +00:00
|
|
|
|
2018-11-19 11:28:28 +00:00
|
|
|
let idx = this._lookupIndex(parent);
|
2012-11-24 17:49:59 +00:00
|
|
|
if (idx < 0) {
|
|
|
|
// parent was not created yet, it will take care
|
|
|
|
// of the dialog when created
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let clone = this._windows[idx];
|
|
|
|
clone.addAttachedDialog(metaWin);
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_windowAdded(metaWorkspace, metaWin) {
|
2011-02-28 19:19:30 +00:00
|
|
|
this._doAddWindow(metaWin);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-28 19:19:30 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_windowRemoved(metaWorkspace, metaWin) {
|
2012-01-11 22:59:17 +00:00
|
|
|
let index = this._allWindows.indexOf(metaWin);
|
2023-08-07 00:51:19 +00:00
|
|
|
if (index !== -1) {
|
2021-08-15 22:36:59 +00:00
|
|
|
metaWin.disconnectObject(this);
|
2012-01-11 22:59:17 +00:00
|
|
|
this._allWindows.splice(index, 1);
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:19:30 +00:00
|
|
|
this._doRemoveWindow(metaWin);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-28 19:19:30 +00:00
|
|
|
|
2018-01-03 07:55:38 +00:00
|
|
|
_windowEnteredMonitor(metaDisplay, monitorIndex, metaWin) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (monitorIndex === this.monitorIndex)
|
2011-02-28 19:19:30 +00:00
|
|
|
this._doAddWindow(metaWin);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-28 19:19:30 +00:00
|
|
|
|
2018-01-03 07:55:38 +00:00
|
|
|
_windowLeftMonitor(metaDisplay, monitorIndex, metaWin) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (monitorIndex === this.monitorIndex)
|
2011-02-28 19:19:30 +00:00
|
|
|
this._doRemoveWindow(metaWin);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-02-28 19:19:30 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateMinimized(metaWin) {
|
2011-05-31 16:49:36 +00:00
|
|
|
if (metaWin.minimized)
|
|
|
|
this._doRemoveWindow(metaWin);
|
|
|
|
else
|
|
|
|
this._doAddWindow(metaWin);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-05-31 16:49:36 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
workspaceRemoved() {
|
2012-01-11 22:24:42 +00:00
|
|
|
if (this._removed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._removed = true;
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
this.metaWorkspace.disconnectObject(this);
|
|
|
|
global.display.disconnectObject(this);
|
|
|
|
this._allWindows.forEach(w => w.disconnectObject(this));
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-01-11 22:24:42 +00:00
|
|
|
|
2019-02-04 11:30:53 +00:00
|
|
|
_onDestroy() {
|
2012-01-11 22:24:42 +00:00
|
|
|
this.workspaceRemoved();
|
2011-01-31 02:18:12 +00:00
|
|
|
this._windows = [];
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2013-01-14 17:38:02 +00:00
|
|
|
// Tests if @actor belongs to this workspace and monitor
|
2017-10-31 00:03:21 +00:00
|
|
|
_isMyWindow(actor) {
|
2013-01-14 17:38:02 +00:00
|
|
|
let win = actor.meta_window;
|
|
|
|
return win.located_on_workspace(this.metaWorkspace) &&
|
2023-08-07 00:51:19 +00:00
|
|
|
(win.get_monitor() === this.monitorIndex);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
|
|
|
// Tests if @win should be shown in the Overview
|
2017-10-31 00:03:21 +00:00
|
|
|
_isOverviewWindow(win) {
|
2014-01-30 17:04:18 +00:00
|
|
|
return !win.get_meta_window().skip_taskbar &&
|
2011-05-31 16:49:36 +00:00
|
|
|
win.get_meta_window().showing_on_its_workspace();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
|
|
|
// Create a clone of a (non-desktop) window and add it to the window list
|
2017-10-31 00:03:21 +00:00
|
|
|
_addWindowClone(win) {
|
2011-01-31 02:18:12 +00:00
|
|
|
let clone = new WindowClone(win);
|
|
|
|
|
2019-08-20 00:20:08 +00:00
|
|
|
clone.connect('selected', (o, time) => {
|
2017-10-31 00:38:18 +00:00
|
|
|
this.activate(time);
|
|
|
|
});
|
|
|
|
clone.connect('drag-begin', () => {
|
|
|
|
Main.overview.beginWindowDrag(clone.metaWindow);
|
|
|
|
});
|
|
|
|
clone.connect('drag-cancelled', () => {
|
|
|
|
Main.overview.cancelledWindowDrag(clone.metaWindow);
|
|
|
|
});
|
|
|
|
clone.connect('drag-end', () => {
|
|
|
|
Main.overview.endWindowDrag(clone.metaWindow);
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
clone.connect('destroy', () => {
|
2018-07-09 10:19:00 +00:00
|
|
|
this._removeWindowClone(clone.metaWindow);
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this._contents.add_actor(clone);
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2021-01-26 00:10:44 +00:00
|
|
|
if (this._windows.length > 0)
|
2019-07-16 09:24:13 +00:00
|
|
|
clone.setStackAbove(this._windows[this._windows.length - 1]);
|
2011-02-14 21:09:10 +00:00
|
|
|
|
2011-01-31 02:18:12 +00:00
|
|
|
this._windows.push(clone);
|
|
|
|
|
|
|
|
return clone;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2018-07-09 10:19:00 +00:00
|
|
|
_removeWindowClone(metaWin) {
|
|
|
|
// find the position of the window in our list
|
2018-11-19 11:28:28 +00:00
|
|
|
let index = this._lookupIndex(metaWin);
|
2018-07-09 10:19:00 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (index === -1)
|
2018-07-09 10:19:00 +00:00
|
|
|
return null;
|
|
|
|
|
|
|
|
return this._windows.splice(index, 1).pop();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-07-09 10:19:00 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
activate(time) {
|
2011-02-14 22:09:04 +00:00
|
|
|
if (this.state > ThumbnailState.NORMAL)
|
|
|
|
return;
|
|
|
|
|
2011-02-09 22:43:09 +00:00
|
|
|
// a click on the already current workspace should go back to the main view
|
2020-06-29 14:50:27 +00:00
|
|
|
if (this.metaWorkspace.active)
|
2011-02-09 22:43:09 +00:00
|
|
|
Main.overview.hide();
|
|
|
|
else
|
|
|
|
this.metaWorkspace.activate(time);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2012-02-10 17:59:05 +00:00
|
|
|
// Draggable target interface used only by ThumbnailsBox
|
2019-09-15 09:45:47 +00:00
|
|
|
handleDragOverInternal(source, actor, time) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (source === Main.xdndHandler) {
|
2011-03-21 22:20:36 +00:00
|
|
|
this.metaWorkspace.activate(time);
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
}
|
|
|
|
|
2011-02-14 22:09:04 +00:00
|
|
|
if (this.state > ThumbnailState.NORMAL)
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
|
2020-06-08 07:50:41 +00:00
|
|
|
if (source.metaWindow &&
|
|
|
|
!this._isMyWindow(source.metaWindow.get_compositor_private()))
|
2011-01-31 02:18:12 +00:00
|
|
|
return DND.DragMotionResult.MOVE_DROP;
|
2019-05-02 15:04:03 +00:00
|
|
|
if (source.app && source.app.can_open_new_window())
|
2019-08-02 10:58:34 +00:00
|
|
|
return DND.DragMotionResult.COPY_DROP;
|
|
|
|
if (!source.app && source.shellWorkspaceLaunch)
|
2011-01-31 02:18:12 +00:00
|
|
|
return DND.DragMotionResult.COPY_DROP;
|
|
|
|
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 02:18:12 +00:00
|
|
|
|
2019-09-15 09:45:47 +00:00
|
|
|
acceptDropInternal(source, actor, time) {
|
2011-02-14 22:09:04 +00:00
|
|
|
if (this.state > ThumbnailState.NORMAL)
|
|
|
|
return false;
|
|
|
|
|
2020-06-08 07:50:41 +00:00
|
|
|
if (source.metaWindow) {
|
|
|
|
let win = source.metaWindow.get_compositor_private();
|
2011-01-31 02:18:12 +00:00
|
|
|
if (this._isMyWindow(win))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
let metaWindow = win.get_meta_window();
|
2022-03-31 15:15:26 +00:00
|
|
|
Main.moveWindowToMonitorAndWorkspace(metaWindow,
|
|
|
|
this.monitorIndex, this.metaWorkspace.index());
|
2011-01-31 02:18:12 +00:00
|
|
|
return true;
|
2019-05-02 15:04:03 +00:00
|
|
|
} else if (source.app && source.app.can_open_new_window()) {
|
2019-09-15 09:45:47 +00:00
|
|
|
if (source.animateLaunchAtPos)
|
|
|
|
source.animateLaunchAtPos(actor.x, actor.y);
|
|
|
|
|
2019-08-02 10:58:34 +00:00
|
|
|
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.
|
2020-03-29 21:51:13 +00:00
|
|
|
source.shellWorkspaceLaunch({
|
|
|
|
workspace: this.metaWorkspace.index(),
|
|
|
|
timestamp: time,
|
|
|
|
});
|
2011-01-31 02:18:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2021-01-25 23:59:46 +00:00
|
|
|
|
2021-02-16 22:06:37 +00:00
|
|
|
setScale(scaleX, scaleY) {
|
2021-02-26 12:36:26 +00:00
|
|
|
this._viewport.set_scale(scaleX, scaleY);
|
2021-01-25 23:59:46 +00:00
|
|
|
}
|
2019-07-25 16:53:00 +00:00
|
|
|
});
|
2011-02-09 20:48:11 +00:00
|
|
|
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const ThumbnailsBox = GObject.registerClass({
|
2019-07-25 22:13:19 +00:00
|
|
|
Properties: {
|
2021-02-16 21:03:29 +00:00
|
|
|
'expand-fraction': GObject.ParamSpec.double(
|
|
|
|
'expand-fraction', 'expand-fraction', 'expand-fraction',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
|
|
|
0, 1, 1),
|
2019-07-25 22:13:19 +00:00
|
|
|
'scale': GObject.ParamSpec.double(
|
|
|
|
'scale', 'scale', 'scale',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
2019-08-20 21:43:54 +00:00
|
|
|
0, Infinity, 0),
|
2021-02-16 19:40:43 +00:00
|
|
|
'should-show': GObject.ParamSpec.boolean(
|
|
|
|
'should-show', 'should-show', 'should-show',
|
|
|
|
GObject.ParamFlags.READABLE,
|
|
|
|
true),
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-25 22:13:19 +00:00
|
|
|
}, class ThumbnailsBox extends St.Widget {
|
2021-02-26 13:09:15 +00:00
|
|
|
_init(scrollAdjustment, monitorIndex) {
|
2021-01-25 23:52:17 +00:00
|
|
|
super._init({
|
|
|
|
style_class: 'workspace-thumbnails',
|
|
|
|
reactive: true,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
2023-08-06 22:40:20 +00:00
|
|
|
pivot_point: new Graphene.Point({x: 0.5, y: 0.5}),
|
2021-01-25 23:52:17 +00:00
|
|
|
});
|
2018-07-06 08:01:39 +00:00
|
|
|
|
2019-04-09 23:26:39 +00:00
|
|
|
this._delegate = this;
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
let indicator = new St.Bin({style_class: 'workspace-thumbnail-indicator'});
|
2011-02-09 20:48:11 +00:00
|
|
|
|
|
|
|
// We don't want the indicator to affect drag-and-drop
|
|
|
|
Shell.util_set_hidden_from_pick(indicator, true);
|
|
|
|
|
|
|
|
this._indicator = indicator;
|
2018-07-06 08:01:39 +00:00
|
|
|
this.add_actor(indicator);
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-02-26 13:09:15 +00:00
|
|
|
this._monitorIndex = monitorIndex;
|
|
|
|
|
2012-02-10 17:59:05 +00:00
|
|
|
this._dropWorkspace = -1;
|
2011-05-05 02:47:07 +00:00
|
|
|
this._dropPlaceholderPos = -1;
|
2023-08-06 22:40:20 +00:00
|
|
|
this._dropPlaceholder = new St.Bin({style_class: 'placeholder'});
|
2018-07-06 08:01:39 +00:00
|
|
|
this.add_actor(this._dropPlaceholder);
|
2012-12-16 22:52:33 +00:00
|
|
|
this._spliceIndex = -1;
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2023-08-11 22:32:35 +00:00
|
|
|
this._maxThumbnailScale = MAX_THUMBNAIL_SCALE;
|
2011-02-11 22:05:35 +00:00
|
|
|
this._targetScale = 0;
|
|
|
|
this._scale = 0;
|
2021-02-16 21:03:29 +00:00
|
|
|
this._expandFraction = 1;
|
2021-02-20 11:44:01 +00:00
|
|
|
this._updateStateId = 0;
|
2011-02-12 19:40:49 +00:00
|
|
|
this._pendingScaleUpdate = false;
|
2011-02-12 21:14:12 +00:00
|
|
|
this._animatingIndicator = false;
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-02-16 19:40:43 +00:00
|
|
|
this._shouldShow = true;
|
|
|
|
|
2011-02-12 19:40:49 +00:00
|
|
|
this._stateCounts = {};
|
2011-02-24 17:17:05 +00:00
|
|
|
for (let key in ThumbnailState)
|
2011-02-12 19:40:49 +00:00
|
|
|
this._stateCounts[ThumbnailState[key]] = 0;
|
2011-02-09 20:48:11 +00:00
|
|
|
|
|
|
|
this._thumbnails = [];
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2023-04-20 01:30:04 +00:00
|
|
|
const clickAction = new Clutter.ClickAction();
|
|
|
|
clickAction.connect('clicked', () => {
|
|
|
|
this._activateThumbnailAtPoint(
|
|
|
|
...clickAction.get_coords(),
|
|
|
|
Clutter.get_current_event_time());
|
|
|
|
});
|
|
|
|
this.add_action(clickAction);
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.connectObject(
|
|
|
|
'showing', () => this._createThumbnails(),
|
|
|
|
'hidden', () => this._destroyThumbnails(),
|
|
|
|
'item-drag-begin', () => this._onDragBegin(),
|
|
|
|
'item-drag-end', () => this._onDragEnd(),
|
|
|
|
'item-drag-cancelled', () => this._onDragCancelled(),
|
|
|
|
'window-drag-begin', () => this._onDragBegin(),
|
|
|
|
'window-drag-end', () => this._onDragEnd(),
|
|
|
|
'window-drag-cancelled', () => this._onDragCancelled(), this);
|
2012-07-24 15:45:07 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._settings = new Gio.Settings({schema_id: MUTTER_SCHEMA});
|
2012-07-24 15:45:07 +00:00
|
|
|
this._settings.connect('changed::dynamic-workspaces',
|
2021-02-16 19:40:43 +00:00
|
|
|
() => this._updateShouldShow());
|
2021-02-20 23:23:07 +00:00
|
|
|
this._updateShouldShow();
|
2017-06-09 10:26:11 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.layoutManager.connectObject('monitors-changed', () => {
|
|
|
|
this._destroyThumbnails();
|
|
|
|
if (Main.overview.visible)
|
|
|
|
this._createThumbnails();
|
|
|
|
}, this);
|
2018-04-25 20:03:16 +00:00
|
|
|
|
2021-03-02 10:45:31 +00:00
|
|
|
// The porthole is the part of the screen we're showing in the thumbnails
|
2021-08-15 22:36:59 +00:00
|
|
|
global.display.connectObject('workareas-changed',
|
|
|
|
() => this._updatePorthole(), this);
|
2021-03-02 10:45:31 +00:00
|
|
|
this._updatePorthole();
|
2019-01-11 22:50:09 +00:00
|
|
|
|
2021-02-17 08:54:14 +00:00
|
|
|
this.connect('notify::visible', () => {
|
|
|
|
if (!this.visible)
|
|
|
|
this._queueUpdateStates();
|
|
|
|
});
|
2021-02-20 11:44:01 +00:00
|
|
|
this.connect('destroy', () => this._onDestroy());
|
2021-02-17 08:54:14 +00:00
|
|
|
|
2019-07-08 08:03:20 +00:00
|
|
|
this._scrollAdjustment = scrollAdjustment;
|
2021-08-15 22:36:59 +00:00
|
|
|
this._scrollAdjustment.connectObject('notify::value',
|
|
|
|
() => this._updateIndicator(), this);
|
2021-02-20 11:44:01 +00:00
|
|
|
}
|
|
|
|
|
2023-08-11 22:32:35 +00:00
|
|
|
get maxThumbnailScale() {
|
|
|
|
return this._maxThumbnailScale;
|
|
|
|
}
|
|
|
|
|
workspaceThumbnail: Update monitor index on 'monitors-changed'
For the primary monitor workspace thumbnail, we must keep the monitor
index in sync with what is currently the primary monitor index,
otherwise we might end up trying to move windows to non-existing
monitors.
For example, if the primary monitor index was 1 when the thumbnail box
was created, but later, the primary monitor index changed to 0, with the
other monitor being turned off, moving a window to one of the workspaces
on the workspace thumbnail, gnome-shell would attempt to move it to the
monitor with the index the primary monitor had in the past, with the
problem being that that monitor no longer exists.
Fix this by listening on the 'monitors-changed' signal on the layout
manager, and update the monitor index of the primary workspace
thumbnails box. Make sure to connect to the signal before creating the
thumbnails box, as the thumbnails box itself will listen to the signal
and recreate its actual thumbnails, and it must do this with the up to
date monitor index.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4075
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1810>
2021-04-14 15:12:12 +00:00
|
|
|
setMonitorIndex(monitorIndex) {
|
|
|
|
this._monitorIndex = monitorIndex;
|
|
|
|
}
|
|
|
|
|
2021-02-20 11:44:01 +00:00
|
|
|
_onDestroy() {
|
2021-05-25 15:03:46 +00:00
|
|
|
this._destroyThumbnails();
|
2021-02-20 11:44:01 +00:00
|
|
|
this._unqueueUpdateStates();
|
|
|
|
|
|
|
|
if (this._settings)
|
|
|
|
this._settings.run_dispose();
|
|
|
|
this._settings = null;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-07-24 15:45:07 +00:00
|
|
|
|
2021-02-16 19:40:43 +00:00
|
|
|
_updateShouldShow() {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {nWorkspaces} = global.workspace_manager;
|
2021-02-16 19:15:21 +00:00
|
|
|
const shouldShow = this._settings.get_boolean('dynamic-workspaces')
|
|
|
|
? nWorkspaces > NUM_WORKSPACES_THRESHOLD
|
|
|
|
: nWorkspaces > 1;
|
2021-02-16 19:40:43 +00:00
|
|
|
|
|
|
|
if (this._shouldShow === shouldShow)
|
|
|
|
return;
|
2018-01-03 07:55:38 +00:00
|
|
|
|
2021-02-16 19:40:43 +00:00
|
|
|
this._shouldShow = shouldShow;
|
|
|
|
this.notify('should-show');
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2021-03-02 18:48:55 +00:00
|
|
|
_updateIndicator() {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {value} = this._scrollAdjustment;
|
|
|
|
const {workspaceManager} = global;
|
2021-03-02 18:48:55 +00:00
|
|
|
const activeIndex = workspaceManager.get_active_workspace_index();
|
|
|
|
|
|
|
|
this._animatingIndicator = value !== activeIndex;
|
|
|
|
|
|
|
|
if (!this._animatingIndicator)
|
|
|
|
this._queueUpdateStates();
|
|
|
|
|
|
|
|
this.queue_relayout();
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_activateThumbnailAtPoint(stageX, stageY, time) {
|
2021-01-25 23:52:17 +00:00
|
|
|
const [r_, x] = this.transform_stage_point(stageX, stageY);
|
2012-02-10 17:59:05 +00:00
|
|
|
|
2021-01-25 23:59:46 +00:00
|
|
|
const thumbnail = this._thumbnails.find(t => x >= t.x && x <= t.x + t.width);
|
2019-08-03 16:39:40 +00:00
|
|
|
if (thumbnail)
|
|
|
|
thumbnail.activate(time);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2015-10-16 16:27:09 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDragBegin() {
|
2011-11-27 15:23:17 +00:00
|
|
|
this._dragCancelled = false;
|
|
|
|
this._dragMonitor = {
|
2019-08-20 21:43:54 +00:00
|
|
|
dragMotion: this._onDragMotion.bind(this),
|
2011-11-27 15:23:17 +00:00
|
|
|
};
|
|
|
|
DND.addDragMonitor(this._dragMonitor);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDragEnd() {
|
2011-11-27 15:23:17 +00:00
|
|
|
if (this._dragCancelled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._endDrag();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDragCancelled() {
|
2011-11-27 15:23:17 +00:00
|
|
|
this._dragCancelled = true;
|
|
|
|
this._endDrag();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_endDrag() {
|
2011-11-27 15:23:17 +00:00
|
|
|
this._clearDragPlaceholder();
|
|
|
|
DND.removeDragMonitor(this._dragMonitor);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDragMotion(dragEvent) {
|
2018-07-06 08:01:39 +00:00
|
|
|
if (!this.contains(dragEvent.targetActor))
|
2011-11-27 15:23:17 +00:00
|
|
|
this._onLeave();
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onLeave() {
|
2011-11-27 15:23:17 +00:00
|
|
|
this._clearDragPlaceholder();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-11-27 15:23:17 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_clearDragPlaceholder() {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._dropPlaceholderPos === -1)
|
2012-02-21 19:46:27 +00:00
|
|
|
return;
|
|
|
|
|
2011-11-27 15:23:17 +00:00
|
|
|
this._dropPlaceholderPos = -1;
|
2018-07-06 08:01:39 +00:00
|
|
|
this.queue_relayout();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
_getPlaceholderTarget(index, spacing, rtl) {
|
|
|
|
const workspace = this._thumbnails[index];
|
|
|
|
|
|
|
|
let targetX1;
|
|
|
|
let targetX2;
|
|
|
|
|
|
|
|
if (rtl) {
|
2021-01-25 23:59:46 +00:00
|
|
|
const baseX = workspace.x + workspace.width;
|
2021-01-25 23:52:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-05-05 02:47:07 +00:00
|
|
|
// Draggable target interface
|
2017-10-31 00:03:21 +00:00
|
|
|
handleDragOver(source, actor, x, y, time) {
|
2020-06-08 07:50:41 +00:00
|
|
|
if (!source.metaWindow &&
|
2019-05-02 15:04:03 +00:00
|
|
|
(!source.app || !source.app.can_open_new_window()) &&
|
2019-08-02 10:58:34 +00:00
|
|
|
(source.app || !source.shellWorkspaceLaunch) &&
|
2023-08-07 00:51:19 +00:00
|
|
|
source !== Main.xdndHandler)
|
2011-05-05 02:47:07 +00:00
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
|
2012-09-22 22:57:57 +00:00
|
|
|
let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces();
|
2018-07-06 08:01:39 +00:00
|
|
|
let spacing = this.get_theme_node().get_length('spacing');
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2012-02-10 17:59:05 +00:00
|
|
|
this._dropWorkspace = -1;
|
|
|
|
let placeholderPos = -1;
|
|
|
|
let length = this._thumbnails.length;
|
2019-08-19 17:55:49 +00:00
|
|
|
for (let i = 0; i < length; i++) {
|
2021-01-25 23:52:17 +00:00
|
|
|
const index = rtl ? length - i - 1 : i;
|
|
|
|
|
|
|
|
if (canCreateWorkspaces && source !== Main.xdndHandler) {
|
|
|
|
const [targetStart, targetEnd] =
|
|
|
|
this._getPlaceholderTarget(index, spacing, rtl);
|
|
|
|
|
|
|
|
if (x > targetStart && x <= targetEnd) {
|
|
|
|
placeholderPos = index;
|
|
|
|
break;
|
|
|
|
}
|
2011-05-05 02:47:07 +00:00
|
|
|
}
|
2012-02-10 17:59:05 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
if (this._withinWorkspace(x, index, rtl)) {
|
|
|
|
this._dropWorkspace = index;
|
|
|
|
break;
|
|
|
|
}
|
2011-05-05 02:47:07 +00:00
|
|
|
}
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._dropPlaceholderPos !== placeholderPos) {
|
2012-02-10 17:59:05 +00:00
|
|
|
this._dropPlaceholderPos = placeholderPos;
|
2018-07-06 08:01:39 +00:00
|
|
|
this.queue_relayout();
|
2012-02-21 19:46:27 +00:00
|
|
|
}
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._dropWorkspace !== -1)
|
2019-09-15 09:45:47 +00:00
|
|
|
return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, actor, time);
|
2023-08-07 00:51:19 +00:00
|
|
|
else if (this._dropPlaceholderPos !== -1)
|
2020-06-08 07:50:41 +00:00
|
|
|
return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP;
|
2012-02-10 17:59:05 +00:00
|
|
|
else
|
2011-05-05 02:47:07 +00:00
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
acceptDrop(source, actor, x, y, time) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._dropWorkspace !== -1) {
|
2019-09-15 09:45:47 +00:00
|
|
|
return this._thumbnails[this._dropWorkspace].acceptDropInternal(source, actor, time);
|
2023-08-07 00:51:19 +00:00
|
|
|
} else if (this._dropPlaceholderPos !== -1) {
|
2020-06-08 07:50:41 +00:00
|
|
|
if (!source.metaWindow &&
|
2019-05-02 15:04:03 +00:00
|
|
|
(!source.app || !source.app.can_open_new_window()) &&
|
2019-08-02 10:58:34 +00:00
|
|
|
(source.app || !source.shellWorkspaceLaunch))
|
2012-02-10 17:59:05 +00:00
|
|
|
return false;
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2020-06-08 07:50:41 +00:00
|
|
|
let isWindow = !!source.metaWindow;
|
2012-02-10 17:59:05 +00:00
|
|
|
|
|
|
|
let newWorkspaceIndex;
|
|
|
|
[newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1];
|
2012-12-16 22:52:33 +00:00
|
|
|
this._spliceIndex = newWorkspaceIndex;
|
|
|
|
|
2015-01-15 22:03:24 +00:00
|
|
|
Main.wm.insertWorkspace(newWorkspaceIndex);
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2014-04-26 14:17:24 +00:00
|
|
|
if (isWindow) {
|
|
|
|
// Move the window to our monitor first if necessary.
|
|
|
|
let thumbMonitor = this._thumbnails[newWorkspaceIndex].monitorIndex;
|
2022-03-31 15:15:26 +00:00
|
|
|
Main.moveWindowToMonitorAndWorkspace(source.metaWindow,
|
|
|
|
thumbMonitor, newWorkspaceIndex, true);
|
2019-05-02 15:04:03 +00:00
|
|
|
} else if (source.app && source.app.can_open_new_window()) {
|
2019-09-15 09:45:47 +00:00
|
|
|
if (source.animateLaunchAtPos)
|
|
|
|
source.animateLaunchAtPos(actor.x, actor.y);
|
|
|
|
|
2019-08-02 10:58:34 +00:00
|
|
|
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.
|
2020-03-29 21:51:13 +00:00
|
|
|
source.shellWorkspaceLaunch({
|
|
|
|
workspace: newWorkspaceIndex,
|
|
|
|
timestamp: time,
|
|
|
|
});
|
2019-08-02 10:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (source.app || (!source.app && source.shellWorkspaceLaunch)) {
|
2012-02-10 17:59:05 +00:00
|
|
|
// 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.
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
Main.wm.keepWorkspaceAlive(workspaceManager.get_workspace_by_index(newWorkspaceIndex),
|
2023-08-06 23:45:22 +00:00
|
|
|
WORKSPACE_KEEP_ALIVE_TIME);
|
2012-02-10 17:59:05 +00:00
|
|
|
}
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2012-12-16 22:52:33 +00:00
|
|
|
// 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);
|
2019-07-25 22:13:19 +00:00
|
|
|
thumbnail.slide_position = 1;
|
2021-01-29 15:25:28 +00:00
|
|
|
thumbnail.collapse_fraction = 1;
|
2012-12-16 22:52:33 +00:00
|
|
|
|
|
|
|
this._queueUpdateStates();
|
|
|
|
|
2012-02-10 17:59:05 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-05-05 02:47:07 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_createThumbnails() {
|
2021-04-29 14:07:21 +00:00
|
|
|
if (this._thumbnails.length > 0)
|
|
|
|
return;
|
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
const {workspaceManager} = global;
|
2022-03-04 21:52:29 +00:00
|
|
|
this._transientSignalHolder = new TransientSignalHolder(this);
|
2021-08-15 22:36:59 +00:00
|
|
|
workspaceManager.connectObject(
|
|
|
|
'notify::n-workspaces', this._workspacesChanged.bind(this),
|
|
|
|
'active-workspace-changed', () => this._updateIndicator(),
|
|
|
|
'workspaces-reordered', () => {
|
2019-07-09 12:26:12 +00:00
|
|
|
this._thumbnails.sort((a, b) => {
|
|
|
|
return a.metaWorkspace.index() - b.metaWorkspace.index();
|
|
|
|
});
|
|
|
|
this.queue_relayout();
|
2022-03-04 21:52:29 +00:00
|
|
|
}, this._transientSignalHolder);
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.connectObject('windows-restacked',
|
2022-03-04 21:52:29 +00:00
|
|
|
this._syncStacking.bind(this), this._transientSignalHolder);
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2011-02-11 22:05:35 +00:00
|
|
|
this._targetScale = 0;
|
|
|
|
this._scale = 0;
|
2011-02-12 19:40:49 +00:00
|
|
|
this._pendingScaleUpdate = false;
|
2021-02-20 11:44:01 +00:00
|
|
|
this._unqueueUpdateStates();
|
2011-02-12 19:40:49 +00:00
|
|
|
|
|
|
|
this._stateCounts = {};
|
2011-02-24 17:17:05 +00:00
|
|
|
for (let key in ThumbnailState)
|
2011-02-12 19:40:49 +00:00
|
|
|
this._stateCounts[ThumbnailState[key]] = 0;
|
|
|
|
|
2018-01-03 07:55:38 +00:00
|
|
|
this.addThumbnails(0, workspaceManager.n_workspaces);
|
2021-03-04 07:14:03 +00:00
|
|
|
|
|
|
|
this._updateShouldShow();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_destroyThumbnails() {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._thumbnails.length === 0)
|
2017-09-20 01:05:16 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-04 21:52:29 +00:00
|
|
|
this._transientSignalHolder.destroy();
|
|
|
|
delete this._transientSignalHolder;
|
2012-12-13 16:00:30 +00:00
|
|
|
|
2011-02-09 20:48:11 +00:00
|
|
|
for (let w = 0; w < this._thumbnails.length; w++)
|
|
|
|
this._thumbnails[w].destroy();
|
|
|
|
this._thumbnails = [];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_workspacesChanged() {
|
2017-10-31 00:38:18 +00:00
|
|
|
let validThumbnails =
|
|
|
|
this._thumbnails.filter(t => t.state <= ThumbnailState.NORMAL);
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
2014-04-24 23:56:31 +00:00
|
|
|
let oldNumWorkspaces = validThumbnails.length;
|
2018-01-03 07:55:38 +00:00
|
|
|
let newNumWorkspaces = workspaceManager.n_workspaces;
|
2012-07-24 15:45:07 +00:00
|
|
|
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
|
|
this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
|
|
|
|
} else {
|
|
|
|
let removedIndex;
|
|
|
|
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
|
|
for (let w = 0; w < oldNumWorkspaces; w++) {
|
2018-01-03 07:55:38 +00:00
|
|
|
let metaWorkspace = workspaceManager.get_workspace_by_index(w);
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._thumbnails[w].metaWorkspace !== metaWorkspace) {
|
2012-07-24 15:45:07 +00:00
|
|
|
removedIndex = w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.removeThumbnails(removedIndex, removedNum);
|
|
|
|
}
|
|
|
|
|
2021-02-16 19:40:43 +00:00
|
|
|
this._updateShouldShow();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2012-07-24 15:45:07 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
addThumbnails(start, count) {
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
|
2011-02-09 20:48:11 +00:00
|
|
|
for (let k = start; k < start + count; k++) {
|
2018-01-03 07:55:38 +00:00
|
|
|
let metaWorkspace = workspaceManager.get_workspace_by_index(k);
|
2021-02-26 13:09:15 +00:00
|
|
|
let thumbnail = new WorkspaceThumbnail(metaWorkspace, this._monitorIndex);
|
2023-08-06 23:45:22 +00:00
|
|
|
thumbnail.setPorthole(
|
|
|
|
this._porthole.x, this._porthole.y,
|
|
|
|
this._porthole.width, this._porthole.height);
|
2011-02-12 19:40:49 +00:00
|
|
|
this._thumbnails.push(thumbnail);
|
2019-07-25 16:53:00 +00:00
|
|
|
this.add_actor(thumbnail);
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-02-16 21:33:28 +00:00
|
|
|
if (this._shouldShow && start > 0 && this._spliceIndex === -1) {
|
2012-12-16 22:52:33 +00:00
|
|
|
// not the initial fill, and not splicing via DND
|
2011-02-12 19:40:49 +00:00
|
|
|
thumbnail.state = ThumbnailState.NEW;
|
2019-07-25 22:13:19 +00:00
|
|
|
thumbnail.slide_position = 1; // start slid out
|
2021-01-29 15:25:28 +00:00
|
|
|
thumbnail.collapse_fraction = 1; // start fully collapsed
|
2011-02-12 19:40:49 +00:00
|
|
|
this._haveNewThumbnails = true;
|
|
|
|
} else {
|
|
|
|
thumbnail.state = ThumbnailState.NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._stateCounts[thumbnail.state]++;
|
2011-02-09 20:48:11 +00:00
|
|
|
}
|
|
|
|
|
2011-02-12 19:40:49 +00:00
|
|
|
this._queueUpdateStates();
|
|
|
|
|
2011-02-11 17:09:23 +00:00
|
|
|
// The thumbnails indicator actually needs to be on top of the thumbnails
|
2019-11-05 19:17:19 +00:00
|
|
|
this.set_child_above_sibling(this._indicator, null);
|
2012-12-16 22:52:33 +00:00
|
|
|
|
|
|
|
// Clear the splice index, we got the message
|
|
|
|
this._spliceIndex = -1;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
removeThumbnails(start, count) {
|
2011-02-12 19:40:49 +00:00
|
|
|
let currentPos = 0;
|
|
|
|
for (let k = 0; k < this._thumbnails.length; k++) {
|
|
|
|
let thumbnail = this._thumbnails[k];
|
|
|
|
|
|
|
|
if (thumbnail.state > ThumbnailState.NORMAL)
|
|
|
|
continue;
|
|
|
|
|
2012-01-17 22:50:31 +00:00
|
|
|
if (currentPos >= start && currentPos < start + count) {
|
|
|
|
thumbnail.workspaceRemoved();
|
2011-02-12 19:40:49 +00:00
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.REMOVING);
|
2012-01-17 22:50:31 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
|
|
|
currentPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._queueUpdateStates();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_syncStacking(overview, stackIndices) {
|
2011-02-09 20:48:11 +00:00
|
|
|
for (let i = 0; i < this._thumbnails.length; i++)
|
|
|
|
this._thumbnails[i].syncStacking(stackIndices);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-09 20:48:11 +00:00
|
|
|
|
2011-02-11 22:05:35 +00:00
|
|
|
set scale(scale) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._scale === scale)
|
2019-07-25 22:13:19 +00:00
|
|
|
return;
|
|
|
|
|
2011-02-11 22:05:35 +00:00
|
|
|
this._scale = scale;
|
2019-07-25 22:13:19 +00:00
|
|
|
this.notify('scale');
|
2018-07-06 08:01:39 +00:00
|
|
|
this.queue_relayout();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-11 22:05:35 +00:00
|
|
|
|
|
|
|
get scale() {
|
|
|
|
return this._scale;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-11 22:05:35 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_setThumbnailState(thumbnail, state) {
|
2011-02-12 19:40:49 +00:00
|
|
|
this._stateCounts[thumbnail.state]--;
|
|
|
|
thumbnail.state = state;
|
|
|
|
this._stateCounts[thumbnail.state]++;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_iterateStateThumbnails(state, callback) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._stateCounts[state] === 0)
|
2011-02-12 19:40:49 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (let i = 0; i < this._thumbnails.length; i++) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._thumbnails[i].state === state)
|
2011-02-12 19:40:49 +00:00
|
|
|
callback.call(this, this._thumbnails[i]);
|
|
|
|
}
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateStates() {
|
2021-02-20 11:44:01 +00:00
|
|
|
this._updateStateId = 0;
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2011-02-12 21:14:12 +00:00
|
|
|
// If we are animating the indicator, wait
|
|
|
|
if (this._animatingIndicator)
|
|
|
|
return;
|
|
|
|
|
2021-02-17 08:54:14 +00:00
|
|
|
// Likewise if we are in the process of hiding
|
|
|
|
if (!this._shouldShow && this.visible)
|
|
|
|
return;
|
|
|
|
|
2011-02-12 21:14:12 +00:00
|
|
|
// Then slide out any thumbnails that have been destroyed
|
2017-10-31 00:38:18 +00:00
|
|
|
this._iterateStateThumbnails(ThumbnailState.REMOVING, thumbnail => {
|
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT);
|
|
|
|
|
2019-07-25 00:06:05 +00:00
|
|
|
thumbnail.ease_property('slide-position', 1, {
|
|
|
|
duration: SLIDE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.LINEAR,
|
|
|
|
onComplete: () => {
|
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT);
|
|
|
|
this._queueUpdateStates();
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-25 00:06:05 +00:00
|
|
|
});
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2011-02-12 19:40:49 +00:00
|
|
|
|
|
|
|
// As long as things are sliding out, don't proceed
|
|
|
|
if (this._stateCounts[ThumbnailState.ANIMATING_OUT] > 0)
|
|
|
|
return;
|
|
|
|
|
2021-01-29 15:25:28 +00:00
|
|
|
// Once that's complete, we can start scaling to the new size,
|
|
|
|
// collapse any removed thumbnails and expand added ones
|
2017-10-31 00:38:18 +00:00
|
|
|
this._iterateStateThumbnails(ThumbnailState.ANIMATED_OUT, thumbnail => {
|
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.COLLAPSING);
|
2019-07-25 00:06:05 +00:00
|
|
|
thumbnail.ease_property('collapse-fraction', 1, {
|
|
|
|
duration: RESCALE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this._stateCounts[thumbnail.state]--;
|
|
|
|
thumbnail.state = ThumbnailState.DESTROYED;
|
|
|
|
|
|
|
|
let index = this._thumbnails.indexOf(thumbnail);
|
|
|
|
this._thumbnails.splice(index, 1);
|
|
|
|
thumbnail.destroy();
|
|
|
|
|
|
|
|
this._queueUpdateStates();
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-25 00:06:05 +00:00
|
|
|
});
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-01-29 15:25:28 +00:00
|
|
|
this._iterateStateThumbnails(ThumbnailState.NEW, thumbnail => {
|
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.EXPANDING);
|
|
|
|
thumbnail.ease_property('collapse-fraction', 0, {
|
|
|
|
duration: SLIDE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.EXPANDED);
|
2021-02-24 22:26:56 +00:00
|
|
|
this._queueUpdateStates();
|
2021-01-29 15:25:28 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2011-02-12 19:40:49 +00:00
|
|
|
if (this._pendingScaleUpdate) {
|
2019-07-25 00:06:05 +00:00
|
|
|
this.ease_property('scale', this._targetScale, {
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
duration: RESCALE_ANIMATION_TIME,
|
2019-08-20 21:43:54 +00:00
|
|
|
onComplete: () => this._queueUpdateStates(),
|
2019-07-25 00:06:05 +00:00
|
|
|
});
|
2011-02-12 19:40:49 +00:00
|
|
|
this._pendingScaleUpdate = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until that's done
|
2021-01-29 15:25:28 +00:00
|
|
|
if (this._scale !== this._targetScale ||
|
|
|
|
this._stateCounts[ThumbnailState.COLLAPSING] > 0 ||
|
|
|
|
this._stateCounts[ThumbnailState.EXPANDING] > 0)
|
2011-02-12 19:40:49 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// And then slide in any new thumbnails
|
2021-01-29 15:25:28 +00:00
|
|
|
this._iterateStateThumbnails(ThumbnailState.EXPANDED, thumbnail => {
|
2017-10-31 00:38:18 +00:00
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN);
|
2019-07-25 00:06:05 +00:00
|
|
|
thumbnail.ease_property('slide-position', 0, {
|
|
|
|
duration: SLIDE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this._setThumbnailState(thumbnail, ThumbnailState.NORMAL);
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-25 00:06:05 +00:00
|
|
|
});
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_queueUpdateStates() {
|
2021-02-20 11:44:01 +00:00
|
|
|
if (this._updateStateId > 0)
|
2011-02-12 19:40:49 +00:00
|
|
|
return;
|
|
|
|
|
2022-09-07 18:23:38 +00:00
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
this._updateStateId = laters.add(
|
2021-02-20 11:44:01 +00:00
|
|
|
Meta.LaterType.BEFORE_REDRAW, () => this._updateStates());
|
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-02-20 11:44:01 +00:00
|
|
|
_unqueueUpdateStates() {
|
2022-09-07 18:23:38 +00:00
|
|
|
if (this._updateStateId) {
|
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
laters.remove(this._updateStateId);
|
|
|
|
}
|
2021-02-20 11:44:01 +00:00
|
|
|
this._updateStateId = 0;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
vfunc_get_preferred_height(forWidth) {
|
2018-07-06 08:01:39 +00:00
|
|
|
let themeNode = this.get_theme_node();
|
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
forWidth = themeNode.adjust_for_width(forWidth);
|
|
|
|
|
2013-10-12 01:27:59 +00:00
|
|
|
let spacing = themeNode.get_length('spacing');
|
2021-01-27 16:29:53 +00:00
|
|
|
let nWorkspaces = this._thumbnails.length;
|
2011-02-12 19:40:49 +00:00
|
|
|
let totalSpacing = (nWorkspaces - 1) * spacing;
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
const avail = forWidth - totalSpacing;
|
|
|
|
|
|
|
|
let scale = (avail / nWorkspaces) / this._porthole.width;
|
2023-08-11 22:32:35 +00:00
|
|
|
scale = Math.min(scale, this._maxThumbnailScale);
|
2018-07-06 08:01:39 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
const height = Math.round(this._porthole.height * scale);
|
|
|
|
return themeNode.adjust_preferred_height(height, height);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
vfunc_get_preferred_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.
|
2018-07-06 08:01:39 +00:00
|
|
|
let themeNode = this.get_theme_node();
|
|
|
|
|
|
|
|
let spacing = themeNode.get_length('spacing');
|
2021-01-27 16:29:53 +00:00
|
|
|
let nWorkspaces = this._thumbnails.length;
|
2011-02-12 19:40:49 +00:00
|
|
|
let totalSpacing = (nWorkspaces - 1) * spacing;
|
|
|
|
|
2021-01-27 16:28:36 +00:00
|
|
|
const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail, index) => {
|
|
|
|
let workspaceSpacing = 0;
|
|
|
|
|
|
|
|
if (index > 0)
|
|
|
|
workspaceSpacing += spacing / 2;
|
|
|
|
if (index < this._thumbnails.length - 1)
|
|
|
|
workspaceSpacing += spacing / 2;
|
|
|
|
|
|
|
|
const progress = 1 - thumbnail.collapse_fraction;
|
2023-08-11 22:32:35 +00:00
|
|
|
const width = (this._porthole.width * this._maxThumbnailScale + workspaceSpacing) * progress;
|
2021-01-27 16:28:36 +00:00
|
|
|
return accumulator + width;
|
|
|
|
}, 0);
|
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
return themeNode.adjust_preferred_width(totalSpacing, naturalWidth);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2019-01-11 22:50:09 +00:00
|
|
|
_updatePorthole() {
|
2021-02-26 13:09:15 +00:00
|
|
|
if (!Main.layoutManager.monitors[this._monitorIndex]) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {x, y, width, height} = global.stage;
|
|
|
|
this._porthole = {x, y, width, height};
|
2019-08-20 00:51:42 +00:00
|
|
|
} else {
|
2021-02-26 13:09:15 +00:00
|
|
|
this._porthole =
|
|
|
|
Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex);
|
2019-08-20 00:51:42 +00:00
|
|
|
}
|
2017-04-12 06:46:54 +00:00
|
|
|
|
2019-01-11 22:50:09 +00:00
|
|
|
this.queue_relayout();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2014-07-31 15:35:27 +00:00
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
2018-07-06 08:01:39 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
|
2011-02-18 14:41:43 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._thumbnails.length === 0) // not visible
|
2011-02-11 22:05:35 +00:00
|
|
|
return;
|
|
|
|
|
2018-07-06 08:01:39 +00:00
|
|
|
let themeNode = this.get_theme_node();
|
|
|
|
box = themeNode.get_content_box(box);
|
2013-10-12 01:27:59 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
const portholeWidth = this._porthole.width;
|
|
|
|
const portholeHeight = this._porthole.height;
|
|
|
|
const spacing = themeNode.get_length('spacing');
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-02-17 19:31:33 +00:00
|
|
|
const nWorkspaces = this._thumbnails.length;
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-02-17 19:13:22 +00:00
|
|
|
// 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 availableWidth = (box.get_width() - totalSpacing) / nWorkspaces;
|
|
|
|
|
|
|
|
const hScale = availableWidth / portholeWidth;
|
|
|
|
const vScale = box.get_height() / portholeHeight;
|
|
|
|
const newScale = Math.min(hScale, vScale);
|
|
|
|
|
|
|
|
if (newScale !== this._targetScale) {
|
|
|
|
if (this._targetScale > 0) {
|
|
|
|
// We don't ease immediately because we need to observe the
|
|
|
|
// ordering in queueUpdateStates - if workspaces have been
|
|
|
|
// removed we need to slide them out as the first thing.
|
|
|
|
this._targetScale = newScale;
|
|
|
|
this._pendingScaleUpdate = true;
|
|
|
|
} else {
|
|
|
|
this._targetScale = this._scale = newScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._queueUpdateStates();
|
|
|
|
}
|
2011-02-11 22:05:35 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
const ratio = portholeWidth / portholeHeight;
|
2021-02-16 21:03:29 +00:00
|
|
|
const thumbnailFullHeight = Math.round(portholeHeight * this._scale);
|
|
|
|
const thumbnailWidth = Math.round(thumbnailFullHeight * ratio);
|
|
|
|
const thumbnailHeight = thumbnailFullHeight * this._expandFraction;
|
2021-01-25 23:52:17 +00:00
|
|
|
const roundedVScale = thumbnailHeight / portholeHeight;
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2023-08-11 22:32:35 +00:00
|
|
|
// We always request size for maxThumbnailScale, distribute
|
2021-02-01 15:28:49 +00:00
|
|
|
// space evently if we use smaller thumbnails
|
|
|
|
const extraWidth =
|
2023-08-11 22:32:35 +00:00
|
|
|
(this._maxThumbnailScale * portholeWidth - thumbnailWidth) * nWorkspaces;
|
2021-02-01 15:28:49 +00:00
|
|
|
box.x1 += Math.round(extraWidth / 2);
|
|
|
|
box.x2 -= Math.round(extraWidth / 2);
|
|
|
|
|
2019-06-27 20:51:10 +00:00
|
|
|
let indicatorValue = this._scrollAdjustment.value;
|
|
|
|
let indicatorUpperWs = Math.ceil(indicatorValue);
|
|
|
|
let indicatorLowerWs = Math.floor(indicatorValue);
|
2011-11-26 23:28:51 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
let indicatorLowerX1 = 0;
|
|
|
|
let indicatorLowerX2 = 0;
|
|
|
|
let indicatorUpperX1 = 0;
|
|
|
|
let indicatorUpperX2 = 0;
|
2019-06-27 20:51:10 +00:00
|
|
|
|
|
|
|
let indicatorThemeNode = this._indicator.get_theme_node();
|
2011-11-26 23:28:51 +00:00
|
|
|
let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + indicatorThemeNode.get_border_width(St.Side.TOP);
|
|
|
|
let indicatorBottomFullBorder = indicatorThemeNode.get_padding(St.Side.BOTTOM) + indicatorThemeNode.get_border_width(St.Side.BOTTOM);
|
|
|
|
let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + indicatorThemeNode.get_border_width(St.Side.LEFT);
|
|
|
|
let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + indicatorThemeNode.get_border_width(St.Side.RIGHT);
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
let x = box.x1;
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._dropPlaceholderPos === -1) {
|
2020-10-24 18:25:49 +00:00
|
|
|
this._dropPlaceholder.allocate_preferred_size(
|
|
|
|
...this._dropPlaceholder.get_position());
|
|
|
|
|
2022-09-07 18:23:38 +00:00
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
|
2011-05-05 02:47:07 +00:00
|
|
|
this._dropPlaceholder.hide();
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2011-05-05 02:47:07 +00:00
|
|
|
}
|
|
|
|
|
2013-10-12 01:27:59 +00:00
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
|
2011-02-11 17:09:23 +00:00
|
|
|
for (let i = 0; i < this._thumbnails.length; i++) {
|
2021-01-25 23:52:17 +00:00
|
|
|
const thumbnail = this._thumbnails[i];
|
2011-02-11 17:09:23 +00:00
|
|
|
if (i > 0)
|
2021-01-25 23:52:17 +00:00
|
|
|
x += spacing - Math.round(thumbnail.collapse_fraction * spacing);
|
|
|
|
|
|
|
|
const y1 = box.y1;
|
|
|
|
const y2 = y1 + thumbnailHeight;
|
|
|
|
|
|
|
|
if (i === this._dropPlaceholderPos) {
|
|
|
|
const [, placeholderWidth] = this._dropPlaceholder.get_preferred_width(-1);
|
|
|
|
childBox.y1 = y1;
|
|
|
|
childBox.y2 = y2;
|
|
|
|
|
|
|
|
if (rtl) {
|
|
|
|
childBox.x2 = box.x2 - Math.round(x);
|
|
|
|
childBox.x1 = box.x2 - Math.round(x + placeholderWidth);
|
|
|
|
} else {
|
|
|
|
childBox.x1 = Math.round(x);
|
|
|
|
childBox.x2 = Math.round(x + placeholderWidth);
|
|
|
|
}
|
2011-02-11 22:05:35 +00:00
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
this._dropPlaceholder.allocate(childBox);
|
2021-01-25 23:52:17 +00:00
|
|
|
|
2022-09-07 18:23:38 +00:00
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
|
2011-05-05 02:47:07 +00:00
|
|
|
this._dropPlaceholder.show();
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2021-01-25 23:52:17 +00:00
|
|
|
x += placeholderWidth + spacing;
|
2011-05-05 02:47:07 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
// We might end up with thumbnailWidth being something like 99.33
|
|
|
|
// pixels. To make this work and not end up with a gap at the end,
|
|
|
|
// we need some thumbnails to be 99 pixels and some 100 pixels width;
|
2011-05-05 02:47:07 +00:00
|
|
|
// we compute an actual scale separately for each thumbnail.
|
2021-01-25 23:52:17 +00:00
|
|
|
const x1 = Math.round(x);
|
|
|
|
const x2 = Math.round(x + thumbnailWidth);
|
|
|
|
const roundedHScale = (x2 - x1) / portholeWidth;
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2011-02-12 19:40:49 +00:00
|
|
|
// Allocating a scaled actor is funny - x1/y1 correspond to the origin
|
|
|
|
// of the actor, but x2/y2 are increased by the *unscaled* size.
|
2021-01-25 23:52:17 +00:00
|
|
|
if (rtl) {
|
|
|
|
childBox.x2 = box.x2 - x1;
|
2021-01-25 23:59:46 +00:00
|
|
|
childBox.x1 = box.x2 - (x1 + thumbnailWidth);
|
2021-01-25 23:52:17 +00:00
|
|
|
} else {
|
|
|
|
childBox.x1 = x1;
|
2021-01-25 23:59:46 +00:00
|
|
|
childBox.x2 = x1 + thumbnailWidth;
|
2021-01-25 23:52:17 +00:00
|
|
|
}
|
2011-02-11 17:09:23 +00:00
|
|
|
childBox.y1 = y1;
|
2021-01-25 23:59:46 +00:00
|
|
|
childBox.y2 = y1 + thumbnailHeight;
|
2011-02-11 17:09:23 +00:00
|
|
|
|
2021-01-25 23:59:46 +00:00
|
|
|
thumbnail.setScale(roundedHScale, roundedVScale);
|
2020-05-09 19:30:26 +00:00
|
|
|
thumbnail.allocate(childBox);
|
2011-02-12 19:40:49 +00:00
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
if (i === indicatorUpperWs) {
|
|
|
|
indicatorUpperX1 = childBox.x1;
|
|
|
|
indicatorUpperX2 = childBox.x2;
|
|
|
|
}
|
|
|
|
if (i === indicatorLowerWs) {
|
|
|
|
indicatorLowerX1 = childBox.x1;
|
|
|
|
indicatorLowerX2 = childBox.x2;
|
|
|
|
}
|
|
|
|
|
2011-02-18 15:31:18 +00:00
|
|
|
// We round the collapsing portion so that we don't get thumbnails resizing
|
|
|
|
// during an animation due to differences in rounded, but leave the uncollapsed
|
|
|
|
// portion unrounded so that non-animating we end up with the right total
|
2021-01-25 23:52:17 +00:00
|
|
|
x += thumbnailWidth - Math.round(thumbnailWidth * thumbnail.collapse_fraction);
|
2011-02-11 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 23:52:17 +00:00
|
|
|
childBox.y1 = box.y1;
|
|
|
|
childBox.y2 = box.y1 + thumbnailHeight;
|
|
|
|
|
|
|
|
const indicatorX1 = indicatorLowerX1 +
|
|
|
|
(indicatorUpperX1 - indicatorLowerX1) * (indicatorValue % 1);
|
|
|
|
const indicatorX2 = indicatorLowerX2 +
|
|
|
|
(indicatorUpperX2 - indicatorLowerX2) * (indicatorValue % 1);
|
|
|
|
|
|
|
|
childBox.x1 = indicatorX1 - indicatorLeftFullBorder;
|
|
|
|
childBox.x2 = indicatorX2 + indicatorRightFullBorder;
|
|
|
|
childBox.y1 -= indicatorTopFullBorder;
|
|
|
|
childBox.y2 += indicatorBottomFullBorder;
|
2020-05-09 19:30:26 +00:00
|
|
|
this._indicator.allocate(childBox);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2021-02-16 19:40:43 +00:00
|
|
|
|
|
|
|
get shouldShow() {
|
|
|
|
return this._shouldShow;
|
|
|
|
}
|
2021-02-16 21:03:29 +00:00
|
|
|
|
|
|
|
set expandFraction(expandFraction) {
|
|
|
|
if (this._expandFraction === expandFraction)
|
|
|
|
return;
|
|
|
|
this._expandFraction = expandFraction;
|
|
|
|
this.notify('expand-fraction');
|
|
|
|
this.queue_relayout();
|
|
|
|
}
|
|
|
|
|
|
|
|
get expandFraction() {
|
|
|
|
return this._expandFraction;
|
|
|
|
}
|
2011-11-20 17:56:27 +00:00
|
|
|
});
|