mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
gtk: add tab works
This commit is contained in:
@ -147,47 +147,20 @@ pub const App = struct {
|
|||||||
|
|
||||||
pub fn newWindow(self: *App, parent_: ?*CoreSurface) !void {
|
pub fn newWindow(self: *App, parent_: ?*CoreSurface) !void {
|
||||||
_ = parent_;
|
_ = parent_;
|
||||||
|
const alloc = self.core_app.alloc;
|
||||||
|
|
||||||
// Grab a surface allocation we'll need it later.
|
// Allocate a fixed pointer for our window. We try to minimize
|
||||||
var surface = try self.core_app.alloc.create(Surface);
|
// allocations but windows and other GUI requirements are so minimal
|
||||||
errdefer self.core_app.alloc.destroy(surface);
|
// compared to the steady-state terminal operation so we use heap
|
||||||
|
// allocation for this.
|
||||||
|
var window = try alloc.create(Window);
|
||||||
|
errdefer alloc.destroy(window);
|
||||||
|
try window.init(self);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the window
|
fn gtkTabAddClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {
|
||||||
const window = c.gtk_application_window_new(self.app);
|
_ = ud;
|
||||||
const gtk_window = @ptrCast(*c.GtkWindow, window);
|
log.warn("CLICK", .{});
|
||||||
errdefer c.gtk_window_destroy(gtk_window);
|
|
||||||
c.gtk_window_set_title(gtk_window, "Ghostty");
|
|
||||||
c.gtk_window_set_default_size(gtk_window, 200, 200);
|
|
||||||
c.gtk_widget_show(window);
|
|
||||||
|
|
||||||
// Create a notebook to hold our tabs.
|
|
||||||
const notebook_widget = c.gtk_notebook_new();
|
|
||||||
const notebook = @ptrCast(*c.GtkNotebook, notebook_widget);
|
|
||||||
c.gtk_notebook_set_tab_pos(notebook, c.GTK_POS_TOP);
|
|
||||||
|
|
||||||
// Initialize the GtkGLArea and attach it to our surface.
|
|
||||||
// The surface starts in the "unrealized" state because we have to
|
|
||||||
// wait for the "realize" callback from GTK to know that the OpenGL
|
|
||||||
// context is ready. See Surface docs for more info.
|
|
||||||
const gl_area = c.gtk_gl_area_new();
|
|
||||||
const label = c.gtk_label_new("Ghostty");
|
|
||||||
try surface.init(self, .{
|
|
||||||
.gl_area = @ptrCast(*c.GtkGLArea, gl_area),
|
|
||||||
.title_label = @ptrCast(*c.GtkLabel, label),
|
|
||||||
});
|
|
||||||
errdefer surface.deinit();
|
|
||||||
if (c.gtk_notebook_append_page(notebook, gl_area, label) < 0) {
|
|
||||||
log.warn("failed to add surface to notebook", .{});
|
|
||||||
return error.GtkAppendPageFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The notebook is our main child
|
|
||||||
c.gtk_window_set_child(gtk_window, notebook_widget);
|
|
||||||
|
|
||||||
// We need to grab focus after it is added to the window. When
|
|
||||||
// creating a window we want to always focus on the widget.
|
|
||||||
const widget = @ptrCast(*c.GtkWidget, gl_area);
|
|
||||||
_ = c.gtk_widget_grab_focus(widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void {
|
fn activate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void {
|
||||||
@ -202,6 +175,109 @@ pub const App = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The state for a single, real GTK window.
|
||||||
|
const Window = struct {
|
||||||
|
app: *App,
|
||||||
|
|
||||||
|
/// The notebook (tab grouping) for this window.
|
||||||
|
notebook: *c.GtkNotebook,
|
||||||
|
|
||||||
|
pub fn init(self: *Window, app: *App) !void {
|
||||||
|
// Set up our own state
|
||||||
|
self.* = .{
|
||||||
|
.app = app,
|
||||||
|
.notebook = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the window
|
||||||
|
const window = c.gtk_application_window_new(app.app);
|
||||||
|
const gtk_window = @ptrCast(*c.GtkWindow, window);
|
||||||
|
errdefer c.gtk_window_destroy(gtk_window);
|
||||||
|
c.gtk_window_set_title(gtk_window, "Ghostty");
|
||||||
|
c.gtk_window_set_default_size(gtk_window, 200, 200);
|
||||||
|
c.gtk_widget_show(window);
|
||||||
|
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT);
|
||||||
|
|
||||||
|
// Create a notebook to hold our tabs.
|
||||||
|
const notebook_widget = c.gtk_notebook_new();
|
||||||
|
const notebook = @ptrCast(*c.GtkNotebook, notebook_widget);
|
||||||
|
self.notebook = notebook;
|
||||||
|
c.gtk_notebook_set_tab_pos(notebook, c.GTK_POS_TOP);
|
||||||
|
|
||||||
|
// Create our add button for new tabs
|
||||||
|
const notebook_add_btn = c.gtk_button_new_from_icon_name("list-add-symbolic");
|
||||||
|
c.gtk_notebook_set_action_widget(notebook, notebook_add_btn, c.GTK_PACK_END);
|
||||||
|
_ = c.g_signal_connect_data(notebook_add_btn, "clicked", c.G_CALLBACK(>kTabAddClick), self, null, c.G_CONNECT_DEFAULT);
|
||||||
|
|
||||||
|
// The notebook is our main child
|
||||||
|
c.gtk_window_set_child(gtk_window, notebook_widget);
|
||||||
|
|
||||||
|
// Add our tab
|
||||||
|
try self.newTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Window) void {
|
||||||
|
// Notify our app we're gone.
|
||||||
|
// TODO
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new tab to this window.
|
||||||
|
pub fn newTab(self: *Window) !void {
|
||||||
|
// Grab a surface allocation we'll need it later.
|
||||||
|
var surface = try self.app.core_app.alloc.create(Surface);
|
||||||
|
errdefer self.app.core_app.alloc.destroy(surface);
|
||||||
|
|
||||||
|
// Initialize the GtkGLArea and attach it to our surface.
|
||||||
|
// The surface starts in the "unrealized" state because we have to
|
||||||
|
// wait for the "realize" callback from GTK to know that the OpenGL
|
||||||
|
// context is ready. See Surface docs for more info.
|
||||||
|
const gl_area = c.gtk_gl_area_new();
|
||||||
|
const label = c.gtk_label_new("Ghostty");
|
||||||
|
try surface.init(self.app, .{
|
||||||
|
.gl_area = @ptrCast(*c.GtkGLArea, gl_area),
|
||||||
|
.title_label = @ptrCast(*c.GtkLabel, label),
|
||||||
|
});
|
||||||
|
errdefer surface.deinit();
|
||||||
|
const page = c.gtk_notebook_append_page(self.notebook, gl_area, label);
|
||||||
|
if (page < 0) {
|
||||||
|
log.warn("failed to add surface to notebook", .{});
|
||||||
|
return error.GtkAppendPageFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to the new tab
|
||||||
|
c.gtk_notebook_set_current_page(self.notebook, page);
|
||||||
|
|
||||||
|
// We need to grab focus after it is added to the window. When
|
||||||
|
// creating a window we want to always focus on the widget.
|
||||||
|
const widget = @ptrCast(*c.GtkWidget, gl_area);
|
||||||
|
_ = c.gtk_widget_grab_focus(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gtkTabAddClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {
|
||||||
|
const self = userdataSelf(ud.?);
|
||||||
|
self.newTab() catch |err| {
|
||||||
|
log.warn("error adding new tab: {}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "destroy" signal for the window
|
||||||
|
fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||||
|
_ = v;
|
||||||
|
log.debug("window destroy", .{});
|
||||||
|
|
||||||
|
const self = userdataSelf(ud.?);
|
||||||
|
const alloc = self.app.core_app.alloc;
|
||||||
|
self.deinit();
|
||||||
|
alloc.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn userdataSelf(ud: *anyopaque) *Window {
|
||||||
|
return @ptrCast(*Window, @alignCast(@alignOf(Window), ud));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Surface = struct {
|
pub const Surface = struct {
|
||||||
/// This is detected by the OpenGL renderer to move to a single-threaded
|
/// This is detected by the OpenGL renderer to move to a single-threaded
|
||||||
/// draw operation. This basically puts locks around our draw path.
|
/// draw operation. This basically puts locks around our draw path.
|
||||||
|
Reference in New Issue
Block a user