A new remote control command to resize OS Windows
This commit is contained in:
parent
aa339a8a9f
commit
419cf78984
@ -26,6 +26,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
could cause incorrect parsing if either the pending buffer capacity or the
|
could cause incorrect parsing if either the pending buffer capacity or the
|
||||||
pending timeout were exceeded (:iss:`3779`)
|
pending timeout were exceeded (:iss:`3779`)
|
||||||
|
|
||||||
|
- A new remote control command to :program:`resize the OS Window <kitty @
|
||||||
|
resize-os-window>`
|
||||||
|
|
||||||
- Graphics protocol: Add support for composing rectangles from one animation
|
- Graphics protocol: Add support for composing rectangles from one animation
|
||||||
frame onto another (:iss:`3809`)
|
frame onto another (:iss:`3809`)
|
||||||
|
|
||||||
|
|||||||
@ -32,11 +32,12 @@ from .fast_data_types import (
|
|||||||
background_opacity_of, change_background_opacity, change_os_window_state,
|
background_opacity_of, change_background_opacity, change_os_window_state,
|
||||||
cocoa_set_menubar_title, create_os_window,
|
cocoa_set_menubar_title, create_os_window,
|
||||||
current_application_quit_request, current_os_window, destroy_global_data,
|
current_application_quit_request, current_os_window, destroy_global_data,
|
||||||
focus_os_window, get_clipboard_string, get_options, global_font_size,
|
focus_os_window, get_clipboard_string, get_options, get_os_window_size,
|
||||||
mark_os_window_for_close, os_window_font_size, patch_global_colors,
|
global_font_size, mark_os_window_for_close, os_window_font_size,
|
||||||
safe_pipe, set_application_quit_request, set_background_image, set_boss,
|
patch_global_colors, safe_pipe, set_application_quit_request,
|
||||||
set_clipboard_string, set_in_sequence_mode, set_options, thread_write,
|
set_background_image, set_boss, set_clipboard_string, set_in_sequence_mode,
|
||||||
toggle_fullscreen, toggle_maximized
|
set_options, set_os_window_size, thread_write, toggle_fullscreen,
|
||||||
|
toggle_maximized
|
||||||
)
|
)
|
||||||
from .keys import get_shortcut, shortcut_matches
|
from .keys import get_shortcut, shortcut_matches
|
||||||
from .layout.base import set_layout_options
|
from .layout.base import set_layout_options
|
||||||
@ -52,10 +53,11 @@ from .tabs import (
|
|||||||
from .types import SingleKey
|
from .types import SingleKey
|
||||||
from .typing import PopenType, TypedDict
|
from .typing import PopenType, TypedDict
|
||||||
from .utils import (
|
from .utils import (
|
||||||
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
func_name, get_editor, get_new_os_window_size, get_primary_selection,
|
||||||
log_error, open_url, parse_address_spec, parse_uri_list,
|
is_path_in_temp_dir, log_error, open_url, parse_address_spec,
|
||||||
platform_window_id, read_shell_environment, remove_socket_file, safe_print,
|
parse_uri_list, platform_window_id, read_shell_environment,
|
||||||
set_primary_selection, single_instance, startup_notification_handler
|
remove_socket_file, safe_print, set_primary_selection, single_instance,
|
||||||
|
startup_notification_handler
|
||||||
)
|
)
|
||||||
from .window import MatchPatternType, Window
|
from .window import MatchPatternType, Window
|
||||||
|
|
||||||
@ -533,11 +535,11 @@ class Boss:
|
|||||||
self.close_window(window)
|
self.close_window(window)
|
||||||
|
|
||||||
def toggle_fullscreen(self, os_window_id: int = 0) -> None:
|
def toggle_fullscreen(self, os_window_id: int = 0) -> None:
|
||||||
'@ac:win: Toggle the fullscreen status of the specified or the active OS Window'
|
'@ac:win: Toggle the fullscreen status of the active OS Window'
|
||||||
toggle_fullscreen(os_window_id)
|
toggle_fullscreen(os_window_id)
|
||||||
|
|
||||||
def toggle_maximized(self, os_window_id: int = 0) -> None:
|
def toggle_maximized(self, os_window_id: int = 0) -> None:
|
||||||
'@ac:win: Toggle the maximized status of the specified or the active OS Window'
|
'@ac:win: Toggle the maximized status of the active OS Window'
|
||||||
toggle_maximized(os_window_id)
|
toggle_maximized(os_window_id)
|
||||||
|
|
||||||
def start(self, first_os_window_id: int) -> None:
|
def start(self, first_os_window_id: int) -> None:
|
||||||
@ -784,6 +786,16 @@ class Boss:
|
|||||||
return None
|
return None
|
||||||
return tab.resize_window_by(window.id, increment, is_horizontal)
|
return tab.resize_window_by(window.id, increment, is_horizontal)
|
||||||
|
|
||||||
|
def resize_os_window(self, os_window_id: int, width: int, height: int, unit: str, incremental: bool = False) -> None:
|
||||||
|
if not incremental and (width < 0 or height < 0):
|
||||||
|
return
|
||||||
|
metrics = get_os_window_size(os_window_id)
|
||||||
|
if metrics is None:
|
||||||
|
return
|
||||||
|
has_window_scaling = is_macos or is_wayland()
|
||||||
|
w, h = get_new_os_window_size(metrics, width, height, unit, incremental, has_window_scaling)
|
||||||
|
set_os_window_size(os_window_id, w, h)
|
||||||
|
|
||||||
def default_bg_changed_for(self, window_id: int) -> None:
|
def default_bg_changed_for(self, window_id: int) -> None:
|
||||||
w = self.window_id_map.get(window_id)
|
w = self.window_id_map.get(window_id)
|
||||||
if w is not None:
|
if w is not None:
|
||||||
|
|||||||
@ -1202,6 +1202,8 @@ class OSWindowSize(TypedDict):
|
|||||||
yscale: float
|
yscale: float
|
||||||
xdpi: float
|
xdpi: float
|
||||||
ydpi: float
|
ydpi: float
|
||||||
|
cell_width: int
|
||||||
|
cell_height: int
|
||||||
|
|
||||||
|
|
||||||
def get_os_window_size(os_window_id: int) -> Optional[OSWindowSize]:
|
def get_os_window_size(os_window_id: int) -> Optional[OSWindowSize]:
|
||||||
|
|||||||
102
kitty/rc/resize_os_window.py
Normal file
102
kitty/rc/resize_os_window.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
from .base import (
|
||||||
|
MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType,
|
||||||
|
PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from kitty.cli_stub import ResizeOSWindowRCOptions as CLIOptions
|
||||||
|
|
||||||
|
|
||||||
|
class ResizeOSWindow(RemoteCommand):
|
||||||
|
'''
|
||||||
|
match: Which window to resize
|
||||||
|
self: Boolean indicating whether to close the window the command is run in
|
||||||
|
incremental: Boolean indicating whether to adjust the size incrementally
|
||||||
|
action: One of :code:`resize, toggle-fullscreen` or :code:`toggle-maximized`
|
||||||
|
unit: One of :code:`cells` or :code:`pixels`
|
||||||
|
width: Integer indicating desired window width
|
||||||
|
height: Integer indicating desired window height
|
||||||
|
'''
|
||||||
|
|
||||||
|
short_desc = 'Resize the specified OS Window'
|
||||||
|
desc = (
|
||||||
|
'Resize the specified OS Window.'
|
||||||
|
' Note that some window managers/environments do not allow applications to resize'
|
||||||
|
' their windows, for example, tiling window managers.'
|
||||||
|
)
|
||||||
|
options_spec = MATCH_WINDOW_OPTION + '''\n
|
||||||
|
--action
|
||||||
|
default=resize
|
||||||
|
choices=resize,toggle-fullscreen,toggle-maximized
|
||||||
|
The action to perform.
|
||||||
|
|
||||||
|
|
||||||
|
--unit
|
||||||
|
default=cells
|
||||||
|
choices=cells,pixels
|
||||||
|
The unit in which to interpret specified sizes
|
||||||
|
|
||||||
|
|
||||||
|
--width
|
||||||
|
default=0
|
||||||
|
type=int
|
||||||
|
Change the width of the window. Zero leaves the width unchanged.
|
||||||
|
|
||||||
|
|
||||||
|
--height
|
||||||
|
default=0
|
||||||
|
type=int
|
||||||
|
Change the height of the window. Zero leaves the height unchanged.
|
||||||
|
|
||||||
|
|
||||||
|
--incremental
|
||||||
|
type=bool-set
|
||||||
|
Treat the specified sizes as increments on the existing window size
|
||||||
|
instead of absolute sizes.
|
||||||
|
|
||||||
|
|
||||||
|
--self
|
||||||
|
type=bool-set
|
||||||
|
If specified resize the window this command is run in, rather than the active window.
|
||||||
|
|
||||||
|
|
||||||
|
--no-response
|
||||||
|
type=bool-set
|
||||||
|
default=false
|
||||||
|
Don't wait for a response indicating the success of the action. Note that
|
||||||
|
using this option means that you will not be notified of failures.
|
||||||
|
'''
|
||||||
|
argspec = ''
|
||||||
|
|
||||||
|
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||||
|
if opts.no_response:
|
||||||
|
global_opts.no_command_response = True
|
||||||
|
return {
|
||||||
|
'match': opts.match, 'action': opts.action, 'unit': opts.unit,
|
||||||
|
'width': opts.width, 'height': opts.height, 'self': opts.self,
|
||||||
|
'incremental': opts.incremental
|
||||||
|
}
|
||||||
|
|
||||||
|
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
|
||||||
|
windows = self.windows_for_match_payload(boss, window, payload_get)
|
||||||
|
if windows:
|
||||||
|
ac = payload_get('action')
|
||||||
|
for os_window_id in {w.os_window_id for w in windows}:
|
||||||
|
if ac == 'resize':
|
||||||
|
boss.resize_os_window(
|
||||||
|
os_window_id, width=payload_get('width'), height=payload_get('height'),
|
||||||
|
unit=payload_get('unit'), incremental=payload_get('incremental')
|
||||||
|
)
|
||||||
|
elif ac == 'toggle-fullscreen':
|
||||||
|
boss.toggle_fullscreen(os_window_id)
|
||||||
|
elif ac == 'toggle-maximized':
|
||||||
|
boss.toggle_maximized(os_window_id)
|
||||||
|
|
||||||
|
|
||||||
|
resize_os_window = ResizeOSWindow()
|
||||||
@ -891,9 +891,11 @@ PYWRAP1(get_os_window_size) {
|
|||||||
int width, height, fw, fh;
|
int width, height, fw, fh;
|
||||||
get_os_window_size(os_window, &width, &height, &fw, &fh);
|
get_os_window_size(os_window, &width, &height, &fw, &fh);
|
||||||
get_os_window_content_scale(os_window, &xdpi, &ydpi, &xscale, &yscale);
|
get_os_window_content_scale(os_window, &xdpi, &ydpi, &xscale, &yscale);
|
||||||
return Py_BuildValue("{si si si si sf sf sd sd}",
|
unsigned int cell_width = os_window->fonts_data->cell_width, cell_height = os_window->fonts_data->cell_height;
|
||||||
|
return Py_BuildValue("{si si si si sf sf sd sd sI sI}",
|
||||||
"width", width, "height", height, "framebuffer_width", fw, "framebuffer_height", fh,
|
"width", width, "height", height, "framebuffer_width", fw, "framebuffer_height", fh,
|
||||||
"xscale", xscale, "yscale", yscale, "xdpi", xdpi, "ydpi", ydpi);
|
"xscale", xscale, "yscale", yscale, "xdpi", xdpi, "ydpi", ydpi,
|
||||||
|
"cell_width", cell_width, "cell_height", cell_height);
|
||||||
END_WITH_OS_WINDOW
|
END_WITH_OS_WINDOW
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ from .typing import AddressFamily, PopenType, Socket, StartupCtx
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .options.types import Options
|
from .options.types import Options
|
||||||
|
from .fast_data_types import OSWindowSize
|
||||||
else:
|
else:
|
||||||
Options = object
|
Options = object
|
||||||
|
|
||||||
@ -692,3 +693,23 @@ class SSHConnectionData(NamedTuple):
|
|||||||
binary: str
|
binary: str
|
||||||
hostname: str
|
hostname: str
|
||||||
port: Optional[int] = None
|
port: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_os_window_size(
|
||||||
|
metrics: 'OSWindowSize', width: int, height: int, unit: str, incremental: bool = False, has_window_scaling: bool = True
|
||||||
|
) -> Tuple[int, int]:
|
||||||
|
if unit == 'cells':
|
||||||
|
cw = metrics['cell_width']
|
||||||
|
ch = metrics['cell_height']
|
||||||
|
if has_window_scaling:
|
||||||
|
cw = int(cw / metrics['xscale'])
|
||||||
|
ch = int(ch / metrics['yscale'])
|
||||||
|
width *= cw
|
||||||
|
height *= ch
|
||||||
|
if incremental:
|
||||||
|
w = metrics['width'] + width
|
||||||
|
h = metrics['height'] + height
|
||||||
|
else:
|
||||||
|
w = width or metrics['width']
|
||||||
|
h = height or metrics['height']
|
||||||
|
return w, h
|
||||||
|
|||||||
@ -12,6 +12,34 @@ is_macos = 'darwin' in _plat
|
|||||||
|
|
||||||
class TestGLFW(BaseTest):
|
class TestGLFW(BaseTest):
|
||||||
|
|
||||||
|
def test_os_window_size_calculation(self):
|
||||||
|
from kitty.utils import get_new_os_window_size
|
||||||
|
|
||||||
|
def t(w, h, width=0, height=0, unit='cells', incremental=False):
|
||||||
|
self.ae((w, h), get_new_os_window_size(metrics, width, height, unit, incremental, has_window_scaling))
|
||||||
|
|
||||||
|
with self.subTest(has_window_scaling=False):
|
||||||
|
has_window_scaling = False
|
||||||
|
metrics = {
|
||||||
|
'width': 200, 'height': 100,
|
||||||
|
'framebuffer_width': 200, 'framebuffer_height': 100,
|
||||||
|
'xscale': 2.0, 'yscale': 2.0, 'xdpi': 192.0, 'ydpi': 192.0,
|
||||||
|
'cell_width': 8, 'cell_height': 16
|
||||||
|
}
|
||||||
|
t(80 * metrics['cell_width'], 100, 80)
|
||||||
|
t(80 * metrics['cell_width'] + metrics['width'], 100, 80, incremental=True)
|
||||||
|
t(1217, 100, 1217, unit='pixels')
|
||||||
|
t(1217 + metrics['width'], 100, 1217, unit='pixels', incremental=True)
|
||||||
|
|
||||||
|
with self.subTest(has_window_scaling=True):
|
||||||
|
has_window_scaling = True
|
||||||
|
metrics['framebuffer_width'] = metrics['width'] * 2
|
||||||
|
metrics['framebuffer_height'] = metrics['height'] * 2
|
||||||
|
t(80 * metrics['cell_width'] / metrics['xscale'], 100, 80)
|
||||||
|
t(80 * metrics['cell_width'] / metrics['xscale'] + metrics['width'], 100, 80, incremental=True)
|
||||||
|
t(1217, 100, 1217, unit='pixels')
|
||||||
|
t(1217 + metrics['width'], 100, 1217, unit='pixels', incremental=True)
|
||||||
|
|
||||||
@unittest.skipIf(is_macos, 'Skipping test on macOS because glfw-cocoa.so is not built with backend_utils')
|
@unittest.skipIf(is_macos, 'Skipping test on macOS because glfw-cocoa.so is not built with backend_utils')
|
||||||
def test_utf_8_strndup(self):
|
def test_utf_8_strndup(self):
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user