apprt/gtk-ng: tab attention for bell

This commit is contained in:
Mitchell Hashimoto
2025-08-13 08:55:24 -07:00
parent 156278e6c1
commit f9896c8ef7
2 changed files with 73 additions and 1 deletions

View File

@@ -275,7 +275,8 @@ pub const Surface = extern struct {
/// The surface view handles the audio bell feature but none of the
/// others so it is up to the embedding widget to react to this.
///
/// Bell ringing will also emit the win.ring-bell action.
/// Bell ringing will also emit the tab.ring-bell and win.ring-bell
/// actions.
pub const bell = struct {
pub const name = "bell";
pub const connect = impl.connect;
@@ -557,6 +558,7 @@ pub const Surface = extern struct {
);
// Activate a window action if it exists
_ = self.as(gtk.Widget).activateAction("tab.ring-bell", null);
_ = self.as(gtk.Widget).activateAction("win.ring-bell", null);
}

View File

@@ -11,6 +11,7 @@ const i18n = @import("../../../os/main.zig").i18n;
const apprt = @import("../../../apprt.zig");
const input = @import("../../../input.zig");
const CoreSurface = @import("../../../Surface.zig");
const ext = @import("../ext.zig");
const gtk_version = @import("../gtk_version.zig");
const adw_version = @import("../adw_version.zig");
const gresource = @import("../build/gresource.zig");
@@ -175,6 +176,9 @@ pub const Tab = extern struct {
fn init(self: *Self, _: *Class) callconv(.c) void {
gtk.Widget.initTemplate(self.as(gtk.Widget));
// Init our actions
self.initActions();
// If our configuration is null then we get the configuration
// from the application.
const priv = self.private();
@@ -194,6 +198,46 @@ pub const Tab = extern struct {
};
}
/// Setup our action map.
fn initActions(self: *Self) void {
// The set of actions. Each action has (in order):
// [0] The action name
// [1] The callback function
// [2] The glib.VariantType of the parameter
//
// For action names:
// https://docs.gtk.org/gio/type_func.Action.name_is_valid.html
const actions = .{
.{ "ring-bell", actionRingBell, null },
};
// We need to collect our actions into a group since we're just
// a plain widget that doesn't implement ActionGroup directly.
const group = gio.SimpleActionGroup.new();
errdefer group.unref();
const map = group.as(gio.ActionMap);
inline for (actions) |entry| {
const action = gio.SimpleAction.new(
entry[0],
entry[2],
);
defer action.unref();
_ = gio.SimpleAction.signals.activate.connect(
action,
*Self,
entry[1],
self,
.{},
);
map.addAction(action.as(gio.Action));
}
self.as(gtk.Widget).insertActionGroup(
"tab",
group.as(gio.ActionGroup),
);
}
//---------------------------------------------------------------
// Properties
@@ -223,6 +267,15 @@ pub const Tab = extern struct {
return core_surface.needsConfirmQuit();
}
/// Get the tab page holding this tab, if any.
fn getTabPage(self: *Self) ?*adw.TabPage {
const tab_view = ext.getAncestor(
adw.TabView,
self.as(gtk.Widget),
) orelse return null;
return tab_view.getPage(self.as(gtk.Widget));
}
//---------------------------------------------------------------
// Virtual methods
@@ -291,6 +344,23 @@ pub const Tab = extern struct {
self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec);
}
fn actionRingBell(
_: *gio.SimpleAction,
_: ?*glib.Variant,
self: *Self,
) callconv(.c) void {
// Future note: I actually don't like this logic living here at all.
// I think a better approach will be for the ring bell action to
// specify its sending surface and then do all this in the window.
// If the page is selected already we don't mark it as needing
// attention. We only want to mark unfocused pages. This will then
// clear when the page is selected.
const page = self.getTabPage() orelse return;
if (page.getSelected() != 0) return;
page.setNeedsAttention(@intFromBool(true));
}
fn closureComputedTitle(
_: *Self,
config_: ?*Config,