%PDF- %PDF-
| Direktori : /usr/share/gnome-shell/extensions/tiling-assistant@ubuntu.com/src/extension/ |
| Current File : //usr/share/gnome-shell/extensions/tiling-assistant@ubuntu.com/src/extension/keybindingHandler.js |
import { Clutter, Meta, Shell, St } from '../dependencies/gi.js';
import { _, Main } from '../dependencies/shell.js';
import { Direction, DynamicKeybindings, Settings, Shortcuts } from '../common.js';
import { Rect, Util } from './utility.js';
import { TilingWindowManager as Twm } from './tilingWindowManager.js';
/**
* Class to handle the keyboard shortcuts (on the extension side) except the
* ones related to the Layouts. For those, see layoutsManager.js.
*/
export default class TilingKeybindingHandler {
constructor() {
const allowInOverview = [Shortcuts.TOGGLE_POPUP];
this._keyBindings = Shortcuts.getAllKeys();
this._keyBindings.forEach(key => {
Main.wm.addKeybinding(
key,
Settings.getGioObject(),
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | (allowInOverview.includes(key) && Shell.ActionMode.OVERVIEW),
this._onCustomKeybindingPressed.bind(this, key)
);
});
}
destroy() {
this._keyBindings.forEach(key => Main.wm.removeKeybinding(key));
this._debuggingIndicators?.forEach(i => i.destroy());
}
/**
* @param {string} shortcutName
*/
async _onCustomKeybindingPressed(shortcutName) {
// Debugging
const debugging = [Shortcuts.DEBUGGING, Shortcuts.DEBUGGING_FREE_RECTS];
if (debugging.includes(shortcutName)) {
if (this._debuggingIndicators) {
this._debuggingIndicators.forEach(i => i.destroy());
this._debuggingIndicators = null;
} else {
const createIndicators = shortcutName === Shortcuts.DEBUGGING
? Util.___debugShowTiledRects
: Util.___debugShowFreeScreenRects;
this._debuggingIndicators = await createIndicators.call(Util);
}
return;
// Toggle the Tiling Popup
} else if (shortcutName === Shortcuts.TOGGLE_POPUP) {
const toggleTo = !Settings.getBoolean(Settings.ENABLE_TILING_POPUP);
Settings.setBoolean(Settings.ENABLE_TILING_POPUP, toggleTo);
Main.notify('Tiling Assistant', toggleTo
? _('Tiling popup enabled')
: _('Tiling popup was disabled'));
return;
}
const window = global.display.focus_window;
if (!window)
return;
// Auto-tile: tile to empty space. If there's none: untile,
// if it's already tiled else maximize
if (shortcutName === Shortcuts.AUTO_FILL) {
if (Twm.isMaximized(window)) {
Twm.untile(window);
} else {
const topTileGroup = Twm.getTopTileGroup({ skipTopWindow: !window.isTiled });
const tRects = topTileGroup.map(w => w.tiledRect);
const tileRect = Twm.getBestFreeRect(tRects, { currRect: window.tiledRect });
Twm.toggleTiling(window, tileRect);
}
// Tile Editing Mode
} else if (shortcutName === Shortcuts.EDIT_MODE) {
const TileEditingMode = await import('./tileEditingMode.js');
const tileEditor = new TileEditingMode.TileEditor();
tileEditor.open();
// Toggle always-on-top
} else if (shortcutName === Shortcuts.ALWAYS_ON_TOP) {
window.is_above() ? window.unmake_above() : window.make_above();
// Toggle maximization vertically
} else if (shortcutName === Shortcuts.MAXIMIZE_V) {
const workArea = new Rect(window.get_work_area_current_monitor());
const currRect = window.tiledRect ?? window.get_frame_rect();
// Is tiled or maximized with this extension
if (window.untiledRect && currRect.height === workArea.height) {
// Is maximized
if (currRect.width === workArea.width) {
const tileRect = new Rect(workArea.x, window.untiledRect.y, workArea.width, window.untiledRect.height);
Twm.tile(window, tileRect);
// Is tiled
} else {
Twm.untile(window);
}
// Maximize vertically even if the height may already be equal to the workArea
// e. g. via double-click titlebar, maximize-window-button or whatever
} else {
const tileRect = new Rect(currRect.x, workArea.y, currRect.width, workArea.height);
Twm.tile(window, tileRect);
}
// Toggle maximization horizontally
} else if (shortcutName === Shortcuts.MAXIMIZE_H) {
const workArea = new Rect(window.get_work_area_current_monitor());
const currRect = window.tiledRect ?? window.get_frame_rect();
// Is tiled or maximized with this extension
if (window.untiledRect && currRect.width === workArea.width) {
// Is maximized
if (currRect.height === workArea.height) {
const tileRect = new Rect(window.untiledRect.x, workArea.y, window.untiledRect.width, workArea.height);
Twm.tile(window, tileRect);
// Is tiled
} else {
Twm.untile(window);
}
// Maximize horizontally even if the width may already be equal to the workArea
// e. g. via double-click titlebar, maximize-window-button or whatever
} else {
const tileRect = new Rect(workArea.x, currRect.y, workArea.width, currRect.height);
Twm.tile(window, tileRect);
}
// Restore window size
} else if (shortcutName === Shortcuts.RESTORE_WINDOW) {
if (window.untiledRect) // Tiled & maximized with gaps
Twm.untile(window, { clampToWorkspace: true });
else if (window.get_maximized())
window.unmaximize(window.get_maximized());
// Center window
} else if (shortcutName === Shortcuts.CENTER_WINDOW) {
const workArea = new Rect(window.get_work_area_current_monitor());
if (window.isTiled) {
const currRect = window.tiledRect;
const tileRect = new Rect(
workArea.center.x - Math.floor(currRect.width / 2),
workArea.center.y - Math.floor(currRect.height / 2),
currRect.width,
currRect.height
);
if (tileRect.equal(currRect))
return;
Twm.tile(window, tileRect, { openTilingPopup: false });
} else if (!Twm.isMaximized(window)) {
if (!window.allows_move())
return;
const currRect = window.get_frame_rect();
const x = workArea.center.x - Math.floor(currRect.width / 2);
const y = workArea.center.y - Math.floor(currRect.height / 2);
if (x === currRect.x && y === currRect.y)
return;
const wActor = window.get_compositor_private();
wActor && Main.wm._prepareAnimationInfo(
global.window_manager,
wActor,
currRect,
Meta.SizeChange.UNMAXIMIZE
);
window.move_frame(false, x, y);
}
// Tile a window but ignore T-A features
} else if ([Shortcuts.TOP_IGNORE_TA, Shortcuts.BOTTOM_IGNORE_TA,
Shortcuts.LEFT_IGNORE_TA, Shortcuts.RIGHT_IGNORE_TA,
Shortcuts.TOP_LEFT_IGNORE_TA, Shortcuts.TOP_RIGHT_IGNORE_TA,
Shortcuts.BOTTOM_LEFT_IGNORE_TA,
Shortcuts.BOTTOM_RIGHT_IGNORE_TA].includes(shortcutName)
) {
const workArea = new Rect(window.get_work_area_current_monitor());
const rect = Twm.getDefaultTileFor(shortcutName, workArea);
Twm.toggleTiling(window, rect, { ignoreTA: true });
// Tile a window
} else {
const dynamicBehavior = Settings.DYNAMIC_KEYBINDINGS;
const dynamicSetting = Settings.getInt(dynamicBehavior);
const windowsStyle = DynamicKeybindings.TILING_STATE_WINDOWS;
const isWindowsStyle = dynamicSetting === windowsStyle;
const workArea = new Rect(window.get_work_area_current_monitor());
const rect = Twm.getTileFor(shortcutName, workArea, window.get_monitor());
switch (dynamicSetting) {
case DynamicKeybindings.FOCUS:
this._dynamicFocus(window, shortcutName);
break;
case DynamicKeybindings.TILING_STATE:
case DynamicKeybindings.TILING_STATE_WINDOWS:
this._dynamicTilingState(window, shortcutName, isWindowsStyle);
break;
case DynamicKeybindings.FAVORITE_LAYOUT:
this._dynamicFavoriteLayout(window, shortcutName);
break;
default:
Twm.toggleTiling(window, rect);
}
}
}
/**
* Tiles or moves the focus depending on the `windows` tiling state.
*
* @param {Meta.Window} window a Meta.Window as the starting position.
* @param {string} shortcutName indicates the direction we tile or move
* the focus to.
*/
_dynamicFocus(window, shortcutName) {
const topTileGroup = Twm.getTopTileGroup({ skipTopWindow: true });
const workArea = new Rect(window.get_work_area_current_monitor());
// Toggle tile state of the window, if it isn't tiled
// or if it is the only window which is.
if (!window.isTiled || topTileGroup.length === 1) {
const rect = Twm.getTileFor(shortcutName, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
}
let direction;
switch (shortcutName) {
case Shortcuts.MAXIMIZE:
case Shortcuts.TOP:
direction = Direction.N;
break;
case Shortcuts.BOTTOM:
direction = Direction.S;
break;
case Shortcuts.LEFT:
direction = Direction.W;
break;
case Shortcuts.RIGHT:
direction = Direction.E;
}
const nearestWindow = Twm.getNearestWindow(
window,
topTileGroup,
direction,
false
);
if (!nearestWindow) {
const rect = Twm.getTileFor(shortcutName, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
}
// Activate() caused problems with an extensions' prefs window, if the
// extensions-app wasn't open.
nearestWindow.focus(global.get_current_time());
// Animation for visibility with a tmp 'tile preview'
const fromRect = window.get_frame_rect();
const focusIndicator = new St.Widget({
style_class: 'tile-preview',
opacity: 0,
x: fromRect.x,
y: fromRect.y,
width: fromRect.width,
height: fromRect.height
});
Main.uiGroup.add_child(focusIndicator);
const toRect = nearestWindow.get_frame_rect();
focusIndicator.ease({
opacity: 255,
x: toRect.x,
y: toRect.y,
width: toRect.width,
height: toRect.height,
duration: 200,
mode: Clutter.AnimationMode.EASE_OUT_QUART,
onComplete: () => {
focusIndicator.ease({
opacity: 0,
duration: 200,
mode: Clutter.AnimationMode.EASE_IN_OUT_CIRC,
delay: 100,
onComplete: () => focusIndicator.destroy()
});
}
});
}
/**
* Changes the tiling state of the `window` based on its current tiling
* state and the activated shortcut.
*
* @param {Meta.Window} window a Meta.Window.
* @param {string} shortcutName the shortcut.
* @param {boolean} isWindowsStyle minimize when the `window` isn't tiled or
* if it's tiled to the bottom and the 'tile to bottom' shortcut is
* activated.
*/
_dynamicTilingState(window, shortcutName, isWindowsStyle) {
const workArea = new Rect(window.get_work_area_current_monitor());
if (Twm.isMaximized(window)) {
switch (shortcutName) {
case Shortcuts.MAXIMIZE:
case Shortcuts.TOP: {
const rect = Twm.getTileFor(Shortcuts.TOP, workArea, window.get_monitor());
Twm.tile(window, rect, { skipAnim: true });
break;
} case Shortcuts.BOTTOM: {
Twm.untile(window);
break;
} default: {
const rect = Twm.getTileFor(shortcutName, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
}
}
return;
} else if (!window.isTiled) {
switch (shortcutName) {
case Shortcuts.BOTTOM: {
if (isWindowsStyle) {
window.minimize();
break;
}
// falls through
} default: {
const rect = Twm.getTileFor(shortcutName, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
}
}
return;
}
const wRect = window.tiledRect;
const isLeftHalf =
wRect.x === workArea.x &&
wRect.y === workArea.y &&
wRect.width !== workArea.width &&
wRect.height === workArea.height;
const isRightHalf =
wRect.x !== workArea.x &&
wRect.y === workArea.y &&
wRect.x2 === workArea.x2 &&
wRect.height === workArea.height;
const isTopHalf =
wRect.x === workArea.x &&
wRect.y === workArea.y &&
wRect.width === workArea.width &&
wRect.height !== workArea.height;
const isBottomHalf =
wRect.x === workArea.x &&
wRect.y !== workArea.y &&
wRect.width === workArea.width &&
wRect.y2 === workArea.y2;
const isTopLeftQuarter =
wRect.x === workArea.x &&
wRect.y === workArea.y &&
wRect.width !== workArea.width &&
wRect.height !== workArea.height;
const isTopRightQuarter =
wRect.x !== workArea.x &&
wRect.y === workArea.y &&
wRect.x2 === workArea.x2 &&
wRect.height !== workArea.height;
const isBottomLeftQuarter =
wRect.x === workArea.x &&
wRect.y !== workArea.y &&
wRect.width !== workArea.width &&
wRect.y2 === workArea.y2;
const isBottomRightQuarter =
wRect.x !== workArea.x &&
wRect.y !== workArea.y &&
wRect.x2 === workArea.x2 &&
wRect.y2 === workArea.y2;
let rect;
if (isLeftHalf) {
switch (shortcutName) {
case Shortcuts.TOP:
case Shortcuts.MAXIMIZE:
rect = Twm.getTileFor(Shortcuts.TOP_LEFT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.BOTTOM_LEFT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.RIGHT:
Twm.untile(window);
return;
}
} else if (isRightHalf) {
switch (shortcutName) {
case Shortcuts.TOP:
case Shortcuts.MAXIMIZE:
rect = Twm.getTileFor(Shortcuts.TOP_RIGHT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.BOTTOM_RIGHT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.LEFT:
Twm.untile(window);
return;
}
} else if (isTopHalf) {
switch (shortcutName) {
case Shortcuts.TOP:
rect = Twm.getTileFor(Shortcuts.MAXIMIZE, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.LEFT:
rect = Twm.getTileFor(Shortcuts.TOP_LEFT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.RIGHT:
rect = Twm.getTileFor(Shortcuts.TOP_RIGHT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
Twm.untile(window);
return;
}
} else if (isBottomHalf) {
switch (shortcutName) {
case Shortcuts.LEFT:
rect = Twm.getTileFor(Shortcuts.BOTTOM_LEFT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.RIGHT:
rect = Twm.getTileFor(Shortcuts.BOTTOM_RIGHT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.TOP:
case Shortcuts.MAXIMIZE:
Twm.untile(window);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.BOTTOM, workArea, window.get_monitor());
isWindowsStyle ? window.minimize() : Twm.toggleTiling(window, rect);
return;
}
} else if (isTopLeftQuarter) {
switch (shortcutName) {
case Shortcuts.RIGHT:
rect = Twm.getTileFor(Shortcuts.TOP, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.LEFT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
}
} else if (isTopRightQuarter) {
switch (shortcutName) {
case Shortcuts.LEFT:
rect = Twm.getTileFor(Shortcuts.TOP, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.RIGHT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
}
} else if (isBottomLeftQuarter) {
switch (shortcutName) {
case Shortcuts.TOP:
case Shortcuts.MAXIMIZE:
rect = Twm.getTileFor(Shortcuts.LEFT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.RIGHT:
rect = Twm.getTileFor(Shortcuts.BOTTOM, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.BOTTOM, workArea, window.get_monitor());
isWindowsStyle ? window.minimize() : Twm.toggleTiling(window, rect);
return;
}
} else if (isBottomRightQuarter) {
switch (shortcutName) {
case Shortcuts.TOP:
case Shortcuts.MAXIMIZE:
rect = Twm.getTileFor(Shortcuts.RIGHT, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.LEFT:
rect = Twm.getTileFor(Shortcuts.BOTTOM, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
return;
case Shortcuts.BOTTOM:
rect = Twm.getTileFor(Shortcuts.BOTTOM, workArea, window.get_monitor());
isWindowsStyle ? window.minimize() : Twm.toggleTiling(window, rect);
return;
}
}
Twm.toggleTiling(window, Twm.getTileFor(shortcutName, workArea, window.get_monitor()));
}
_dynamicFavoriteLayout(window, shortcutName) {
const workArea = new Rect(window.get_work_area_current_monitor());
const toggleTiling = () => {
const rect = Twm.getTileFor(shortcutName, workArea, window.get_monitor());
Twm.toggleTiling(window, rect);
};
if (!window.isTiled) {
toggleTiling();
return;
}
const favoriteLayout = Util.getFavoriteLayout(window.get_monitor());
if (favoriteLayout.length <= 1) {
toggleTiling();
return;
}
let direction;
switch (shortcutName) {
case Shortcuts.TOP:
case Shortcuts.MAXIMIZE:
direction = Direction.N;
break;
case Shortcuts.BOTTOM:
direction = Direction.S;
break;
case Shortcuts.LEFT:
direction = Direction.W;
break;
case Shortcuts.RIGHT:
direction = Direction.E;
}
if (direction) {
const neighbor = window.tiledRect.getNeighbor(direction, favoriteLayout);
Twm.tile(window, neighbor, { openTilingPopup: false });
} else {
toggleTiling();
}
}
}