morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
backend.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4#include <chrono>
5#include <core/tui/Screen.hpp>
6#include <functional>
7#include <memory>
8
9#include "../attributes.hpp"
10#include "../ui/backend.hpp"
11
12/// @file
13/// @brief `morph::tui::Backend`: the terminal implementation of `ui::IViewBackend`.
14///
15/// Specified in `docs/spec/tui/frontend.md`, "Widgets".
16
17namespace morph::tui {
18
19/// @brief Builds every widget of a mounted view tree as a `core::tui::Component` on one `Screen`.
20///
21/// Containers arrange their children in cells when the screen renders them; a Dialog and an open Dropdown are
22/// screen overlays; drag-and-drop runs on the mouse with pointer capture. The factories, documented on
23/// `ui::IViewBackend`, run on the screen's thread. Widgets must be destroyed before the backend, and the backend
24/// before the screen.
25///
26/// The widgets show no tooltips, so nothing here needs the screen's hover timer (`Screen::tickHover`). A widget that
27/// goes drops the screen's hover target, so a caller that does tick it never reaches a destroyed component.
28class Backend final : public ui::IViewBackend {
29public:
30 /// @brief Reads the current time.
31 using Clock = std::function<std::chrono::steady_clock::time_point()>;
32
33 /// @param screen Where every widget renders. Borrowed: it must outlive the backend.
34 /// @param now The clock the widgets read the time from (a table telling a double click from two clicks); empty
35 /// reads `std::chrono::steady_clock`. A test passes one it advances itself.
36 explicit Backend(::core::tui::Screen& screen MORPH_LIFETIMEBOUND, Clock now = {});
37 /// @brief Destroys the backend; every widget it made must already be gone.
38 ~Backend() override;
39 Backend(Backend const&) = delete;
40 Backend& operator=(Backend const&) = delete;
41 Backend(Backend&&) = delete;
42 Backend& operator=(Backend&&) = delete;
43
44 [[nodiscard]] std::unique_ptr<ui::TextWidget> createText(ui::ContainerWidget* parent) override;
45 [[nodiscard]] std::unique_ptr<ui::ButtonWidget> createButton(ui::ContainerWidget* parent) override;
46 [[nodiscard]] std::unique_ptr<ui::TextInputWidget> createTextInput(ui::ContainerWidget* parent,
47 ui::TextInputMode mode) override;
48 [[nodiscard]] std::unique_ptr<ui::CheckboxWidget> createCheckbox(ui::ContainerWidget* parent) override;
49 [[nodiscard]] std::unique_ptr<ui::SelectWidget> createSelect(ui::ContainerWidget* parent,
50 ui::SelectStyle style) override;
51 [[nodiscard]] std::unique_ptr<ui::MenuWidget> createMenu(ui::ContainerWidget* parent) override;
52 [[nodiscard]] std::unique_ptr<ui::StackWidget> createStack(ui::ContainerWidget* parent, ui::Axis axis) override;
53 [[nodiscard]] std::unique_ptr<ui::GridWidget> createGrid(ui::ContainerWidget* parent) override;
54 [[nodiscard]] std::unique_ptr<ui::SpacerWidget> createSpacer(ui::ContainerWidget* parent) override;
55 [[nodiscard]] std::unique_ptr<ui::PanelWidget> createPanel(ui::ContainerWidget* parent) override;
56 [[nodiscard]] std::unique_ptr<ui::ScrollWidget> createScroll(ui::ContainerWidget* parent, ui::Axis axis) override;
57 [[nodiscard]] std::unique_ptr<ui::SlotWidget> createSlot(ui::ContainerWidget* parent) override;
58 [[nodiscard]] std::unique_ptr<ui::TabsWidget> createTabs(ui::ContainerWidget* parent) override;
59 [[nodiscard]] std::unique_ptr<ui::DialogWidget> createDialog(ui::ContainerWidget* parent) override;
60 [[nodiscard]] std::unique_ptr<ui::ToastWidget> createToast(ui::ContainerWidget* parent) override;
61 [[nodiscard]] std::unique_ptr<ui::BusyWidget> createBusy(ui::ContainerWidget* parent) override;
62 [[nodiscard]] std::unique_ptr<ui::TableWidget> createTable(ui::ContainerWidget* parent) override;
63 [[nodiscard]] std::unique_ptr<ui::DateTimeInputWidget> createDateTimeInput(ui::ContainerWidget* parent,
64 ui::DateMode mode,
65 int offsetMinutes) override;
66 [[nodiscard]] std::unique_ptr<ui::SliderWidget> createSlider(ui::ContainerWidget* parent) override;
67 [[nodiscard]] std::unique_ptr<ui::FilePickerWidget> createFilePicker(ui::ContainerWidget* parent,
68 ui::FilePickerMode mode) override;
69 [[nodiscard]] std::unique_ptr<ui::BannerWidget> createBanner(ui::ContainerWidget* parent) override;
70 [[nodiscard]] std::unique_ptr<ui::BadgeWidget> createBadge(ui::ContainerWidget* parent) override;
71 [[nodiscard]] std::unique_ptr<ui::ProgressWidget> createProgress(ui::ContainerWidget* parent) override;
72 [[nodiscard]] std::unique_ptr<ui::StepsWidget> createSteps(ui::ContainerWidget* parent) override;
73 [[nodiscard]] std::unique_ptr<ui::KeyValueWidget> createKeyValue(ui::ContainerWidget* parent) override;
74 [[nodiscard]] std::unique_ptr<ui::EmptyStateWidget> createEmptyState(ui::ContainerWidget* parent) override;
75 [[nodiscard]] std::unique_ptr<ui::DrawerWidget> createDrawer(ui::ContainerWidget* parent, ui::Side side) override;
76 [[nodiscard]] std::unique_ptr<ui::SplitterWidget> createSplitter(ui::ContainerWidget* parent,
77 ui::Axis axis) override;
78 [[nodiscard]] std::unique_ptr<ui::CollapsibleWidget> createCollapsible(ui::ContainerWidget* parent) override;
79 [[nodiscard]] std::unique_ptr<ui::DropZoneWidget> createDropZone(ui::ContainerWidget* parent) override;
80
81 /// @brief Sizes every root widget to the screen's viewport and centres every open dialog; call before each
82 /// `Screen::draw()`, which is how a resize reaches the tree.
83 void fit();
84
85 /// @brief Moves keyboard focus to the next focusable widget — inside the innermost open dialog when one is
86 /// open, because `Screen::focusNext` does not know overlays.
87 void focusNext();
88
89 /// @brief Moves keyboard focus to the previous focusable widget, with `focusNext`'s dialog rule.
90 void focusPrev();
91
92 /// @brief Focuses the first focusable widget when nothing has focus yet, or when the focus is on an open
93 /// dialog's frame (the dialog held nothing focusable when it opened, and has since gained something).
94 void focusFirst();
95
96 /// @brief Ends the drag-and-drop gesture in progress, if any, with no drop, as Esc does.
97 ///
98 /// Esc reaches the gesture through the widget that has the keyboard focus; a frontend calls this for an Esc no
99 /// widget took, so that a drag ends while nothing is focused too.
100 /// @return True when a drag was in progress and has ended.
101 bool endDrag();
102
103 /// @brief Whether any Busy widget is spinning, so the frontend should keep advancing the animation.
104 /// @return True while at least one Busy is active.
105 [[nodiscard]] bool animating() const noexcept;
106
107 /// @brief Moves every spinning Busy to its next frame; the next draw shows it.
108 void advanceAnimation() noexcept;
109
110private:
111 struct Impl;
112 std::unique_ptr<Impl> _impl;
113};
114
115} // namespace morph::tui
std::unique_ptr< ui::BusyWidget > createBusy(ui::ContainerWidget *parent) override
Makes a progress indicator.
std::unique_ptr< ui::DialogWidget > createDialog(ui::ContainerWidget *parent) override
Makes a modal overlay.
std::unique_ptr< ui::TableWidget > createTable(ui::ContainerWidget *parent) override
Makes a table.
std::unique_ptr< ui::ScrollWidget > createScroll(ui::ContainerWidget *parent, ui::Axis axis) override
Makes a scroll area.
std::unique_ptr< ui::SpacerWidget > createSpacer(ui::ContainerWidget *parent) override
Makes empty space.
void fit()
Sizes every root widget to the screen's viewport and centres every open dialog; call before each Scre...
std::unique_ptr< ui::BadgeWidget > createBadge(ui::ContainerWidget *parent) override
Makes a badge.
std::unique_ptr< ui::DrawerWidget > createDrawer(ui::ContainerWidget *parent, ui::Side side) override
Makes a drawer.
std::unique_ptr< ui::PanelWidget > createPanel(ui::ContainerWidget *parent) override
Makes a titled frame.
std::unique_ptr< ui::StepsWidget > createSteps(ui::ContainerWidget *parent) override
Makes a steps indicator.
std::unique_ptr< ui::SliderWidget > createSlider(ui::ContainerWidget *parent) override
Makes a slider.
bool animating() const noexcept
Whether any Busy widget is spinning, so the frontend should keep advancing the animation.
bool endDrag()
Ends the drag-and-drop gesture in progress, if any, with no drop, as Esc does.
std::unique_ptr< ui::SplitterWidget > createSplitter(ui::ContainerWidget *parent, ui::Axis axis) override
Makes a splitter.
void advanceAnimation() noexcept
Moves every spinning Busy to its next frame; the next draw shows it.
std::unique_ptr< ui::FilePickerWidget > createFilePicker(ui::ContainerWidget *parent, ui::FilePickerMode mode) override
Makes a file path field.
std::unique_ptr< ui::ProgressWidget > createProgress(ui::ContainerWidget *parent) override
Makes a progress bar.
std::unique_ptr< ui::TextWidget > createText(ui::ContainerWidget *parent) override
Makes read-only text.
void focusNext()
Moves keyboard focus to the next focusable widget — inside the innermost open dialog when one is open...
std::unique_ptr< ui::TabsWidget > createTabs(ui::ContainerWidget *parent) override
Makes a tab bar.
std::unique_ptr< ui::SelectWidget > createSelect(ui::ContainerWidget *parent, ui::SelectStyle style) override
Makes a choice of one option.
~Backend() override
Destroys the backend; every widget it made must already be gone.
void focusFirst()
Focuses the first focusable widget when nothing has focus yet, or when the focus is on an open dialog...
std::unique_ptr< ui::ButtonWidget > createButton(ui::ContainerWidget *parent) override
Makes a push button.
std::unique_ptr< ui::CollapsibleWidget > createCollapsible(ui::ContainerWidget *parent) override
Makes a collapsible section.
std::unique_ptr< ui::GridWidget > createGrid(ui::ContainerWidget *parent) override
Makes a grid.
std::unique_ptr< ui::KeyValueWidget > createKeyValue(ui::ContainerWidget *parent) override
Makes a key-value list.
Backend(::core::tui::Screen &screen, Clock now={})
std::unique_ptr< ui::DropZoneWidget > createDropZone(ui::ContainerWidget *parent) override
Makes a drop zone.
std::unique_ptr< ui::EmptyStateWidget > createEmptyState(ui::ContainerWidget *parent) override
Makes an empty state.
std::function< std::chrono::steady_clock::time_point()> Clock
Reads the current time.
Definition backend.hpp:31
std::unique_ptr< ui::SlotWidget > createSlot(ui::ContainerWidget *parent) override
Makes a slot.
std::unique_ptr< ui::DateTimeInputWidget > createDateTimeInput(ui::ContainerWidget *parent, ui::DateMode mode, int offsetMinutes) override
Makes a date or date-and-time field.
void focusPrev()
Moves keyboard focus to the previous focusable widget, with focusNext's dialog rule.
std::unique_ptr< ui::ToastWidget > createToast(ui::ContainerWidget *parent) override
Makes a transient message.
std::unique_ptr< ui::TextInputWidget > createTextInput(ui::ContainerWidget *parent, ui::TextInputMode mode) override
Makes a text field.
std::unique_ptr< ui::MenuWidget > createMenu(ui::ContainerWidget *parent) override
Makes a command list.
std::unique_ptr< ui::StackWidget > createStack(ui::ContainerWidget *parent, ui::Axis axis) override
Makes a stack.
std::unique_ptr< ui::CheckboxWidget > createCheckbox(ui::ContainerWidget *parent) override
Makes a check box.
std::unique_ptr< ui::BannerWidget > createBanner(ui::ContainerWidget *parent) override
Makes a banner.
A widget with children. A factory given this container appends the new widget as its last child.
Definition backend.hpp:103
Makes widgets. Each factory appends the new widget to parent, or makes a root when it is null,...
Definition backend.hpp:810
The backend contract: the retained widgets a mount drives, and the factory that makes them.
TextInputMode
How a TextInput edits.
Definition view.hpp:209
DateMode
What a DateTimeInput edits.
Definition view.hpp:228
Side
The edge a Drawer slides in from, in reading direction.
Definition view.hpp:249
FilePickerMode
Whether a FilePicker chooses an existing file or a file to write.
Definition view.hpp:234
SelectStyle
How a Select presents its options.
Definition view.hpp:216
Axis
The direction a stack arranges, or a scroll area scrolls, its content.
Definition view.hpp:222