mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-23 03:10:01 +08:00
Initial prototype
This commit is contained in:
commit
49aeaa49c5
8
.clang-format
Normal file
8
.clang-format
Normal file
@ -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<vector<int> >' in existing files gets formatted to
|
||||||
|
# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
|
||||||
|
# 'int>>' if the file already contains at least one such instance.)
|
||||||
|
Standard: Cpp11
|
5
CMakeLists.txt
Normal file
5
CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_subdirectory(ftxui)
|
||||||
|
add_subdirectory(examples)
|
1
examples/CMakeLists.txt
Normal file
1
examples/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(text)
|
4
examples/text/CMakeLists.txt
Normal file
4
examples/text/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
add_executable(main
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(main PRIVATE ftxui)
|
38
examples/text/main.cpp
Normal file
38
examples/text/main.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "ftxui/core/screen.hpp"
|
||||||
|
#include "ftxui/core/dom/elements.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user