mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-10-31 10:38:09 +08:00 
			
		
		
		
	Initial prototype
This commit is contained in:
		
							
								
								
									
										52
									
								
								ftxui/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								ftxui/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| cmake_minimum_required(VERSION 3.0) | ||||
| project(ftxui) | ||||
|  | ||||
| add_library(ftxui | ||||
|   src/ftxui/core/component.cpp | ||||
|   src/ftxui/core/dom/flex.cpp | ||||
|   src/ftxui/core/dom/hbox.cpp | ||||
|   src/ftxui/core/dom/node.cpp | ||||
|   src/ftxui/core/dom/text.cpp | ||||
|   src/ftxui/core/dom/vbox.cpp | ||||
|   src/ftxui/core/screen.cpp | ||||
|   src/ftxui/core/terminal.cpp | ||||
|   src/ftxui/util/string.cpp | ||||
| ) | ||||
|  | ||||
|  | ||||
| target_include_directories(ftxui | ||||
|   PUBLIC include | ||||
|   PRIVATE src | ||||
| ) | ||||
|  | ||||
| target_compile_features(ftxui PUBLIC cxx_std_17) | ||||
| target_compile_options(ftxui PRIVATE -Wall) | ||||
|  | ||||
| # Note: For gtest, please follow: | ||||
| # https://stackoverflow.com/questions/24295876/cmake-cannot-find-a-googletest-required-library | ||||
| find_package(GTest) | ||||
| find_package(Threads) | ||||
| if (GTEST_FOUND AND THREADS_FOUND) | ||||
|   function(add_new_test test_name test_files) | ||||
|     add_executable(${ARGV}) | ||||
|     target_link_libraries(${test_name} | ||||
|       PRIVATE | ||||
|         ftxui | ||||
|         Threads::Threads | ||||
|         ${GTEST_BOTH_LIBRARIES} | ||||
|     ) | ||||
|     target_include_directories(ftxui | ||||
|       PRIVATE | ||||
|         ${GTest_INCLUDE_DIRS} | ||||
|         ftxui | ||||
|     ) | ||||
|     gtest_discover_tests(${test_name}) | ||||
|     add_test(${test_name} ${test_name}) | ||||
|   endfunction(add_new_test) | ||||
|  | ||||
|   add_new_test(dom_tests | ||||
|     src/ftxui/core/dom/text_test.cpp | ||||
|     src/ftxui/core/dom/hbox_test.cpp | ||||
|     src/ftxui/core/dom/vbox_test.cpp | ||||
|   ) | ||||
| endif() | ||||
							
								
								
									
										1
									
								
								ftxui/include/ftxui/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ftxui/include/ftxui/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| State => Components => Document => Text. | ||||
							
								
								
									
										11
									
								
								ftxui/include/ftxui/core/box.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ftxui/include/ftxui/core/box.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #ifndef FTX_UI_CORE_BOX | ||||
| #define FTX_UI_CORE_BOX | ||||
|  | ||||
| struct Box { | ||||
|   int left; | ||||
|   int right; | ||||
|   int top; | ||||
|   int bottom; | ||||
| }; | ||||
|  | ||||
| #endif /* end of include guard: FTX_UI_CORE_BOX */ | ||||
							
								
								
									
										23
									
								
								ftxui/include/ftxui/core/component.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								ftxui/include/ftxui/core/component.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #ifndef FTXUI_STATE_HPP | ||||
| #define FTXUI_STATE_HPP | ||||
|  | ||||
| #include "ftxui/core/requirement.hpp" | ||||
| #include "ftxui/core/document.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| class Component { | ||||
|  public: | ||||
|   virtual Document Render() = 0; | ||||
|  | ||||
|   // Requirement ------------------------------------------------------------- | ||||
|   virtual void ComputeRequirement(); | ||||
|   Requirement requirement() { return requirement_; } | ||||
|  | ||||
|  private: | ||||
|   Requirement requirement_; | ||||
| }; | ||||
|  | ||||
| };  // namespace ftxui | ||||
|  | ||||
| #endif /* end of include guard: FTXUI_STATE_HPP */ | ||||
							
								
								
									
										12
									
								
								ftxui/include/ftxui/core/document.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								ftxui/include/ftxui/core/document.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #ifndef FTXUI_DOCUMENT_HPP | ||||
