diff --git a/app/layout/Rail.zig b/app/layout/Rail.zig index a2cc01cd1..eef483634 100644 --- a/app/layout/Rail.zig +++ b/app/layout/Rail.zig @@ -3,7 +3,6 @@ const std = @import("std"); const dvui = @import("dvui"); const core = @import("core"); -const PluginStore = @import("../store/PluginStore.zig"); const Layout = @import("Layout.zig"); const Chooser = Layout.Chooser; @@ -20,8 +19,8 @@ pub const Host = struct { drawFooterItems: *const fn (ctx: ?*anyopaque, size: f32) void, /// Whether the side pane is on screen, as opposed to closed or folded away. sidePaneShown: *const fn (ctx: ?*anyopaque) bool, - /// How many installed plugins wait for the person to say whether to load them. - undecidedPluginCount: *const fn (ctx: ?*anyopaque) usize, + /// How many plugins wait for a load decision in this view; zero for other views. + undecidedPluginCount: *const fn (ctx: ?*anyopaque, view_id: []const u8) usize, /// An icon's rect in physical pixels, to register with a custom title bar it overlaps. iconRect: *const fn (ctx: ?*anyopaque, r: dvui.Rect.Physical) void, }; @@ -174,14 +173,10 @@ fn pickAction(host: Host, c: *const Chooser, so_far: Action) Action { return if (host.vtable.sidePaneShown(host.ctx)) .close else .open; } -/// One rail icon: the chooser's item, drawn as the view's glyph with the store's attention badge. +/// One rail icon: the chooser's item, drawn as the view's glyph with the host's attention badge for that view. /// Returns the bottom of the item's cell, in physical pixels. fn drawIcon(host: Host, c: *Chooser, view: *Layout.Surface, index: usize) f32 { - // Only the store view can carry one; nothing else in the rail has a pending-decision notion. - const undecided_count: usize = if (std.mem.eql(u8, view.id, PluginStore.view_id)) - host.vtable.undecidedPluginCount(host.ctx) - else - 0; + const undecided_count = host.vtable.undecidedPluginCount(host.ctx, view.id); // Say what the dot means — a bare badge on a 20px rail icon is otherwise a riddle. const detail: ?[]const u8 = if (undecided_count > 0) dvui.currentWindow().arena().print("{d} plugin{s} ready to load", .{ diff --git a/src/editor/layout.zig b/src/editor/layout.zig index 40d70df6b..4d6412d68 100644 --- a/src/editor/layout.zig +++ b/src/editor/layout.zig @@ -190,7 +190,7 @@ fn explorerPane(ctx: ?*anyopaque, f: *Layout, keywords: []const []const u8) Layo return editor.explorer.draw(editor, f, keywords); } -fn railHost(editor: *fizzy.Editor) Rail.Host { +pub fn railHost(editor: *fizzy.Editor) Rail.Host { return .{ .ctx = editor, .vtable = &.{ .drawFooterItems = railFooterItems, .sidePaneShown = railSidePaneShown, @@ -217,7 +217,9 @@ fn railSidePaneShown(ctx: ?*anyopaque) bool { return if (editor.regionFor(sidebar)) |r| !r.isClosed() and !r.isFolded() else !editor.explorer.closed; } -fn railUndecidedCount(ctx: ?*anyopaque) usize { +fn railUndecidedCount(ctx: ?*anyopaque, view_id: []const u8) usize { + // The rail knows no view by name; only the store's view holds the card that offers a build. + if (!std.mem.eql(u8, view_id, app.store.Store.view_id)) return 0; const editor: *fizzy.Editor = @ptrCast(@alignCast(ctx.?)); return editor.app.undecidedPluginCount(); } diff --git a/tests/integration.zig b/tests/integration.zig index 888ae523b..663648f27 100644 --- a/tests/integration.zig +++ b/tests/integration.zig @@ -3067,6 +3067,23 @@ fn expectNoPanel(editor: *fizzy.Editor) !void { try std.testing.expect(editor.regionFor(fizzy.sdk.keywords.ide.panel) == null); } +test "rail: the undecided-plugin badge counts for the store view and no other" { + var ctx = try shim.init(std.testing.allocator); + defer ctx.deinit(std.testing.allocator); + const editor = ctx.editor; + editor.app.gpa = std.testing.allocator; + defer { + for (editor.app.undecided_plugin_ids.items) |id| editor.app.gpa.free(id); + editor.app.undecided_plugin_ids.clearAndFree(editor.app.gpa); + } + try editor.app.trackUndecidedPlugin("one"); + try editor.app.trackUndecidedPlugin("two"); + + const host = fizzy.layout.railHost(editor); + try std.testing.expectEqual(@as(usize, 2), host.vtable.undecidedPluginCount(host.ctx, @import("app").store.Store.view_id)); + try std.testing.expectEqual(@as(usize, 0), host.vtable.undecidedPluginCount(host.ctx, "test.files")); +} + test "chrome: the defaults are fizzy's own frame, Sidebar left of Main with a Panel" { try std.testing.expectEqual(fizzy.Editor.Chrome{}, fizzy.Editor.Chrome.configured); try withChrome(.{}, expectSidebarLeft);