diff --git a/tiling-assistant@leleat-on-github/media/tile-base-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-base-symbolic.svg
new file mode 100644
index 00000000..126bc597
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-base-symbolic.svg
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-bottom-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-bottom-symbolic.svg
new file mode 100644
index 00000000..67c176a7
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-bottom-symbolic.svg
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-horizontal-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-horizontal-symbolic.svg
new file mode 100644
index 00000000..a5bead91
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-horizontal-symbolic.svg
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-left-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-left-symbolic.svg
new file mode 100644
index 00000000..b05a1efa
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-left-symbolic.svg
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-maximize-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-maximize-symbolic.svg
new file mode 100644
index 00000000..0fdeecbf
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-maximize-symbolic.svg
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-q1-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-q1-symbolic.svg
new file mode 100644
index 00000000..62bb30f8
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-q1-symbolic.svg
@@ -0,0 +1,28 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-q2-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-q2-symbolic.svg
new file mode 100644
index 00000000..9e951c38
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-q2-symbolic.svg
@@ -0,0 +1,28 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-q3-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-q3-symbolic.svg
new file mode 100644
index 00000000..492a17a3
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-q3-symbolic.svg
@@ -0,0 +1,28 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-q4-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-q4-symbolic.svg
new file mode 100644
index 00000000..c38b541b
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-q4-symbolic.svg
@@ -0,0 +1,28 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-quarter-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-quarter-symbolic.svg
new file mode 100644
index 00000000..c2e1e192
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-quarter-symbolic.svg
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-right-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-right-symbolic.svg
new file mode 100644
index 00000000..a333b058
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-right-symbolic.svg
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-top-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-top-symbolic.svg
new file mode 100644
index 00000000..f6346e72
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-top-symbolic.svg
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/media/tile-vertical-symbolic.svg b/tiling-assistant@leleat-on-github/media/tile-vertical-symbolic.svg
new file mode 100644
index 00000000..3ee05ea9
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/media/tile-vertical-symbolic.svg
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/tiling-assistant@leleat-on-github/prefs.js b/tiling-assistant@leleat-on-github/prefs.js
index 2769cccc..5b63d72d 100644
--- a/tiling-assistant@leleat-on-github/prefs.js
+++ b/tiling-assistant@leleat-on-github/prefs.js
@@ -74,6 +74,7 @@ export default class Prefs extends ExtensionPreferences {
'enable-advanced-experimental-features',
'disable-tile-groups',
'low-performance-move-mode',
+ 'enable-layout-picker',
'monitor-switch-grace-period',
'adapt-edge-tiling-to-favorite-layout',
'enable-tile-animations',
diff --git a/tiling-assistant@leleat-on-github/schemas/org.gnome.shell.extensions.tiling-assistant.gschema.xml b/tiling-assistant@leleat-on-github/schemas/org.gnome.shell.extensions.tiling-assistant.gschema.xml
index 88c07c63..38f477ff 100644
--- a/tiling-assistant@leleat-on-github/schemas/org.gnome.shell.extensions.tiling-assistant.gschema.xml
+++ b/tiling-assistant@leleat-on-github/schemas/org.gnome.shell.extensions.tiling-assistant.gschema.xml
@@ -239,6 +239,9 @@
false
+
+ true
+
false
diff --git a/tiling-assistant@leleat-on-github/src/extension/layoutPicker.js b/tiling-assistant@leleat-on-github/src/extension/layoutPicker.js
new file mode 100644
index 00000000..1ec4a072
--- /dev/null
+++ b/tiling-assistant@leleat-on-github/src/extension/layoutPicker.js
@@ -0,0 +1,402 @@
+import { Clutter, GObject, St, Gio } from '../dependencies/gi.js';
+import { Main, Extension } from '../dependencies/shell.js';
+
+const LayoutPickerVisibility = {
+ HIDDEN: 0,
+ PEAK: 1,
+ SHOWN: 2
+};
+
+function iconPath(name) {
+ const path = Extension.lookupByURL(import.meta.url)
+ .dir
+ .get_child(`media/${name}-symbolic.svg`)
+ .get_path();
+
+ return path;
+}
+
+export const LayoutPickerTileType = Object.freeze({
+ NONE: 0,
+ LEFT: 1,
+ RIGHT: 2,
+ TOP: 3,
+ BOTTOM: 4,
+ Q1: 5,
+ Q2: 6,
+ Q3: 7,
+ Q4: 8,
+ MAXIMIZE: 9
+});
+
+// omit '-symbolic.svg' as it is managed by iconPath function already
+const ICONS = {
+ vertical: {
+ none: 'tile-vertical',
+
+ states: {
+ [LayoutPickerTileType.TOP]: 'tile-top',
+ [LayoutPickerTileType.BOTTOM]: 'tile-bottom'
+ }
+ },
+
+ horizontal: {
+ none: 'tile-horizontal',
+
+ states: {
+ [LayoutPickerTileType.LEFT]: 'tile-left',
+ [LayoutPickerTileType.RIGHT]: 'tile-right'
+ }
+ },
+
+ quarter: {
+ none: 'tile-quarter',
+
+ states: {
+ [LayoutPickerTileType.Q1]: 'tile-q1',
+ [LayoutPickerTileType.Q2]: 'tile-q2',
+ [LayoutPickerTileType.Q3]: 'tile-q3',
+ [LayoutPickerTileType.Q4]: 'tile-q4'
+ }
+ },
+
+ maximize: {
+ none: 'tile-base',
+
+ states: {
+ [LayoutPickerTileType.MAXIMIZE]: 'tile-maximize'
+ }
+ }
+};
+
+export const LayoutPicker = GObject.registerClass({
+ Properties: {
+ 'tile-type': GObject.ParamSpec.int(
+ 'tile-type', 'tile-type', 'tile-type',
+ GObject.ParamFlags.READWRITE,
+ LayoutPickerTileType.NONE,
+ LayoutPickerTileType.MAXIMIZE,
+ LayoutPickerTileType.NONE
+ )
+ }
+}, class LayoutPicker extends St.Bin {
+ _init() {
+ super._init({
+ style_class: 'tiling-menu-container'
+ });
+
+ this._container = new St.BoxLayout({
+ x_expand: true,
+ orientation: Clutter.Orientation.HORIZONTAL,
+ style_class: 'popup-menu-content'
+ });
+
+ this._visibility = LayoutPickerVisibility.HIDDEN;
+ this._dragging = false;
+
+ this.set_child(this._container);
+
+ this._addChrome();
+
+ this._icons = {};
+
+ for (const [group, config] of Object.entries(ICONS)) {
+ // start with icons that appear to be not hovered
+ const icon = this._createIcon(config.none);
+
+ config.icon = icon;
+ this._icons[group] = icon;
+
+ this._container.add_child(icon);
+ }
+
+ // e.g a dock is enabled and or disabled
+ global.display.connectObject('workareas-changed', () => {
+ this._updateAllocation(global.display.get_current_monitor());
+ }, this);
+
+ Main.layoutManager.connectObject('monitors-changed', () => {
+ this._updateAllocation(global.display.get_current_monitor());
+ }, this);
+
+ // GNOME Shell doesn't disable extensions when shutting down, so stop
+ // reacting to monitor and work-area changes as soon as it shuts down.
+ global.connectObject('shutdown', () => {
+ global.display.disconnectObject(this);
+ Main.layoutManager.disconnectObject(this);
+ }, this);
+
+ // The picker only needs to know when the move it reacts to is over,
+ // so it can hide itself without the move handler having to track it.
+ global.display.connectObject('grab-op-end', () => this.onMoveFinished(), this);
+
+ // just in case extension is enabled and disable
+ this._updateAllocation(global.display.get_current_monitor());
+
+ this.connectObject('notify::translation-y', () => {
+ this.set_clip(
+ 0,
+ this.height - this.translation_y,
+ this.width,
+ this.height
+ );
+ }, this);
+ }
+
+ _setVisibility(visibility) {
+ if (this._visibility === visibility)
+ return;
+
+ this._visibility = visibility;
+
+ this.opacity = 255;
+ this.reactive = true;
+
+
+ let positions = [
+ 0,
+ this._container.get_theme_node().get_padding(St.Side.Bottom) +
+ this.get_theme_node().get_padding(St.Side.BOTTOM),
+ this.height
+ ];
+
+ this.remove_all_transitions();
+
+ this.ease({
+ translation_y: positions[visibility],
+ duration: 250,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.opacity = this._visibility !== LayoutPickerVisibility.HIDDEN ? 255 : 0;
+ this.reactive = this._visibility !== LayoutPickerVisibility.HIDDEN;
+
+ // once picker is fully shown onMoving might not be able to update tile type
+ // for situations like no cursor update after picker is fully shown.
+ // this could causes incorrect tile type shown.
+ if (this._visibility === LayoutPickerVisibility.SHOWN) {
+ let [curX, curY] = global.get_pointer();
+ this._updateLayoutPickerTileType(curX, curY);
+ }
+ }
+
+ });
+ }
+
+ onMoving(curX, curY, monitorIndex = global.display.get_current_monitor()) {
+ if (this._dragging === false)
+ return;
+
+ this._updateAllocation(monitorIndex);
+
+ let [w, h] = this.get_size();
+ let [mx, my_] = this.get_transformed_position();
+
+ const themeNode = this.get_theme_node();
+ let paddingLeft = themeNode.get_padding(St.Side.LEFT);
+ let paddingRight = themeNode.get_padding(St.Side.RIGHT);
+ let paddingBottom = this.get_theme_node().get_padding(St.Side.BOTTOM);
+
+ const monitorArea = Main.layoutManager.monitors[monitorIndex];
+ const activeWs = global.workspace_manager.get_active_workspace();
+ const workArea = activeWs.get_work_area_for_monitor(monitorIndex);
+
+ // using monitorArea.y instead of workArea.y as upper bound to compensate with chromes such us the top bar height
+ // placing cursor above workArea.y causes visibility glitch. workArea.y and monitorArea.y will be same for other monitor anyways.
+ if (
+ curY >= monitorArea.y &&
+ curY <= workArea.y + h - paddingBottom &&
+ curX >= mx + paddingLeft &&
+ curX <= mx + w - paddingRight
+ )
+ this._setVisibility(LayoutPickerVisibility.SHOWN);
+ else
+ this._setVisibility(LayoutPickerVisibility.PEAK);
+
+ if (this._visibility === LayoutPickerVisibility.SHOWN)
+ this._updateLayoutPickerTileType(curX, curY);
+ else
+ this._resetTileType();
+ }
+
+ onMoveStarted() {
+ this._dragging = true;
+ this._resetTileType();
+ this._setVisibility(LayoutPickerVisibility.PEAK);
+ }
+
+ onMoveFinished() {
+ this._dragging = false;
+ this._resetTileType();
+ this._setVisibility(LayoutPickerVisibility.HIDDEN);
+ }
+
+ _resetTileType() {
+ this.tileType = LayoutPickerTileType.NONE;
+ this._clearIcons();
+ }
+
+ _setLayoutPickerIcon(tileType) {
+ this._clearIcons();
+
+ for (const config of Object.values(ICONS)) {
+ const iconName = config.states[tileType];
+
+ if (!iconName)
+ continue;
+
+ this._updateIcon(config.icon, iconName);
+ break;
+ }
+ }
+
+ _createIcon(name) {
+ let fallback_gicon = Gio.FileIcon.new(
+ Gio.File.new_for_path(iconPath(name))
+ );
+
+ let gicon = new Gio.ThemedIcon({
+ name: `${name}-symbolic`
+ });
+
+ return new St.Icon({
+ gicon,
+ fallback_gicon
+ });
+ }
+
+ _updateIcon(icon, name) {
+ let fallback_gicon = Gio.FileIcon.new(
+ Gio.File.new_for_path(iconPath(name))
+ );
+
+ let gicon = new Gio.ThemedIcon({
+ name: `${name}-symbolic`
+ });
+ icon.set({
+ gicon,
+ fallback_gicon
+ });
+ }
+
+ _clearIcons() {
+ for (const config of Object.values(ICONS))
+ this._updateIcon(config.icon, config.none);
+ }
+
+ _updateLayoutPickerTileType(curX, curY) {
+ let rect = icon => {
+ let [x, y] = icon.get_transformed_position();
+ let [w, h] = icon.get_size();
+ return { x, y, w, h };
+ };
+
+ let contains = ({ x, y, w, h }) =>
+ curX >= x &&
+ curX <= x + w &&
+ curY >= y &&
+ curY <= y + h;
+ const horizontal = rect(this._icons.horizontal);
+
+ if (contains(horizontal)) {
+ const leftPortion = curX < horizontal.x + horizontal.w / 2;
+
+ this.tileType = leftPortion
+ ? LayoutPickerTileType.LEFT
+ : LayoutPickerTileType.RIGHT;
+
+ this._setLayoutPickerIcon(this.tileType);
+ return;
+ }
+
+ const vertical = rect(this._icons.vertical);
+
+ if (contains(vertical)) {
+ const topPortion = curY <= vertical.y + vertical.h / 2;
+
+ this.tileType = topPortion
+ ? LayoutPickerTileType.TOP
+ : LayoutPickerTileType.BOTTOM;
+
+ this._setLayoutPickerIcon(this.tileType);
+ return;
+ }
+
+ const quarter = rect(this._icons.quarter);
+
+ if (contains(quarter)) {
+ const leftPortion = curX < quarter.x + quarter.w / 2;
+ const topPortion = curY <= quarter.y + quarter.h / 2;
+
+ if (topPortion)
+ { this.tileType = leftPortion
+ ? LayoutPickerTileType.Q2
+ : LayoutPickerTileType.Q1; }
+ else
+ { this.tileType = leftPortion
+ ? LayoutPickerTileType.Q3
+ : LayoutPickerTileType.Q4; }
+
+ this._setLayoutPickerIcon(this.tileType);
+ return;
+ }
+
+ this.tileType = contains(rect(this._icons.maximize))
+ ? LayoutPickerTileType.MAXIMIZE
+ : LayoutPickerTileType.NONE;
+
+ this._setLayoutPickerIcon(this.tileType);
+ }
+
+ _updateAllocation(monitorIndex) {
+ // The current monitor can already be gone (e.g. when the last monitor
+ // is removed during shutdown), so make sure it still exists before
+ // asking Mutter for its work area.
+ if (!Main.layoutManager.monitors[monitorIndex])
+ return;
+
+ const activeWs = global.workspace_manager.get_active_workspace();
+ const workArea = activeWs.get_work_area_for_monitor(monitorIndex);
+
+ if (workArea === null)
+ return;
+
+ const [, natWidth] = this.get_preferred_width(-1);
+ const [, natHeight] = this.get_preferred_height(-1);
+
+ const targetX = Math.round(workArea.x + (workArea.width - natWidth) / 2);
+ const targetY = Math.round(workArea.y - natHeight);
+
+ if (targetX === this.x && targetY === this.y)
+ return;
+
+ this.set_position(targetX, targetY);
+
+ this.remove_all_transitions();
+
+ this._visibility = LayoutPickerVisibility.HIDDEN;
+ this.translation_y = 0;
+ this.opacity = 0;
+ this.reactive = false;
+ }
+
+ _addChrome() {
+ Main.layoutManager.addChrome(this, {
+ affectsStruts: false,
+ trackFullscreen: false
+ });
+ }
+
+ _untrackChrome() {
+ Main.layoutManager.untrackChrome(this);
+ }
+
+ destroy() {
+ this._untrackChrome();
+
+ global.display.disconnectObject(this);
+ Main.layoutManager.disconnectObject(this);
+
+ super.destroy();
+ }
+});
+
diff --git a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js
index d224221f..56cd4b8a 100644
--- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js
+++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js
@@ -5,6 +5,7 @@ import { WINDOW_ANIMATION_TIME } from '../dependencies/unexported/windowManager.
import { MoveModes, Orientation, Settings } from '../common.js';
import { Rect, Util } from './utility.js';
import { TilingWindowManager as Twm } from './tilingWindowManager.js';
+import { LayoutPicker, LayoutPickerTileType } from './layoutPicker.js';
const [MajorShellVersion] = Util.getShellVersion();
@@ -108,6 +109,8 @@ export default class TilingMoveHandler {
this
);
handleWindowActionKeyConflict();
+
+ this._layoutPicker = new LayoutPicker();
}
destroy() {
@@ -119,6 +122,8 @@ export default class TilingMoveHandler {
this._tilePreview.destroy();
+ this._layoutPicker.destroy();
+
if (this._latestMonitorLockTimerId) {
GLib.Source.remove(this._latestMonitorLockTimerId);
this._latestMonitorLockTimerId = null;
@@ -126,7 +131,7 @@ export default class TilingMoveHandler {
if (this._latestPreviewTimerId) {
GLib.Source.remove(this._latestPreviewTimerId);
- this._latestPreviewTimerId = null;
+ this._latestPreviewTimerId = 0;
}
if (this._restoreSizeTimerId) {
@@ -158,6 +163,12 @@ export default class TilingMoveHandler {
if (window.is_skip_taskbar())
return;
+ // The picker is driven by the pointer, so it is useless for a
+ // keyboard-driven move.
+ if (grabOp !== Meta.GrabOp.KEYBOARD_MOVING &&
+ Settings.getBoolean('enable-layout-picker'))
+ this._layoutPicker.onMoveStarted();
+
// Also work with a window, which was maximized by GNOME natively
// because it may have been tiled with this extension before being
// maximized so we need to restore its size to pre-tiling.
@@ -172,6 +183,8 @@ export default class TilingMoveHandler {
// Try to restore the window size
if (window.tiledRect || this._wasMaximizedOnStart) {
+ this._layoutPicker.onMoveFinished();
+
let counter = 0;
this._restoreSizeTimerId && GLib.Source.remove(this._restoreSizeTimerId);
this._restoreSizeTimerId = GLib.timeout_add(GLib.PRIORITY_HIGH_IDLE, 10, () => {
@@ -334,6 +347,8 @@ export default class TilingMoveHandler {
const [x, y] = this.getDragCoords();
const currPointerPos = { x, y };
+ this._layoutPicker.onMoving(x, y, this._monitorNr);
+
if (lowPerfMode) {
if (!this._isGrabOp) {
this._movingTimerId = null;
@@ -527,12 +542,20 @@ export default class TilingMoveHandler {
const wRect = window.get_frame_rect();
const workArea = new Rect(window.get_work_area_for_monitor(this._monitorNr));
+ const layoutPickerTileType = this._layoutPicker.tileType;
+ const isPicking = layoutPickerTileType !== LayoutPickerTileType.NONE;
+
const vDetectionSize = Settings.getInt('vertical-preview-area');
- const pointerAtTopEdge = this._lastPointerPos.y <= workArea.y + vDetectionSize;
- const pointerAtBottomEdge = this._lastPointerPos.y >= workArea.y2 - vDetectionSize;
+ const pointerAtTopEdge = this._lastPointerPos.y <= workArea.y + vDetectionSize ||
+ layoutPickerTileType === LayoutPickerTileType.TOP ||
+ layoutPickerTileType === LayoutPickerTileType.MAXIMIZE;
+ const pointerAtBottomEdge = this._lastPointerPos.y >= workArea.y2 - vDetectionSize ||
+ layoutPickerTileType === LayoutPickerTileType.BOTTOM;
const hDetectionSize = Settings.getInt('horizontal-preview-area');
- const pointerAtLeftEdge = this._lastPointerPos.x <= workArea.x + hDetectionSize;
- const pointerAtRightEdge = this._lastPointerPos.x >= workArea.x2 - hDetectionSize;
+ const pointerAtLeftEdge = this._lastPointerPos.x <= workArea.x + hDetectionSize ||
+ layoutPickerTileType === LayoutPickerTileType.LEFT;
+ const pointerAtRightEdge = this._lastPointerPos.x >= workArea.x2 - hDetectionSize ||
+ layoutPickerTileType === LayoutPickerTileType.RIGHT;
// Also use window's pos for top and bottom area detection for quarters
// because global.get_pointer's y isn't accurate (no idea why...) when
// grabbing the titlebar & slowly going from the left/right sides to
@@ -540,10 +563,17 @@ export default class TilingMoveHandler {
const titleBarGrabbed = this._lastPointerPos.y - wRect.y < 50;
const windowAtTopEdge = titleBarGrabbed && wRect.y === workArea.y;
const windowAtBottomEdge = wRect.y >= workArea.y2 - 75;
- const tileTopLeftQuarter = pointerAtLeftEdge && (pointerAtTopEdge || windowAtTopEdge);
- const tileTopRightQuarter = pointerAtRightEdge && (pointerAtTopEdge || windowAtTopEdge);
- const tileBottomLeftQuarter = pointerAtLeftEdge && (pointerAtBottomEdge || windowAtBottomEdge);
- const tileBottomRightQuarter = pointerAtRightEdge && (pointerAtBottomEdge || windowAtBottomEdge);
+ // An explicit picker selection always takes precedence over the edge
+ // detection, otherwise a physically detected edge could override it
+ // (e.g. show a quarter while the picker selected a half).
+ const tileTopLeftQuarter = layoutPickerTileType === LayoutPickerTileType.Q2 ||
+ !isPicking && pointerAtLeftEdge && (pointerAtTopEdge || windowAtTopEdge);
+ const tileTopRightQuarter = layoutPickerTileType === LayoutPickerTileType.Q1 ||
+ !isPicking && pointerAtRightEdge && (pointerAtTopEdge || windowAtTopEdge);
+ const tileBottomLeftQuarter = layoutPickerTileType === LayoutPickerTileType.Q3 ||
+ !isPicking && pointerAtLeftEdge && (pointerAtBottomEdge || windowAtBottomEdge);
+ const tileBottomRightQuarter = layoutPickerTileType === LayoutPickerTileType.Q4 ||
+ !isPicking && pointerAtRightEdge && (pointerAtBottomEdge || windowAtBottomEdge);
if (tileTopLeftQuarter) {
this._tileRect = Twm.getTileFor('tile-topleft-quarter', workArea, this._monitorNr);
@@ -564,10 +594,15 @@ export default class TilingMoveHandler {
const shouldMaximize =
isLandscape && !Settings.getBoolean('enable-hold-maximize-inverse-landscape') ||
!isLandscape && !Settings.getBoolean('enable-hold-maximize-inverse-portrait');
- const tileRect = shouldMaximize
+ const onlyMaximize = !isPicking &&
+ Settings.getBoolean('enable-layout-picker');
+ const tileRect = onlyMaximize || shouldMaximize && !isPicking ||
+ layoutPickerTileType === LayoutPickerTileType.MAXIMIZE
? workArea
: Twm.getTileFor('tile-top-half', workArea, this._monitorNr);
- const holdTileRect = shouldMaximize
+ const holdTileRect = !onlyMaximize &&
+ (shouldMaximize && !isPicking ||
+ layoutPickerTileType === LayoutPickerTileType.TOP)
? Twm.getTileFor('tile-top-half', workArea, this._monitorNr)
: workArea;
// Dont open preview / start new timer if preview was already one for the top
@@ -579,6 +614,14 @@ export default class TilingMoveHandler {
this._tileRect = tileRect;
this._tilePreview.open(window, this._tileRect.meta, this._monitorNr);
+ if (tileRect.equal(holdTileRect)) {
+ if (this._latestPreviewTimerId) {
+ GLib.Source.remove(this._latestPreviewTimerId);
+ this._latestPreviewTimerId = 0;
+ }
+ return;
+ }
+
let timerId = 0;
this._latestPreviewTimerId && GLib.Source.remove(this._latestPreviewTimerId);
this._latestPreviewTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
@@ -592,7 +635,7 @@ export default class TilingMoveHandler {
this._tilePreview.open(window, this._tileRect.meta, this._monitorNr);
}
- this._latestPreviewTimerId = null;
+ this._latestPreviewTimerId = 0;
return GLib.SOURCE_REMOVE;
});
timerId = this._latestPreviewTimerId;
diff --git a/tiling-assistant@leleat-on-github/src/ui/prefs.ui b/tiling-assistant@leleat-on-github/src/ui/prefs.ui
index a9b5a6cd..627afa2b 100644
--- a/tiling-assistant@leleat-on-github/src/ui/prefs.ui
+++ b/tiling-assistant@leleat-on-github/src/ui/prefs.ui
@@ -40,6 +40,20 @@