diff --git a/README.md b/README.md index a8d76ebd..8242b201 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ Use the branch corresponding to your GNOME Shell version (`git checkout branch_n - **GNOME 42 through 44:** Use the `master_jammy` branch. - **GNOME 45:** Use the `master_mantic` branch. - **GNOME 46+:** Use the `master_noble` branch. +- **GNOME 49+:** Use the `master_resolute` branch. GNU Make and TypeScript are also required to build the project. diff --git a/src/auto_tiler.ts b/src/auto_tiler.ts index 2a5cc51f..848847fa 100644 --- a/src/auto_tiler.ts +++ b/src/auto_tiler.ts @@ -29,6 +29,43 @@ export class AutoTiler { this.attached = attached; } + private sync_smart_gapped(ext: Ext, fork: Fork) { + let toplevel = fork; + let parent = this.forest.parents.get(toplevel.entity); + + /* Walk upwards to get the actual top-level root from the fork. */ + while (parent) { + const next = this.forest.forks.get(parent); + if (!next) + break; + + toplevel = next; + parent = this.forest.parents.get(toplevel.entity); + } + + const smart_gapped = ( + toplevel.is_toplevel && + toplevel.smart_gapped && + toplevel.right === null + ); + + /* Set smart_gapped for each window. */ + for (const branch of this.forest.iter(toplevel.entity)) { + let entities: Entity[] = []; + + if (branch.inner.kind === NodeKind.WINDOW) + entities = [branch.inner.entity]; + else if (branch.inner.kind === NodeKind.STACK) + entities = branch.inner.entities; + + for (const entity of entities) { + const win = ext.windows.get(entity); + if (win) + win.smart_gapped = smart_gapped; + } + } + } + /** Swap window associations in the auto-tiler * * Call this when a window has swapped positions with another, so that we @@ -106,13 +143,6 @@ export class AutoTiler { rect.height -= ext.gap_outer * 2; } - if (fork.left.inner.kind === 2) { - const win = ext.windows.get(fork.left.inner.entity); - if (win) { - win.smart_gapped = fork.smart_gapped; - } - } - fork.area = fork.set_area(rect.clone()); fork.length_left = Math.round(fork.prev_ratio * fork.length()); this.tile(ext, fork, fork.area); @@ -132,7 +162,6 @@ export class AutoTiler { const [entity, fork] = this.forest.create_toplevel(win.entity, rect.clone(), workspace_id); this.forest.on_attach(entity, win.entity); fork.smart_gapped = smart_gaps; - win.smart_gapped = smart_gaps; this.tile(ext, fork, rect); } @@ -490,6 +519,7 @@ export class AutoTiler { } tile(ext: Ext, fork: Fork, area: Rectangle) { + this.sync_smart_gapped(ext, fork); this.forest.tile(ext, fork, area); } diff --git a/src/extension.ts b/src/extension.ts index 503407f3..ed3c9664 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -909,10 +909,10 @@ export class Ext extends Ecs.System { ) { if (prev.rect().contains(win.rect())) { if (prev.is_maximized()) { - prev.meta.unmaximize(Meta.MaximizeFlags.BOTH); + prev.meta.unmaximize(); } } else if (prev.stack) { - prev.meta.unmaximize(Meta.MaximizeFlags.BOTH); + prev.meta.unmaximize(); this.auto_tiler.forest.stacks.get(prev.stack)?.restack(); } } @@ -1034,7 +1034,7 @@ export class Ext extends Ecs.System { compare.is_maximized() && win.entity[0] !== compare.entity[0] ) { - compare.meta.unmaximize(Meta.MaximizeFlags.BOTH); + compare.meta.unmaximize(); } } } @@ -1257,10 +1257,8 @@ export class Ext extends Ecs.System { } if (this.auto_tiler) { - if (this.is_floating(win)) { - win.meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL); - win.meta.unmaximize(Meta.MaximizeFlags.VERTICAL); - win.meta.unmaximize(Meta.MaximizeFlags.BOTH); + if (this.is_floating(win) && win.is_maximized()) { + win.meta.unmaximize(); } this.register(Events.window_move(this, win, rect)); @@ -1268,7 +1266,7 @@ export class Ext extends Ecs.System { win.move(this, rect, () => { }); // if the resulting dimensions of rect == next if (rect.width == next_area.width && rect.height == next_area.height) { - win.meta.maximize(Meta.MaximizeFlags.BOTH); + win.meta.maximize(); } } } @@ -2715,6 +2713,7 @@ export default class PopShellExtension extends Extension { } ext.keybindings.enable(ext.keybindings.global).enable(ext.keybindings.window_focus); + ext.tiler.enable_keybindings(ext); if (ext.settings.tile_by_default()) { ext.auto_tile_on(); @@ -2740,6 +2739,7 @@ export default class PopShellExtension extends Extension { layoutManager.removeChrome(ext.overlay); ext.keybindings.disable(ext.keybindings.global).disable(ext.keybindings.window_focus); + ext.tiler.disable_keybindings(ext); if (ext.auto_tiler) { ext.auto_tiler.destroy(ext); diff --git a/src/keybindings.ts b/src/keybindings.ts index 394ea4e6..69cb9752 100644 --- a/src/keybindings.ts +++ b/src/keybindings.ts @@ -8,6 +8,8 @@ export class Keybindings { global: Object; window_focus: Object; + private active: Set = new Set(); + private ext: Ext; constructor(ext: Ext) { @@ -62,15 +64,22 @@ export class Keybindings { }; } - enable(keybindings: any) { + enable(keybindings: any, modes: number = Shell.ActionMode.NORMAL) { for (const name in keybindings) { + if (this.active.has(name)) { + wm.allowKeybinding(name, modes); + continue; + } + wm.addKeybinding( name, this.ext.settings.ext, Meta.KeyBindingFlags.NONE, - Shell.ActionMode.NORMAL, + modes, keybindings[name], ); + + this.active.add(name); } return this; @@ -78,7 +87,20 @@ export class Keybindings { disable(keybindings: Object) { for (const name in keybindings) { + if (!this.active.has(name)) + continue; + wm.removeKeybinding(name); + this.active.delete(name); + } + + return this; + } + + allow(keybindings: Object, modes: number) { + for (const name in keybindings) { + if (this.active.has(name)) + wm.allowKeybinding(name, modes); } return this; diff --git a/src/mod.d.ts b/src/mod.d.ts index e1ac012f..9b039dad 100644 --- a/src/mod.d.ts +++ b/src/mod.d.ts @@ -306,13 +306,15 @@ declare namespace Meta { is_skip_taskbar(): boolean; make_above(): void; make_fullscreen(): void; - maximize(flags: MaximizeFlags): void; + maximize(): void; + unmaximize(): void; + unminimize(): void; + set_maximize_flags(flags: MaximizeFlags): void; + set_unmaximize_flags(flags: MaximizeFlags): void; move_frame(user_op: boolean, x: number, y: number): void; move_resize_frame(user_op: boolean, x: number, y: number, w: number, h: number): boolean; raise(): void; skip_taskbar: boolean; - unmaximize(flags: any): void; - unminimize(): void; } interface WindowActor extends Clutter.Actor { diff --git a/src/stack.ts b/src/stack.ts index 60d553bb..5667d3e3 100644 --- a/src/stack.ts +++ b/src/stack.ts @@ -91,8 +91,14 @@ const TabButton = GObject.registerClass( }), ); - close_button.connect('clicked', () => { - window.meta.delete(global.get_current_time()); + this._running_close = false; + + close_button.connect('clicked', () => { + if (!this._running_close) { + this._running_close = true; + window.meta.delete(global.get_current_time()); + this._running_close = false; + } }); close_button.set_x_align(Clutter.ActorAlign.END); diff --git a/src/tiling.ts b/src/tiling.ts index 8ea72562..58f1dd0d 100644 --- a/src/tiling.ts +++ b/src/tiling.ts @@ -17,6 +17,7 @@ import { AutoTiler } from './auto_tiler.js'; import { Fork } from './fork.js'; import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; const { ShellWindow } = window; @@ -60,6 +61,14 @@ export class Tiler { }; } + enable_keybindings(ext: Ext) { + ext.keybindings.enable(this.keybindings, Shell.ActionMode.NONE); + } + + disable_keybindings(ext: Ext) { + ext.keybindings.disable(this.keybindings); + } + toggle_orientation(ext: Ext) { const window = ext.focus_window(); if (window && ext.auto_tiler) { @@ -749,7 +758,7 @@ export class Tiler { this.window = win.entity; if (win.is_maximized()) { - win.meta.unmaximize(Meta.MaximizeFlags.BOTH); + win.meta.unmaximize(); } // Set overlay to match window @@ -763,7 +772,9 @@ export class Tiler { }); } - ext.keybindings.disable(ext.keybindings.window_focus).enable(this.keybindings); + ext.keybindings + .allow(ext.keybindings.window_focus, Shell.ActionMode.NONE) + .allow(this.keybindings, Shell.ActionMode.NORMAL); } } @@ -817,7 +828,9 @@ export class Tiler { ext.overlay.visible = false; // Disable tiling keybindings - ext.keybindings.disable(this.keybindings).enable(ext.keybindings.window_focus); + ext.keybindings + .allow(this.keybindings, Shell.ActionMode.NONE) + .allow(ext.keybindings.window_focus, Shell.ActionMode.NORMAL); } } diff --git a/src/window.ts b/src/window.ts index 2fbd2928..0adf2d5b 100644 --- a/src/window.ts +++ b/src/window.ts @@ -373,7 +373,7 @@ export class ShellWindow { if (actor) { if (this.is_maximized()) { - meta.unmaximize(Meta.MaximizeFlags.BOTH); + meta.unmaximize(); } actor.remove_all_transitions();