Files
PaperWM/scratch.js
Ole Jørgen Brønner c896eed6a7 free-resize: clear _initialResizeDone flag on makeScratch
The window is resized automatically when moved to scratch so makes sense to
automatically maximize on reinsertion. Ideally some special casing on whether
the window was explicitly sized should also be done.
2019-10-15 20:38:23 +02:00

230 lines
6.9 KiB
JavaScript

var Extension;
if (imports.misc.extensionUtils.extensions) {
Extension = imports.misc.extensionUtils.extensions["paperwm@hedning:matrix.org"];
} else {
Extension = imports.ui.main.extensionManager.lookup("paperwm@hedning:matrix.org");
}
var Meta = imports.gi.Meta;
var Main = imports.ui.main;
var TopBar = Extension.imports.topbar;
var Tiling = Extension.imports.tiling;
var utils = Extension.imports.utils;
var debug = utils.debug;
var float, scratchFrame; // symbols used for expando properties on metawindow
function focusMonitor() {
if (global.display.focus_window) {
return Main.layoutManager.monitors[global.display.focus_window.get_monitor()]
} else {
return Main.layoutManager.primaryMonitor;
}
}
/**
Tween window to "frame-coordinate" (targetX, targetY).
The frame is moved once the tween is done.
The actual window actor (not clone) is tweened to ensure it's on top of the
other windows/clones (clones if the space animates)
*/
function tweenScratch(metaWindow, targetX, targetY, tweenParams={}) {
let Tweener = Extension.imports.utils.tweener;
let Settings = Extension.imports.settings;
let f = metaWindow.get_frame_rect();
let b = metaWindow.get_buffer_rect();
let dx = f.x - b.x;
let dy = f.y - b.y;
Tweener.addTween(metaWindow.get_compositor_private(), Object.assign(
{
time: Settings.prefs.animation_time,
x: targetX - dx,
y: targetY - dy,
},
tweenParams,
{
onComplete: function(...args) {
metaWindow.move_frame(true, targetX , targetY);
tweenParams.onComplete && tweenParams.onComplete.apply(this, args);
}
}));
}
function makeScratch(metaWindow) {
let fromNonScratch = !metaWindow[float];
let fromTiling = false;
// Relevant when called while navigating. Use the position the user actually sees.
let windowPositionSeen;
if (fromNonScratch) {
// Figure out some stuff before the window is removed from the tiling
let space = Tiling.spaces.spaceOfWindow(metaWindow);
fromTiling = space.indexOf(metaWindow) > -1;
windowPositionSeen = metaWindow.clone.get_transformed_position().map(Math.round);
}
metaWindow[float] = true;
metaWindow.make_above();
metaWindow.stick(); // NB! Removes the window from the tiling (synchronously)
if (!metaWindow.minimized)
Tiling.showWindow(metaWindow);
if (fromTiling) {
metaWindow._initialResizeDone = false; // [free-resize] automaximize on future insertion
let f = metaWindow.get_frame_rect();
let targetFrame = null;
if (metaWindow[scratchFrame]) {
let sf = metaWindow[scratchFrame];
if (utils.monitorOfPoint(sf.x, sf.y) === focusMonitor()) {
targetFrame = sf;
}
}
if (!targetFrame) {
// Default to moving the window slightly down and reducing the height
let vDisplacement = 30;
let [x, y] = windowPositionSeen; // The window could be non-placable so can't use frame
targetFrame = new Meta.Rectangle({
x: x, y: y + vDisplacement,
width: f.width,
height: Math.min(f.height - vDisplacement, Math.floor(f.height * 0.9))
})
}
if (!metaWindow.minimized) {
metaWindow.move_resize_frame(true, f.x, f.y,
targetFrame.width, targetFrame.height);
tweenScratch(metaWindow, targetFrame.x, targetFrame.y,
{onComplete: () => delete metaWindow[scratchFrame]});
} else {
// Can't restore the scratch geometry immediately since it distort the minimize animation
// ASSUMPTION: minimize animation is not disabled and not already done
let actor = metaWindow.get_compositor_private();
let signal = actor.connect('effects-completed', () => {
metaWindow.move_resize_frame(true, targetFrame.x, targetFrame.y,
targetFrame.width, targetFrame.height);
actor.disconnect(signal)
})
}
}
let monitor = focusMonitor();
if (monitor.clickOverlay)
monitor.clickOverlay.hide();
}
function unmakeScratch(metaWindow) {
if (!metaWindow[scratchFrame])
metaWindow[scratchFrame] = metaWindow.get_frame_rect();
metaWindow[float] = false;
metaWindow.unmake_above();
metaWindow.unstick();
}
function toggle(metaWindow) {
if (isScratchWindow(metaWindow)) {
unmakeScratch(metaWindow);
hide();
} else {
makeScratch(metaWindow);
if (metaWindow.has_focus) {
let space = Tiling.spaces.get(global.workspace_manager.get_active_workspace());
space.setSelectionInactive();
}
}
}
function isScratchWindow(metaWindow) {
return metaWindow && metaWindow[float];
}
/** Return scratch windows in MRU order */
function getScratchWindows() {
return global.display.get_tab_list(Meta.TabList.NORMAL, null)
.filter(isScratchWindow);
}
function isScratchActive() {
return getScratchWindows().some(metaWindow => !metaWindow.minimized);
}
function toggleScratch() {
if (isScratchActive())
hide();
else
show();
}
function toggleScratchWindow() {
let focus = global.display.focus_window;
if (isScratchWindow(focus))
hide();
else
show(true);
}
function show(top) {
let windows = getScratchWindows();
if (windows.length === 0) {
return;
}
if (top)
windows = windows.slice(0,1);
TopBar.show();
windows.slice().reverse()
.map(function(meta_window) {
meta_window.unminimize();
meta_window.make_above();
meta_window.get_compositor_private().show();
});
windows[0].activate(global.get_current_time());
let monitor = focusMonitor();
if (monitor.clickOverlay)
monitor.clickOverlay.hide();
}
function hide() {
let windows = getScratchWindows();
windows.map(function(meta_window) {
meta_window.minimize();
});
}
// Monkey patch the alt-space menu
var PopupMenu = imports.ui.popupMenu;
var WindowMenu = imports.ui.windowMenu;
var originalBuildMenu = WindowMenu.WindowMenu.prototype._buildMenu;
function init() {
float = Symbol();
scratchFrame = Symbol();
}
function enable() {
WindowMenu.WindowMenu.prototype._buildMenu =
function (window) {
let item;
item = this.addAction(_('Scratch'), () => {
toggle(window);
});
if (isScratchWindow(window))
item.setOrnament(PopupMenu.Ornament.CHECK);
originalBuildMenu.call(this, window);
};
}
function disable() {
WindowMenu.WindowMenu.prototype._buildMenu = originalBuildMenu;
}