Skip to main content

Crate rsynko_ui

Crate rsynko_ui 

Source
Expand description

§rsynko-ui

rsynko-ui defines what a transfer manager looks like without saying what draws it. It owns the key map, the menu a page offers, the composition of every page, the words a reader reads, the weight each of them carries, and the resolution a progress bar states a share at. rsynko-manager owns what the application means; this specification owns how that meaning is presented.

A renderer supplies mechanism only: colors, widgets, borders, layout, cursors, and input decoding. Two renderers that disagree about every one of those still agree about everything here.

§Specification surface

Input is a value, not a callback:

  • Key names one key independently of the mechanism reporting it;
  • Keystroke adds the modifiers that change what a key denotes, and EXIT_KEYSTROKE names the one chord every page binds, which is what leaves the application;
  • KeyBinding states the keys reaching one intention and the menu action gating it.

KeyBindingExt derives the map from the current page: page_bindings states every binding a page holds, keystroke_meaning states what one keystroke denotes there, and action_keys states which keys reach one action. KeyInputExt::apply_keystroke applies what a keystroke denotes and refuses it when the gating action has no meaning, so a disabled menu entry and a pressed key refuse the same intention for the same reason.

use rsynko_ui::{EXIT_KEYSTROKE, Key, Keystroke};

assert_eq!(Keystroke::plain(Key::Up).label(), "↑");
assert_eq!(Keystroke::plain(Key::Character(' ')).label(), "Space");
assert_eq!(EXIT_KEYSTROKE.label(), "Ctrl+Q");

MenuItem states one offered entry: the action, the keys reaching it, the verb it performs here, and whether it currently means anything. MenuPresentationExt::menu_items derives the whole menu from the page’s bindings and the manager’s own availability, so a menu never states a key the page does not bind.

use rsynko_manager::{ActionAvailability, ManagerAction};
use rsynko_ui::{Key, Keystroke, MenuItem};

let item = MenuItem {
    action: ManagerAction::Activate,
    keys: vec![Keystroke::plain(Key::Enter)],
    verb: "Details".to_owned(),
    availability: ActionAvailability::Enabled,
};

assert_eq!(item.label(), "[Enter] Details");

Emphasis is the weight a run of text carries — heading, selected, muted, running, failed — never a color. PhaseVocabularyExt, ControlVocabularyExt, StreamVocabularyExt, and SpaceVocabularyExt name the manager’s own values the way a reader reads them, and bytes_label, duration_label, and elided state quantities in the width a column has.

use rsynko_manager::TransferPhase;
use rsynko_ui::{Emphasis, PhaseVocabularyExt, bytes_label, elided};

assert_eq!(TransferPhase::Downloading.phase_label(), "Downloading");
assert_eq!(TransferPhase::Downloading.phase_emphasis(), Emphasis::Running);
assert_eq!(TransferPhase::Paused.phase_marker(), "○");
assert_eq!(bytes_label(19_084_083), "18.2 MiB");
assert_eq!(elided("a portable video title", 10), "a portabl…");

Gauge states a completed share at an eighth of a cell, the finest resolution a terminal column has. The unfilled remainder is counted, not drawn, so a renderer states the track however its medium can.

use rsynko_ui::Gauge;

let gauge = Gauge::of(28, 8);

assert_eq!((gauge.filled, gauge.leading, gauge.track), (2, 1, 5));
assert_eq!(gauge.width(), 8);
assert_eq!(gauge.text(), "██▏     ");

FormatDescriptionAlg states what distinguishes one selectable format from its alternatives, and FormatLabelExt::format_label derives the aligned line a chooser compares them by. FormatChoiceViewAlg states what discovery has said about one request’s formats, as DiscoveryState.

§Screens

ScreenSyntax is the presentation vocabulary itself: runs of text carrying an emphasis, lines, rows, bodies that hold rows or a message or a draft or a record, and one screen composed from a header, a body, a status, and a footer. ManagerScreenExt::screen composes every manager page out of that vocabulary alone. Nothing in the composition names a widget, a border, a color, or a terminal.

ScreenSyntax::screen_text closes the loop: every renderer can say what its own screen reads as, which is what lets one presentation law run against the renderer that will actually draw it rather than against a stand-in.

§Laws

GaugeLaws::gauge_laws checks that a gauge occupies its width whatever share it states, never states a share before it is reached, and states nothing at zero and everything at one hundred.

KeyBindingLaws::key_binding_laws checks that no page binds one keystroke twice, that every bound keystroke denotes its binding, that exit is bound everywhere and no other modified keystroke denotes anything, that typed scalars insert exactly where a draft is held, and that a refused key leaves the manager as it was.

MenuPresentationLaws::menu_presentation_laws checks that every entry states its keys in brackets, that every stated key denotes that entry’s action, that every page offers exit last, and that an entry is stated as unavailable exactly when its action has no meaning.

ScreenLaws::screen_laws checks that every page names the path that reached it, states every menu entry it offers, names the key that fills an empty collection, states every offered details control exactly once, and states every note a record holds. A scenario reads screens back through the renderer ScreenLawFixture supplies, so a renderer is checked, not simulated.

Composing a screen is an extension, and its bounds are the vocabulary. Nothing here names a color, a widget, or a terminal:

