commits
tags
#include "torlinkc/ui/colored_window.hpp"
#include <algorithm>
#include <array>
#include <memory>
#include <string>
#include <utility>
#include <ftxui/dom/node.hpp>
#include <ftxui/dom/requirement.hpp>
#include <ftxui/dom/take_any_args.hpp>
#include <ftxui/screen/box.hpp>
#include <ftxui/screen/screen.hpp>
// Adapted from FTXUI v5.0.0's internal (unexported) `Border` node
// (src/ftxui/dom/border.cpp, MIT-licensed) with the ROUNDED charset and a
// mandatory foreground color baked in, since the public `window()` in this
// FTXUI version takes neither.
namespace torlinkc::ui {
namespace {
using ftxui::Box;
using ftxui::Color;
using ftxui::Element;
using ftxui::Elements;
using ftxui::Node;
using ftxui::Pixel;
using ftxui::Screen;
constexpr std::array<const char*, 6> kRoundedCharset = {"╭", "╮", "╰", "╯", "─", "│"};
class ColoredWindowNode : public Node {
public:
ColoredWindowNode(Elements children, Color color) : Node(std::move(children)), color_(color) {}
void ComputeRequirement() override {
Node::ComputeRequirement();
requirement_ = children_[0]->requirement();
requirement_.min_x += 2;
requirement_.min_y += 2;
requirement_.min_x = std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
requirement_.selected_box.x_min++;
requirement_.selected_box.x_max++;
requirement_.selected_box.y_min++;
requirement_.selected_box.y_max++;
}
void SetBox(Box box) override {
Node::SetBox(box);
Box title_box;
title_box.x_min = box.x_min + 1;
title_box.x_max = box.x_max - 1;
title_box.y_min = box.y_min;
title_box.y_max = box.y_min;
children_[1]->SetBox(title_box);
box.x_min++;
box.x_max--;
box.y_min++;
box.y_max--;
children_[0]->SetBox(box);
}
void Render(Screen& screen) override {
children_[0]->Render(screen);
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) return;
screen.at(box_.x_min, box_.y_min) = kRoundedCharset[0];
screen.at(box_.x_max, box_.y_min) = kRoundedCharset[1];
screen.at(box_.x_min, box_.y_max) = kRoundedCharset[2];
screen.at(box_.x_max, box_.y_max) = kRoundedCharset[3];
for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
Pixel& p1 = screen.PixelAt(x, box_.y_min);
Pixel& p2 = screen.PixelAt(x, box_.y_max);
p1.character = kRoundedCharset[4];
p2.character = kRoundedCharset[4];
p1.automerge = true;
p2.automerge = true;
}
for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
Pixel& p3 = screen.PixelAt(box_.x_min, y);
Pixel& p4 = screen.PixelAt(box_.x_max, y);
p3.character = kRoundedCharset[5];
p4.character = kRoundedCharset[5];
p3.automerge = true;
p4.automerge = true;
}
children_[1]->Render(screen);
// Overwriting the perimeter's foreground_color after the title renders
// is what tints the title text too (it sits on the top edge), while
// children_[0] (the interior) was already drawn above and is untouched.
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, box_.y_min).foreground_color = color_;
screen.PixelAt(x, box_.y_max).foreground_color = color_;
}
for (int y = box_.y_min; y <= box_.y_max; ++y) {
screen.PixelAt(box_.x_min, y).foreground_color = color_;
screen.PixelAt(box_.x_max, y).foreground_color = color_;
}
}
private:
Color color_;
};
} // namespace
Element coloredWindow(Element title, Element content, Color color) {
return std::make_shared<ColoredWindowNode>(ftxui::unpack(std::move(content), std::move(title)), color);
}
} // namespace torlinkc::ui