| #define FTXUI_DOCUMENT_HPP | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| class Document { | ||||
|     | ||||
| }; | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif /* end of include guard: FTXUI_DOCUMENT_HPP */ | ||||
							
								
								
									
										38
									
								
								ftxui/include/ftxui/core/dom/elements.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								ftxui/include/ftxui/core/dom/elements.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| #ifndef FTXUI_CORE_DOM_ELEMENTS_HPP | ||||
| #define FTXUI_CORE_DOM_ELEMENTS_HPP | ||||
|  | ||||
| #include "ftxui/core/dom/node.hpp" | ||||
| #include <initializer_list> | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| using Child = std::unique_ptr<Node>; | ||||
| using Children = std::vector<std::unique_ptr<Node>>; | ||||
|  | ||||
| std::unique_ptr<Node> vbox(Children); | ||||
| std::unique_ptr<Node> hbox(Children); | ||||
| std::unique_ptr<Node> text(std::wstring text); | ||||
| std::unique_ptr<Node> flex(); | ||||
|  | ||||
| template <class... Args> | ||||
| std::vector<Child> unpack(Args... args) { | ||||
|   std::vector<Child> vec; | ||||
|   (vec.push_back(std::forward<Args>(args)), ...); | ||||
|   return vec; | ||||
| } | ||||
|  | ||||
| template <class... Args> | ||||
| std::unique_ptr<Node> vbox(Args... children) { | ||||
|   return vbox(unpack(std::forward<Args>(children)...)); | ||||
| } | ||||
|  | ||||
| template <class... Args> | ||||
| std::unique_ptr<Node> hbox(Args... children) { | ||||
|   return hbox(unpack(std::forward<Args>(children)...)); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
|  | ||||
| #endif /* end of include guard: FTXUI_CORE_DOM_ELEMENTS_HPP */ | ||||
							
								
								
									
										44
									
								
								ftxui/include/ftxui/core/dom/node.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								ftxui/include/ftxui/core/dom/node.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| #ifndef CORE_DOM_NODE_HPP | ||||
| #define CORE_DOM_NODE_HPP | ||||
|  | ||||
| #include <memory> | ||||
| #include <vector> | ||||
|  | ||||
| #include "ftxui/core/requirement.hpp" | ||||
| #include "ftxui/core/screen.hpp" | ||||
| #include "ftxui/core/box.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| class Node { | ||||
|  public: | ||||
|   Node(); | ||||
|   Node(std::vector<std::unique_ptr<Node>> children); | ||||
|   virtual ~Node(); | ||||
|  | ||||
|   // Step 1: Direction parent <= children. | ||||
|   //         Compute layout requirement. Tell parent what dimensions this | ||||
|   //         element wants to be. | ||||
|   virtual void ComputeRequirement(); | ||||
|   Requirement requirement() { return requirement_; } | ||||
|  | ||||
|   // Step 2: Direction parent => children. | ||||
|   //         Assign this element its final dimensions. | ||||
|   virtual void SetBox(Box box); | ||||
|  | ||||
|   // Step 3: Draw this element. | ||||
|   virtual void Render(Screen& screen); | ||||
|  | ||||
|   std::vector<std::unique_ptr<Node>> children; | ||||
|  protected: | ||||
|   Requirement requirement_; | ||||
|   Box box_; | ||||
| }; | ||||
|  | ||||
| void Render(Screen& screen, Node* node); | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
|  | ||||
| #endif /* end of include guard: CORE_DOM_NODE_HPP */ | ||||
							
								
								
									
										28
									
								
								ftxui/include/ftxui/core/requirement.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								ftxui/include/ftxui/core/requirement.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #ifndef FTXUI_LAYOUT_REQUIREMENT_HPP | ||||
| #define FTXUI_LAYOUT_REQUIREMENT_HPP | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| struct Requirement { | ||||
|   // Minimal dimensions. | ||||
|   struct { | ||||
|     int x = 0; | ||||
|     int y = 0; | ||||
|   } min; | ||||
|  | ||||
|   // Maximal dimensions. | ||||
|   struct { | ||||
|     int x = -1; | ||||
|     int y = -1; | ||||
|   } max; | ||||
|  | ||||
|   // Flex. | ||||
|   struct { | ||||
|     int x = 0; | ||||
|     int y = 0; | ||||
|   } flex; | ||||
| }; | ||||
|  | ||||
| };  // namespace ftxui | ||||
|  | ||||
| #endif /* end of include guard: FTXUI_LAYOUT_REQUIREMENT_HPP */ | ||||
							
								
								
									
										28
									
								
								ftxui/include/ftxui/core/screen.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								ftxui/include/ftxui/core/screen.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #ifndef FTXUI_CORE_SCREEN | ||||
