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
|
||||
/schemas/gschemas.compiled
|
||||
/aerospike.zip
|
||||
/debug.log
|
||||
|
67
extension.ts
67
extension.ts
@ -21,6 +21,7 @@ export default class aerospike extends Extension {
|
||||
enable() {
|
||||
Logger.log("STARTING AEROSPIKE!")
|
||||
this.bindSettings();
|
||||
this.setupKeybindings();
|
||||
this.windowManager.enable()
|
||||
}
|
||||
|
||||
@ -32,24 +33,24 @@ export default class aerospike extends Extension {
|
||||
|
||||
private bindSettings() {
|
||||
// Monitor settings changes
|
||||
this.settings.connect('changed::keybinding-1', () => {
|
||||
log(`Keybinding 1 changed to: ${this.settings.get_strv('keybinding-1')}`);
|
||||
this.refreshKeybinding('keybinding-1');
|
||||
this.settings.connect('changed::move-left', () => {
|
||||
log(`Keybinding 1 changed to: ${this.settings.get_strv('move-left')}`);
|
||||
this.refreshKeybinding('move-left');
|
||||
});
|
||||
|
||||
this.settings.connect('changed::keybinding-2', () => {
|
||||
log(`Keybinding 2 changed to: ${this.settings.get_strv('keybinding-2')}`);
|
||||
this.refreshKeybinding('keybinding-2');
|
||||
this.settings.connect('changed::move-right', () => {
|
||||
log(`Keybinding 2 changed to: ${this.settings.get_strv('move-right')}`);
|
||||
this.refreshKeybinding('move-right');
|
||||
});
|
||||
|
||||
this.settings.connect('changed::keybinding-3', () => {
|
||||
log(`Keybinding 3 changed to: ${this.settings.get_strv('keybinding-3')}`);
|
||||
this.refreshKeybinding('keybinding-3');
|
||||
this.settings.connect('changed::join-with-left', () => {
|
||||
log(`Keybinding 3 changed to: ${this.settings.get_strv('join-with-left')}`);
|
||||
this.refreshKeybinding('join-with-left');
|
||||
});
|
||||
|
||||
this.settings.connect('changed::keybinding-4', () => {
|
||||
log(`Keybinding 4 changed to: ${this.settings.get_strv('keybinding-4')}`);
|
||||
this.refreshKeybinding('keybinding-4');
|
||||
this.settings.connect('changed::join-with-right', () => {
|
||||
log(`Keybinding 4 changed to: ${this.settings.get_strv('join-with-right')}`);
|
||||
this.refreshKeybinding('join-with-right');
|
||||
});
|
||||
|
||||
this.settings.connect('changed::dropdown-option', () => {
|
||||
@ -67,24 +68,24 @@ export default class aerospike extends Extension {
|
||||
}
|
||||
|
||||
switch (settingName) {
|
||||
case 'keybinding-1':
|
||||
this.bindKeybinding('keybinding-1', () => {
|
||||
log('Keybinding 1 was pressed!');
|
||||
case 'move-left':
|
||||
this.bindKeybinding('move-left', () => {
|
||||
Logger.info('Keybinding 1 was pressed!');
|
||||
});
|
||||
break;
|
||||
case 'keybinding-2':
|
||||
this.bindKeybinding('keybinding-2', () => {
|
||||
log('Keybinding 2 was pressed!');
|
||||
case 'move-right':
|
||||
this.bindKeybinding('move-right', () => {
|
||||
Logger.info('Keybinding 2 was pressed!');
|
||||
});
|
||||
break;
|
||||
case 'keybinding-3':
|
||||
this.bindKeybinding('keybinding-3', () => {
|
||||
log('Keybinding 3 was pressed!');
|
||||
case 'join-with-left':
|
||||
this.bindKeybinding('join-with-left', () => {
|
||||
Logger.info('Keybinding 3 was pressed!');
|
||||
});
|
||||
break;
|
||||
case 'keybinding-4':
|
||||
this.bindKeybinding('keybinding-4', () => {
|
||||
log('Keybinding 4 was pressed!');
|
||||
case 'join-with-right':
|
||||
this.bindKeybinding('join-with-right', () => {
|
||||
Logger.info('Keybinding 4 was pressed!');
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -98,20 +99,20 @@ export default class aerospike extends Extension {
|
||||
}
|
||||
|
||||
private setupKeybindings() {
|
||||
this.bindKeybinding('keybinding-1', () => {
|
||||
log('Keybinding 1 was pressed!');
|
||||
this.bindKeybinding('move-left', () => {
|
||||
Logger.info('Keybinding 1 was pressed!');
|
||||
});
|
||||
|
||||
this.bindKeybinding('keybinding-2', () => {
|
||||
log('Keybinding 2 was pressed!');
|
||||
this.bindKeybinding('move-right', () => {
|
||||
Logger.info('Keybinding 2 was pressed!');
|
||||
});
|
||||
|
||||
this.bindKeybinding('keybinding-3', () => {
|
||||
log('Keybinding 3 was pressed!');
|
||||
this.bindKeybinding('join-with-left', () => {
|
||||
Logger.info('Keybinding 3 was pressed!');
|
||||
});
|
||||
|
||||
this.bindKeybinding('keybinding-4', () => {
|
||||
log('Keybinding 4 was pressed!');
|
||||
this.bindKeybinding('join-with-right', () => {
|
||||
Logger.info('Keybinding 4 was pressed!');
|
||||
});
|
||||
}
|
||||
|
||||
@ -125,7 +126,7 @@ export default class aerospike extends Extension {
|
||||
const keyBindingAction = Main.wm.addKeybinding(
|
||||
settingName,
|
||||
this.settings,
|
||||
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.ActionMode.NORMAL,
|
||||
callback
|
||||
);
|
||||
|
4
prefs.ts
4
prefs.ts
@ -1,4 +1,4 @@
|
||||
// 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"?>
|
||||
<schemalist>
|
||||
<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">
|
||||
<default>'option1'</default>
|
||||
<summary>Dropdown selection</summary>
|
||||
@ -36,5 +12,30 @@
|
||||
<summary>Selected color</summary>
|
||||
<description>Color chosen from the color picker</description>
|
||||
</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>
|
||||
</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 { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.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) {
|
||||
// Create settings object
|
||||
const settings = this.getSettings('org.gnome.shell.extensions.aerospike');
|
||||
@ -17,17 +18,6 @@ export default class MyExtensionPreferences extends ExtensionPreferences {
|
||||
});
|
||||
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
|
||||
const optionsGroup = new Adw.PreferencesGroup({
|
||||
@ -115,49 +105,82 @@ export default class MyExtensionPreferences extends ExtensionPreferences {
|
||||
const color = colorButton.get_rgba().to_string();
|
||||
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(
|
||||
group: Adw.PreferencesGroup,
|
||||
settings: Gio.Settings,
|
||||
key: string,
|
||||
title: string
|
||||
) {
|
||||
const shortcutsRow = new Adw.ActionRow({
|
||||
title: title,
|
||||
});
|
||||
|
||||
group.add(shortcutsRow);
|
||||
|
||||
// Create a button for setting shortcuts
|
||||
const shortcutButton = new Gtk.Button({
|
||||
valign: Gtk.Align.CENTER,
|
||||
label: settings.get_strv(key)[0] || _("Disabled")
|
||||
});
|
||||
|
||||
shortcutsRow.add_suffix(shortcutButton);
|
||||
shortcutsRow.set_activatable_widget(shortcutButton);
|
||||
|
||||
// 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();
|
||||
});
|
||||
// Helper function to create a keybinding mapping object
|
||||
private createKeybindingMap() {
|
||||
return {
|
||||
from(settings: Gio.Settings, bind: string) {
|
||||
return settings.get_strv(bind).join(',');
|
||||
},
|
||||
to(settings: Gio.Settings, bind: string, value: string) {
|
||||
if (!!value) {
|
||||
const mappings = value.split(',').map((x) => {
|
||||
const [, key, mods] = Gtk.accelerator_parse(x);
|
||||
return Gtk.accelerator_valid(key, mods) && Gtk.accelerator_name(key, mods);
|
||||
});
|
||||
// Filter out any false values to ensure we only have strings
|
||||
const stringMappings = mappings.filter((x): x is string => typeof x === 'string');
|
||||
if (stringMappings.length > 0) {
|
||||
Logger.debug("setting", bind, "to", stringMappings);
|
||||
settings.set_strv(bind, stringMappings);
|
||||
}
|
||||
} else {
|
||||
// If value deleted, unset the mapping
|
||||
settings.set_strv(bind, []);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ export default class WindowContainer {
|
||||
// Add window to managed windows
|
||||
this._tiledItems.push(winWrap);
|
||||
this._tiledWindowLookup.set(winWrap.getWindowId(), winWrap);
|
||||
winWrap.setParent(this);
|
||||
// winWrap.setParent(this);
|
||||
queueEvent({
|
||||
name: "tiling-windows",
|
||||
callback: () => {
|
||||
|
@ -14,6 +14,7 @@ export class WindowWrapper {
|
||||
readonly _windowMinimizedHandler: WindowMinimizedHandler;
|
||||
readonly _signals: number[] = [];
|
||||
_parent: WindowContainer | null = null;
|
||||
_dragging: boolean = false;
|
||||
|
||||
constructor(
|
||||
window: Meta.Window,
|
||||
@ -43,16 +44,23 @@ export class WindowWrapper {
|
||||
return this._window.get_frame_rect();
|
||||
}
|
||||
|
||||
setParent(parent: WindowContainer): void {
|
||||
this._parent = parent;
|
||||
startDragging(): void {
|
||||
this._dragging = true;
|
||||
}
|
||||
stopDragging(): void {
|
||||
this._dragging = false;
|
||||
}
|
||||
|
||||
getParent(): WindowContainer | null {
|
||||
if (this._parent == null) {
|
||||
Logger.warn(`Attempting to get parent for window without parent ${JSON.stringify(this)}`);
|
||||
}
|
||||
return this._parent
|
||||
}
|
||||
// setParent(parent: WindowContainer): void {
|
||||
// this._parent = 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(
|
||||
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
|
||||
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);
|
||||
const actor = this._window.get_compositor_private();
|
||||
|
||||
|
@ -44,6 +44,7 @@ export default class WindowManager implements IWindowManager {
|
||||
|
||||
_grabbedWindowMonitor: number = _UNUSED_MONITOR_ID;
|
||||
_grabbedWindowId: number = _UNUSED_WINDOW_ID;
|
||||
_changingGrabbedMonitor: boolean = false;
|
||||
|
||||
constructor() {
|
||||
|
||||
@ -73,7 +74,6 @@ export default class WindowManager implements IWindowManager {
|
||||
}),
|
||||
global.display.connect("window-entered-monitor", (display, monitor, window) => {
|
||||
Logger.log("WINDOW HAS ENTERED NEW MONITOR!")
|
||||
// this._moveWindowToMonitor(window, monitor);
|
||||
}),
|
||||
global.display.connect('window-created', (display, window) => {
|
||||
this.handleWindowCreated(display, window);
|
||||
@ -91,11 +91,11 @@ export default class WindowManager implements IWindowManager {
|
||||
}),
|
||||
)
|
||||
|
||||
this._windowManagerSignals = [
|
||||
global.window_manager.connect("show-tile-preview", (_, _metaWindow, _rect, _num) => {
|
||||
Logger.log("SHOW TITLE PREVIEW!")
|
||||
}),
|
||||
];
|
||||
// this._windowManagerSignals = [
|
||||
// global.window_manager.connect("show-tile-preview", (_, _metaWindow, _rect, _num) => {
|
||||
// Logger.log("SHOW TITLE PREVIEW!")
|
||||
// }),
|
||||
// ];
|
||||
|
||||
this._workspaceManagerSignals = [
|
||||
global.workspace_manager.connect("showing-desktop-changed", () => {
|
||||
@ -204,81 +204,75 @@ export default class WindowManager implements IWindowManager {
|
||||
this._grabbedWindowId = _UNUSED_WINDOW_ID;
|
||||
var rect = window.get_frame_rect()
|
||||
Logger.info("Release Location", window.get_monitor(), rect.x, rect.y, rect.width, rect.height)
|
||||
const old_mon_id = this._grabbedWindowMonitor;
|
||||
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)
|
||||
}
|
||||
// previously window was moved to a new monitor here instead of it being fluid during drag events.
|
||||
this._tileMonitors();
|
||||
Logger.info("monitor_start and monitor_end", this._grabbedWindowMonitor, window.get_monitor());
|
||||
}
|
||||
|
||||
_moveWindowToMonitor(window: Meta.Window, monitorId: number): void {
|
||||
Logger.info("MOVING WINDOW TO MONITOR", window.get_id(), monitorId);
|
||||
let wrapped = undefined;
|
||||
for (const monitor of this._monitors.values()) {
|
||||
wrapped = monitor.getWindow(window.get_id());
|
||||
if (wrapped !== undefined) {
|
||||
Logger.error("FOUND WINDOW IN MONITOR")
|
||||
monitor.removeWindow(wrapped);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (wrapped === undefined) {
|
||||
Logger.error("WINDOW NOT DEFINED")
|
||||
wrapped = new WindowWrapper(window, this.handleWindowMinimized);
|
||||
wrapped.connectWindowSignals(this);
|
||||
}
|
||||
|
||||
wrapped.startDragging()
|
||||
let new_mon = this._monitors.get(monitorId);
|
||||
new_mon?.addWindow(wrapped)
|
||||
this._tileMonitors();
|
||||
Logger.info("UPDATE MONITOR", new_mon);
|
||||
this._grabbedWindowMonitor = monitorId;
|
||||
wrapped.stopDragging();
|
||||
}
|
||||
|
||||
public handleWindowPositionChanged(winWrap: WindowWrapper): void {
|
||||
if (this._changingGrabbedMonitor) {
|
||||
return;
|
||||
}
|
||||
if (winWrap.getWindowId() === this._grabbedWindowId) {
|
||||
const rect = winWrap.getRect();
|
||||
// Logger.log("GRABBED WINDOW POSITION CHANGED", rect.x);
|
||||
const [mouseX, mouseY, _] = global.get_pointer();
|
||||
this._monitors.get(winWrap.getMonitor())?.itemDragged(winWrap, mouseX, mouseY);
|
||||
|
||||
// Log or use the coordinates
|
||||
// console.log(`Mouse position: X=${mouseX}, Y=${mouseY}`);
|
||||
let monitorIndex = -1;
|
||||
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 {
|
||||
Logger.warn("WARNING MINIMIZING WINDOW");
|
||||
Logger.log("WARNING MINIMIZED", JSON.stringify(winWrap));
|
||||
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._monitors.get(monitor_id)?.removeWindow(winWrap);
|
||||
|
||||
Logger.warn("WARNING MINIMIZED ITEMS", JSON.stringify(this._minimizedItems));
|
||||
this._tileMonitors()
|
||||
}
|
||||
|
||||
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._addWindowWrapperToMonitor(winWrap);
|
||||
this._tileMonitors()
|
||||
@ -292,10 +286,8 @@ export default class WindowManager implements IWindowManager {
|
||||
}
|
||||
|
||||
public captureExistingWindows() {
|
||||
Logger.log("CAPTURING WINDOWS")
|
||||
const workspace = global.workspace_manager.get_active_workspace();
|
||||
const windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||
Logger.log("WINDOWS", windows);
|
||||
windows.forEach(window => {
|
||||
if (this._isWindowTileable(window)) {
|
||||
this.addWindowToMonitor(window);
|
||||
@ -343,9 +335,6 @@ export default class WindowManager implements IWindowManager {
|
||||
}
|
||||
|
||||
_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) {
|
||||
this._minimizedItems.set(winWrap.getWindow().get_id(), winWrap);
|
||||
} 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) {
|
||||
|
||||
if (!window || !window.get_compositor_private()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._isWindowTilingBlocked(window)) {
|
||||
return false;
|
||||
}
|
||||
const windowType = window.get_window_type();
|
||||
Logger.log("WINDOW TYPE", windowType);
|
||||
Logger.log("WINDOW TILING CHECK",);
|
||||
|
||||
// Skip certain types of windows
|
||||
return !window.is_skip_taskbar() &&
|
||||
windowType !== Meta.WindowType.DESKTOP &&
|
||||
|
Reference in New Issue
Block a user