From c5b9dd4a6f69e0211c4174c69bd9ac546d5f60d8 Mon Sep 17 00:00:00 2001 From: Alynx Zhou Date: Wed, 9 Apr 2025 17:12:36 +0800 Subject: [PATCH 1/3] feat: snap assistant position Allow putting snap assistant to bottom because GNOME has a top panel but has no bottom panel by default. https://github.com/domferr/tilingshell/issues/26 --- ...e.shell.extensions.tilingshell.gschema.xml | 7 ++- src/components/snapassist/snapAssist.ts | 49 ++++++++++++++++--- src/prefs.ts | 36 +++++++++++++- src/settings/settings.ts | 14 ++++++ 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/resources/schemas/org.gnome.shell.extensions.tilingshell.gschema.xml b/resources/schemas/org.gnome.shell.extensions.tilingshell.gschema.xml index 3a861fad..2815478f 100644 --- a/resources/schemas/org.gnome.shell.extensions.tilingshell.gschema.xml +++ b/resources/schemas/org.gnome.shell.extensions.tilingshell.gschema.xml @@ -24,9 +24,14 @@ true - Enable snap assist + Enable snap assistant Move the window on top of the screen to snap assist it. + + 0 + Position of snap assistant + Position of snap assistant, 0 is TOP, 1 is BOTTOM. + true Shows indicator diff --git a/src/components/snapassist/snapAssist.ts b/src/components/snapassist/snapAssist.ts index ceb2b385..7378627c 100644 --- a/src/components/snapassist/snapAssist.ts +++ b/src/components/snapassist/snapAssist.ts @@ -4,7 +4,7 @@ import SnapAssistTile from './snapAssistTile'; import SnapAssistLayout from './snapAssistLayout'; import Layout from '../layout/Layout'; import Tile from '../layout/Tile'; -import Settings from '@settings/settings'; +import Settings, { AssistantPosition } from '@settings/settings'; import GlobalState from '@utils/globalState'; import SignalHandling from '@utils/signalHandling'; import { @@ -37,6 +37,15 @@ class SnapAssistContent extends St.BoxLayout { GObject.ParamFlags.READWRITE, false, ), + snapAssistantPosition: GObject.ParamSpec.int( + 'snapAssistantPosition', + 'snapAssistantPosition', + 'Position of snap assistant', + GObject.ParamFlags.READWRITE, + 0, + 2, + 0 + ), snapAssistantThreshold: GObject.ParamSpec.uint( 'snapAssistantThreshold', 'snapAssistantThreshold', @@ -67,6 +76,7 @@ class SnapAssistContent extends St.BoxLayout { private _hoveredInfo: [SnapAssistTile, SnapAssistLayout] | undefined; private _padding: number; private _blur: boolean; + private _snapAssistantPosition: AssistantPosition; private _snapAssistantThreshold: number; private _snapAssistantAnimationTime: number; private _monitorIndex: number; @@ -88,6 +98,7 @@ class SnapAssistContent extends St.BoxLayout { this._showing = true; this._padding = 0; this._blur = false; + this._snapAssistantPosition = AssistantPosition.TOP; this._snapAssistantAnimationTime = 100; this._monitorIndex = monitorIndex; this._snapAssistantThreshold = @@ -99,6 +110,12 @@ class SnapAssistContent extends St.BoxLayout { 'blur', Gio.SettingsBindFlags.GET, ); + Settings.bind( + Settings.KEY_SNAP_ASSISTANT_POSITION, + this, + 'snapAssistantPosition', + Gio.SettingsBindFlags.GET, + ); Settings.bind( Settings.KEY_SNAP_ASSISTANT_THRESHOLD, this, @@ -143,6 +160,12 @@ class SnapAssistContent extends St.BoxLayout { this._applyStyle(); } + private set snapAssistantPosition(value: AssistantPosition) { + if (value < AssistantPosition.TOP || value > AssistantPosition.BOTTOM) + value = AssistantPosition.TOP; + this._snapAssistantPosition = value; + } + private set snapAssistantThreshold(value: number) { this._snapAssistantThreshold = value * getMonitorScalingFactor(this._monitorIndex); @@ -205,6 +228,15 @@ class SnapAssistContent extends St.BoxLayout { } private get _desiredY(): number { + if (this._snapAssistantPosition === AssistantPosition.BOTTOM) + return this._isEnlarged + ? Math.min( + this._container.height - this.height, + this._container.height - this._snapAssistantThreshold - + this.height / 2 - + this._padding, + ) + : this._container.height - this._padding; return this._isEnlarged ? Math.max( 0, @@ -316,10 +348,13 @@ class SnapAssistContent extends St.BoxLayout { } } - const height = - this.height + (this._isEnlarged ? 0 : this._snapAssistantThreshold); - const minY = this._container.y; - const maxY = this._container.y + this._desiredY + height; + const yThreshold = this._isEnlarged ? 0 : this._snapAssistantThreshold; + const minY = this._snapAssistantPosition === AssistantPosition.TOP + ? this._container.y + : this._container.y + this._desiredY - yThreshold; + const maxY = this._snapAssistantPosition === AssistantPosition.TOP + ? this._container.y + this._desiredY + this.height + yThreshold + : this._container.y + this._container.height; const minX = this._container.x + this.x - this._snapAssistantThreshold; const maxX = this._container.x + @@ -413,7 +448,6 @@ export default class SnapAssist extends St.Widget { super(); parent.add_child(this); this.workArea = workArea; - this.set_clip(0, 0, workArea.width, workArea.height); if (scalingFactor) enableScalingFactorSupport(this, scalingFactor); this._content = new SnapAssistContent(this, monitorIndex); @@ -421,7 +455,8 @@ export default class SnapAssist extends St.Widget { public set workArea(newWorkArea: Mtk.Rectangle) { this.set_position(newWorkArea.x, newWorkArea.y); - this.set_width(newWorkArea.width); + // To put snap assistant at bottom, let the container fill the area. + this.set_size(newWorkArea.width, newWorkArea.height); this.set_clip(0, 0, newWorkArea.width, newWorkArea.height); } diff --git a/src/prefs.ts b/src/prefs.ts index dfddce4c..336aa362 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,5 +1,5 @@ import { Gtk, Adw, Gio, GLib, Gdk, GObject } from '@gi.prefs'; -import Settings, { ActivationKey } from './settings/settings'; +import Settings, { ActivationKey, AssistantPosition } from './settings/settings'; import { logger } from './utils/logger'; import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; import Layout from '@components/layout/Layout'; @@ -179,6 +179,11 @@ export default class TilingShellExtensionPreferences extends ExtensionPreference Settings.KEY_SNAP_ASSIST, _('Enable Snap Assistant'), _('Move the window on top of the screen to snap assist it'), + this._buildAssistantPositionDropDown( + Settings.SNAP_ASSISTANT_POSITION, + (val: AssistantPosition) => + (Settings.SNAP_ASSISTANT_POSITION = val), + ), ); behaviourGroup.add(snapAssistRow); @@ -1062,6 +1067,35 @@ export default class TilingShellExtensionPreferences extends ExtensionPreference return dropdown; } + _buildAssistantPositionDropDown( + initialValue: AssistantPosition, + onChange: (_: AssistantPosition) => void, + styleClass?: string, + ) { + const options = new Gtk.StringList(); + const assistantPositions = [ + AssistantPosition.TOP, + AssistantPosition.BOTTOM, + ]; + assistantPositions.forEach((p) => options.append(AssistantPosition[p])); + const dropdown = new Gtk.DropDown({ + model: options, + selected: initialValue, + }); + dropdown.connect('notify::selected-item', (dd: Gtk.DropDown) => { + const index = dd.get_selected(); + const selected = + index < 0 || index >= assistantPositions.length + ? AssistantPostion.TOP + : assistantPositions[index]; + onChange(selected); + }); + if (styleClass) dropdown.add_css_class(styleClass); + dropdown.set_vexpand(false); + dropdown.set_valign(Gtk.Align.CENTER); + return dropdown; + } + _buildLinkButton(label: string, uri: string): Gtk.Button { const btn = new Gtk.Button({ label, diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 6dd81553..898ac486 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -9,6 +9,11 @@ export enum ActivationKey { SUPER, } +export enum AssistantPosition { + TOP = 0, + BOTTOM, +} + /** ------------- Utility functions ------------- */ function get_string(key: string): string { return ( @@ -99,6 +104,7 @@ export default class Settings { static KEY_ACTIVE_SCREEN_EDGES = 'active-screen-edges'; static KEY_TOP_EDGE_MAXIMIZE = 'top-edge-maximize'; static KEY_OVERRIDE_WINDOW_MENU = 'override-window-menu'; + static KEY_SNAP_ASSISTANT_POSITION = 'snap-assistant-position'; static KEY_SNAP_ASSISTANT_THRESHOLD = 'snap-assistant-threshold'; static KEY_ENABLE_WINDOW_BORDER = 'enable-window-border'; static KEY_INNER_GAPS = 'inner-gaps'; @@ -342,6 +348,14 @@ export default class Settings { set_boolean(Settings.KEY_OVERRIDE_WINDOW_MENU, val); } + static get SNAP_ASSISTANT_POSITION(): AssistantPosition { + return get_number(Settings.KEY_SNAP_ASSISTANT_POSITION); + } + + static set SNAP_ASSISTANT_POSITION(val: AssistantPosition) { + set_number(Settings.KEY_SNAP_ASSISTANT_POSITION, val); + } + static get SNAP_ASSISTANT_THRESHOLD(): number { return get_number(Settings.KEY_SNAP_ASSISTANT_THRESHOLD); } From 310a2488f9900f59c550ac1a6764181cb2953df0 Mon Sep 17 00:00:00 2001 From: Alynx Zhou Date: Thu, 10 Apr 2025 11:46:53 +0800 Subject: [PATCH 2/3] fix: reduce edge tiling offset on bottom 16px makes user unable to trigger snap assistant on bottom because snap assistant happens to be 16px when it is not enlarged. --- src/components/tilingsystem/edgeTilingManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/tilingsystem/edgeTilingManager.ts b/src/components/tilingsystem/edgeTilingManager.ts index 71eb8053..3295ad25 100644 --- a/src/components/tilingsystem/edgeTilingManager.ts +++ b/src/components/tilingsystem/edgeTilingManager.ts @@ -8,7 +8,7 @@ import Settings from '@settings/settings'; import { registerGObjectClass } from '@utils/gjs'; const EDGE_TILING_OFFSET = 16; -const TOP_EDGE_TILING_OFFSET = 8; +const VERTICAL_EDGE_TILING_OFFSET = 8; const QUARTER_PERCENTAGE = 0.5; @registerGObjectClass @@ -124,11 +124,11 @@ export default class EdgeTilingManager extends GObject.Object { }): boolean { return ( pointerPos.x <= this._workArea.x + EDGE_TILING_OFFSET || - pointerPos.y <= this._workArea.y + TOP_EDGE_TILING_OFFSET || + pointerPos.y <= this._workArea.y + VERTICAL_EDGE_TILING_OFFSET || pointerPos.x >= this._workArea.x + this._workArea.width - EDGE_TILING_OFFSET || pointerPos.y >= - this._workArea.y + this._workArea.height - EDGE_TILING_OFFSET + this._workArea.y + this._workArea.height - VERTICAL_EDGE_TILING_OFFSET ); } From 88ceef6d692224b7ccfb041de587146d4d115c85 Mon Sep 17 00:00:00 2001 From: Alynx Zhou Date: Thu, 10 Apr 2025 11:55:12 +0800 Subject: [PATCH 3/3] fix: give snap assistant higher priority than edge tiling Otherwise user must keep very precisious movement because if they reach edges snap assistant will be closed and edge tiling is started. They are not conflict because typically user still has a large area to trigger edge tiling except snap assistant. --- src/components/tilingsystem/tilingManager.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/tilingsystem/tilingManager.ts b/src/components/tilingsystem/tilingManager.ts index dc1f3f13..92b01b27 100644 --- a/src/components/tilingsystem/tilingManager.ts +++ b/src/components/tilingsystem/tilingManager.ts @@ -695,6 +695,14 @@ export class TilingManager { this._selectedTilesPreview.close(true); } + if (Settings.SNAP_ASSIST) { + this._snapAssist.onMovingWindow( + window, + true, + currPointerPos, + ); + } + if ( Settings.ACTIVE_SCREEN_EDGES && !this._snapAssistingInfo.isSnapAssisting && @@ -704,20 +712,11 @@ export class TilingManager { this._edgeTilingManager.startEdgeTiling(currPointerPos); if (changed) this._showEdgeTiling(window, rect, x, y, tilingLayout); - this._snapAssist.close(true); } else { if (this._edgeTilingManager.isPerformingEdgeTiling()) { this._selectedTilesPreview.close(true); this._edgeTilingManager.abortEdgeTiling(); } - - if (Settings.SNAP_ASSIST) { - this._snapAssist.onMovingWindow( - window, - true, - currPointerPos, - ); - } } return GLib.SOURCE_CONTINUE;