use alux_ext::ext;
use rsynko_ui::{Emphasis, GAUGE_WIDTH, Gauge, ScreenSyntax};

#[ext(name = ExampleHeadingExt)]
impl<This> This
where
    This: ScreenSyntax,
{
    /// States one transfer as its name, how far it has come, and the share it has completed.
    fn example_heading(&self, title: &str, percent: u16) -> This::Line {
        self.line(
            [
                self.text(format!("{title}  "), Emphasis::Heading),
                self.gauge(Gauge::of(percent, GAUGE_WIDTH)),
                self.text(format!(" {percent:>3}%"), Emphasis::Plain),
            ]
            .into_iter(),
        )
    }

    /// States one page holding rows under a name, the cursor resting on the first of them.
    fn example_page(&self, title: &str, rows: impl Iterator<Item = This::Line>) -> This::Screen {
        let rows = rows.map(|line| self.row([line].into_iter())).collect::<Vec<_>>();
        let focused = (!rows.is_empty()).then_some(0);
        self.screen(
            self.line([self.text(title, Emphasis::Name)].into_iter()),
            self.rows(title, focused, rows.into_iter()),
            "Actions",
            self.line([].into_iter()),
        )
    }
}

What a chooser reads about one format is derived the same way, from what the format states about itself:

use alux_ext::ext;
use rsynko_manager::MediaStreams;
use rsynko_ui::{FormatDescriptionAlg, FormatLabelExt};

#[ext(name = ExampleChoiceExt)]
impl<This> This
where
    This: FormatDescriptionAlg,
{
    /// States one offered choice, marking the one the request currently fixes.
    fn example_choice(&self, chosen: bool) -> String {
        let marker = if chosen { "▸" } else { " " };
        format!("{marker} {}", self.format_label())
    }

    /// Observes whether the format carries everything a viewer needs by itself.
    fn example_complete(&self) -> bool {
        self.format_streams() == Some(MediaStreams::AudioVideo)
    }
}

Structs§

Application
Names the application one screen belongs to.
Gauge
States how much of a fixed-width track one completed share fills.
KeyBinding
Binds keystrokes on one page to the intention they denote.
Keystroke
Denotes one key press together with the modifiers that change what it denotes.
MenuItem
States one offered menu entry: what reaches it, what it does, and whether it means anything.

Enums§

DiscoveryState
Denotes what discovery has stated about one request’s selectable formats.
Emphasis
Denotes how much weight one piece of stated text carries.
Key
Names one key independently of the mechanism that reports it.

Constants§

ARMED_MODE
States the mode a request is in once the next run of it will write.
CHOICE_KEY_COLUMNS
Names the column budget one offered choice states its own name in.
COMPACT_TITLE_COLUMNS
Names the column budget a compact row gives a title.
CURSOR_MARK
Marks the value the cursor rests on.
EXIT_KEYSTROKE
Names the keystroke that leaves the application from every page.
EXPANDED_MARK
Marks the one request whose details are open.
EXPANDED_TITLE_COLUMNS
Names the column budget an expanded heading gives a title.
FIELD_LABEL_COLUMNS
Names the column budget every field label is stated in.
FIELD_VALUE_COLUMNS
Names the column budget one field value is stated in before it continues underneath itself.
GAUGE_WIDTH
Names the bar width every progress gauge is drawn in.
PATH_SEPARATOR
Separates the pages one page rests under, from the outermost inward.
REHEARSING_MODE
States the mode a request is in while it will only say what it would do.
SUBMISSION_EXAMPLES
States the shapes a source may be written in, and what each of them means.
UNSTATED
States a value no observation supplied.

Traits§

ChangeVocabularyExt
Names what a rehearsal states would happen.
ControlVocabularyExt
Names the details controls a reader sees.
FormatChoiceViewAlg
Specifies what one queue entry states about the formats a chooser may offer.
FormatDescriptionAlg
Specifies what one selectable format states where a chooser can read it.
FormatLabelExt
Derives what a chooser reads about one format.
FormatRolesExt
Derives which stream roles a request may actually be asked for.
GaugeLaws
Authors the progress-gauge laws.
KeyBindingExt
States the keys one page binds and what they denote there.
KeyBindingLaws
Authors the key-binding laws.
KeyInputExt
Interprets input as the intentions it denotes.
ManagerScreenExt
Composes every manager page from what the manager states about itself.
MenuPresentationExt
Derives the menu one page offers from the keys it binds and the actions it enables.
MenuPresentationLaws
Authors the menu-presentation laws.
PhaseVocabularyExt
Names the lifecycle phases a reader sees.
ScreenLawFixture
Supplies the renderer a presentation scenario cannot author for itself.
ScreenLaws
Authors the screen-composition laws.
ScreenSyntax
Specifies the presentation vocabulary every renderer supplies.
SpaceVocabularyExt
Names the meanings Space carries.
StreamVocabularyExt
Names the stream roles a reader chooses between.

Functions§

bytes_label
States a byte count in the largest unit stating it in full.
duration_label
States a duration in minutes and seconds, and in hours once it reaches one.
elided
States text within a column budget, ending elided text with an ellipsis.

Type Aliases§

ManagerKeyBinding
Names the binding one manager’s pages state, whatever that manager carries.