Skip to content

Commit

Permalink
improved(?) & reenabled opening in tiled state
Browse files Browse the repository at this point in the history
  • Loading branch information
Leleat committed Jan 3, 2021
1 parent 4f8d231 commit bb675a8
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 72 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ Development started on GNOME 3.36. But as of November 2020 all development happe
- Windows-like (quarter) tiling: when a window is tiled and at least half the screen is occupied by tiled windows, a Dash containing icons for all non-tiled windows will open. The Dash will be centered on the free screen space.
Activating an icon will tile the corresponding window to fill the free space.

- Holding `Ctrl` while DNDing a window over another tiled window will make them share the same space. See the dnd preview below. (Idea from the Shelltile extension). Hovering over empty space while holding `Ctrl` will tile to that space.
- You can directly open an app from GNOME's search results or appGrid in a tiled state (left or right) by holding `Shift` or `Alt`. It will also effect other extensions which extend appDisplay.AppIcon (e.g. Dash-to-Dock). **experimental: needs feedback**

- You can hold `Shift` while activating an icon to tile the window to the top or the left half of the available screen depending on the orientation of the free screen space and `Alt` to tile the window to the bottom or right half (-> spiral-ish tiling). **Your windows shouldn't be tiled to be smaller than a screen quarter unless your resolution is 1440p+. Otherwise you will hit the window's minimum size and the tiled windows will overlap each other.**

- Holding `Ctrl` while DNDing a window over another tiled window will make them share the same space. See the dnd preview below. (Idea from the Shelltile extension). Hovering over empty space while holding `Ctrl` will tile to that space.

- Raise/focus tiled windows as a group.

- Resize tiled windows in a group (**horizontal and vertical only**). You can escape the grouped resizing by holding `Ctrl`. Combined with the keybindings to fill the available screen space and to tile to existing tiled windows this enables more complex layouts.
- Resize tiled windows in a group (**horizontal and vertical only**). You can escape the group-resizing by holding `Ctrl` (doesn't work with GTK apps). This way only the windows directly opposed to the resized window will resize along. Combined with the keybindings to fill the available screen space and to tile to existing tiled windows this enables more complex layouts.

- Configurable window gaps

Expand Down
128 changes: 60 additions & 68 deletions tiling-assistant@leleat-on-github/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,79 +61,71 @@ function enable() {
);
});

// --- disabled until I can come up with a good way since it is unreliable/buggy in edge cases ---
// TODO: also open apps to fill empty space
// change appDisplay.AppIcon.activate function.
// allow to directly open an app in a tiled state
// via holding Alt or Shift when activating the icon
// this.oldAppActivateFunc = appDisplay.AppIcon.prototype.activate;
// appDisplay.AppIcon.prototype.activate = function (button) {
// let event = Clutter.get_current_event();
// let modifiers = event ? event.get_state() : 0;
// let isAltPressed = modifiers & Clutter.ModifierType.MOD1_MASK;
// let isShiftPressed = modifiers & Clutter.ModifierType.SHIFT_MASK;
// let isMiddleButton = button && button == Clutter.BUTTON_MIDDLE;
// let isCtrlPressed = (modifiers & Clutter.ModifierType.CONTROL_MASK) != 0;
// let openNewWindow = this.app.can_open_new_window() &&
// this.app.state == Shell.AppState.RUNNING &&
// (isCtrlPressed || isMiddleButton);

// if (this.app.state == Shell.AppState.STOPPED || openNewWindow || isShiftPressed || isAltPressed)
// this.animateLaunch();

// if (openNewWindow) {
// this.app.open_new_window(-1);

// // main new code
// } else if ((isShiftPressed || isAltPressed) && this.app.can_open_new_window()) {
// let wCount = this.app.get_windows().length;
// let sIdWindowsChanged = 0, sIdFocus = 0, sIdFirstFrame = 0;

// sIdWindowsChanged = this.app.connect("windows-changed", (app) => {
// let newWCount = app.get_windows().length;

// if (wCount < newWCount) { // new window created
// let window = app.get_windows()[0];

// // focus signal is probably unreliable, but couldn"t come up with a better way to wait for the window
// sIdFocus = window.connect("focus", () => {
// window.disconnect(sIdFocus);

// // here we try to ignore loading screens; different apps use different windows for loading screens:
// // For ex.: Krita's and GIMP's loading screen returns true for is_skip_taskbar()
// // Steam's loading screen is a normal window, which doesn"t skip the taskbar but doesn"t allow_resize()
// if (window.is_skip_taskbar() || !window.allows_move() || !window.allows_resize())
// return;

// let wActor = window.get_compositor_private();
// sIdFirstFrame = wActor.connect("first-frame", () => {
// app.disconnect(sIdWindowsChanged);
// wActor.disconnect(sIdFirstFrame);

