HintBar

NAME

App::Moneymoor::View::HintBar - the one canonical keybind-hint table, rendered as styled RichText spans that truncate a group at a time.

SYNOPSIS


use App::Moneymoor::View::HintBar;

# Every hint the app can show, keyed by context.
hint-contexts();                  # (budget generic login reconcile
                                   #  register reports sidebar)
hint-groups('sidebar').head;      # Enter => open

# The footer paints these straight into a Selkie::Widget::RichText.
$footer.set-content(
    hint-spans('budget', $footer.cols, theme => $main.theme,
               icons => $main.icons),
);

DESCRIPTION

The bottom line of every App::Moneymoor screen is a keybind cheat-sheet that changes with focus. Screen::Main owns the footer widget and Screen::Main::Subscriptions decides which context is active, so this module owns the one %HINTS table and the one renderer rather than letting the two hand-roll their own copies β€” one flat fg-dim line each, hard-truncated by the terminal edge with no indication that anything had been cut off, is the failure mode this exists to avoid.

This module owns all of it: one %HINTS table, one renderer.

Contexts

A context is a bare string key β€” 'budget', 'register' β€” not a style bundle and not a span list. That matters: the screens pick the context inside a store subscription, and Selkie::Store's change digest keys objects by .WHICH, so a selector that returned freshly-built Selkie::Style or Span objects would read as "changed" on every tick and re-push the whole footer every frame. Selectors return the key; the callback builds the spans.

  • generic β€” no recognised focus target: the app-wide keys only.

  • login β€” the login screen's three keys. Rendered once, at build time, into the fixed-width dialog's bottom line: unlike the main screens, Screen::Login never resizes, so it neither subscribes to a context nor repaints on resize.

  • budget β€” the budget tab's envelope table.

  • register β€” the accounts tab's transaction register.

  • reconcile β€” register focus while reconcile mode (Ctrl+R) is active; overrides register for the duration.

  • sidebar β€” the accounts tab's account list.

  • reports β€” the reports tab.

An unrecognised context falls back to generic rather than throwing β€” same fallback semantics as App::Moneymoor::View::EmptyState and App::Moneymoor::Themes::load.

Truncation

Hints are dropped a whole group at a time, never mid-group: half a keybind ("Ctrl+1..0 pers") is worse than no keybind. Trailing groups are dropped until what remains β€” plus the … ^h tail β€” fits the requested width. The tail says two things at once: "there is more" and "Ctrl+H shows all of it". It is ^h rather than ? because ? is deliberately not a global bind (a payee name or memo field may legitimately contain one β€” see the comment on the ctrl+h bind in Screen::Main::Keybinds).

Widths narrower than a single group plus the tail degrade to the tail alone; a width of zero or less returns no spans at all. Neither throws β€” the footer is laid out by whatever the terminal happens to be, and a one-column terminal must not take the app down.

Rendering contract

Each group renders as < key label > β€” a key span in the palette accent, bold, then a label span in fg-dim carrying the separating space. Groups are joined by a Β· in fg-dimmer, and the tail is fg-dimmer too, so the eye lands on the keys first and the dividers disappear.

The returned spans total at most $width columns ($width - $reserve when a reservation is given), so they occupy exactly one line of a Selkie::Widget::RichText. There is no leading margin space: RichText's wrapper drops a whitespace token at column 0, so a margin span would be silently eaten β€” the bar starts flush left.

EXAMPLES

Width comes from the widget, because that is what the spans have to fit in; a screen that has not been laid out yet reports zero columns, so callers substitute a sane default and repaint when the real width arrives (both screens do this from an App.on-resize callback):


method !repaint-hints() {
    my $w = $!hint-footer.cols > 0 ?? $!hint-footer.cols !! 80;
    $!hint-footer.set-content(
        hint-spans($!hint-context, $w, theme => $!theme, icons => self.icons),
    );
}

Reserving room on the right

:reserve subtracts columns before any fitting is done, for a right-aligned segment the caller paints itself (a clock, a sync indicator). The hint groups simply behave as though the terminal were that much narrower:


