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 0000000..1dad3e5 Binary files /dev/null and b/src/images/icon.png differ 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"