gtk: get 'Change Title' working with older distros

This commit is contained in:
Jeffrey C. Ollie
2025-02-22 12:54:08 -06:00
parent 7c19dd5a33
commit 4f3c4037aa
7 changed files with 103 additions and 21 deletions

View File

@ -25,7 +25,7 @@ pub fn init(comptime name: []const u8, comptime kind: enum { blp, ui }) Builder
// GResource.
const gresource = @import("gresource.zig");
for (gresource.blueprint_files) |blueprint_file| {
if (std.mem.eql(u8, blueprint_file, name)) break;
if (std.mem.eql(u8, blueprint_file.name, name)) break;
} else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig");
},
.ui => {

View File

@ -31,6 +31,7 @@ const inspector = @import("inspector.zig");
const gtk_key = @import("key.zig");
const c = @import("c.zig").c;
const Builder = @import("Builder.zig");
const adwaita = @import("adwaita.zig");
const log = std.log.scoped(.gtk_surface);
@ -1020,16 +1021,17 @@ fn resolveTitle(self: *Surface, title: [:0]const u8) [:0]const u8 {
}
pub fn promptTitle(self: *Surface) !void {
if (!adwaita.versionAtLeast(1, 5, 0)) return;
const window = self.container.window() orelse return;
var builder = Builder.init("prompt-title-dialog", .blp);
defer builder.deinit();
const entry: *gtk.Entry = @ptrCast(builder.getObject("title_entry"));
const entry = gobject.ext.cast(gtk.Entry, builder.getObject("title_entry").?).?;
entry.getBuffer().setText(self.getTitle() orelse "", -1);
const dialog: *adw.AlertDialog = @ptrCast(builder.getObject("prompt_title_dialog"));
dialog.choose(@ptrCast(window.window), null, &gtkPromptTitleResponse, self);
const dialog = gobject.ext.cast(adw.AlertDialog, builder.getObject("prompt_title_dialog").?).?;
dialog.choose(@ptrCast(window.window), null, gtkPromptTitleResponse, self);
}
/// Set the current working directory of the surface.
@ -2320,12 +2322,13 @@ fn g_value_holds(value_: ?*c.GValue, g_type: c.GType) bool {
}
fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.C) void {
const dialog: *adw.AlertDialog = @ptrCast(source_object.?);
const self = userdataSelf(ud.?);
if (!adwaita.versionAtLeast(1, 5, 0)) return;
const dialog = gobject.ext.cast(adw.AlertDialog, source_object.?).?;
const self = userdataSelf(ud orelse return);
const response = dialog.chooseFinish(result);
if (std.mem.orderZ(u8, "ok", response) == .eq) {
const title_entry: *gtk.Entry = gobject.ext.cast(gtk.Entry, dialog.getExtraChild().?).?;
const title_entry = gobject.ext.cast(gtk.Entry, dialog.getExtraChild().?).?;
const title = std.mem.span(title_entry.getBuffer().getText());
// if the new title is empty and the user has set the title previously, restore the terminal provided title

View File

@ -0,0 +1,57 @@
const std = @import("std");
pub const c = @cImport({
@cInclude("adwaita.h");
});
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
var it = try std.process.argsWithAllocator(alloc);
defer it.deinit();
_ = it.next();
const major = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMajorVersion, 10);
const minor = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMinorVersion, 10);
const micro = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMicroVersion, 10);
const output = it.next() orelse return error.NoOutput;
const input = it.next() orelse return error.NoInput;
if (c.ADW_MAJOR_VERSION < major or
(c.ADW_MAJOR_VERSION == major and c.ADW_MINOR_VERSION < minor) or
(c.ADW_MAJOR_VERSION == major and c.ADW_MINOR_VERSION == minor and c.ADW_MICRO_VERSION < micro))
{
// If the Adwaita version is too old, generate an "empty" file.
const file = try std.fs.createFileAbsolute(output, .{
.truncate = true,
});
try file.writeAll(
\\<?xml version="1.0" encoding="UTF-8"?>
\\<interface domain="com.mitchellh.ghostty"/>
);
defer file.close();
return;
}
var compiler = std.process.Child.init(
&.{
"blueprint-compiler",
"compile",
"--output",
output,
input,
},
alloc,
);
const term = try compiler.spawnAndWait();
switch (term) {
.Exited => |rc| {
if (rc != 0) std.posix.exit(1);
},
else => std.posix.exit(1),
}
}

