Files
aerospike-gnome/src/wm/tabBar.ts
T
Lucas Oskorep cde5a4d58f
Build and Test / build (push) Successful in 26s
Build and Test / release (push) Successful in 3s
feat: upgrade to gnome 51
Adds GNOME Shell 51 to shell-version and moves the build onto the
GNOME 51 type packages.

- tabBar: pass orientation instead of vertical to St.BoxLayout. The vertical
  property was removed from St widgets in 51 (deprecated in 48); orientation
  exists in 48 and later, so this keeps 48-51 working. Verified against the
  installed St-51 typelib
- switch to @girs/gjs 5.5.0 and the -51 namespace packages (shell/st/meta/
  clutter plus gio/glib/gobject/gtk/gdk/adw/pango)
- drop @girs/gnome-shell 50 and the stale @girs/mtk-17 dependency. The
  umbrella is GNOME 50 only and pulls St-18 and Meta-18 types that clash
  with the -51 packages
- types/: hand-written declarations for the Shell JS modules imported here
  (extension.js, prefs.js, main.js). Upstream 51 types (51.0.0-alpha.1) are
  not published to npm yet
- TypeScript 6.0.3 rather than 7: typescript-eslint requires <6.1.0 and
  ts-jest requires <7
- eslint 10.12, @eslint/js 10.0.1, eslint-plugin-jsdoc 65.3.4,
  typescript-eslint 8.71.2 and jest 30.5.2 / ts-jest 29.4.14
2026-10-15 01:54:51 -04:00

134 lines
4.0 KiB
TypeScript

import Clutter from 'gi://Clutter';
import Pango from 'gi://Pango';
import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import {Logger} from "../utils/logger.js";
import {WindowWrapper} from "./window.js";
import {Rect} from "../utils/rect.js";
export const TAB_BAR_HEIGHT = 24;
type TabClickedCallback = (index: number) => void;
export class TabBar {
private _bar: St.BoxLayout;
private _buttons: St.Button[] = [];
private _activeIndex: number = 0;
private _onTabClicked: TabClickedCallback;
private _visible: boolean = false;
constructor(onTabClicked: TabClickedCallback) {
this._onTabClicked = onTabClicked;
this._bar = new St.BoxLayout({
style_class: 'aerospike-tab-bar',
// `vertical` was removed from St widgets in GNOME Shell 51 (deprecated
// in 48); `orientation` is its replacement and exists in 48 and later.
orientation: Clutter.Orientation.HORIZONTAL,
reactive: true,
can_focus: false,
track_hover: false,
});
// Force all tabs to equal width regardless of text length
(this._bar.layout_manager as Clutter.BoxLayout).homogeneous = true;
}
/**
* Rebuild all tab buttons from the current list of window items.
*/
rebuild(items: WindowWrapper[], activeIndex: number): void {
// Remove old buttons
this._bar.destroy_all_children();
this._buttons = [];
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({
style_class: 'aerospike-tab',
reactive: true,
can_focus: false,
track_hover: true,
x_expand: true,
child: label,
});
button.connect('clicked', () => {
this._onTabClicked(index);
});
this._bar.add_child(button);
this._buttons.push(button);
});
this.setActive(activeIndex);
}
/**
* Update just the title text of a single tab (e.g. when a window title changes).
*/
updateTabTitle(index: number, title: string): void {
if (index < 0 || index >= this._buttons.length) return;
const label = this._buttons[index].get_child() as St.Label;
if (label) label.set_text(title);
}
/**
* Highlight the active tab and dim the rest.
*/
setActive(index: number): void {
this._activeIndex = index;
this._buttons.forEach((btn, i) => {
if (i === index) {
btn.add_style_class_name('aerospike-tab-active');
} else {
btn.remove_style_class_name('aerospike-tab-active');
}
});
}
/**
* Position and size the tab bar at the given screen rect.
*/
setPosition(rect: Rect): void {
this._bar.set_position(rect.x, rect.y);
this._bar.set_size(rect.width, rect.height);
}
show(): void {
if (this._visible) return;
this._visible = true;
Main.layoutManager.uiGroup.add_child(this._bar);
this._bar.show();
Logger.log("TabBar shown");
}
hide(): void {
if (!this._visible) return;
this._visible = false;
this._bar.hide();
if (this._bar.get_parent()) {
Main.layoutManager.uiGroup.remove_child(this._bar);
}
Logger.log("TabBar hidden");
}
destroy(): void {
this.hide();
this._bar.destroy_all_children();
this._buttons = [];
this._bar.destroy();
Logger.log("TabBar destroyed");
}
isVisible(): boolean {
return this._visible;
}
}