mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #2608 from Pangoraw/toggle_split_zoom
gtk: implement toggle_split_zoom
This commit is contained in:
@ -471,12 +471,12 @@ pub fn performAction(
|
||||
.mouse_shape => try self.setMouseShape(target, value),
|
||||
.mouse_over_link => self.setMouseOverLink(target, value),
|
||||
.toggle_tab_overview => self.toggleTabOverview(target),
|
||||
.toggle_split_zoom => self.toggleSplitZoom(target),
|
||||
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
||||
.quit_timer => self.quitTimer(value),
|
||||
|
||||
// Unimplemented
|
||||
.close_all_windows,
|
||||
.toggle_split_zoom,
|
||||
.toggle_quick_terminal,
|
||||
.toggle_visibility,
|
||||
.size_limit,
|
||||
@ -671,6 +671,13 @@ fn toggleTabOverview(_: *App, target: apprt.Target) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn toggleSplitZoom(_: *App, target: apprt.Target) void {
|
||||
switch (target) {
|
||||
.app => {},
|
||||
.surface => |surface| surface.rt_surface.toggleSplitZoom(),
|
||||
}
|
||||
}
|
||||
|
||||
fn toggleWindowDecorations(
|
||||
_: *App,
|
||||
target: apprt.Target,
|
||||
|
@ -77,6 +77,7 @@ pub fn init(
|
||||
});
|
||||
errdefer surface.destroy(alloc);
|
||||
sibling.dimSurface();
|
||||
sibling.setSplitZoom(false);
|
||||
|
||||
// Create the actual GTKPaned, attach the proper children.
|
||||
const orientation: c_uint = switch (direction) {
|
||||
@ -258,7 +259,7 @@ pub fn grabFocus(self: *Split) void {
|
||||
/// Update the paned children to represent the current state.
|
||||
/// This should be called anytime the top/left or bottom/right
|
||||
/// element is changed.
|
||||
fn updateChildren(self: *const Split) void {
|
||||
pub fn updateChildren(self: *const Split) void {
|
||||
// We have to set both to null. If we overwrite the pane with
|
||||
// the same value, then GTK bugs out (the GL area unrealizes
|
||||
// and never rerealizes).
|
||||
@ -372,7 +373,15 @@ fn directionNext(self: *const Split, from: Side) ?struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn removeChildren(self: *const Split) void {
|
||||
c.gtk_paned_set_start_child(@ptrCast(self.paned), null);
|
||||
c.gtk_paned_set_end_child(@ptrCast(self.paned), null);
|
||||
pub fn detachTopLeft(self: *const Split) void {
|
||||
c.gtk_paned_set_start_child(self.paned, null);
|
||||
}
|
||||
|
||||
pub fn detachBottomRight(self: *const Split) void {
|
||||
c.gtk_paned_set_end_child(self.paned, null);
|
||||
}
|
||||
|
||||
fn removeChildren(self: *const Split) void {
|
||||
self.detachTopLeft();
|
||||
self.detachBottomRight();
|
||||
}
|
||||
|
@ -330,6 +330,9 @@ url_widget: ?URLWidget = null,
|
||||
/// The overlay that shows resizing information.
|
||||
resize_overlay: ResizeOverlay = .{},
|
||||
|
||||
/// Whether or not the current surface is zoomed in (see `toggle_split_zoom`).
|
||||
zoomed_in: bool = false,
|
||||
|
||||
/// If non-null this is the widget on the overlay which dims the surface when it is unfocused
|
||||
unfocused_widget: ?*c.GtkWidget = null,
|
||||
|
||||
@ -643,6 +646,8 @@ pub fn redraw(self: *Surface) void {
|
||||
|
||||
/// Close this surface.
|
||||
pub fn close(self: *Surface, processActive: bool) void {
|
||||
self.setSplitZoom(false);
|
||||
|
||||
// If we're not part of a window hierarchy, we never confirm
|
||||
// so we can just directly remove ourselves and exit.
|
||||
const window = self.container.window() orelse {
|
||||
@ -791,7 +796,16 @@ pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void
|
||||
}
|
||||
|
||||
pub fn grabFocus(self: *Surface) void {
|
||||
if (self.container.tab()) |tab| tab.focus_child = self;
|
||||
if (self.container.tab()) |tab| {
|
||||
// If any other surface was focused and zoomed in, set it to non zoomed in
|
||||
// so that self can grab focus.
|
||||
if (tab.focus_child) |focus_child| {
|
||||
if (focus_child.zoomed_in and focus_child != self) {
|
||||
focus_child.setSplitZoom(false);
|
||||
}
|
||||
}
|
||||
tab.focus_child = self;
|
||||
}
|
||||
|
||||
const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area));
|
||||
_ = c.gtk_widget_grab_focus(widget);
|
||||
@ -801,7 +815,7 @@ pub fn grabFocus(self: *Surface) void {
|
||||
|
||||
fn updateTitleLabels(self: *Surface) void {
|
||||
// If we have no title, then we have nothing to update.
|
||||
const title = self.title_text orelse return;
|
||||
const title = self.getTitle() orelse return;
|
||||
|
||||
// If we have a tab and are the focused child, then we have to update the tab
|
||||
if (self.container.tab()) |tab| {
|
||||
@ -822,9 +836,19 @@ fn updateTitleLabels(self: *Surface) void {
|
||||
}
|
||||
}
|
||||
|
||||
const zoom_title_prefix = "🔍 ";
|
||||
|
||||
pub fn setTitle(self: *Surface, slice: [:0]const u8) !void {
|
||||
const alloc = self.app.core_app.alloc;
|
||||
const copy = try alloc.dupeZ(u8, slice);
|
||||
|
||||
// Always allocate with the "🔍 " at the beginning and slice accordingly
|
||||
// is the surface is zoomed in or not.
|
||||
const copy: [:0]const u8 = copy: {
|
||||
const new_title = try alloc.allocSentinel(u8, zoom_title_prefix.len + slice.len, 0);
|
||||
@memcpy(new_title[0..zoom_title_prefix.len], zoom_title_prefix);
|
||||
@memcpy(new_title[zoom_title_prefix.len..], slice);
|
||||
break :copy new_title;
|
||||
};
|
||||
errdefer alloc.free(copy);
|
||||
|
||||
if (self.title_text) |old| alloc.free(old);
|
||||
@ -834,7 +858,14 @@ pub fn setTitle(self: *Surface, slice: [:0]const u8) !void {
|
||||
}
|
||||
|
||||
pub fn getTitle(self: *Surface) ?[:0]const u8 {
|
||||
return self.title_text;
|
||||
if (self.title_text) |title_text| {
|
||||
return if (self.zoomed_in)
|
||||
title_text
|
||||
else
|
||||
title_text[zoom_title_prefix.len..];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn setMouseShape(
|
||||
@ -1875,3 +1906,41 @@ pub fn present(self: *Surface) void {
|
||||
|
||||
self.grabFocus();
|
||||
}
|
||||
|
||||
fn detachFromSplit(self: *Surface) void {
|
||||
const split = self.container.split() orelse return;
|
||||
switch (self.container.splitSide() orelse unreachable) {
|
||||
.top_left => split.detachTopLeft(),
|
||||
.bottom_right => split.detachBottomRight(),
|
||||
}
|
||||
}
|
||||
|
||||
fn attachToSplit(self: *Surface) void {
|
||||
const split = self.container.split() orelse return;
|
||||
split.updateChildren();
|
||||
}
|
||||
|
||||
pub fn setSplitZoom(self: *Surface, new_split_zoom: bool) void {
|
||||
if (new_split_zoom == self.zoomed_in) return;
|
||||
const tab = self.container.tab() orelse return;
|
||||
|
||||
const tab_widget = tab.elem.widget();
|
||||
const surface_widget = self.primaryWidget();
|
||||
|
||||
if (new_split_zoom) {
|
||||
self.detachFromSplit();
|
||||
c.gtk_box_remove(tab.box, tab_widget);
|
||||
c.gtk_box_append(tab.box, surface_widget);
|
||||
} else {
|
||||
c.gtk_box_remove(tab.box, surface_widget);
|
||||
self.attachToSplit();
|
||||
c.gtk_box_append(tab.box, tab_widget);
|
||||
}
|
||||
|
||||
self.zoomed_in = new_split_zoom;
|
||||
self.grabFocus();
|
||||
}
|
||||
|
||||
pub fn toggleSplitZoom(self: *Surface) void {
|
||||
self.setSplitZoom(!self.zoomed_in);
|
||||
}
|
||||
|
@ -317,8 +317,7 @@ pub const Action = union(enum) {
|
||||
/// Focus on a split in a given direction.
|
||||
goto_split: SplitFocusDirection,
|
||||
|
||||
/// zoom/unzoom the current split. This is currently only supported
|
||||
/// on macOS. Contributions welcome for other platforms.
|
||||
/// zoom/unzoom the current split.
|
||||
toggle_split_zoom: void,
|
||||
|
||||
/// Resize the current split by moving the split divider in the given
|
||||
|
Reference in New Issue
Block a user