From 6fffcdc5fae908b50591fd8130ca093c3e9dd960 Mon Sep 17 00:00:00 2001 From: Salvatore Merone Date: Sun, 1 Oct 2023 13:59:37 +0200 Subject: [PATCH] Gnome 45 Support (#76) --- rollup.config.js | 32 ++- src/extension/app.ts | 423 +++++++++++++--------------- src/extension/editor.ts | 34 ++- src/extension/hotkeys.ts | 16 +- src/extension/layouts_utils.ts | 27 +- src/extension/logging.ts | 6 +- src/extension/modifiers.ts | 4 +- src/extension/monitors.ts | 3 +- src/extension/prefs_builder.ts | 495 +++++++++++++-------------------- src/extension/settings.ts | 22 +- src/extension/shellversion.ts | 9 +- src/images/icon.png | Bin 0 -> 48541 bytes src/metadata.json | 4 +- 13 files changed, 479 insertions(+), 596 deletions(-) create mode 100644 src/images/icon.png diff --git a/rollup.config.js b/rollup.config.js index 9c31f53..40d8b84 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,24 +1,43 @@ import { defineConfig } from 'rollup'; import copy from 'rollup-plugin-copy' +// Let Rollup know that these libraries are known and it should not complain about them, +const externalImports = [ + 'gi://St', + 'gi://GLib', + 'gi://Gio', + 'gi://Gtk', + 'gi://GObject', + 'gi://Clutter', + 'resource:///org/gnome/shell/ui/main.js', + 'resource:///org/gnome/shell/ui/panelMenu.js', + 'resource:///org/gnome/shell/ui/popupMenu.js', + 'resource:///org/gnome/shell/ui/modalDialog.js', + 'gi://Meta', + 'gi://Shell', + 'gi://Adw', + 'resource:///org/gnome/shell/extensions/extension.js', + 'resource:///org/gnome/shell/misc/config.js', + 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js' +] + export default defineConfig([ { input: "build/app.js", output: { file: "dist/extension.js", - format: "cjs", - esModule: false, + esModule: true, }, plugins: [ stripExports(), - ] + ], + external: externalImports }, { input: "build/prefs_builder.js", output: { file: "dist/prefs.js", - format: "cjs", - esModule: false, + esModule: true, }, plugins: [ stripExports(), @@ -33,7 +52,8 @@ export default defineConfig([ { src: 'src/schemas/org.gnome.shell.extensions.gsnap.gschema.xml', dest: 'dist/schemas' }, ] }) - ] + ], + external: externalImports } ]); diff --git a/src/extension/app.ts b/src/extension/app.ts index af9f1db..2848865 100644 --- a/src/extension/app.ts +++ b/src/extension/app.ts @@ -1,15 +1,23 @@ // GJS import system -declare var imports: any; declare var global: any; +// @ts-ignore +import Gio from 'gi://Gio'; +// @ts-ignore +import St from 'gi://St'; +// @ts-ignore +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +// @ts-ignore +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; +// @ts-ignore +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; +// @ts-ignore +import { Extension, gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js'; + import { log } from './logging'; import { ShellVersion } from './shellversion'; import { bind as bindHotkeys, unbind as unbindHotkeys, Bindings } from './hotkeys'; import { ZoneEditor, ZonePreview, TabbedZoneManager, EntryDialog, ZoneManager } from "./editor"; -const Gio = imports.gi.Gio; -const St = imports.gi.St; -const Gettext = imports.gettext; -const _ = Gettext.gettext; import { Display, MetaSizeChange, @@ -26,6 +34,7 @@ import { } from './monitors'; import { + SettingsObject, deinitSettings, getBoolSetting, gridSettings, @@ -50,25 +59,10 @@ import ModifiersManager, { MODIFIERS_ENUM } from './modifiers'; ******************************************************************/ -/***************************************************************** - CONST & VARS - *****************************************************************/ - -// Library imports -const Main = imports.ui.main; -const GObject = imports.gi.GObject; -const PanelMenu = imports.ui.panelMenu; -const PopupMenu = imports.ui.popupMenu; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - // Getter for accesing "get_active_workspace" on GNOME <=2.28 and >= 2.30 const WorkspaceManager: WorkspaceManagerInterface = ( global.screen || global.workspace_manager); -let launcher: GSnapStatusButtonClass | null; -let enabled = false; -let monitorsChangedConnect: any = false; const trackedWindows: Window[] = global.trackedWindows = []; const SHELL_VERSION = ShellVersion.defaultVersion(); @@ -80,119 +74,10 @@ enum MoveDirection { Right } -const keyBindings: Bindings = new Map([ - [SETTINGS.MOVE_FOCUSED_UP, () => { - globalApp.moveFocusedWindow(MoveDirection.Up) - }], - [SETTINGS.MOVE_FOCUSED_DOWN, () => { - globalApp.moveFocusedWindow(MoveDirection.Down) - }], - [SETTINGS.MOVE_FOCUSED_LEFT, () => { - globalApp.moveFocusedWindow(MoveDirection.Left) - }], - [SETTINGS.MOVE_FOCUSED_RIGHT, () => { - globalApp.moveFocusedWindow(MoveDirection.Right) - }], -]); - -const key_bindings_presets: Bindings = new Map([ - [SETTINGS.PRESET_RESIZE_1, () => { - globalApp.setLayout(0); - }], - [SETTINGS.PRESET_RESIZE_2, () => { - globalApp.setLayout(1); - }], - [SETTINGS.PRESET_RESIZE_3, () => { - globalApp.setLayout(2); - }], - [SETTINGS.PRESET_RESIZE_4, () => { - globalApp.setLayout(3); - }], - [SETTINGS.PRESET_RESIZE_5, () => { - globalApp.setLayout(4); - }], - [SETTINGS.PRESET_RESIZE_6, () => { - globalApp.setLayout(5); - }], - [SETTINGS.PRESET_RESIZE_7, () => { - globalApp.setLayout(6); - }], - [SETTINGS.PRESET_RESIZE_8, () => { - globalApp.setLayout(7); - }], - [SETTINGS.PRESET_RESIZE_9, () => { - globalApp.setLayout(8); - }], - [SETTINGS.PRESET_RESIZE_10, () => { - globalApp.setLayout(9); - }], - [SETTINGS.PRESET_RESIZE_11, () => { - globalApp.setLayout(10); - }], - [SETTINGS.PRESET_RESIZE_12, () => { - globalApp.setLayout(11); - }], - [SETTINGS.PRESET_RESIZE_13, () => { - globalApp.setLayout(12); - }], - [SETTINGS.PRESET_RESIZE_14, () => { - - }], - [SETTINGS.PRESET_RESIZE_15, () => { - - }], - [SETTINGS.PRESET_RESIZE_16, () => { - - }], - [SETTINGS.PRESET_RESIZE_17, () => { - - }], - [SETTINGS.PRESET_RESIZE_18, () => { - - }], - [SETTINGS.PRESET_RESIZE_19, () => { - - }], - [SETTINGS.PRESET_RESIZE_20, () => { - - }], - [SETTINGS.PRESET_RESIZE_21, () => { - - }], - [SETTINGS.PRESET_RESIZE_22, () => { - - }], - [SETTINGS.PRESET_RESIZE_23, () => { - - }], - [SETTINGS.PRESET_RESIZE_24, () => { - - }], - [SETTINGS.PRESET_RESIZE_25, () => { - - }], - [SETTINGS.PRESET_RESIZE_26, () => { - - }], - [SETTINGS.PRESET_RESIZE_27, () => { - - }], - [SETTINGS.PRESET_RESIZE_28, () => { - - }], - [SETTINGS.PRESET_RESIZE_29, () => { - - }], - [SETTINGS.PRESET_RESIZE_30, () => { - - }], -]); - -const keyBindingGlobalResizes: Bindings = new Map([ - -]); - -class App { +export default class App extends Extension { + private metadata: any; + private settings: SettingsObject | null = null; + private indicator: PanelMenu.Button; private editor: (ZoneEditor | null)[]; private preview: (ZonePreview | null)[]; private tabManager: (ZoneManager | null)[]; @@ -200,6 +85,12 @@ class App { private layoutsUtils: LayoutsUtils; private isGrabbing: boolean = false; private minimizedWindows: Window[]; + private isEnabled: boolean = false; + private monitorsChangedConnect: any = false; + private restackConnection: any; + private workspaceSwitchedConnect: any; + private workareasChangedConnect: any; + private currentLayoutIdxPerMonitor: number[]; public layouts: LayoutsSettings = { @@ -221,21 +112,132 @@ class App { ] }; - constructor() { + + private keyBindings: Bindings = new Map([ + [SETTINGS.MOVE_FOCUSED_UP, () => { + this.moveFocusedWindow(MoveDirection.Up) + }], + [SETTINGS.MOVE_FOCUSED_DOWN, () => { + this.moveFocusedWindow(MoveDirection.Down) + }], + [SETTINGS.MOVE_FOCUSED_LEFT, () => { + this.moveFocusedWindow(MoveDirection.Left) + }], + [SETTINGS.MOVE_FOCUSED_RIGHT, () => { + this.moveFocusedWindow(MoveDirection.Right) + }], + ]); + + private key_bindings_presets: Bindings = new Map([ + [SETTINGS.PRESET_RESIZE_1, () => { + this.setLayout(0); + }], + [SETTINGS.PRESET_RESIZE_2, () => { + this.setLayout(1); + }], + [SETTINGS.PRESET_RESIZE_3, () => { + this.setLayout(2); + }], + [SETTINGS.PRESET_RESIZE_4, () => { + this.setLayout(3); + }], + [SETTINGS.PRESET_RESIZE_5, () => { + this.setLayout(4); + }], + [SETTINGS.PRESET_RESIZE_6, () => { + this.setLayout(5); + }], + [SETTINGS.PRESET_RESIZE_7, () => { + this.setLayout(6); + }], + [SETTINGS.PRESET_RESIZE_8, () => { + this.setLayout(7); + }], + [SETTINGS.PRESET_RESIZE_9, () => { + this.setLayout(8); + }], + [SETTINGS.PRESET_RESIZE_10, () => { + this.setLayout(9); + }], + [SETTINGS.PRESET_RESIZE_11, () => { + this.setLayout(10); + }], + [SETTINGS.PRESET_RESIZE_12, () => { + this.setLayout(11); + }], + [SETTINGS.PRESET_RESIZE_13, () => { + this.setLayout(12); + }], + [SETTINGS.PRESET_RESIZE_14, () => { + + }], + [SETTINGS.PRESET_RESIZE_15, () => { + + }], + [SETTINGS.PRESET_RESIZE_16, () => { + + }], + [SETTINGS.PRESET_RESIZE_17, () => { + + }], + [SETTINGS.PRESET_RESIZE_18, () => { + + }], + [SETTINGS.PRESET_RESIZE_19, () => { + + }], + [SETTINGS.PRESET_RESIZE_20, () => { + + }], + [SETTINGS.PRESET_RESIZE_21, () => { + + }], + [SETTINGS.PRESET_RESIZE_22, () => { + + }], + [SETTINGS.PRESET_RESIZE_23, () => { + + }], + [SETTINGS.PRESET_RESIZE_24, () => { + + }], + [SETTINGS.PRESET_RESIZE_25, () => { + + }], + [SETTINGS.PRESET_RESIZE_26, () => { + + }], + [SETTINGS.PRESET_RESIZE_27, () => { + + }], + [SETTINGS.PRESET_RESIZE_28, () => { + + }], + [SETTINGS.PRESET_RESIZE_29, () => { + + }], + [SETTINGS.PRESET_RESIZE_30, () => { + + }], + ]); + + private keyBindingGlobalResizes: Bindings = new Map([ + + ]); + + constructor(metadata: any) { + super(metadata); const monitors = activeMonitors().length; this.editor = new Array(monitors); this.preview = new Array(monitors); this.tabManager = new Array(monitors); this.currentLayoutIdxPerMonitor = new Array(monitors); this.modifiersManager = new ModifiersManager(); - this.layoutsUtils = new LayoutsUtils(); + this.layoutsUtils = new LayoutsUtils(super.path); this.minimizedWindows = new Array(); + this.metadata = metadata; } - private restackConnection: any; - private workspaceSwitchedConnect: any; - private workareasChangedConnect: any; - setLayout(layoutIndex: number, monitorIndex = -1) { if (this.layouts.definitions.length <= layoutIndex) { return; @@ -296,6 +298,11 @@ class App { } enable() { + this.settings = super.getSettings() as SettingsObject; + initSettings(this.settings, () => this.changed_settings()); + log("Extension enable begin"); + SHELL_VERSION.print_version(); + this.layouts = this.layoutsUtils.loadLayoutSettings(); log(JSON.stringify(this.layouts)); if (this.refreshLayouts()) { @@ -303,7 +310,7 @@ class App { } this.setToCurrentWorkspace(); - monitorsChangedConnect = Main.layoutManager.connect( + this.monitorsChangedConnect = Main.layoutManager.connect( 'monitors-changed', () => { activeMonitors().forEach(m => { this.tabManager[m.index]?.layoutWindows(); @@ -444,28 +451,32 @@ class App { }); }); - launcher = new GSnapStatusButton('tiling-icon') as GSnapStatusButtonClass; - launcher.label = "Layouts"; - if (gridSettings[SETTINGS.SHOW_ICON]) { - Main.panel.addToStatusArea("GSnapStatusButton", launcher); - this.reloadMenu(); - } + this.createIndicator() - bindHotkeys(keyBindings); + bindHotkeys(this.keyBindings, this.settings); if (gridSettings[SETTINGS.GLOBAL_PRESETS]) { - bindHotkeys(key_bindings_presets); + bindHotkeys(this.key_bindings_presets, this.settings); } if (gridSettings[SETTINGS.MOVERESIZE_ENABLED]) { - bindHotkeys(keyBindingGlobalResizes); + bindHotkeys(this.keyBindingGlobalResizes, this.settings); } this.modifiersManager.enable(); - enabled = true; + this.isEnabled = true; log("Extension enable completed"); } + changed_settings() { + log("changed_settings"); + if (this.isEnabled) { + this.disable(); + this.enable(); + } + log("changed_settings complete"); + } + refreshLayouts(): boolean { let changed = false; @@ -592,9 +603,22 @@ class App { this.minimizedWindows = []; } + createIndicator() { + this.indicator = new PanelMenu.Button(0.0, this.metadata.name, false); + this.indicator.label = "Layouts"; + let icon = new St.Icon({ style_class: 'tiling-icon' }); + icon.gicon = Gio.icon_new_for_string(`${super.path}/images/tray.svg`); + this.indicator.add_child(icon); + + if (gridSettings[SETTINGS.SHOW_ICON]) { + Main.panel.addToStatusArea("GSnapStatusButton", this.indicator); + this.reloadMenu(); + } + } + reloadMenu() { - if (launcher == null) return; - launcher.menu.removeAll(); + if (this.indicator == null) return; + this.indicator.menu.removeAll(); let resetLayoutButton = new PopupMenu.PopupMenuItem(_("Reset Layout")); let editLayoutButton = new PopupMenu.PopupMenuItem(_("Edit Layout")); let saveLayoutButton = new PopupMenu.PopupMenuItem(_("Save Layout")); @@ -607,35 +631,35 @@ class App { let currentMonitorIndex = getCurrentMonitorIndex(); if (this.editor[currentMonitorIndex] != null) { - launcher.menu.addMenuItem(resetLayoutButton); - launcher.menu.addMenuItem(saveLayoutButton); - launcher.menu.addMenuItem(cancelEditingButton); + this.indicator.menu.addMenuItem(resetLayoutButton); + this.indicator.menu.addMenuItem(saveLayoutButton); + this.indicator.menu.addMenuItem(cancelEditingButton); } else { const monitorsCount = activeMonitors().length; for (let mI = 0; mI < monitorsCount; mI++) { if (monitorsCount > 1) { let monitorName = new PopupMenu.PopupSubMenuMenuItem(_(`Monitor ${mI}`)); - launcher.menu.addMenuItem(monitorName); + this.indicator.menu.addMenuItem(monitorName); this.createLayoutMenuItems(mI).forEach(i => (monitorName).menu.addMenuItem(i)); } else { this.createLayoutMenuItems(mI).forEach(i => - launcher?.menu.addMenuItem(i)); + this.indicator?.menu.addMenuItem(i)); } } let sep = new PopupMenu.PopupSeparatorMenuItem(); - launcher.menu.addMenuItem(sep); - launcher.menu.addMenuItem(editLayoutButton); - launcher.menu.addMenuItem(renameLayoutButton); - launcher.menu.addMenuItem(newLayoutButton); + this.indicator.menu.addMenuItem(sep); + this.indicator.menu.addMenuItem(editLayoutButton); + this.indicator.menu.addMenuItem(renameLayoutButton); + this.indicator.menu.addMenuItem(newLayoutButton); // Add an entry-point for more settings - launcher.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - const settingsButton = launcher.menu.addAction('Settings', - () => ExtensionUtils.openPrefs()); - launcher.menu.addMenuItem(settingsButton); + this.indicator.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + const settingsButton = this.indicator.menu.addAction('Settings', + () => super.openPreferences()); + this.indicator.menu.addMenuItem(settingsButton); } @@ -772,7 +796,9 @@ class App { disable() { log("Extension disable begin"); - enabled = false; + deinitSettings(); + this.settings = null; + this.isEnabled = false; this.modifiersManager.destroy(); this.preview?.forEach(p => { p?.destroy(); p = null }); this.editor?.forEach(e => { e?.destroy(); e = null; }); @@ -786,10 +812,10 @@ class App { global.display.disconnect(this.restackConnection); this.restackConnection = false; } - if (monitorsChangedConnect) { + if (this.monitorsChangedConnect) { log("Disconnecting monitors-changed"); - Main.layoutManager.disconnect(monitorsChangedConnect); - monitorsChangedConnect = false; + Main.layoutManager.disconnect(this.monitorsChangedConnect); + this.monitorsChangedConnect = false; } if (this.workareasChangedConnect) { @@ -797,12 +823,12 @@ class App { this.workareasChangedConnect = false; } - unbindHotkeys(keyBindings); - unbindHotkeys(key_bindings_presets); - unbindHotkeys(keyBindingGlobalResizes); + unbindHotkeys(this.keyBindings); + unbindHotkeys(this.key_bindings_presets); + unbindHotkeys(this.keyBindingGlobalResizes); - launcher?.destroy(); - launcher = null; + this.indicator?.destroy(); + this.indicator = null; } @@ -811,8 +837,6 @@ class App { */ onFocus() { } - showMenu() { } - private setToCurrentWorkspace() { let currentWorkspaceIdx = WorkspaceManager.get_active_workspace().index(); activeMonitors().forEach(m => { @@ -821,50 +845,3 @@ class App { }); } } - -const globalApp = new App(); - -class GSnapStatusButtonClass extends PanelMenu.Button { - _init() { - super._init(0.0, "gSnap", false); - - this._icon = new St.Icon({ style_class: 'tiling-icon' }); - this._icon.gicon = Gio.icon_new_for_string(`${Me.path}/images/tray.svg`); - this.add_actor(this._icon); - this.connect('button-press-event', this._onButtonPress); - log("GSnapStatusButton _init done"); - } - - - _onButtonPress(_actor: any, _event: any) { - log(`_onButtonPress Click Toggle Status on system panel ${this}`); - globalApp.showMenu(); - } -} - -const GSnapStatusButton = GObject.registerClass({ - GTypeName: 'GSnapStatusButton', -}, GSnapStatusButtonClass -); - -function changed_settings() { - log("changed_settings"); - if (enabled) { - disable(); - enable(); - } - log("changed_settings complete"); -} - -export function enable() { - initSettings(changed_settings); - log("Extension enable begin"); - SHELL_VERSION.print_version(); - - globalApp.enable(); -} - -export function disable() { - deinitSettings(); - globalApp.disable(); -} diff --git a/src/extension/editor.ts b/src/extension/editor.ts index ba8aea7..29a62bf 100644 --- a/src/extension/editor.ts +++ b/src/extension/editor.ts @@ -1,6 +1,17 @@ -// GJS import system -declare var imports: any; declare var global: any; +// @ts-ignore +import St from 'gi://St'; +// @ts-ignore +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +// @ts-ignore +import GLib from 'gi://GLib'; +// @ts-ignore +import GObject from 'gi://GObject'; +// @ts-ignore +import Clutter from 'gi://Clutter'; +// @ts-ignore +import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js'; + import { log } from './logging'; import { @@ -15,14 +26,6 @@ import { areEqual, getWorkAreaByMonitor, getTrackedWindowsOfMonitor, Monitor, Wo import { Layout, LayoutItem } from './layouts'; -// Library imports -const St = imports.gi.St; -const Main = imports.ui.main; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; -const Clutter = imports.gi.Clutter; -const ModalDialog = imports.ui.modalDialog; - const ANIMATION_SPEED = 100; // animation speed of zones' fade-in, fade-out, position and size changes export class ZoneBase { @@ -1098,22 +1101,17 @@ class EntryDialogClass extends ModalDialog.ModalDialog { public _init() { super._init({}); - this.setButtons([{ + super.setButtons([{ label: "OK", action: () => { this.onOkay(this.entry.text); - this.close(global.get_current_time()); + super.close(global.get_current_time()); }, key: Clutter.Escape }]); let box = new St.BoxLayout({ vertical: true }); - this.contentLayout.add(box); - - // const MySelf = ExtensionUtils.getCurrentExtension(); - // let gicon = new Gio.FileIcon({file: Gio.file_new_for_path(MySelf.path + "/icons/icon.png")}); - // let icon = new St.Icon({gicon: gicon}); - // box.add(icon); + super.contentLayout.add(box); this.label = new St.Label({ text: "" }); box.add(this.label); diff --git a/src/extension/hotkeys.ts b/src/extension/hotkeys.ts index 515c7b9..eb2475d 100644 --- a/src/extension/hotkeys.ts +++ b/src/extension/hotkeys.ts @@ -5,13 +5,13 @@ declare const imports: any; declare const global: any; // Library imports -const Main = imports.ui.main; -const Meta = imports.gi.Meta; -const Shell = imports.gi.Shell; +// @ts-ignore +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +// @ts-ignore +import Meta from 'gi://Meta'; +// @ts-ignore +import Shell from 'gi://Shell'; -// Extension imports -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const ExtensionUtils = imports.misc.extensionUtils; /** * Bindings is a dictionary that maps a hotkey name to a function that handles * the press of the key that is bound to that action. @@ -20,9 +20,7 @@ export type Bindings = Map void>; export type BindingsOld = {[name in KeyBindingSettingName]: () => void}; -export function bind(keyBindings: Bindings) { - // Globals - let settings = ExtensionUtils.getSettings(); +export function bind(keyBindings: Bindings, settings: any) { log("Binding keys"); keyBindings.forEach((callback: () => void, key: KeyBindingSettingName) => { //const key = keyString as KeyBindingSettingName; diff --git a/src/extension/layouts_utils.ts b/src/extension/layouts_utils.ts index 7a49410..43b3a1a 100644 --- a/src/extension/layouts_utils.ts +++ b/src/extension/layouts_utils.ts @@ -1,13 +1,14 @@ +// @ts-ignore +import GLib from 'gi://GLib'; + import { LayoutsSettings } from "./layouts"; import { log } from "./logging"; -import { ShellVersion } from "./shellversion"; - -// GJS import system -declare var imports: any; -const GLib = imports.gi.GLib; -const Me = imports.misc.extensionUtils.getCurrentExtension(); export class LayoutsUtils { + constructor(private basePath: string) { + + } + get configPath() { return GLib.build_pathv('/', [GLib.get_user_config_dir(), 'gSnap']); } @@ -50,7 +51,7 @@ export class LayoutsUtils { } private _loadLayoutsV1FromExtensionDir(): LayoutsSettings | null { - const oldLayoutsPath = GLib.build_filenamev([Me.path, 'layouts.json']); + const oldLayoutsPath = GLib.build_filenamev([this.basePath, 'layouts.json']); return this._loadFromJsonFile(oldLayoutsPath); } @@ -63,16 +64,8 @@ export class LayoutsUtils { if (ok) { log(`Found in ${filePath}`); - - let contentsString = ''; - if(ShellVersion.defaultVersion().version_at_least_41()) { - const decoder = new TextDecoder('utf-8'); - contentsString = decoder.decode(contents); - } else { - const ByteArray = imports.byteArray; - contentsString = ByteArray.toString(contents); - } - + const decoder = new TextDecoder('utf-8'); + let contentsString = decoder.decode(contents); return JSON.parse(contentsString); } } catch (exception) { diff --git a/src/extension/logging.ts b/src/extension/logging.ts index 793a233..89f8a8e 100644 --- a/src/extension/logging.ts +++ b/src/extension/logging.ts @@ -4,10 +4,6 @@ let debug: boolean = false; -declare const global: { - log(message: string): void -}; - /** * If called with a false argument, log statements are suppressed. */ @@ -24,6 +20,6 @@ export function setLoggingEnabled(enabled: boolean): void { */ export function log(message: string): void { if(debug) { - global.log("gSnap " + message); + console.warn("gSnap " + message); } } diff --git a/src/extension/modifiers.ts b/src/extension/modifiers.ts index 0a2666a..e34ebc4 100644 --- a/src/extension/modifiers.ts +++ b/src/extension/modifiers.ts @@ -1,9 +1,9 @@ -declare var imports: any; declare var global: any; import { log } from './logging'; -const GLib = imports.gi.GLib; +// @ts-ignore +import GLib from 'gi://GLib'; export enum MODIFIERS_ENUM { SHIFT, diff --git a/src/extension/monitors.ts b/src/extension/monitors.ts index d9d58a2..2c47749 100644 --- a/src/extension/monitors.ts +++ b/src/extension/monitors.ts @@ -2,7 +2,8 @@ declare var imports: any; declare var global: any; -const Main = imports.ui.main; +// @ts-ignore +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import { Window, WindowType, diff --git a/src/extension/prefs_builder.ts b/src/extension/prefs_builder.ts index d57b96a..2999861 100644 --- a/src/extension/prefs_builder.ts +++ b/src/extension/prefs_builder.ts @@ -1,8 +1,13 @@ -// Library imports -declare var imports: any; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Gio = imports.gi.Gio; +// @ts-ignore +import Gio from 'gi://Gio'; +// @ts-ignore +import Gtk from 'gi://Gtk'; +// @ts-ignore +import GObject from 'gi://GObject'; +// @ts-ignore +import Adw from 'gi://Adw'; +// @ts-ignore +import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; import * as SETTINGS from "./settings_data"; @@ -26,33 +31,100 @@ const pretty_names = { [SETTINGS.MOVE_FOCUSED_RIGHT]: 'Move focused window right', } -function set_child(widget: any, child: any) { - if (Gtk.get_major_version() >= 4) { - widget.set_child(child); - } else { - widget.add(child); +export default class GSnapPreferences extends ExtensionPreferences { + private settings: any; + + fillPreferencesWindow(window: any) { + this.settings = super.getSettings(); + window._settings = this.settings; + + const preferencesPage = new Adw.PreferencesPage({ + title: "Preferences", + icon_name: "emblem-system-symbolic" + }); + preferencesPage.add(this.basics()); + preferencesPage.add(this.miscSettings()); + window.add(preferencesPage); + + const shortcutsPage = new Adw.PreferencesPage({ + title: "Shortcuts", + icon_name: "preferences-desktop-keyboard-symbolic" + }); + shortcutsPage.add(this.shortcuts()); + window.add(shortcutsPage); + + const aboutPage = new Adw.PreferencesPage({ + title: "About", + icon_name: "help-about-symbolic" + }); + aboutPage.add(this.about()); + aboutPage.add(this.aboutLinks()); + window.add(aboutPage); } -} -function box_append(box: any, child: any) { - if (Gtk.get_major_version() >= 4) { - box.append(child); - } else { - box.add(child); + basics() { + const group = new Adw.PreferencesGroup({ + title: 'Basics' + }); + + this.add_check(group, SETTINGS.SHOW_ICON, "Show icon"); + const show_tabs_check = this.add_check(group, SETTINGS.SHOW_TABS, + "Show tabs", + "This feature is not supported if you have the \"Hold ALT to span multiple zones\" feature enabled"); + + this.add_check(group, SETTINGS.MOVERESIZE_ENABLED, "Enable accelerators for moving and resizing windows"); + this.add_check(group, SETTINGS.USE_MODIFIER, "Hold CTRL to snap windows"); + const span_multiple_zones_check = this.add_check(group, SETTINGS.SPAN_MULTIPLE_ZONES, + "Hold ALT to span multiple zones", + "This feature is not supported if you have the \"Show tabs\" feature enabled"); + + // disable "Span multiple zones" setting if "Show tabs" setting was already enabled + if (this.settings.get_boolean(SETTINGS.SHOW_TABS)) { + span_multiple_zones_check.set_sensitive(false); + // disable "Show tabs" setting if "Span multiple zones" setting was already enabled + } else if (this.settings.get_boolean(SETTINGS.SPAN_MULTIPLE_ZONES)) { + show_tabs_check.set_sensitive(false); + } + + // Watch for changes to the setting SETTINGS.SHOW_TABS + this.settings.connect(`changed::${SETTINGS.SHOW_TABS}`, (settings: any, changed_key: string) => { + // disable "Span multiple zones" setting if "Show tabs" setting is enabled by the user + span_multiple_zones_check.set_sensitive(!settings.get_boolean(changed_key)); + }); + + // Watch for changes to the setting SETTINGS.SPAN_MULTIPLE_ZONES + this.settings.connect(`changed::${SETTINGS.SPAN_MULTIPLE_ZONES}`, (settings: any, changed_key: string) => { + // disable "Show tabs" setting if "Span multiple zones" setting is enabled by the user + show_tabs_check.set_sensitive(!settings.get_boolean(changed_key)); + }); + + this.add_check(group, SETTINGS.ANIMATIONS_ENABLED, "Enable animations"); + + this.add_int(group, SETTINGS.WINDOW_MARGIN, + "Window margin", + 0, 32, 1, + "Window margins and invisible borders around screen.", + ); + + return group; } -} -class PrefsBuilder { - accel_tab(notebook: any) { - let settings = imports.misc.extensionUtils.getSettings(); - let ks_grid = new Gtk.Grid({ - column_spacing: 10, - orientation: Gtk.Orientation.VERTICAL, - row_spacing: 10, + miscSettings() { + const group = new Adw.PreferencesGroup({ + title: 'Miscellaneous' }); - ks_grid.set_margin_start(24); - ks_grid.set_margin_top(24); + this.add_check(group, SETTINGS.DEBUG, + "Debug", + "To see debug messages, in terminal run journalctl /usr/bin/gnome-shell -f"); + + return group; + } + + shortcuts() { + const group = new Adw.PreferencesGroup({ + title: 'Shortcuts' + }); let model = new Gtk.ListStore(); @@ -64,35 +136,25 @@ class PrefsBuilder { ]); for (let key in pretty_names) { - this.append_hotkey(model, settings, key, (pretty_names as any)[key]); + this.add_hotkey(model, key, (pretty_names as any)[key]); } - let treeview = new Gtk.TreeView({ - 'model': model, - 'hexpand': true - }); - - let col; - let cellrend; - - cellrend = new Gtk.CellRendererText(); - - col = new Gtk.TreeViewColumn({ - 'title': 'Keybinding', - 'expand': true + const titleRenderer = new Gtk.CellRendererText(); + const titleColumn = new Gtk.TreeViewColumn({ + title: 'Shortcut', + expand: true }); - col.pack_start(cellrend, true); - col.add_attribute(cellrend, 'text', 1); + titleColumn.pack_start(titleRenderer, true); + titleColumn.add_attribute(titleRenderer, 'text', 1); - treeview.append_column(col); - - cellrend = new Gtk.CellRendererAccel({ - 'editable': true, + const keybindingsCellRenderer = new Gtk.CellRendererAccel({ + editable: true, 'accel-mode': Gtk.CellRendererAccelMode.GTK }); - cellrend.connect('accel-cleared', function (_rend: any, str_iter: string) { + const _settings = this.settings as any; + keybindingsCellRenderer.connect('accel-cleared', function (_rend: any, str_iter: string) { let [success, iter] = model.get_iter_from_string(str_iter); if (!success) { @@ -101,297 +163,140 @@ class PrefsBuilder { let name = model.get_value(iter, 0); model.set(iter, [3], [0]); - settings.set_strv(name, ['']); + _settings.set_strv(name, ['']); }); - cellrend.connect('accel-edited', function (rend: any, str_iter: string, key: any, mods: any) { + keybindingsCellRenderer.connect('accel-edited', function (rend: any, str_iter: string, key: any, mods: any) { let value = Gtk.accelerator_name(key, mods); - - let [success, iter] = model.get_iter_from_string(str_iter); - - if (!success) { throw new Error("Something be broken, yo."); } let name = model.get_value(iter, 0); - model.set(iter, [2, 3], [mods, key]); - - settings.set_strv(name, [value]); + _settings.set_strv(name, [value]); }); - col = new Gtk.TreeViewColumn({ - 'title': 'Accel' + const keybindingsColumn = new Gtk.TreeViewColumn({ + title: 'Keybindings' }); + keybindingsColumn.pack_end(keybindingsCellRenderer, false); + keybindingsColumn.add_attribute(keybindingsCellRenderer, 'accel-mods', 2); + keybindingsColumn.add_attribute(keybindingsCellRenderer, 'accel-key', 3); - col.pack_end(cellrend, false); - col.add_attribute(cellrend, 'accel-mods', 2); - col.add_attribute(cellrend, 'accel-key', 3); - - treeview.append_column(col); - - let text = "Keyboard shortcuts. Arrows are used to move window and are not re-assignable."; - ks_grid.attach_next_to(new Gtk.Label({ - label: text, - halign: Gtk.Align.START, - justify: Gtk.Justification.LEFT, - use_markup: false, - wrap: true, - }), null, Gtk.PositionType.BOTTOM, 1, 1); - ks_grid.attach_next_to(treeview, null, Gtk.PositionType.BOTTOM, 1, 1); - - let ks_window = new Gtk.ScrolledWindow({ 'vexpand': true }); - set_child(ks_window, ks_grid) - let ks_label = new Gtk.Label({ - label: "Accelerators", - halign: Gtk.Align.START, - use_markup: false, + const treeview = new Gtk.TreeView({ + model, + hexpand: true }); - notebook.append_page(ks_window, ks_label); + treeview.append_column(titleColumn); + treeview.append_column(keybindingsColumn); + group.add(treeview); + return group; } - basics_tab(notebook: any) { - let settings = imports.misc.extensionUtils.getSettings(); + about() { + const group = new Adw.PreferencesGroup(); - let bs_grid = new Gtk.Grid({ - column_spacing: 10, - orientation: Gtk.Orientation.VERTICAL, - row_spacing: 10, + var logo = new Gtk.Image({ + width_request: 150, + height_request: 150, + }); + logo.set_from_file(`${super.path}/images/icon.png`); + + const nameLabel = new Gtk.Label({ + label: `gSnap`, + xalign: .5, + margin_top: 10, + margin_bottom: 5, + hexpand: true, + halign: Gtk.Align.CENTER, + use_markup: true }); - bs_grid.set_margin_start(24); - bs_grid.set_margin_top(24); - - - this.add_check("Show icon", SETTINGS.SHOW_ICON, bs_grid, settings); - const show_tabs_check = this.add_check("Show tabs", SETTINGS.SHOW_TABS, bs_grid, settings); - this.add_label( - "This feature is not supported if you have the \"Hold ALT to span multiple zones\" feature enabled", - bs_grid); - this.add_check("Enable accelerators for moving and resizing windows", SETTINGS.MOVERESIZE_ENABLED, bs_grid, settings); - this.add_check("Hold CTRL to snap windows", SETTINGS.USE_MODIFIER, bs_grid, settings); - const span_multiple_zones_check = this.add_check("Hold ALT to span multiple zones", SETTINGS.SPAN_MULTIPLE_ZONES, bs_grid, settings); - this.add_label( - "This feature is not supported if you have the \"Show tabs\" feature enabled", - bs_grid); - // disable "Span multiple zones" setting if "Show tabs" setting was already enabled - if (settings.get_boolean(SETTINGS.SHOW_TABS)) { - span_multiple_zones_check.set_sensitive(false); - // disable "Show tabs" setting if "Span multiple zones" setting was already enabled - } else if (settings.get_boolean(SETTINGS.SPAN_MULTIPLE_ZONES)) { - show_tabs_check.set_sensitive(false); - } - - this.add_check("Enable animations", SETTINGS.ANIMATIONS_ENABLED, bs_grid, settings); - - this.add_check("Debug", SETTINGS.DEBUG, bs_grid, settings); - this.add_label("To see debug messages, in terminal run journalctl /usr/bin/gnome-shell -f", bs_grid); - let bs_window = new Gtk.ScrolledWindow({ 'vexpand': true }); - set_child(bs_window, bs_grid); - let bs_label = new Gtk.Label({ - label: "Basic", - halign: Gtk.Align.START, - use_markup: false, - }); - notebook.append_page(bs_window, bs_label); - - // Watch for changes to the setting SETTINGS.SHOW_TABS - settings.connect(`changed::${SETTINGS.SHOW_TABS}`, (settings: any, changed_key: string) => { - // disable "Span multiple zones" setting if "Show tabs" setting is enabled by the user - span_multiple_zones_check.set_sensitive(!settings.get_boolean(changed_key)); + const descriptionLabel = new Gtk.Label({ + label: `Organize windows in customizable snap zones like FancyZones on Windows.`, + xalign: .5, + margin_bottom: 20, + hexpand: true, + halign: Gtk.Align.CENTER, + use_markup: true }); - // Watch for changes to the setting SETTINGS.SPAN_MULTIPLE_ZONES - settings.connect(`changed::${SETTINGS.SPAN_MULTIPLE_ZONES}`, (settings: any, changed_key: string) => { - // disable "Show tabs" setting if "Span multiple zones" setting is enabled by the user - show_tabs_check.set_sensitive(!settings.get_boolean(changed_key)); - }); + group.add(logo); + group.add(nameLabel); + group.add(descriptionLabel); + return group; } - margins_tab(notebook: any) { - let settings = imports.misc.extensionUtils.getSettings(); - let mg_grid = new Gtk.Grid({ - column_spacing: 10, - orientation: Gtk.Orientation.VERTICAL, - row_spacing: 10, + aboutLinks() { + const group = new Adw.PreferencesGroup({ + title: 'Useful links' }); - mg_grid.set_margin_start(24); - mg_grid.set_margin_top(24); - - let text = "Window margins and invisible borders around screen."; - mg_grid.attach_next_to(new Gtk.Label({ - label: text, - halign: Gtk.Align.START, - justify: Gtk.Justification.LEFT, - use_markup: false, - wrap: true, - }), null, Gtk.PositionType.BOTTOM, 1, 1) - - this.add_int("Window margin", SETTINGS.WINDOW_MARGIN, mg_grid, settings, 0, 240, 1, 10); - - let mg_window = new Gtk.ScrolledWindow({ 'vexpand': true }); - set_child(mg_window, mg_grid); - let mg_label = new Gtk.Label({ - label: "Margins", - halign: Gtk.Align.START, - use_markup: false, - }); - notebook.append_page(mg_window, mg_label); + this.add_linkbutton( + group, + 'Contribute to the project', + 'https://github.com/GnomeSnapExtensions/gSnap'); + this.add_linkbutton( + group, + 'Report a bug', + 'https://github.com/GnomeSnapExtensions/gSnap/issues'); + + return group; } - help_tab(notebook: any) { - let weblink = 'https://github.com/micahosborne/gSnap/blob/master/README.md'; - let hl_link = new Gtk.LinkButton({ - label: weblink, - uri: weblink, - halign: Gtk.Align.CENTER, + add_check(group: any, setting: string, title: string, subtitle: string | null = null) { + const toggle = new Gtk.Switch({ + active: this.settings.get_boolean(setting), valign: Gtk.Align.CENTER, }); - let hl_label = new Gtk.Label({ - label: "Help", - halign: Gtk.Align.START, - use_markup: false, - }); - notebook.append_page(hl_link, hl_label); - } - - public build() { - let notebook = new Gtk.Notebook(); + this.settings.bind(setting, toggle, 'active', Gio.SettingsBindFlags.DEFAULT); - this.basics_tab(notebook); - this.accel_tab(notebook); - //presets_tab(notebook); - this.margins_tab(notebook); - this.help_tab(notebook); - - let main_vbox = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - spacing: 10 + const row = new Adw.ActionRow({ + title, + subtitle, + activatable_widget: toggle, }); - - if (Gtk.get_major_version() >= 4) { - main_vbox.prepend(notebook, true, true, 0); - } else { - main_vbox.pack_start(notebook, true, true, 0); - main_vbox.show_all(); - } - - return main_vbox; - } - - add_check(check_label: string, SETTINGS: string, grid: any, settings: any) { - let check = new Gtk.CheckButton({ label: check_label, margin_top: 6 }); - settings.bind(SETTINGS, check, 'active', Gio.SettingsBindFlags.DEFAULT); - grid.attach_next_to(check, null, Gtk.PositionType.BOTTOM, 1, 1); - return check; + row.add_suffix(toggle); + group.add(row); + return toggle; } - add_int(int_label: string, SETTINGS: string, grid: any, settings: any, minv: number, maxv: number, incre: number, page: number) { - let item = new IntSelect(int_label); - item.set_args(minv, maxv, incre, page); - settings.bind(SETTINGS, item.spin, 'value', Gio.SettingsBindFlags.DEFAULT); - grid.attach_next_to(item.actor, null, Gtk.PositionType.BOTTOM, 1, 1); - } - - add_text(text_label: string, SETTINGS: string, grid:any, settings:any, width: number) { - let item = new TextEntry(text_label); - item.set_args(width); - settings.bind(SETTINGS, item.textentry, 'text', Gio.SettingsBindFlags.DEFAULT); - grid.attach_next_to(item.actor, null, Gtk.PositionType.BOTTOM, 1, 1); - } + add_int(group: any, setting: string, title: string, lower: number, upper: number, step_increment: number, subtitle: string | null = null) { + const spin = Gtk.SpinButton.new_with_range(lower, upper, step_increment); + spin.set_valign(Gtk.Align.CENTER); + this.settings.bind(setting, spin.get_adjustment(), 'value', Gio.SettingsBindFlags.DEFAULT); - add_label(label: string, grid: any) { - let text = "To see debug messages, in terminal run journalctl /usr/bin/gnome-shell -f"; - let gtk_label = new Gtk.Label({ - label: label, - halign: Gtk.Align.START, - justify: Gtk.Justification.LEFT, - use_markup: false, - wrap: true, + const row = new Adw.ActionRow({ + title, + subtitle, + activatableWidget: spin }); - grid.attach_next_to(gtk_label, null, Gtk.PositionType.BOTTOM, 1, 1); - - return gtk_label; + row.add_suffix(spin); + group.add(row); } - append_hotkey(model: any, settings: any, name: string, pretty_name: string) { - let _ok, key, mods; - - if (Gtk.get_major_version() >= 4) { - // ignore ok as failure treated as disabled - [_ok, key, mods] = Gtk.accelerator_parse(settings.get_strv(name)[0]); - } else { - [key, mods] = Gtk.accelerator_parse(settings.get_strv(name)[0]); - } - - let row = model.insert(-1); - - model.set(row, [0, 1, 2, 3], [name, pretty_name, mods, key]); - } -} - -// grabbed from sysmonitor code -class IntSelect { - label: any; //Gtk.Label - spin: any; //Gtk.SpinButton - actor: any; //Gtk.Box - - constructor(name: string) { - this.label = new Gtk.Label({ - label: name + ":", - halign: Gtk.Align.START + add_linkbutton(group: any, title: string, uri: string) { + let button = new Gtk.LinkButton({ + uri: uri, + halign: Gtk.Align.CENTER, + valign: Gtk.Align.CENTER, }); - this.spin = new Gtk.SpinButton({ - halign: Gtk.Align.END + const row = new Adw.ActionRow({ + title, + activatable_widget: button, }); - this.actor = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 }); - this.actor.set_homogeneous(true); - box_append(this.actor, this.label) - box_append(this.actor, this.spin) - this.spin.set_numeric(true); + row.add_suffix(button); + group.add(row); } - set_args(minv: number, maxv: number, incre: number, page: number) { - this.spin.set_range(minv, maxv); - this.spin.set_increments(incre, page); - } + add_hotkey(model: any, name: string, pretty_name: string) { + // ignore ok as failure treated as disabled + const [_ok, key, mods] = Gtk.accelerator_parse(this.settings.get_strv(name)[0]); - set_value(value: number) { - this.spin.set_value(value); - } -} - -class TextEntry { - label: any; //Gtk.Label - textentry: any; //Gtk.Entry - actor: any; //Gtk.Box - - constructor(name: string) { - this.label = new Gtk.Label({ label: name + ":" }); - this.textentry = new Gtk.Entry(); - this.actor = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 }); - this.actor.set_homogeneous(true); - box_append(this.actor, this.label); - box_append(this.actor, this.textentry); - this.textentry.set_text(""); - } - set_args(width: number) { - this.textentry.set_width_chars(width); - } - set_value(value: string) { - this.textentry.set_text(value); + let row = model.insert(-1); + model.set(row, [0, 1, 2, 3], [name, pretty_name, mods, key]); } } - -export function init() { - -} - -export function buildPrefsWidget() { - let builder = new PrefsBuilder(); - return builder.build(); -} diff --git a/src/extension/settings.ts b/src/extension/settings.ts index 313a5fd..23c465b 100644 --- a/src/extension/settings.ts +++ b/src/extension/settings.ts @@ -1,13 +1,10 @@ // GJS import system -declare var imports: any; import * as SETTINGS from './settings_data'; import { log, setLoggingEnabled } from "./logging"; -const ExtensionUtils = imports.misc.extensionUtils; - -export const gridSettings = new SETTINGS.ParsedSettings(); +export var gridSettings: SETTINGS.ParsedSettings; export interface SettingsObject { get_boolean(name: SETTINGS.BoolSettingName): boolean | undefined; @@ -21,11 +18,11 @@ export interface SettingsObject { disconnect(c: any): void; }; -let settings: SettingsObject; -let settingsConnection: any = null; +let _settings: SettingsObject; +let _settingsConnection: any = null; export function getBoolSetting(settingName: SETTINGS.BoolSettingName): boolean { - const value = settings.get_boolean(settingName); + const value = _settings.get_boolean(settingName); if (value === undefined) { log("Undefined settings " + settingName); return false; @@ -34,7 +31,7 @@ export function getBoolSetting(settingName: SETTINGS.BoolSettingName): boolean { } export function getIntSetting(settingsValue: SETTINGS.NumberSettingName) { - let iss = settings.get_int(settingsValue); + let iss = _settings.get_int(settingsValue); if (iss === undefined) { log("Undefined settings " + settingsValue); return 0; @@ -43,12 +40,13 @@ export function getIntSetting(settingsValue: SETTINGS.NumberSettingName) { } } -export function initSettings(changed_settings: () => void) { - settings = ExtensionUtils.getSettings(); - settingsConnection = settings.connect('changed', changed_settings); +export function initSettings(settings: SettingsObject, changed_settings: () => void) { + _settings = settings; + _settingsConnection = _settings.connect('changed', changed_settings); setLoggingEnabled(getBoolSetting(SETTINGS.DEBUG)); log("Init settings"); + gridSettings = new SETTINGS.ParsedSettings(); gridSettings[SETTINGS.SHOW_ICON] = getBoolSetting(SETTINGS.SHOW_ICON); gridSettings[SETTINGS.SHOW_TABS] = getBoolSetting(SETTINGS.SHOW_TABS); gridSettings[SETTINGS.WINDOW_MARGIN] = getIntSetting(SETTINGS.WINDOW_MARGIN); @@ -57,5 +55,5 @@ export function initSettings(changed_settings: () => void) { } export function deinitSettings() { - settings.disconnect(settingsConnection); + _settings.disconnect(_settingsConnection); } diff --git a/src/extension/shellversion.ts b/src/extension/shellversion.ts index 86357c8..d7fb4b9 100644 --- a/src/extension/shellversion.ts +++ b/src/extension/shellversion.ts @@ -4,15 +4,12 @@ import {log} from './logging'; -declare var imports: any; - interface ConfigObject { PACKAGE_VERSION: string; } -function getConfig(): ConfigObject { - return imports.misc.config; -} +// @ts-ignore +import * as Config from 'resource:///org/gnome/shell/misc/config.js'; interface Version { major: number; @@ -49,7 +46,7 @@ export class ShellVersion { } public static defaultVersion(): ShellVersion { - return ShellVersion.parse(getConfig().PACKAGE_VERSION); + return ShellVersion.parse(Config.PACKAGE_VERSION); } public static parse(version: string): ShellVersion { diff --git a/src/images/icon.png b/src/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dad3e5f031029c212e9cb45575e1a6a1dda52cc GIT binary patch literal 48541 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 literal 0 HcmV?d00001 diff --git a/src/metadata.json b/src/metadata.json index f330579..32de04c 100644 --- a/src/metadata.json +++ b/src/metadata.json @@ -2,8 +2,8 @@ "uuid": "gSnap@micahosborne", "name": "gSnap", "description": "Organize windows in customizable snap zones like FancyZones on Windows.", - "version": 15, - "shell-version": ["40", "41", "42", "43", "44"], + "version": 17, + "shell-version": ["45"], "url": "https://github.com/GnomeSnapExtensions/gSnap", "settings-schema": "org.gnome.shell.extensions.gsnap", "gettext-domain": "gsnap@micahosborne"