Budget

NAME

App::Moneymoor::Screen::Budget - the budget tab: the Ready-to-Assign pill, the envelope table and the detail rail.

SYNOPSIS


use App::Moneymoor::Screen::Budget;

# Built by Screen::Main's content-host seam, never directly:
my $tab = App::Moneymoor::Screen::Budget.new(main => $main);
$tab.build($content-host);       # mounts the panes
$tab.install-subscriptions;      # store โ†’ rows / rail / pill
$tab.install-keybinds;           # a m f x n g e h u i, and Enter

# What the modals in Screen::Main::Modals read back:
$tab.selected-row;               # the row hash under the cursor
$tab.selected-category-id;       # Int, or the type object on a header
$tab.selected-group-id;          # Int, or the type object on a category

DESCRIPTION

VBox (the content host)
     โ”œ Border(fixed 3, 'August 2026')      the Ready-to-Assign pill
     โ”‚   โ”” HBox: RichText(flex)            'Ready to Assign  ยฃ412.50'
     โ”‚           Text(flex, right)         'ยฃ300.00 assigned in future periods'
     โ”” HBox(flex)
         โ”œ Border(flex, 'Envelopes ยท August 2026') โ†’ Table
         โ”” Border(fixed 30, '<category>')          โ†’ RichText rail

The Table's five columns are Category (flex), Assigned, Activity, Available and Target; View::BudgetRow renders every cell.

Everything this class paints comes out of two pure modules โ€” View::BudgetRow for the grid and the pill, View::InspectorPane for the rail โ€” and this file is the wiring: which widget, which subscription, which key.

Group headers live in the same Table as the rows

Selkie::Widget::Table has no notion of a section, so the group headers are ordinary rows carrying < kind => 'group' >. That buys one grid, one cursor and one scrollbar; it costs the two things this class has to handle by hand:

  • The cursor can sit on a header. Every action checks C<selected-row><kind> and either does the group-flavoured thing (e edits the group, d deletes it, Enter folds it) or says why it can't. It never silently acts on the wrong row.

  • Row indices are not stable across a rebuild. Folding a group or hiding an envelope changes what lives at index 7. So the cursor is restored by category id after every set-rows, and only then is the selection re-announced to the store.

set-rows and select-index both fire on-select, which is how the store learns where the cursor is โ€” so both are wrapped in a suppression flag during a rebuild, and one deliberate announcement is made at the end. Without it, a rebuild would announce the row that happens to be under the old index, the rail would repaint for a category the user never chose, and (once the rebuild was itself triggered by a selection-keyed subscription) it would do it every frame.

Sorting is off

Every column is < sortable => False >. Sorting reorders the view rows without knowing that some of them are headers, which would scatter the categories away from their groups. The grid's order is the user's own sort_order, which is what the category and group editors exist to change.

The rail is mounted, not hidden

i toggles budget/inspector, and the tab is rebuilt rather than the rail being resized to nothing: a zero-allocation child of an HBox has to be parked or it paints outside its parent, and "rebuild the tab" is a path that already exists, already restores the cursor and already re-registers every subscription. The rebuild is driven by a subscription anchored on the content host โ€” the one widget in the tab that survives its own rebuild.

METHODS

  • build($host) โ€” construct and mount. Registers the table as the tab's budget hint-context pane and its initial focus target.

  • install-subscriptions() / install-keybinds() โ€” the two seams Screen::Main::Subscriptions::install-content and Screen::Main::Keybinds::install-content call.

  • refresh-rows() / refresh-inspector() / refresh-rta() โ€” the subscription callbacks, public because a modal that has just mutated something outside the store's sight can ask for a repaint.

  • table, selected-row, selected-category-id, selected-group-id, inspector-mounted.

SEE ALSO

  • the rows, keyed on both change tokens plus the period and the two view switches;

  • the rail, keyed on both change tokens and the cursor;

  • the pill, keyed on the derivation and the period;

  • the rail's presence, which rebuilds the tab.

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.