// let workArea = window.get_work_area_current_monitor();
// let rect = new Meta.Rectangle({
// x: workArea.x + (isShiftPressed) ? 0 : workArea.width / 2,
// y: workArea.y,
// width: workArea.width / 2,
// height: workArea.height
// });

// Funcs.tileWindow(window, rect);
// });
// });
// }

// wCount = newWCount;
// });
this.oldAppActivateFunc = appDisplay.AppIcon.prototype.activate;
appDisplay.AppIcon.prototype.activate = function (button) {
let event = Clutter.get_current_event();
let modifiers = event ? event.get_state() : 0;
let isAltPressed = modifiers & Clutter.ModifierType.MOD1_MASK;
let isShiftPressed = modifiers & Clutter.ModifierType.SHIFT_MASK;
let isMiddleButton = button && button == Clutter.BUTTON_MIDDLE;
let isCtrlPressed = (modifiers & Clutter.ModifierType.CONTROL_MASK) != 0;
let openNewWindow = this.app.can_open_new_window() &&
this.app.state == Shell.AppState.RUNNING &&
(isCtrlPressed || isMiddleButton);

if (this.app.state == Shell.AppState.STOPPED || openNewWindow || isShiftPressed || isAltPressed)
this.animateLaunch();

if (openNewWindow) {
this.app.open_new_window(-1);

// main new code
} else if ((isShiftPressed || isAltPressed) && this.app.can_open_new_window()) {
let winTracker = Shell.WindowTracker.get_default();
let ogApp = this.app;
let wCreatedId = global.display.connect("window-created", (src, window) => {
// here we try to ignore loading screens; different apps use different windows for loading screens:
// For ex.: Krita's and GIMP's loading screen returns true for is_skip_taskbar()
// Steam's loading screen is a normal window, which doesn't skip the taskbar but doesn't allow_resize()
if (window.get_window_type() != Meta.WindowType.NORMAL || window.is_skip_taskbar() || !window.allows_move() || !window.allows_resize())
return;

global.display.disconnect(wCreatedId);

// in case window detection above didn't work properly.
// this breaks, if the current to-be-tiled app loads (-> load screen) and the user opens another window
// ... acceptable downside for me
let appOpened = winTracker.get_window_app(window)
if (appOpened != ogApp)
return;

let wActor = window.get_compositor_private();
let firstFrameID = wActor.connect("first-frame", () => {
wActor.disconnect(firstFrameID);

let workArea = window.get_work_area_current_monitor();
let rect = new Meta.Rectangle({
x: workArea.x + (isShiftPressed) ? 0 : workArea.width / 2,
y: workArea.y,
width: workArea.width / 2,
height: workArea.height
});

Funcs.tileWindow(window, rect);
});
});

// this.app.open_new_window(-1);
this.app.open_new_window(-1);

// } else {
// this.app.activate();
// }
} else {
this.app.activate();
}

// main.overview.hide();
// };
main.overview.hide();
};

// change main.panel._getDraggableWindowForPosition to also include windows tiled with this extension
this.oldGetDraggableWindowForPosition = main.panel._getDraggableWindowForPosition;
Expand Down Expand Up @@ -177,7 +169,7 @@ function disable() {
});

// restore old function
// appDisplay.AppIcon.prototype.activate = this.oldAppActivateFunc;
appDisplay.AppIcon.prototype.activate = this.oldAppActivateFunc;
main.panel._getDraggableWindowForPosition = this.oldGetDraggableWindowForPosition;

// delete custom properties
Expand Down
5 changes: 3 additions & 2 deletions tiling-assistant@leleat-on-github/funcs.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ function restoreWindowSize(window, restoreFullPos = false) {
let relativeMouseX = (mouseX - currWindowFrame.x) / currWindowFrame.width;
let newPosX = mouseX - oldRect.width * relativeMouseX;

// user_op with true needed to properly restore big window in the bottom half via DND
// user_op with true to properly restore big windows via DND so they can go partly offscreen
window.move_frame(true, newPosX, currWindowFrame.y); // Wayland workaround for DND/restore position
window.move_resize_frame(true, newPosX, currWindowFrame.y, oldRect.width, oldRect.height);
}
Expand Down Expand Up @@ -699,7 +699,8 @@ function resizeComplementingWindows(resizedWindow, grabOp, gap) {
// called via keybinding:
// tile a window to existing layout below it
function replaceTiledWindow(window) {
let currTileGroup = getTopTileGroup();
let openWindows = getOpenWindows();
let currTileGroup = getTopTileGroup(openWindows, (openWindows[0].isTiled) ? false : true);
if (!currTileGroup.length)
return;

Expand Down

0 comments on commit bb675a8

Please sign in to comment.