From 5300e3346d2fafc36806f6c7ae3ec9b25efc1823 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 9 Jan 2025 20:08:12 +0100 Subject: [PATCH] feat(win32 gui): add adjust_fontsize command to zoom in/out --- src/keybind/builtin/flow.json | 2 + src/renderer/win32/renderer.zig | 5 ++ src/tui/mainview.zig | 10 ++++ src/win32/gui.zig | 88 ++++++++++++++++++++++----------- 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 168aebe..6345f23 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -1,6 +1,8 @@ { "project": { "press": [ + ["ctrl+plus", "adjust_fontsize", 1.0], + ["ctrl+minus", "adjust_fontsize", -1.0], ["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]], ["f7", ["create_scratch_buffer", "*build*"], ["shell_execute_insert", "zig", "build"]], ["alt+d", ["shell_execute_log", "date"]] diff --git a/src/renderer/win32/renderer.zig b/src/renderer/win32/renderer.zig index 171e5e9..65337b1 100644 --- a/src/renderer/win32/renderer.zig +++ b/src/renderer/win32/renderer.zig @@ -371,6 +371,11 @@ fn update_window_style(self: *Self) void { } } +pub fn adjust_fontsize(self: *Self, amount: f32) void { + const hwnd = self.hwnd orelse return; + gui.adjust_fontsize(hwnd, amount); +} + pub fn set_terminal_cursor_color(self: *Self, color: Color) void { _ = self; _ = color; diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index db6aa7f..e225177 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -9,6 +9,7 @@ const project_manager = @import("project_manager"); const log = @import("log"); const shell = @import("shell"); const builtin = @import("builtin"); +const build_options = @import("build_options"); const Plane = @import("renderer").Plane; const input = @import("input"); @@ -633,6 +634,15 @@ const cmds = struct { try shell.execute(self.allocator, cmd, .{ .out = handlers.out }); } pub const shell_execute_insert_meta = .{ .arguments = &.{.string} }; + + pub fn adjust_fontsize(_: *Self, ctx: Ctx) Result { + var amount: f32 = undefined; + if (!try ctx.args.match(.{tp.extract(&amount)})) + return error.InvalidArgument; + if (build_options.gui) + tui.current().rdr.adjust_fontsize(amount); + } + pub const adjust_fontsize_meta = .{ .arguments = &.{.float} }; }; pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { diff --git a/src/win32/gui.zig b/src/win32/gui.zig index aa36949..72a92d7 100644 --- a/src/win32/gui.zig +++ b/src/win32/gui.zig @@ -21,9 +21,11 @@ const HResultError = ddui.HResultError; const WM_APP_EXIT = win32.WM_APP + 1; const WM_APP_SET_BACKGROUND = win32.WM_APP + 2; +const WM_APP_ADJUST_FONTSIZE = win32.WM_APP + 3; const WM_APP_EXIT_RESULT = 0x45feaa11; const WM_APP_SET_BACKGROUND_RESULT = 0x369a26cd; +const WM_APP_ADJUST_FONTSIZE_RESULT = 0x79aba9ef; pub const DropWriter = struct { pub const WriteError = error{}; @@ -57,8 +59,9 @@ const global = struct { var d2d_factory: *win32.ID2D1Factory = undefined; var conf: ?gui_config = null; var fontface: ?[:0]const u16 = null; + var fontsize: ?f32 = null; - var text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{}; + var text_format_editor: ddui.TextFormatCache(FontCacheParams, createTextFormatEditor) = .{}; const shared_screen = struct { var mutex: std.Thread.Mutex = .{}; @@ -148,10 +151,11 @@ fn d2dColorFromVAxis(color: vaxis.Cell.Color) win32.D2D_COLOR_F { }; } -const Dpi = struct { - value: u32, - pub fn eql(self: Dpi, other: Dpi) bool { - return self.value == other.value; +const FontCacheParams = struct { + dpi: u32, + fontsize: f32, + pub fn eql(self: FontCacheParams, other: FontCacheParams) bool { + return self.dpi == other.dpi and self.fontsize == other.fontsize; } }; @@ -184,11 +188,17 @@ fn getFontFace() [:0]const u16 { return global.fontface.?; } -fn createTextFormatEditor(dpi: Dpi) *win32.IDWriteTextFormat { - const conf = getConfig(); +fn getFontSize() f32 { + if (global.fontsize == null) { + global.fontsize = @floatFromInt(getConfig().fontsize); + } + return global.fontsize.?; +} + +fn createTextFormatEditor(params: FontCacheParams) *win32.IDWriteTextFormat { var err: HResultError = undefined; return ddui.createTextFormat(global.dwrite_factory, &err, .{ - .size = win32.scaleDpi(f32, @as(f32, @floatFromInt(conf.fontsize)), dpi.value), + .size = win32.scaleDpi(f32, params.fontsize, params.dpi), .family_name = getFontFace(), }) catch std.debug.panic("{s} failed, hresult=0x{x}", .{ err.context, err.hr }); } @@ -456,7 +466,7 @@ fn entry(pid: thespian.pid) !void { break :blk dpi; }; - const text_format_editor = global.text_format_editor.getOrCreate(Dpi{ .value = @max(dpi.x, dpi.y) }); + const text_format_editor = global.text_format_editor.getOrCreate(FontCacheParams{ .dpi = @max(dpi.x, dpi.y), .fontsize = @floatFromInt(conf.fontsize) }); const cell_size = getCellSize(text_format_editor); const initial_placement = calcWindowPlacement( maybe_monitor, @@ -556,6 +566,15 @@ pub fn set_window_background(hwnd: win32.HWND, color: u32) void { )); } +pub fn adjust_fontsize(hwnd: win32.HWND, amount: f32) void { + std.debug.assert(WM_APP_ADJUST_FONTSIZE_RESULT == win32.SendMessageW( + hwnd, + WM_APP_ADJUST_FONTSIZE, + @as(u32, @bitCast(amount)), + 0, + )); +} + pub fn updateScreen(screen: *const vaxis.Screen) void { global.shared_screen.mutex.lock(); defer global.shared_screen.mutex.unlock(); @@ -585,6 +604,25 @@ pub fn updateScreen(screen: *const vaxis.Screen) void { }; } +fn updateWindowSize(hwnd: win32.HWND, edge: ?win32.WPARAM) void { + const dpi = win32.dpiFromHwnd(hwnd); + const text_format_editor = global.text_format_editor.getOrCreate(FontCacheParams{ .dpi = dpi, .fontsize = getFontSize() }); + const cell_size = getCellSize(text_format_editor); + + var window_rect: win32.RECT = undefined; + if (0 == win32.GetWindowRect(hwnd, &window_rect)) fatalWin32( + "GetWindowRect", + win32.GetLastError(), + ); + const new_rect = shrinkWindowRectForCells( + dpi, + window_rect, + edge, + cell_size, + ); + setWindowPosRect(hwnd, new_rect); +} + // NOTE: we round the text metric up to the nearest integer which // means our background rectangles will be aligned. We accomodate // for any gap added by doing this by centering the text. @@ -948,7 +986,7 @@ const WinKey = struct { @intFromEnum(win32.VK_F4) => input.key.f4, @intFromEnum(win32.VK_F5) => input.key.f5, @intFromEnum(win32.VK_F6) => input.key.f6, - @intFromEnum(win32.VK_F7) => input.key.f8, + @intFromEnum(win32.VK_F7) => input.key.f7, @intFromEnum(win32.VK_F8) => input.key.f8, @intFromEnum(win32.VK_F9) => input.key.f9, @intFromEnum(win32.VK_F10) => input.key.f10, @@ -1093,7 +1131,7 @@ fn WndProc( } state.maybe_d2d.?.target.ID2D1RenderTarget.BeginDraw(); - const text_format_editor = global.text_format_editor.getOrCreate(Dpi{ .value = dpi }); + const text_format_editor = global.text_format_editor.getOrCreate(FontCacheParams{ .dpi = dpi, .fontsize = getFontSize() }); state.currently_rendered_cell_size = getCellSize(text_format_editor); { @@ -1125,7 +1163,7 @@ fn WndProc( win32.WM_GETDPISCALEDSIZE => { const inout_size: *win32.SIZE = @ptrFromInt(@as(usize, @bitCast(lparam))); const new_dpi: u32 = @intCast(0xffffffff & wparam); - const text_format_editor = global.text_format_editor.getOrCreate(Dpi{ .value = new_dpi }); + const text_format_editor = global.text_format_editor.getOrCreate(FontCacheParams{ .dpi = new_dpi, .fontsize = getFontSize() }); const cell_size = getCellSize(text_format_editor); const new_rect = shrinkWindowRectForCells( new_dpi, @@ -1183,22 +1221,7 @@ fn WndProc( }, win32.WM_EXITSIZEMOVE => { const state = stateFromHwnd(hwnd); - const dpi = win32.dpiFromHwnd(hwnd); - const text_format_editor = global.text_format_editor.getOrCreate(Dpi{ .value = dpi }); - const cell_size = getCellSize(text_format_editor); - - var window_rect: win32.RECT = undefined; - if (0 == win32.GetWindowRect(hwnd, &window_rect)) fatalWin32( - "GetWindowRect", - win32.GetLastError(), - ); - const new_rect = shrinkWindowRectForCells( - dpi, - window_rect, - state.last_sizing_edge, - cell_size, - ); - setWindowPosRect(hwnd, new_rect); + updateWindowSize(hwnd, state.last_sizing_edge); state.last_sizing_edge = null; return 0; }, @@ -1235,6 +1258,13 @@ fn WndProc( win32.invalidateHwnd(hwnd); return WM_APP_SET_BACKGROUND_RESULT; }, + WM_APP_ADJUST_FONTSIZE => { + const amount: f32 = @bitCast(@as(u32, @intCast(0xFFFFFFFFF & wparam))); + global.fontsize = @max(getFontSize() + amount, 1.0); + updateWindowSize(hwnd, win32.WMSZ_BOTTOMRIGHT); + win32.invalidateHwnd(hwnd); + return WM_APP_ADJUST_FONTSIZE_RESULT; + }, win32.WM_CREATE => { std.debug.assert(global.state == null); const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam))); @@ -1269,7 +1299,7 @@ fn sendResize( ); } const single_cell_size = getCellSize( - global.text_format_editor.getOrCreate(Dpi{ .value = @intCast(dpi) }), + global.text_format_editor.getOrCreate(FontCacheParams{ .dpi = @intCast(dpi), .fontsize = getFontSize() }), ); const client_pixel_size = getClientSize(hwnd); const client_cell_size: XY(u16) = .{