feat: equal-sized tabs and constantly updated tab titles
This commit is contained in:
@@ -377,6 +377,13 @@ export default class WindowContainer {
|
|||||||
this._tabBar.show();
|
this._tabBar.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public entry point to refresh tab titles (e.g. when a window title changes).
|
||||||
|
*/
|
||||||
|
refreshTabTitles(): void {
|
||||||
|
this._updateTabBar();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update tab bar state (active highlight, titles) without a full rebuild.
|
* Update tab bar state (active highlight, titles) without a full rebuild.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ export default class Monitor {
|
|||||||
this._workspaces.push(new WindowContainer(this._workArea));
|
this._workspaces.push(new WindowContainer(this._workArea));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshTabTitlesForWindow(winWrap: WindowWrapper): void {
|
||||||
|
const wsId = winWrap.getWorkspace();
|
||||||
|
if (wsId >= 0 && wsId < this._workspaces.length) {
|
||||||
|
this._workspaces[wsId].refreshTabTitles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hideTabBars(): void {
|
hideTabBars(): void {
|
||||||
for (const container of this._workspaces) {
|
for (const container of this._workspaces) {
|
||||||
container.hideTabBar();
|
container.hideTabBar();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Clutter from 'gi://Clutter';
|
import Clutter from 'gi://Clutter';
|
||||||
|
import Pango from 'gi://Pango';
|
||||||
import St from 'gi://St';
|
import St from 'gi://St';
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||||
import {Logger} from "../utils/logger.js";
|
import {Logger} from "../utils/logger.js";
|
||||||
@@ -25,6 +26,8 @@ export class TabBar {
|
|||||||
can_focus: false,
|
can_focus: false,
|
||||||
track_hover: false,
|
track_hover: false,
|
||||||
});
|
});
|
||||||
|
// Force all tabs to equal width regardless of text length
|
||||||
|
(this._bar.layout_manager as Clutter.BoxLayout).homogeneous = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,19 +39,22 @@ export class TabBar {
|
|||||||
this._buttons = [];
|
this._buttons = [];
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {
|
||||||
|
const label = new St.Label({
|
||||||
|
text: item.getTabLabel(),
|
||||||
|
style_class: 'aerospike-tab-label',
|
||||||
|
y_align: Clutter.ActorAlign.CENTER,
|
||||||
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
|
x_expand: true,
|
||||||
|
});
|
||||||
|
label.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
|
|
||||||
const button = new St.Button({
|
const button = new St.Button({
|
||||||
style_class: 'aerospike-tab',
|
style_class: 'aerospike-tab',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
can_focus: false,
|
can_focus: false,
|
||||||
track_hover: true,
|
track_hover: true,
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
child: new St.Label({
|
child: label,
|
||||||
text: item.getTabLabel(),
|
|
||||||
style_class: 'aerospike-tab-label',
|
|
||||||
y_align: Clutter.ActorAlign.CENTER,
|
|
||||||
x_align: Clutter.ActorAlign.CENTER,
|
|
||||||
x_expand: true,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
button.connect('clicked', () => {
|
button.connect('clicked', () => {
|
||||||
|
|||||||
@@ -48,7 +48,13 @@ export class WindowWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTabLabel(): string {
|
getTabLabel(): string {
|
||||||
const appName = this._window.get_wm_class() ?? '';
|
const rawAppName = this._window.get_wm_class() ?? '';
|
||||||
|
// Strip reverse-domain prefix (e.g. "org.gnome.Nautilus" -> "Nautilus")
|
||||||
|
const lastName = rawAppName.includes('.')
|
||||||
|
? (rawAppName.split('.').pop() ?? rawAppName)
|
||||||
|
: rawAppName;
|
||||||
|
// Capitalize first letter
|
||||||
|
const appName = lastName.charAt(0).toUpperCase() + lastName.slice(1);
|
||||||
const title = this._window.get_title() ?? 'Untitled';
|
const title = this._window.get_title() ?? 'Untitled';
|
||||||
if (appName && appName.toLowerCase() !== title.toLowerCase()) {
|
if (appName && appName.toLowerCase() !== title.toLowerCase()) {
|
||||||
return `${appName} | ${title}`;
|
return `${appName} | ${title}`;
|
||||||
@@ -108,6 +114,9 @@ export class WindowWrapper {
|
|||||||
this._window.connect("size-changed", () => {
|
this._window.connect("size-changed", () => {
|
||||||
windowManager.handleWindowPositionChanged(this);
|
windowManager.handleWindowPositionChanged(this);
|
||||||
}),
|
}),
|
||||||
|
this._window.connect('notify::title', () => {
|
||||||
|
windowManager.handleWindowTitleChanged(this);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export interface IWindowManager {
|
|||||||
|
|
||||||
handleWindowPositionChanged(winWrap: WindowWrapper): void;
|
handleWindowPositionChanged(winWrap: WindowWrapper): void;
|
||||||
|
|
||||||
|
handleWindowTitleChanged(winWrap: WindowWrapper): void;
|
||||||
|
|
||||||
syncActiveWindow(): number | null;
|
syncActiveWindow(): number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +422,11 @@ export default class WindowManager implements IWindowManager {
|
|||||||
this._tileMonitors();
|
this._tileMonitors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleWindowTitleChanged(window: WindowWrapper): void {
|
||||||
|
const mon_id = window._window.get_monitor();
|
||||||
|
this._monitors.get(mon_id)?.refreshTabTitlesForWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
public addWindowToMonitor(window: Meta.Window) {
|
public addWindowToMonitor(window: Meta.Window) {
|
||||||
Logger.log("ADDING WINDOW TO MONITOR", window, window);
|
Logger.log("ADDING WINDOW TO MONITOR", window, window);
|
||||||
var wrapper = new WindowWrapper(window, (winWrap) => this.handleWindowMinimized(winWrap))
|
var wrapper = new WindowWrapper(window, (winWrap) => this.handleWindowMinimized(winWrap))
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
color: rgba(255, 255, 255, 0.5);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aerospike-tab:hover {
|
.aerospike-tab:hover {
|
||||||
@@ -28,4 +29,5 @@
|
|||||||
|
|
||||||
.aerospike-tab-label {
|
.aerospike-tab-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user