mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +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_shape => try self.setMouseShape(target, value),
|
||||||
.mouse_over_link => self.setMouseOverLink(target, value),
|
.mouse_over_link => self.setMouseOverLink(target, value),
|
||||||
.toggle_tab_overview => self.toggleTabOverview(target),
|
.toggle_tab_overview => self.toggleTabOverview(target),
|
||||||
|
.toggle_split_zoom => self.toggleSplitZoom(target),
|
||||||
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
||||||
.quit_timer => self.quitTimer(value),
|
.quit_timer => self.quitTimer(value),
|
||||||
|
|
||||||
// Unimplemented
|
// Unimplemented
|
||||||
.close_all_windows,
|
.close_all_windows,
|
||||||
.toggle_split_zoom,
|
|
||||||
.toggle_quick_terminal,
|
.toggle_quick_terminal,
|
||||||
.toggle_visibility,
|
.toggle_visibility,
|
||||||
.size_limit,
|
.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(
|
fn toggleWindowDecorations(
|
||||||
_: *App,
|
_: *App,
|
||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
|
@ -77,6 +77,7 @@ pub fn init(
|
|||||||
});
|
});
|
||||||
errdefer surface.destroy(alloc);
|
errdefer surface.destroy(alloc);
|
||||||
sibling.dimSurface();
|
sibling.dimSurface();
|
||||||
|
sibling.setSplitZoom(false);
|
||||||
|
|
||||||
// Create the actual GTKPaned, attach the proper children.
|
// Create the actual GTKPaned, attach the proper children.
|
||||||
const orientation: c_uint = switch (direction) {
|
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.
|
/// Update the paned children to represent the current state.
|
||||||
/// This should be called anytime the top/left or bottom/right
|
/// This should be called anytime the top/left or bottom/right
|
||||||
/// element is changed.
|
/// 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
|
// We have to set both to null. If we overwrite the pane with
|
||||||
// the same value, then GTK bugs out (the GL area unrealizes
|
// the same value, then GTK bugs out (the GL area unrealizes
|
||||||
// and never rerealizes).
|
// and never rerealizes).
|
||||||
@ -372,7 +373,15 @@ fn directionNext(self: *const Split, from: Side) ?struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn removeChildren(self: *const Split) void {
|
pub fn detachTopLeft(self: *const Split) void {
|
||||||
c.gtk_paned_set_start_child(@ptrCast(self.paned), null);
|
c.gtk_paned_set_start_child(self.paned, null);
|
||||||
c.gtk_paned_set_end_child(@ptrCast(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.
|
/// The overlay that shows resizing information.
|
||||||
resize_overlay: ResizeOverlay = .{},
|
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
|
/// If non-null this is the widget on the overlay which dims the surface when it is unfocused
|
||||||
unfocused_widget: ?*c.GtkWidget = null,
|
unfocused_widget: ?*c.GtkWidget = null,
|
||||||
|
|
||||||
@ -643,6 +646,8 @@ pub fn redraw(self: *Surface) void {
|
|||||||
|
|
||||||
/// Close this surface.
|
/// Close this surface.
|
||||||
pub fn close(self: *Surface, processActive: bool) void {
|
pub fn close(self: *Surface, processActive: bool) void {
|
||||||
|
self.setSplitZoom(false);
|
||||||
|
|
||||||
// If we're not part of a window hierarchy, we never confirm
|
// If we're not part of a window hierarchy, we never confirm
|
||||||
// so we can just directly remove ourselves and exit.
|
// so we can just directly remove ourselves and exit.
|
||||||
const window = self.container.window() orelse {
|
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 {
|
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));
|
const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area));
|
||||||
_ = c.gtk_widget_grab_focus(widget);
|
_ = c.gtk_widget_grab_focus(widget);
|
||||||
@ -801,7 +815,7 @@ pub fn grabFocus(self: *Surface) void {
|
|||||||
|
|
||||||
fn updateTitleLabels(self: *Surface) void {
|
fn updateTitleLabels(self: *Surface) void {
|
||||||
// If we have no title, then we have nothing to update.
|
// 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 we have a tab and are the focused child, then we have to update the tab
|
||||||
if (self.container.tab()) |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 {
|
pub fn setTitle(self: *Surface, slice: [:0]const u8) !void {
|
||||||
const alloc = self.app.core_app.alloc;
|
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);
|
errdefer alloc.free(copy);
|
||||||
|
|
||||||
if (self.title_text) |old| alloc.free(old);
|
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 {
|
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(
|
pub fn setMouseShape(
|
||||||
@ -1875,3 +1906,41 @@ pub fn present(self: *Surface) void {
|
|||||||
|
|
||||||
self.grabFocus();
|
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.
|
/// Focus on a split in a given direction.
|
||||||
goto_split: SplitFocusDirection,
|
goto_split: SplitFocusDirection,
|
||||||
|
|
||||||
/// zoom/unzoom the current split. This is currently only supported
|
/// zoom/unzoom the current split.
|
||||||
/// on macOS. Contributions welcome for other platforms.
|
|
||||||
toggle_split_zoom: void,
|
toggle_split_zoom: void,
|
||||||
|
|
||||||
/// Resize the current split by moving the split divider in the given
|
/// Resize the current split by moving the split divider in the given
|
||||||
|
Reference in New Issue
Block a user