View File

@ -57,7 +57,17 @@ pub const ui_files = [_][]const u8{
"menu-window-titlebar_menu",
"menu-surface-context_menu",
};
pub const blueprint_files = [_][]const u8{"prompt-title-dialog"};
pub const VersionedBlueprint = struct {
major: u16,
minor: u16,
micro: u16,
name: []const u8,
};
pub const blueprint_files = [_]VersionedBlueprint{
.{ .major = 1, .minor = 5, .micro = 0, .name = "prompt-title-dialog" },
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -72,9 +82,9 @@ pub fn main() !void {
var it = try std.process.argsWithAllocator(alloc);
defer it.deinit();
while (it.next()) |filename| {
if (std.mem.eql(u8, std.fs.path.extension(filename), ".ui")) {
try extra_ui_files.append(try alloc.dupe(u8, filename));
while (it.next()) |argument| {
if (std.mem.eql(u8, std.fs.path.extension(argument), ".ui")) {
try extra_ui_files.append(try alloc.dupe(u8, argument));
}
}
@ -144,7 +154,7 @@ pub const dependencies = deps: {
index += 1;
}
for (blueprint_files) |blueprint_file| {
deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{s}.blp", .{blueprint_file});
deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{s}.blp", .{blueprint_file.name});
index += 1;
}
break :deps deps;

View File

@ -6,8 +6,8 @@ Adw.AlertDialog prompt_title_dialog {
body: _("Leave blank to restore the default title.");
responses [
cancel: _("Cancel"),
ok: _("OK") suggested
cancel: _("Cancel") suggested,
ok: _("OK") destructive
]
focus-widget: title_entry;

View File

@ -443,6 +443,7 @@ pub fn add(
.{ "glib", "glib2" },
.{ "gtk", "gtk4" },
.{ "gdk", "gdk4" },
.{ "adw", "adw1" },
};
inline for (gobject_imports) |import| {
const name, const module = import;
@ -451,7 +452,6 @@ pub fn add(
step.linkSystemLibrary2("gtk4", dynamic_link_opts);
step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
step.root_module.addImport("adw", gobject.module("adw1"));
if (self.config.x11) {
step.linkSystemLibrary2("X11", dynamic_link_opts);
@ -500,14 +500,24 @@ pub fn add(
const generate = b.addRunArtifact(generate_gresource_xml);
const gtk_blueprint_compiler = b.addExecutable(.{
.name = "gtk_blueprint_compiler",
.root_source_file = b.path("src/apprt/gtk/blueprint_compiler.zig"),
.target = b.host,
});
gtk_blueprint_compiler.linkSystemLibrary2("gtk4", dynamic_link_opts);
gtk_blueprint_compiler.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
gtk_blueprint_compiler.linkLibC();
for (gresource.blueprint_files) |blueprint_file| {
const blueprint_compiler = b.addSystemCommand(&.{
"blueprint-compiler",
"compile",
"--output",
const blueprint_compiler = b.addRunArtifact(gtk_blueprint_compiler);
blueprint_compiler.addArgs(&.{
b.fmt("{d}", .{blueprint_file.major}),
b.fmt("{d}", .{blueprint_file.minor}),
b.fmt("{d}", .{blueprint_file.micro}),
});
const ui_file = blueprint_compiler.addOutputFileArg(b.fmt("{s}.ui", .{blueprint_file}));
blueprint_compiler.addFileArg(b.path(b.fmt("src/apprt/gtk/ui/{s}.blp", .{blueprint_file})));
const ui_file = blueprint_compiler.addOutputFileArg(b.fmt("{s}.ui", .{blueprint_file.name}));
blueprint_compiler.addFileArg(b.path(b.fmt("src/apprt/gtk/ui/{s}.blp", .{blueprint_file.name})));
generate.addFileArg(ui_file);
}

View File

@ -5,9 +5,11 @@ FROM docker.io/library/debian:${DISTRO_VERSION}
RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \
apt-get -qq -y --no-install-recommends install \
# Build Tools
blueprint-compiler \
build-essential \
libbz2-dev \
libonig-dev \
libxml2-utils \
lintian \
lsb-release \
libxml2-utils \