Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "auto unstack" switch #1657

Open
wants to merge 9 commits into
base: master_jammy
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions schemas/org.gnome.shell.extensions.pop-shell.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@
<summary>Hide the outer gap when a tree contains only one window</summary>
</key>

<key type="b" name="auto-unstack">
<default>false</default>
<summary>Destroy stacks when separated by the mouse</summary>
</key>

<key type="b" name="snap-to-grid">
<default>false</default>
<summary>Snaps windows to the tiling grid on drop</summary>
Expand Down
4 changes: 2 additions & 2 deletions src/auto_tiler.ts
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is me trying to move the imports back to their rightful place

Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ export class AutoTiler {
}

/** Detaches the window from a tiling branch, if it is attached to one. */
detach_window(ext: Ext, win: Entity) {
detach_window(ext: Ext, win: Entity, destroy_stack: boolean = true) {
this.attached.take_with(win, (prev_fork: Entity) => {
const reflow_fork = this.forest.detach(ext, prev_fork, win);
const reflow_fork = this.forest.detach(ext, prev_fork, win, destroy_stack);

if (reflow_fork) {
const fork = reflow_fork[1];
Expand Down
24 changes: 18 additions & 6 deletions src/forest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export class Forest extends Ecs.World {
}

/** Detaches an entity from the a fork, re-arranging the fork's tree as necessary */
detach(ext: Ext, fork_entity: Entity, window: Entity): [Entity, Fork.Fork] | null {
detach(ext: Ext, fork_entity: Entity, window: Entity, destroy_stack: boolean = false): [Entity, Fork.Fork] | null {
const fork = this.forks.get(fork_entity);
if (!fork) return null;

Expand Down Expand Up @@ -387,8 +387,11 @@ export class Forest extends Ecs.World {
ext,
fork.left.inner as Node.NodeStack,
window,
() => {
if (fork.right) {
destroy_stack,
(window: undefined | Entity) => {
if (window)
fork.left = Node.Node.window(window);
else if (fork.right) {
fork.left = fork.right
fork.right = null
if (parent) {
Expand Down Expand Up @@ -428,9 +431,14 @@ export class Forest extends Ecs.World {
ext,
fork.right.inner as Node.NodeStack,
window,
() => {
fork.right = null
destroy_stack,
(window) => {
if (window)
fork.right = Node.Node.window(window);
else {
fork.right = null;
this.reassign_to_parent(fork, fork.left)
}
},
);
}
Expand Down Expand Up @@ -714,7 +722,7 @@ export class Forest extends Ecs.World {
}

/** Removes window from stack, destroying the stack if it was the last window. */
private remove_from_stack(ext: Ext, stack: Node.NodeStack, window: Entity, on_last: () => void) {
private remove_from_stack(ext: Ext, stack: Node.NodeStack, window: Entity, destroy_stack: boolean, on_last: (win?: Entity) => void) {
if (stack.entities.length === 1) {
this.stacks.remove(stack.idx)?.destroy();
on_last();
Expand All @@ -723,6 +731,10 @@ export class Forest extends Ecs.World {
if (s) {
Node.stack_remove(this, stack, window)
}
if (destroy_stack && stack.entities.length === 1) {
on_last(stack.entities[0])
this.stacks.remove(stack.idx)?.destroy()
}
}

const win = ext.windows.get(window);
Expand Down
72 changes: 44 additions & 28 deletions src/prefs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { }
export { };

const ExtensionUtils = imports.misc.extensionUtils;
// @ts-ignore
Expand All @@ -8,9 +8,9 @@ const { Gtk } = imports.gi;

const { Settings } = imports.gi.Gio;

import * as settings from 'settings';
import * as log from 'log';
import * as focus from 'focus';
import * as log from 'log';
import * as settings from 'settings';

interface AppWidgets {
fullscreen_launcher: any,
Expand All @@ -20,6 +20,7 @@ interface AppWidgets {
outer_gap: any,
show_skip_taskbar: any,
smart_gaps: any,
auto_unstack: any,
snap_to_grid: any,
window_titles: any,
mouse_cursor_focus_position: any,
Expand Down Expand Up @@ -52,6 +53,12 @@ function settings_dialog_new(): Gtk.Container {
Settings.sync();
})

app.auto_unstack.set_active(ext.auto_unstack());
app.auto_unstack.connect('state-set', (_widget: any, state: boolean) => {
ext.set_auto_unstack(state);
Settings.sync();
});

app.outer_gap.set_text(String(ext.gap_outer()));
app.outer_gap.connect('activate', (widget: any) => {
let parsed = parseInt((widget.get_text() as string).trim());
Expand All @@ -71,7 +78,7 @@ function settings_dialog_new(): Gtk.Container {
});

app.log_level.set_active(ext.log_level());
app.log_level.connect("changed", () => {
app.log_level.connect('changed', () => {
let active_id = app.log_level.get_active_id();
ext.set_log_level(active_id);
});
Expand All @@ -89,20 +96,20 @@ function settings_dialog_new(): Gtk.Container {
});

app.mouse_cursor_focus_position.set_active(ext.mouse_cursor_focus_location());
app.mouse_cursor_focus_position.connect("changed", () => {
app.mouse_cursor_focus_position.connect('changed', () => {
let active_id = app.mouse_cursor_focus_position.get_active_id();
ext.set_mouse_cursor_focus_location(active_id);
});

app.fullscreen_launcher.set_active(ext.fullscreen_launcher())
app.fullscreen_launcher.connect('state-set', (_widget: any, state: boolean) => {
ext.set_fullscreen_launcher(state)
app.fullscreen_launcher.set_active(ext.fullscreen_launcher());
app.fullscreen_launcher.connect('state-set',(_widget: any, state: boolean) => {
ext.set_fullscreen_launcher(state);
Settings.sync()
});

app.stacking_with_mouse.set_active(ext.stacking_with_mouse())
app.stacking_with_mouse.set_active(ext.stacking_with_mouse());
app.stacking_with_mouse.connect('state-set', (_widget: any, state: boolean) => {
ext.set_stacking_with_mouse(state)
ext.set_stacking_with_mouse(state);
Settings.sync()
});

Expand Down Expand Up @@ -135,6 +142,11 @@ function settings_dialog_view(): [AppWidgets, Gtk.Container] {
xalign: 0.0
})

const unstack_label = new Gtk.Label({
label: "Destroy stacks when separated by the mouse",
xalign: 0.0
})

const show_skip_taskbar_label = new Gtk.Label({
label: "Show Minimize to Tray Windows",
xalign: 0.0
Expand All @@ -155,31 +167,32 @@ function settings_dialog_view(): [AppWidgets, Gtk.Container] {
xalign: 0.0
})

const [inner_gap, outer_gap] = gaps_section(grid, 9);
const [inner_gap, outer_gap] = gaps_section(grid, 10);

const settings = {
inner_gap,
outer_gap,
fullscreen_launcher: new Gtk.Switch({ halign: Gtk.Align.END }),
stacking_with_mouse: new Gtk.Switch({ halign: Gtk.Align.END }),
smart_gaps: new Gtk.Switch({ halign: Gtk.Align.END }),
auto_unstack: new Gtk.Switch({ halign: Gtk.Align.END }),
snap_to_grid: new Gtk.Switch({ halign: Gtk.Align.END }),
window_titles: new Gtk.Switch({ halign: Gtk.Align.END }),
show_skip_taskbar: new Gtk.Switch({ halign: Gtk.Align.END }),
mouse_cursor_follows_active_window: new Gtk.Switch({ halign: Gtk.Align.END }),
mouse_cursor_focus_position: build_combo(
grid,
7,
8,
focus.FocusPosition,
'Mouse Cursor Focus Position',
"Mouse Cursor Focus Position"
),
log_level: build_combo(
grid,
8,
log.LOG_LEVELS,
'Log Level',
)
}
grid,
9,
log.LOG_LEVELS,
"Log Level"
),
};

grid.attach(win_label, 0, 0, 1, 1)
grid.attach(settings.window_titles, 1, 0, 1, 1)
Expand All @@ -190,19 +203,22 @@ function settings_dialog_view(): [AppWidgets, Gtk.Container] {
grid.attach(smart_label, 0, 2, 1, 1)
grid.attach(settings.smart_gaps, 1, 2, 1, 1)

grid.attach(fullscreen_launcher_label, 0, 3, 1, 1)
grid.attach(settings.fullscreen_launcher, 1, 3, 1, 1)
grid.attach(unstack_label, 0, 3, 1, 1)
grid.attach(settings.auto_unstack, 1, 3, 1, 1)

grid.attach(fullscreen_launcher_label, 0, 4, 1, 1)
grid.attach(settings.fullscreen_launcher, 1, 4, 1, 1)

grid.attach(stacking_with_mouse, 0, 4, 1, 1)
grid.attach(settings.stacking_with_mouse, 1, 4, 1, 1)
grid.attach(stacking_with_mouse, 0, 5, 1, 1)
grid.attach(settings.stacking_with_mouse, 1, 5, 1, 1)

grid.attach(show_skip_taskbar_label, 0, 5, 1, 1)
grid.attach(settings.show_skip_taskbar, 1, 5, 1, 1)
grid.attach(show_skip_taskbar_label, 0, 6, 1, 1)
grid.attach(settings.show_skip_taskbar, 1, 6, 1, 1)

grid.attach(mouse_cursor_follows_active_window_label, 0, 6, 1, 1)
grid.attach(settings.mouse_cursor_follows_active_window, 1, 6, 1, 1)
grid.attach(mouse_cursor_follows_active_window_label, 0, 7, 1, 1)
grid.attach(settings.mouse_cursor_follows_active_window, 1, 7, 1, 1)

return [settings, grid]
return [settings, grid];
}

function gaps_section(grid: any, top: number): [any, any] {
Expand Down
47 changes: 28 additions & 19 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const Me = imports.misc.extensionUtils.getCurrentExtension();

const { Gio, Gdk } = imports.gi;

const DARK = ["dark", "adapta", "plata", "dracula"]
const DARK = ["dark", "adapta", "plata", "dracula"];

interface Settings extends GObject.Object {
get_boolean(key: string): boolean;
Expand All @@ -14,34 +14,34 @@ interface Settings extends GObject.Object {
get_string(key: string): string;
set_string(key: string, value: string): void;

bind(key: string, object: GObject.Object, property: string, flags: any): void
bind(key: string, object: GObject.Object, property: string, flags: any): void;
}

function settings_new_id(schema_id: string): Settings | null {
try {
return new Gio.Settings({ schema_id });
} catch (why) {
if (schema_id !== "org.gnome.shell.extensions.user-theme") {
global.log(`failed to get settings for ${schema_id}: ${why}`)
global.log(`failed to get settings for ${schema_id}: ${why}`);
}

return null
return null;
}
}

function settings_new_schema(schema: string): Settings {
const GioSSS = Gio.SettingsSchemaSource;
const schemaDir = Me.dir.get_child("schemas");

let schemaSource = schemaDir.query_exists(null) ?
GioSSS.new_from_directory(schemaDir.get_path(), GioSSS.get_default(), false) :
let schemaSource = schemaDir.query_exists(null)
? GioSSS.new_from_directory(schemaDir.get_path(), GioSSS.get_default(), false) :
GioSSS.get_default();

const schemaObj = schemaSource.lookup(schema, true);

if (!schemaObj) {
throw new Error("Schema " + schema + " could not be found for extension "
+ Me.metadata.uuid + ". Please check your installation.")
+ Me.metadata.uuid + ". Please check your installation.");
}

return new Gio.Settings({ settings_schema: schemaObj });
Expand All @@ -52,19 +52,20 @@ const ACTIVE_HINT_BORDER_RADIUS = "active-hint-border-radius";
const STACKING_WITH_MOUSE = "stacking-with-mouse";
const COLUMN_SIZE = "column-size";
const EDGE_TILING = "edge-tiling";
const FULLSCREEN_LAUNCHER = "fullscreen-launcher"
const FULLSCREEN_LAUNCHER = "fullscreen-launcher";
const GAP_INNER = "gap-inner";
const GAP_OUTER = "gap-outer";
const ROW_SIZE = "row-size";
const SHOW_TITLE = "show-title";
const SMART_GAPS = "smart-gaps";
const AUTO_UNSTACK = "auto-unstack";
const SNAP_TO_GRID = "snap-to-grid";
const TILE_BY_DEFAULT = "tile-by-default";
const HINT_COLOR_RGBA = "hint-color-rgba";
const DEFAULT_RGBA_COLOR = "rgba(251, 184, 108, 1)"; //pop-orange
const LOG_LEVEL = "log-level";
const SHOW_SKIPTASKBAR = "show-skip-taskbar";
const MOUSE_CURSOR_FOLLOWS_ACTIVE_WINDOW = "mouse-cursor-follows-active-window"
const MOUSE_CURSOR_FOLLOWS_ACTIVE_WINDOW = "mouse-cursor-follows-active-window";
const MOUSE_CURSOR_FOCUS_LOCATION = "mouse-cursor-focus-location";

export class ExtensionSettings {
Expand Down Expand Up @@ -96,7 +97,7 @@ export class ExtensionSettings {
}

fullscreen_launcher(): boolean {
return this.ext.get_boolean(FULLSCREEN_LAUNCHER)
return this.ext.get_boolean(FULLSCREEN_LAUNCHER);
}

gap_inner(): number {
Expand All @@ -120,19 +121,19 @@ export class ExtensionSettings {

theme(): string {
return this.shell
? this.shell.get_string("name")
: this.int
? this.int.get_string("gtk-theme")
: "Adwaita"
? this.shell.get_string("name")
: this.int
? this.int.get_string("gtk-theme")
: "Adwaita"
}

is_dark(): boolean {
const theme = this.theme().toLowerCase()
return DARK.some(dark => theme.includes(dark))
const theme = this.theme().toLowerCase();
return DARK.some(dark => theme.includes(dark));
}

is_high_contrast(): boolean {
return this.theme().toLowerCase() === "highcontrast"
return this.theme().toLowerCase() === "highcontrast";
}

row_size(): number {
Expand All @@ -147,6 +148,10 @@ export class ExtensionSettings {
return this.ext.get_boolean(SMART_GAPS);
}

auto_unstack(): boolean {
return this.ext.get_boolean(AUTO_UNSTACK);
}

snap_to_grid(): boolean {
return this.ext.get_boolean(SNAP_TO_GRID);
}
Expand Down Expand Up @@ -196,11 +201,11 @@ export class ExtensionSettings {
}

set_edge_tiling(enable: boolean) {
this.mutter?.set_boolean(EDGE_TILING, enable)
this.mutter?.set_boolean(EDGE_TILING, enable);
}

set_fullscreen_launcher(enable: boolean) {
this.ext.set_boolean(FULLSCREEN_LAUNCHER, enable)
this.ext.set_boolean(FULLSCREEN_LAUNCHER, enable);
}

set_gap_inner(gap: number) {
Expand Down Expand Up @@ -233,6 +238,10 @@ export class ExtensionSettings {
this.ext.set_boolean(SMART_GAPS, set);
}

set_auto_unstack(set: boolean) {
this.ext.set_boolean(AUTO_UNSTACK, set);
}

set_snap_to_grid(set: boolean) {
this.ext.set_boolean(SNAP_TO_GRID, set);
}
Expand Down
Loading