From d342a40bef074ec16bf17e12b122c5feba709e6a Mon Sep 17 00:00:00 2001 From: Sominemo Date: Wed, 8 Nov 2023 01:14:26 +0200 Subject: [PATCH 1/6] Migrate to GNOME 45 fixes #175 --- fildemGMenu@gonza.com/extension.js | 49 +++++++++++++++-------------- fildemGMenu@gonza.com/metadata.json | 6 +--- fildemGMenu@gonza.com/prefs.js | 29 ++++++++++------- fildemGMenu@gonza.com/settings.js | 16 ++++++---- 4 files changed, 55 insertions(+), 45 deletions(-) diff --git a/fildemGMenu@gonza.com/extension.js b/fildemGMenu@gonza.com/extension.js index e5ca00a..450df36 100644 --- a/fildemGMenu@gonza.com/extension.js +++ b/fildemGMenu@gonza.com/extension.js @@ -1,19 +1,23 @@ 'use strict'; -const { loadInterfaceXML } = imports.misc.fileUtils; +// import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/fileUtils.js'; -const { Clutter, Gio, GLib, GObject, Meta, St } = imports.gi; +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio?version=2.0'; +import GLib from 'gi://GLib?version=2.0'; +import GObject from 'gi://GObject?version=2.0'; +import Meta from 'gi://Meta'; +import St from 'gi://St?'; +import Shell from 'gi://Shell'; -const AppSystem = imports.gi.Shell.AppSystem.get_default(); -const WinTracker = imports.gi.Shell.WindowTracker.get_default(); +import { Extension as GExtension } from "resource:///org/gnome/shell/extensions/extension.js"; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +const WinTracker = Shell.WindowTracker.get_default(); -const Settings = Me.imports.settings.FildemGlobalMenuSettings; -const Main = imports.ui.main; -const PanelMenu = imports.ui.panelMenu; -const WindowMenu = imports.ui.windowMenu; +import { FildemGlobalMenuSettings as Settings } from './settings.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; +import * as WindowMenu from 'resource:///org/gnome/shell/ui/windowMenu.js'; function log(msg) { @@ -106,7 +110,7 @@ const WindowActions = class WindowActions { if (rightMonitorIndex != -1) this.actions.push('Move to Monitor Right'); } - + if (win.can_close()) this.actions.push('Close'); @@ -448,7 +452,7 @@ const MenuBar = class MenuBar { this._restoreLabel(); this._hideMenu(); const overview = Main.overview.visibleTarget; - const focusApp = WinTracker.focus_app || Main.panel.statusArea.appMenu._targetApp; + const focusApp = WinTracker.focus_app || Main.panel.statusArea.appMenu?._targetApp; if (focusApp) { let windowData = {}; // TODO does the window matter? @@ -680,15 +684,14 @@ class Extension { let extension; -function init(metadata) { -} - -function enable() { - let settings = new Settings(Me.metadata['settings-schema']); - extension = new Extension(settings); -} +export default class FildemMenuExtension extends GExtension { + enable() { + let settings = new Settings(this, this.metadata['settings-schema']); + extension = new Extension(settings); + } -function disable() { - extension.destroy(); - extension = null; -} + disable() { + extension.destroy(); + extension = null; + } +} \ No newline at end of file diff --git a/fildemGMenu@gonza.com/metadata.json b/fildemGMenu@gonza.com/metadata.json index ac650b8..803265c 100644 --- a/fildemGMenu@gonza.com/metadata.json +++ b/fildemGMenu@gonza.com/metadata.json @@ -3,11 +3,7 @@ "description": "Global menu for Gnome", "settings-schema": "org.gnome.shell.extensions.fildem-global-menu", "shell-version": [ - "3.36", - "3.38", - "40", - "41", - "42" + "45" ], "url": "https://github.com/gonzaarcr/Fildem", "uuid": "fildemGMenu@gonza.com" diff --git a/fildemGMenu@gonza.com/prefs.js b/fildemGMenu@gonza.com/prefs.js index 18642eb..88ec992 100644 --- a/fildemGMenu@gonza.com/prefs.js +++ b/fildemGMenu@gonza.com/prefs.js @@ -1,10 +1,11 @@ -const GObject = imports.gi.GObject; -const Gio = imports.gi.Gio; -const Gtk = imports.gi.Gtk; -const Config = imports.misc.config; +import GObject from 'gi://GObject?version=2.0'; +import Gio from 'gi://Gio?version=2.0'; +import Gtk from 'gi://Gtk?version=4.0'; +import * as Config from 'resource:///org/gnome/Shell/Extensions/js/misc/config.js'; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const Settings = Me.imports.settings.FildemGlobalMenuSettings; +// import {Extension as Me} from 'resource:///org/gnome/shell/extensions/extension.js'; +import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; +import {FildemGlobalMenuSettings as Settings} from './settings.js'; const SHELL_VERSION = Config.PACKAGE_VERSION; @@ -12,11 +13,11 @@ const SHELL_VERSION = Config.PACKAGE_VERSION; const PrefsWidget = GObject.registerClass( class PrefsWidget extends Gtk.Box { - _init(settings, params) { + _init(conf, settings, params) { super._init(params); this._buildable = new Gtk.Builder(); - this._buildable.add_from_file(Me.path + '/settings.ui'); + this._buildable.add_from_file(conf.path + '/settings.ui'); let prefsWidget = this._getWidget('prefs_widget'); if (SHELL_VERSION < '40') { @@ -87,9 +88,15 @@ function init() { } -function buildPrefsWidget() { - let settings = new Settings(Me.metadata['settings-schema']); - let widget = new PrefsWidget(settings); +export default class FildemMenuExtensionPrefs extends ExtensionPreferences { + getPreferencesWidget() { + return buildPrefsWidget(this); + } +} + +function buildPrefsWidget(conf) { + let settings = new Settings(conf, conf.metadata['settings-schema']); + let widget = new PrefsWidget(conf, settings); widget.show_all(); return widget; diff --git a/fildemGMenu@gonza.com/settings.js b/fildemGMenu@gonza.com/settings.js index 206c1f2..3e4b148 100644 --- a/fildemGMenu@gonza.com/settings.js +++ b/fildemGMenu@gonza.com/settings.js @@ -1,13 +1,15 @@ -const GObject = imports.gi.GObject; -const Gio = imports.gi.Gio; +import GObject from 'gi://GObject?version=2.0'; +import Gio from 'gi://Gio?version=2.0'; const GioSSS = Gio.SettingsSchemaSource; -const Me = imports.misc.extensionUtils.getCurrentExtension(); - var FildemGlobalMenuSettings = GObject.registerClass( class FildemGlobalMenuSettings extends Gio.Settings { - _init(schema) { - let schemaDir = Me.dir.get_child('schemas'); + constructor(conf, schema) { + super(schema, conf); + } + + _init(schema, conf) { + let schemaDir = conf.dir.get_child('schemas'); let schemaSource = null; if (schemaDir.query_exists(null)) { @@ -26,3 +28,5 @@ class FildemGlobalMenuSettings extends Gio.Settings { super._init({ settings_schema: schemaObj }); } }); + +export {FildemGlobalMenuSettings}; \ No newline at end of file From 05bb8da8e7f876f4907dd62db1e6d62b7966bb58 Mon Sep 17 00:00:00 2001 From: Jatin Chaudhary Date: Sat, 4 May 2024 14:34:44 +0530 Subject: [PATCH 2/6] Update metadata.json add gnome 46 support tested and working with no issue --- fildemGMenu@gonza.com/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fildemGMenu@gonza.com/metadata.json b/fildemGMenu@gonza.com/metadata.json index 803265c..02c0a55 100644 --- a/fildemGMenu@gonza.com/metadata.json +++ b/fildemGMenu@gonza.com/metadata.json @@ -3,7 +3,8 @@ "description": "Global menu for Gnome", "settings-schema": "org.gnome.shell.extensions.fildem-global-menu", "shell-version": [ - "45" + "45", + "46" ], "url": "https://github.com/gonzaarcr/Fildem", "uuid": "fildemGMenu@gonza.com" From e4a2cc54da81d509053690468fbed2d1d9606045 Mon Sep 17 00:00:00 2001 From: Sominemo Date: Wed, 4 Sep 2024 23:55:34 +0300 Subject: [PATCH 3/6] Mark GNOME 47 as supported --- fildemGMenu@gonza.com/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fildemGMenu@gonza.com/metadata.json b/fildemGMenu@gonza.com/metadata.json index 02c0a55..8f2562d 100644 --- a/fildemGMenu@gonza.com/metadata.json +++ b/fildemGMenu@gonza.com/metadata.json @@ -4,7 +4,8 @@ "settings-schema": "org.gnome.shell.extensions.fildem-global-menu", "shell-version": [ "45", - "46" + "46", + "47" ], "url": "https://github.com/gonzaarcr/Fildem", "uuid": "fildemGMenu@gonza.com" From 686979515ef0190c37df6df5bbf02dd9e6f27aa2 Mon Sep 17 00:00:00 2001 From: Sominemo Date: Thu, 20 Mar 2025 11:56:40 +0200 Subject: [PATCH 4/6] Confirmed to work on GNOME 48 --- fildemGMenu@gonza.com/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fildemGMenu@gonza.com/metadata.json b/fildemGMenu@gonza.com/metadata.json index 8f2562d..4addda6 100644 --- a/fildemGMenu@gonza.com/metadata.json +++ b/fildemGMenu@gonza.com/metadata.json @@ -5,7 +5,8 @@ "shell-version": [ "45", "46", - "47" + "47", + "48" ], "url": "https://github.com/gonzaarcr/Fildem", "uuid": "fildemGMenu@gonza.com" From b9925c5d9d0424b44f082341e198137a0049a136 Mon Sep 17 00:00:00 2001 From: Sominemo Date: Sat, 20 Sep 2025 02:04:44 +0300 Subject: [PATCH 5/6] Confirmed to work on GNOME 49 --- fildemGMenu@gonza.com/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fildemGMenu@gonza.com/metadata.json b/fildemGMenu@gonza.com/metadata.json index 4addda6..a2fd8e0 100644 --- a/fildemGMenu@gonza.com/metadata.json +++ b/fildemGMenu@gonza.com/metadata.json @@ -6,7 +6,8 @@ "45", "46", "47", - "48" + "48", + "49" ], "url": "https://github.com/gonzaarcr/Fildem", "uuid": "fildemGMenu@gonza.com" From fa77b266d66b5940a1a23cdd4740f166b07614a6 Mon Sep 17 00:00:00 2001 From: Sominemo Date: Thu, 26 Feb 2026 16:47:27 +0200 Subject: [PATCH 6/6] GNOME 50 support --- fildemGMenu@gonza.com/extension.js | 1292 ++++++++--------- fildemGMenu@gonza.com/metadata.json | 3 +- ....extensions.fildem-global-menu.gschema.xml | 2 +- 3 files changed, 637 insertions(+), 660 deletions(-) diff --git a/fildemGMenu@gonza.com/extension.js b/fildemGMenu@gonza.com/extension.js index 450df36..4de6639 100644 --- a/fildemGMenu@gonza.com/extension.js +++ b/fildemGMenu@gonza.com/extension.js @@ -1,697 +1,673 @@ 'use strict'; -// import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/fileUtils.js'; - import Clutter from 'gi://Clutter'; import Gio from 'gi://Gio?version=2.0'; import GLib from 'gi://GLib?version=2.0'; import GObject from 'gi://GObject?version=2.0'; import Meta from 'gi://Meta'; -import St from 'gi://St?'; +import St from 'gi://St'; import Shell from 'gi://Shell'; -import { Extension as GExtension } from "resource:///org/gnome/shell/extensions/extension.js"; - -const WinTracker = Shell.WindowTracker.get_default(); - -import { FildemGlobalMenuSettings as Settings } from './settings.js'; +import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; import * as WindowMenu from 'resource:///org/gnome/shell/ui/windowMenu.js'; +import { FildemGlobalMenuSettings as Settings } from './settings.js'; -function log(msg) { - const debug = true; - if (debug) - global.log('[FILDEM_MENU] ' + msg); -} - +const WinTracker = Shell.WindowTracker.get_default(); const WindowActions = class WindowActions { - constructor() { - this._win = global.display.get_focus_window(); - this.actions = []; - } - - // gitlab.gnome.org/GNOME/gnome-shell/-/blob/gnome-3-36/js/ui/windowMenu.js - getActions() { - let type = this._win.get_window_type(); - let win = this._win; - - if (win.can_minimize()) - this.actions.push('Minimize'); - - if (win.can_maximize()) - this.actions.push(win.get_maximized() ? 'Unmaximize' : 'Maximize'); - - if (win.allows_move()) - this.actions.push('Move'); - - if (win.allows_resize()) - this.actions.push('Resize'); - - if (win.titlebar_is_onscreen() && type != Meta.WindowType.DOCK && type != Meta.WindowType.DESKTOP) - this.actions.push('Move Titlebar Onscreen') - - if (win.get_maximized() == Meta.MaximizeFlags.BOTH - || type == Meta.WindowType.DOCK - || type == Meta.WindowType.DESKTOP - || type == Meta.WindowType.SPLASHSCREEN) { - - this.actions.push('Always on Top' + (win.is_above() ? ' ✓' : '')); - } - - if (Main.sessionMode.hasWorkspaces - && (!Meta.prefs_get_workspaces_only_on_primary() || win.is_on_primary_monitor())) { - - let isSticky = win.is_on_all_workspaces(); - - if (win.is_always_on_all_workspaces()) { - this.actions.push('Always on Visible Workspace' + (isSticky ? ' ✓' : '')); - } - - if (!isSticky) { - let workspace = win.get_workspace(); - if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) - this.actions.push('Move to Workspace Left'); - - if (workspace != workspace.get_neighbor(Meta.MotionDirection.RIGHT)) - this.actions.push('Move to Workspace Right'); - - if (workspace != workspace.get_neighbor(Meta.MotionDirection.UP)) - this.actions.push('Move to Workspace Up'); - - if (workspace != workspace.get_neighbor(Meta.MotionDirection.DOWN)) - this.actions.push('Move to Workspace Down'); - } - } - - let display = global.display; - let nMonitors = display.get_n_monitors(); - let monitorIndex = win.get_monitor(); - if (nMonitors > 1 && monitorIndex >= 0) { - let dir = Meta.DisplayDirection.UP; - let upMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); - if (upMonitorIndex != -1) - this.actions.push('Move to Monitor Up'); - - dir = Meta.DisplayDirection.DOWN; - let downMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); - if (downMonitorIndex != -1) - this.actions.push('Move to Monitor Down'); - - dir = Meta.DisplayDirection.LEFT; - let leftMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); - if (leftMonitorIndex != -1) - this.actions.push('Move to Monitor Left'); - - dir = Meta.DisplayDirection.RIGHT; - let rightMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); - if (rightMonitorIndex != -1) - this.actions.push('Move to Monitor Right'); - } - - if (win.can_close()) - this.actions.push('Close'); - - return this.actions; - } - - _doAction(action) { - if (action.endsWith(' ✓')) { - action = action.substr(0, action.length - 2); - } - let win = this._win; - switch (action) { - case 'Minimize': - win.minimize(); - break; - case 'Unmaximize': - win.unmaximize(Meta.MaximizeFlags.BOTH); - break; - case 'Maximize': - win.maximize(Meta.MaximizeFlags.BOTH); - break; - case 'Move': - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { - WindowMenu.WindowMenu.prototype._grabAction(win, Meta.GrabOp.KEYBOARD_MOVING, global.display.get_current_time_roundtrip()); - }); - break; - case 'Resize': - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { - WindowMenu.WindowMenu.prototype._grabAction(win, Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, global.display.get_current_time_roundtrip()); - }); - break; - case 'Move Titlebar Onscreen': - win.shove_titlebar_onscreen(); - break; - case 'Always on Top': - if (win.is_above()) - win.unmake_above(); - else - win.make_above(); - break; - case 'Always on Visible Workspace': - if (win.is_on_all_workspaces()) - win.unstick(); - else - win.stick(); - break; - case 'Move to Workspace Left': - this._moveToWorkspace(Meta.MotionDirection.LEFT); - break; - case 'Move to Workspace Right': - this._moveToWorkspace(Meta.MotionDirection.RIGHT); - break; - case 'Move to Workspace Up': - this._moveToWorkspace(Meta.MotionDirection.UP); - break; - case 'Move to Workspace Down': - this._moveToWorkspace(Meta.MotionDirection.DOWN); - break; - case 'Move to Monitor Up': - this._moveToMonitor(Meta.DisplayDirection.UP); - break; - case 'Move to Monitor Down': - this._moveToMonitor(Meta.DisplayDirection.DOWN); - break; - case 'Move to Monitor Left': - this._moveToMonitor(Meta.DisplayDirection.LEFT); - break; - case 'Move to Monitor Right': - this._moveToMonitor(Meta.DisplayDirection.RIGHT); - break; - case 'Close': - win.delete(global.get_current_time()); - break; - } - } - - _moveToWorkspace(dir) { - let workspace = this._win.get_workspace(); - this._win.change_workspace(workspace.get_neighbor(dir)); - } - - _moveToMonitor(dir) { - let monitorIndex = window.get_monitor(); - let newMonitorIndex = global.display.get_monitor_neighbor_index(monitorIndex, dir); - if (newMonitorIndex != -1) { - this._win.move_to_monitor(newMonitorIndex); - } - } + constructor(ext) { + this.ext = ext; + this._win = global.display.get_focus_window(); + this.actions = []; + } + + getActions() { + if (!this._win) return this.actions; + + let type = this._win.get_window_type(); + let win = this._win; + + if (win.can_minimize()) + this.actions.push('Minimize'); + + if (win.can_maximize()) { + let isMaximized = typeof win.is_maximized === 'function' ? win.is_maximized() : win.get_maximized(); + this.actions.push(isMaximized ? 'Unmaximize' : 'Maximize'); + } + + if (win.allows_move()) + this.actions.push('Move'); + + if (win.allows_resize()) + this.actions.push('Resize'); + + if (win.titlebar_is_onscreen() && type != Meta.WindowType.DOCK && type != Meta.WindowType.DESKTOP) + this.actions.push('Move Titlebar Onscreen'); + + let isMax = typeof win.is_maximized === 'function' ? win.is_maximized() : win.get_maximized(); + if (isMax + || type == Meta.WindowType.DOCK + || type == Meta.WindowType.DESKTOP + || type == Meta.WindowType.SPLASHSCREEN) { + + this.actions.push('Always on Top' + (win.is_above() ? ' ✓' : '')); + } + + if (Main.sessionMode.hasWorkspaces + && (!Meta.prefs_get_workspaces_only_on_primary() || win.is_on_primary_monitor())) { + + let isSticky = win.is_on_all_workspaces(); + + if (win.is_always_on_all_workspaces()) { + this.actions.push('Always on Visible Workspace' + (isSticky ? ' ✓' : '')); + } + + if (!isSticky) { + let workspace = win.get_workspace(); + if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) + this.actions.push('Move to Workspace Left'); + + if (workspace != workspace.get_neighbor(Meta.MotionDirection.RIGHT)) + this.actions.push('Move to Workspace Right'); + + if (workspace != workspace.get_neighbor(Meta.MotionDirection.UP)) + this.actions.push('Move to Workspace Up'); + + if (workspace != workspace.get_neighbor(Meta.MotionDirection.DOWN)) + this.actions.push('Move to Workspace Down'); + } + } + + let display = global.display; + let nMonitors = display.get_n_monitors(); + let monitorIndex = win.get_monitor(); + if (nMonitors > 1 && monitorIndex >= 0) { + let dir = Meta.DisplayDirection.UP; + let upMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); + if (upMonitorIndex != -1) this.actions.push('Move to Monitor Up'); + + dir = Meta.DisplayDirection.DOWN; + let downMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); + if (downMonitorIndex != -1) this.actions.push('Move to Monitor Down'); + + dir = Meta.DisplayDirection.LEFT; + let leftMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); + if (leftMonitorIndex != -1) this.actions.push('Move to Monitor Left'); + + dir = Meta.DisplayDirection.RIGHT; + let rightMonitorIndex = display.get_monitor_neighbor_index(monitorIndex, dir); + if (rightMonitorIndex != -1) this.actions.push('Move to Monitor Right'); + } + + if (win.can_close()) + this.actions.push('Close'); + + return this.actions; + } + + _doAction(action) { + if (!this._win) return; + + if (action.endsWith(' ✓')) { + action = action.substr(0, action.length - 2); + } + let win = this._win; + switch (action) { + case 'Minimize': win.minimize(); break; + case 'Unmaximize': + if (typeof win.set_maximize_flags === 'function') win.unmaximize(); + else win.unmaximize(Meta.MaximizeFlags.BOTH); + break; + case 'Maximize': + if (typeof win.set_maximize_flags === 'function') win.maximize(); + else win.maximize(Meta.MaximizeFlags.BOTH); + break; + case 'Move': + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { + WindowMenu.WindowMenu.prototype._grabAction(win, Meta.GrabOp.KEYBOARD_MOVING, global.display.get_current_time_roundtrip()); + }); + break; + case 'Resize': + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { + WindowMenu.WindowMenu.prototype._grabAction(win, Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, global.display.get_current_time_roundtrip()); + }); + break; + case 'Move Titlebar Onscreen': win.shove_titlebar_onscreen(); break; + case 'Always on Top': + if (win.is_above()) win.unmake_above(); + else win.make_above(); + break; + case 'Always on Visible Workspace': + if (win.is_on_all_workspaces()) win.unstick(); + else win.stick(); + break; + case 'Move to Workspace Left': this._moveToWorkspace(Meta.MotionDirection.LEFT); break; + case 'Move to Workspace Right': this._moveToWorkspace(Meta.MotionDirection.RIGHT); break; + case 'Move to Workspace Up': this._moveToWorkspace(Meta.MotionDirection.UP); break; + case 'Move to Workspace Down': this._moveToWorkspace(Meta.MotionDirection.DOWN); break; + case 'Move to Monitor Up': this._moveToMonitor(Meta.DisplayDirection.UP); break; + case 'Move to Monitor Down': this._moveToMonitor(Meta.DisplayDirection.DOWN); break; + case 'Move to Monitor Left': this._moveToMonitor(Meta.DisplayDirection.LEFT); break; + case 'Move to Monitor Right': this._moveToMonitor(Meta.DisplayDirection.RIGHT); break; + case 'Close': win.delete(global.get_current_time()); break; + } + } + + _moveToWorkspace(dir) { + let workspace = this._win.get_workspace(); + this._win.change_workspace(workspace.get_neighbor(dir)); + } + + _moveToMonitor(dir) { + let monitorIndex = this._win.get_monitor(); + let newMonitorIndex = global.display.get_monitor_neighbor_index(monitorIndex, dir); + if (newMonitorIndex != -1) { + this._win.move_to_monitor(newMonitorIndex); + } + } } -/** - * A single Button like File, Edit, etc. - */ var MenuButton = GObject.registerClass( class MenuButton extends PanelMenu.Button { - - _init(label, menuBar) { - label = label.replace('_', ''); - super._init(0.0, label); - this._label = label; - this._menuBar = menuBar; - - this.box = new St.BoxLayout({style_class: 'panel-status-menu-box menubar-button'}); - this.labelWidget = new St.Label({ - text: this._label, - y_align: Clutter.ActorAlign.CENTER, - reactive: true - }); - this.box.add_child(this.labelWidget); - this.add_child(this.box); - this.connect('button-release-event', this.onButtonEvent.bind(this)); - } - - _onStyleChanged(actor) { - super._onStyleChanged(actor); - let padding = this._menuBar.extension.settings.get_int('min-padding'); - this._minHPadding = padding; - this._natHPadding = padding; - } - - onButtonEvent(actor, event) { - if (event.get_button() !== 1) - return Clutter.EVENT_PROPAGATE; - - this._menuBar.onButtonClicked(this._label); - return Clutter.EVENT_STOP; - } + _init(label, menuBar, ext) { + super._init(0.0, label.replace('_', ''), true); + + this._label = label.replace('_', ''); + this._menuBar = menuBar; + this.ext = ext; + + this.box = new St.BoxLayout({ style_class: 'panel-status-menu-box menubar-button' }); + this.labelWidget = new St.Label({ + text: this._label, + y_align: Clutter.ActorAlign.CENTER, + reactive: false + }); + + this.box.add_child(this.labelWidget); + this.add_child(this.box); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + } + + _onButtonPress(actor, event) { + if (event.get_button() !== 1) + return Clutter.EVENT_PROPAGATE; + + this._menuBar.onButtonClicked(this._label); + return Clutter.EVENT_STOP; + } + + _onStyleChanged(actor) { + super._onStyleChanged(actor); + let padding = this.ext.customSettings.get_int('min-padding'); + this._minHPadding = padding; + this._natHPadding = padding; + } }); -const Cache = class Cache { - constructor() { - this.N = 10; - this.lru = []; - this.entries = {}; - this.lastQueriedKey = ''; - } - - get(key) { - this.lastQueriedKey = key; - return this.entries[key]; - } - - _set(key, value) { - if (this.entries[key]) { - const oldItem = this.lru.splice(this.lru.indexOf(key), 1); - } - this.lru.push(key); - this.entries[key] = value; - - if (this.lru.length > this.N) { - const toRemove = this.lru.pop(); - this.entries[toRemove] = undefined; - } - } - - withCache(f) { - const self = this; - const g = (param) => { - self._set(self.lastQueriedKey, param); - f(param); - } - return g; - } +class Cache { + constructor() { + this.N = 10; + this.lru = []; + this.entries = {}; + this.lastQueriedKey = ''; + } + + get(key) { + this.lastQueriedKey = key; + return this.entries[key]; + } + + _set(key, value) { + if (this.entries[key]) { + this.lru.splice(this.lru.indexOf(key), 1); + } + this.lru.push(key); + this.entries[key] = value; + + if (this.lru.length > this.N) { + const toRemove = this.lru.pop(); + this.entries[toRemove] = undefined; + } + } + + withCache(f) { + return (param) => { + this._set(this.lastQueriedKey, param); + f(param); + }; + } } -/** - * This is a manager not a container - */ -const MenuBar = class MenuBar { - constructor(proxy, extension) { - this._menuButtons = []; - this._proxy = proxy; - this.extension = extension; - // pixels from x_0 to the start of the menu - this._width_offset = 300; - this.MARGIN_FIRST_ELEMENT = 4; - this._isShowingMenu = false; - - this._cache = new Cache(); - - this._notifyFocusWinId = global.display.connect('notify::focus-window', this._onWindowSwitched.bind(this)); - this._proxy.listeners['SendTopLevelMenus'].push(this._cache.withCache(this.setMenus.bind(this))); - this._proxy.listeners['MenuOnOff'].push(this._onMenuOnOff.bind(this)); - Main.panel.reactive = true; - Main.panel.track_hover = true; - - this._panelEvHandlers = []; - this._forceShowMenu = false; - this._showAppMenuButton = false; - this.setForceShowMenu(); - this.setHideAppMenuButton(); - - Main.overview.connect('showing', this._onOverviewOpened.bind(this)); - Main.overview.connect('hiding', this._onOverviewClosed.bind(this)); - } - - setForceShowMenu() { - this._forceShowMenu = !this.extension.settings.get_boolean('show-only-when-hover'); - - if (!this._forceShowMenu) { - this._panelEvHandlers.push(Main.panel.connect('enter-event', this._onPanelEnter.bind(this))); - this._panelEvHandlers.push(Main.panel.connect('leave-event', this._onPanelLeave.bind(this))); - } else { - for (let h of this._panelEvHandlers) { - Main.panel.disconnect(h); - } - this._panelEvHandlers = []; - } - } - - setHideAppMenuButton() { - this._showAppMenuButton = !this.extension.settings.get_boolean('hide-app-menu'); - - let appBtn = Main.panel._leftBox.get_children().filter(item => { - item.get_first_child().constructor.name == 'AppMenuButton' - }); - if (appBtn.length > 0) { - this._appMenuButton = appBtn[0]; - } - this._restoreLabel(); - } - - addMenuButton(label, setmargin) { - let menuButton = new MenuButton(label, this); - this._menuButtons.push(menuButton); - const nItems = Main.panel._leftBox.get_children().length; - menuButton.hide(); - if (setmargin) - menuButton.set_style('margin-left: '+ this.MARGIN_FIRST_ELEMENT + 'px') - Main.panel.addToStatusArea(label, menuButton, nItems, 'left'); - } - - setMenus(menus) { - // The expansion/shrink can be annoying, so we only do it - // when there’s no menus - if (menus.length === 0) { - this._hideMenu(); - } - this.removeAll(); - let first = true; - for (let menu of menus) { - this.addMenuButton(menu, first); - first = false; - } - if (this._forceShowMenu && !Main.overview.visibleTarget) { - this._onPanelEnter(); - } - } - - _onPanelEnter() { - if (this._menuButtons.length === 0 || Main.overview.visibleTarget) - return; - - this._hideAppMenuButton(); - this._showMenu(); - } - - // Hides the label and calculates the width - _hideAppMenuButton() { - let width = 0; - for (let el of Main.panel._leftBox.get_children()) { - let firstChild = el.get_first_child(); - if (firstChild === this._menuButtons[0]) { - this._width_offset = width; - break; - } - if (firstChild.constructor.name == 'AppMenuButton') { - // [Deprecated] - this._appMenuButton = firstChild; - let label = firstChild._label; - - if (!this._showAppMenuButton) { - label.hide(); - } - this._width_offset = width + el.width; - // break; - } - if (el.is_visible()) { - width += el.get_width(); - } - } - } - - _showMenu() { - this._menuButtons.forEach(btn => btn.show()); - this._menuButtons.forEach(btn => btn.ease({ - opacity: 255, - mode: Clutter.AnimationMode.EASE_OUT_QUART, - duration: 250 - })); - } - - _onPanelLeave() { - if (this._isShowingMenu || this._forceShowMenu) - return; - - this._hideMenu(); - } - - _hideMenu() { - this._menuButtons.forEach(btn => btn.ease({ - opacity: 0, - mode: Clutter.AnimationMode.EASE_OUT_QUART, - duration: 100, - onComplete: () => { this._menuButtons.forEach(btn => btn.hide()); this._restoreLabel() } - })); - } - - _restoreLabel() { - if (this._appMenuButton) { - this._appMenuButton._label.show(); - } - } - - _onMenuOnOff(on) { - if (on) { - this._onPanelEnter(); - this.onButtonClicked('__fildem_move', this._width_offset); - } else { - this._isShowingMenu = false; - this._onPanelLeave(); - } - } - - onButtonClicked(label) { - this._isShowingMenu = true; - this._proxy.EchoSignal(label, this._width_offset); - } - - removeAll() { - for (let e of this._menuButtons) { - e.destroy(); - } - this._menuButtons = []; - } - - _onWindowSwitched() { - this.removeAll(); - this._restoreLabel(); - this._hideMenu(); - const overview = Main.overview.visibleTarget; - const focusApp = WinTracker.focus_app || Main.panel.statusArea.appMenu?._targetApp; - if (focusApp) { - let windowData = {}; - // TODO does the window matter? - let win = focusApp.get_windows()[0]; - let appId = focusApp.get_id(); // *.desktop - - // Check cache - let cachedValue = this._cache.get(appId); - if (cachedValue) { - this.setMenus(cachedValue); - } - - // global.log(`app id: ${focusApp.get_id()} win id: ${win.get_id()}`); - // TODO check pixel-saver extension for others way of obtaining xid - let xid = ''; - try { - xid = parseInt(win.get_description().match(/0x[0-9a-f]+/)[0]); - } catch (e) {} - windowData['xid'] = String(xid); - for (let p in win) { - if (p.startsWith('gtk_') && win[p] != null) { - windowData[p] = win[p]; - } - } - this._proxy.WindowSwitched(windowData); - } - } - - _onOverviewOpened() { - this._hideMenu(); - } - - _onOverviewClosed() { - if (this._forceShowMenu && this._menuButtons.length) { - this._hideAppMenuButton(); - this._showMenu(); - } - } - - _disconnectAll() { - // AppSystem.disconnect(this._appStateChangedId); - // WinTracker.disconnect(this._notifyFocusAppId); - for (let h of this._panelEvHandlers) { - Main.panel.disconnect(h); - } - global.display.disconnect(this._notifyFocusWinId); - } - - destroy() { - this._disconnectAll(); - this.removeAll(); - this._restoreLabel(); - } -}; +class MenuBar { + constructor(proxy, ext) { + this._menuButtons = []; + this._proxy = proxy; + this.ext = ext; + this._width_offset = 300; + this.MARGIN_FIRST_ELEMENT = 4; + this._isShowingMenu = false; + + this._cache = new Cache(); + + this._notifyFocusWinId = global.display.connect('notify::focus-window', this._onWindowSwitched.bind(this)); + this._proxy.listeners['SendTopLevelMenus'].push(this._cache.withCache(this.setMenus.bind(this))); + this._proxy.listeners['MenuOnOff'].push(this._onMenuOnOff.bind(this)); + + Main.panel.reactive = true; + Main.panel.track_hover = true; + + this._panelEvHandlers = []; + this._forceShowMenu = false; + this.setForceShowMenu(); + + Main.overview.connect('showing', this._onOverviewOpened.bind(this)); + Main.overview.connect('hiding', this._onOverviewClosed.bind(this)); + } + + setForceShowMenu() { + this._forceShowMenu = !this.ext.customSettings.get_boolean('show-only-when-hover'); + + if (!this._forceShowMenu) { + this._panelEvHandlers.push(Main.panel.connect('enter-event', this._onPanelEnter.bind(this))); + this._panelEvHandlers.push(Main.panel.connect('leave-event', this._onPanelLeave.bind(this))); + } else { + for (let h of this._panelEvHandlers) { + Main.panel.disconnect(h); + } + this._panelEvHandlers = []; + } + } + + addMenuButton(label, setmargin) { + try { + let menuButton = new MenuButton(label, this, this.ext); + this._menuButtons.push(menuButton); + const nItems = Main.panel._leftBox.get_children().length; + menuButton.hide(); + + if (setmargin) { + menuButton.set_style(`margin-left: ${this.MARGIN_FIRST_ELEMENT}px`); + } + + Main.panel.addToStatusArea(`fildem-menu-${label}`, menuButton, nItems, 'left'); + } catch (e) { + this.ext.logError(`Error adding menu button: ${e.message}`); + } + } + + setMenus(menus) { + try { + if (!menus) return; + + if (menus.length === 0) { + this._hideMenu(); + } + this.removeAll(); + + let first = true; + for (let menu of menus) { + this.addMenuButton(menu, first); + first = false; + } + if (this._forceShowMenu && !Main.overview.visibleTarget) { + this._onPanelEnter(); + } + } catch (e) { + this.ext.logError(`Crash in setMenus: ${e.message}`); + } + } + + _onPanelEnter() { + if (this._menuButtons.length === 0 || Main.overview.visibleTarget) return; + this._calculateOffset(); + this._showMenu(); + } + + _calculateOffset() { + if (this._menuButtons.length > 0) { + try { + let [x, y] = this._menuButtons[0].get_transformed_position(); + this._width_offset = x; + } catch (e) { + this.ext.logError(`Error calculating offset: ${e.message}`); + } + } + } + + _showMenu() { + this._menuButtons.forEach(btn => { + btn.show(); + btn.ease({ + opacity: 255, + mode: Clutter.AnimationMode.EASE_OUT_QUART, + duration: 250 + }); + }); + } + + _onPanelLeave() { + if (this._isShowingMenu || this._forceShowMenu) return; + this._hideMenu(); + } + + async _hideMenu() { + let promises = this._menuButtons.map(btn => { + return new Promise(resolve => { + if (typeof btn.easeAsync === 'function') { + btn.easeAsync({ + opacity: 0, + mode: Clutter.AnimationMode.EASE_OUT_QUART, + duration: 100 + }).then(() => { + btn.hide(); + resolve(); + }).catch(e => { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { + this.ext.logError(`Error hiding button: ${e.message}`); + } + resolve(); + }); + } else { + btn.ease({ + opacity: 0, + mode: Clutter.AnimationMode.EASE_OUT_QUART, + duration: 100, + onComplete: () => { + btn.hide(); + resolve(); + } + }); + } + }); + }); + await Promise.all(promises); + } + + _onMenuOnOff(on) { + if (on) { + this._onPanelEnter(); + this.onButtonClicked('__fildem_move'); + } else { + this._isShowingMenu = false; + this._onPanelLeave(); + } + } + + onButtonClicked(label) { + this._isShowingMenu = true; + this._calculateOffset(); + this._proxy.EchoSignal(label, this._width_offset); + } + + removeAll() { + for (let e of this._menuButtons) { + try { + e.destroy(); + } catch (err) { + this.ext.logError(`Error destroying button: ${err.message}`); + } + } + this._menuButtons = []; + } + + _onWindowSwitched() { + this.removeAll(); + this._hideMenu(); + const focusApp = WinTracker.focus_app; + + if (focusApp) { + let appId = focusApp.get_id() || ''; + + let cachedValue = this._cache.get(appId); + if (cachedValue) { + this.setMenus(cachedValue); + } + + let windowData = {}; + let win = focusApp.get_windows()[0]; + let xid = ''; + + try { + if (win && win.get_description()) { + let match = win.get_description().match(/0x[0-9a-f]+/); + if (match) xid = parseInt(match[0]); + } + } catch (e) { + this.ext.logError(`Error getting window XID: ${e.message}`); + } + + windowData['xid'] = String(xid); + for (let p in win) { + if (p.startsWith('gtk_') && win[p] != null) { + windowData[p] = win[p]; + } + } + this._proxy.WindowSwitched(windowData); + } + } + + _onOverviewOpened() { + this._hideMenu(); + } + + _onOverviewClosed() { + if (this._forceShowMenu && this._menuButtons.length) { + this._calculateOffset(); + this._showMenu(); + } + } + + _disconnectAll() { + for (let h of this._panelEvHandlers) { + Main.panel.disconnect(h); + } + global.display.disconnect(this._notifyFocusWinId); + } + + destroy() { + this._disconnectAll(); + this.removeAll(); + } +} const ifaceXml = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; const TestProxy = Gio.DBusProxy.makeProxyWrapper(ifaceXml); - const BUS_NAME = 'com.gonzaarcr.appmenu'; const BUS_PATH = '/com/gonzaarcr/appmenu'; class MyProxy { - constructor() { - this._createProxy(); - this._handlerIds = []; - } - - async _createProxy() { - this._proxy = new TestProxy( - Gio.DBus.session, - BUS_NAME, - BUS_PATH, - this._onProxyReady.bind(this) - ); - this.listeners = { - 'MenuActivated': [], - 'SendTopLevelMenus': [], - 'MenuOnOff': [] - } - } - - async _onProxyReady(result, error) { - let id = undefined; - id = this._proxy.connectSignal('SendTopLevelMenus', this._onSendTopLevelMenus.bind(this)); - this._handlerIds.push(id); - id = this._proxy.connectSignal('RequestWindowActionsSignal', this._onRequestWindowActionsSignal.bind(this)); - this._handlerIds.push(id); - id = this._proxy.connectSignal('ActivateWindowActionSignal', this._onActivateWindowActionSignal.bind(this)); - this._handlerIds.push(id); - id = this._proxy.connectSignal('MenuOnOff', this._onMenuOnOff.bind(this)); - this._handlerIds.push(id); - } - - async _onMenuActivated(proxy, nameOwner, args) { - global.log(`TestSignal: ${args[0]}`); - } - - async _onSendTopLevelMenus(proxy, nameOwner, args) { - let topLevelMenus = args[0]; - for (let callback of this.listeners['SendTopLevelMenus']) { - callback(topLevelMenus); - } - } - - async _onRequestWindowActionsSignal(proxy, nameOwner, args) { - this._currentWindow = new WindowActions(); - let actions = this._currentWindow.getActions(); - this._proxy.ListWindowActionsRemote(actions); - } - - async _onActivateWindowActionSignal(proxy, nameOwner, args) { - this._currentWindow._doAction(args[0]); - } - - async _onMenuOnOff(proxy, nameOwner, args) { - for (let callback of this.listeners['MenuOnOff']) { - callback(args[0]); - } - } - - _onNameOwnerChanged(proxy, sender, [name, oldOwner, newOwner]) { - global.log(`${name} ${oldOwner} ${newOwner}`) - } - - async WindowSwitched(windowData) { - this._proxy.WindowSwitchedRemote(windowData); - } - - async EchoSignal(menu, x) { - this._proxy.EchoSignalRemote(menu, x); - } - - destroy() { - for (let id of this._handlerIds) { - this._proxy.disconnectSignal(id); - } - this._handlerIds = []; - } -}; - - -class Extension { - constructor(settings) { - this.settings = settings; - this._handlerIds = []; - this.myProxy = new MyProxy(); - this.menubar = new MenuBar(this.myProxy, this); - - this._connectSettings(); - } - - _connectSettings() { - this._handlerIds.push(this.settings.connect( - 'changed::show-only-when-hover', - () => { this.menubar.setForceShowMenu(); } - )); - this._handlerIds.push(this.settings.connect( - 'changed::hide-app-menu', - () => { this.menubar.setHideAppMenuButton(); } - )); - } - - destroy() { - this._disconnectSettings(); - - this.menubar.destroy(); - this.myProxy.destroy(); - } - - _disconnectSettings() { - for (let h of this._handlerIds) { - this.settings.disconnect(h); - } - } + constructor(ext) { + this.ext = ext; + this._handlerIds = []; + this.listeners = { + 'MenuActivated': [], + 'SendTopLevelMenus': [], + 'MenuOnOff': [] + }; + this._createProxy(); + } + + _createProxy() { + this._proxy = new TestProxy( + Gio.DBus.session, + BUS_NAME, + BUS_PATH, + this._onProxyReady.bind(this) + ); + } + + _onProxyReady(result, error) { + if (error) { + this.ext.logError(`DBus initialization error: ${error}`); + return; + } + this._handlerIds.push(this._proxy.connectSignal('SendTopLevelMenus', this._onSendTopLevelMenus.bind(this))); + this._handlerIds.push(this._proxy.connectSignal('RequestWindowActionsSignal', this._onRequestWindowActionsSignal.bind(this))); + this._handlerIds.push(this._proxy.connectSignal('ActivateWindowActionSignal', this._onActivateWindowActionSignal.bind(this))); + this._handlerIds.push(this._proxy.connectSignal('MenuOnOff', this._onMenuOnOff.bind(this))); + } + + _onSendTopLevelMenus(proxy, nameOwner, args) { + try { + let topLevelMenus = args[0]; + for (let callback of this.listeners['SendTopLevelMenus']) { + callback(topLevelMenus); + } + } catch (e) { + this.ext.logError(`Crash in _onSendTopLevelMenus: ${e.message}`); + } + } + + _onRequestWindowActionsSignal(proxy, nameOwner, args) { + try { + this._currentWindow = new WindowActions(this.ext); + let actions = this._currentWindow.getActions(); + this._proxy.ListWindowActionsRemote(actions); + } catch (e) { + this.ext.logError(`Crash in _onRequestWindowActionsSignal: ${e.message}`); + } + } + + _onActivateWindowActionSignal(proxy, nameOwner, args) { + try { + this._currentWindow._doAction(args[0]); + } catch (e) { + this.ext.logError(`Crash in _onActivateWindowActionSignal: ${e.message}`); + } + } + + _onMenuOnOff(proxy, nameOwner, args) { + try { + for (let callback of this.listeners['MenuOnOff']) { + callback(args[0]); + } + } catch (e) { + this.ext.logError(`Crash in _onMenuOnOff: ${e.message}`); + } + } + + WindowSwitched(windowData) { + try { + this._proxy.WindowSwitchedRemote(windowData); + } catch (e) { + this.ext.logError(`Crash in WindowSwitched: ${e.message}`); + } + } + + EchoSignal(menu, x) { + try { + this._proxy.EchoSignalRemote(menu, x); + } catch (e) { + this.ext.logError(`Crash in EchoSignal: ${e.message}`); + } + } + + destroy() { + for (let id of this._handlerIds) { + this._proxy.disconnectSignal(id); + } + this._handlerIds = []; + } } - -let extension; - -export default class FildemMenuExtension extends GExtension { - enable() { - let settings = new Settings(this, this.metadata['settings-schema']); - extension = new Extension(settings); - } - - disable() { - extension.destroy(); - extension = null; - } +export default class FildemMenuExtension extends Extension { + logInfo(msg) { + if (this.logger) this.logger.info(msg); + else console.log(`[FILDEM_MENU] ${msg}`); + } + + logError(msg) { + if (this.logger) this.logger.error(msg); + else console.error(`[FILDEM_MENU] ${msg}`); + } + + logDebug(msg) { + if (this.logger) this.logger.debug(msg); + else console.log(`[FILDEM_MENU] [DEBUG] ${msg}`); + } + + enable() { + this.customSettings = new Settings(this, this.metadata['settings-schema']); + this._handlerIds = []; + + this.myProxy = new MyProxy(this); + this.menubar = new MenuBar(this.myProxy, this); + + this._handlerIds.push(this.customSettings.connect( + 'changed::show-only-when-hover', + () => { this.menubar.setForceShowMenu(); } + )); + } + + disable() { + for (let h of this._handlerIds) { + this.customSettings.disconnect(h); + } + this._handlerIds = []; + + if (this.menubar) { + this.menubar.destroy(); + this.menubar = null; + } + if (this.myProxy) { + this.myProxy.destroy(); + this.myProxy = null; + } + this.customSettings = null; + } } \ No newline at end of file diff --git a/fildemGMenu@gonza.com/metadata.json b/fildemGMenu@gonza.com/metadata.json index a2fd8e0..d0f4b4b 100644 --- a/fildemGMenu@gonza.com/metadata.json +++ b/fildemGMenu@gonza.com/metadata.json @@ -7,7 +7,8 @@ "46", "47", "48", - "49" + "49", + "50" ], "url": "https://github.com/gonzaarcr/Fildem", "uuid": "fildemGMenu@gonza.com" diff --git a/fildemGMenu@gonza.com/schemas/org.gnome.shell.extensions.fildem-global-menu.gschema.xml b/fildemGMenu@gonza.com/schemas/org.gnome.shell.extensions.fildem-global-menu.gschema.xml index c6dcacf..6e33f83 100644 --- a/fildemGMenu@gonza.com/schemas/org.gnome.shell.extensions.fildem-global-menu.gschema.xml +++ b/fildemGMenu@gonza.com/schemas/org.gnome.shell.extensions.fildem-global-menu.gschema.xml @@ -2,7 +2,7 @@ - 6 + 7 Button paddings (tweak this if the menu and items desynchronize)