Skip to content
Merged
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
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "O-tiling",
"description": "Auto-tiling extension for GNOME Shell with 'Aura' focus border, customizable workspace overview styling, and theme consistency.",
"version-name": "2.8.6",
"version-name": "2.8.8",
"uuid": "o-tiling@oliwebd.github.com",
"settings-schema": "org.gnome.shell.extensions.o-tiling",
"shell-version": [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package",
"name": "o-tiling",
"version": "2.8.6",
"version": "2.8.8",
"description": "Auto-tiling extension for GNOME Shell with 'Aura' focus border, customizable workspace overview styling, and theme consistency.",
"author": "oliwebd",
"type": "module",
Expand Down
10 changes: 5 additions & 5 deletions src/engine/auto_tiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export class AutoTiler {
* - First tries to tile onto the focused window
* - Then tries to tile onto a monitor
*/
auto_tile(ext: Ext, win: ShellWindow, ignore_focus: boolean = false) {
auto_tile(ext: Ext, win: ShellWindow, _ignore_focus?: boolean) {
this.detach_window(ext, win.entity);
log.debug(`attach to workspace: finding largest window`);
this.attach_to_workspace(ext, win, ext.workspace_id(win));
Expand Down Expand Up @@ -520,6 +520,9 @@ export class AutoTiler {
if (fork_entity) {
this.detach_window(ext, focused.entity);
ext.add_tag(focused.entity, Tags.Floating);
} else if (!ext.contains_tag(focused.entity, Tags.Floating)) {
// Window is unmanaged — tile it on toggle
this.auto_tile(ext, focused, false);
}
}
}
Expand Down Expand Up @@ -656,10 +659,7 @@ export class AutoTiler {
return null;
}

private fetch_mode(ext: Ext, win: ShellWindow, ignore_focus: boolean = false): Result<ShellWindow, string> {
if (ignore_focus) {
return Err('ignoring focus');
}
private fetch_mode(ext: Ext, win: ShellWindow): Result<ShellWindow, string> {

const prev = ext.previously_focused(win);

Expand Down
17 changes: 1 addition & 16 deletions src/engine/forest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,22 +359,7 @@ export class Forest extends Ecs.World {
monitor: MonitorID,
): [Entity, Fork.Fork] {
const entity = this.create_entity();
let orient = area.width > area.height ? Lib.Orientation.HORIZONTAL : Lib.Orientation.VERTICAL;

const toplevel = this.find_toplevel([monitor, workspace]);
if (toplevel) {
let win_count = 0;
for (const node of this.iter(toplevel)) {
if (node.inner.kind === 2) {
win_count++;
} else if (node.inner.kind === 3) {
win_count += node.inner.entities.length;
}
}
if (win_count >= 3) {
orient = Lib.Orientation.VERTICAL;
}
}
const orient = area.width > area.height ? Lib.Orientation.HORIZONTAL : Lib.Orientation.VERTICAL;

const primary = Fork.get_primary_monitor_index() === monitor;
const fork = new Fork.Fork(entity, left, right, area, workspace, monitor, orient, primary);
Expand Down
20 changes: 2 additions & 18 deletions src/engine/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,7 @@ export class Stack {
const comp = this.tabs.length;
this.tabs.push(tab);
this.bind_hint_events(tab);
const _n = this.tabs.length;
if (_n > 0) {
this.change_tab_color(this.tabs[0]);
if (_n > 1) {
this.change_tab_color(this.tabs[1]);
this.change_tab_color(this.tabs[_n - 1]);
if (_n > 2) this.change_tab_color(this.tabs[_n - 2]);
}
}
for (const t of this.tabs) this.change_tab_color(t);
if (this.active_id !== -1 && this.tabs[this.active_id]) {
this.change_tab_color(this.tabs[this.active_id]);
}
Expand Down Expand Up @@ -526,15 +518,7 @@ export class Stack {
}

this.tabs.splice(idx, 1);
const _n = this.tabs.length;
if (_n > 0) {
this.change_tab_color(this.tabs[0]);
if (_n > 1) {
this.change_tab_color(this.tabs[1]);
this.change_tab_color(this.tabs[_n - 1]);
if (_n > 2) this.change_tab_color(this.tabs[_n - 2]);
}
}
for (const t of this.tabs) this.change_tab_color(t);
if (this.active_id !== -1 && this.tabs[this.active_id]) {
this.change_tab_color(this.tabs[this.active_id]);
}
Expand Down
5 changes: 3 additions & 2 deletions src/engine/tiling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ export function locate_monitor(
const from = win.meta.get_monitor();
const ref = win.meta.get_work_area_for_monitor(from) as any;
const mm = (global as any).backend.get_monitor_manager();
const n_monitors = mm ? mm.get_logical_monitors().length : 1;
const monitors = mm ? mm.get_logical_monitors() : [];

const { UP, DOWN, LEFT } = Meta.DisplayDirection;

Expand All @@ -871,7 +871,8 @@ export function locate_monitor(

let next: [number, number, Rectangular] | null = null;

for (let mon = 0; mon < n_monitors; mon += 1) {
for (const lm of monitors) {
const mon = lm.get_number();
if (mon === from) continue;

const work_area = win.meta.get_work_area_for_monitor(mon);
Expand Down
32 changes: 26 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@ export class Ext extends Ecs.System<ExtEvent> {
for (const window of this.windows.values()) {
if (window.workspace_id() === id && window.is_tilable(this)) {
if (!this.auto_tiler.attached.contains(window.entity)) {
this.auto_tiler.auto_tile(this, window, true);
this.auto_tiler.auto_tile(this, window);
}
}
}
Expand Down Expand Up @@ -1292,8 +1292,8 @@ export class Ext extends Ecs.System<ExtEvent> {
}

// Update the active tab in the stack.
if (null !== this.auto_tiler && null !== win.stack) {
ext?.auto_tiler?.forest.stacks.get(win.stack)?.activate(win.entity);
if (this.auto_tiler !== null && win.stack !== null) {
this.auto_tiler.forest.stacks.get(win.stack)?.activate(win.entity);
}

this.unmaximize_workspace(win);
Expand Down Expand Up @@ -1380,9 +1380,22 @@ export class Ext extends Ecs.System<ExtEvent> {
}
this._bordered_entity = this.focus_window()?.entity ?? null;
} else {
this.hide_all_borders();
const focus = this.focus_window();
if (focus && focus.same_workspace()) {

// When the mouse enters a panel icon or menu, GNOME temporarily sets
// focus to null. Calling hide_all_borders() then show_border() in that
// short cycle produces a visible flash. Bail out here — the border stays
// put until a real window focus change occurs.
if (!focus) return;

// Hide only the border of the window that is LOSING focus, not all borders.
if (this._bordered_entity !== null && !Ecs.entity_eq(this._bordered_entity, focus.entity)) {
const prev = this.windows.get(this._bordered_entity);
prev?.hide_border();
}
this._bordered_entity = null;

if (focus.same_workspace()) {
focus.show_border();
this._bordered_entity = focus.entity;
}
Expand Down Expand Up @@ -2336,7 +2349,9 @@ export class Ext extends Ecs.System<ExtEvent> {
// Disconnect all signals for the removed workspace
const to_delete = [];
for (const [ws, signals] of this.workspace_signals) {
if (ws.index() === -1) { // -1 means it's being/has been removed
let idx = -1;
try { idx = ws.index(); } catch (_) { idx = -1; }
if (idx === -1) { // -1 means it's being/has been removed
for (const signal of signals) {
try { ws.disconnect(signal); } catch (_) { }
}
Expand Down Expand Up @@ -2933,7 +2948,11 @@ export class Ext extends Ecs.System<ExtEvent> {
if (indicator) {
indicator.toggle_tiled.setToggleState(false);
if (indicator.toggle_tiled.updateIcon) indicator.toggle_tiled.updateIcon(false);
indicator.toggle_workspace_tiled?.setToggleState(false);
}

this.prev_focused = [null, null];
this.workspace_active.clear();
}

/**
Expand Down Expand Up @@ -2999,6 +3018,7 @@ export class Ext extends Ecs.System<ExtEvent> {
// The toggle_tiled switch reflects overall "extension enabled" state
indicator.toggle_tiled.setToggleState(true); // extension is ON now
if (indicator.toggle_tiled.updateIcon) indicator.toggle_tiled.updateIcon(true);
indicator.update_workspace_tiling_state();
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/system/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,7 @@ export const SKIPTASKBAR_EXCEPTIONS: Array<WindowRule> = [
{ class: 'plank' },
];

export interface FloatRule {
class?: string;
title?: string;
}


/** Compiled rule with pre-built RegExp for hot-path matching. */
interface CompiledRule {
Expand Down
65 changes: 45 additions & 20 deletions src/system/window_buttons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { ExtensionSettings } from './settings.js';
export class WindowButtonsManager {
private _settings: ExtensionSettings;
private _signalIds: number[] = [];
private _originalLayout: string | null = null; // ← save original

constructor(settings: ExtensionSettings) {
this._settings = settings;
Expand All @@ -16,11 +17,19 @@ export class WindowButtonsManager {
* Enables the manager and connects settings signals.
*/
enable() {
// Save the layout BEFORE we touch it (only once)
const wm = this._settings.wm;
if (wm && this._originalLayout === null) {
this._originalLayout = wm.get_string('button-layout');
}

this._signalIds.push(
this._settings.ext.connect('changed::show-minimize-maximize-buttons', () => this.sync()),
this._settings.ext.connect('changed::show-close-button', () => this.sync())
);
this.sync();

// ↓ Do NOT call sync() here — let the user's existing layout stand.
// sync() only fires when the extension's own settings change.
}

/**
Expand All @@ -31,6 +40,12 @@ export class WindowButtonsManager {
this._settings.ext.disconnect(id);
}
this._signalIds = [];

// Restore the original layout when the extension is disabled
if (this._originalLayout !== null && this._settings.wm) {
this._settings.wm.set_string('button-layout', this._originalLayout);
this._originalLayout = null;
}
}

/**
Expand All @@ -43,32 +58,42 @@ export class WindowButtonsManager {
const show_min_max = this._settings.show_minimize_maximize_buttons();
const show_close = this._settings.show_close_button();

let layout = wm.get_string('button-layout');
let [left, right] = layout.split(":");
const isRight = right ? true : false;
const layout = wm.get_string('button-layout');
const [left, right] = layout.split(':');

const BTN = ['maximize', 'minimize', 'close'];

// ↓ FIXED: check whether buttons are currently on the LEFT, not just
// whether the right side has *any* content (e.g. "appmenu").
const leftHasButtons = (left ?? '').split(',').some(s => BTN.includes(s.trim()));
const rightHasButtons = (right ?? '').split(',').some(s => BTN.includes(s.trim()));

// If buttons are currently on the left, keep them left.
// If on the right (or not present yet), default to right.
const isRight = !leftHasButtons && !rightHasButtons
? true // no buttons anywhere yet → default right
: rightHasButtons; // honour current placement

// removes controls
const BTN = ["maximize", "minimize", "close"];
const BtnRight = (right ?? "").split(",").filter((s) => !BTN.includes(s));
const BtnLeft = (left ?? "").split(",").filter((s) => !BTN.includes(s));
const BtnRight = (right ?? '').split(',').filter(s => !BTN.includes(s.trim()));
const BtnLeft = (left ?? '').split(',').filter(s => !BTN.includes(s.trim()));

if (show_min_max) {
if (isRight) {
BtnRight.push("minimize", "maximize");
} else {
BtnLeft.splice(0, 0, "minimize", "maximize");
}
if (isRight) {
BtnRight.push('minimize', 'maximize');
} else {
BtnLeft.splice(0, 0, 'minimize', 'maximize');
}
}

if (show_close) {
if (isRight) {
BtnRight.push("close");
} else {
BtnLeft.splice(0, 0, "close");
}
if (isRight) {
BtnRight.push('close');
} else {
BtnLeft.splice(0, 0, 'close');
}
}

const new_layout = `${BtnLeft.join(",")}:${BtnRight.join(",")}`;
wm.set_string("button-layout", new_layout);
const new_layout = `${BtnLeft.join(',')}:${BtnRight.join(',')}`;
wm.set_string('button-layout', new_layout);
}
}
12 changes: 10 additions & 2 deletions src/ui/panel_transparency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ export class PanelTransparencyManager {
/* === O-Tiling: Panel Transparency === */

/* The main panel bar */
#panel {
#panel,
#panel.solid,
#panel:overview,
#panel.login-screen,
#panel.unlock-screen {
background-color: rgba(0, 0, 0, ${alpha}) !important;
background-image: none !important;
box-shadow: none !important;
Expand Down Expand Up @@ -146,7 +150,11 @@ export class PanelTransparencyManager {

/* Blur-style backdrop (only applied when blurStyle = true) */
${this._blurStyle ? `
#panel {
#panel,
#panel.solid,
#panel:overview,
#panel.login-screen,
#panel.unlock-screen {
background-image: ${bg} !important;
}
` : ''}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/workspace_switcher_style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function isGnome50(): boolean {
const major = parseInt(PACKAGE_VERSION.split('.')[0], 10);
return major >= 50;
} catch (_) {
return true;
return false;
}
}

Expand Down
Loading
Loading