diff --git a/Makefile b/Makefile index 7d9ae7f..c9c1b0f 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,13 @@ node_modules: package.json dist/extension.js dist/prefs.js: node_modules tsc +dist/stylesheet.css: stylesheet.css + cp stylesheet.css dist/ + schemas/gschemas.compiled: schemas/org.gnome.shell.extensions.$(NAME).gschema.xml glib-compile-schemas schemas -$(NAME).zip: dist/extension.js dist/prefs.js schemas/gschemas.compiled +$(NAME).zip: dist/extension.js dist/prefs.js dist/stylesheet.css schemas/gschemas.compiled @cp -r schemas dist/ @cp metadata.json dist/ @(cd dist && zip ../$(NAME).zip -9r .) diff --git a/extension.ts b/extension.ts index 1656f20..14f1825 100644 --- a/extension.ts +++ b/extension.ts @@ -5,82 +5,124 @@ import St from "gi://St"; import Shell from "gi://Shell"; import * as Main from "resource:///org/gnome/shell/ui/main.js"; import { Extension } from "resource:///org/gnome/shell/extensions/extension.js"; +import { Button as PanelMenuButton } from "resource:///org/gnome/shell/ui/panelMenu.js"; +import { EventEmitter } from "resource:///org/gnome/shell/misc/signals.js"; import { SystemIndicator, QuickMenuToggle } from "resource:///org/gnome/shell/ui/quickSettings.js"; import GObject from "gi://GObject"; -import { Button as PanelMenuButton } from "resource:///org/gnome/shell/ui/panelMenu.js"; - -const MyIndicator = GObject.registerClass( - class MyIndicator extends SystemIndicator { - _indicator: any; - _settings?: Gio.Settings; - constructor(extensioObject: Extension) { - super(); - - this._indicator = this._addIndicator(); - this._indicator.icon_name = "selection-mode-symbolic"; - - this._settings = extensioObject.getSettings(); - this._settings!.bind("float", this._indicator, "active", Gio.SettingsBindFlags.DEFAULT); - - this.quickSettingsItems.push(this._indicator); - } - } -); - -const ExampleToggle = GObject.registerClass( - class ExampleToggle extends QuickMenuToggle { - _settings: Gio.Settings; - constructor(extensionObject: Extension) { - super({ - title: "Float", - subtitle: "Floating Window", - iconName: "selection-mode-symbolic", - toggleMode: true, - }); - - this._settings = extensionObject.getSettings(); - this._settings.bind("float", this, "checked", Gio.SettingsBindFlags.DEFAULT); - } - } -); export default class MyExtension extends Extension { - _button?: PanelMenuButton; - indicator?: SystemIndicator; + button?: PanelMenuButton; settings?: Gio.Settings; + float: boolean = false; + updateFloat: EventEmitter = new EventEmitter(); + + _actorSignalIds: Map = new Map(); + _windowSignalIds: Map = new Map(); enable() { - const icon = new St.Icon({ - icon_name: "face-laugh-symbolic", - style_class: "system-status-icon", - }); + // this.button = new ToggleButton(this); - this._button = new PanelMenuButton(0.0, this.metadata.name, false); - this._button.add_child(icon); - this.settings = this.getSettings(); + // Main.panel.addToStatusArea(this.uuid, this.button); + this._actorSignalIds.set(Main.overview, [ + Main.overview.connect("showing", () => this._updateFloating.bind(this)), + Main.overview.connect("hiding", () => this._updateFloating.bind(this)), + ]); - this._button.connect("button-press-event", () => this.toggleFloat()); - this._button.connect("touch-event", () => this.toggleFloat()); + this._actorSignalIds.set(Main.sessionMode, [Main.sessionMode.connect("updated", () => this._updateFloating.bind(this))]); - // this._button.addChild(); - // this.indicator = new MyIndicator(this); - // this.indicator.quickSettingsItems.push(new ExampleToggle(this)); + for (const window_actor of global.get_window_actors()) { + this._onWindowActorAdded(window_actor); + } - // Main.panel.statusArea.quickSettings.addExternalIndicator(this.indicator); - Main.panel.addToStatusArea(this.uuid, this._button); - } + this._actorSignalIds.set(global.window_group, [ + global.window_group.connect("actor-added", this._onWindowActorAdded.bind(this)), + global.window_group.connect("actor-removed", this._onWindowActorRemoved.bind(this)), + ]); - toggleFloat() { - let bool = this.getSettings().get_boolean("float"); - console.log(bool); - this.getSettings().set_boolean("float", !bool); + this._actorSignalIds.set(global.window_manager, [global.window_manager.connect("switch-workspace", this._updateFloating.bind(this))]); } disable() { - this._button?.destroy(); - this._button = undefined; - // this.indicator?.quickSettingsItems.forEach((item) => item.destroy()); - // this.indicator?.destroy(); - // this.indicator = undefined; + for (const actorSignalIds of [this._actorSignalIds, this._windowSignalIds]) { + for (const [actor, signalIds] of actorSignalIds) { + for (const signalId of signalIds) { + actor.disconnect(signalId); + } + } + } + this._actorSignalIds.clear(); + this._windowSignalIds.clear(); + + this._setFloat(false); + } + + _onWindowActorAdded(metaWindowActor: Meta.WindowActor) { + this._windowSignalIds.set(metaWindowActor, [ + metaWindowActor.connect("notify::allocation", this._updateFloating.bind(this)), + metaWindowActor.connect("notify::visible", this._updateFloating.bind(this)), + metaWindowActor.connect("destroy", () => this._onWindowActorRemoved.bind(this, metaWindowActor)), + ]); + } + + _onWindowActorRemoved(metaWindowActor: Meta.WindowActor) { + for (const signalId of this._windowSignalIds.get(metaWindowActor)) { + metaWindowActor.disconnect(signalId); + } + this._windowSignalIds.delete(metaWindowActor); + this._updateFloating(); + } + + _updateFloating() { + const workspace_manager = global.workspaceManager; + const active_workspace = workspace_manager.get_active_workspace(); + const windows = active_workspace.list_windows().filter((metaWindow) => { + return !metaWindow.skip_taskbar && metaWindow.showing_on_its_workspace() && !metaWindow.is_hidden(); + }); + for (let win of windows) { + console.log("================== Window ================"); + console.log(win.title); + console.log(win.maximizedHorizontally); //switch + } + + const maximized = windows.some((metaWindow) => { + return metaWindow.maximizedHorizontally; + }); + + this._setFloat(!maximized); + } + + _setFloat(float: boolean) { + console.log("Float: ", float); + Main.panel.add_style_class_name(float ? "floating" : "solid"); + Main.panel.remove_style_class_name(float ? "solid" : "floating"); } } + +const ToggleButton = GObject.registerClass( + class ToggleButton extends PanelMenuButton { + _extension: MyExtension; + constructor(extension: MyExtension) { + super(0.0, extension.metadata.name, false); + let icon = new St.Icon({ + icon_name: "face-laugh-symbolic", + style_class: "system-status-icon", + }); + + this._extension = extension; + + this.add_child(icon); + + this.connect("button-press-event", () => this.toggleFloat()); + this.connect("touch-event", () => this.toggleFloat()); + } + + toggleFloat() { + this._extension.float = !this._extension.float; + this._extension.updateFloat.emit("update"); + this._extension._updateFloating(); + // for (const window of global.get_window_actors()) { + // console.log(window.get_parent()); + // } + } + } +); diff --git a/floatbar.zip b/floatbar.zip index deca3d3..0eda8a0 100644 Binary files a/floatbar.zip and b/floatbar.zip differ diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 0000000..11b31f5 --- /dev/null +++ b/stylesheet.css @@ -0,0 +1,9 @@ +#panel.floating { + margin: 3px; + border-radius: 9999px; +} + +#panel.solid { + margin: 0; + border-radius: 0; +}