win32 gui: shrink window to be a multiple of size width
Let the win32 voodoo flow...
This commit is contained in:
parent
59bb2c0b45
commit
87a355b854
1 changed files with 203 additions and 32 deletions
|
@ -53,6 +53,7 @@ const global = struct {
|
||||||
var dwrite_factory: *win32.IDWriteFactory = undefined;
|
var dwrite_factory: *win32.IDWriteFactory = undefined;
|
||||||
var d2d_factory: *win32.ID2D1Factory = undefined;
|
var d2d_factory: *win32.ID2D1Factory = undefined;
|
||||||
var conf: ?*gui_config = null;
|
var conf: ?*gui_config = null;
|
||||||
|
var text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{};
|
||||||
|
|
||||||
const shared_screen = struct {
|
const shared_screen = struct {
|
||||||
var mutex: std.Thread.Mutex = .{};
|
var mutex: std.Thread.Mutex = .{};
|
||||||
|
@ -243,11 +244,11 @@ const State = struct {
|
||||||
pid: thespian.pid,
|
pid: thespian.pid,
|
||||||
maybe_d2d: ?D2d = null,
|
maybe_d2d: ?D2d = null,
|
||||||
erase_bg_done: bool = false,
|
erase_bg_done: bool = false,
|
||||||
text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{},
|
|
||||||
scroll_delta: isize = 0,
|
scroll_delta: isize = 0,
|
||||||
currently_rendered_cell_size: ?XY(i32) = null,
|
currently_rendered_cell_size: ?XY(i32) = null,
|
||||||
background: ?u32 = null,
|
background: ?u32 = null,
|
||||||
conf: gui_config,
|
conf: gui_config,
|
||||||
|
last_sizing_edge: ?win32.WPARAM = null,
|
||||||
};
|
};
|
||||||
fn stateFromHwnd(hwnd: win32.HWND) *State {
|
fn stateFromHwnd(hwnd: win32.HWND) *State {
|
||||||
const addr: usize = @bitCast(win32.GetWindowLongPtrW(hwnd, @enumFromInt(0)));
|
const addr: usize = @bitCast(win32.GetWindowLongPtrW(hwnd, @enumFromInt(0)));
|
||||||
|
@ -336,32 +337,16 @@ const WindowPlacement = struct {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn calcWindowPlacement(initial_window_x: u16, initial_window_y: u16) WindowPlacement {
|
fn calcWindowPlacement(
|
||||||
|
maybe_monitor: ?win32.HMONITOR,
|
||||||
|
dpi: u32,
|
||||||
|
cell_size: XY(i32),
|
||||||
|
initial_window_x: u16,
|
||||||
|
initial_window_y: u16,
|
||||||
|
) WindowPlacement {
|
||||||
var result = WindowPlacement.default;
|
var result = WindowPlacement.default;
|
||||||
|
|
||||||
const monitor = win32.MonitorFromPoint(
|
const monitor = maybe_monitor orelse return result;
|
||||||
.{ .x = 0, .y = 0 },
|
|
||||||
win32.MONITOR_DEFAULTTOPRIMARY,
|
|
||||||
) orelse {
|
|
||||||
std.log.warn("MonitorFromPoint failed with {}", .{win32.GetLastError().fmt()});
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
result.dpi = blk: {
|
|
||||||
var dpi: XY(u32) = undefined;
|
|
||||||
const hr = win32.GetDpiForMonitor(
|
|
||||||
monitor,
|
|
||||||
win32.MDT_EFFECTIVE_DPI,
|
|
||||||
&dpi.x,
|
|
||||||
&dpi.y,
|
|
||||||
);
|
|
||||||
if (hr < 0) {
|
|
||||||
std.log.warn("GetDpiForMonitor failed, hresult=0x{x}", .{@as(u32, @bitCast(hr))});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
break :blk dpi;
|
|
||||||
};
|
|
||||||
std.log.debug("primary monitor dpi {}x{}", .{ result.dpi.x, result.dpi.y });
|
|
||||||
|
|
||||||
const work_rect: win32.RECT = blk: {
|
const work_rect: win32.RECT = blk: {
|
||||||
var info: win32.MONITORINFO = undefined;
|
var info: win32.MONITORINFO = undefined;
|
||||||
|
@ -386,14 +371,26 @@ fn calcWindowPlacement(initial_window_x: u16, initial_window_y: u16) WindowPlace
|
||||||
.x = win32.scaleDpi(i32, @intCast(initial_window_x), result.dpi.x),
|
.x = win32.scaleDpi(i32, @intCast(initial_window_x), result.dpi.x),
|
||||||
.y = win32.scaleDpi(i32, @intCast(initial_window_y), result.dpi.y),
|
.y = win32.scaleDpi(i32, @intCast(initial_window_y), result.dpi.y),
|
||||||
};
|
};
|
||||||
result.size = .{
|
const unadjusted_size: XY(i32) = .{
|
||||||
.x = @min(wanted_size.x, work_size.x),
|
.x = @min(wanted_size.x, work_size.x),
|
||||||
.y = @min(wanted_size.y, work_size.y),
|
.y = @min(wanted_size.y, work_size.y),
|
||||||
};
|
};
|
||||||
result.pos = .{
|
const unadjusted_rect: win32.RECT = ddui.rectIntFromSize(.{
|
||||||
// TODO: maybe we should shift this window away from the center?
|
.left = work_rect.left + @divTrunc(work_size.x - unadjusted_size.x, 2),
|
||||||
.x = work_rect.left + @divTrunc(work_size.x - result.size.x, 2),
|
.top = work_rect.top + @divTrunc(work_size.y - unadjusted_size.y, 2),
|
||||||
.y = work_rect.top + @divTrunc(work_size.y - result.size.y, 2),
|
.width = unadjusted_size.x,
|
||||||
|
.height = unadjusted_size.y,
|
||||||
|
});
|
||||||
|
const adjusted_rect: win32.RECT = shrinkWindowRectForCells(
|
||||||
|
dpi,
|
||||||
|
unadjusted_rect,
|
||||||
|
null,
|
||||||
|
cell_size,
|
||||||
|
);
|
||||||
|
result.pos = .{ .x = adjusted_rect.left, .y = adjusted_rect.top };
|
||||||
|
result.size = .{
|
||||||
|
.x = adjusted_rect.right - adjusted_rect.left,
|
||||||
|
.y = adjusted_rect.bottom - adjusted_rect.top,
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -420,7 +417,39 @@ fn entry(pid: thespian.pid) !void {
|
||||||
root.write_config(conf, arena_instance.allocator()) catch
|
root.write_config(conf, arena_instance.allocator()) catch
|
||||||
std.log.err("failed to write gui config file", .{});
|
std.log.err("failed to write gui config file", .{});
|
||||||
|
|
||||||
|
const maybe_monitor: ?win32.HMONITOR = blk: {
|
||||||
|
break :blk win32.MonitorFromPoint(
|
||||||
|
.{ .x = 0, .y = 0 },
|
||||||
|
win32.MONITOR_DEFAULTTOPRIMARY,
|
||||||
|
) orelse {
|
||||||
|
std.log.warn("MonitorFromPoint failed with {}", .{win32.GetLastError().fmt()});
|
||||||
|
break :blk null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const dpi: XY(u32) = blk: {
|
||||||
|
const monitor = maybe_monitor orelse break :blk .{ .x = 96, .y = 96 };
|
||||||
|
var dpi: XY(u32) = undefined;
|
||||||
|
const hr = win32.GetDpiForMonitor(
|
||||||
|
monitor,
|
||||||
|
win32.MDT_EFFECTIVE_DPI,
|
||||||
|
&dpi.x,
|
||||||
|
&dpi.y,
|
||||||
|
);
|
||||||
|
if (hr < 0) {
|
||||||
|
std.log.warn("GetDpiForMonitor failed, hresult=0x{x}", .{@as(u32, @bitCast(hr))});
|
||||||
|
break :blk .{ .x = 96, .y = 96 };
|
||||||
|
}
|
||||||
|
std.log.debug("primary monitor dpi {}x{}", .{ dpi.x, dpi.y });
|
||||||
|
break :blk dpi;
|
||||||
|
};
|
||||||
|
|
||||||
|
const text_format_editor = global.text_format_editor.getOrCreate(Dpi{ .value = @max(dpi.x, dpi.y) });
|
||||||
|
const cell_size = getCellSize(text_format_editor);
|
||||||
const initial_placement = calcWindowPlacement(
|
const initial_placement = calcWindowPlacement(
|
||||||
|
maybe_monitor,
|
||||||
|
@max(dpi.x, dpi.y),
|
||||||
|
cell_size,
|
||||||
conf.initial_window_x,
|
conf.initial_window_x,
|
||||||
conf.initial_window_y,
|
conf.initial_window_y,
|
||||||
);
|
);
|
||||||
|
@ -1054,7 +1083,7 @@ fn WndProc(
|
||||||
}
|
}
|
||||||
state.maybe_d2d.?.target.ID2D1RenderTarget.BeginDraw();
|
state.maybe_d2d.?.target.ID2D1RenderTarget.BeginDraw();
|
||||||
|
|
||||||
const text_format_editor = state.text_format_editor.getOrCreate(Dpi{ .value = dpi });
|
const text_format_editor = global.text_format_editor.getOrCreate(Dpi{ .value = dpi });
|
||||||
state.currently_rendered_cell_size = getCellSize(text_format_editor);
|
state.currently_rendered_cell_size = getCellSize(text_format_editor);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1083,6 +1112,28 @@ fn WndProc(
|
||||||
} else if (err.hr < 0) std.debug.panic("paint error: {}", .{err});
|
} else if (err.hr < 0) std.debug.panic("paint error: {}", .{err});
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
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 cell_size = getCellSize(text_format_editor);
|
||||||
|
const new_rect = shrinkWindowRectForCells(
|
||||||
|
new_dpi,
|
||||||
|
.{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = inout_size.cx,
|
||||||
|
.bottom = inout_size.cy,
|
||||||
|
},
|
||||||
|
win32.WMSZ_BOTTOMRIGHT,
|
||||||
|
cell_size,
|
||||||
|
);
|
||||||
|
inout_size.* = .{
|
||||||
|
.cx = new_rect.right,
|
||||||
|
.cy = new_rect.bottom,
|
||||||
|
};
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
win32.WM_DPICHANGED => {
|
win32.WM_DPICHANGED => {
|
||||||
const dpi = win32.dpiFromHwnd(hwnd);
|
const dpi = win32.dpiFromHwnd(hwnd);
|
||||||
if (dpi != win32.hiword(wparam)) @panic("unexpected hiword dpi");
|
if (dpi != win32.hiword(wparam)) @panic("unexpected hiword dpi");
|
||||||
|
@ -1115,6 +1166,32 @@ fn WndProc(
|
||||||
sendResize(hwnd);
|
sendResize(hwnd);
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
win32.WM_SIZING => {
|
||||||
|
const state = stateFromHwnd(hwnd);
|
||||||
|
state.last_sizing_edge = wparam;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
state.last_sizing_edge = null;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
win32.WM_DISPLAYCHANGE => {
|
win32.WM_DISPLAYCHANGE => {
|
||||||
win32.invalidateHwnd(hwnd);
|
win32.invalidateHwnd(hwnd);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1191,7 +1268,7 @@ fn sendResize(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const single_cell_size = getCellSize(
|
const single_cell_size = getCellSize(
|
||||||
state.text_format_editor.getOrCreate(Dpi{ .value = @intCast(dpi) }),
|
global.text_format_editor.getOrCreate(Dpi{ .value = @intCast(dpi) }),
|
||||||
);
|
);
|
||||||
const client_pixel_size = getClientSize(hwnd);
|
const client_pixel_size = getClientSize(hwnd);
|
||||||
const client_cell_size: XY(u16) = .{
|
const client_cell_size: XY(u16) = .{
|
||||||
|
@ -1233,6 +1310,100 @@ fn getClientSize(hwnd: win32.HWND) XY(i32) {
|
||||||
std.debug.assert(rect.top == 0);
|
std.debug.assert(rect.top == 0);
|
||||||
return .{ .x = rect.right, .y = rect.bottom };
|
return .{ .x = rect.right, .y = rect.bottom };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shrinkWindowRectForCells(
|
||||||
|
dpi: u32,
|
||||||
|
rect: win32.RECT,
|
||||||
|
maybe_edge: ?win32.WPARAM,
|
||||||
|
cell_size: XY(i32),
|
||||||
|
) win32.RECT {
|
||||||
|
const window_size: XY(i32) = .{
|
||||||
|
.x = rect.right - rect.left,
|
||||||
|
.y = rect.bottom - rect.top,
|
||||||
|
};
|
||||||
|
const client_inset = getClientInset(dpi);
|
||||||
|
const client_size: XY(i32) = .{
|
||||||
|
.x = window_size.x - client_inset.x,
|
||||||
|
.y = window_size.y - client_inset.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
const diff: XY(i32) = .{
|
||||||
|
.x = -@mod(client_size.x, cell_size.x),
|
||||||
|
.y = -@mod(client_size.y, cell_size.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
const Adjustment = enum { low, high, both };
|
||||||
|
const adjustments: XY(Adjustment) = if (maybe_edge) |edge| switch (edge) {
|
||||||
|
win32.WMSZ_LEFT => .{ .x = .low, .y = .both },
|
||||||
|
win32.WMSZ_RIGHT => .{ .x = .high, .y = .both },
|
||||||
|
win32.WMSZ_TOP => .{ .x = .both, .y = .low },
|
||||||
|
win32.WMSZ_TOPLEFT => .{ .x = .low, .y = .low },
|
||||||
|
win32.WMSZ_TOPRIGHT => .{ .x = .high, .y = .low },
|
||||||
|
win32.WMSZ_BOTTOM => .{ .x = .both, .y = .high },
|
||||||
|
win32.WMSZ_BOTTOMLEFT => .{ .x = .low, .y = .high },
|
||||||
|
win32.WMSZ_BOTTOMRIGHT => .{ .x = .high, .y = .high },
|
||||||
|
else => .{ .x = .both, .y = .both },
|
||||||
|
} else .{ .x = .both, .y = .both };
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.left = rect.left - switch (adjustments.x) {
|
||||||
|
.low => diff.x,
|
||||||
|
.high => 0,
|
||||||
|
.both => @divTrunc(diff.x, 2),
|
||||||
|
},
|
||||||
|
.top = rect.top - switch (adjustments.y) {
|
||||||
|
.low => diff.y,
|
||||||
|
.high => 0,
|
||||||
|
.both => @divTrunc(diff.y, 2),
|
||||||
|
},
|
||||||
|
.right = rect.right + switch (adjustments.x) {
|
||||||
|
.low => 0,
|
||||||
|
.high => diff.x,
|
||||||
|
.both => @divTrunc(diff.x + 1, 2),
|
||||||
|
},
|
||||||
|
.bottom = rect.bottom + switch (adjustments.y) {
|
||||||
|
.low => 0,
|
||||||
|
.high => diff.y,
|
||||||
|
.both => @divTrunc(diff.y + 1, 2),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getClientInset(dpi: u32) XY(i32) {
|
||||||
|
var rect: win32.RECT = .{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = 0,
|
||||||
|
.bottom = 0,
|
||||||
|
};
|
||||||
|
if (0 == win32.AdjustWindowRectExForDpi(
|
||||||
|
&rect,
|
||||||
|
window_style,
|
||||||
|
0,
|
||||||
|
window_style_ex,
|
||||||
|
dpi,
|
||||||
|
)) fatalWin32(
|
||||||
|
"AdjustWindowRect",
|
||||||
|
win32.GetLastError(),
|
||||||
|
);
|
||||||
|
return .{
|
||||||
|
.x = rect.right - rect.left,
|
||||||
|
.y = rect.bottom - rect.top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setWindowPosRect(hwnd: win32.HWND, rect: win32.RECT) void {
|
||||||
|
if (0 == win32.SetWindowPos(
|
||||||
|
hwnd,
|
||||||
|
null, // ignored via NOZORDER
|
||||||
|
rect.left,
|
||||||
|
rect.top,
|
||||||
|
rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top,
|
||||||
|
.{ .NOZORDER = 1 },
|
||||||
|
)) fatalWin32("SetWindowPos", win32.GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn XY(comptime T: type) type {
|
pub fn XY(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
x: T,
|
x: T,
|
||||||
|
|
Loading…
Add table
Reference in a new issue