apprt/gtk: surface has inspector state

This commit is contained in:
Mitchell Hashimoto
2023-10-20 08:44:23 -07:00
parent a6dfad9689
commit 199e1cae29
3 changed files with 146 additions and 23 deletions

View File

@ -263,12 +263,6 @@ pub fn run(self: *App) !void {
log.warn("error handling configuration changes err={}", .{err});
};
// TODO: temporary, remove: show our inspector window
{
const win = try inspector.Window.create(self.core_app.alloc, self);
_ = win;
}
while (self.running) {
_ = c.g_main_context_iteration(self.ctx, 1);

View File

@ -14,6 +14,7 @@ const CoreSurface = @import("../../Surface.zig");
const App = @import("App.zig");
const Window = @import("Window.zig");
const inspector = @import("inspector.zig");
const c = @import("c.zig");
const log = std.log.scoped(.gtk);
@ -76,6 +77,9 @@ font_size: ?font.face.DesiredSize = null,
size: apprt.SurfaceSize,
cursor_pos: apprt.CursorPos,
/// Inspector state.
inspector: ?*inspector.Inspector = null,
/// Key input states. See gtkKeyPressed for detailed descriptions.
in_keypress: bool = false,
im_context: *c.GtkIMContext,
@ -218,6 +222,9 @@ pub fn deinit(self: *Surface) void {
// We don't allocate anything if we aren't realized.
if (!self.realized) return;
// Delete our inspector if we have one
self.controlInspector(.hide);
// Remove ourselves from the list of known surfaces in the app.
self.app.core_app.deleteSurface(self);
@ -281,6 +288,33 @@ pub fn close(self: *Surface, processActive: bool) void {
c.gtk_widget_show(alert);
}
pub fn controlInspector(self: *Surface, mode: input.InspectorMode) void {
const show = switch (mode) {
.toggle => self.inspector == null,
.show => true,
.hide => false,
};
if (!show) {
if (self.inspector) |v| {
v.close(true);
self.inspector = null;
}
return;
}
// If we already have an inspector, we don't need to show anything.
if (self.inspector != null) return;
self.inspector = inspector.Inspector.create(
self,
.{ .window = {} },
) catch |err| {
log.err("failed to control inspector err={}", .{err});
return;
};
}
pub fn toggleFullscreen(self: *Surface, mac_non_native: configpkg.NonNativeFullscreen) void {
self.window.toggleFullscreen(mac_non_native);
}

View File

@ -1,7 +1,9 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const App = @import("App.zig");
const Surface = @import("Surface.zig");
const TerminalWindow = @import("Window.zig");
const ImguiWidget = @import("ImguiWidget.zig");
const c = @import("c.zig");
@ -9,37 +11,127 @@ const icon = @import("icon.zig");
const log = std.log.scoped(.inspector);
/// A window to hold a dedicated inspector instance.
pub const Window = struct {
app: *App,
/// Inspector is the primary stateful object that represents a terminal
/// inspector. An inspector is 1:1 with a Surface and is owned by a Surface.
/// Closing a surface must close its inspector.
pub const Inspector = struct {
/// The surface that owns this inspector.
surface: *Surface,
/// The current state of where this inspector is rendered. The Inspector
/// is the state of the inspector but this is the state of the GUI.
location: LocationState,
/// This is true if we want to destroy this inspector as soon as the
/// location is closed. For example: set this to true, request the
/// window be closed, let GTK do its cleanup, then note this to destroy
/// the inner state.
request_destroy: bool = false,
/// Location where the inspector will be launched.
pub const Location = union(LocationKey) {
hidden: void,
window: void,
};
/// The internal state for each possible location.
const LocationState = union(LocationKey) {
hidden: void,
window: Window,
};
const LocationKey = enum {
/// No GUI, but load the inspector state.
hidden,
/// A dedicated window for the inspector.
window,
};
/// Create an inspector for the given surface in the given location.
pub fn create(surface: *Surface, location: Location) !*Inspector {
const alloc = surface.app.core_app.alloc;
var ptr = try alloc.create(Inspector);
errdefer alloc.destroy(ptr);
try ptr.init(surface, location);
return ptr;
}
/// Destroy all memory associated with this inspector. You generally
/// should NOT call this publicly and should call `close` instead to
/// use the GTK lifecycle.
pub fn destroy(self: *Inspector) void {
assert(self.location == .hidden);
const alloc = self.allocator();
self.deinit();
alloc.destroy(self);
}
fn init(self: *Inspector, surface: *Surface, location: Location) !void {
self.* = .{
.surface = surface,
.location = undefined,
};
switch (location) {
.hidden => self.location = .{ .hidden = {} },
.window => try self.initWindow(),
}
}
fn deinit(self: *Inspector) void {
_ = self;
}
/// Request the inspector is closed. If request_destroy is true, also
/// destroy the inspector state when the GUI is closed.
pub fn close(self: *Inspector, request_destroy: bool) void {
self.request_destroy = request_destroy;
switch (self.location) {
.hidden => self.locationDidClose(),
.window => |v| v.close(),
}
}
fn locationDidClose(self: *Inspector) void {
self.location = .{ .hidden = {} };
if (self.request_destroy) self.destroy();
}
fn allocator(self: *const Inspector) Allocator {
return self.surface.app.core_app.alloc;
}
fn initWindow(self: *Inspector) !void {
self.location = .{ .window = undefined };
try self.location.window.init(self);
}
};
/// A dedicated window to hold an inspector instance.
const Window = struct {
inspector: *Inspector,
window: *c.GtkWindow,
icon: icon.Icon,
imgui_widget: ImguiWidget,
pub fn create(alloc: Allocator, app: *App) !*Window {
var window = try alloc.create(Window);
errdefer alloc.destroy(window);
try window.init(app);
return window;
}
pub fn init(self: *Window, app: *App) !void {
pub fn init(self: *Window, inspector: *Inspector) !void {
// Initialize to undefined
self.* = .{
.app = app,
.inspector = inspector,
.icon = undefined,
.window = undefined,
.imgui_widget = undefined,
};
// Create the window
const window = c.gtk_application_window_new(app.app);
const window = c.gtk_application_window_new(inspector.surface.app.app);
const gtk_window: *c.GtkWindow = @ptrCast(window);
errdefer c.gtk_window_destroy(gtk_window);
self.window = gtk_window;
c.gtk_window_set_title(gtk_window, "Ghostty");
c.gtk_window_set_default_size(gtk_window, 1000, 600);
self.icon = try icon.appIcon(self.app, window);
self.icon = try icon.appIcon(self.inspector.surface.app, window);
c.gtk_window_set_icon_name(gtk_window, self.icon.name);
// Initialize our imgui widget
@ -55,7 +147,12 @@ pub const Window = struct {
}
pub fn deinit(self: *Window) void {
self.icon.deinit(self.app);
self.icon.deinit(self.inspector.surface.app);
self.inspector.locationDidClose();
}
pub fn close(self: *const Window) void {
c.gtk_window_destroy(self.window);
}
/// "destroy" signal for the window
@ -64,8 +161,6 @@ pub const Window = struct {
log.debug("window destroy", .{});
const self: *Window = @ptrCast(@alignCast(ud.?));
const alloc = self.app.core_app.alloc;
self.deinit();
alloc.destroy(self);
}
};