mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-08-16 18:10:32 +03:00
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
|
||||
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
|
||||
frame onto another (:iss:`3809`)
|
||||
|
||||
|
@ -32,11 +32,12 @@
|
||||
background_opacity_of, change_background_opacity, change_os_window_state,
|
||||
cocoa_set_menubar_title, create_os_window,
|
||||
current_application_quit_request, current_os_window, destroy_global_data,
|
||||
focus_os_window, get_clipboard_string, get_options, global_font_size,
|
||||
mark_os_window_for_close, os_window_font_size, patch_global_colors,
|
||||
safe_pipe, set_application_quit_request, set_background_image, set_boss,
|
||||
set_clipboard_string, set_in_sequence_mode, set_options, thread_write,
|
||||
toggle_fullscreen, toggle_maximized
|
||||
focus_os_window, get_clipboard_string, get_options, get_os_window_size,
|
||||
global_font_size, mark_os_window_for_close, os_window_font_size,
|
||||
patch_global_colors, safe_pipe, set_application_quit_request,
|
||||
set_background_image, set_boss, set_clipboard_string, set_in_sequence_mode,
|
||||
set_options, set_os_window_size, thread_write, toggle_fullscreen,
|
||||
toggle_maximized
|
||||
)
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
from .layout.base import set_layout_options
|
||||
@ -52,10 +53,11 @@
|
||||
from .types import SingleKey
|
||||
from .typing import PopenType, TypedDict
|
||||
from .utils import (
|
||||
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
||||
log_error, open_url, parse_address_spec, parse_uri_list,
|
||||
platform_window_id, read_shell_environment, remove_socket_file, safe_print,
|
||||
set_primary_selection, single_instance, startup_notification_handler
|
||||
func_name, get_editor, get_new_os_window_size, get_primary_selection,
|
||||
is_path_in_temp_dir, log_error, open_url, parse_address_spec,
|
||||
parse_uri_list, platform_window_id, read_shell_environment,
|
||||
remove_socket_file, safe_print, set_primary_selection, single_instance,
|
||||
startup_notification_handler
|
||||
)
|
||||
from .window import MatchPatternType, Window
|
||||
|
||||
@ -533,11 +535,11 @@ def close_tab_no_confirm(self, tab: Tab) -> None:
|
||||
self.close_window(window)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
def start(self, first_os_window_id: int) -> None:
|
||||
@ -784,6 +786,16 @@ def resize_layout_window(self, window: Window, increment: float, is_horizontal:
|
||||
return None
|
||||
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:
|
||||
w = self.window_id_map.get(window_id)
|
||||
if w is not None:
|
||||
|
@ -1202,6 +1202,8 @@ class OSWindowSize(TypedDict):
|
||||
yscale: float
|
||||
xdpi: float
|
||||
ydpi: float
|
||||
cell_width: int
|
||||
cell_height: int
|
||||
|
||||
|
||||
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;
|
||||
get_os_window_size(os_window, &width, &height, &fw, &fh);
|
||||
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,
|
||||
"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
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .options.types import Options
|
||||
from .fast_data_types import OSWindowSize
|
||||
else:
|
||||
Options = object
|
||||
|
||||
@ -692,3 +693,23 @@ class SSHConnectionData(NamedTuple):
|
||||
binary: str
|
||||
hostname: str
|
||||
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 @@
|
||||
|
||||
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')
|
||||
def test_utf_8_strndup(self):
|
||||
import ctypes
|
||||
|
Loading…
Reference in New Issue
Block a user