feat(win32 gui): add adjust_fontsize command to zoom in/out

This commit is contained in:
CJ van den Berg 2025-01-09 20:08:12 +01:00
parent fd86653db1
commit 5300e3346d
4 changed files with 76 additions and 29 deletions

View file

@ -1,6 +1,8 @@
{ {
"project": { "project": {
"press": [ "press": [
["ctrl+plus", "adjust_fontsize", 1.0],
["ctrl+minus", "adjust_fontsize", -1.0],
["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]], ["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]],
["f7", ["create_scratch_buffer", "*build*"], ["shell_execute_insert", "zig", "build"]], ["f7", ["create_scratch_buffer", "*build*"], ["shell_execute_insert", "zig", "build"]],
["alt+d", ["shell_execute_log", "date"]] ["alt+d", ["shell_execute_log", "date"]]

View file

@ -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 { pub fn set_terminal_cursor_color(self: *Self, color: Color) void {
_ = self; _ = self;
_ = color; _ = color;

View file

@ -9,6 +9,7 @@ const project_manager = @import("project_manager");
const log = @import("log"); const log = @import("log");
const shell = @import("shell"); const shell = @import("shell");
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_options = @import("build_options");
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
const input = @import("input"); const input = @import("input");
@ -633,6 +634,15 @@ const cmds = struct {
try shell.execute(self.allocator, cmd, .{ .out = handlers.out }); try shell.execute(self.allocator, cmd, .{ .out = handlers.out });
} }
pub const shell_execute_insert_meta = .{ .arguments = &.{.string} }; 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 { pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result {

View file

@ -21,9 +21,11 @@ const HResultError = ddui.HResultError;
const WM_APP_EXIT = win32.WM_APP + 1; const WM_APP_EXIT = win32.WM_APP + 1;
const WM_APP_SET_BACKGROUND = win32.WM_APP + 2; 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_EXIT_RESULT = 0x45feaa11;
const WM_APP_SET_BACKGROUND_RESULT = 0x369a26cd; const WM_APP_SET_BACKGROUND_RESULT = 0x369a26cd;
const WM_APP_ADJUST_FONTSIZE_RESULT = 0x79aba9ef;
pub const DropWriter = struct { pub const DropWriter = struct {
pub const WriteError = error{}; pub const WriteError = error{};
@ -57,8 +59,9 @@ const global = struct {
var d2d_factory: *win32.ID2D1Factory = undefined; var d2d_factory: *win32.ID2D1Factory = undefined;
var conf: ?gui_config = null; var conf: ?gui_config = null;
var fontface: ?[:0]const u16 = 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 { const shared_screen = struct {
var mutex: std.Thread.Mutex = .{}; var mutex: std.Thread.Mutex = .{};
@ -148,10 +151,11 @@ fn d2dColorFromVAxis(color: vaxis.Cell.Color) win32.D2D_COLOR_F {
}; };
} }
const Dpi = struct { const FontCacheParams = struct {
value: u32, dpi: u32,
pub fn eql(self: Dpi, other: Dpi) bool { fontsize: f32,
return self.value == other.value; 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.?; return global.fontface.?;
} }
fn createTextFormatEditor(dpi: Dpi) *win32.IDWriteTextFormat { fn getFontSize() f32 {
const conf = getConfig(); if (global.fontsize == null) {
global.fontsize = @floatFromInt(getConfig().fontsize);
}
return global.fontsize.?;
}
fn createTextFormatEditor(params: FontCacheParams) *win32.IDWriteTextFormat {
var err: HResultError = undefined; var err: HResultError = undefined;
return ddui.createTextFormat(global.dwrite_factory, &err, .{ 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(), .family_name = getFontFace(),
}) catch std.debug.panic("{s} failed, hresult=0x{x}", .{ err.context, err.hr }); }) 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; 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 cell_size = getCellSize(text_format_editor);
const initial_placement = calcWindowPlacement( const initial_placement = calcWindowPlacement(
maybe_monitor, 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 { pub fn updateScreen(screen: *const vaxis.Screen) void {
global.shared_screen.mutex.lock(); global.shared_screen.mutex.lock();
defer global.shared_screen.mutex.unlock(); 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 // NOTE: we round the text metric up to the nearest integer which
// means our background rectangles will be aligned. We accomodate // means our background rectangles will be aligned. We accomodate
// for any gap added by doing this by centering the text. // 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_F4) => input.key.f4,
@intFromEnum(win32.VK_F5) => input.key.f5, @intFromEnum(win32.VK_F5) => input.key.f5,
@intFromEnum(win32.VK_F6) => input.key.f6, @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_F8) => input.key.f8,
@intFromEnum(win32.VK_F9) => input.key.f9, @intFromEnum(win32.VK_F9) => input.key.f9,
@intFromEnum(win32.VK_F10) => input.key.f10, @intFromEnum(win32.VK_F10) => input.key.f10,
@ -1093,7 +1131,7 @@ fn WndProc(
} }
state.maybe_d2d.?.target.ID2D1RenderTarget.BeginDraw(); 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); state.currently_rendered_cell_size = getCellSize(text_format_editor);
{ {
@ -1125,7 +1163,7 @@ fn WndProc(
win32.WM_GETDPISCALEDSIZE => { win32.WM_GETDPISCALEDSIZE => {
const inout_size: *win32.SIZE = @ptrFromInt(@as(usize, @bitCast(lparam))); const inout_size: *win32.SIZE = @ptrFromInt(@as(usize, @bitCast(lparam)));
const new_dpi: u32 = @intCast(0xffffffff & wparam); 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 cell_size = getCellSize(text_format_editor);
const new_rect = shrinkWindowRectForCells( const new_rect = shrinkWindowRectForCells(
new_dpi, new_dpi,
@ -1183,22 +1221,7 @@ fn WndProc(
}, },
win32.WM_EXITSIZEMOVE => { win32.WM_EXITSIZEMOVE => {
const state = stateFromHwnd(hwnd); const state = stateFromHwnd(hwnd);
const dpi = win32.dpiFromHwnd(hwnd); updateWindowSize(hwnd, state.last_sizing_edge);
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);
state.last_sizing_edge = null; state.last_sizing_edge = null;
return 0; return 0;
}, },
@ -1235,6 +1258,13 @@ fn WndProc(
win32.invalidateHwnd(hwnd); win32.invalidateHwnd(hwnd);
return WM_APP_SET_BACKGROUND_RESULT; 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 => { win32.WM_CREATE => {
std.debug.assert(global.state == null); std.debug.assert(global.state == null);
const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam))); const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam)));
@ -1269,7 +1299,7 @@ fn sendResize(
); );
} }
const single_cell_size = getCellSize( 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_pixel_size = getClientSize(hwnd);
const client_cell_size: XY(u16) = .{ const client_cell_size: XY(u16) = .{