feat: enable the window manager to be able to drag across monitors and support keybindings propperly in the extension settings
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
/schemas/gschemas.compiled
|
/schemas/gschemas.compiled
|
||||||
/aerospike.zip
|
/aerospike.zip
|
||||||
|
/debug.log
|
||||||
|
67
extension.ts
67
extension.ts
@@ -21,6 +21,7 @@ export default class aerospike extends Extension {
|
|||||||
enable() {
|
enable() {
|
||||||
Logger.log("STARTING AEROSPIKE!")
|
Logger.log("STARTING AEROSPIKE!")
|
||||||
this.bindSettings();
|
this.bindSettings();
|
||||||
|
this.setupKeybindings();
|
||||||
this.windowManager.enable()
|
this.windowManager.enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,24 +33,24 @@ export default class aerospike extends Extension {
|
|||||||
|
|
||||||
private bindSettings() {
|
private bindSettings() {
|
||||||
// Monitor settings changes
|
// Monitor settings changes
|
||||||
this.settings.connect('changed::keybinding-1', () => {
|
this.settings.connect('changed::move-left', () => {
|
||||||
log(`Keybinding 1 changed to: ${this.settings.get_strv('keybinding-1')}`);
|
log(`Keybinding 1 changed to: ${this.settings.get_strv('move-left')}`);
|
||||||
this.refreshKeybinding('keybinding-1');
|
this.refreshKeybinding('move-left');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.connect('changed::keybinding-2', () => {
|
this.settings.connect('changed::move-right', () => {
|
||||||
log(`Keybinding 2 changed to: ${this.settings.get_strv('keybinding-2')}`);
|
log(`Keybinding 2 changed to: ${this.settings.get_strv('move-right')}`);
|
||||||
this.refreshKeybinding('keybinding-2');
|
this.refreshKeybinding('move-right');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.connect('changed::keybinding-3', () => {
|
this.settings.connect('changed::join-with-left', () => {
|
||||||
log(`Keybinding 3 changed to: ${this.settings.get_strv('keybinding-3')}`);
|
log(`Keybinding 3 changed to: ${this.settings.get_strv('join-with-left')}`);
|
||||||
this.refreshKeybinding('keybinding-3');
|
this.refreshKeybinding('join-with-left');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.connect('changed::keybinding-4', () => {
|
this.settings.connect('changed::join-with-right', () => {
|
||||||
log(`Keybinding 4 changed to: ${this.settings.get_strv('keybinding-4')}`);
|
log(`Keybinding 4 changed to: ${this.settings.get_strv('join-with-right')}`);
|
||||||
this.refreshKeybinding('keybinding-4');
|
this.refreshKeybinding('join-with-right');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.connect('changed::dropdown-option', () => {
|
this.settings.connect('changed::dropdown-option', () => {
|
||||||
@@ -67,24 +68,24 @@ export default class aerospike extends Extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (settingName) {
|
switch (settingName) {
|
||||||
case 'keybinding-1':
|
case 'move-left':
|
||||||
this.bindKeybinding('keybinding-1', () => {
|
this.bindKeybinding('move-left', () => {
|
||||||
log('Keybinding 1 was pressed!');
|
Logger.info('Keybinding 1 was pressed!');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'keybinding-2':
|
case 'move-right':
|
||||||
this.bindKeybinding('keybinding-2', () => {
|
this.bindKeybinding('move-right', () => {
|
||||||
log('Keybinding 2 was pressed!');
|
Logger.info('Keybinding 2 was pressed!');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'keybinding-3':
|
case 'join-with-left':
|
||||||
this.bindKeybinding('keybinding-3', () => {
|
this.bindKeybinding('join-with-left', () => {
|
||||||
log('Keybinding 3 was pressed!');
|
Logger.info('Keybinding 3 was pressed!');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'keybinding-4':
|
case 'join-with-right':
|
||||||
this.bindKeybinding('keybinding-4', () => {
|
this.bindKeybinding('join-with-right', () => {
|
||||||
log('Keybinding 4 was pressed!');
|
Logger.info('Keybinding 4 was pressed!');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -98,20 +99,20 @@ export default class aerospike extends Extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setupKeybindings() {
|
private setupKeybindings() {
|
||||||
this.bindKeybinding('keybinding-1', () => {
|
this.bindKeybinding('move-left', () => {
|
||||||
log('Keybinding 1 was pressed!');
|
Logger.info('Keybinding 1 was pressed!');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.bindKeybinding('keybinding-2', () => {
|
this.bindKeybinding('move-right', () => {
|
||||||
log('Keybinding 2 was pressed!');
|
Logger.info('Keybinding 2 was pressed!');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.bindKeybinding('keybinding-3', () => {
|
this.bindKeybinding('join-with-left', () => {
|
||||||
log('Keybinding 3 was pressed!');
|
Logger.info('Keybinding 3 was pressed!');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.bindKeybinding('keybinding-4', () => {
|
this.bindKeybinding('join-with-right', () => {
|
||||||
log('Keybinding 4 was pressed!');
|
Logger.info('Keybinding 4 was pressed!');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ export default class aerospike extends Extension {
|
|||||||
const keyBindingAction = Main.wm.addKeybinding(
|
const keyBindingAction = Main.wm.addKeybinding(
|
||||||
settingName,
|
settingName,
|
||||||
this.settings,
|
this.settings,
|
||||||
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
Meta.KeyBindingFlags.NONE,
|
||||||
Shell.ActionMode.NORMAL,
|
Shell.ActionMode.NORMAL,
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
|
4
prefs.ts
4
prefs.ts
@@ -1,4 +1,4 @@
|
|||||||
// This file is just a wrapper around the compiled TypeScript code
|
// This file is just a wrapper around the compiled TypeScript code
|
||||||
import MyExtensionPreferences from './src/prefs/prefs.js';
|
import AerospikeExtensions from './src/prefs/prefs.js';
|
||||||
|
|
||||||
export default MyExtensionPreferences;
|
export default AerospikeExtensions;
|
@@ -1,30 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<schemalist>
|
<schemalist>
|
||||||
<schema id="org.gnome.shell.extensions.aerospike" path="/org/gnome/shell/extensions/aerospike/">
|
<schema id="org.gnome.shell.extensions.aerospike" path="/org/gnome/shell/extensions/aerospike/">
|
||||||
<key name="keybinding-1" type="as">
|
|
||||||
<default><![CDATA[['<Super>1']]]></default>
|
|
||||||
<summary>Keybinding for action 1</summary>
|
|
||||||
<description>Keyboard shortcut for triggering action 1</description>
|
|
||||||
</key>
|
|
||||||
|
|
||||||
<key name="keybinding-2" type="as">
|
|
||||||
<default><![CDATA[['<Super>2']]]></default>
|
|
||||||
<summary>Keybinding for action 2</summary>
|
|
||||||
<description>Keyboard shortcut for triggering action 2</description>
|
|
||||||
</key>
|
|
||||||
|
|
||||||
<key name="keybinding-3" type="as">
|
|
||||||
<default><![CDATA[['<Super>3']]]></default>
|
|
||||||
<summary>Keybinding for action 3</summary>
|
|
||||||
<description>Keyboard shortcut for triggering action 3</description>
|
|
||||||
</key>
|
|
||||||
|
|
||||||
<key name="keybinding-4" type="as">
|
|
||||||
<default><![CDATA[['<Super>4']]]></default>
|
|
||||||
<summary>Keybinding for action 4</summary>
|
|
||||||
<description>Keyboard shortcut for triggering action 4</description>
|
|
||||||
</key>
|
|
||||||
|
|
||||||
<key name="dropdown-option" type="s">
|
<key name="dropdown-option" type="s">
|
||||||
<default>'option1'</default>
|
<default>'option1'</default>
|
||||||
<summary>Dropdown selection</summary>
|
<summary>Dropdown selection</summary>
|
||||||
@@ -36,5 +12,30 @@
|
|||||||
<summary>Selected color</summary>
|
<summary>Selected color</summary>
|
||||||
<description>Color chosen from the color picker</description>
|
<description>Color chosen from the color picker</description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
|
<key name="move-left" type="as">
|
||||||
|
<default><![CDATA[['<Super>1']]]></default>
|
||||||
|
<summary>Keybinding for action 1</summary>
|
||||||
|
<description>Keyboard shortcut for triggering action 1</description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="move-right" type="as">
|
||||||
|
<default><![CDATA[['<Super>2']]]></default>
|
||||||
|
<summary>Keybinding for action 2</summary>
|
||||||
|
<description>Keyboard shortcut for triggering action 2</description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="join-with-left" type="as">
|
||||||
|
<default><![CDATA[['<Super>3']]]></default>
|
||||||
|
<summary>Keybinding for action 3</summary>
|
||||||
|
<description>Keyboard shortcut for triggering action 3</description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="join-with-right" type="as">
|
||||||
|
<default><![CDATA[['<Super>4']]]></default>
|
||||||
|
<summary>Keybinding for action 4</summary>
|
||||||
|
<description>Keyboard shortcut for triggering action 4</description>
|
||||||
|
</key>
|
||||||
|
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
@@ -0,0 +1,83 @@
|
|||||||
|
// Gnome imports
|
||||||
|
import Adw from 'gi://Adw';
|
||||||
|
import Gtk from 'gi://Gtk';
|
||||||
|
import Gio from 'gi://Gio';
|
||||||
|
import GObject from 'gi://GObject';
|
||||||
|
import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||||
|
import { Logger } from '../utils/logger.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EntryRow class for handling text input including keybindings
|
||||||
|
*/
|
||||||
|
export class EntryRow extends Adw.EntryRow {
|
||||||
|
static {
|
||||||
|
GObject.registerClass(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(params: {
|
||||||
|
title: string,
|
||||||
|
settings: Gio.Settings,
|
||||||
|
bind: string,
|
||||||
|
map?: {
|
||||||
|
from: (settings: Gio.Settings, bind: string) => string,
|
||||||
|
to: (settings: Gio.Settings, bind: string, value: string) => void
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
super({ title: params.title });
|
||||||
|
|
||||||
|
const { settings, bind, map } = params;
|
||||||
|
|
||||||
|
// When text changes, update settings
|
||||||
|
this.connect('changed', () => {
|
||||||
|
const text = this.get_text();
|
||||||
|
if (typeof text === 'string') {
|
||||||
|
if (map) {
|
||||||
|
map.to(settings, bind, text);
|
||||||
|
} else {
|
||||||
|
settings.set_string(bind, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set initial text from settings
|
||||||
|
const current = map ? map.from(settings, bind) : settings.get_string(bind);
|
||||||
|
this.set_text(current ?? '');
|
||||||
|
|
||||||
|
// Add reset button
|
||||||
|
this.add_suffix(
|
||||||
|
new ResetButton({
|
||||||
|
settings,
|
||||||
|
bind,
|
||||||
|
onReset: () => {
|
||||||
|
this.set_text((map ? map.from(settings, bind) : settings.get_string(bind)) ?? '');
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset button for settings
|
||||||
|
*/
|
||||||
|
export class ResetButton extends Gtk.Button {
|
||||||
|
static {
|
||||||
|
GObject.registerClass(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(params: {
|
||||||
|
settings?: Gio.Settings,
|
||||||
|
bind: string,
|
||||||
|
onReset?: () => void
|
||||||
|
}) {
|
||||||
|
super({
|
||||||
|
icon_name: 'edit-clear-symbolic',
|
||||||
|
tooltip_text: _('Reset'),
|
||||||
|
valign: Gtk.Align.CENTER,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.connect('clicked', () => {
|
||||||
|
params.settings?.reset(params.bind);
|
||||||
|
params.onReset?.();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -4,8 +4,9 @@ import Gtk from 'gi://Gtk';
|
|||||||
import Gdk from 'gi://Gdk';
|
import Gdk from 'gi://Gdk';
|
||||||
import { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
import { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||||
import {Logger} from "../utils/logger.js";
|
import {Logger} from "../utils/logger.js";
|
||||||
|
import {EntryRow} from "./keybindings.js";
|
||||||
|
|
||||||
export default class MyExtensionPreferences extends ExtensionPreferences {
|
export default class AerospikeExtensions extends ExtensionPreferences {
|
||||||
async fillPreferencesWindow(window: Adw.PreferencesWindow) {
|
async fillPreferencesWindow(window: Adw.PreferencesWindow) {
|
||||||
// Create settings object
|
// Create settings object
|
||||||
const settings = this.getSettings('org.gnome.shell.extensions.aerospike');
|
const settings = this.getSettings('org.gnome.shell.extensions.aerospike');
|
||||||
@@ -17,17 +18,6 @@ export default class MyExtensionPreferences extends ExtensionPreferences {
|
|||||||
});
|
});
|
||||||
window.add(page);
|
window.add(page);
|
||||||
|
|
||||||
// Create keybindings group
|
|
||||||
const keybindingsGroup = new Adw.PreferencesGroup({
|
|
||||||
title: _('Keyboard Shortcuts'),
|
|
||||||
});
|
|
||||||
page.add(keybindingsGroup);
|
|
||||||
|
|
||||||
// Add keybinding rows
|
|
||||||
this.addKeybindingRow(keybindingsGroup, settings, 'keybinding-1', _('Action 1'));
|
|
||||||
this.addKeybindingRow(keybindingsGroup, settings, 'keybinding-2', _('Action 2'));
|
|
||||||
this.addKeybindingRow(keybindingsGroup, settings, 'keybinding-3', _('Action 3'));
|
|
||||||
this.addKeybindingRow(keybindingsGroup, settings, 'keybinding-4', _('Action 4'));
|
|
||||||
|
|
||||||
// Create options group
|
// Create options group
|
||||||
const optionsGroup = new Adw.PreferencesGroup({
|
const optionsGroup = new Adw.PreferencesGroup({
|
||||||
@@ -115,49 +105,82 @@ export default class MyExtensionPreferences extends ExtensionPreferences {
|
|||||||
const color = colorButton.get_rgba().to_string();
|
const color = colorButton.get_rgba().to_string();
|
||||||
settings.set_string('color-selection', color);
|
settings.set_string('color-selection', color);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create keybindings group
|
||||||
|
const keybindingsGroup = new Adw.PreferencesGroup({
|
||||||
|
title: _('Keyboard Shortcuts'),
|
||||||
|
description: `${_("Syntax")}: <Super>h, <Shift>g, <Super><Shift>h
|
||||||
|
${_("Legend")}: <Super> - ${_("Windows key")}, <Primary> - ${_("Control key")}
|
||||||
|
${_("Delete text to unset. Press Return key to accept.")}`,
|
||||||
|
});
|
||||||
|
page.add(keybindingsGroup);
|
||||||
|
|
||||||
|
// Add keybinding rows as EntryRows with proper mapping
|
||||||
|
// Use the helper function to create the map object
|
||||||
|
const keybindingMap = this.createKeybindingMap();
|
||||||
|
|
||||||
|
keybindingsGroup.add(
|
||||||
|
new EntryRow({
|
||||||
|
title: _('Action 1'),
|
||||||
|
settings: settings,
|
||||||
|
bind: 'move-left',
|
||||||
|
map: keybindingMap
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
keybindingsGroup.add(
|
||||||
|
new EntryRow({
|
||||||
|
title: _('Action 2'),
|
||||||
|
settings: settings,
|
||||||
|
bind: 'move-right',
|
||||||
|
map: keybindingMap
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
keybindingsGroup.add(
|
||||||
|
new EntryRow({
|
||||||
|
title: _('Action 3'),
|
||||||
|
settings: settings,
|
||||||
|
bind: 'join-with-left',
|
||||||
|
map: keybindingMap
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
keybindingsGroup.add(
|
||||||
|
new EntryRow({
|
||||||
|
title: _('Action 4'),
|
||||||
|
settings: settings,
|
||||||
|
bind: 'join-with-right',
|
||||||
|
map: keybindingMap
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private addKeybindingRow(
|
// Helper function to create a keybinding mapping object
|
||||||
group: Adw.PreferencesGroup,
|
private createKeybindingMap() {
|
||||||
settings: Gio.Settings,
|
return {
|
||||||
key: string,
|
from(settings: Gio.Settings, bind: string) {
|
||||||
title: string
|
return settings.get_strv(bind).join(',');
|
||||||
) {
|
},
|
||||||
const shortcutsRow = new Adw.ActionRow({
|
to(settings: Gio.Settings, bind: string, value: string) {
|
||||||
title: title,
|
if (!!value) {
|
||||||
});
|
const mappings = value.split(',').map((x) => {
|
||||||
|
const [, key, mods] = Gtk.accelerator_parse(x);
|
||||||
group.add(shortcutsRow);
|
return Gtk.accelerator_valid(key, mods) && Gtk.accelerator_name(key, mods);
|
||||||
|
});
|
||||||
// Create a button for setting shortcuts
|
// Filter out any false values to ensure we only have strings
|
||||||
const shortcutButton = new Gtk.Button({
|
const stringMappings = mappings.filter((x): x is string => typeof x === 'string');
|
||||||
valign: Gtk.Align.CENTER,
|
if (stringMappings.length > 0) {
|
||||||
label: settings.get_strv(key)[0] || _("Disabled")
|
Logger.debug("setting", bind, "to", stringMappings);
|
||||||
});
|
settings.set_strv(bind, stringMappings);
|
||||||
|
}
|
||||||
shortcutsRow.add_suffix(shortcutButton);
|
} else {
|
||||||
shortcutsRow.set_activatable_widget(shortcutButton);
|
// If value deleted, unset the mapping
|
||||||
|
settings.set_strv(bind, []);
|
||||||
// When clicking the button, show a dialog or start listening for keystroke
|
}
|
||||||
shortcutButton.connect('clicked', () => {
|
},
|
||||||
// Show a simple popup stating that the shortcut is being recorded
|
};
|
||||||
const dialog = new Gtk.MessageDialog({
|
|
||||||
modal: true,
|
|
||||||
text: _("Press a key combination to set as shortcut"),
|
|
||||||
secondary_text: _("Press Esc to cancel or Backspace to disable"),
|
|
||||||
buttons: Gtk.ButtonsType.CANCEL,
|
|
||||||
transient_for: group.get_root() as Gtk.Window
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a keypress event controller
|
|
||||||
const controller = new Gtk.EventControllerKey();
|
|
||||||
dialog.add_controller(controller);
|
|
||||||
|
|
||||||
controller.connect('key-pressed', (_controller, keyval, keycode, state) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.present();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -34,7 +34,7 @@ export default class WindowContainer {
|
|||||||
// Add window to managed windows
|
// Add window to managed windows
|
||||||
this._tiledItems.push(winWrap);
|
this._tiledItems.push(winWrap);
|
||||||
this._tiledWindowLookup.set(winWrap.getWindowId(), winWrap);
|
this._tiledWindowLookup.set(winWrap.getWindowId(), winWrap);
|
||||||
winWrap.setParent(this);
|
// winWrap.setParent(this);
|
||||||
queueEvent({
|
queueEvent({
|
||||||
name: "tiling-windows",
|
name: "tiling-windows",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
|
@@ -14,6 +14,7 @@ export class WindowWrapper {
|
|||||||
readonly _windowMinimizedHandler: WindowMinimizedHandler;
|
readonly _windowMinimizedHandler: WindowMinimizedHandler;
|
||||||
readonly _signals: number[] = [];
|
readonly _signals: number[] = [];
|
||||||
_parent: WindowContainer | null = null;
|
_parent: WindowContainer | null = null;
|
||||||
|
_dragging: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
window: Meta.Window,
|
window: Meta.Window,
|
||||||
@@ -43,16 +44,23 @@ export class WindowWrapper {
|
|||||||
return this._window.get_frame_rect();
|
return this._window.get_frame_rect();
|
||||||
}
|
}
|
||||||
|
|
||||||
setParent(parent: WindowContainer): void {
|
startDragging(): void {
|
||||||
this._parent = parent;
|
this._dragging = true;
|
||||||
|
}
|
||||||
|
stopDragging(): void {
|
||||||
|
this._dragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getParent(): WindowContainer | null {
|
// setParent(parent: WindowContainer): void {
|
||||||
if (this._parent == null) {
|
// this._parent = parent;
|
||||||
Logger.warn(`Attempting to get parent for window without parent ${JSON.stringify(this)}`);
|
// }
|
||||||
}
|
//
|
||||||
return this._parent
|
// getParent(): WindowContainer | null {
|
||||||
}
|
// if (this._parent == null) {
|
||||||
|
// Logger.warn(`Attempting to get parent for window without parent ${JSON.stringify(this)}`);
|
||||||
|
// }
|
||||||
|
// return this._parent
|
||||||
|
// }
|
||||||
|
|
||||||
connectWindowSignals(
|
connectWindowSignals(
|
||||||
windowManager: IWindowManager,
|
windowManager: IWindowManager,
|
||||||
@@ -114,7 +122,11 @@ export class WindowWrapper {
|
|||||||
|
|
||||||
// This is meant to be an exact copy of Forge's move function, renamed to maintain your API
|
// This is meant to be an exact copy of Forge's move function, renamed to maintain your API
|
||||||
safelyResizeWindow(rect: Rect): void {
|
safelyResizeWindow(rect: Rect): void {
|
||||||
// Keep minimal logging
|
// Keep minimal logging
|
||||||
|
if (this._dragging) {
|
||||||
|
Logger.info("STOPPED RESIZE BECAUSE ITEM IS BEING DRAGGED")
|
||||||
|
return
|
||||||
|
}
|
||||||
Logger.log("SAFELY RESIZE", rect.x, rect.y, rect.width, rect.height);
|
Logger.log("SAFELY RESIZE", rect.x, rect.y, rect.width, rect.height);
|
||||||
const actor = this._window.get_compositor_private();
|
const actor = this._window.get_compositor_private();
|
||||||
|
|
||||||
|
@@ -44,6 +44,7 @@ export default class WindowManager implements IWindowManager {
|
|||||||
|
|
||||||
_grabbedWindowMonitor: number = _UNUSED_MONITOR_ID;
|
_grabbedWindowMonitor: number = _UNUSED_MONITOR_ID;
|
||||||
_grabbedWindowId: number = _UNUSED_WINDOW_ID;
|
_grabbedWindowId: number = _UNUSED_WINDOW_ID;
|
||||||
|
_changingGrabbedMonitor: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
@@ -73,7 +74,6 @@ export default class WindowManager implements IWindowManager {
|
|||||||
}),
|
}),
|
||||||
global.display.connect("window-entered-monitor", (display, monitor, window) => {
|
global.display.connect("window-entered-monitor", (display, monitor, window) => {
|
||||||
Logger.log("WINDOW HAS ENTERED NEW MONITOR!")
|
Logger.log("WINDOW HAS ENTERED NEW MONITOR!")
|
||||||
// this._moveWindowToMonitor(window, monitor);
|
|
||||||
}),
|
}),
|
||||||
global.display.connect('window-created', (display, window) => {
|
global.display.connect('window-created', (display, window) => {
|
||||||
this.handleWindowCreated(display, window);
|
this.handleWindowCreated(display, window);
|
||||||
@@ -91,11 +91,11 @@ export default class WindowManager implements IWindowManager {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
this._windowManagerSignals = [
|
// this._windowManagerSignals = [
|
||||||
global.window_manager.connect("show-tile-preview", (_, _metaWindow, _rect, _num) => {
|
// global.window_manager.connect("show-tile-preview", (_, _metaWindow, _rect, _num) => {
|
||||||
Logger.log("SHOW TITLE PREVIEW!")
|
// Logger.log("SHOW TITLE PREVIEW!")
|
||||||
}),
|
// }),
|
||||||
];
|
// ];
|
||||||
|
|
||||||
this._workspaceManagerSignals = [
|
this._workspaceManagerSignals = [
|
||||||
global.workspace_manager.connect("showing-desktop-changed", () => {
|
global.workspace_manager.connect("showing-desktop-changed", () => {
|
||||||
@@ -204,81 +204,75 @@ export default class WindowManager implements IWindowManager {
|
|||||||
this._grabbedWindowId = _UNUSED_WINDOW_ID;
|
this._grabbedWindowId = _UNUSED_WINDOW_ID;
|
||||||
var rect = window.get_frame_rect()
|
var rect = window.get_frame_rect()
|
||||||
Logger.info("Release Location", window.get_monitor(), rect.x, rect.y, rect.width, rect.height)
|
Logger.info("Release Location", window.get_monitor(), rect.x, rect.y, rect.width, rect.height)
|
||||||
const old_mon_id = this._grabbedWindowMonitor;
|
// previously window was moved to a new monitor here instead of it being fluid during drag events.
|
||||||
const new_mon_id = window.get_monitor();
|
|
||||||
|
|
||||||
Logger.info("MONITOR MATCH", old_mon_id !== new_mon_id);
|
|
||||||
if (old_mon_id !== new_mon_id) {
|
|
||||||
Logger.trace("MOVING MONITOR");
|
|
||||||
let old_mon = this._monitors.get(old_mon_id);
|
|
||||||
let new_mon = this._monitors.get(new_mon_id);
|
|
||||||
if (old_mon === undefined || new_mon === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let wrapped = old_mon.getWindow(window.get_id())
|
|
||||||
if (wrapped === undefined) {
|
|
||||||
wrapped = new WindowWrapper(window, this.handleWindowMinimized);
|
|
||||||
} else {
|
|
||||||
old_mon.removeWindow(wrapped)
|
|
||||||
}
|
|
||||||
new_mon.addWindow(wrapped)
|
|
||||||
}
|
|
||||||
this._tileMonitors();
|
this._tileMonitors();
|
||||||
Logger.info("monitor_start and monitor_end", this._grabbedWindowMonitor, window.get_monitor());
|
Logger.info("monitor_start and monitor_end", this._grabbedWindowMonitor, window.get_monitor());
|
||||||
}
|
}
|
||||||
|
|
||||||
_moveWindowToMonitor(window: Meta.Window, monitorId: number): void {
|
_moveWindowToMonitor(window: Meta.Window, monitorId: number): void {
|
||||||
|
Logger.info("MOVING WINDOW TO MONITOR", window.get_id(), monitorId);
|
||||||
let wrapped = undefined;
|
let wrapped = undefined;
|
||||||
for (const monitor of this._monitors.values()) {
|
for (const monitor of this._monitors.values()) {
|
||||||
wrapped = monitor.getWindow(window.get_id());
|
wrapped = monitor.getWindow(window.get_id());
|
||||||
if (wrapped !== undefined) {
|
if (wrapped !== undefined) {
|
||||||
|
Logger.error("FOUND WINDOW IN MONITOR")
|
||||||
monitor.removeWindow(wrapped);
|
monitor.removeWindow(wrapped);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wrapped === undefined) {
|
if (wrapped === undefined) {
|
||||||
|
Logger.error("WINDOW NOT DEFINED")
|
||||||
wrapped = new WindowWrapper(window, this.handleWindowMinimized);
|
wrapped = new WindowWrapper(window, this.handleWindowMinimized);
|
||||||
wrapped.connectWindowSignals(this);
|
wrapped.connectWindowSignals(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrapped.startDragging()
|
||||||
let new_mon = this._monitors.get(monitorId);
|
let new_mon = this._monitors.get(monitorId);
|
||||||
new_mon?.addWindow(wrapped)
|
new_mon?.addWindow(wrapped)
|
||||||
this._tileMonitors();
|
Logger.info("UPDATE MONITOR", new_mon);
|
||||||
|
this._grabbedWindowMonitor = monitorId;
|
||||||
|
wrapped.stopDragging();
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleWindowPositionChanged(winWrap: WindowWrapper): void {
|
public handleWindowPositionChanged(winWrap: WindowWrapper): void {
|
||||||
|
if (this._changingGrabbedMonitor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (winWrap.getWindowId() === this._grabbedWindowId) {
|
if (winWrap.getWindowId() === this._grabbedWindowId) {
|
||||||
const rect = winWrap.getRect();
|
|
||||||
// Logger.log("GRABBED WINDOW POSITION CHANGED", rect.x);
|
|
||||||
const [mouseX, mouseY, _] = global.get_pointer();
|
const [mouseX, mouseY, _] = global.get_pointer();
|
||||||
this._monitors.get(winWrap.getMonitor())?.itemDragged(winWrap, mouseX, mouseY);
|
|
||||||
|
|
||||||
// Log or use the coordinates
|
let monitorIndex = -1;
|
||||||
// console.log(`Mouse position: X=${mouseX}, Y=${mouseY}`);
|
for (let i = 0; i < global.display.get_n_monitors(); i++) {
|
||||||
|
const workArea = global.workspace_manager.get_active_workspace().get_work_area_for_monitor(i);
|
||||||
|
if (mouseX >= workArea.x && mouseX < workArea.x + workArea.width &&
|
||||||
|
mouseY >= workArea.y && mouseY < workArea.y + workArea.height) {
|
||||||
|
monitorIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (monitorIndex === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorIndex !== this._grabbedWindowMonitor) {
|
||||||
|
this._changingGrabbedMonitor = true;
|
||||||
|
Logger.log("CHANGING MONITOR FOR WINDOW");
|
||||||
|
this._moveWindowToMonitor(winWrap.getWindow(), monitorIndex);
|
||||||
|
this._changingGrabbedMonitor = false
|
||||||
|
}
|
||||||
|
this._monitors.get(monitorIndex)?.itemDragged(winWrap, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public handleWindowMinimized(winWrap: WindowWrapper): void {
|
public handleWindowMinimized(winWrap: WindowWrapper): void {
|
||||||
Logger.warn("WARNING MINIMIZING WINDOW");
|
|
||||||
Logger.log("WARNING MINIMIZED", JSON.stringify(winWrap));
|
|
||||||
const monitor_id = winWrap.getWindow().get_monitor()
|
const monitor_id = winWrap.getWindow().get_monitor()
|
||||||
Logger.log("WARNING MINIMIZED", monitor_id);
|
|
||||||
Logger.warn("WARNING MINIMIZED", this._monitors);
|
|
||||||
|
|
||||||
this._minimizedItems.set(winWrap.getWindowId(), winWrap);
|
this._minimizedItems.set(winWrap.getWindowId(), winWrap);
|
||||||
this._monitors.get(monitor_id)?.removeWindow(winWrap);
|
this._monitors.get(monitor_id)?.removeWindow(winWrap);
|
||||||
|
|
||||||
Logger.warn("WARNING MINIMIZED ITEMS", JSON.stringify(this._minimizedItems));
|
|
||||||
this._tileMonitors()
|
this._tileMonitors()
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleWindowUnminimized(winWrap: WindowWrapper): void {
|
public handleWindowUnminimized(winWrap: WindowWrapper): void {
|
||||||
Logger.log("WINDOW UNMINIMIZED");
|
|
||||||
Logger.log("WINDOW UNMINIMIZED", winWrap == null);
|
|
||||||
// Logger.log("WINDOW UNMINIMIZED", winWrap);
|
|
||||||
// Logger.log("WINDOW UNMINIMIZED", winWrap.getWindowId());
|
|
||||||
this._minimizedItems.delete(winWrap.getWindowId());
|
this._minimizedItems.delete(winWrap.getWindowId());
|
||||||
this._addWindowWrapperToMonitor(winWrap);
|
this._addWindowWrapperToMonitor(winWrap);
|
||||||
this._tileMonitors()
|
this._tileMonitors()
|
||||||
@@ -292,10 +286,8 @@ export default class WindowManager implements IWindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public captureExistingWindows() {
|
public captureExistingWindows() {
|
||||||
Logger.log("CAPTURING WINDOWS")
|
|
||||||
const workspace = global.workspace_manager.get_active_workspace();
|
const workspace = global.workspace_manager.get_active_workspace();
|
||||||
const windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
const windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||||
Logger.log("WINDOWS", windows);
|
|
||||||
windows.forEach(window => {
|
windows.forEach(window => {
|
||||||
if (this._isWindowTileable(window)) {
|
if (this._isWindowTileable(window)) {
|
||||||
this.addWindowToMonitor(window);
|
this.addWindowToMonitor(window);
|
||||||
@@ -343,9 +335,6 @@ export default class WindowManager implements IWindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_addWindowWrapperToMonitor(winWrap: WindowWrapper) {
|
_addWindowWrapperToMonitor(winWrap: WindowWrapper) {
|
||||||
Logger.log("Adding window", JSON.stringify(winWrap));
|
|
||||||
Logger.log("Adding window raw", JSON.stringify(winWrap.getWindow()));
|
|
||||||
Logger.log("Adding window raw", JSON.stringify(winWrap.getWindow().minimized));
|
|
||||||
if (winWrap.getWindow().minimized) {
|
if (winWrap.getWindow().minimized) {
|
||||||
this._minimizedItems.set(winWrap.getWindow().get_id(), winWrap);
|
this._minimizedItems.set(winWrap.getWindow().get_id(), winWrap);
|
||||||
} else {
|
} else {
|
||||||
@@ -360,14 +349,35 @@ export default class WindowManager implements IWindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block_titles = [
|
||||||
|
"org.gnome.Shell.Extensions",
|
||||||
|
]
|
||||||
|
|
||||||
|
_isWindowTilingBlocked(window: Meta.Window) : boolean {
|
||||||
|
Logger.info("title", window.get_title());
|
||||||
|
Logger.info("description", window.get_description());
|
||||||
|
Logger.info("class", window.get_wm_class());
|
||||||
|
Logger.info("class", window.get_wm_class_instance());
|
||||||
|
return this.block_titles.some((title) => {
|
||||||
|
if (window.get_title() === title) {
|
||||||
|
Logger.log("WINDOW BLOCKED FROM TILING", window.get_title());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_isWindowTileable(window: Meta.Window) {
|
_isWindowTileable(window: Meta.Window) {
|
||||||
|
|
||||||
if (!window || !window.get_compositor_private()) {
|
if (!window || !window.get_compositor_private()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (this._isWindowTilingBlocked(window)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const windowType = window.get_window_type();
|
const windowType = window.get_window_type();
|
||||||
Logger.log("WINDOW TYPE", windowType);
|
Logger.log("WINDOW TILING CHECK",);
|
||||||
|
|
||||||
// Skip certain types of windows
|
// Skip certain types of windows
|
||||||
return !window.is_skip_taskbar() &&
|
return !window.is_skip_taskbar() &&
|
||||||
windowType !== Meta.WindowType.DESKTOP &&
|
windowType !== Meta.WindowType.DESKTOP &&
|
||||||
|
Reference in New Issue
Block a user