From 20b5e4a13cf327a4ef4f4ed93abf4324aa076e87 Mon Sep 17 00:00:00 2001 From: "Martin N." Date: Mon, 15 Apr 2024 14:12:27 +1000 Subject: [PATCH 1/5] feat: Allow configuring horizontal/vertical window size This can be useful for large or ultrawide monitors. For now, the window position is always centered, but that could be made configurable in a future iteration, if desired. --- ddterm/app/application.js | 6 ++- ddterm/app/appwindow.js | 20 ++++++-- ddterm/pref/positionsize.js | 9 ++-- ddterm/pref/ui/prefs-position-size.ui | 44 ++++++++++++++++-- ddterm/pref/ui/prefs-shortcuts.ui | 22 +++++++-- ddterm/shell/geometry.js | 49 +++++++++++++------- ddterm/shell/wm.js | 14 +++--- schemas/com.github.amezin.ddterm.gschema.xml | 16 +++++-- 8 files changed, 135 insertions(+), 45 deletions(-) diff --git a/ddterm/app/application.js b/ddterm/app/application.js index 1205c8b7..4b85d1a4 100644 --- a/ddterm/app/application.js +++ b/ddterm/app/application.js @@ -292,8 +292,10 @@ class Application extends Gtk.Application { const shortcut_actions = { 'shortcut-window-hide': 'win.hide', - 'shortcut-window-size-inc': 'win.window-size-inc', - 'shortcut-window-size-dec': 'win.window-size-dec', + 'shortcut-window-hsize-inc': 'win.window-hsize-inc', + 'shortcut-window-hsize-dec': 'win.window-hsize-dec', + 'shortcut-window-vsize-inc': 'win.window-vsize-inc', + 'shortcut-window-vsize-dec': 'win.window-vsize-dec', 'shortcut-background-opacity-inc': 'win.background-opacity-inc', 'shortcut-background-opacity-dec': 'win.background-opacity-dec', 'shortcut-toggle-maximize': 'app.window-maximize', diff --git a/ddterm/app/appwindow.js b/ddterm/app/appwindow.js index 66560864..4719defe 100644 --- a/ddterm/app/appwindow.js +++ b/ddterm/app/appwindow.js @@ -269,15 +269,25 @@ class DDTermAppWindow extends Gtk.ApplicationWindow { 'toggle': this.toggle.bind(this), 'show': () => this.present_with_time(Gdk.CURRENT_TIME), 'hide': () => this.hide(), - 'window-size-dec': () => { + 'window-hsize-dec': () => { if (this.settings.get_boolean('window-maximize')) - this.settings.set_double('window-size', 1.0 - HEIGHT_MOD); + this.settings.set_double('window-hsize', 1.0 - HEIGHT_MOD); else - this.adjust_double_setting('window-size', -HEIGHT_MOD); + this.adjust_double_setting('window-hsize', -HEIGHT_MOD); }, - 'window-size-inc': () => { + 'window-hsize-inc': () => { if (!this.settings.get_boolean('window-maximize')) - this.adjust_double_setting('window-size', HEIGHT_MOD); + this.adjust_double_setting('window-hsize', HEIGHT_MOD); + }, + 'window-vsize-dec': () => { + if (this.settings.get_boolean('window-maximize')) + this.settings.set_double('window-vsize', 1.0 - HEIGHT_MOD); + else + this.adjust_double_setting('window-vsize', -HEIGHT_MOD); + }, + 'window-vsize-inc': () => { + if (!this.settings.get_boolean('window-maximize')) + this.adjust_double_setting('window-vsize', HEIGHT_MOD); }, 'background-opacity-dec': () => { this.adjust_double_setting('background-opacity', -OPACITY_MOD); diff --git a/ddterm/pref/positionsize.js b/ddterm/pref/positionsize.js index 02b1ac11..75085b08 100644 --- a/ddterm/pref/positionsize.js +++ b/ddterm/pref/positionsize.js @@ -37,7 +37,8 @@ export const PositionSizeWidget = GObject.registerClass({ Children: [ 'monitor_combo', 'window_pos_combo', - 'window_size_scale', + 'window_hsize_scale', + 'window_vsize_scale', ], Properties: { 'settings': GObject.ParamSpec.object( @@ -104,10 +105,12 @@ export const PositionSizeWidget = GObject.registerClass({ this.enable_monitor_combo(); bind_widget(this.settings, 'window-position', this.window_pos_combo); - bind_widget(this.settings, 'window-size', this.window_size_scale); + bind_widget(this.settings, 'window-hsize', this.window_hsize_scale); + bind_widget(this.settings, 'window-vsize', this.window_vsize_scale); const percent_format = new Intl.NumberFormat(undefined, { style: 'percent' }); - set_scale_value_format(this.window_size_scale, percent_format); + set_scale_value_format(this.window_hsize_scale, percent_format); + set_scale_value_format(this.window_vsize_scale, percent_format); } get title() { diff --git a/ddterm/pref/ui/prefs-position-size.ui b/ddterm/pref/ui/prefs-position-size.ui index 1d85d04e..f4f7ecf3 100644 --- a/ddterm/pref/ui/prefs-position-size.ui +++ b/ddterm/pref/ui/prefs-position-size.ui @@ -24,7 +24,12 @@ along with ddterm GNOME Shell extension. If not, see - + + 1 + 0.01 + 0.10 + + 1 0.01 0.10 @@ -44,9 +49,9 @@ along with ddterm GNOME Shell extension. If not, see True False start - Window _size: + Window _horizontal size: True - window_size_scale + window_hsize_scale 0 @@ -54,11 +59,11 @@ along with ddterm GNOME Shell extension. If not, see - + True True True - window_size_adjustment + window_hsize_adjustment True 2 2 @@ -68,6 +73,35 @@ along with ddterm GNOME Shell extension. If not, see 5 + + + True + False + start + Window _vertical size: + True + window_vsize_scale + + + 0 + 6 + + + + + True + True + True + window_vsize_adjustment + True + 2 + 2 + + + 1 + 6 + + True diff --git a/ddterm/pref/ui/prefs-shortcuts.ui b/ddterm/pref/ui/prefs-shortcuts.ui index c0f765ba..6d25cf8d 100644 --- a/ddterm/pref/ui/prefs-shortcuts.ui +++ b/ddterm/pref/ui/prefs-shortcuts.ui @@ -83,15 +83,29 @@ along with ddterm GNOME Shell extension. If not, see False - shortcut-window-size-inc - Increase Window Size + shortcut-window-hsize-inc + Increase Horizontal Window Size 0 0 False - shortcut-window-size-dec - Decrease Window Size + shortcut-window-hsize-dec + Decrease Horizontal Window Size + 0 + 0 + False + + + shortcut-window-vsize-inc + Increase Vertical Window Size + 0 + 0 + False + + + shortcut-window-vsize-dec + Decrease Vertical Window Size 0 0 False diff --git a/ddterm/shell/geometry.js b/ddterm/shell/geometry.js index eeba95c6..aa796707 100644 --- a/ddterm/shell/geometry.js +++ b/ddterm/shell/geometry.js @@ -82,8 +82,17 @@ export const WindowGeometry = GObject.registerClass({ Meta.MaximizeFlags, Meta.MaximizeFlags.VERTICAL ), - 'window-size': GObject.ParamSpec.double( - 'window-size', + 'window-hsize': GObject.ParamSpec.double( + 'window-hsize', + '', + '', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.EXPLICIT_NOTIFY, + 0, + 1, + 1 + ), + 'window-vsize': GObject.ParamSpec.double( + 'window-vsize', '', '', GObject.ParamFlags.READWRITE | GObject.ParamFlags.EXPLICIT_NOTIFY, @@ -121,7 +130,8 @@ export const WindowGeometry = GObject.registerClass({ this._workareas_changed_handler = global.display.connect('workareas-changed', this._update_workarea.bind(this)); - this.connect('notify::window-size', this._update_target_rect.bind(this)); + this.connect('notify::window-hsize', this._update_target_rect.bind(this)); + this.connect('notify::window-vsize', this._update_target_rect.bind(this)); this.connect('notify::window-position', this._update_window_position.bind(this)); this.connect('notify::window-monitor', this.update_monitor.bind(this)); this.connect('notify::window-monitor-connector', this.update_monitor.bind(this)); @@ -137,29 +147,33 @@ export const WindowGeometry = GObject.registerClass({ } } - static get_target_rect(workarea, monitor_scale, size, window_pos) { + static get_target_rect(workarea, monitor_scale, hsize, vsize, window_pos) { const target_rect = workarea.copy(); - if (window_pos === Meta.Side.LEFT || window_pos === Meta.Side.RIGHT) { - target_rect.width *= size; - target_rect.width -= target_rect.width % monitor_scale; + target_rect.width *= hsize; + target_rect.width -= target_rect.width % monitor_scale; + target_rect.height *= vsize; + target_rect.height -= target_rect.height % monitor_scale; - if (window_pos === Meta.Side.RIGHT) - target_rect.x += workarea.width - target_rect.width; - } else { - target_rect.height *= size; - target_rect.height -= target_rect.height % monitor_scale; + if (window_pos === Meta.Side.RIGHT) + target_rect.x += workarea.width - target_rect.width; - if (window_pos === Meta.Side.BOTTOM) - target_rect.y += workarea.height - target_rect.height; - } + if (window_pos === Meta.Side.BOTTOM) + target_rect.y += workarea.height - target_rect.height; + + if (window_pos === Meta.Side.TOP || window_pos === Meta.Side.BOTTOM) + target_rect.x = (workarea.width - target_rect.width) / 2; + + if (window_pos === Meta.Side.LEFT || window_pos === Meta.Side.RIGHT) + target_rect.y = (workarea.height - target_rect.height) / 2; return target_rect; } bind_settings(settings) { [ - 'window-size', + 'window-hsize', + 'window-vsize', 'window-position', 'window-monitor', 'window-monitor-connector', @@ -334,7 +348,8 @@ export const WindowGeometry = GObject.registerClass({ const target_rect = WindowGeometry.get_target_rect( this._workarea, Math.floor(global.display.get_monitor_scale(this._monitor_index)), - this.window_size, + this.window_hsize, + this.window_vsize, this.window_position ); diff --git a/ddterm/shell/wm.js b/ddterm/shell/wm.js index df83e39a..32d85766 100644 --- a/ddterm/shell/wm.js +++ b/ddterm/shell/wm.js @@ -93,7 +93,8 @@ export const WindowManager = GObject.registerClass({ this._settings_handlers = Object.entries({ 'changed::window-above': this._set_window_above.bind(this), 'changed::window-stick': this._set_window_stick.bind(this), - 'changed::window-size': this._disable_window_maximize_setting.bind(this), + 'changed::window-hsize': this._disable_window_maximize_setting.bind(this), + 'changed::window-vsize': this._disable_window_maximize_setting.bind(this), 'changed::window-maximize': this._set_window_maximized.bind(this), 'changed::override-window-animation': this._setup_animation_overrides.bind(this), 'changed::show-animation': this._update_show_animation.bind(this), @@ -587,11 +588,11 @@ export const WindowManager = GObject.registerClass({ this.debug?.('Updating size setting on grab end'); const frame_rect = win.get_frame_rect(); - const size = this.geometry.orientation === Clutter.Orientation.HORIZONTAL - ? frame_rect.width / this.geometry.workarea.width - : frame_rect.height / this.geometry.workarea.height; + const hsize = frame_rect.width / this.geometry.workarea.width; + const vsize = frame_rect.height / this.geometry.workarea.height; - this.settings.set_double('window-size', Math.min(1.0, size)); + this.settings.set_double('window-hsize', Math.min(1.0, hsize)); + this.settings.set_double('window-vsize', Math.min(1.0, vsize)); } unmaximize_for_resize(flags) { @@ -604,7 +605,8 @@ export const WindowManager = GObject.registerClass({ // There is a _update_window_geometry() call after successful unmaximize. // It must set window size to 100%. - this.settings.set_double('window-size', 1.0); + this.settings.set_double('window-hsize', 1.0); + this.settings.set_double('window-vsize', 1.0); Main.wm.skipNextEffect(this.window.get_compositor_private()); this.window.unmaximize(flags); diff --git a/schemas/com.github.amezin.ddterm.gschema.xml b/schemas/com.github.amezin.ddterm.gschema.xml index 5fbf3b48..c918c74c 100644 --- a/schemas/com.github.amezin.ddterm.gschema.xml +++ b/schemas/com.github.amezin.ddterm.gschema.xml @@ -132,7 +132,11 @@ - + + 1 + + + 0.6 @@ -384,10 +388,16 @@ - + + Down']]]> + + + Up']]]> + + Down']]]> - + Up']]]> From 97c77ecdca1b683a6879712c94d3f71df55f2ad4 Mon Sep 17 00:00:00 2001 From: "Martin N." Date: Wed, 17 Apr 2024 09:58:57 +1000 Subject: [PATCH 2/5] feat: Rename window horizontal/vertical size to width/height --- ddterm/pref/ui/prefs-position-size.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ddterm/pref/ui/prefs-position-size.ui b/ddterm/pref/ui/prefs-position-size.ui index f4f7ecf3..549e774b 100644 --- a/ddterm/pref/ui/prefs-position-size.ui +++ b/ddterm/pref/ui/prefs-position-size.ui @@ -49,7 +49,7 @@ along with ddterm GNOME Shell extension. If not, see True False start - Window _horizontal size: + Window _width: True window_hsize_scale @@ -78,7 +78,7 @@ along with ddterm GNOME Shell extension. If not, see True False start - Window _vertical size: + Window _height: True window_vsize_scale From be5ac10fca619c4fd06ff26dd97285198a2cb2ea Mon Sep 17 00:00:00 2001 From: "Martin N." Date: Wed, 17 Apr 2024 11:56:12 +1000 Subject: [PATCH 3/5] prefs: Swap window width/height when switching window position The settings change happens correctly, but for some reason the UI does not reflect the change. --- ddterm/shell/geometry.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ddterm/shell/geometry.js b/ddterm/shell/geometry.js index aa796707..ff34ee58 100644 --- a/ddterm/shell/geometry.js +++ b/ddterm/shell/geometry.js @@ -238,6 +238,16 @@ export const WindowGeometry = GObject.registerClass({ this.notify('pivot-point'); } + _swap_window_sizes() { + var hsize = this.window_hsize; + this.window_hsize = this.window_vsize; + this.window_vsize = hsize; + // TODO: Is this right? + this.notify('window-hsize'); + this.notify('window-vsize'); + this.notify('target-rect'); + } + _set_orientation(new_orientation) { if (this._orientation === new_orientation) return; @@ -320,12 +330,18 @@ export const WindowGeometry = GObject.registerClass({ switch (this.window_position) { case Meta.Side.LEFT: case Meta.Side.RIGHT: + if (this._orientation !== Clutter.Orientation.HORIZONTAL) + this._swap_window_sizes(); + this._set_orientation(Clutter.Orientation.HORIZONTAL); this._set_maximize_flag(Meta.MaximizeFlags.HORIZONTAL); break; case Meta.Side.TOP: case Meta.Side.BOTTOM: + if (this._orientation !== Clutter.Orientation.VERTICAL) + this._swap_window_sizes(); + this._set_orientation(Clutter.Orientation.VERTICAL); this._set_maximize_flag(Meta.MaximizeFlags.VERTICAL); } From 01a5dc49b2bde331456bd53f8a092b1636d85384 Mon Sep 17 00:00:00 2001 From: "Martin N." Date: Wed, 17 Apr 2024 11:57:27 +1000 Subject: [PATCH 4/5] fix: Only set maximize flag if window size 100% --- ddterm/shell/geometry.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ddterm/shell/geometry.js b/ddterm/shell/geometry.js index ff34ee58..bb609e60 100644 --- a/ddterm/shell/geometry.js +++ b/ddterm/shell/geometry.js @@ -334,7 +334,9 @@ export const WindowGeometry = GObject.registerClass({ this._swap_window_sizes(); this._set_orientation(Clutter.Orientation.HORIZONTAL); - this._set_maximize_flag(Meta.MaximizeFlags.HORIZONTAL); + + if (this.window_vsize >= 1.0) + this._set_maximize_flag(Meta.MaximizeFlags.HORIZONTAL); break; case Meta.Side.TOP: @@ -343,7 +345,9 @@ export const WindowGeometry = GObject.registerClass({ this._swap_window_sizes(); this._set_orientation(Clutter.Orientation.VERTICAL); - this._set_maximize_flag(Meta.MaximizeFlags.VERTICAL); + + if (this.window_hsize >= 1.0) + this._set_maximize_flag(Meta.MaximizeFlags.VERTICAL); } if (this._orientation === Clutter.Orientation.HORIZONTAL) From 62df5a5c509cbcd36bf042c1814af5c1d7ddd2ba Mon Sep 17 00:00:00 2001 From: "Martin N." Date: Wed, 17 Apr 2024 12:09:13 +1000 Subject: [PATCH 5/5] test: Try to fix tests for window size Purely focusing on getting existing tests to pass by assuming that the secondary size will always be 100% (which is how it used to work). --- test/test_extension.py | 77 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/test/test_extension.py b/test/test_extension.py index 200461c0..8ed9b0f2 100644 --- a/test/test_extension.py +++ b/test/test_extension.py @@ -1,5 +1,6 @@ import collections import contextlib +import dataclasses import enum import functools import logging.handlers @@ -90,21 +91,21 @@ def resize_point(frame_rect, window_pos): def compute_target_rect(size, pos, monitor): x, y, width, height = monitor.workarea + window_size = WindowSize.get_maximized_size_from_position(size, pos) round_to = int(monitor.scale) - if pos in [WindowPosition.TOP, WindowPosition.BOTTOM]: - height *= size - height -= height % round_to + height *= window_size.height + height -= height % round_to - if pos == WindowPosition.BOTTOM: - y += monitor.workarea.height - height - else: - width *= size - width -= width % round_to + if pos == WindowPosition.BOTTOM: + y += monitor.workarea.height - height - if pos == WindowPosition.RIGHT: - x += monitor.workarea.width - width + width *= window_size.width + width -= width % round_to + + if pos == WindowPosition.RIGHT: + x += monitor.workarea.width - width return Rect(x, y, width, height) @@ -140,6 +141,45 @@ def verify_window_geometry(test_interface, size, maximize, pos, monitor): assert actual_frame_rect == target_rect_unmaximized +@dataclasses.dataclass +class WindowSize: + width: float + height: float + position: WindowPosition + + _HORIZONTAL_SIZE_POSITIONS = [WindowPosition.TOP, WindowPosition.BOTTOM] + + def get_primary_size(self) -> float: + return self.width if self.position in self._HORIZONTAL_SIZE_POSITIONS else self.height + + @classmethod + def get_primary_size_setting(cls, position: WindowPosition) -> str: + return "window-hsize" if position in cls._HORIZONTAL_SIZE_POSITIONS else "window-vsize" + + @classmethod + def get_maximized_size_from_position( + cls, + primary_size: float, + position: WindowPosition) -> "WindowSize": + """ + Get window horizontal/vertical size based on the given position. + + On top/bottom positions, primary size is horizontal. + On left/right positions, primary size is vertical. + + This is a simplified helper to get a secondary size set to 100% for now, until tests + are updated/added to support secondary size as well. + """ + if position in cls._HORIZONTAL_SIZE_POSITIONS: + height = primary_size + width = 1.0 + else: + height = 1.0 + width = primary_size + + return WindowSize(width=width, height=height, position=position) + + @contextlib.contextmanager def wait_move_resize( test_interface, @@ -510,7 +550,10 @@ def test_show(self, test_api, monitor_config, window_pos, window_size, window_ma window_monitor = test_api.layout.resolve_monitor(monitor_config) prev_maximize = test_api.settings.get('window-maximize') - test_api.settings.set_double('window-size', window_size) + window_size_object = WindowSize.get_maximized_size_from_position(window_size, window_pos) + test_api.settings.set_double('window-hsize', window_size_object.width) + test_api.settings.set_double('window-vsize', window_size_object.height) + test_api.settings.set_string('window-position', window_pos) test_api.settings.set_string('window-monitor', monitor_config.setting) test_api.settings.set_boolean( @@ -622,8 +665,9 @@ def test_mouse_resize( test_api.mouse_sim.button(False) with glib_util.SignalWait(test_api.dbus, 'g-signal') as wait3: + window_size_setting = WindowSize.get_primary_size_setting(window_pos) while compute_target_rect( - size=test_api.settings.get('window-size'), + size=test_api.settings.get(window_size_setting), pos=window_pos, monitor=monitor ) != target_frame_rect: @@ -708,7 +752,8 @@ def test_unmaximize_correct_size( window_pos, monitor, ) as wait1: - test_api.settings.set_double('window-size', window_size2) + window_size_setting = WindowSize.get_primary_size_setting(window_pos) + test_api.settings.set_double(window_size_setting, window_size2) wait1() with wait_move_resize( @@ -756,7 +801,8 @@ def test_unmaximize_on_size_change( window_pos, monitor, ) as wait: - test_api.settings.set_double('window-size', window_size2) + window_size_setting = WindowSize.get_primary_size_setting(window_pos) + test_api.settings.set_double(window_size_setting, window_size2) wait() PARAM_TYPES = { @@ -820,7 +866,8 @@ def test_dark_mode(self, test_api, container, shell_dbus_api, x11_display): glib_util.flush_main_loop() - test_api.settings.set_double('window-size', 1) + test_api.settings.set_double('window-hsize', 1) + test_api.settings.set_double('window-vsize', 1) test_api.settings.set_string('window-position', WindowPosition.TOP) test_api.settings.set_string('window-monitor', MonitorSetting.PRIMARY) test_api.settings.set_boolean('window-maximize', True)