From e577d67f2a7cfe97fcdbf8c30f1137faa61ca475 Mon Sep 17 00:00:00 2001
From: Arthur Sonzogni <sonzogniarthur@gmail.com>
Date: Wed, 19 Sep 2018 21:52:25 +0200
Subject: [PATCH] Start the gauge widget.

---
 examples/CMakeLists.txt                   |  4 ++-
 examples/gauge/CMakeLists.txt             |  4 +++
 examples/gauge/main.cpp                   | 30 ++++++++++++++++++
 examples/separator/CMakeLists.txt         |  4 +++
 examples/separator/main.cpp               | 26 ++++++++++++++++
 examples/text/CMakeLists.txt              |  4 ---
 examples/vbox_hbox/CMakeLists.txt         |  4 +++
 examples/{text => vbox_hbox}/main.cpp     |  4 +--
 ftxui/CMakeLists.txt                      |  3 ++
 ftxui/include/ftxui/core/dom/elements.hpp | 20 +++++++++---
 ftxui/include/ftxui/core/screen.hpp       |  5 +++
 ftxui/src/ftxui/core/dom/centered.cpp     | 20 ++++++++++++
 ftxui/src/ftxui/core/dom/flex.cpp         | 16 ++++++++++
 ftxui/src/ftxui/core/dom/gauge.cpp        | 32 +++++++++++++++++++
 ftxui/src/ftxui/core/dom/separator.cpp    | 38 +++++++++++++++++++++++
 ftxui/src/ftxui/core/dom/vbox.cpp         |  2 +-
 ftxui/src/ftxui/core/screen.cpp           |  7 +++++
 17 files changed, 211 insertions(+), 12 deletions(-)
 create mode 100644 examples/gauge/CMakeLists.txt
 create mode 100644 examples/gauge/main.cpp
 create mode 100644 examples/separator/CMakeLists.txt
 create mode 100644 examples/separator/main.cpp
 delete mode 100644 examples/text/CMakeLists.txt
 create mode 100644 examples/vbox_hbox/CMakeLists.txt
 rename examples/{text => vbox_hbox}/main.cpp (92%)
 create mode 100644 ftxui/src/ftxui/core/dom/centered.cpp
 create mode 100644 ftxui/src/ftxui/core/dom/gauge.cpp
 create mode 100644 ftxui/src/ftxui/core/dom/separator.cpp

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index f0ca2d76..1e2e8486 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1 +1,3 @@
-add_subdirectory(text)
+add_subdirectory(gauge)
+add_subdirectory(separator)
+add_subdirectory(vbox_hbox)
diff --git a/examples/gauge/CMakeLists.txt b/examples/gauge/CMakeLists.txt
new file mode 100644
index 00000000..ea0a9644
--- /dev/null
+++ b/examples/gauge/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(gauge_example
+  main.cpp
+)
+target_link_libraries(gauge_example PRIVATE ftxui)
diff --git a/examples/gauge/main.cpp b/examples/gauge/main.cpp
new file mode 100644
index 00000000..d1bdcd7e
--- /dev/null
+++ b/examples/gauge/main.cpp
@@ -0,0 +1,30 @@
+#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 document =
+    hbox(
+      flex(vbox(
+        gauge(0.1),
+        gauge(0.2),
+        gauge(0.3)
+      )),
+      flex(vbox(
+        gauge(0.1),
+        gauge(0.8),
+        gauge(0.3)
+      ))
+    );
+  //auto screen = ftxui::Screen::WholeTerminal();
+  auto screen = ftxui::Screen::TerminalOutput(document);
+  Render(screen, document.get());
+
+  std::cout << screen.ToString();
+
+  getchar();
+
+  return 0;
+}
diff --git a/examples/separator/CMakeLists.txt b/examples/separator/CMakeLists.txt
new file mode 100644
index 00000000..f2922117
--- /dev/null
+++ b/examples/separator/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(separator_example
+  main.cpp
+)
+target_link_libraries(separator_example PRIVATE ftxui)
diff --git a/examples/separator/main.cpp b/examples/separator/main.cpp
new file mode 100644
index 00000000..0c23f326
--- /dev/null
+++ b/examples/separator/main.cpp
@@ -0,0 +1,26 @@
+#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 document =
+    hbox(
+      text(L"left-column"),
+      separator(),
+      flex(vbox(
+        flex(center(text(L"right-column"))),
+        separator(),
+        center(text(L"bottom-column"))
+      ))
+    );
+  auto screen = ftxui::Screen::WholeTerminal();
+  Render(screen, document.get());
+
+  std::cout << screen.ToString();
+
+  getchar();
+
+  return 0;
+}
diff --git a/examples/text/CMakeLists.txt b/examples/text/CMakeLists.txt
deleted file mode 100644
index bd709466..00000000
--- a/examples/text/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-add_executable(main
-  main.cpp
-)
-target_link_libraries(main PRIVATE ftxui)
diff --git a/examples/vbox_hbox/CMakeLists.txt b/examples/vbox_hbox/CMakeLists.txt
new file mode 100644
index 00000000..4db6ff6e
--- /dev/null
+++ b/examples/vbox_hbox/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(vbox_hbox_example
+  main.cpp
+)
+target_link_libraries(vbox_hbox_example PRIVATE ftxui)
diff --git a/examples/text/main.cpp b/examples/vbox_hbox/main.cpp
similarity index 92%
rename from examples/text/main.cpp
rename to examples/vbox_hbox/main.cpp
index 59b1e7fb..bae80901 100644
--- a/examples/text/main.cpp
+++ b/examples/vbox_hbox/main.cpp
@@ -5,7 +5,7 @@
 int main(int argc, const char *argv[])
 {
   using namespace ftxui::dom;
-  auto root =
+  auto document =
     vbox(
       hbox(
         text(L"north-west"),
@@ -28,7 +28,7 @@ int main(int argc, const char *argv[])
       )
     );
   auto screen = ftxui::Screen::WholeTerminal();
-  Render(screen, root.get());
+  Render(screen, document.get());
 
   std::cout << screen.ToString();
 
diff --git a/ftxui/CMakeLists.txt b/ftxui/CMakeLists.txt
index 18e128ad..2ff59703 100644
--- a/ftxui/CMakeLists.txt
+++ b/ftxui/CMakeLists.txt
@@ -6,8 +6,11 @@ add_library(ftxui
   src/ftxui/core/dom/flex.cpp
   src/ftxui/core/dom/hbox.cpp
   src/ftxui/core/dom/node.cpp
+  src/ftxui/core/dom/separator.cpp
   src/ftxui/core/dom/text.cpp
+  src/ftxui/core/dom/centered.cpp
   src/ftxui/core/dom/vbox.cpp
+  src/ftxui/core/dom/gauge.cpp
   src/ftxui/core/screen.cpp
   src/ftxui/core/terminal.cpp
   src/ftxui/util/string.cpp
diff --git a/ftxui/include/ftxui/core/dom/elements.hpp b/ftxui/include/ftxui/core/dom/elements.hpp
index 2c2e5dd5..a1b1116f 100644
--- a/ftxui/include/ftxui/core/dom/elements.hpp
+++ b/ftxui/include/ftxui/core/dom/elements.hpp
@@ -7,17 +7,29 @@
 namespace ftxui {
 namespace dom {
 
-using Child = std::unique_ptr<Node>;
+using Element = std::unique_ptr<Node>;
 using Children = std::vector<std::unique_ptr<Node>>;
 
+
+// --- Layout ----
 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();
+std::unique_ptr<Node> flex(Element);
+
+// --- Widget --
+std::unique_ptr<Node> text(std::wstring text);
+std::unique_ptr<Node> separator();
+std::unique_ptr<Node> gauge(float ratio);
+
+// --- Decorator ---
+std::unique_ptr<Node> hcenter(Element);
+std::unique_ptr<Node> vcenter(Element);
+std::unique_ptr<Node> center(Element);
 
 template <class... Args>
-std::vector<Child> unpack(Args... args) {
-  std::vector<Child> vec;
+std::vector<Element> unpack(Args... args) {
+  std::vector<Element> vec;
   (vec.push_back(std::forward<Args>(args)), ...);
   return vec;
 }
diff --git a/ftxui/include/ftxui/core/screen.hpp b/ftxui/include/ftxui/core/screen.hpp
index c02e1d8e..9d676010 100644
--- a/ftxui/include/ftxui/core/screen.hpp
+++ b/ftxui/include/ftxui/core/screen.hpp
@@ -3,8 +3,12 @@
 
 #include <string>
 #include <vector>
+#include <memory>
 
 namespace ftxui {
+namespace dom {
+  class Node;
+}
 
 class Screen {
  public:
@@ -16,6 +20,7 @@ class Screen {
   size_t dimy() { return dimy_;}
 
   static Screen WholeTerminal();
+  static Screen TerminalOutput(std::unique_ptr<dom::Node>& element);
 
  private:
   size_t dimx_;
diff --git a/ftxui/src/ftxui/core/dom/centered.cpp b/ftxui/src/ftxui/core/dom/centered.cpp
new file mode 100644
index 00000000..254c5738
--- /dev/null
+++ b/ftxui/src/ftxui/core/dom/centered.cpp
@@ -0,0 +1,20 @@
+#include "ftxui/core/dom/node.hpp"
+#include "ftxui/core/dom/elements.hpp"
+
+namespace ftxui {
+namespace dom {
+
+std::unique_ptr<Node> hcenter(Element child) {
+  return hbox(flex(), std::move(child), flex());
+}
+
+std::unique_ptr<Node> vcenter(Element child) {
+  return vbox(flex(), std::move(child), flex());
+}
+
+std::unique_ptr<Node> center(Element child) {
+  return hcenter(vcenter(std::move(child)));
+}
+
+} // namespace dom
+} // namespace ftxui
diff --git a/ftxui/src/ftxui/core/dom/flex.cpp b/ftxui/src/ftxui/core/dom/flex.cpp
index 1643ccd4..99afa610 100644
--- a/ftxui/src/ftxui/core/dom/flex.cpp
+++ b/ftxui/src/ftxui/core/dom/flex.cpp
@@ -1,4 +1,5 @@
 #include "ftxui/core/dom/node.hpp"
+#include "ftxui/core/dom/elements.hpp"
 
 namespace ftxui {
 namespace dom {
@@ -6,18 +7,33 @@ namespace dom {
 class Flex : public Node {
  public:
   Flex() {}
+  Flex(Element child) : Node(unpack(std::move(child))) {}
   ~Flex() override {}
   void ComputeRequirement() {
     requirement_.min.x = 0;
     requirement_.min.y = 0;
+    if (!children.empty()) {
+      children[0]->ComputeRequirement();
+      requirement_ = children[0]->requirement();
+    }
     requirement_.flex.x = 1;
     requirement_.flex.y = 1;
   }
+
+  void SetBox(Box box) override {
+    if (children.empty())
+      return;
+    children[0]->SetBox(box);
+  }
 };
 
 std::unique_ptr<Node> flex() {
   return std::make_unique<Flex>();
 }
 
+std::unique_ptr<Node> flex(Element child) {
+  return std::make_unique<Flex>(std::move(child));
+}
+
 };  // namespace dom
 };  // namespace ftxui
diff --git a/ftxui/src/ftxui/core/dom/gauge.cpp b/ftxui/src/ftxui/core/dom/gauge.cpp
new file mode 100644
index 00000000..0c3f9f63
--- /dev/null
+++ b/ftxui/src/ftxui/core/dom/gauge.cpp
@@ -0,0 +1,32 @@
+#include "ftxui/core/dom/node.hpp"
+#include "ftxui/core/dom/elements.hpp"
+
+namespace ftxui {
+namespace dom {
+
+class Gauge : public Node {
+ public:
+  Gauge(float progress) : progress_(progress) {}
+  ~Gauge() {}
+
+  void ComputeRequirement() override {
+    requirement_.flex.x = 1;
+    requirement_.min.y = 1;
+  }
+
+  void Render(Screen& screen) override {
+    float y = box_.top;
+    int limit = box_.left + progress_ * (box_.right - box_.left);
+    for(int i = box_.left; i<=limit; ++i)
+      screen.at(i, y) = 'X';
+  }
+ private:
+  float progress_;
+};
+
+std::unique_ptr<Node> gauge(float progress) {
+  return std::make_unique<Gauge>(progress);
+}
+
+};  // namespace dom
+};  // namespace ftxui
diff --git a/ftxui/src/ftxui/core/dom/separator.cpp b/ftxui/src/ftxui/core/dom/separator.cpp
new file mode 100644
index 00000000..cf9a1582
--- /dev/null
+++ b/ftxui/src/ftxui/core/dom/separator.cpp
@@ -0,0 +1,38 @@
+#include "ftxui/core/dom/node.hpp"
+
+namespace ftxui {
+namespace dom {
+
+class Separator : public Node {
+ public:
+  Separator() {}
+  ~Separator() override {}
+  void ComputeRequirement() override {
+    requirement_.min.x = 1;
+    requirement_.min.y = 1;
+  }
+
+  void Render(Screen& screen) override {
+    bool is_column = (box_.right == box_.left);
+    bool is_line = (box_.top == box_.bottom);
+
+    wchar_t c = U'+';
+    if (is_line && !is_column)
+      c = U'─';
+    else if (!is_line && is_column)
+      c = U'│';
+
+    for (int y = box_.top; y <= box_.bottom; ++y) {
+      for (int x = box_.left; x <= box_.right; ++x) {
+        screen.at(x, y) = c;
+      }
+    }
+  }
+};
+
+std::unique_ptr<Node> separator() {
+  return std::make_unique<Separator>();
+}
+
+};  // namespace dom
+};  // namespace ftxui
diff --git a/ftxui/src/ftxui/core/dom/vbox.cpp b/ftxui/src/ftxui/core/dom/vbox.cpp
index a2bd4d72..386945c6 100644
--- a/ftxui/src/ftxui/core/dom/vbox.cpp
+++ b/ftxui/src/ftxui/core/dom/vbox.cpp
@@ -35,7 +35,7 @@ class VBox : public Node {
     int remaining_flex = flex_sum;
     int remaining_extra_space = extra_space;
 
-    int y = box.left;
+    int y = box.top;
     for (auto& child : children) {
       if (y > box.right)
         break;
diff --git a/ftxui/src/ftxui/core/screen.cpp b/ftxui/src/ftxui/core/screen.cpp
index 0411e6b6..768e17e8 100644
--- a/ftxui/src/ftxui/core/screen.cpp
+++ b/ftxui/src/ftxui/core/screen.cpp
@@ -1,6 +1,7 @@
 #include "ftxui/core/screen.hpp"
 #include "ftxui/core/terminal.hpp"
 #include "ftxui/util/string.hpp"
+#include "ftxui/core/dom/node.hpp"
 
 #include <sstream>
 
@@ -28,4 +29,10 @@ Screen Screen::WholeTerminal() {
   return Screen(size.dimx, size.dimy);
 }
 
+Screen Screen::TerminalOutput(std::unique_ptr<dom::Node>& element) {
+  element->ComputeRequirement();
+  Terminal::Dimensions size = Terminal::Size();
+  return Screen(size.dimx, element->requirement().min.y);
+}
+
 };  // namespace ftxui
-- 
GitLab