my @spans = hint-spans('budget', 100, :$theme, reserve => 12);
@spansΒ».text.join.chars;      # <= 88

Inspecting the table

hint-groups is the table itself, so a test (or a future help overlay) can assert against the pairs rather than parsing a rendered string:


for hint-contexts() -> $ctx {
    for hint-groups($ctx) -> $group {
        say "$ctx: {$group.key} = {$group.value}";
    }
}

EXPORTS

  • hint-contexts(-- List)> β€” every context key, sorted.

  • hint-groups(Str $context -- List)> β€” that context's ordered < key => label > pairs, falling back to generic.

  • hint-spans(Str $context, Int $width, :$theme!, :$icons, :$reserve -- List)> β€” the Selkie::Widget::RichText::Spans to paint.

SEE ALSO

App::Moneymoor v0.4.2

YNAB-style envelope budgeting: a derivation engine

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

DBIish:ver<0.6.7>:auth<zef:raku-community-modules>Notcurses::Native:ver<0.6.5+>:auth<zef:apogee>Selkie:ver<0.16.0+>:auth<zef:apogee>JSON::Fast:ver<0.19>:auth<cpan:TIMOTIMO>MacOS::NativeLib:ver<0.0.6>:auth<zef:lizmat>

Test Dependencies

Provides

  • App::Moneymoor
  • App::Moneymoor::Config
  • App::Moneymoor::DB
  • App::Moneymoor::Gateway::Account
  • App::Moneymoor::Gateway::Assignment
  • App::Moneymoor::Gateway::Category
  • App::Moneymoor::Gateway::Payee
  • App::Moneymoor::Gateway::Transaction
  • App::Moneymoor::Handlers::Boot
  • App::Moneymoor::Model::Account
  • App::Moneymoor::Model::Assignment
  • App::Moneymoor::Model::Category
  • App::Moneymoor::Model::CategoryGroup
  • App::Moneymoor::Model::Payee
  • App::Moneymoor::Model::Split
  • App::Moneymoor::Model::Transaction
  • App::Moneymoor::Screen::Accounts
  • App::Moneymoor::Screen::Budget
  • App::Moneymoor::Screen::Login
  • App::Moneymoor::Screen::Main
  • App::Moneymoor::Screen::Main::Keybinds
  • App::Moneymoor::Screen::Main::Modals
  • App::Moneymoor::Screen::Main::Subscriptions
  • App::Moneymoor::Screen::Reports
  • App::Moneymoor::Service::Budget
  • App::Moneymoor::Service::Icons
  • App::Moneymoor::Service::Target
  • App::Moneymoor::Service::Workspace
  • App::Moneymoor::StoreHandlers
  • App::Moneymoor::Theme
  • App::Moneymoor::Theme::Catppuccin
  • App::Moneymoor::Theme::Dracula
  • App::Moneymoor::Theme::Everforest
  • App::Moneymoor::Theme::Gruvbox
  • App::Moneymoor::Theme::Kanagawa
  • App::Moneymoor::Theme::Monokai
  • App::Moneymoor::Theme::Nord
  • App::Moneymoor::Theme::OneDark
  • App::Moneymoor::Theme::RosePine
  • App::Moneymoor::Theme::Solarized
  • App::Moneymoor::Theme::TokyoNight
  • App::Moneymoor::Themes
  • App::Moneymoor::UI
  • App::Moneymoor::Util::Money
  • App::Moneymoor::Util::Period
  • App::Moneymoor::View::BudgetRow
  • App::Moneymoor::View::EmptyState
  • App::Moneymoor::View::HintBar
  • App::Moneymoor::View::InspectorPane
  • App::Moneymoor::View::ModalChrome
  • App::Moneymoor::View::RegisterRow
  • App::Moneymoor::View::ReportRow
  • App::Moneymoor::Widget::BannerBar
  • App::Moneymoor::Widget::BootProgressModal

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite β€” the markup and publishing tools behind this site.