diff --git a/examples/component/canvas_animated.cpp b/examples/component/canvas_animated.cpp index b89280ee..27e9c1e7 100644 --- a/examples/component/canvas_animated.cpp +++ b/examples/component/canvas_animated.cpp @@ -22,7 +22,7 @@ int main(int argc, const char* argv[]) { c.DrawPointLine(mouse_x, mouse_y, 80, 10, Color::Red); c.DrawPointLine(80, 10, 80, 40, Color::Blue); c.DrawPointLine(80, 40, mouse_x, mouse_y, Color::Green); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // A triangle following the mouse, using block characters. @@ -32,7 +32,7 @@ int main(int argc, const char* argv[]) { c.DrawBlockLine(mouse_x, mouse_y, 80, 10, Color::Red); c.DrawBlockLine(80, 10, 80, 40, Color::Blue); c.DrawBlockLine(80, 40, mouse_x, mouse_y, Color::Green); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // A circle following the mouse, using braille characters. @@ -40,7 +40,7 @@ int main(int argc, const char* argv[]) { auto c = Canvas(100, 100); c.DrawText(0,0, "A circle (braille)"); c.DrawPointCircle(mouse_x, mouse_y, 30); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // A circle following the mouse, using block characters. @@ -48,7 +48,7 @@ int main(int argc, const char* argv[]) { auto c = Canvas(100, 100); c.DrawText(0,0, "A circle (block)"); c.DrawBlockCircle(mouse_x, mouse_y, 30); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // A filled circle following the mouse, using braille characters. @@ -56,7 +56,7 @@ int main(int argc, const char* argv[]) { auto c = Canvas(100, 100); c.DrawText(0,0, "A circle filled (braille)"); c.DrawPointCircleFilled(mouse_x, mouse_y, 30); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // A filled circle following the mouse, using block characters. @@ -64,7 +64,7 @@ int main(int argc, const char* argv[]) { auto c = Canvas(100, 100); c.DrawText(0,0, "A circle filled (block)"); c.DrawBlockCircleFilled(mouse_x, mouse_y, 30); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // An ellipse following the mouse, using braille characters. @@ -72,7 +72,7 @@ int main(int argc, const char* argv[]) { auto c = Canvas(100, 100); c.DrawText(0,0, "An ellipse (braille)"); c.DrawPointEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // An ellipse following the mouse, using block characters. @@ -80,7 +80,7 @@ int main(int argc, const char* argv[]) { auto c = Canvas(100, 100); c.DrawText(0,0, "An ellipse (block)"); c.DrawBlockEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // An ellipse following the mouse filled, using braille characters. @@ -89,7 +89,7 @@ int main(int argc, const char* argv[]) { c.DrawText(0,0, "A filled ellipse (braille)"); c.DrawPointEllipseFilled(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // An ellipse following the mouse filled, using block characters. @@ -99,7 +99,7 @@ int main(int argc, const char* argv[]) { c.DrawBlockEllipseFilled(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); c.DrawBlockEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); // A text following the mouse @@ -112,7 +112,7 @@ int main(int argc, const char* argv[]) { p.underlined = true; p.bold = true; }); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); auto renderer_plot_1 = Renderer([&] { @@ -128,7 +128,7 @@ int main(int argc, const char* argv[]) { for (int x = 1; x < 99; x++) c.DrawPointLine(x, ys[x], x + 1, ys[x + 1]); - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); auto renderer_plot_2 = Renderer([&] { @@ -145,7 +145,7 @@ int main(int argc, const char* argv[]) { c.DrawPointLine(x, 50+ys[x], x, 50-ys[x], Color::Red); } - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); auto renderer_plot_3 = Renderer([&] { @@ -180,7 +180,7 @@ int main(int argc, const char* argv[]) { } } - return ElementFrom(std::move(c)); + return canvas(std::move(c)); }); diff --git a/examples/dom/canvas.cpp b/examples/dom/canvas.cpp index 9f2fca24..c5095421 100644 --- a/examples/dom/canvas.cpp +++ b/examples/dom/canvas.cpp @@ -12,30 +12,30 @@ int main(int argc, const char* argv[]) { using namespace ftxui; - auto canvas = Canvas(100, 100); + auto c = Canvas(100, 100); - canvas.DrawText(0, 0, "This is a canvas", [](Pixel& p) -> void { + c.DrawText(0, 0, "This is a canvas", [](Pixel& p) { p.foreground_color = Color::Red; p.underlined = true; }); // Triangle: - canvas.DrawPointLine(10, 10, 80, 10, Color::Red); - canvas.DrawPointLine(80, 10, 80, 40, Color::Blue); - canvas.DrawPointLine(80, 40, 10, 10, Color::Green); + c.DrawPointLine(10, 10, 80, 10, Color::Red); + c.DrawPointLine(80, 10, 80, 40, Color::Blue); + c.DrawPointLine(80, 40, 10, 10, Color::Green); // Circle, not filled and filled: - canvas.DrawPointCircle(30, 50, 20); - canvas.DrawPointCircleFilled(40, 40, 10); + c.DrawPointCircle(30, 50, 20); + c.DrawPointCircleFilled(40, 40, 10); // Plot a function: std::vector ys(100); for (int x = 0; x < 100; x++) ys[x] = 80 + 20 * cos(x * 0.2); for (int x = 0; x < 99; x++) - canvas.DrawPointLine(x, ys[x], x + 1, ys[x + 1], Color::Red); + c.DrawPointLine(x, ys[x], x + 1, ys[x + 1], Color::Red); - auto document = ElementFrom(&canvas) | border; + auto document = canvas(&c) | border; auto screen = Screen::Create(Dimension::Fit(document)); Render(screen, document); diff --git a/examples/dom/flexbox.cpp b/examples/dom/flexbox.cpp deleted file mode 100644 index deac3927..00000000 --- a/examples/dom/flexbox.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include // for size_t -#include // for getchar -#include // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN -#include // for Full, Screen -#include // for allocator, shared_ptr -#include // for operator+, to_string, char_traits, string - -#include "ftxui/dom/flexbox_config.hpp" // for ftxui -#include "ftxui/dom/node.hpp" // for Render - -int main(int argc, const char* argv[]) { - using namespace ftxui; - - auto image = Canvas(100, 100); - - auto document = vbox({ - make_box("header"), - hbox({ - make_box("left side"), - make_box("center") | flex, - make_box("right side"), - }) | flex, - make_box("footer") - }); - - //auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); - //auto screen = Screen::Create(Dimension::Fit(document)); - auto screen = Screen::Create(Dimension::Full()); - Render(screen, document); - screen.Print(); - getchar(); - - return 0; -} - -// Copyright 2020 Arthur Sonzogni. All rights reserved. -// Use of this source code is governed by the MIT license that can be found in -// the LICENSE file. diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 5f378229..dea66a6c 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -59,7 +59,9 @@ Element paragraphAlignCenter(std::string text); Element paragraphAlignJustify(std::string text); Element graph(GraphFunction); Element emptyElement(); -Element ElementFrom(ConstRef); +Element canvas(ConstRef); +Element canvas(int width, int height, std::function); +Element canvas(std::function); // -- Decorator --- Element bold(Element); diff --git a/src/ftxui/dom/canvas.cpp b/src/ftxui/dom/canvas.cpp index e25b3cb5..4114c717 100644 --- a/src/ftxui/dom/canvas.cpp +++ b/src/ftxui/dom/canvas.cpp @@ -85,7 +85,7 @@ void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) { } void Canvas::DrawPointOn(int x, int y) { - if (!IsIn(x,y)) + if (!IsIn(x, y)) return; Cell& cell = storage_[XY{x / 2, y / 4}]; if (cell.type != CellType::kBraille) { @@ -98,7 +98,7 @@ void Canvas::DrawPointOn(int x, int y) { } void Canvas::DrawPointOff(int x, int y) { - if (!IsIn(x,y)) + if (!IsIn(x, y)) return; Cell& cell = storage_[XY{x / 2, y / 4}]; if (cell.type != CellType::kBraille) { @@ -111,7 +111,7 @@ void Canvas::DrawPointOff(int x, int y) { } void Canvas::DrawPointToggle(int x, int y) { - if (!IsIn(x,y)) + if (!IsIn(x, y)) return; Cell& cell = storage_[XY{x / 2, y / 4}]; if (cell.type != CellType::kBraille) { @@ -203,19 +203,19 @@ void Canvas::DrawPointEllipse(int x1, int y1, int r1, int r2) { } void Canvas::DrawPointEllipse(int x1, - int y1, - int r1, - int r2, - const Color& color) { + int y1, + int r1, + int r2, + const Color& color) { DrawPointEllipse(x1, y1, r1, r2, - [color](Pixel& p) { p.foreground_color = color; }); + [color](Pixel& p) { p.foreground_color = color; }); } void Canvas::DrawPointEllipse(int x1, - int y1, - int r1, - int r2, - const Stylizer& s) { + int y1, + int r1, + int r2, + const Stylizer& s) { int x = -r1; int y = 0; int e2 = r2; @@ -250,19 +250,19 @@ void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) { } void Canvas::DrawPointEllipseFilled(int x1, - int y1, - int r1, - int r2, - const Color& color) { + int y1, + int r1, + int r2, + const Color& color) { DrawPointEllipseFilled(x1, y1, r1, r2, - [color](Pixel& p) { p.foreground_color = color; }); + [color](Pixel& p) { p.foreground_color = color; }); } void Canvas::DrawPointEllipseFilled(int x1, - int y1, - int r1, - int r2, - const Stylizer& s) { + int y1, + int r1, + int r2, + const Stylizer& s) { int x = -r1; int y = 0; int e2 = r2; @@ -310,7 +310,7 @@ void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) { } void Canvas::DrawBlockOn(int x, int y) { - if (!IsIn(x,y)) + if (!IsIn(x, y)) return; y /= 2; Cell& cell = storage_[XY{x / 2, y / 2}]; @@ -326,7 +326,7 @@ void Canvas::DrawBlockOn(int x, int y) { } void Canvas::DrawBlockOff(int x, int y) { - if (!IsIn(x,y)) + if (!IsIn(x, y)) return; Cell& cell = storage_[XY{x / 2, y / 4}]; if (cell.type != CellType::kBlock) { @@ -342,7 +342,7 @@ void Canvas::DrawBlockOff(int x, int y) { } void Canvas::DrawBlockToggle(int x, int y) { - if (!IsIn(x,y)) + if (!IsIn(x, y)) return; Cell& cell = storage_[XY{x / 2, y / 4}]; if (cell.type != CellType::kBlock) { @@ -440,19 +440,19 @@ void Canvas::DrawBlockEllipse(int x1, int y1, int r1, int r2) { } void Canvas::DrawBlockEllipse(int x1, - int y1, - int r1, - int r2, - const Color& color) { + int y1, + int r1, + int r2, + const Color& color) { DrawBlockEllipse(x1, y1, r1, r2, - [color](Pixel& p) { p.foreground_color = color; }); + [color](Pixel& p) { p.foreground_color = color; }); } void Canvas::DrawBlockEllipse(int x1, - int y1, - int r1, - int r2, - const Stylizer& s) { + int y1, + int r1, + int r2, + const Stylizer& s) { y1 /= 2; r2 /= 2; int x = -r1; @@ -489,19 +489,19 @@ void Canvas::DrawBlockEllipseFilled(int x1, int y1, int r1, int r2) { } void Canvas::DrawBlockEllipseFilled(int x1, - int y1, - int r1, - int r2, - const Color& color) { + int y1, + int r1, + int r2, + const Color& color) { DrawBlockEllipseFilled(x1, y1, r1, r2, - [color](Pixel& p) { p.foreground_color = color; }); + [color](Pixel& p) { p.foreground_color = color; }); } void Canvas::DrawBlockEllipseFilled(int x1, - int y1, - int r1, - int r2, - const Stylizer& s) { + int y1, + int r1, + int r2, + const Stylizer& s) { y1 /= 2; r2 /= 2; int x = -r1; @@ -512,7 +512,7 @@ void Canvas::DrawBlockEllipseFilled(int x1, int err = dx + dy; do { - for(int xx = x1+x; xx <= x1-x; ++xx) { + for (int xx = x1 + x; xx <= x1 - x; ++xx) { DrawBlock(xx, 2 * (y1 + y), true, s); DrawBlock(xx, 2 * (y1 - y), true, s); } @@ -528,7 +528,7 @@ void Canvas::DrawBlockEllipseFilled(int x1, } while (x <= 0); while (y++ < r2) { - for(int yy = y1+y; yy <= y1-y; ++yy) { + for (int yy = y1 + y; yy <= y1 - y; ++yy) { DrawBlock(x1, 2 * yy, true, s); } } @@ -567,29 +567,71 @@ void Canvas::Style(int x, int y, const Stylizer& style) { style(storage_[XY{x / 2, y / 4}].content); } -Element ElementFrom(ConstRef canvas) { - class Impl : public Node { +namespace { + +class CanvasNodeBase : public Node { + public: + CanvasNodeBase() {} + + void Render(Screen& screen) override { + const Canvas& c = canvas(); + int y_max = std::min(c.width() * 2, box_.y_max - box_.y_min + 1); + int x_max = std::min(c.height() * 4, box_.x_max - box_.x_min + 1); + for (int y = 0; y < y_max; ++y) { + for (int x = 0; x < x_max; ++x) { + screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x * 2, y * 4); + } + } + } + + virtual const Canvas& canvas() = 0; +}; + +} // namespace + +Element canvas(ConstRef canvas) { + class Impl : public CanvasNodeBase { public: Impl(ConstRef canvas) : canvas_(canvas) { requirement_.min_x = (canvas_->width() + 1) / 2; requirement_.min_y = (canvas_->height() + 3) / 4; } - - void Render(Screen& screen) override { - int y_max = std::min(requirement_.min_y, box_.y_max - box_.y_min + 1); - int x_max = std::min(requirement_.min_x, box_.x_max - box_.x_min + 1); - for (int y = 0; y < y_max; ++y) { - for (int x = 0; x < x_max; ++x) { - screen.PixelAt(box_.x_min + x, box_.y_min + y) = - canvas_->GetPixel(x * 2, y * 4); - } - } - } - - private: + const Canvas& canvas() final { return *canvas_; } ConstRef canvas_; }; return std::make_shared(std::move(canvas)); } +Element canvas(int width, int height, std::function fn) { + class Impl : public CanvasNodeBase { + public: + Impl(int width, int height, std::function fn) + : width_(width), height_(height), fn_(std::move(fn)) {} + + void ComputeRequirement() final { + requirement_.min_x = (width_ + 1) / 2; + requirement_.min_y = (height_ + 3) / 4; + } + + void Render(Screen& screen) final { + int width = (box_.y_max - box_.y_min + 1) * 2; + int height = (box_.x_max - box_.x_min + 1) * 4; + canvas_ = Canvas(width, height); + fn_(canvas_); + CanvasNodeBase::Render(screen); + } + + const Canvas& canvas() final { return canvas_; } + Canvas canvas_; + int width_; + int height_; + std::function fn_; + }; + return std::make_shared(width, height, std::move(fn)); +} + +Element canvas(std::function fn) { + return canvas(12, 12, std::move(fn)); +} + } // namespace ftxui