| #define FTXUI_CORE_SCREEN | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| class Screen { | ||||
|  public: | ||||
|   Screen(size_t dimx, size_t dimy); | ||||
|   wchar_t& at(size_t x, size_t y); | ||||
|   std::string ToString(); | ||||
|  | ||||
|   size_t dimx() { return dimx_;} | ||||
|   size_t dimy() { return dimy_;} | ||||
|  | ||||
|   static Screen WholeTerminal(); | ||||
|  | ||||
|  private: | ||||
|   size_t dimx_; | ||||
|   size_t dimy_; | ||||
|   std::vector<std::wstring> lines_; | ||||
| }; | ||||
|  | ||||
| };  // namespace ftxui | ||||
|  | ||||
| #endif /* end of include guard: FTXUI_CORE_SCREEN */ | ||||
							
								
								
									
										4
									
								
								ftxui/include/ftxui/util/string.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ftxui/include/ftxui/util/string.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| #include <string> | ||||
|  | ||||
| std::string to_string(const std::wstring& s); | ||||
| std::wstring to_wstring(const std::string& s); | ||||
							
								
								
									
										5
									
								
								ftxui/src/ftxui/core/component.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ftxui/src/ftxui/core/component.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| #include "ftxui/core/component.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
|   void Component::ComputeRequirement() {} | ||||
| } // namespace ftxui. | ||||
							
								
								
									
										23
									
								
								ftxui/src/ftxui/core/dom/flex.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								ftxui/src/ftxui/core/dom/flex.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #include "ftxui/core/dom/node.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| class Flex : public Node { | ||||
