mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
apprt/gtk: add View to help with auto-layout views
This commit is contained in:
@ -7,14 +7,13 @@ const configpkg = @import("../../config.zig");
|
||||
const Config = configpkg.Config;
|
||||
|
||||
const App = @import("App.zig");
|
||||
const View = @import("View.zig");
|
||||
const c = @import("c.zig");
|
||||
|
||||
const log = std.log.scoped(.gtk);
|
||||
|
||||
app: *App,
|
||||
|
||||
layout: *c.GtkConstraintLayout,
|
||||
|
||||
pub fn create(app: *App) !void {
|
||||
if (app.config_errors_window != null) return error.InvalidOperation;
|
||||
|
||||
@ -28,8 +27,6 @@ pub fn create(app: *App) !void {
|
||||
|
||||
/// Not public because this should be called by the GTK lifecycle.
|
||||
fn destroy(self: *ConfigErrors) void {
|
||||
c.g_object_unref(self.layout);
|
||||
|
||||
const alloc = self.app.core_app.alloc;
|
||||
self.app.config_errors_window = null;
|
||||
alloc.destroy(self);
|
||||
@ -43,75 +40,33 @@ fn init(self: *ConfigErrors, app: *App) !void {
|
||||
c.gtk_window_set_title(gtk_window, "Configuration Errors");
|
||||
c.gtk_window_set_default_size(gtk_window, 600, 300);
|
||||
|
||||
// Box to store our widgets
|
||||
const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 12);
|
||||
c.gtk_widget_set_vexpand(box, 1);
|
||||
c.gtk_widget_set_hexpand(box, 1);
|
||||
c.gtk_window_set_child(@ptrCast(window), box);
|
||||
|
||||
// We use a constraint-based layout so the window is resizeable
|
||||
const layout = c.gtk_constraint_layout_new();
|
||||
errdefer c.g_object_unref(layout);
|
||||
c.gtk_widget_set_layout_manager(@ptrCast(box), layout);
|
||||
|
||||
// Create all of our widgets
|
||||
// All our widgets
|
||||
const label = c.gtk_label_new(
|
||||
"One or more configuration errors were found while loading " ++
|
||||
"the configuration. Please review the errors below and reload " ++
|
||||
"your configuration or ignore the erroneous lines.",
|
||||
);
|
||||
c.gtk_label_set_wrap(@ptrCast(label), 1);
|
||||
|
||||
const buf = try contentsBuffer(&app.config);
|
||||
defer c.g_object_unref(buf);
|
||||
const text = c.gtk_text_view_new_with_buffer(buf);
|
||||
errdefer c.g_object_unref(text);
|
||||
c.gtk_text_view_set_editable(@ptrCast(text), 0);
|
||||
c.gtk_text_view_set_cursor_visible(@ptrCast(text), 0);
|
||||
|
||||
const ignore_button = c.gtk_button_new_with_label("Ignore");
|
||||
errdefer c.g_object_unref(ignore_button);
|
||||
var buttons = try ButtonsView.init(self);
|
||||
errdefer buttons.deinit();
|
||||
|
||||
const reload_button = c.gtk_button_new_with_label("Reload Configuration");
|
||||
errdefer c.g_object_unref(reload_button);
|
||||
|
||||
// This hooks up all our widgets to the window so they can be laid out
|
||||
// using the constraint-based layout.
|
||||
c.gtk_widget_set_parent(label, box);
|
||||
c.gtk_widget_set_name(label, "label");
|
||||
c.gtk_widget_set_parent(text, box);
|
||||
c.gtk_widget_set_name(text, "text");
|
||||
c.gtk_widget_set_parent(ignore_button, box);
|
||||
c.gtk_widget_set_name(ignore_button, "ignorebutton");
|
||||
c.gtk_widget_set_parent(reload_button, box);
|
||||
c.gtk_widget_set_name(reload_button, "reloadbutton");
|
||||
|
||||
var gerr: ?*c.GError = null;
|
||||
const list = c.gtk_constraint_layout_add_constraints_from_description(
|
||||
@ptrCast(layout),
|
||||
&vfl,
|
||||
vfl.len,
|
||||
8,
|
||||
8,
|
||||
&gerr,
|
||||
"label",
|
||||
label,
|
||||
"text",
|
||||
text,
|
||||
"ignorebutton",
|
||||
ignore_button,
|
||||
"reloadbutton",
|
||||
reload_button,
|
||||
@as(?*anyopaque, null),
|
||||
);
|
||||
if (gerr) |err| {
|
||||
defer c.g_error_free(err);
|
||||
log.warn("error building window message={s}", .{err.message});
|
||||
return error.OperationFailed;
|
||||
}
|
||||
c.g_list_free(list);
|
||||
// Create our view
|
||||
const view = try View.init(&.{
|
||||
.{ .name = "label", .widget = label },
|
||||
.{ .name = "text", .widget = text },
|
||||
.{ .name = "buttons", .widget = buttons.view.root },
|
||||
}, &rootvfl);
|
||||
errdefer view.deinit();
|
||||
|
||||
// We can do additional settings once the layout is setup
|
||||
c.gtk_label_set_wrap(@ptrCast(label), 1);
|
||||
c.gtk_text_view_set_editable(@ptrCast(text), 0);
|
||||
c.gtk_text_view_set_cursor_visible(@ptrCast(text), 0);
|
||||
c.gtk_text_view_set_top_margin(@ptrCast(text), 8);
|
||||
c.gtk_text_view_set_bottom_margin(@ptrCast(text), 8);
|
||||
c.gtk_text_view_set_left_margin(@ptrCast(text), 8);
|
||||
@ -121,13 +76,11 @@ fn init(self: *ConfigErrors, app: *App) !void {
|
||||
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT);
|
||||
|
||||
// Show the window
|
||||
c.gtk_window_set_child(@ptrCast(window), view.root);
|
||||
c.gtk_widget_show(window);
|
||||
|
||||
// Set some state
|
||||
self.* = .{
|
||||
.app = app,
|
||||
.layout = @ptrCast(layout),
|
||||
};
|
||||
self.* = .{ .app = app };
|
||||
}
|
||||
|
||||
fn gtkDestroy(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||
@ -152,12 +105,41 @@ fn contentsBuffer(config: *const Config) !*c.GtkTextBuffer {
|
||||
return buf;
|
||||
}
|
||||
|
||||
const ButtonsView = struct {
|
||||
view: View,
|
||||
|
||||
pub fn init(root: *ConfigErrors) !ButtonsView {
|
||||
// Todo for events
|
||||
_ = root;
|
||||
|
||||
const ignore_button = c.gtk_button_new_with_label("Ignore");
|
||||
errdefer c.g_object_unref(ignore_button);
|
||||
|
||||
const reload_button = c.gtk_button_new_with_label("Reload Configuration");
|
||||
errdefer c.g_object_unref(reload_button);
|
||||
|
||||
// Create our view
|
||||
const view = try View.init(&.{
|
||||
.{ .name = "ignore", .widget = ignore_button },
|
||||
.{ .name = "reload", .widget = reload_button },
|
||||
}, &vfl);
|
||||
errdefer view.deinit();
|
||||
|
||||
return .{ .view = view };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ButtonsView) void {
|
||||
self.view.deinit();
|
||||
}
|
||||
|
||||
const vfl = [_][*:0]const u8{
|
||||
"H:[ignore]-8-[reload]-8-|",
|
||||
};
|
||||
};
|
||||
|
||||
const rootvfl = [_][*:0]const u8{
|
||||
"H:|-8-[label]-8-|",
|
||||
"H:|[text]|",
|
||||
"H:[ignorebutton]-8-[reloadbutton]-8-|",
|
||||
"V:|[label(<=100)][text(>=100)]-[ignorebutton]-|",
|
||||
"V:|[label(<=100)][text(>=100)]-[reloadbutton]-|",
|
||||
"V:[label][text]-[ignorebutton]",
|
||||
"V:[label][text]-[reloadbutton]",
|
||||
"H:|[buttons]|",
|
||||
"V:|[label(<=100)][text(>=100)]-[buttons]-|",
|
||||
};
|
||||
|
77
src/apprt/gtk/View.zig
Normal file
77
src/apprt/gtk/View.zig
Normal file
@ -0,0 +1,77 @@
|
||||
/// View helps with creating a view with a constraint layout by
|
||||
/// managing all the boilerplate. The caller is responsible for
|
||||
/// providing the widgets, their names, and the VFL code and gets
|
||||
/// a root box as a result ready to be used.
|
||||
const View = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig");
|
||||
|
||||
const log = std.log.scoped(.gtk);
|
||||
|
||||
/// The box that contains all of the widgets.
|
||||
root: *c.GtkWidget,
|
||||
|
||||
/// A single widget used in the view.
|
||||
pub const Widget = struct {
|
||||
/// The name of the widget used for the layout code. This is also
|
||||
/// the name set for the widget for CSS styling.
|
||||
name: [:0]const u8,
|
||||
|
||||
/// The widget itself.
|
||||
widget: *c.GtkWidget,
|
||||
};
|
||||
|
||||
/// Initialize a new constraint layout view with the given widgets
|
||||
/// and VFL.
|
||||
pub fn init(widgets: []const Widget, vfl: []const [*:0]const u8) !View {
|
||||
// Box to store all our widgets
|
||||
const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0);
|
||||
errdefer c.g_object_unref(box);
|
||||
c.gtk_widget_set_vexpand(box, 1);
|
||||
c.gtk_widget_set_hexpand(box, 1);
|
||||
|
||||
// Setup our constraint layout and attach it to the box
|
||||
const layout = c.gtk_constraint_layout_new();
|
||||
errdefer c.g_object_unref(layout);
|
||||
c.gtk_widget_set_layout_manager(@ptrCast(box), layout);
|
||||
|
||||
// Setup our views table
|
||||
const views = c.g_hash_table_new(c.g_str_hash, c.g_str_equal);
|
||||
defer c.g_hash_table_unref(views);
|
||||
|
||||
// Add our widgets
|
||||
for (widgets) |widget| {
|
||||
c.gtk_widget_set_parent(widget.widget, box);
|
||||
c.gtk_widget_set_name(widget.widget, widget.name);
|
||||
_ = c.g_hash_table_insert(
|
||||
views,
|
||||
@constCast(@ptrCast(widget.name.ptr)),
|
||||
widget.widget,
|
||||
);
|
||||
}
|
||||
|
||||
// Add all of our constraints for layout
|
||||
var err_: ?*c.GError = null;
|
||||
const list = c.gtk_constraint_layout_add_constraints_from_descriptionv(
|
||||
@ptrCast(layout),
|
||||
vfl.ptr,
|
||||
vfl.len,
|
||||
8,
|
||||
8,
|
||||
views,
|
||||
&err_,
|
||||
);
|
||||
if (err_) |err| {
|
||||
defer c.g_error_free(err);
|
||||
log.warn("error building view message={s}", .{err.message});
|
||||
return error.OperationFailed;
|
||||
}
|
||||
c.g_list_free(list);
|
||||
|
||||
return .{ .root = box };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *View) void {
|
||||
_ = self;
|
||||
}
|
Reference in New Issue
Block a user