Miscellaneous refactoring (#160)

* Reorganize ContainerBase

- Reduce Container overloads using default arguments
- Extract member function pointers to virtual functions
- Separate classes for Vertical, Horizontal and Tab containers

* Collect unpack from NodeDecorator subclasses

* Reduce redundant expansion for aliases
This commit is contained in:
Tushar Maheshwari 2021-07-20 13:29:47 +05:30 committed by GitHub
parent 210e8c5863
commit 09805e5e86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 100 additions and 142 deletions

View File

@ -24,26 +24,19 @@ int main(int argc, const char* argv[]) {
int shift = 0; int shift = 0;
class Graph { auto my_graph = [&shift](int width, int height) {
public: std::vector<int> output(width);
Graph(int* shift) : shift_(shift) {} for (int i = 0; i < width; ++i) {
std::vector<int> operator()(int width, int height) { float v = 0.5f;
std::vector<int> output(width); v += 0.1f * sin((i + shift) * 0.1f);
for (int i = 0; i < width; ++i) { v += 0.2f * sin((i + shift + 10) * 0.15f);
float v = 0; v += 0.1f * sin((i + shift) * 0.03f);
v += 0.1f * sin((i + *shift_) * 0.1f); v *= height;
v += 0.2f * sin((i + *shift_ + 10) * 0.15f); output[i] = (int)v;
v += 0.1f * sin((i + *shift_) * 0.03f);
v *= height;
v += 0.5f * height;
output[i] = (int)v;
}
return output;
} }
int* shift_; return output;
}; };
Graph my_graph(&shift);
auto htop = Renderer([&] { auto htop = Renderer([&] {
auto frequency = vbox({ auto frequency = vbox({
text(L"Frequency [Mhz]") | hcenter, text(L"Frequency [Mhz]") | hcenter,
@ -53,7 +46,7 @@ int main(int argc, const char* argv[]) {
filler(), filler(),
text(L"1200 "), text(L"1200 "),
filler(), filler(),
text(L"0% "), text(L"0 "),
}), }),
graph(std::ref(my_graph)) | flex, graph(std::ref(my_graph)) | flex,
}) | flex, }) | flex,

View File

@ -24,7 +24,6 @@ class ScreenInteractive : public Screen {
static ScreenInteractive FitComponent(); static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput(); static ScreenInteractive TerminalOutput();
~ScreenInteractive();
void Loop(Component); void Loop(Component);
std::function<void()> ExitLoopClosure(); std::function<void()> ExitLoopClosure();

View File

@ -36,7 +36,7 @@ class Node {
virtual void Render(Screen& screen); virtual void Render(Screen& screen);
protected: protected:
std::vector<Element> children_; Elements children_;
Requirement requirement_; Requirement requirement_;
Box box_; Box box_;
}; };

View File

@ -13,44 +13,10 @@ namespace ftxui {
class ContainerBase : public ComponentBase { class ContainerBase : public ComponentBase {
public: public:
static Component Vertical() { return Vertical({}); } ContainerBase(Components children, int* selector)
static Component Vertical(Components children) { : selector_(selector ? selector : &selected_) {
return Vertical(std::move(children), /*selector=*/nullptr);
}
static Component Vertical(Components children, int* selector) {
auto container = std::make_shared<ContainerBase>();
container->event_handler_ = &ContainerBase::VerticalEvent;
container->render_handler_ = &ContainerBase::VerticalRender;
container->selector_ = selector ? selector : &container->selected_;
for (Component& child : children) for (Component& child : children)
container->Add(std::move(child)); Add(std::move(child));
return container;
}
static Component Horizontal() { return Horizontal({}); }
static Component Horizontal(Components children) {
return Horizontal(std::move(children), /*selector=*/nullptr);
}
static Component Horizontal(Components children, int* selector) {
auto container = std::make_shared<ContainerBase>();
container->event_handler_ = &ContainerBase::HorizontalEvent;
container->render_handler_ = &ContainerBase::HorizontalRender;
container->selector_ = selector ? selector : &container->selected_;
for (Component& child : children)
container->Add(std::move(child));
return container;
}
static Component Tab(int* selector) { return Tab({}, selector); }
static Component Tab(Components children, int* selector) {
auto container = std::make_shared<ContainerBase>();
container->selector_ = selector ? selector : &container->selected_;
container->event_handler_ = &ContainerBase::TabEvent;
container->render_handler_ = &ContainerBase::TabRender;
container->is_tab_ = true;
for (Component& child : children)
container->Add(std::move(child));
return container;
} }
// Component override. // Component override.
@ -64,7 +30,7 @@ class ContainerBase : public ComponentBase {
if (ActiveChild() && ActiveChild()->OnEvent(event)) if (ActiveChild() && ActiveChild()->OnEvent(event))
return true; return true;
return (this->*event_handler_)(event); return EventHandler(event);
} }
Component ActiveChild() override { Component ActiveChild() override {
@ -83,10 +49,36 @@ class ContainerBase : public ComponentBase {
} }
} }
private: protected:
// Handlers // Handlers
virtual bool EventHandler(Event) { return false; }
bool VerticalEvent(Event event) { virtual bool OnMouseEvent(Event event) {
for (Component& child : children_) {
if (child->OnEvent(event))
return true;
}
return false;
}
int selected_ = 0;
int* selector_ = nullptr;
};
class VerticalContainer : public ContainerBase {
public:
using ContainerBase::ContainerBase;
Element Render() override {
Elements elements;
for (auto& it : children_)
elements.push_back(it->Render());
if (elements.size() == 0)
return text(L"Empty container");
return vbox(std::move(elements));
}
bool EventHandler(Event event) override {
int old_selected = *selector_; int old_selected = *selector_;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k'))
(*selector_)--; (*selector_)--;
@ -100,8 +92,22 @@ class ContainerBase : public ComponentBase {
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_)); *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
return old_selected != *selector_; return old_selected != *selector_;
} }
};
bool HorizontalEvent(Event event) { class HorizontalContainer : public ContainerBase {
public:
using ContainerBase::ContainerBase;
Element Render() override {
Elements elements;
for (auto& it : children_)
elements.push_back(it->Render());
if (elements.size() == 0)
return text(L"Empty container");
return hbox(std::move(elements));
}
bool EventHandler(Event event) override {
int old_selected = *selector_; int old_selected = *selector_;
if (event == Event::ArrowLeft || event == Event::Character('h')) if (event == Event::ArrowLeft || event == Event::Character('h'))
(*selector_)--; (*selector_)--;
@ -115,55 +121,22 @@ class ContainerBase : public ComponentBase {
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_)); *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
return old_selected != *selector_; return old_selected != *selector_;
} }
};
bool TabEvent(Event) { return false; } class TabContainer : public ContainerBase {
public:
using ContainerBase::ContainerBase;
bool OnMouseEvent(Event event) { Element Render() override {
if (is_tab_)
return ActiveChild()->OnEvent(event);
for (Component& child : children_) {
if (child->OnEvent(event))
return true;
}
return false;
}
using EventHandler = bool (ContainerBase::*)(Event);
using RenderHandler = Element (ContainerBase::*)();
Element Render() override { return (this->*render_handler_)(); }
Element VerticalRender() {
Elements elements;
for (auto& it : children_)
elements.push_back(it->Render());
if (elements.size() == 0)
return text(L"Empty container");
return vbox(std::move(elements));
}
Element HorizontalRender() {
Elements elements;
for (auto& it : children_)
elements.push_back(it->Render());
if (elements.size() == 0)
return text(L"Empty container");
return hbox(std::move(elements));
}
Element TabRender() {
Component active_child = ActiveChild(); Component active_child = ActiveChild();
if (active_child) if (active_child)
return active_child->Render(); return active_child->Render();
return text(L"Empty container"); return text(L"Empty container");
} }
EventHandler event_handler_; bool OnMouseEvent(Event event) override {
RenderHandler render_handler_; return ActiveChild()->OnEvent(event);
int selected_ = 0; }
int* selector_ = nullptr;
bool is_tab_ = false;
}; };
namespace Container { namespace Container {
@ -185,7 +158,7 @@ namespace Container {
/// }); /// });
/// ``` /// ```
Component Vertical(Components children) { Component Vertical(Components children) {
return ContainerBase::Vertical(std::move(children)); return Vertical(std::move(children), nullptr);
} }
/// @brief A list of components, drawn one by one vertically and navigated /// @brief A list of components, drawn one by one vertically and navigated
@ -207,7 +180,7 @@ Component Vertical(Components children) {
/// }); /// });
/// ``` /// ```
Component Vertical(Components children, int* selector) { Component Vertical(Components children, int* selector) {
return ContainerBase::Vertical(std::move(children), selector); return std::make_shared<VerticalContainer>(std::move(children), selector);
} }
/// @brief A list of components, drawn one by one horizontally and navigated /// @brief A list of components, drawn one by one horizontally and navigated
@ -228,7 +201,7 @@ Component Vertical(Components children, int* selector) {
/// }, &selected_children); /// }, &selected_children);
/// ``` /// ```
Component Horizontal(Components children) { Component Horizontal(Components children) {
return ContainerBase::Horizontal(std::move(children)); return Horizontal(std::move(children), nullptr);
} }
/// @brief A list of components, drawn one by one horizontally and navigated /// @brief A list of components, drawn one by one horizontally and navigated
@ -250,14 +223,14 @@ Component Horizontal(Components children) {
/// }, selected_children); /// }, selected_children);
/// ``` /// ```
Component Horizontal(Components children, int* selector) { Component Horizontal(Components children, int* selector) {
return ContainerBase::Horizontal(std::move(children), selector); return std::make_shared<HorizontalContainer>(std::move(children), selector);
} }
/// @brief A list of components, where only one is drawn and interacted with at /// @brief A list of components, where only one is drawn and interacted with at
/// a time. The |selector| gives the index of the selected component. This is /// a time. The |selector| gives the index of the selected component. This is
/// useful to implement tabs. /// useful to implement tabs.
/// @param selector The index of the drawn children.
/// @param children The list of components. /// @param children The list of components.
/// @param selector The index of the drawn children.
/// @ingroup component /// @ingroup component
/// @see ContainerBase /// @see ContainerBase
/// ///
@ -273,7 +246,7 @@ Component Horizontal(Components children, int* selector) {
/// }, &tab_drawn); /// }, &tab_drawn);
/// ``` /// ```
Component Tab(Components children, int* selector) { Component Tab(Components children, int* selector) {
return ContainerBase::Tab(std::move(children), selector); return std::make_shared<TabContainer>(std::move(children), selector);
} }
} // namespace Container } // namespace Container

View File

@ -241,8 +241,6 @@ ScreenInteractive::ScreenInteractive(int dimx,
event_sender_ = event_receiver_->MakeSender(); event_sender_ = event_receiver_->MakeSender();
} }
ScreenInteractive::~ScreenInteractive() {}
// static // static
ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) { ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) {
return ScreenInteractive(dimx, dimy, Dimension::Fixed, false); return ScreenInteractive(dimx, dimy, Dimension::Fixed, false);

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, blink #include "ftxui/dom/elements.hpp" // for Element, blink
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -26,7 +26,7 @@ class Blink : public NodeDecorator {
/// @brief The text drawn alternates in between visible and hidden. /// @brief The text drawn alternates in between visible and hidden.
/// @ingroup dom /// @ingroup dom
Element blink(Element child) { Element blink(Element child) {
return std::make_shared<Blink>(unpack(std::move(child))); return std::make_shared<Blink>(std::move(child));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, bold #include "ftxui/dom/elements.hpp" // for Element, bold
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -26,7 +26,7 @@ class Bold : public NodeDecorator {
/// @brief Use a bold font, for elements with more emphasis. /// @brief Use a bold font, for elements with more emphasis.
/// @ingroup dom /// @ingroup dom
Element bold(Element child) { Element bold(Element child) {
return std::make_shared<Bold>(unpack(std::move(child))); return std::make_shared<Bold>(std::move(child));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, clear_under #include "ftxui/dom/elements.hpp" // for Element, clear_under
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -30,7 +30,7 @@ class ClearUnder : public NodeDecorator {
/// @see ftxui::dbox /// @see ftxui::dbox
/// @ingroup dom /// @ingroup dom
Element clear_under(Element child) { Element clear_under(Element child) {
return std::make_shared<ClearUnder>(unpack(std::move(child))); return std::make_shared<ClearUnder>(std::move(child));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Decorator, Elements, bgcolor, color #include "ftxui/dom/elements.hpp" // for Element, Decorator, bgcolor, color
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" // for Color #include "ftxui/screen/color.hpp" // for Color
@ -11,8 +11,8 @@ namespace ftxui {
class BgColor : public NodeDecorator { class BgColor : public NodeDecorator {
public: public:
BgColor(Elements children, Color color) BgColor(Element child, Color color)
: NodeDecorator(std::move(children)), color_(color) {} : NodeDecorator(std::move(child)), color_(color) {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
@ -28,8 +28,8 @@ class BgColor : public NodeDecorator {
class FgColor : public NodeDecorator { class FgColor : public NodeDecorator {
public: public:
FgColor(Elements children, Color color) FgColor(Element child, Color color)
: NodeDecorator(std::move(children)), color_(color) {} : NodeDecorator(std::move(child)), color_(color) {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
@ -55,7 +55,7 @@ class FgColor : public NodeDecorator {
/// Element document = color(Color::Green, text(L"Success")), /// Element document = color(Color::Green, text(L"Success")),
/// ``` /// ```
Element color(Color color, Element child) { Element color(Color color, Element child) {
return std::make_shared<FgColor>(unpack(std::move(child)), color); return std::make_shared<FgColor>(std::move(child), color);
} }
/// @brief Set the background color of an element. /// @brief Set the background color of an element.
@ -70,7 +70,7 @@ Element color(Color color, Element child) {
/// Element document = bgcolor(Color::Green, text(L"Success")), /// Element document = bgcolor(Color::Green, text(L"Success")),
/// ``` /// ```
Element bgcolor(Color color, Element child) { Element bgcolor(Color color, Element child) {
return std::make_shared<BgColor>(unpack(std::move(child)), color); return std::make_shared<BgColor>(std::move(child), color);
} }
/// @brief Decorate using a foreground color. /// @brief Decorate using a foreground color.

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, dim #include "ftxui/dom/elements.hpp" // for Element, dim
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -26,7 +26,7 @@ class Dim : public NodeDecorator {
/// @brief Use a light font, for elements with less emphasis. /// @brief Use a light font, for elements with less emphasis.
/// @ingroup dom /// @ingroup dom
Element dim(Element child) { Element dim(Element child) {
return std::make_shared<Dim>(unpack(std::move(child))); return std::make_shared<Dim>(std::move(child));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -16,8 +16,7 @@ namespace ftxui {
class Select : public Node { class Select : public Node {
public: public:
Select(std::vector<std::shared_ptr<Node>> children) Select(Elements children) : Node(std::move(children)) {}
: Node(std::move(children)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
Node::ComputeRequirement(); Node::ComputeRequirement();
@ -31,7 +30,7 @@ class Select : public Node {
}; };
void SetBox(Box box) override { void SetBox(Box box) override {
box_ = box; Node::SetBox(box);
children_[0]->SetBox(box); children_[0]->SetBox(box);
} }
}; };
@ -44,7 +43,7 @@ Element select(Element child) {
class Focus : public Select { class Focus : public Select {
public: public:
Focus(std::vector<Element> children) : Select(std::move(children)) {} using Select::Select;
void ComputeRequirement() override { void ComputeRequirement() override {
Select::ComputeRequirement(); Select::ComputeRequirement();
@ -85,7 +84,7 @@ Element focus(Element child) {
class Frame : public Node { class Frame : public Node {
public: public:
Frame(std::vector<Element> children, bool x_frame, bool y_frame) Frame(Elements children, bool x_frame, bool y_frame)
: Node(std::move(children)), x_frame_(x_frame), y_frame_(y_frame) {} : Node(std::move(children)), x_frame_(x_frame), y_frame_(y_frame) {}
void ComputeRequirement() override { void ComputeRequirement() override {

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, inverted #include "ftxui/dom/elements.hpp" // for Element, inverted
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -27,7 +27,7 @@ class Inverted : public NodeDecorator {
/// colors. /// colors.
/// @ingroup dom /// @ingroup dom
Element inverted(Element child) { Element inverted(Element child) {
return std::make_shared<Inverted>(unpack(std::move(child))); return std::make_shared<Inverted>(std::move(child));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -3,7 +3,8 @@
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/node.hpp" // for Node, Elements #include "ftxui/dom/elements.hpp" // for Element, unpack
#include "ftxui/dom/node.hpp" // for Node
namespace ftxui { namespace ftxui {
struct Box; struct Box;
@ -11,7 +12,7 @@ struct Box;
// Helper class. // Helper class.
class NodeDecorator : public Node { class NodeDecorator : public Node {
public: public:
NodeDecorator(Elements children) : Node(std::move(children)) {} NodeDecorator(Element child) : Node(unpack(std::move(child))) {}
void ComputeRequirement() override; void ComputeRequirement() override;
void SetBox(Box box) override; void SetBox(Box box) override;
}; };

View File

@ -1,7 +1,7 @@
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, underlined #include "ftxui/dom/elements.hpp" // for Element, underlined
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -26,7 +26,7 @@ class Underlined : public NodeDecorator {
/// @brief Make the underlined element to be underlined. /// @brief Make the underlined element to be underlined.
/// @ingroup dom /// @ingroup dom
Element underlined(Element child) { Element underlined(Element child) {
return std::make_shared<Underlined>(unpack(std::move(child))); return std::make_shared<Underlined>(std::move(child));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -38,11 +38,6 @@ static const char* MOVE_LEFT = "\r";
static const char* MOVE_UP = "\x1B[1A"; static const char* MOVE_UP = "\x1B[1A";
static const char* CLEAR_LINE = "\x1B[2K"; static const char* CLEAR_LINE = "\x1B[2K";
bool In(const Box& stencil, int x, int y) {
return stencil.x_min <= x && x <= stencil.x_max && //
stencil.y_min <= y && y <= stencil.y_max;
}
Pixel dev_null_pixel; Pixel dev_null_pixel;
#if defined(_WIN32) #if defined(_WIN32)
@ -195,7 +190,7 @@ wchar_t& Screen::at(int x, int y) {
/// @param x The pixel position along the x-axis. /// @param x The pixel position along the x-axis.
/// @param y The pixel position along the y-axis. /// @param y The pixel position along the y-axis.
Pixel& Screen::PixelAt(int x, int y) { Pixel& Screen::PixelAt(int x, int y) {
return In(stencil, x, y) ? pixels_[y][x] : dev_null_pixel; return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel;
} }
/// @brief Return a string to be printed in order to reset the cursor position /// @brief Return a string to be printed in order to reset the cursor position