mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-01 02:58:12 +08:00 
			
		
		
		
	Pipeable decoration and the package_manager example.
- Pipeable decorator. - package_manager example.
This commit is contained in:
		| @@ -22,7 +22,7 @@ | ||||
| ## Level 3: ftxui::component::Component | ||||
|   A hierarchical set of component. A component render itself by producing | ||||
|   ftxui::dom::Node in Component::Render(). | ||||
|    | ||||
|  | ||||
|   Some component can handle events: | ||||
|     * keyboard | ||||
|     * mouse | ||||
|   | ||||
| @@ -18,17 +18,17 @@ using Children = std::vector<Child>; | ||||
| Element vbox(Children); | ||||
| Element hbox(Children); | ||||
| Element dbox(Children); | ||||
| Element flex(); | ||||
| Element filler(); | ||||
| Element flex(Element); | ||||
|  | ||||
| // --- Widget -- | ||||
| Element text(std::wstring text); | ||||
| Element separator(); | ||||
| Element gauge(float ratio); | ||||
| Element frame(Child); | ||||
| Element frame(Child title, Child content); | ||||
| Element frame(Element); | ||||
| Element window(Child title, Child content); | ||||
|  | ||||
| // -- Style --- | ||||
| // -- Decorator --- | ||||
| Element bold(Element); | ||||
| Element dim(Element); | ||||
| Element inverted(Element); | ||||
| @@ -46,8 +46,13 @@ Element center(Element); | ||||
|  | ||||
| // --- Util --- | ||||
| Element nothing(Element element); | ||||
| Decorator compose(Decorator, Decorator); | ||||
|  | ||||
| // Pipe elements into decorator togethers. | ||||
| // Examples: text("ftxui") | bold | underlined; | ||||
| Element operator|(Element, Decorator); | ||||
| Decorator operator|(Decorator, Decorator); | ||||
|  | ||||
| // Make container able to take several children. | ||||
| template <class... Args> | ||||
| Children unpack(Args... args) { | ||||
|   Children vec; | ||||
| @@ -55,20 +60,15 @@ Children unpack(Args... args) { | ||||
|   return vec; | ||||
| } | ||||
|  | ||||
| template <class... Args> | ||||
| Element vbox(Args... children) { | ||||
|   return vbox(unpack(std::forward<Args>(children)...)); | ||||
| } | ||||
| #define TAKE_ANY_ARGS(container) \ | ||||
|   template <class... Args> \ | ||||
|   Element container(Args... children) { \ | ||||
|     return container(unpack(std::forward<Args>(children)...)); \ | ||||
|   } \ | ||||
|  | ||||
| template <class... Args> | ||||
| Element hbox(Args... children) { | ||||
|   return hbox(unpack(std::forward<Args>(children)...)); | ||||
| } | ||||
|  | ||||
| template <class... Args> | ||||
| Element dbox(Args... children) { | ||||
|   return dbox(unpack(std::forward<Args>(children)...)); | ||||
| } | ||||
| TAKE_ANY_ARGS(vbox) | ||||
| TAKE_ANY_ARGS(hbox) | ||||
| TAKE_ANY_ARGS(dbox) | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
|   | ||||
| @@ -49,7 +49,7 @@ class Screen { | ||||
|   // Fill with space. | ||||
|   void Clear(); | ||||
|  | ||||
|  private: | ||||
|  protected: | ||||
|   size_t dimx_; | ||||
|   size_t dimy_; | ||||
|   std::vector<std::vector<Pixel>> pixels_; | ||||
|   | ||||
| @@ -14,7 +14,10 @@ namespace component { | ||||
|  | ||||
| class ScreenInteractive : public Screen { | ||||
|   public: | ||||
|     ScreenInteractive(size_t dimx, size_t dimy); | ||||
|     static ScreenInteractive FixedSize(size_t dimx, size_t dimy); | ||||
|     static ScreenInteractive Fullscreen(); | ||||
|     static ScreenInteractive TerminalOutput(); | ||||
|  | ||||
|     ~ScreenInteractive(); | ||||
|     component::Delegate* delegate(); | ||||
|     void Loop(); | ||||
| @@ -27,6 +30,15 @@ class ScreenInteractive : public Screen { | ||||
| 	 void Clear(); | ||||
| 	 void Draw(); | ||||
| 	 bool quit_ = false; | ||||
|  | ||||
|    enum class Dimension { | ||||
|      Fixed, | ||||
|      TerminalOutput, | ||||
|      Fullscreen, | ||||
|    }; | ||||
|    Dimension dimension_ = Dimension::Fixed; | ||||
|  | ||||
|    ScreenInteractive(size_t dimx, size_t dimy, Dimension dimension); | ||||
| }; | ||||
|  | ||||
| }  // namespace ftxui | ||||
|   | ||||
| @@ -15,14 +15,14 @@ dom::Element Input::Render() { | ||||
|   // Placeholder. | ||||
|   if (content.size() == 0) { | ||||
|     if (is_focused) | ||||
|       return flex(inverted(dim(text(placeholder)))); | ||||
|       return text(placeholder) | dim | inverted | flex; | ||||
|     else | ||||
|       return flex(dim(text(placeholder))); | ||||
|       return text(placeholder) | dim | flex; | ||||
|   } | ||||
|  | ||||
|   // Not focused. | ||||
|   if (!is_focused) | ||||
|     return flex(text(content)); | ||||
|     return text(content) | flex; | ||||
|  | ||||
|   std::wstring part_before_cursor = content.substr(0,cursor_position); | ||||
|   std::wstring part_at_cursor = cursor_position < (int)content.size() | ||||
| @@ -31,11 +31,12 @@ dom::Element Input::Render() { | ||||
|   std::wstring part_after_cursor = cursor_position < (int)content.size() - 1 | ||||
|                                        ? content.substr(cursor_position + 1) | ||||
|                                        : L""; | ||||
|   return flex(inverted(hbox(             // | ||||
|       text(part_before_cursor),          // | ||||
|       underlined(text(part_at_cursor)),  // | ||||
|       text(part_after_cursor)            // | ||||
|       )));                               // | ||||
|   return | ||||
|     hbox( | ||||
|       text(part_before_cursor), | ||||
|       text(part_at_cursor) | underlined, | ||||
|       text(part_after_cursor) | ||||
|     ) | flex | inverted; | ||||
| } | ||||
| bool Input::OnEvent(Event event) { | ||||
|   std::wstring c; | ||||
|   | ||||
| @@ -10,18 +10,16 @@ dom::Element Toggle::Render() { | ||||
|   auto highlight = Focused() ? inverted : bold; | ||||
|  | ||||
|   Children children; | ||||
|   children.push_back(text(L"[")); | ||||
|  | ||||
|   for(size_t i = 0; i<options.size(); ++i) { | ||||
|     // Separator. | ||||
|     if (i != 0) | ||||
|       children.push_back(text(L"|")); | ||||
|       children.push_back(separator()); | ||||
|  | ||||
|     // Entry. | ||||
|     auto style = i == activated ? highlight : dim; | ||||
|     children.push_back(style(text(options[i]))); | ||||
|   } | ||||
|   children.push_back(text(L"]")); | ||||
|   return hbox(std::move(children)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,12 +10,12 @@ class Bold : public NodeDecorator { | ||||
|   ~Bold() override {} | ||||
|  | ||||
|   void Render(Screen& screen) override { | ||||
|     Node::Render(screen); | ||||
|     for (int y = box_.top; y <= box_.bottom; ++y) { | ||||
|       for (int x = box_.left; x <= box_.right; ++x) { | ||||
|         screen.PixelAt(x,y).bold = true;  | ||||
|       } | ||||
|     } | ||||
|     Node::Render(screen); | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -10,12 +10,12 @@ class BgColor : public NodeDecorator { | ||||
|       : NodeDecorator(std::move(children)), color_(color) {} | ||||
|  | ||||
|   void Render(Screen& screen) override { | ||||
|     NodeDecorator::Render(screen); | ||||
|     for (int y = box_.top; y <= box_.bottom; ++y) { | ||||
|       for (int x = box_.left; x <= box_.right; ++x) { | ||||
|         screen.PixelAt(x, y).background_color = color_; | ||||
|       } | ||||
|     } | ||||
|     NodeDecorator::Render(screen); | ||||
|   } | ||||
|  | ||||
|   Color color_; | ||||
| @@ -28,12 +28,12 @@ class FgColor : public NodeDecorator { | ||||
|   ~FgColor() override {} | ||||
|  | ||||
|   void Render(Screen& screen) override { | ||||
|     NodeDecorator::Render(screen); | ||||
|     for (int y = box_.top; y <= box_.bottom; ++y) { | ||||
|       for (int x = box_.left; x <= box_.right; ++x) { | ||||
|         screen.PixelAt(x, y).foreground_color = color_; | ||||
|       } | ||||
|     } | ||||
|     NodeDecorator::Render(screen); | ||||
|   } | ||||
|  | ||||
|   Color color_; | ||||
|   | ||||
| @@ -5,11 +5,11 @@ namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| std::unique_ptr<Node> hcenter(Element child) { | ||||
|   return hbox(flex(), std::move(child), flex()); | ||||
|   return hbox(filler(), std::move(child), filler()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<Node> vcenter(Element child) { | ||||
|   return vbox(flex(), std::move(child), flex()); | ||||
|   return vbox(filler(), std::move(child), filler()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<Node> center(Element child) { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class Flex : public Node { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Node> flex() { | ||||
| std::unique_ptr<Node> filler() { | ||||
|   return std::make_unique<Flex>(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -84,9 +84,15 @@ std::unique_ptr<Node> frame(Child child) { | ||||
|   return std::make_unique<Frame>(unpack(std::move(child))); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<Node> frame(Child title, Child content) { | ||||
| std::unique_ptr<Node> window(Child title, Child content) { | ||||
|   return std::make_unique<Frame>(unpack(std::move(content), std::move(title))); | ||||
| } | ||||
|  | ||||
| Decorator boxed() { | ||||
|   return [](Child child) { | ||||
|     return frame(std::move(child)); | ||||
|   }; | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
|   | ||||
| @@ -62,7 +62,7 @@ TEST(HBoxTest, ScreenBigger2) { | ||||
| TEST(HBoxTest, ScreenSmaller1Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     filler(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(11,1); | ||||
| @@ -74,7 +74,7 @@ TEST(HBoxTest, ScreenSmaller1Flex) { | ||||
| TEST(HBoxTest, ScreenSmaller2Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     filler(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(10,1); | ||||
| @@ -86,7 +86,7 @@ TEST(HBoxTest, ScreenSmaller2Flex) { | ||||
| TEST(HBoxTest, ScreenFitFlex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     filler(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(12,1); | ||||
| @@ -98,7 +98,7 @@ TEST(HBoxTest, ScreenFitFlex) { | ||||
| TEST(HBoxTest, ScreenBigger1Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     filler(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(13,1); | ||||
| @@ -110,7 +110,7 @@ TEST(HBoxTest, ScreenBigger1Flex) { | ||||
| TEST(HBoxTest, ScreenBigger2Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     filler(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(14,1); | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class Separator : public Node { | ||||
|     wchar_t c = U'+'; | ||||
|     if (is_line && !is_column) | ||||
|       c = U'─'; | ||||
|     else if (!is_line && is_column) | ||||
|     else | ||||
|       c = U'│'; | ||||
|  | ||||
|     for (int y = box_.top; y <= box_.bottom; ++y) { | ||||
|   | ||||
| @@ -12,9 +12,17 @@ Decorator compose(Decorator a, Decorator b) { | ||||
|     a = std::move(a), | ||||
|     b = std::move(b) | ||||
|   ](Element element) { | ||||
|     return a(b(std::move(element))); | ||||
|     return b(a(std::move(element))); | ||||
|   }; | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
| Decorator operator|(Decorator a, Decorator b) { | ||||
|   return compose(a, b); | ||||
| } | ||||
|  | ||||
| Element operator|(Element e, Decorator d) { | ||||
|   return d(std::move(e)); | ||||
| } | ||||
|  | ||||
| }  // namespace dom | ||||
| }  // namespace ftxui | ||||
|   | ||||
| @@ -37,7 +37,7 @@ TEST(VBoxTest, ScreenBigger2) { | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenSmaller1Flex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); | ||||
|   auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); | ||||
|   Screen screen(6, 1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
| @@ -45,7 +45,7 @@ TEST(VBoxTest, ScreenSmaller1Flex) { | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenFitFlex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); | ||||
|   auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); | ||||
|   Screen screen(6, 2); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
| @@ -53,14 +53,14 @@ TEST(VBoxTest, ScreenFitFlex) { | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenBigger1Flex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); | ||||
|   auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); | ||||
|   Screen screen(6, 3); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1\n      \ntext_2", screen.ToString()); | ||||
| } | ||||
| TEST(VBoxTest, ScreenBigger2Flex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); | ||||
|   auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); | ||||
|   Screen screen(6, 4); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   | ||||
| @@ -12,28 +12,19 @@ Screen::Screen(size_t dimx, size_t dimy) | ||||
|  | ||||
| void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) { | ||||
|   if (next.bold != previous.bold) | ||||
|     ss << (next.bold ? L"\e[1m" : L"\e[0m"); | ||||
|  | ||||
|   if (next.inverted != previous.inverted) | ||||
|     ss << (next.inverted ? L"\e[7m" : L"\e[27m"); | ||||
|  | ||||
|   if (next.underlined != previous.underlined) | ||||
|     ss << (next.underlined ? L"\e[4m" : L"\e[24m"); | ||||
|  | ||||
|     ss << (next.bold ? L"\e[1m" : L"\e[22m"); // Can't use 21 here. | ||||
|   if (next.dim != previous.dim) | ||||
|     ss << (next.dim ? L"\e[2m" : L"\e[22m"); | ||||
|  | ||||
|   if (next.underlined != previous.underlined) | ||||
|     ss << (next.underlined ? L"\e[4m" : L"\e[24m"); | ||||
|   if (next.blink != previous.blink) | ||||
|     ss << (next.blink ? L"\e[5m" : L"\e[25m"); | ||||
|  | ||||
|   if (next.foreground_color != previous.foreground_color) { | ||||
|     ss << L"\e[" + to_wstring(std::to_string((uint8_t)next.foreground_color)) + | ||||
|               L"m"; | ||||
|   } | ||||
|   if (next.background_color != previous.background_color) { | ||||
|     ss << L"\e[" + | ||||
|               to_wstring(std::to_string(10 + (uint8_t)next.background_color)) + | ||||
|               L"m"; | ||||
|   if (next.inverted != previous.inverted) | ||||
|     ss << (next.inverted ? L"\e[7m" : L"\e[27m"); | ||||
|   if (next.foreground_color != previous.foreground_color || | ||||
|       next.background_color != previous.background_color) { | ||||
|     ss << L"\e[" + to_wstring(std::to_string((uint8_t)next.foreground_color)) + L"m"; | ||||
|     ss << L"\e[" + to_wstring(std::to_string(10 + (uint8_t)next.background_color)) + L"m"; | ||||
|   } | ||||
|  | ||||
|   previous = next; | ||||
| @@ -49,8 +40,11 @@ std::string Screen::ToString() { | ||||
|       UpdatePixelStyle(ss, previous_pixel, pixels_[y][x]); | ||||
|       ss << pixels_[y][x].character; | ||||
|     } | ||||
|  | ||||
|     if (y + 1 < dimy_) | ||||
|       ss << '\n'; | ||||
|     Pixel final_pixel; | ||||
|     UpdatePixelStyle(ss, previous_pixel, final_pixel); | ||||
|   } | ||||
|  | ||||
|   return to_string(ss.str()); | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #include "ftxui/screen_interactive.hpp" | ||||
|  | ||||
| #include "ftxui/component/component.hpp" | ||||
| #include "ftxui/component/delegate.hpp" | ||||
| #include "ftxui/terminal.hpp" | ||||
| #include <iostream> | ||||
| #include <stdio.h> | ||||
| #include <termios.h> | ||||
| @@ -86,10 +88,27 @@ class ScreenInteractive::Delegate : public component::Delegate { | ||||
|   component::Component* component() override { return component_; } | ||||
| }; | ||||
|  | ||||
| ScreenInteractive::ScreenInteractive(size_t dimx, size_t dimy) | ||||
|     : Screen(dimx, dimy), delegate_(new Delegate) {} | ||||
| ScreenInteractive::ScreenInteractive(size_t dimx, | ||||
|                                      size_t dimy, | ||||
|                                      Dimension dimension) | ||||
|     : Screen(dimx, dimy), delegate_(new Delegate), dimension_(dimension) {} | ||||
| ScreenInteractive::~ScreenInteractive() {} | ||||
|  | ||||
| // static | ||||
| ScreenInteractive ScreenInteractive::FixedSize(size_t dimx, size_t dimy) { | ||||
|   return ScreenInteractive(dimx, dimy, Dimension::Fixed); | ||||
| } | ||||
|  | ||||
| // static | ||||
| ScreenInteractive ScreenInteractive::Fullscreen() { | ||||
|   return ScreenInteractive(0, 0, Dimension::Fullscreen); | ||||
| } | ||||
|  | ||||
| // static | ||||
| ScreenInteractive ScreenInteractive::TerminalOutput() { | ||||
|   return ScreenInteractive(0, 0, Dimension::TerminalOutput); | ||||
| } | ||||
|  | ||||
| void ScreenInteractive::Loop() { | ||||
|   std::cout << "\033[?9h";    /* Send Mouse Row & Column on Button Press */ | ||||
|   std::cout << "\033[?1000h"; /* Send Mouse X & Y on button press and release */ | ||||
| @@ -110,14 +129,12 @@ void ScreenInteractive::Loop() { | ||||
|   tcsetattr(STDIN_FILENO, TCSANOW, &terminal_configuration_new); | ||||
|  | ||||
|   Draw(); | ||||
|   while (!quit_) { | ||||
|   while(!quit_) { | ||||
|     delegate_->OnEvent(GetEvent()); | ||||
|  | ||||
|     Clear(); | ||||
|     Draw(); | ||||
|   } | ||||
|   std::cout << std::endl; | ||||
|   //Clear(); | ||||
|   } while(!quit_); | ||||
|   //std::cout << std::endl; | ||||
|  | ||||
|   // Restore the old terminal configuration. | ||||
|   tcsetattr(STDIN_FILENO, TCSANOW, &terminal_configuration_old); | ||||
| @@ -125,6 +142,29 @@ void ScreenInteractive::Loop() { | ||||
|  | ||||
| void ScreenInteractive::Draw() { | ||||
|   auto document = delegate_->component()->Render(); | ||||
|   size_t dimx; | ||||
|   size_t dimy; | ||||
|   switch(dimension_) { | ||||
|     case Dimension::Fixed: | ||||
|       break; | ||||
|     case Dimension::TerminalOutput: | ||||
|       document->ComputeRequirement(); | ||||
|       dimx = Terminal::Size().dimx; | ||||
|       dimy = document->requirement().min.y; | ||||
|       break; | ||||
|     case Dimension::Fullscreen: | ||||
|       document->ComputeRequirement(); | ||||
|       dimx = Terminal::Size().dimx; | ||||
|       dimy = Terminal::Size().dimy; | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   if (dimx != dimx_ || dimy != dimy_) { | ||||
|     dimx_ = dimx; | ||||
|     dimy_ = dimy; | ||||
|     pixels_ = std::vector<std::vector<Pixel>>(dimy, std::vector<Pixel>(dimx)); | ||||
|   } | ||||
|  | ||||
|   Render(*this, document.get()); | ||||
|   std::cout << ToString() << std::flush; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Arthur Sonzogni
					Arthur Sonzogni