displayLayout and Rendering TUI library |
git clone git://git.dimitrijedobrota.com/display.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | 8f7a83b9741e3012294f7f4d4876c6be54514213 |
parent | dd1a7e97d6177f2229a16a9eba752efbf8507608 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Tue, 18 Feb 2025 15:35:20 +0100 |
Add different types of window borders * Window derivatives no longer use absolute position * There is a padding that will be used by the base * Improve types.hpp consistency * Better line drawing interface
Diffstat:M | CMakeLists.txt | | | +- |
M | example/example.cpp | | | +++--- |
M | example/navig/navig.cpp | | | +++++++------ |
M | include/display/element.hpp | | | ++-- |
M | include/display/layout_rigid.hpp | | | +- |
M | include/display/types.hpp | | | +++++++++++++----- |
M | include/display/window.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
M | include/display/window_pivot.hpp | | | +++++++++------- |
M | source/window.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
9 files changed, 196 insertions(+), 40 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)
project(
display
VERSION 0.1.29
VERSION 0.1.30
DESCRIPTION "TUI library"
HOMEPAGE_URL "git://git.dimitrijedobrota.com/display.git"
LANGUAGES CXX
diff --git a/example/example.cpp b/example/example.cpp
@@ -12,7 +12,7 @@ namespace
using display::PvtX, display::PvtY;
using display::sz_t, display::dim_t, display::piv_t, display::place_t;
class WindowCustom : public display::WindowPivot
class WindowCustom : public display::WindowPivot<display::Window>
{
public:
explicit WindowCustom(place_t aplc, piv_t piv, dim_t dim)
@@ -27,8 +27,8 @@ public:
color_red = (color_red + 25) % 256;
std::cout << alec::background(color_red, 65, 65);
line_empty(/* reset = */ true);
for (std::size_t i = 1; i < ahgt(); i++) {
line_reset();
for (std::size_t i = 0; i < hgt(); i++) {
line_empty();
}
diff --git a/example/navig/navig.cpp b/example/navig/navig.cpp
@@ -14,7 +14,7 @@ namespace
bool is_finished = false; // NOLINT
class WindowCustom : public display::WindowPivot
class WindowCustom : public display::WindowPivot<display::WindowBorderBox>
{
public:
WindowCustom(display::place_t aplc,
@@ -28,8 +28,8 @@ public:
void render() const override
{
std::cout << alec::background_v<alec::Color::BLUE>;
line_reset();
line_empty(/* reset = */ true);
line_center(m_menu.title);
line_empty();
for (std::size_t i = 0; i < m_menu.items.size(); i++) {
@@ -43,7 +43,8 @@ public:
std::cout << alec::foreground_v<alec::Color::DEFAULT>;
}
}
line_empty();
display::WindowBorderBox::render();
std::cout << alec::background_v<alec::Color::DEFAULT>;
std::cout << std::flush;
@@ -94,8 +95,8 @@ private:
width = std::max(width, item.prompt.size());
}
return {static_cast<display::sz_t>(width + 2),
static_cast<display::sz_t>(menu.items.size() + 4)};
return {static_cast<display::sz_t>(width),
static_cast<display::sz_t>(menu.items.size() + 2)};
}
example::menu_t m_menu;
@@ -166,7 +167,7 @@ int menu_t::visit(const menu_t& menu)
stk.push(&menu);
}
layout.set_child<WindowCustom>(piv_t(PvtX::Center, PvtY::Center), *stk.top());
layout.set_child<WindowCustom>(piv_t(PvtX::Right, PvtY::Bottom), *stk.top());
layout.render();
return 0;
diff --git a/include/display/element.hpp b/include/display/element.hpp
@@ -26,8 +26,8 @@ public:
virtual void input(event& evnt) = 0;
const auto& aplc() const { return m_aplc; }
const auto& apos() const { return aplc().apos; }
const auto& adim() const { return aplc().adim; }
const auto& apos() const { return aplc().pos; }
const auto& adim() const { return aplc().dim; }
const auto& axpos() const { return apos().x; }
const auto& aypos() const { return apos().y; }
const auto& awth() const { return adim().width; }
diff --git a/include/display/layout_rigid.hpp b/include/display/layout_rigid.hpp
@@ -16,7 +16,7 @@ class LayoutRigid : public LayoutMulti<T>
public:
using layout_t = std::vector<std::vector<std::uint8_t>>;
LayoutRigid(place_t aplc, layout_t layout); // NOLINT
LayoutRigid(place_t aplc, layout_t layout);
private:
std::size_t count_and_pad(layout_t& layout) const;
diff --git a/include/display/types.hpp b/include/display/types.hpp
@@ -31,6 +31,14 @@ struct dim_t
{
}
dim_t operator+(dim_t rhs) const
{
return {
static_cast<sz_t>(width + rhs.width),
static_cast<sz_t>(height + rhs.height),
};
}
sz_t width;
sz_t height;
};
@@ -70,14 +78,14 @@ struct pos_t
struct place_t
{
place_t(pos_t aposval, dim_t adimval)
: apos(aposval)
, adim(adimval)
place_t(pos_t posval, dim_t dimval)
: pos(posval)
, dim(dimval)
{
}
pos_t apos;
dim_t adim;
pos_t pos;
dim_t dim;
};
enum class PvtX : std::uint8_t
diff --git a/include/display/window.hpp b/include/display/window.hpp
@@ -9,23 +9,96 @@ namespace display
class Window : public Element
{
public:
explicit Window(place_t aplc)
using ustring = std::basic_string<unsigned char>;
explicit Window(place_t aplc, dim_t padding = {0, 0})
: Element(aplc)
, m_padding(padding)
{
}
void render() const override {}
void clear() const override;
void input(event& /* unused */) override {}
protected:
std::ostream& next_line(bool reset = false) const;
void line_empty(bool reset = false) const;
static dim_t padding() { return {0, 0}; }
std::ostream& set_cursor(sz_t posy, sz_t posx) const;
std::ostream& line_next() const;
void line_reset() const;
void line_empty() const;
void line_left(const std::string& text) const;
void line_center(const std::string& text) const;
void line_right(const std::string& text) const;
place_t plc() const { return {pos(), dim()}; }
pos_t pos() const { return {xpos(), ypos()}; }
dim_t dim() const { return {wth(), hgt()}; }
sz_t xpos() const { return axpos() + (m_padding.width / 2); }
sz_t ypos() const { return aypos() + (m_padding.height / 2); }
sz_t wth() const { return awth() - m_padding.width; }
sz_t hgt() const { return ahgt() - m_padding.height; }
private:
friend class WindowBorder;
friend class WindowBox;
friend class WindowBorderBox;
using Element::adim;
using Element::ahgt;
using Element::aplc;
using Element::apos;
using Element::awth;
using Element::axpos;
using Element::aypos;
dim_t m_padding;
mutable display::sz_t m_ypos = 0;
};
class WindowBorder : public Window
{
public:
explicit WindowBorder(place_t aplc)
: Window(aplc, padding())
{
}
void render() const override;
protected:
static dim_t padding() { return {2, 2}; }
};
class WindowBox : public Window
{
public:
explicit WindowBox(place_t aplc)
: Window(aplc, padding())
{
}
void render() const override;
protected:
static dim_t padding() { return {2, 2}; }
};
class WindowBorderBox : public Window
{
public:
explicit WindowBorderBox(place_t aplc)
: Window(aplc, padding())
{
}
void render() const override;
protected:
static dim_t padding() { return {4, 2}; }
};
} // namespace display
diff --git a/include/display/window_pivot.hpp b/include/display/window_pivot.hpp
@@ -6,11 +6,13 @@
namespace display
{
class WindowPivot : public Window
template<typename T>
requires(std::is_base_of_v<Window, T>)
class WindowPivot : public T
{
public:
WindowPivot(place_t aplc, piv_t piv, dim_t dim)
: Window(place(aplc, piv, dim))
: T(place(aplc, piv, dim, T::padding()))
, m_piv(piv)
, m_dim(dim)
{
@@ -18,17 +20,17 @@ public:
void resize(place_t aplc) override
{
Window::resize(place(aplc, m_piv, m_dim));
T::resize(place(aplc, m_piv, m_dim, T::padding()));
}
protected:
static place_t place(place_t aplc, piv_t piv, dim_t dim)
static place_t place(place_t aplc, piv_t piv, dim_t dim, dim_t padding)
{
const auto [cols, rows] = aplc.adim;
const auto [cols, rows] = aplc.dim;
const sz_t colsh = cols / 2;
const sz_t rowsh = rows / 2;
const auto [wdth, hght] = dim;
const auto [wdth, hght] = dim + padding;
const sz_t wdthm = wdth / 2;
const sz_t hghtm = hght / 2;
@@ -69,7 +71,7 @@ protected:
break;
}
return {aplc.apos + start, end - start};
return {aplc.pos + start, end - start};
}
private:
diff --git a/source/window.cpp b/source/window.cpp
@@ -1,4 +1,5 @@
#include <format>
#include <fstream>
#include <iostream>
#include "display/window.hpp"
@@ -11,39 +12,110 @@ void Window::clear() const
std::cout << alec::background_v<alec::Color::DEFAULT>;
std::cout << alec::foreground_v<alec::Color::DEFAULT>;
line_empty(/* reset = */ true);
for (std::size_t i = 1; i < ahgt(); i++) {
line_empty();
for (sz_t i = 0; i < aypos() + ahgt(); i++) {
set_cursor(i, axpos()) << std::string(awth(), ' ');
}
std::cout << std::flush;
}
std::ostream& Window::next_line(bool reset) const
// Window
void Window::line_reset() const
{
m_ypos = ypos();
}
std::ostream& Window::set_cursor(sz_t posy, sz_t posx) const
{
return std::cout << alec::cursor_position(posy + 1, posx + 1);
}
std::ostream& Window::line_next() const
{
if (reset) {
m_ypos = aypos();
if (m_ypos == ypos() + hgt()) {
static std::ofstream null;
null.setstate(std::ios_base::badbit);
return null;
}
return std::cout << alec::cursor_position(++m_ypos, axpos() + 1);
return set_cursor(m_ypos++, xpos());
}
void Window::line_empty(bool reset) const
void Window::line_empty() const
{
next_line(reset) << std::string(awth(), ' ');
line_next() << std::string(wth(), ' ');
}
void Window::line_left(const std::string& text) const
{
next_line() << std::format(" {:<{}} ", text, awth() - 2);
line_next() << std::format("{:<{}}", text, wth());
}
void Window::line_center(const std::string& text) const
{
next_line() << std::format("{:^{}}", text, awth());
line_next() << std::format("{:^{}}", text, wth());
}
void Window::line_right(const std::string& text) const
{
next_line() << std::format(" {:>{}} ", text, awth() - 2);
line_next() << std::format("{:>{}}", text, wth());
}
void WindowBorder::render() const
{
set_cursor(aypos(), axpos()) << std::string(awth(), ' ');
for (sz_t i = ypos(); i < ypos() + hgt(); i++) {
set_cursor(i, axpos()) << ' ';
set_cursor(i, axpos() + awth() - 1) << ' ';
}
set_cursor(aypos() + ahgt() - 1, axpos()) << std::string(awth(), ' ');
}
void WindowBox::render() const
{
set_cursor(aypos(), axpos());
std::cout << "┌";
for (sz_t i = 0; i < wth(); i++) {
std::cout << "─";
}
std::cout << "┐";
for (sz_t i = ypos(); i < ypos() + hgt(); i++) {
set_cursor(i, axpos()) << "│";
set_cursor(i, axpos() + awth() - 1) << "│";
}
set_cursor(aypos() + ahgt() - 1, axpos());
std::cout << "└";
for (sz_t i = 0; i < wth(); i++) {
std::cout << "─";
}
std::cout << "┘";
}
void WindowBorderBox::render() const
{
set_cursor(aypos(), axpos());
std::cout << "┌─";
for (sz_t i = 0; i < wth(); i++) {
std::cout << "─";
}
std::cout << "─┐";
for (sz_t i = ypos(); i < ypos() + hgt(); i++) {
set_cursor(i, axpos()) << "│ ";
set_cursor(i, axpos() + awth() - 2) << " │";
}
set_cursor(aypos() + ahgt() - 1, axpos());
std::cout << "└─";
for (sz_t i = 0; i < wth(); i++) {
std::cout << "─";
}
std::cout << "─┘";
}
} // namespace display