commit 49aeaa49c5f23e0aa09d97bc4f6fa1f1a0f4dfea Author: Arthur Sonzogni Date: Tue Sep 18 08:48:40 2018 +0200 Initial prototype diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6fdf1dc --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +# Defines the Chromium style for automatic reformatting. +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium +# This defaults to 'Auto'. Explicitly set it for a while, so that +# 'vector >' in existing files gets formatted to +# 'vector>'. ('Auto' means that clang-format will only use +# 'int>>' if the file already contains at least one such instance.) +Standard: Cpp11 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e56a391 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.0) +enable_testing() + +add_subdirectory(ftxui) +add_subdirectory(examples) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..f0ca2d7 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(text) diff --git a/examples/text/CMakeLists.txt b/examples/text/CMakeLists.txt new file mode 100644 index 0000000..bd70946 --- /dev/null +++ b/examples/text/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(main + main.cpp +) +target_link_libraries(main PRIVATE ftxui) diff --git a/examples/text/main.cpp b/examples/text/main.cpp new file mode 100644 index 0000000..59b1e7f --- /dev/null +++ b/examples/text/main.cpp @@ -0,0 +1,38 @@ +#include "ftxui/core/screen.hpp" +#include "ftxui/core/dom/elements.hpp" +#include + +int main(int argc, const char *argv[]) +{ + using namespace ftxui::dom; + auto root = + vbox( + hbox( + text(L"north-west"), + flex(), + text(L"north-east") + ), + flex(), + hbox( + hbox( + flex(), + text(L"center"), + flex() + ) + ), + flex(), + hbox( + text(L"south-west"), + flex(), + text(L"south-east") + ) + ); + auto screen = ftxui::Screen::WholeTerminal(); + Render(screen, root.get()); + + std::cout << screen.ToString(); + + getchar(); + + return 0; +} diff --git a/ftxui/CMakeLists.txt b/ftxui/CMakeLists.txt new file mode 100644 index 0000000..18e128a --- /dev/null +++ b/ftxui/CMakeLists.txt @@ -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() diff --git a/ftxui/include/ftxui/README.md b/ftxui/include/ftxui/README.md new file mode 100644 index 0000000..5b0dc90 --- /dev/null +++ b/ftxui/include/ftxui/README.md @@ -0,0 +1 @@ +State => Components => Document => Text. diff --git a/ftxui/include/ftxui/core/box.hpp b/ftxui/include/ftxui/core/box.hpp new file mode 100644 index 0000000..021a594 --- /dev/null +++ b/ftxui/include/ftxui/core/box.hpp @@ -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 */ diff --git a/ftxui/include/ftxui/core/component.hpp b/ftxui/include/ftxui/core/component.hpp new file mode 100644 index 0000000..fb0c4f0 --- /dev/null +++ b/ftxui/include/ftxui/core/component.hpp @@ -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 */ diff --git a/ftxui/include/ftxui/core/document.hpp b/ftxui/include/ftxui/core/document.hpp new file mode 100644 index 0000000..51034f9 --- /dev/null +++ b/ftxui/include/ftxui/core/document.hpp @@ -0,0 +1,12 @@ +#ifndef FTXUI_DOCUMENT_HPP +#define FTXUI_DOCUMENT_HPP + +namespace ftxui { + +class Document { + +}; + +}; + +#endif /* end of include guard: FTXUI_DOCUMENT_HPP */ diff --git a/ftxui/include/ftxui/core/dom/elements.hpp b/ftxui/include/ftxui/core/dom/elements.hpp new file mode 100644 index 0000000..2c2e5dd --- /dev/null +++ b/ftxui/include/ftxui/core/dom/elements.hpp @@ -0,0 +1,38 @@ +#ifndef FTXUI_CORE_DOM_ELEMENTS_HPP +#define FTXUI_CORE_DOM_ELEMENTS_HPP + +#include "ftxui/core/dom/node.hpp" +#include + +namespace ftxui { +namespace dom { + +using Child = std::unique_ptr; +using Children = std::vector>; + +std::unique_ptr vbox(Children); +std::unique_ptr hbox(Children); +std::unique_ptr text(std::wstring text); +std::unique_ptr flex(); + +template +std::vector unpack(Args... args) { + std::vector vec; + (vec.push_back(std::forward(args)), ...); + return vec; +} + +template +std::unique_ptr vbox(Args... children) { + return vbox(unpack(std::forward(children)...)); +} + +template +std::unique_ptr hbox(Args... children) { + return hbox(unpack(std::forward(children)...)); +} + +}; // namespace dom +}; // namespace ftxui + +#endif /* end of include guard: FTXUI_CORE_DOM_ELEMENTS_HPP */ diff --git a/ftxui/include/ftxui/core/dom/node.hpp b/ftxui/include/ftxui/core/dom/node.hpp new file mode 100644 index 0000000..f843228 --- /dev/null +++ b/ftxui/include/ftxui/core/dom/node.hpp @@ -0,0 +1,44 @@ +#ifndef CORE_DOM_NODE_HPP +#define CORE_DOM_NODE_HPP + +#include +#include + +#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> 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> 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 */ diff --git a/ftxui/include/ftxui/core/requirement.hpp b/ftxui/include/ftxui/core/requirement.hpp new file mode 100644 index 0000000..74089b4 --- /dev/null +++ b/ftxui/include/ftxui/core/requirement.hpp @@ -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 */ diff --git a/ftxui/include/ftxui/core/screen.hpp b/ftxui/include/ftxui/core/screen.hpp new file mode 100644 index 0000000..c02e1d8 --- /dev/null +++ b/ftxui/include/ftxui/core/screen.hpp @@ -0,0 +1,28 @@ +#ifndef FTXUI_CORE_SCREEN +#define FTXUI_CORE_SCREEN + +#include +#include + +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 lines_; +}; + +}; // namespace ftxui + +#endif /* end of include guard: FTXUI_CORE_SCREEN */ diff --git a/ftxui/include/ftxui/util/string.hpp b/ftxui/include/ftxui/util/string.hpp new file mode 100644 index 0000000..d2383e7 --- /dev/null +++ b/ftxui/include/ftxui/util/string.hpp @@ -0,0 +1,4 @@ +#include + +std::string to_string(const std::wstring& s); +std::wstring to_wstring(const std::string& s); diff --git a/ftxui/src/ftxui/core/component.cpp b/ftxui/src/ftxui/core/component.cpp new file mode 100644 index 0000000..cd28bc8 --- /dev/null +++ b/ftxui/src/ftxui/core/component.cpp @@ -0,0 +1,5 @@ +#include "ftxui/core/component.hpp" + +namespace ftxui { + void Component::ComputeRequirement() {} +} // namespace ftxui. diff --git a/ftxui/src/ftxui/core/dom/flex.cpp b/ftxui/src/ftxui/core/dom/flex.cpp new file mode 100644 index 0000000..1643ccd --- /dev/null +++ b/ftxui/src/ftxui/core/dom/flex.cpp @@ -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 flex() { + return std::make_unique(); +} + +}; // namespace dom +}; // namespace ftxui diff --git a/ftxui/src/ftxui/core/dom/hbox.cpp b/ftxui/src/ftxui/core/dom/hbox.cpp new file mode 100644 index 0000000..dfd66d7 --- /dev/null +++ b/ftxui/src/ftxui/core/dom/hbox.cpp @@ -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 hbox(Children children) { + return std::make_unique(std::move(children)); +} + +}; // namespace dom +}; // namespace ftxui diff --git a/ftxui/src/ftxui/core/dom/hbox_test.cpp b/ftxui/src/ftxui/core/dom/hbox_test.cpp new file mode 100644 index 0000000..fb05538 --- /dev/null +++ b/ftxui/src/ftxui/core/dom/hbox_test.cpp @@ -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 diff --git a/ftxui/src/ftxui/core/dom/node.cpp b/ftxui/src/ftxui/core/dom/node.cpp new file mode 100644 index 0000000..db6b266 --- /dev/null +++ b/ftxui/src/ftxui/core/dom/node.cpp @@ -0,0 +1,38 @@ +#include "ftxui/core/dom/node.hpp" + +namespace ftxui { +namespace dom { + +Node::Node() {} +Node::Node(std::vector> 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 diff --git a/ftxui/src/ftxui/core/dom/text.cpp b/ftxui/src/ftxui/core/dom/text.cpp new file mode 100644 index 0000000..7b604fb --- /dev/null +++ b/ftxui/src/ftxui/core/dom/text.cpp @@ -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 text(std::wstring text) { + return std::make_unique(text); +} + +}; // namespace dom +}; // namespace ftxui diff --git a/ftxui/src/ftxui/core/dom/text_test.cpp b/ftxui/src/ftxui/core/dom/text_test.cpp new file mode 100644 index 0000000..72775ac --- /dev/null +++ b/ftxui/src/ftxui/core/dom/text_test.cpp @@ -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 diff --git a/ftxui/src/ftxui/core/dom/vbox.cpp b/ftxui/src/ftxui/core/dom/vbox.cpp new file mode 100644 index 0000000..a2bd4d7 --- /dev/null +++ b/ftxui/src/ftxui/core/dom/vbox.cpp @@ -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 vbox(Children children) { + return std::make_unique(std::move(children)); +} + +}; // namespace dom +}; // namespace ftxui diff --git a/ftxui/src/ftxui/core/dom/vbox_test.cpp b/ftxui/src/ftxui/core/dom/vbox_test.cpp new file mode 100644 index 0000000..f9005a6 --- /dev/null +++ b/ftxui/src/ftxui/core/dom/vbox_test.cpp @@ -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 diff --git a/ftxui/src/ftxui/core/screen.cpp b/ftxui/src/ftxui/core/screen.cpp new file mode 100644 index 0000000..0411e6b --- /dev/null +++ b/ftxui/src/ftxui/core/screen.cpp @@ -0,0 +1,31 @@ +#include "ftxui/core/screen.hpp" +#include "ftxui/core/terminal.hpp" +#include "ftxui/util/string.hpp" + +#include + +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 diff --git a/ftxui/src/ftxui/core/terminal.cpp b/ftxui/src/ftxui/core/terminal.cpp new file mode 100644 index 0000000..f8bf7c8 --- /dev/null +++ b/ftxui/src/ftxui/core/terminal.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + +#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 diff --git a/ftxui/src/ftxui/core/terminal.hpp b/ftxui/src/ftxui/core/terminal.hpp new file mode 100644 index 0000000..2310bac --- /dev/null +++ b/ftxui/src/ftxui/core/terminal.hpp @@ -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 */ diff --git a/ftxui/src/ftxui/util/string.cpp b/ftxui/src/ftxui/util/string.cpp new file mode 100644 index 0000000..8569495 --- /dev/null +++ b/ftxui/src/ftxui/util/string.cpp @@ -0,0 +1,14 @@ +#include "ftxui/util/string.hpp" + +#include +#include + +std::string to_string(const std::wstring& s) { + std::wstring_convert> converter; + return converter.to_bytes(s); +} + +std::wstring to_wstring(const std::string& s) { + std::wstring_convert> converter; + return converter.from_bytes(s); +}