displayLayout and Rendering TUI library |
git clone git://git.dimitrijedobrota.com/display.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 0cb1feca33b58d8358f70672dde123e633ea5451 |
parent | da954532dc2d74ba345a97b86b277ca63307d251 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Wed, 21 May 2025 18:14:32 +0200 |
Improve type system
M | .clang-tidy | | | + |
M | example/example.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++ ----------------------------------- |
M | example/navig/CMakeLists.txt | | | ++ - |
M | example/navig/navig.cpp | | | ++++++++++++++++++++++++++++++++++++ -------------------------- |
M | include/display/display.hpp | | | ++++ ---- |
M | include/display/element.hpp | | | +++++++ ------- |
M | include/display/layout.hpp | | | ++++++ ------ |
M | include/display/layout_rigid.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++ ------------------------------------ |
M | include/display/types.hpp | | | +++++++++++++++++++++++++++++++++++++++++++ --------------------------------------- |
M | include/display/window.hpp | | | ++++++++ -------- |
M | source/display.cpp | | | ++++++ ---- |
M | source/element.cpp | | | +++++++ ------ |
M | source/window.cpp | | | ++++++++++++++++++ ---------------- |
M | source/window_pivot.cpp | | | ++++++++++++++++++ ---------------- |
M | test/source/utility_test.cpp | | | +++ -- |
15 files changed, 347 insertions(+), 290 deletions(-)
diff --git a/ .clang-tidy b/ .clang-tidy
@@ -15,6 +15,7 @@
Checks: "*,\
-misc-include-cleaner,\
-misc-non-private-member-variables-in-classes,\
-misc-no-recursion,\
-modernize-use-designated-initializers,\
-modernize-use-trailing-return-type,\
-readability-suspicious-call-argument,\
-*-ranges,\
diff --git a/ example/example.cpp b/ example/example.cpp
@@ -9,25 +9,34 @@
namespace
{
using namespace display; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
using namespace display; // NOLINT(*namespace*)
using namespace based::literals; // NOLINT(*namespace*)
class WindowCustom : public WindowPivot
{
public:
explicit WindowCustom(plc_t aplc, piv_t piv, dim_t dim)
: WindowPivot(aplc, pad_t(0, 0), piv, dim)
: WindowPivot(aplc, pad_t(0_w, 0_h), piv, dim)
{
}
void render() const override
{
static int color_red = 0;
color_red = (color_red + 25) % 256;
std::cout << alec::background(color_red, 65, 65);
static constexpr based::u8 inc = 0_u8;
static constexpr based::u8 mod = 256_u8;
static constexpr based::u8 color_green = 0_u8;
static constexpr based::u8 color_blue = 0_u8;
static based::u8 color_red = 0_u8;
color_red = (color_red + inc) % mod;
std::cout << alec::background(
color_red.value, color_green.value, color_blue.value
);
line_reset();
for (auto i = hgt_t(0); i < hgt(); i++) {
for (auto i = 0_h; i < hgt(); ++i) {
line_empty();
}
@@ -40,17 +49,23 @@
class LayoutCustom : public LayoutRigid<Layout<WindowCustom>>
{
public:
explicit LayoutCustom(plc_t aplc)
// NOLINTNEXTLINE(*magic*)
: LayoutRigid(aplc, {{0, 1, 2}, {7, 8, 3}, {6, 5, 4}})
{
append().set_child(piv_t(PvtX::Left, PvtY::Top), dim_t(12, 4));
append().set_child(piv_t(PvtX::Center, PvtY::Top), dim_t(12, 4));
append().set_child(piv_t(PvtX::Right, PvtY::Top), dim_t(12, 4));
append().set_child(piv_t(PvtX::Right, PvtY::Center), dim_t(12, 4));
append().set_child(piv_t(PvtX::Right, PvtY::Bottom), dim_t(12, 4));
append().set_child(piv_t(PvtX::Center, PvtY::Bottom), dim_t(12, 4));
append().set_child(piv_t(PvtX::Left, PvtY::Bottom), dim_t(12, 4));
append().set_child(piv_t(PvtX::Left, PvtY::Center), dim_t(12, 4));
append().set_child(piv_t(PvtX::Center, PvtY::Center), dim_t(12, 4));
using x = piv_t::x;
using y = piv_t::y;
const dim_t dim(12_w, 4_h);
append().set_child(piv_t(x::left, y::top), dim);
append().set_child(piv_t(x::center, y::top), dim);
append().set_child(piv_t(x::right, y::top), dim);
append().set_child(piv_t(x::right, y::center), dim);
append().set_child(piv_t(x::right, y::bottom), dim);
append().set_child(piv_t(x::center, y::bottom), dim);
append().set_child(piv_t(x::left, y::bottom), dim);
append().set_child(piv_t(x::left, y::center), dim);
append().set_child(piv_t(x::center, y::center), dim);
}
};
@@ -64,23 +79,24 @@
public:
const auto valid = [&](std::size_t xpos, std::size_t ypos)
{
return xpos >= 0 && xpos < n.value() && ypos >= 0 && ypos < m.value();
return xpos < n.value && ypos < m.value;
};
const auto get = [&](std::size_t xpos, std::size_t ypos) -> std::uint8_t
const auto get = [&](std::size_t xpos, std::size_t ypos) -> based::bu8
{
return valid(xpos, ypos) ? layout[xpos][ypos] : 0xFF;
static constexpr auto not_found = 0xFF;
return valid(xpos, ypos) ? layout[xpos][ypos] : not_found;
};
for (std::size_t i = 0; i <= n.value(); i++) {
for (std::size_t j = 0; j <= m.value(); j++) {
const std::uint8_t ptl = get(i - 1, j - 1);
const std::uint8_t ptr = get(i - 1, j);
const std::uint8_t pbl = get(i, j - 1);
const std::uint8_t pbr = get(i, j);
for (std::size_t i = 0; i <= n.value; i++) {
for (std::size_t j = 0; j <= m.value; j++) {
const based::bu8 ptl = get(i - 1, j - 1);
const based::bu8 ptr = get(i - 1, j);
const based::bu8 pbl = get(i, j - 1);
const based::bu8 pbr = get(i, j);
uint8_t mask = 0;
mask |= ((ptl != ptr) ? 1U : 0U) << 0U; // Top
based::bu8 mask = 0;
mask |= ((ptl != ptr) ? 1U : 0U); // Top
mask |= ((ptr != pbr) ? 1U : 0U) << 1U; // Right
mask |= ((pbr != pbl) ? 1U : 0U) << 2U; // Bottom
mask |= ((pbl != ptl) ? 1U : 0U) << 3U; // Left
@@ -114,25 +130,25 @@
public:
const auto [pos, dim] = LayoutRigid<LayoutCustom>::place(i);
// Top of each element
set_cursor(pos.x + 1, pos.y);
for (auto j = wth_t(1); j < dim.width; j++) {
set_cursor(pos.x + 1_x, pos.y);
for (auto j = 1_w; j < dim.width; ++j) {
std::cout << "─";
}
// Left of each element
for (auto j = pos.y + 1; j < pos.y + dim.height; j++) {
for (auto j = pos.y + 1_y; j < pos.y + dim.height; ++j) {
set_cursor(pos.x, j) << "│";
}
}
// Right of the layout
for (auto j = aypos() + 1; j < aypos() + ahgt(); j++) {
set_cursor(axpos() + awth() - 1, j) << "│";
for (auto j = aypos() + 1_y; j < aypos() + ahgt(); ++j) {
set_cursor(axpos() + awth() - 1_w, j) << "│";
}
// Bottom of the layout
set_cursor(axpos() + 1, aypos() + ahgt() - 1);
for (auto i = wth_t(2); i < awth(); i++) {
set_cursor(axpos() + 1_x, aypos() + ahgt() - 1_h);
for (auto i = 2_w; i < awth(); ++i) {
std::cout << "─";
}
@@ -141,8 +157,8 @@
public:
const auto unw = w / m;
const auto unh = h / n;
for (const auto [mask, wth, hgt] : m_corners) {
const auto xloc = wth != m ? xpos_t((wth * unw).value()) : axpos() + w;
const auto yloc = hgt != n ? ypos_t((hgt * unh).value()) : aypos() + h;
const auto xloc = wth != m ? xpos_t((wth * unw).value) : axpos() + w;
const auto yloc = hgt != n ? ypos_t((hgt * unh).value) : aypos() + h;
set_cursor(xloc, yloc) << corner_t::lookup[mask]; // NOLINT
};
@@ -153,17 +169,17 @@
private:
plc_t place(std::size_t idx)
{
const auto [pos, dim] = LayoutRigid<LayoutCustom>::place(idx);
dim_t sub = {1, 1};
dim_t sub = {1_w, 1_h};
if (get_record(idx).addw) {
sub.width += 1;
sub.width += 1_w;
}
if (get_record(idx).addh) {
sub.height += 1;
sub.height += 1_h;
}
return {pos + pos_t(1, 1), dim - sub};
return {pos + pos_t(1_x, 1_y), dim - sub};
}
struct corner_t
@@ -175,14 +191,14 @@
private:
};
// clang-format on
corner_t(std::uint8_t mask, wth_t wigth, hgt_t height) // NOLINT
: mask(mask)
, wigth(wigth)
, height(height)
corner_t(based::bu8 msk, wth_t wth, hgt_t hgt) // NOLINT
: mask(msk)
, wigth(wth)
, height(hgt)
{
}
std::uint8_t mask;
based::bu8 mask;
wth_t wigth;
hgt_t height;
};
diff --git a/ example/navig/CMakeLists.txt b/ example/navig/CMakeLists.txt
@@ -4,7 +4,8 @@
configure_file(menu.conf menu.conf COPYONLY)
add_custom_command(
OUTPUT menu.hpp menu.cpp
COMMAND ${stamen_DIR}/../../bin/stamen -n example menu.conf
# COMMAND ${stamen_DIR}/../../bin/stamen -n example menu.conf
COMMAND stamen -n example menu.conf
DEPENDS menu.conf
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating menu files"
diff --git a/ example/navig/navig.cpp b/ example/navig/navig.cpp
@@ -10,6 +10,8 @@
namespace
{
using namespace display::literals; // NOLINT(*namespace*)
bool is_finished = false; // NOLINT
using display::WindowPivot;
@@ -17,51 +19,52 @@
using display::WindowPivot;
class WindowCustom : public WindowPivot
{
public:
WindowCustom(display::plc_t aplc,
display::piv_t piv,
const example::menu_t& menu)
: WindowPivot(aplc, {4, 2, 5, 4}, piv, calc_dim(menu))
WindowCustom(
display::plc_t aplc, display::piv_t piv, const example::menu_t& menu
)
// NOLINTNEXTLINE(*magic*)
: WindowPivot(aplc, {4_w, 2_h, 5_w, 4_h}, piv, calc_dim(menu))
, m_menu(menu)
{
}
void render() const override
{
std::cout << alec::background_v<alec::Color::BLUE>;
std::cout << alec::background_v<alec::color::blue>;
line_reset();
line_center(m_menu.title);
line_empty();
for (std::size_t i = 0; i < m_menu.items.size(); i++) {
if (m_selected == i) {
std::cout << alec::foreground_v<alec::Color::GREEN>;
std::cout << alec::foreground_v<alec::color::green>;
}
line_right(m_menu.items[i].prompt);
if (m_selected == i) {
std::cout << alec::foreground_v<alec::Color::DEFAULT>;
std::cout << alec::foreground_v<alec::color::def>;
}
}
WindowPivot::render();
WindowPivot::render_border();
std::cout << alec::background_v<alec::Color::DEFAULT>;
std::cout << alec::background_v<alec::color::def>;
std::cout << std::flush;
}
void input(display::event& evnt) override
{
if (evnt.type() != display::event::Type::KEY) {
if (evnt.type() != display::event::type::key) {
return;
}
if (evnt.key() == 'j') {
if (m_selected + 1 < m_menu.items.size()) {
if (m_selected + 1U < m_menu.items.size()) {
m_selected++;
}
evnt.type() = display::event::Type::NONE;
evnt.type() = display::event::type::none;
render();
return;
}
@@ -70,20 +73,20 @@
public:
if (m_selected > 0) {
m_selected--;
}
evnt.type() = display::event::Type::NONE;
evnt.type() = display::event::type::none;
render();
return;
}
if (evnt.key() == 'l') {
m_menu.items[m_selected].callback(m_selected);
evnt.type() = display::event::Type::NONE;
evnt.type() = display::event::type::none;
return;
}
if (evnt.key() == 'h') {
m_menu.callback(0);
evnt.type() = display::event::Type::NONE;
evnt.type() = display::event::type::none;
return;
}
}
@@ -91,12 +94,17 @@
public:
private:
static display::dim_t calc_dim(const example::menu_t& menu)
{
std::size_t width = menu.title.size();
using wth_t = display::wth_t;
using hgt_t = display::hgt_t;
wth_t width {menu.title.size()};
for (const auto& item : menu.items) {
width = std::max(width, item.prompt.size());
const wth_t lwidth {item.prompt.size()};
width = std::max(width, lwidth);
}
return {display::wth_t(width), display::hgt_t(menu.items.size() + 2)};
const hgt_t height {menu.items.size() + 2};
return {width, height};
}
example::menu_t m_menu;
@@ -112,7 +120,7 @@
int operation1(std::size_t /* unused */) // NOLINT
{
std::cout << alec::cursor_position(1, 1) << "operation 1";
std::cout << alec::cursor_position(2, 1)
<< alec::erase_line_v<alec::Motion::WHOLE>
<< alec::erase_line_v<alec::motion::whole>
<< "Some operation is done";
std::cout << std::flush;
return 1;
@@ -122,7 +130,7 @@
int operation2(std::size_t /* unused */) // NOLINT
{
std::cout << alec::cursor_position(1, 1) << "operation 2";
std::cout << alec::cursor_position(2, 1)
<< alec::erase_line_v<alec::Motion::WHOLE>
<< alec::erase_line_v<alec::motion::whole>
<< "Some other operation is done";
std::cout << std::flush;
return 1;
@@ -132,7 +140,7 @@
int operation3(std::size_t /* unused */) // NOLINT
{
std::cout << alec::cursor_position(1, 1) << "operation 3";
std::cout << alec::cursor_position(2, 1)
<< alec::erase_line_v<alec::Motion::WHOLE>
<< alec::erase_line_v<alec::motion::whole>
<< "Yet another operation is done";
std::cout << std::flush;
return 1;
@@ -141,7 +149,7 @@
int operation3(std::size_t /* unused */) // NOLINT
int finish(std::size_t /* unused */) // NOLINT
{
std::cout << alec::cursor_position(1, 1)
<< alec::erase_line_v<alec::Motion::WHOLE>;
<< alec::erase_line_v<alec::motion::whole>;
std::cout << "finishing...";
std::cout << std::flush;
is_finished = true;
@@ -151,7 +159,7 @@
int finish(std::size_t /* unused */) // NOLINT
int menu_t::visit(const menu_t& menu)
{
using display::Display, display::Layout;
using display::PvtX, display::PvtY, display::piv_t;
using display::piv_t;
auto& layout = Display::display().layout();
@@ -167,7 +175,9 @@
int menu_t::visit(const menu_t& menu)
stk.push(&menu);
}
layout.set_child<WindowCustom>(piv_t(PvtX::Right, PvtY::Bottom), *stk.top());
layout.set_child<WindowCustom>(
piv_t(piv_t::x::right, piv_t::y::bottom), *stk.top()
);
layout.render();
return 0;
@@ -185,13 +195,13 @@
int main()
while (!is_finished) {
auto evnt = display.get_event();
if (evnt.type() == event::Type::RESIZE) {
std::cout << alec::erase_display_v<alec::Motion::WHOLE>;
if (evnt.type() == event::type::resize) {
std::cout << alec::erase_display_v<alec::motion::whole>;
display.render();
continue;
}
if (evnt.type() == event::Type::KEY) {
if (evnt.type() == event::type::key) {
if (evnt.key() == 'q') {
break;
}
diff --git a/ include/display/display.hpp b/ include/display/display.hpp
@@ -17,12 +17,12 @@
public:
Display& operator=(const Display&) = delete;
Display& operator=(Display&&) = delete;
const auto& layout() const { return m_layout; }
auto& layout() { return m_layout; }
[[nodiscard]] const auto& layout() const { return m_layout; }
[[nodiscard]] auto& layout() { return m_layout; }
event get_event();
[[nodiscard]] event get_event();
bool get_resized() const;
[[nodiscard]] bool get_resized() const;
void set_resized();
void reset_resized();
diff --git a/ include/display/element.hpp b/ include/display/element.hpp
@@ -30,13 +30,13 @@
public:
void render_border() const;
plc_t aplc() const { return m_aplc; }
pos_t apos() const { return aplc().pos; }
dim_t adim() const { return aplc().dim; }
xpos_t axpos() const { return apos().x; }
ypos_t aypos() const { return apos().y; }
wth_t awth() const { return adim().width; }
hgt_t ahgt() const { return adim().height; }
[[nodiscard]] plc_t aplc() const { return m_aplc; }
[[nodiscard]] pos_t apos() const { return aplc().pos; }
[[nodiscard]] dim_t adim() const { return aplc().dim; }
[[nodiscard]] xpos_t axpos() const { return apos().x; }
[[nodiscard]] ypos_t aypos() const { return apos().y; }
[[nodiscard]] wth_t awth() const { return adim().width; }
[[nodiscard]] hgt_t ahgt() const { return adim().height; }
private:
plc_t m_aplc;
diff --git a/ include/display/layout.hpp b/ include/display/layout.hpp
@@ -62,19 +62,19 @@
public:
template<typename M = T>
requires(std::is_base_of_v<T, M>)
const M& get_child() const
[[nodiscard]] const M& get_child() const
{
return *dynamic_cast<M*>(m_child.get());
}
template<typename M = T>
requires(std::is_base_of_v<T, M>)
M& get_child()
[[nodiscard]] M& get_child()
{
return *dynamic_cast<M*>(m_child.get());
}
bool has_child() const { return m_child != nullptr; }
[[nodiscard]] bool has_child() const { return m_child != nullptr; }
private:
ptr_t m_child;
@@ -134,19 +134,19 @@
public:
template<typename M = T>
requires(std::is_base_of_v<T, M>)
const M& get(std::size_t idx) const
[[nodiscard]] const M& get(std::size_t idx) const
{
return *dynamic_cast<M*>(m_children[idx].get());
}
template<typename M = T>
requires(std::is_base_of_v<T, M>)
M& get(std::size_t idx)
[[nodiscard]] M& get(std::size_t idx)
{
return *dynamic_cast<M*>(m_children[idx].get());
}
std::size_t size() const { return m_children.size(); }
[[nodiscard]] std::size_t size() const { return m_children.size(); }
protected:
template<typename M = T, class... Args>
diff --git a/ include/display/layout_rigid.hpp b/ include/display/layout_rigid.hpp
@@ -4,17 +4,21 @@
#include <unordered_set>
#include <vector>
#include <based/types/types.hpp>
#include "display/layout.hpp"
#include "display/types.hpp"
namespace display
{
using namespace literals; // NOLINT(*namespace*)
template<typename T = Element>
class LayoutRigid : public LayoutMulti<T>
{
public:
using layout_t = std::vector<std::vector<std::uint8_t>>;
using layout_t = std::vector<std::vector<based::bu8>>;
LayoutRigid(plc_t aplc, layout_t layout);
@@ -37,7 +41,7 @@
public:
}
protected:
plc_t place(std::size_t idx) const
[[nodiscard]] plc_t place(std::size_t idx) const
{
const auto [m, n] = m_grid;
const auto [w, h] = this->adim();
@@ -48,11 +52,13 @@
protected:
{
const auto [sw, sh] = share;
const auto wth = addw ? w - (unw * (m - sw.value())) : unw * sw.value();
const auto hgt = addh ? h - (unh * (n - sh.value())) : unh * sh.value();
const auto wth =
addw ? w - (unw * (m - wth_t(sw.value))) : unw * wth_t(sw.value);
const auto hgt =
addh ? h - (unh * (n - hgt_t(sh.value))) : unh * hgt_t(sh.value);
if constexpr (std::is_same_v<R, pos_t>) {
return {xpos_t(wth.value()), ypos_t(hgt.value())};
return {xpos_t(wth.value), ypos_t(hgt.value)};
} else {
return {wth, hgt};
}
@@ -63,13 +69,16 @@
protected:
return {this->apos() + start, dim};
}
const auto& get_record(std::size_t idx) const { return m_recs[idx]; }
auto get_grid() const { return m_grid; }
[[nodiscard]] auto get_grid() const { return m_grid; }
[[nodiscard]] const auto& get_record(std::size_t idx) const
{
return m_recs[idx];
}
struct record_t
{
pos_t start = {0, 0};
dim_t dim = {0, 0};
pos_t start = {0_x, 0_y};
dim_t dim = {0_w, 0_h};
bool addw = false;
bool addh = false;
};
@@ -96,18 +105,19 @@
LayoutRigid<T>::LayoutRigid(plc_t aplc, layout_t layout)
template<typename T>
std::size_t LayoutRigid<T>::count_and_pad(layout_t& layout) const
{
std::unordered_set<std::uint8_t> ust;
std::unordered_set<based::bu8> ust;
static constexpr based::bu8 padd = 0xFF;
for (std::size_t i = 0U; i < m_grid.height.value(); i++) {
for (std::size_t j = 0U; j < m_grid.width.value(); j++) {
for (std::size_t i = 0U; i < m_grid.height.value; i++) {
for (std::size_t j = 0U; j < m_grid.width.value; j++) {
ust.insert(layout[i][j]);
}
layout[i].emplace_back(0xFF);
layout[i].emplace_back(padd);
}
layout.emplace_back(m_grid.width.value(), 0xFF);
layout.emplace_back(m_grid.width.value, padd);
for (std::size_t i = 0U; i < m_grid.height.value(); i++) {
for (std::size_t j = 0U; j < m_grid.width.value(); j++) {
for (std::size_t i = 0U; i < m_grid.height.value; i++) {
for (std::size_t j = 0U; j < m_grid.width.value; j++) {
if (layout[i][j] >= ust.size()) {
throw std::runtime_error("Invalid layout [Number]");
}
@@ -122,23 +132,23 @@
void LayoutRigid<T>::handle_cols(const layout_t& layout)
{
const auto [m, n] = get_grid();
for (std::size_t i = 0U; i < n.value(); i++) {
m_recs[layout[i][m.value() - 1]].addw = true;
for (auto i = 0_h; i < n; ++i) {
m_recs[layout[i.value][m.value - 1]].addw = true;
auto cnt = wth_t(1);
for (std::size_t j = 0; j < m.value(); j++) {
const auto crnt = layout[i][j];
auto cnt = 1_w;
for (auto j = 0_w; j < m; ++j) {
const auto crnt = layout[i.value][j.value];
if (crnt == layout[i][j + 1]) {
cnt++;
if (crnt == layout[i.value][j.value + 1]) {
++cnt;
continue;
}
auto& count = m_recs[crnt].dim.width;
auto& pos = m_recs[crnt].start.x;
const auto total = xpos_t(j) - cnt + 1;
const auto total = xpos_t() + j - cnt + 1_w;
if (count.value() != 0) {
if (count.value != 0) {
if (pos != total || count != cnt) {
throw std::runtime_error("Invalid layout [Shape Col]");
}
@@ -147,7 +157,7 @@
void LayoutRigid<T>::handle_cols(const layout_t& layout)
count = cnt;
}
cnt = wth_t(1);
cnt = 1_w;
}
}
}
@@ -157,23 +167,23 @@
void LayoutRigid<T>::handle_rows(const layout_t& layout)
{
const auto [m, n] = get_grid();
for (std::size_t j = 0U; j < m.value(); j++) {
m_recs[layout[n.value() - 1][j]].addh = true;
for (auto j = 0_w; j < m; ++j) {
m_recs[layout[n.value - 1][j.value]].addh = true;
auto cnt = hgt_t(1);
for (std::size_t i = 0; i < n.value(); i++) {
const auto crnt = layout[i][j];
auto cnt = 1_h;
for (auto i = 0_h; i < n; ++i) {
const auto crnt = layout[i.value][j.value];
if (crnt == layout[i + 1][j]) {
cnt++;
if (crnt == layout[i.value + 1][j.value]) {
++cnt;
continue;
}
auto& count = m_recs[crnt].dim.height;
auto& pos = m_recs[crnt].start.y;
const auto total = ypos_t(i) - cnt + 1;
const auto total = ypos_t() + i - cnt + 1_h;
if (count.value() != 0) {
if (count.value != 0) {
if (pos != total || count != cnt) {
throw std::runtime_error("Invalid layout [Shape Row]");
}
@@ -182,7 +192,7 @@
void LayoutRigid<T>::handle_rows(const layout_t& layout)
count = cnt;
}
cnt = hgt_t(1);
cnt = 1_h;
}
}
}
diff --git a/ include/display/types.hpp b/ include/display/types.hpp
@@ -6,114 +6,132 @@
#include <alec/alec.hpp>
#include <alec/terminal.hpp>
#include <based/enum/enum.hpp>
#include <based/types/strong.hpp>
#include <based/types/types.hpp>
namespace display
{
using event = alec::event;
template<char C>
struct val_t
/* ----- Basic Types ----- */
struct xpos_t : based::strong_type<based::bu16, xpos_t>
{
using sz_t = std::uint16_t;
using strong_type::strong_type;
};
explicit val_t(std::size_t xpos)
: v(static_cast<sz_t>(xpos))
{
}
struct ypos_t : based::strong_type<based::bu16, ypos_t>
{
using strong_type::strong_type;
};
explicit val_t(int xpos)
: v(static_cast<sz_t>(xpos))
struct wth_t : based::strong_type<based::bu16, wth_t>
{
using strong_type::strong_type;
explicit wth_t(std::size_t wth)
: strong_type(static_cast<wth_t::basic_type>(wth))
{
}
};
explicit val_t(sz_t xpos) // NOLINT
: v(xpos)
struct hgt_t : based::strong_type<based::bu16, hgt_t>
{
using strong_type::strong_type;
explicit hgt_t(std::size_t hgt)
: strong_type(static_cast<hgt_t::basic_type>(hgt))
{
}
};
auto value() const { return v; }
auto& value() { return v; }
// clang-format off
val_t operator+(val_t rhs) const { return val_t(static_cast<sz_t>(v + rhs.v)); }
val_t operator-(val_t rhs) const { return val_t(static_cast<sz_t>(v - rhs.v)); }
val_t operator*(val_t rhs) const { return val_t(static_cast<sz_t>(v * rhs.v)); }
val_t operator/(val_t rhs) const { return val_t(static_cast<sz_t>(v / rhs.v)); }
val_t operator+(int rhs) const { return val_t(static_cast<sz_t>(v + rhs)); }
val_t operator-(int rhs) const { return val_t(static_cast<sz_t>(v - rhs)); }
val_t operator*(int rhs) const { return val_t(static_cast<sz_t>(v * rhs)); }
val_t operator/(int rhs) const { return val_t(static_cast<sz_t>(v / rhs)); }
auto& operator+=(val_t rhs) { v += rhs.v; return *this; }
auto& operator-=(val_t rhs) { v -= rhs.v; return *this; }
auto& operator*=(val_t rhs) { v *= rhs.v; return *this; }
auto& operator/=(val_t rhs) { v /= rhs.v; return *this; }
auto& operator+=(int rhs) { v += rhs; return *this; }
auto& operator-=(int rhs) { v -= rhs; return *this; }
auto& operator*=(int rhs) { v *= rhs; return *this; }
auto& operator/=(int rhs) { v /= rhs; return *this; }
auto& operator++() { return *this += 1; }
auto& operator--() { return *this -= 1; }
val_t operator++(int) { return val_t(v++); }
val_t operator--(int) { return val_t(v--); }
auto operator<=>(const val_t&) const = default;
auto compare(xpos_t, xpos_t) -> bool;
auto order(xpos_t, xpos_t) -> bool;
auto preinc(xpos_t) -> xpos_t;
auto add(xpos_t, xpos_t) -> xpos_t;
auto add(xpos_t, wth_t) -> xpos_t;
auto sub(xpos_t, xpos_t) -> wth_t;
auto sub(xpos_t, wth_t) -> xpos_t;
auto div(xpos_t, xpos_t) -> xpos_t;
auto compare(ypos_t, ypos_t) -> bool;
auto order(ypos_t, ypos_t) -> bool;
auto preinc(ypos_t) -> ypos_t;
auto add(ypos_t, ypos_t) -> ypos_t;
auto add(ypos_t, hgt_t) -> ypos_t;
auto sub(ypos_t, ypos_t) -> hgt_t;
auto sub(ypos_t, hgt_t) -> ypos_t;
auto div(ypos_t, ypos_t) -> ypos_t;
auto compare(wth_t, wth_t) -> bool;
auto order(wth_t, wth_t) -> bool;
auto preinc(wth_t) -> wth_t;
auto add(wth_t, wth_t) -> wth_t;
auto sub(wth_t, wth_t) -> wth_t;
auto mul(wth_t, wth_t) -> wth_t;
auto div(wth_t, wth_t) -> wth_t;
auto compare(hgt_t, hgt_t) -> bool;
auto order(hgt_t, hgt_t) -> bool;
auto preinc(hgt_t) -> hgt_t;
auto add(hgt_t, hgt_t) -> hgt_t;
auto sub(hgt_t, hgt_t) -> hgt_t;
auto mul(hgt_t, hgt_t) -> hgt_t;
auto div(hgt_t, hgt_t) -> hgt_t;
namespace literals
{
friend std::ostream& operator<<(std::ostream& ost, val_t cord) {
return ost << cord.value();
}
constexpr auto operator""_x(unsigned long long val)
{
return xpos_t {static_cast<xpos_t::basic_type>(val)};
}
// clang-format on
constexpr auto operator""_y(unsigned long long val)
{
return ypos_t {static_cast<ypos_t::basic_type>(val)};
}
private:
sz_t v; // NOLINT
};
constexpr auto operator""_w(unsigned long long val)
{
return wth_t {static_cast<wth_t::basic_type>(val)};
}
using xpos_t = val_t<'x'>;
using ypos_t = val_t<'y'>;
using wth_t = val_t<'w'>;
using hgt_t = val_t<'h'>;
constexpr auto operator""_h(unsigned long long val)
{
return hgt_t {static_cast<hgt_t::basic_type>(val)};
}
// clang-format off
inline xpos_t operator+(xpos_t lhs, wth_t rhs) { return xpos_t(lhs.value() + rhs.value()); }
inline xpos_t operator-(xpos_t lhs, wth_t rhs) { return xpos_t(lhs.value() - rhs.value()); }
inline ypos_t operator+(ypos_t lhs, hgt_t rhs) { return ypos_t(lhs.value() + rhs.value()); }
inline ypos_t operator-(ypos_t lhs, hgt_t rhs) { return ypos_t(lhs.value() - rhs.value()); }
// clang-format on
} // namespace literals
struct dim_t
{
dim_t(int wdth, int hght) // NOLINT
dim_t(wth_t wdth, hgt_t hght)
: width(wdth)
, height(hght)
{
}
dim_t(wth_t wdth, hgt_t hght) // NOLINT
dim_t(hgt_t hght, wth_t wdth)
: width(wdth)
, height(hght)
{
}
dim_t(std::pair<std::uint16_t, std::uint16_t> pair) // NOLINT
: width(std::get<0>(pair))
, height(std::get<1>(pair))
dim_t(std::pair<based::bu16, based::bu16> pair) // NOLINT
: width(static_cast<wth_t::basic_type>(std::get<0>(pair)))
, height(static_cast<hgt_t::basic_type>(std::get<1>(pair)))
{
}
dim_t operator+(dim_t rhs) const
friend dim_t operator+(dim_t lhs, dim_t rhs)
{
return {width + rhs.width, height + rhs.height};
return {lhs.width + rhs.width, lhs.height + rhs.height};
}
dim_t operator-(dim_t rhs) const
friend dim_t operator-(dim_t lhs, dim_t rhs)
{
return {width - rhs.width, height - rhs.height};
return {lhs.width - rhs.width, lhs.height - rhs.height};
}
wth_t width;
@@ -122,25 +140,12 @@
struct dim_t
struct pad_t
{
pad_t(int width, int height) // NOLINT
pad_t(wth_t width, hgt_t height)
: pad_t(width, height, width, height)
{
}
pad_t(
int leftpos, // NOLINT
int toppos,
int rightpos,
int bottompos
)
: left(leftpos)
, right(rightpos)
, top(toppos)
, bottom(bottompos)
{
}
pad_t(wth_t width, hgt_t height) // NOLINT
pad_t(hgt_t height, wth_t width)
: pad_t(width, height, width, height)
{
}
@@ -153,8 +158,8 @@
struct pad_t
{
}
wth_t width() const { return left + right; }
hgt_t height() const { return top + bottom; }
[[nodiscard]] wth_t width() const { return left + right; }
[[nodiscard]] hgt_t height() const { return top + bottom; }
friend dim_t operator+(dim_t dim, pad_t rhs)
{
@@ -169,30 +174,26 @@
struct pad_t
struct pos_t
{
using sz_t = std::uint16_t;
pos_t(int xpos, int ypos)
pos_t(xpos_t xpos, ypos_t ypos)
: x(xpos)
, y(ypos)
{
}
pos_t(sz_t xpos, sz_t ypos)
pos_t(ypos_t ypos, xpos_t xpos)
: x(xpos)
, y(ypos)
{
}
pos_t(xpos_t xpos, ypos_t ypos)
: x(xpos)
, y(ypos)
friend pos_t operator+(pos_t lhs, pos_t rhs)
{
return {lhs.x + rhs.x, lhs.y + rhs.y};
}
pos_t operator+(pos_t rhs) const { return {x + rhs.x, y + rhs.y}; }
dim_t operator-(pos_t rhs) const
friend dim_t operator-(pos_t lhs, pos_t rhs)
{
return {wth_t((x - rhs.x).value()), hgt_t((y - rhs.y).value())};
return {wth_t((lhs.x - rhs.x).value), hgt_t((lhs.y - rhs.y).value)};
}
pos_t operator+(dim_t rhs) const { return {x + rhs.width, y + rhs.height}; }
@@ -209,34 +210,44 @@
struct plc_t
{
}
plc_t(dim_t dimval, pos_t posval)
: pos(posval)
, dim(dimval)
{
}
pos_t pos;
dim_t dim;
};
enum class PvtX : std::uint8_t
{
Left,
Center,
Right
};
enum class PvtY : std::uint8_t
{
Top,
Center,
Bottom
};
#define ENUM_PVTX left, center, right
#define ENUM_PVTY top, center, bottom
struct piv_t
{
piv_t(PvtX pvtx, PvtY pvty)
: x(pvtx)
, y(pvty)
BASED_DECLARE_ENUM(x, based::bu8, 0, ENUM_PVTX)
BASED_DECLARE_ENUM(y, based::bu8, 0, ENUM_PVTY)
piv_t(x::enum_type pvtx, y::enum_type pvty)
: piv_x(pvtx)
, piv_y(pvty)
{
}
piv_t(y::enum_type pvty, x::enum_type pvtx)
: piv_x(pvtx)
, piv_y(pvty)
{
}
PvtX x;
PvtY y;
x::enum_type piv_x;
y::enum_type piv_y;
};
BASED_DEFINE_ENUM_CLASS(piv_t, x, based::bu8, 0, ENUM_PVTX)
#undef ENUM_PVTX
BASED_DEFINE_ENUM_CLASS(piv_t, y, based::bu8, 0, ENUM_PVTY)
#undef ENUM_PVTY
} // namespace display
diff --git a/ include/display/window.hpp b/ include/display/window.hpp
@@ -20,7 +20,7 @@
public:
void input(event& /* unused */) override {}
protected:
pad_t padd() const { return m_padd; }
[[nodiscard]] pad_t padd() const { return m_padd; }
std::ostream& line_next() const;
@@ -30,13 +30,13 @@
protected:
void line_center(const std::string& text) const;
void line_right(const std::string& text) const;
plc_t plc() const { return {pos(), dim()}; }
pos_t pos() const { return {xpos(), ypos()}; }
dim_t dim() const { return {wth(), hgt()}; }
xpos_t xpos() const { return axpos() + m_padd.left; }
ypos_t ypos() const { return aypos() + m_padd.top; }
wth_t wth() const { return awth() - m_padd.width(); }
hgt_t hgt() const { return ahgt() - m_padd.height(); }
[[nodiscard]] plc_t plc() const { return {pos(), dim()}; }
[[nodiscard]] pos_t pos() const { return {xpos(), ypos()}; }
[[nodiscard]] dim_t dim() const { return {wth(), hgt()}; }
[[nodiscard]] xpos_t xpos() const { return axpos() + m_padd.left; }
[[nodiscard]] ypos_t ypos() const { return aypos() + m_padd.top; }
[[nodiscard]] wth_t wth() const { return awth() - m_padd.width(); }
[[nodiscard]] hgt_t hgt() const { return ahgt() - m_padd.height(); }
private:
using Element::adim;
diff --git a/ source/display.cpp b/ source/display.cpp
@@ -8,10 +8,10 @@
namespace
{
template<const char* Val>
template<const char* val>
inline void write()
{
::write(STDIN_FILENO, Val, sizeof(Val));
::write(STDIN_FILENO, val, sizeof(val));
}
} // namespace
@@ -19,6 +19,8 @@
inline void write()
namespace display
{
using namespace literals; // NOLINT(*namespace*)
bool Display::is_resize_track = false;
Display& Display::display()
@@ -28,7 +30,7 @@
Display& Display::display()
}
Display::Display()
: m_layout(plc_t(pos_t(0, 0), alec::get_screen_size()))
: m_layout(plc_t(pos_t(0_x, 0_y), alec::get_screen_size()))
{
struct sigaction old_sig_action = {};
sigaction(SIGWINCH, nullptr, &old_sig_action);
@@ -90,7 +92,7 @@
bool Display::get_resized() const
void Display::resize()
{
m_layout.resize(plc_t(pos_t(0, 0), alec::get_screen_size()));
m_layout.resize(plc_t(pos_t(0_x, 0_y), alec::get_screen_size()));
}
void Display::render() const
diff --git a/ source/element.cpp b/ source/element.cpp
@@ -7,7 +7,7 @@
namespace display
std::ostream& Element::set_cursor(xpos_t xpos, ypos_t ypos)
{
return std::cout << alec::cursor_position(ypos.value() + 1, xpos.value() + 1);
return std::cout << alec::cursor_position(ypos.value + 1, xpos.value + 1);
}
std::ostream& Element::set_cursor(pos_t pos)
@@ -17,22 +17,23 @@
std::ostream& Element::set_cursor(pos_t pos)
void Element::render_border() const
{
using namespace literals; // NOLINT(*namespace*)
set_cursor(axpos(), aypos());
std::cout << "┌";
for (auto i = wth_t(2); i < awth(); i++) {
for (auto i = 2_w; i < awth(); ++i) {
std::cout << "─";
}
std::cout << "┐";
for (ypos_t j = aypos() + 1; j < aypos() + ahgt(); j++) {
for (auto j = aypos() + 1_y; j < aypos() + ahgt(); ++j) {
set_cursor(axpos(), j) << "│";
set_cursor(axpos() + awth() - 1, j) << "│";
set_cursor(axpos() + awth() - 1_w, j) << "│";
}
set_cursor(axpos(), aypos() + ahgt() - 1);
set_cursor(axpos(), aypos() + ahgt() - 1_h);
std::cout << "└";
for (auto i = wth_t(2); i < awth(); i++) {
for (auto i = 2_w; i < awth(); ++i) {
std::cout << "─";
}
std::cout << "┘";
diff --git a/ source/window.cpp b/ source/window.cpp
@@ -7,15 +7,17 @@
namespace display
{
using namespace literals; // NOLINT(*namespace*)
void Window::render() const
{
const auto space = std::string(awth().value(), ' ');
const auto space = std::string(awth().value, ' ');
for (auto j = aypos(); j < aypos() + m_padd.top; j++) {
for (auto j = 0_y; j < aypos() + m_padd.top; ++j) {
set_cursor(axpos(), j) << space;
}
for (auto j = m_ypos; j < aypos() + ahgt(); j++) {
for (auto j = m_ypos; j < aypos() + ahgt(); ++j) {
set_cursor(axpos(), j) << space;
}
std::cout << std::flush;
@@ -26,8 +28,8 @@
void Window::clear() const
std::cout << alec::background_v<alec::color::def>;
std::cout << alec::foreground_v<alec::color::def>;
for (auto j = ypos_t(0); j < aypos() + ahgt(); j++) {
set_cursor(axpos(), j) << std::string(awth().value(), ' ');
for (auto j = 0_y; j < aypos() + ahgt(); ++j) {
set_cursor(axpos(), j) << std::string(awth().value, ' ');
}
std::cout << std::flush;
@@ -46,36 +48,36 @@
std::ostream& Window::line_next() const
return null;
}
return set_cursor(axpos(), m_ypos++);
return set_cursor(axpos(), ++m_ypos);
}
void Window::line_empty() const
{
line_next() << std::string(awth().value(), ' ');
line_next() << std::string(awth().value, ' ');
}
void Window::line_left(const std::string& text) const
{
const auto left = std::string(m_padd.left.value(), ' ');
const auto right = std::string(m_padd.right.value(), ' ');
const auto left = std::string(m_padd.left.value, ' ');
const auto right = std::string(m_padd.right.value, ' ');
line_next() << left << std::format("{:<{}}", text, wth().value()) << right;
line_next() << left << std::format("{:<{}}", text, wth().value) << right;
}
void Window::line_center(const std::string& text) const
{
const auto left = std::string(m_padd.left.value(), ' ');
const auto right = std::string(m_padd.right.value(), ' ');
const auto left = std::string(m_padd.left.value, ' ');
const auto right = std::string(m_padd.right.value, ' ');
line_next() << left << std::format("{:^{}}", text, wth().value()) << right;
line_next() << left << std::format("{:^{}}", text, wth().value) << right;
}
void Window::line_right(const std::string& text) const
{
const auto left = std::string(m_padd.left.value(), ' ');
const auto right = std::string(m_padd.right.value(), ' ');
const auto left = std::string(m_padd.left.value, ' ');
const auto right = std::string(m_padd.right.value, ' ');
line_next() << left << std::format("{:>{}}", text, wth().value()) << right;
line_next() << left << std::format("{:>{}}", text, wth().value) << right;
}
} // namespace display
diff --git a/ source/window_pivot.cpp b/ source/window_pivot.cpp
@@ -5,43 +5,45 @@
namespace display
{
using namespace literals; // NOLINT(*namespace*)
plc_t WindowPivot::place(plc_t aplc, piv_t piv, dim_t dim)
{
const auto [awth, ahth] = aplc.dim;
const auto [wth, hgt] = dim;
dim_t start(0, 0);
dim_t end(0, 0);
dim_t start(0_w, 0_h);
dim_t end(0_w, 0_h);
using display::add_lim, display::sub_lim;
switch (piv.x) {
case PvtX::Left:
start.width = wth_t(0);
switch (piv.piv_x()) {
case piv_t::x::left():
start.width = 0_w;
end.width = add_lim(start.width, wth, awth);
break;
case PvtX::Center:
start.width = sub_lim((awth / 2), (wth / 2), wth_t(0));
case piv_t::x::center():
start.width = sub_lim((awth / 2_w), (wth / 2_w), 0_w);
end.width = add_lim(start.width, wth, awth);
break;
case PvtX::Right:
case piv_t::x::right():
end.width = awth;
start.width = sub_lim(end.width, wth, wth_t(0));
start.width = sub_lim(end.width, wth, 0_w);
break;
}
switch (piv.y) {
case PvtY::Top:
start.height = hgt_t(0);
switch (piv.piv_y()) {
case piv_t::y::top():
start.height = 0_h;
end.height = add_lim(start.height, hgt, ahth);
break;
case PvtY::Center:
start.height = sub_lim((ahth / 2), (hgt / 2), hgt_t(0));
case piv_t::y::center():
start.height = sub_lim((ahth / 2_h), (hgt / 2_h), 0_h);
end.height = add_lim(start.height, hgt, ahth);
break;
case PvtY::Bottom:
case piv_t::y::bottom():
end.height = ahth;
start.height = sub_lim(end.height, hgt, hgt_t(0));
start.height = sub_lim(end.height, hgt, 0_h);
break;
}
diff --git a/ test/source/utility_test.cpp b/ test/source/utility_test.cpp
@@ -1,13 +1,14 @@
#include <cstdint>
#include <limits>
#include "display/utility.hpp"
#include <based/types/types.hpp>
int main()
{
using namespace display; // NOLINT
using sz_t = std::uint16_t;
using sz_t = based::bu16;
using lim = std::numeric_limits<sz_t>;
static constexpr const sz_t zero = 0;