feat(win32 gui): read fontface and size from gui_config

This commit is contained in:
CJ van den Berg 2025-01-08 15:06:53 +01:00
parent 1acc9b107e
commit 9e4e81eb0d
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
8 changed files with 61 additions and 14 deletions

View file

@ -452,6 +452,7 @@ pub fn build_exe(
.{ .name = "thespian", .module = thespian_mod }, .{ .name = "thespian", .module = thespian_mod },
.{ .name = "cbor", .module = cbor_mod }, .{ .name = "cbor", .module = cbor_mod },
.{ .name = "config", .module = config_mod }, .{ .name = "config", .module = config_mod },
.{ .name = "gui_config", .module = gui_config_mod },
.{ .name = "log", .module = log_mod }, .{ .name = "log", .module = log_mod },
.{ .name = "command", .module = command_mod }, .{ .name = "command", .module = command_mod },
.{ .name = "EventHandler", .module = EventHandler_mod }, .{ .name = "EventHandler", .module = EventHandler_mod },

View file

@ -1,4 +1,4 @@
fontface: [] const u8 = "Cascadia Code", fontface: [] const u8 = "Cascadia Code",
fontsize: f32 = 17.5, fontsize: u8 = 17,
include_files: []const u8 = "", include_files: []const u8 = "",

View file

@ -199,6 +199,7 @@
["r", "open_recent_project"], ["r", "open_recent_project"],
["p", "open_command_palette"], ["p", "open_command_palette"],
["c", "open_config"], ["c", "open_config"],
["g", "open_gui_config"],
["k", "open_keybind_config"], ["k", "open_keybind_config"],
["t", "change_theme"], ["t", "change_theme"],
["q", "quit"], ["q", "quit"],

View file

@ -393,7 +393,11 @@ pub fn free_config(allocator: std.mem.Allocator, bufs: [][]const u8) void {
for (bufs) |buf| allocator.free(buf); for (bufs) |buf| allocator.free(buf);
} }
var config_mutex: std.Thread.Mutex = .{};
pub fn read_config(T: type, allocator: std.mem.Allocator) struct { T, [][]const u8 } { pub fn read_config(T: type, allocator: std.mem.Allocator) struct { T, [][]const u8 } {
config_mutex.lock();
defer config_mutex.unlock();
var bufs: [][]const u8 = &[_][]const u8{}; var bufs: [][]const u8 = &[_][]const u8{};
const file_name = get_app_config_file_name(application_name, @typeName(T)) catch return .{ .{}, bufs }; const file_name = get_app_config_file_name(application_name, @typeName(T)) catch return .{ .{}, bufs };
var conf: T = .{}; var conf: T = .{};
@ -404,7 +408,7 @@ pub fn read_config(T: type, allocator: std.mem.Allocator) struct { T, [][]const
fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) void { fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) void {
read_json_config_file(T, allocator, conf, bufs, file_name) catch |e| read_json_config_file(T, allocator, conf, bufs, file_name) catch |e|
log.logger("config").print_err("read_config", "error reading config file: {any}", .{e}); std.log.err("error reading config file '{s}' {any}", .{ file_name, e });
return; return;
} }
@ -453,6 +457,8 @@ fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bu
} }
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void { pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void {
config_mutex.lock();
defer config_mutex.unlock();
return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf)))); return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf))));
} }
@ -643,14 +649,8 @@ fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8)
fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ![]const u8 { fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ![]const u8 {
const local = struct { const local = struct {
var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined; var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined;
var config_file: ?[]const u8 = null;
}; };
const config_file = if (local.config_file) |file| return std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name });
file
else
try std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name });
local.config_file = config_file;
return config_file;
} }
pub fn get_config_file_name(T: type) ![]const u8 { pub fn get_config_file_name(T: type) ![]const u8 {

View file

@ -134,7 +134,10 @@ pub fn stop(self: *Self) void {
// the window is created and we call dispatch_initialized // the window is created and we call dispatch_initialized
const hwnd = self.hwnd orelse unreachable; const hwnd = self.hwnd orelse unreachable;
gui.stop(hwnd); gui.stop(hwnd);
if (self.thread) |thread| thread.join(); if (self.thread) |thread| {
thread.join();
self.thread = null;
}
} }
pub fn stdplane(self: *Self) Plane { pub fn stdplane(self: *Self) Plane {

View file

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const build_options = @import("build_options");
const tp = @import("thespian"); const tp = @import("thespian");
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
@ -25,7 +26,19 @@ input_namespace: []const u8,
const Self = @This(); const Self = @This();
const menu_commands = &[_][]const u8{ const menu_commands = if (build_options.gui) &[_][]const u8{
"open_help",
"open_file",
"open_recent",
"open_recent_project",
"open_command_palette",
"open_config",
"open_gui_config",
"open_keybind_config",
"toggle_input_mode",
"change_theme",
"quit",
} else &[_][]const u8{
"open_help", "open_help",
"open_file", "open_file",
"open_recent", "open_recent",

View file

@ -378,11 +378,17 @@ const cmds = struct {
pub const open_help_meta = .{ .description = "Open help" }; pub const open_help_meta = .{ .description = "Open help" };
pub fn open_config(_: *Self, _: Ctx) Result { pub fn open_config(_: *Self, _: Ctx) Result {
const file_name = try root.get_config_file_name(@TypeOf(tui.current().config)); const file_name = try root.get_config_file_name(@import("config"));
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } }); try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
} }
pub const open_config_meta = .{ .description = "Edit configuration file" }; pub const open_config_meta = .{ .description = "Edit configuration file" };
pub fn open_gui_config(_: *Self, _: Ctx) Result {
const file_name = try root.get_config_file_name(@import("gui_config"));
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
}
pub const open_gui_config_meta = .{ .description = "Edit gui configuration file" };
pub fn create_scratch_buffer(self: *Self, ctx: Ctx) Result { pub fn create_scratch_buffer(self: *Self, ctx: Ctx) Result {
try self.check_all_not_dirty(); try self.check_all_not_dirty();
tui.reset_drag_context(); tui.reset_drag_context();

View file

@ -52,6 +52,7 @@ const global = struct {
var icons: Icons = undefined; var icons: Icons = undefined;
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;
const shared_screen = struct { const shared_screen = struct {
var mutex: std.Thread.Mutex = .{}; var mutex: std.Thread.Mutex = .{};
@ -149,10 +150,26 @@ const Dpi = struct {
}; };
fn createTextFormatEditor(dpi: Dpi) *win32.IDWriteTextFormat { fn createTextFormatEditor(dpi: Dpi) *win32.IDWriteTextFormat {
const default_config = gui_config{};
const fontface_utf8 = if (global.conf) |conf| conf.fontface else blk: {
std.log.err("global gui config not found", .{});
break :blk default_config.fontface;
};
const fontsize = if (global.conf) |conf| conf.fontsize else default_config.fontsize;
var buf: [4096]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
var fontface = std.ArrayList(u16).init(fba.allocator());
std.unicode.utf8ToUtf16LeArrayList(&fontface, fontface_utf8) catch {
std.log.err("fontface contains invalid UTF-8", .{});
fontface.clearRetainingCapacity();
std.unicode.utf8ToUtf16LeArrayList(&fontface, default_config.fontface) catch {};
};
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, 14, dpi.value), .size = win32.scaleDpi(f32, @as(f32, @floatFromInt(fontsize)), dpi.value),
.family_name = win32.L("Cascadia Code"), .family_name = fontface.toOwnedSliceSentinel(0) catch @panic("OOM:createTextFormatEditor"),
}) 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 });
} }
@ -230,6 +247,7 @@ const State = struct {
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,
}; };
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)));
@ -422,6 +440,8 @@ fn entry(pid: thespian.pid) !void {
); );
const conf, _ = root.read_config(gui_config, arena_instance.allocator()); const conf, _ = root.read_config(gui_config, arena_instance.allocator());
root.write_config(conf, arena_instance.allocator()) catch
std.log.err("failed to write gui config file", .{});
var create_args = CreateWindowArgs{ var create_args = CreateWindowArgs{
.allocator = arena_instance.allocator(), .allocator = arena_instance.allocator(),
@ -1132,7 +1152,10 @@ fn WndProc(
state.* = .{ state.* = .{
.pid = create_args.pid, .pid = create_args.pid,
.conf = create_args.conf,
}; };
global.conf = &state.conf;
const existing = win32.SetWindowLongPtrW( const existing = win32.SetWindowLongPtrW(
hwnd, hwnd,
@enumFromInt(0), @enumFromInt(0),