diff --git a/.gitignore b/.gitignore index e67238b4..8a8458bc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,4 @@ debian/* !debian/*install .confirm_shortcut_change .vscode -node_modules -tsconfig.tsbuildinfo \ No newline at end of file +node_modules \ No newline at end of file diff --git a/Makefile b/Makefile index bffe3752..d83c5946 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ sources = src/*.ts *.css all: depcheck compile clean: - rm -rf _build target tsconfig.tsbuildinfo + rm -rf _build target # Configure local settings on system configure: diff --git a/metadata.json b/metadata.json index 84a0c5ed..51d2b7ac 100644 --- a/metadata.json +++ b/metadata.json @@ -5,11 +5,8 @@ "uuid": "pop-shell@system76.com", "settings-schema": "org.gnome.shell.extensions.pop-shell", "shell-version": [ - "45", "46", "47", - "48", - "49", - "50" + "48" ] } diff --git a/src/auto_tiler.ts b/src/auto_tiler.ts index 2a5cc51f..b9ffa33b 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); } @@ -700,7 +730,7 @@ export class AutoTiler { } private toggle_orientation_(ext: Ext, focused: ShellWindow): Result { - if (focused.is_maximized()) { + if (focused.meta.get_maximized()) { return Err('cannot toggle maximized window'); } diff --git a/src/color_dialog/tsconfig.json b/src/color_dialog/tsconfig.json index 6e61c341..b08b2ac7 100644 --- a/src/color_dialog/tsconfig.json +++ b/src/color_dialog/tsconfig.json @@ -4,8 +4,8 @@ "target": "es2015", "strict": true, "outDir": "../../target/color_dialog", - "rootDir": "./src", "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "lib": ["es2015"], "pretty": true, "removeComments": true, diff --git a/src/config.ts b/src/config.ts index 440d4ec0..29ba761c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -59,6 +59,8 @@ export const DEFAULT_FLOAT_RULES: Array = [ { class: 'zoom' }, { class: '^.*action=join.*$' }, { class: 'gjs' }, + { class: 'io.github.bugaevc.wl-clipboard' }, + { class: 'org.gnome.NautilusPreviewer' }, ]; export interface WindowRule { @@ -77,6 +79,8 @@ export const SKIPTASKBAR_EXCEPTIONS: Array = [ { class: 'Guake' }, { class: 'Com.github.amezin.ddterm' }, { class: 'plank' }, + { class: 'io.github.bugaevc.wl-clipboard' }, + { class: 'org.gnome.NautilusPreviewer' }, ]; export interface FloatRule { diff --git a/src/extension.ts b/src/extension.ts index 503407f3..442ee8c9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,13 +44,6 @@ import Gio from 'gi://Gio'; import St from 'gi://St'; import Shell from 'gi://Shell'; import Meta from 'gi://Meta'; -// Try to import Mtk for newer GNOME versions, fallback to Meta for older versions -let Mtk: any; -try { - Mtk = imports.gi.Mtk; -} catch (e) { - Mtk = null; -} const { GlobalEvent, WindowEvent } = Events; const { cursor_rect, is_keyboard_op, is_resize_op, is_move_op } = Lib; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; @@ -487,7 +480,7 @@ export class Ext extends Ecs.System { if (old) { try { GLib.source_remove(old); - } catch (_) { } + } catch (_) {} } const new_s = GLib.timeout_add(GLib.PRIORITY_LOW, 500, () => { @@ -1265,7 +1258,7 @@ export class Ext extends Ecs.System { this.register(Events.window_move(this, win, rect)); } else { - win.move(this, rect, () => { }); + 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); @@ -1488,7 +1481,7 @@ export class Ext extends Ecs.System { [area, monitor_attachment] = (win.stack === null && attach_to.stack === null && is_sibling) || - (win.stack === null && is_sibling) + (win.stack === null && is_sibling) ? [fork.area, false] : [attach_to.meta.get_frame_rect(), false]; } else { @@ -1519,8 +1512,8 @@ export class Ext extends Ecs.System { ? [area.x, area.y, half_width, area.height] : [area.x + half_width, area.y, half_width, area.height] : swap - ? [area.x, area.y, area.width, half_height] - : [area.x, area.y + half_height, area.width, half_height]; + ? [area.x, area.y, area.width, half_height] + : [area.x, area.y + half_height, area.width, half_height]; this.overlay.x = new_area[0]; this.overlay.y = new_area[1]; @@ -1549,8 +1542,8 @@ export class Ext extends Ecs.System { this.current_style = this.settings.is_dark() ? Style.Dark : this.settings.is_high_contrast() - ? Style.HighContrast - : Style.Light; + ? Style.HighContrast + : Style.Light; } /** Handle window maximization notifications */ @@ -2299,7 +2292,7 @@ export class Ext extends Ecs.System { this.moved_by_mouse = false; } - update_display_configuration_before() { } + update_display_configuration_before() {} update_display_configuration(workareas_only: boolean) { if (!this.auto_tiler || sessionMode.isLocked) return; @@ -2630,10 +2623,7 @@ export class Ext extends Ecs.System { cursor_status(): [Rectangle, number] { const cursor = cursor_rect(); - // Use Mtk.Rectangle if available (newer GNOME), otherwise fallback to Meta.Rectangle - const rect = Mtk ? - new Mtk.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }) : - new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); + const rect = new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); const monitor = display.get_monitor_index_for_rect(rect); return [cursor, monitor]; } @@ -2675,12 +2665,12 @@ let ext: Ext | null = null; let indicator: Indicator | null = null; declare global { - var popShellExtension: any; + var popShellExtension: any; } export default class PopShellExtension extends Extension { enable() { - globalThis.popShellExtension = this; + globalThis.popShellExtension = this; log.info('enable'); if (!ext) { @@ -2715,6 +2705,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 +2731,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/floating_exceptions/src/main.ts b/src/floating_exceptions/src/main.ts index de8d3126..9da3b700 100644 --- a/src/floating_exceptions/src/main.ts +++ b/src/floating_exceptions/src/main.ts @@ -6,7 +6,7 @@ import Gtk from 'gi://Gtk?version=3.0'; import Pango from 'gi://Pango'; /** The directory that this script is executed from. */ -const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack!.split(':')[0].slice(1)); +const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack.split(':')[0].slice(1)); /** Add our directory so we can import modules from it. */ imports.searchPath.push(SCRIPT_DIR); diff --git a/src/floating_exceptions/tsconfig.json b/src/floating_exceptions/tsconfig.json index fb275f36..c3a76870 100644 --- a/src/floating_exceptions/tsconfig.json +++ b/src/floating_exceptions/tsconfig.json @@ -4,8 +4,8 @@ "target": "es2015", // "strict": true, "outDir": "../../target/floating_exceptions", - "rootDir": "./src", "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "lib": ["es2015"], "pretty": true, "removeComments": true, 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..bb840e58 100644 --- a/src/mod.d.ts +++ b/src/mod.d.ts @@ -275,8 +275,6 @@ declare namespace Meta { minimized: Readonly; window_type: Readonly; decorated: Readonly; - maximized_horizontally: Readonly; - maximized_vertically: Readonly; activate(time: number): void; change_workspace_by_index(workspace: number, append: boolean): void; @@ -286,6 +284,7 @@ declare namespace Meta { get_display(): Meta.Display | null; get_description(): string; get_frame_rect(): Rectangular; + get_maximized(): number; get_monitor(): number; get_pid(): number; get_role(): null | string; 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..7577f435 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) { @@ -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/utils.ts b/src/utils.ts index 8f335c09..e89a15c6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,11 +10,7 @@ const { Ok, Err } = result; const { Error } = error; export function is_wayland(): boolean { - if (typeof Meta.is_wayland_compositor === 'function') { - return Meta.is_wayland_compositor(); - } - // GNOME 50+ removed X11 support; always Wayland - return true; + return Meta.is_wayland_compositor(); } export function block_signal(object: GObject.Object, signal: SignalID) { diff --git a/src/window.ts b/src/window.ts index 2fbd2928..5be8ae5e 100644 --- a/src/window.ts +++ b/src/window.ts @@ -276,7 +276,7 @@ export class ShellWindow { } is_maximized(): boolean { - return this.meta.maximized_horizontally || this.meta.maximized_vertically; + return this.meta.get_maximized() !== 0; } /** @@ -300,7 +300,7 @@ export class ShellWindow { } is_snap_edge(): boolean { - return this.meta.maximized_vertically && !this.meta.maximized_horizontally; + return this.meta.get_maximized() == Meta.MaximizeFlags.VERTICAL; } is_tilable(ext: Ext): boolean { @@ -372,9 +372,9 @@ export class ShellWindow { const actor = meta.get_compositor_private(); if (actor) { - if (this.is_maximized()) { - meta.unmaximize(Meta.MaximizeFlags.BOTH); - } + meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL); + meta.unmaximize(Meta.MaximizeFlags.VERTICAL); + meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); actor.remove_all_transitions(); ext.movements.insert(this.entity, clone); diff --git a/tsconfig.json b/tsconfig.json index 16972668..82fa7c6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,8 +5,8 @@ "target": "es2020", "strict": true, "outDir": "./target", - "rootDir": "./src", "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "lib": [ "es2020" ],