|  public: | ||||
|   Flex() {} | ||||
|   ~Flex() override {} | ||||
|   void ComputeRequirement() { | ||||
|     requirement_.min.x = 0; | ||||
|     requirement_.min.y = 0; | ||||
|     requirement_.flex.x = 1; | ||||
|     requirement_.flex.y = 1; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Node> flex() { | ||||
|   return std::make_unique<Flex>(); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										68
									
								
								ftxui/src/ftxui/core/dom/hbox.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								ftxui/src/ftxui/core/dom/hbox.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| #include "ftxui/core/dom/node.hpp" | ||||
| #include "ftxui/core/dom/elements.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| class HBox : public Node { | ||||
|  public: | ||||
|   HBox(Children children) : Node(std::move(children)) {} | ||||
|   ~HBox() {} | ||||
|  | ||||
|   void ComputeRequirement() override { | ||||
|     requirement_.min.x = 0; | ||||
|     requirement_.min.y = 0; | ||||
|     requirement_.flex.x = 1; | ||||
|     requirement_.flex.y = 0; | ||||
|     for (auto& child : children) { | ||||
|       child->ComputeRequirement(); | ||||
|       requirement_.min.x += child->requirement().min.x; | ||||
|       requirement_.min.y = | ||||
|           std::max(requirement_.min.y, child->requirement().min.y); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void SetBox(Box box) override { | ||||
|     Node::SetBox(box); | ||||
|  | ||||
|     int flex_sum = 0; | ||||
|     for (auto& child : children) | ||||
|       flex_sum += child->requirement().flex.x; | ||||
|  | ||||
|     int space = box.right - box.left + 1; | ||||
|     int extra_space = space - requirement_.min.x; | ||||
|  | ||||
|     int remaining_flex = flex_sum; | ||||
|     int remaining_extra_space = extra_space; | ||||
|  | ||||
|     int x = box.left; | ||||
|     for (auto& child : children) { | ||||
|       if (x > box.right) | ||||
|         break; | ||||
|  | ||||
|       Box child_box = box; | ||||
|       child_box.left = x; | ||||
|  | ||||
|       child_box.right = x + child->requirement().min.x - 1; | ||||
|  | ||||
|       if (child->requirement().flex.x && remaining_extra_space > 0) { | ||||
|         int added_space = remaining_extra_space * child->requirement().flex.x / | ||||
|                           remaining_flex; | ||||
|         remaining_extra_space -= added_space; | ||||
|         remaining_flex -= child->requirement().flex.x; | ||||
|         child_box.right += added_space; | ||||
|       } | ||||
|       child_box.right = std::min(child_box.right, box.right); | ||||
|  | ||||
|       child->SetBox(child_box); | ||||
|       x = child_box.right + 1; | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Node> hbox(Children children) { | ||||
|   return std::make_unique<HBox>(std::move(children)); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										123
									
								
								ftxui/src/ftxui/core/dom/hbox_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								ftxui/src/ftxui/core/dom/hbox_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| #include "ftxui/core/dom/elements.hpp" | ||||
| #include "ftxui/core/screen.hpp" | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| TEST(HBoxTest, ScreenSmaller1) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(11,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text_", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenSmaller2) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(10,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenFit) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(12,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenBigger1) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(13,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text_2 ", screen.ToString()); | ||||
| } | ||||
| TEST(HBoxTest, ScreenBigger2) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(14,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text_2  ", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenSmaller1Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(11,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text_", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenSmaller2Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(10,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenFitFlex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(12,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1text_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenBigger1Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(13,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1 text_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(HBoxTest, ScreenBigger2Flex) { | ||||
|   auto root = hbox( | ||||
|     text(L"text_1"), | ||||
|     flex(), | ||||
|     text(L"text_2") | ||||
|   ); | ||||
|   Screen screen(14,1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1  text_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										38
									
								
								ftxui/src/ftxui/core/dom/node.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								ftxui/src/ftxui/core/dom/node.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| #include "ftxui/core/dom/node.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| Node::Node() {} | ||||
| Node::Node(std::vector<std::unique_ptr<Node>> children) | ||||
|     : children(std::move(children)) {} | ||||
| Node::~Node() {} | ||||
|  | ||||
| void Node::ComputeRequirement() {} | ||||
| void Node::SetBox(Box box) { | ||||
|   box_ = box; | ||||
| } | ||||
| void Node::Render(Screen& screen) { | ||||
|   for(auto& child : children) | ||||
|     child->Render(screen); | ||||
| } | ||||
|  | ||||
| void Render(Screen& screen, Node* node) { | ||||
|   // Step 1: Find what dimension this elements wants to be. | ||||
|   node->ComputeRequirement(); | ||||
|    | ||||
|   Box box; | ||||
|   box.left = 0; | ||||
|   box.top = 0; | ||||
|   box.right = screen.dimx() - 1; | ||||
|   box.bottom = screen.dimy() - 1; | ||||
|    | ||||
|   // Step 2: Assign a dimension to the element. | ||||
|   node->SetBox(box); | ||||
|  | ||||
|   // Step 3: Draw the element. | ||||
|   node->Render(screen); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										37
									
								
								ftxui/src/ftxui/core/dom/text.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ftxui/src/ftxui/core/dom/text.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #include "ftxui/core/dom/node.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| class Text : public Node { | ||||
|  public: | ||||
|   Text(std::wstring text) : Node(), text_(text) {} | ||||
|   ~Text() {} | ||||
|  | ||||
|   void ComputeRequirement() override { | ||||
|     requirement_.min.x = text_.size(); | ||||
|     requirement_.min.y = 1; | ||||
|   } | ||||
|  | ||||
|   void Render(Screen& screen) override { | ||||
|     int x = box_.left; | ||||
|     int y = box_.top; | ||||
|     if (y > box_.bottom) | ||||
|       return; | ||||
|     for (wchar_t c : text_) { | ||||
|       if (x > box_.right) | ||||
|         return; | ||||
|       screen.at(x++, y) = c; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   std::wstring text_; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Node> text(std::wstring text) { | ||||
|   return std::make_unique<Text>(text); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										49
									
								
								ftxui/src/ftxui/core/dom/text_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								ftxui/src/ftxui/core/dom/text_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #include "ftxui/core/dom/elements.hpp" | ||||
| #include "ftxui/core/screen.hpp" | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| TEST(TextTest, ScreenHeightSmaller) { | ||||
|   auto element = text(L"test"); | ||||
|   Screen screen(2, 0); | ||||
|   Render(screen, element.get()); | ||||
|  | ||||
|   EXPECT_EQ("", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(TextTest, ScreenSmaller) { | ||||
|   auto element = text(L"test"); | ||||
|   Screen screen(2, 1); | ||||
|   Render(screen, element.get()); | ||||
|  | ||||
|   EXPECT_EQ("te", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(TextTest, ScreenFit) { | ||||
|   auto element = text(L"test"); | ||||
|   Screen screen(4, 1); | ||||
|   Render(screen, element.get()); | ||||
|  | ||||
|   EXPECT_EQ("test", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(TextTest, ScreenBigger) { | ||||
|   auto element = text(L"test"); | ||||
|   Screen screen(6, 1); | ||||
|   Render(screen, element.get()); | ||||
|  | ||||
|   EXPECT_EQ("test  ", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(TextTest, ScreenBigger2) { | ||||
|   auto element = text(L"test"); | ||||
|   Screen screen(6, 2); | ||||
|   Render(screen, element.get()); | ||||
|  | ||||
|   EXPECT_EQ("test  \n      ", screen.ToString()); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										68
									
								
								ftxui/src/ftxui/core/dom/vbox.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								ftxui/src/ftxui/core/dom/vbox.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| #include "ftxui/core/dom/node.hpp" | ||||
| #include "ftxui/core/dom/elements.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| class VBox : public Node { | ||||
|  public: | ||||
|   VBox(Children children) : Node(std::move(children)) {} | ||||
|   ~VBox() {} | ||||
|  | ||||
|   void ComputeRequirement() { | ||||
|     requirement_.min.x = 0; | ||||
|     requirement_.min.y = 0; | ||||
|     requirement_.flex.x = 0; | ||||
|     requirement_.flex.y = 1; | ||||
|     for (auto& child : children) { | ||||
|       child->ComputeRequirement(); | ||||
|       requirement_.min.y += child->requirement().min.y; | ||||
|       requirement_.min.x = | ||||
|           std::max(requirement_.min.x, child->requirement().min.x); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void SetBox(Box box) { | ||||
|     Node::SetBox(box); | ||||
|  | ||||
|     int flex_sum = 0; | ||||
|     for (auto& child : children) | ||||
|       flex_sum += child->requirement().flex.y; | ||||
|  | ||||
|     int space = box.bottom - box.top + 1; | ||||
|     int extra_space = space - requirement_.min.y; | ||||
|  | ||||
|     int remaining_flex = flex_sum; | ||||
|     int remaining_extra_space = extra_space; | ||||
|  | ||||
|     int y = box.left; | ||||
|     for (auto& child : children) { | ||||
|       if (y > box.right) | ||||
|         break; | ||||
|  | ||||
|       Box child_box = box; | ||||
|       child_box.top = y; | ||||
|  | ||||
|       child_box.bottom = y + child->requirement().min.y - 1; | ||||
|  | ||||
|       if (child->requirement().flex.y && remaining_extra_space > 0) { | ||||
|         int added_space = remaining_extra_space * child->requirement().flex.y / | ||||
|                           remaining_flex; | ||||
|         remaining_extra_space -= added_space; | ||||
|         remaining_flex -= child->requirement().flex.y; | ||||
|         child_box.bottom += added_space; | ||||
|       } | ||||
|       child_box.bottom = std::min(child_box.bottom, box.bottom); | ||||
|  | ||||
|       child->SetBox(child_box); | ||||
|       y = child_box.bottom + 1; | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Node> vbox(Children children) { | ||||
|   return std::make_unique<VBox>(std::move(children)); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										71
									
								
								ftxui/src/ftxui/core/dom/vbox_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								ftxui/src/ftxui/core/dom/vbox_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #include "ftxui/core/dom/elements.hpp" | ||||
| #include "ftxui/core/screen.hpp" | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| namespace ftxui { | ||||
| namespace dom { | ||||
|  | ||||
| TEST(VBoxTest, ScreenSmaller1) { | ||||
|   auto root = vbox(text(L"text_1"), text(L"text_2")); | ||||
|   Screen screen(6, 1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenFit) { | ||||
|   auto root = vbox(text(L"text_1"), text(L"text_2")); | ||||
|   Screen screen(6, 2); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1\ntext_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenBigger1) { | ||||
|   auto root = vbox(text(L"text_1"), text(L"text_2")); | ||||
|   Screen screen(6, 3); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1\ntext_2\n      ", screen.ToString()); | ||||
| } | ||||
| TEST(VBoxTest, ScreenBigger2) { | ||||
|   auto root = vbox(text(L"text_1"), text(L"text_2")); | ||||
|   Screen screen(6, 4); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1\ntext_2\n      \n      ", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenSmaller1Flex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); | ||||
|   Screen screen(6, 1); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenFitFlex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); | ||||
|   Screen screen(6, 2); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1\ntext_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| TEST(VBoxTest, ScreenBigger1Flex) { | ||||
|   auto root = vbox(text(L"text_1"), flex(), 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")); | ||||
|   Screen screen(6, 4); | ||||
|   Render(screen, root.get()); | ||||
|  | ||||
|   EXPECT_EQ("text_1\n      \n      \ntext_2", screen.ToString()); | ||||
| } | ||||
|  | ||||
| };  // namespace dom | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										31
									
								
								ftxui/src/ftxui/core/screen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								ftxui/src/ftxui/core/screen.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| #include "ftxui/core/screen.hpp" | ||||
| #include "ftxui/core/terminal.hpp" | ||||
| #include "ftxui/util/string.hpp" | ||||
|  | ||||
| #include <sstream> | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| Screen::Screen(size_t dimx, size_t dimy) | ||||
|     : dimx_(dimx), dimy_(dimy), lines_(dimy, std::wstring(dimx, U' ')) {} | ||||
|  | ||||
| std::string Screen::ToString() { | ||||
|   std::stringstream ss; | ||||
|   for (size_t y = 0; y < dimy_; ++y) { | ||||
|     ss << to_string(lines_[y]); | ||||
|     if (y + 1 < dimy_) | ||||
|       ss << '\n'; | ||||
|   } | ||||
|   return ss.str(); | ||||
| } | ||||
|  | ||||
| wchar_t& Screen::at(size_t x, size_t y) { | ||||
|   return lines_[y][x]; | ||||
| } | ||||
|  | ||||
| Screen Screen::WholeTerminal() { | ||||
|   Terminal::Dimensions size = Terminal::Size(); | ||||
|   return Screen(size.dimx, size.dimy); | ||||
| } | ||||
|  | ||||
| };  // namespace ftxui | ||||
							
								
								
									
										15
									
								
								ftxui/src/ftxui/core/terminal.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								ftxui/src/ftxui/core/terminal.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| #include <sys/ioctl.h> | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "ftxui/core/terminal.hpp" | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| Terminal::Dimensions Terminal::Size() { | ||||
|   winsize w; | ||||
|   ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); | ||||
|   return Dimensions{w.ws_col, w.ws_row}; | ||||
| } | ||||
|  | ||||
| } // namespace ftxui | ||||
							
								
								
									
										18
									
								
								ftxui/src/ftxui/core/terminal.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								ftxui/src/ftxui/core/terminal.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #ifndef FTXUI_CORE_TERMINAL_HPP | ||||
| #define FTXUI_CORE_TERMINAL_HPP | ||||
|  | ||||
| namespace ftxui { | ||||
|  | ||||
| class Terminal { | ||||
|  public: | ||||
|   struct Dimensions { | ||||
|     int dimx; | ||||
|     int dimy; | ||||
|   }; | ||||
|  | ||||
|   static Dimensions Size(); | ||||
| }; | ||||
|  | ||||
| }  // namespace ftxui | ||||
|  | ||||
| #endif /* end of include guard: FTXUI_CORE_TERMINAL_HPP */ | ||||
							
								
								
									
										14
									
								
								ftxui/src/ftxui/util/string.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ftxui/src/ftxui/util/string.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include "ftxui/util/string.hpp" | ||||
|  | ||||
| #include <codecvt> | ||||
| #include <locale> | ||||
|  | ||||
| std::string to_string(const std::wstring& s) { | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; | ||||
|   return converter.to_bytes(s); | ||||
| } | ||||
|  | ||||
| std::wstring to_wstring(const std::string& s) { | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; | ||||
|   return converter.from_bytes(s); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Arthur Sonzogni
					Arthur Sonzogni