mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-12-28 09:04:40 +00:00
apprt/gtk: key state overlay text is dynamic
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
const std = @import("std");
|
||||
const adw = @import("adw");
|
||||
const glib = @import("glib");
|
||||
const gobject = @import("gobject");
|
||||
const gtk = @import("gtk");
|
||||
|
||||
const ext = @import("../ext.zig");
|
||||
const gresource = @import("../build/gresource.zig");
|
||||
const Application = @import("application.zig").Application;
|
||||
const Common = @import("../class.zig").Common;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_key_state_overlay);
|
||||
@@ -25,19 +27,6 @@ pub const KeyStateOverlay = extern struct {
|
||||
});
|
||||
|
||||
pub const properties = struct {
|
||||
pub const active = struct {
|
||||
pub const name = "active";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
bool,
|
||||
.{
|
||||
.default = false,
|
||||
.accessor = C.privateShallowFieldAccessor("active"),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const tables = struct {
|
||||
pub const name = "tables";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
@@ -50,7 +39,7 @@ pub const KeyStateOverlay = extern struct {
|
||||
?*ext.StringList,
|
||||
.{
|
||||
.getter = getTables,
|
||||
.getter_transfer = .full,
|
||||
.getter_transfer = .none,
|
||||
.setter = setTables,
|
||||
.setter_transfer = .full,
|
||||
},
|
||||
@@ -88,7 +77,7 @@ pub const KeyStateOverlay = extern struct {
|
||||
?*ext.StringList,
|
||||
.{
|
||||
.getter = getSequence,
|
||||
.getter_transfer = .full,
|
||||
.getter_transfer = .none,
|
||||
.setter = setSequence,
|
||||
.setter_transfer = .full,
|
||||
},
|
||||
@@ -114,19 +103,6 @@ pub const KeyStateOverlay = extern struct {
|
||||
);
|
||||
};
|
||||
|
||||
pub const pending = struct {
|
||||
pub const name = "pending";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
bool,
|
||||
.{
|
||||
.default = false,
|
||||
.accessor = C.privateShallowFieldAccessor("pending"),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"valign-target" = struct {
|
||||
pub const name = "valign-target";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
@@ -142,18 +118,12 @@ pub const KeyStateOverlay = extern struct {
|
||||
};
|
||||
|
||||
const Private = struct {
|
||||
/// Whether the overlay is active/visible.
|
||||
active: bool = false,
|
||||
|
||||
/// The key table stack.
|
||||
tables: ?*ext.StringList = null,
|
||||
|
||||
/// The key sequence.
|
||||
sequence: ?*ext.StringList = null,
|
||||
|
||||
/// Whether we're waiting for more keys in a sequence.
|
||||
pending: bool = false,
|
||||
|
||||
/// Target vertical alignment for the overlay.
|
||||
valign_target: gtk.Align = .end,
|
||||
|
||||
@@ -165,19 +135,11 @@ pub const KeyStateOverlay = extern struct {
|
||||
}
|
||||
|
||||
fn getTables(self: *Self) ?*ext.StringList {
|
||||
const priv = self.private();
|
||||
if (priv.tables) |tables| {
|
||||
return ext.StringList.create(tables.allocator(), tables.strings) catch null;
|
||||
}
|
||||
return null;
|
||||
return self.private().tables;
|
||||
}
|
||||
|
||||
fn getSequence(self: *Self) ?*ext.StringList {
|
||||
const priv = self.private();
|
||||
if (priv.sequence) |sequence| {
|
||||
return ext.StringList.create(sequence.allocator(), sequence.strings) catch null;
|
||||
}
|
||||
return null;
|
||||
return self.private().sequence;
|
||||
}
|
||||
|
||||
fn setTables(self: *Self, value: ?*ext.StringList) void {
|
||||
@@ -186,8 +148,11 @@ pub const KeyStateOverlay = extern struct {
|
||||
old.destroy();
|
||||
priv.tables = null;
|
||||
}
|
||||
if (value) |v| {
|
||||
priv.tables = v;
|
||||
}
|
||||
|
||||
priv.tables = value;
|
||||
self.as(gobject.Object).notifyByPspec(properties.tables.impl.param_spec);
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"has-tables".impl.param_spec);
|
||||
}
|
||||
|
||||
@@ -197,17 +162,22 @@ pub const KeyStateOverlay = extern struct {
|
||||
old.destroy();
|
||||
priv.sequence = null;
|
||||
}
|
||||
if (value) |v| {
|
||||
priv.sequence = v;
|
||||
}
|
||||
|
||||
priv.sequence = value;
|
||||
self.as(gobject.Object).notifyByPspec(properties.sequence.impl.param_spec);
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"has-sequence".impl.param_spec);
|
||||
}
|
||||
|
||||
fn getHasTables(self: *Self) bool {
|
||||
return self.private().tables != null;
|
||||
const v = self.private().tables orelse return false;
|
||||
return v.strings.len > 0;
|
||||
}
|
||||
|
||||
fn getHasSequence(self: *Self) bool {
|
||||
return self.private().sequence != null;
|
||||
const v = self.private().sequence orelse return false;
|
||||
return v.strings.len > 0;
|
||||
}
|
||||
|
||||
fn closureShowChevron(
|
||||
@@ -218,6 +188,50 @@ pub const KeyStateOverlay = extern struct {
|
||||
return if (has_tables and has_sequence) 1 else 0;
|
||||
}
|
||||
|
||||
fn closureHasState(
|
||||
_: *Self,
|
||||
has_tables: bool,
|
||||
has_sequence: bool,
|
||||
) callconv(.c) c_int {
|
||||
return if (has_tables or has_sequence) 1 else 0;
|
||||
}
|
||||
|
||||
fn closureTablesText(
|
||||
_: *Self,
|
||||
tables: ?*ext.StringList,
|
||||
) callconv(.c) ?[*:0]const u8 {
|
||||
const list = tables orelse return null;
|
||||
if (list.strings.len == 0) return null;
|
||||
|
||||
var buf: std.Io.Writer.Allocating = .init(Application.default().allocator());
|
||||
defer buf.deinit();
|
||||
|
||||
for (list.strings, 0..) |s, i| {
|
||||
if (i > 0) buf.writer.writeAll(" > ") catch return null;
|
||||
buf.writer.writeAll(s) catch return null;
|
||||
}
|
||||
|
||||
return glib.ext.dupeZ(u8, buf.written());
|
||||
}
|
||||
|
||||
fn closureSequenceText(
|
||||
_: *Self,
|
||||
sequence: ?*ext.StringList,
|
||||
) callconv(.c) ?[*:0]const u8 {
|
||||
const list = sequence orelse return null;
|
||||
if (list.strings.len == 0) return null;
|
||||
|
||||
var buf: std.Io.Writer.Allocating = .init(Application.default().allocator());
|
||||
defer buf.deinit();
|
||||
|
||||
for (list.strings, 0..) |s, i| {
|
||||
if (i > 0) buf.writer.writeAll(" ") catch return null;
|
||||
buf.writer.writeAll(s) catch return null;
|
||||
}
|
||||
|
||||
return glib.ext.dupeZ(u8, buf.written());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Template callbacks
|
||||
|
||||
@@ -303,15 +317,16 @@ pub const KeyStateOverlay = extern struct {
|
||||
// Template Callbacks
|
||||
class.bindTemplateCallback("on_drag_end", &onDragEnd);
|
||||
class.bindTemplateCallback("show_chevron", &closureShowChevron);
|
||||
class.bindTemplateCallback("has_state", &closureHasState);
|
||||
class.bindTemplateCallback("tables_text", &closureTablesText);
|
||||
class.bindTemplateCallback("sequence_text", &closureSequenceText);
|
||||
|
||||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
properties.active.impl,
|
||||
properties.tables.impl,
|
||||
properties.@"has-tables".impl,
|
||||
properties.sequence.impl,
|
||||
properties.@"has-sequence".impl,
|
||||
properties.pending.impl,
|
||||
properties.@"valign-target".impl,
|
||||
});
|
||||
|
||||
|
||||
@@ -832,6 +832,10 @@ pub const Surface = extern struct {
|
||||
const priv = self.private();
|
||||
const alloc = Application.default().allocator();
|
||||
|
||||
self.as(gobject.Object).freezeNotify();
|
||||
defer self.as(gobject.Object).thawNotify();
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"key-sequence".impl.param_spec);
|
||||
|
||||
switch (value) {
|
||||
.trigger => |trigger| {
|
||||
// Convert the trigger to a human-readable label
|
||||
@@ -865,6 +869,10 @@ pub const Surface = extern struct {
|
||||
const priv = self.private();
|
||||
const alloc = Application.default().allocator();
|
||||
|
||||
self.as(gobject.Object).freezeNotify();
|
||||
defer self.as(gobject.Object).thawNotify();
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"key-table".impl.param_spec);
|
||||
|
||||
switch (value) {
|
||||
.activate => |name| {
|
||||
// Duplicate the name string and push onto stack
|
||||
|
||||
@@ -2,7 +2,7 @@ using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $GhosttyKeyStateOverlay: Adw.Bin {
|
||||
visible: bind template.active;
|
||||
visible: bind $has_state(template.has-tables, template.has-sequence) as <bool>;
|
||||
valign-target: end;
|
||||
halign: center;
|
||||
valign: bind template.valign-target;
|
||||
@@ -30,7 +30,7 @@ template $GhosttyKeyStateOverlay: Adw.Bin {
|
||||
|
||||
Label tables_label {
|
||||
visible: bind template.has-tables;
|
||||
label: bind template.tables-text;
|
||||
label: bind $tables_text(template.tables) as <string>;
|
||||
xalign: 0.0;
|
||||
}
|
||||
|
||||
@@ -45,13 +45,13 @@ template $GhosttyKeyStateOverlay: Adw.Bin {
|
||||
|
||||
Label sequence_label {
|
||||
visible: bind template.has-sequence;
|
||||
label: bind template.sequence-text;
|
||||
label: bind $sequence_text(template.sequence) as <string>;
|
||||
xalign: 0.0;
|
||||
}
|
||||
|
||||
Spinner pending_spinner {
|
||||
visible: bind template.pending;
|
||||
spinning: bind template.pending;
|
||||
visible: bind template.has-sequence;
|
||||
spinning: bind template.has-sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user