displayLayout and Rendering TUI library |
git clone git://git.dimitrijedobrota.com/display.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | 2479b140bae99f942c5f3ea57bffa321a9e42c6d |
parent | f01eb04eefe3ac6e000b5ef7ea6ae635c1b02f01 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Wed, 12 Feb 2025 11:12:07 +0100 |
Separate into multiple files, extract WindowPivot
Diffstat:M | CMakeLists.txt | | | +++++--- |
M | example/example.cpp | | | +++++++++++++++++++++++--------------------- |
M | include/display/layout.hpp | | | ++++++++++++---------------------------------------------------- |
A | include/display/layout_free.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | include/display/layout_rigid.hpp | | | ++++++++++++++++++++++++++++++++++ |
M | include/display/screen.hpp | | | +++++++++++++ |
M | include/display/types.hpp | | | ----------------------- |
M | include/display/window.hpp | | | ++++-------------------- |
A | include/display/window_pivot.hpp | | | +++++++++++++++++++++++++++++++++++++++ |
M | source/layout.cpp | | | +-------------------------------------------- |
A | source/layout_free.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++ |
A | source/layout_rigid.cpp | | | +++++++++++++++++++++++++++ |
D | source/window.cpp | | | ----------------------------------------------------------------- |
A | source/window_pivot.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
14 files changed, 325 insertions(+), 228 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)
project(
display
VERSION 0.1.15
VERSION 0.1.16
DESCRIPTION "TUI library"
HOMEPAGE_URL "https://example.com/"
LANGUAGES CXX
@@ -20,9 +20,11 @@ find_package(alec 0.1.13 CONFIG REQUIRED)
add_library(
display_display
source/display.cpp
source/layout.cpp
source/screen.cpp
source/window.cpp
# ---- utility
source/layout_free.cpp
source/layout_rigid.cpp
source/window_pivot.cpp
)
target_link_libraries(display_display PUBLIC alec::alec)
add_library(display::display ALIAS display_display)
diff --git a/example/example.cpp b/example/example.cpp
@@ -4,12 +4,14 @@
#include "alec/alec.hpp"
#include "display/display.hpp"
#include "display/layout.hpp"
#include "display/layout_free.hpp"
#include "display/layout_rigid.hpp"
#include "display/window_pivot.hpp"
namespace
{
void renderer(const display::Window& win, display::place_t plc)
void renderer(const display::WindowPivot& win, display::place_t plc)
{
using display::place_t;
@@ -33,18 +35,18 @@ void fill(display::LayoutFree& layout)
{
using display::pos_t, display::dim_t, display::piv_t;
using display::PvtX, display::PvtY;
using display::Window;
using display::WindowPivot;
// clang-format off
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Left, PvtY::Top));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Center, PvtY::Top));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Right, PvtY::Top));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Right, PvtY::Center));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Right, PvtY::Bottom));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Center, PvtY::Bottom));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Left, PvtY::Bottom));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Left, PvtY::Center));
layout.append<Window>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Center, PvtY::Center));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Left, PvtY::Top));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Center, PvtY::Top));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Right, PvtY::Top));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Right, PvtY::Center));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Right, PvtY::Bottom));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Center, PvtY::Bottom));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Left, PvtY::Bottom));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Left, PvtY::Center));
layout.append<WindowPivot>(renderer, pos_t(), dim_t(10, 5), piv_t(PvtX::Center, PvtY::Center));
// clang-format on
}
@@ -65,15 +67,15 @@ int main()
const display::sz_t midw = width / 2;
const display::sz_t midh = height / 2;
layout[(start + 0) % 8]->pos() = {0, 0};
layout[(start + 1) % 8]->pos() = {midw, 0};
layout[(start + 2) % 8]->pos() = {width, 0};
layout[(start + 3) % 8]->pos() = {width, midh};
layout[(start + 4) % 8]->pos() = {width, height};
layout[(start + 5) % 8]->pos() = {midw, height};
layout[(start + 6) % 8]->pos() = {0, height};
layout[(start + 7) % 8]->pos() = {0, midh};
layout[8]->pos() = {midw, midh};
layout.get<WindowPivot>((start + 0) % 8).pos() = {0, 0};
layout.get<WindowPivot>((start + 1) % 8).pos() = {midw, 0};
layout.get<WindowPivot>((start + 2) % 8).pos() = {width, 0};
layout.get<WindowPivot>((start + 3) % 8).pos() = {width, midh};
layout.get<WindowPivot>((start + 4) % 8).pos() = {width, height};
layout.get<WindowPivot>((start + 5) % 8).pos() = {midw, height};
layout.get<WindowPivot>((start + 6) % 8).pos() = {0, height};
layout.get<WindowPivot>((start + 7) % 8).pos() = {0, midh};
layout.get<WindowPivot>(8).pos() = {midw, midh};
};
auto& layout1 =
diff --git a/include/display/layout.hpp b/include/display/layout.hpp
@@ -1,71 +1,31 @@
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include "display/screen.hpp"
#include "display/types.hpp"
#include "display/window.hpp"
namespace display
{
class LayoutFree : public Layout
class Layout
{
public:
using recalc_f = std::function<void(LayoutFree&)>;
LayoutFree(recalc_f f_recalc) // NOLINT
: m_recalc(std::move(f_recalc))
{
}
Layout() = default;
Window* operator[](std::size_t idx) { return m_windows[idx].get(); }
const Window* operator[](std::size_t idx) const
{
return m_windows[idx].get();
}
Layout(const Layout&) = delete;
Layout& operator=(const Layout&) = delete;
template<typename T, class... Args>
T& append(Args&&... args)
{
m_windows.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
m_is_sorted = false;
return *dynamic_cast<T*>(m_windows.back().get());
}
Layout(Layout&&) = delete;
Layout& operator=(Layout&&) = delete;
void resize(dim_t dim) override;
int render(pos_t pos) const override;
virtual ~Layout() = default;
private:
recalc_f m_recalc;
const dim_t& dim() const { return m_dim; }
dim_t& dim() { return m_dim; }
std::vector<std::unique_ptr<Window>> m_windows;
mutable bool m_is_sorted = true;
};
class LayoutRigid : public Layout
{
public:
using recalc_f = std::function<void(LayoutRigid&)>;
LayoutRigid(recalc_f f_recalc) // NOLINT
: m_recalc(std::move(f_recalc))
{
}
auto& screen1() { return m_screen1; }
auto& screen2() { return m_screen2; }
void resize(dim_t dim) override;
int render(pos_t pos) const override;
virtual void resize(dim_t dim) = 0;
virtual int render(pos_t pos) const = 0;
private:
recalc_f m_recalc;
Screen m_screen1;
Screen m_screen2;
dim_t m_dim;
};
} // namespace display
diff --git a/include/display/layout_free.hpp b/include/display/layout_free.hpp
@@ -0,0 +1,57 @@
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include "display/layout.hpp"
#include "display/types.hpp"
#include "display/window.hpp"
namespace display
{
class LayoutFree : public Layout
{
public:
using recalc_f = std::function<void(LayoutFree&)>;
LayoutFree(recalc_f f_recalc) // NOLINT
: m_recalc(std::move(f_recalc))
{
}
Window* operator[](std::size_t idx) { return m_wins[idx].get(); }
const Window* operator[](std::size_t idx) const { return m_wins[idx].get(); }
template<typename T, class... Args>
T& append(Args&&... args)
{
m_wins.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
m_is_sorted = false;
return get<T>(m_wins.size() - 1);
}
template<typename T>
const T& get(std::size_t idx) const
{
return *dynamic_cast<T*>(m_wins[idx].get());
}
template<typename T>
T& get(std::size_t idx)
{
return *dynamic_cast<T*>(m_wins[idx].get());
}
void resize(dim_t dim) override;
int render(pos_t pos) const override;
private:
recalc_f m_recalc;
std::vector<std::unique_ptr<Window>> m_wins;
mutable bool m_is_sorted = true;
};
} // namespace display
diff --git a/include/display/layout_rigid.hpp b/include/display/layout_rigid.hpp
@@ -0,0 +1,34 @@
#pragma once
#include <functional>
#include "display/screen.hpp"
#include "display/types.hpp"
namespace display
{
class LayoutRigid : public Layout
{
public:
using recalc_f = std::function<void(LayoutRigid&)>;
LayoutRigid(recalc_f f_recalc) // NOLINT
: m_recalc(std::move(f_recalc))
{
}
auto& screen1() { return m_screen1; }
auto& screen2() { return m_screen2; }
void resize(dim_t dim) override;
int render(pos_t pos) const override;
private:
recalc_f m_recalc;
Screen m_screen1;
Screen m_screen2;
};
} // namespace display
diff --git a/include/display/screen.hpp b/include/display/screen.hpp
@@ -2,6 +2,7 @@
#include <memory>
#include "display/layout.hpp"
#include "display/types.hpp"
namespace display
@@ -29,6 +30,18 @@ public:
T& set_layout(Args&&... args)
{
m_layout = std::make_unique<T>(std::forward<Args>(args)...);
return get_layout<T>();
}
template<typename T>
const T& get_layout() const
{
return *dynamic_cast<T*>(m_layout.get());
}
template<typename T>
T& get_layout()
{
return *dynamic_cast<T*>(m_layout.get());
}
diff --git a/include/display/types.hpp b/include/display/types.hpp
@@ -87,27 +87,4 @@ struct piv_t
PvtY y;
};
class Layout
{
public:
Layout() = default;
Layout(const Layout&) = delete;
Layout& operator=(const Layout&) = delete;
Layout(Layout&&) = delete;
Layout& operator=(Layout&&) = delete;
virtual ~Layout() = default;
const dim_t& dim() const { return m_dim; }
dim_t& dim() { return m_dim; }
virtual void resize(dim_t dim) = 0;
virtual int render(pos_t pos) const = 0;
private:
dim_t m_dim;
};
} // namespace display
diff --git a/include/display/window.hpp b/include/display/window.hpp
@@ -1,6 +1,5 @@
#pragma once
#include <functional>
#include <optional>
#include "display/types.hpp"
@@ -11,13 +10,8 @@ namespace display
class Window
{
public:
using render_f = std::function<void(const Window&, place_t place)>;
Window(render_f frender, pos_t pos, dim_t dim, piv_t piv = {})
: m_renderer(std::move(frender))
, m_pos(pos)
, m_dim(dim)
, m_piv(piv)
Window(pos_t pos = {}) // NOLINT
: m_pos(pos)
{
}
@@ -32,21 +26,11 @@ public:
const auto& pos() const { return m_pos; }
auto& pos() { return m_pos; }
const auto& dim() const { return m_dim; }
auto& dim() { return m_dim; }
const auto& piv() const { return m_piv; }
auto& piv() { return m_piv; }
virtual void render(place_t place) const;
std::optional<place_t> place(dim_t bounds) const;
virtual std::optional<place_t> place(dim_t bounds) = 0;
virtual void render(place_t place) const = 0;
private:
render_f m_renderer;
pos_t m_pos;
dim_t m_dim;
piv_t m_piv;
};
} // namespace display
diff --git a/include/display/window_pivot.hpp b/include/display/window_pivot.hpp
@@ -0,0 +1,39 @@
#pragma once
#include <functional>
#include "display/types.hpp"
#include "display/window.hpp"
namespace display
{
class WindowPivot : public Window
{
public:
using render_f = std::function<void(const WindowPivot&, place_t place)>;
WindowPivot(render_f frender, pos_t pos, dim_t dim, piv_t piv = {})
: Window(pos)
, m_renderer(std::move(frender))
, m_dim(dim)
, m_piv(piv)
{
}
const auto& dim() const { return m_dim; }
auto& dim() { return m_dim; }
const auto& piv() const { return m_piv; }
auto& piv() { return m_piv; }
std::optional<place_t> place(dim_t bounds) override;
void render(place_t place) const override;
private:
render_f m_renderer;
dim_t m_dim;
piv_t m_piv;
};
} // namespace display
diff --git a/source/layout.cpp b/source/layout.cpp
@@ -1,51 +1,8 @@
#include <algorithm>
#include <numeric>
#include "display/layout.hpp"
#include "display/window.hpp"
#include "display/layout_rigid.hpp"
namespace display
{
void LayoutFree::resize(dim_t dim)
{
this->dim() = dim;
if (m_recalc != nullptr) {
m_recalc(*this);
}
}
int LayoutFree::render(pos_t pos) const
{
static std::vector<std::uint8_t> idxs;
if (!m_is_sorted) {
idxs.resize(m_windows.size());
std::iota(idxs.begin(), idxs.end(), 0);
std::stable_sort(
idxs.begin(),
idxs.end(),
[&](auto left, auto right)
{ return m_windows[left]->pos().z < m_windows[right]->pos().z; });
m_is_sorted = true;
}
for (const auto idx : idxs) {
const auto& win = m_windows[idx];
const auto plc = win->place(this->dim());
if (!plc.has_value()) {
continue;
}
win->render(plc.value() + pos);
}
return 0;
}
void LayoutRigid::resize(dim_t dim)
{
this->dim() = dim;
diff --git a/source/layout_free.cpp b/source/layout_free.cpp
@@ -0,0 +1,45 @@
#include <algorithm>
#include <numeric>
#include "display/layout_free.hpp"
namespace display
{
void LayoutFree::resize(dim_t dim)
{
this->dim() = dim;
if (m_recalc != nullptr) {
m_recalc(*this);
}
}
int LayoutFree::render(pos_t pos) const
{
static std::vector<std::uint8_t> idxs;
if (!m_is_sorted) {
idxs.resize(m_wins.size());
std::iota(idxs.begin(), idxs.end(), 0);
std::stable_sort(
idxs.begin(),
idxs.end(),
[&](auto left, auto right)
{ return m_wins[left]->pos().z < m_wins[right]->pos().z; });
m_is_sorted = true;
}
for (const auto idx : idxs) {
const auto& win = m_wins[idx];
const auto plc = win->place(this->dim());
if (!plc.has_value()) {
continue;
}
win->render(plc.value() + pos);
}
return 0;
}
} // namespace display
diff --git a/source/layout_rigid.cpp b/source/layout_rigid.cpp
@@ -0,0 +1,27 @@
#include "display/layout_rigid.hpp"
namespace display
{
void LayoutRigid::resize(dim_t dim)
{
this->dim() = dim;
const sz_t mid = this->dim().height / 2;
m_screen1.resize({dim.width, mid});
m_screen2.resize({dim.width, mid});
if (m_recalc != nullptr) {
m_recalc(*this);
}
}
int LayoutRigid::render(pos_t pos) const
{
const sz_t mid = this->dim().height / 2;
m_screen1.render(pos + pos_t(0, 0));
m_screen2.render(pos + pos_t(0, mid + 1));
return 0;
}
} // namespace display
diff --git a/source/window.cpp b/source/window.cpp
@@ -1,65 +0,0 @@
#include "display/window.hpp"
#include "display/utility.hpp"
namespace display
{
void Window::render(place_t place) const
{
if (m_renderer != nullptr) {
m_renderer(*this, place);
}
}
std::optional<place_t> Window::place(dim_t bounds) const
{
const auto [cols, rows] = bounds;
const auto [posx, posy, _] = pos();
if (posx > cols || posy > rows) {
return {};
}
const auto [wdth, hght] = dim();
const display::sz_t zero = 0;
const sz_t wdthm = wdth / 2;
const sz_t hghtm = hght / 2;
pos_t start;
pos_t end;
switch (piv().x) {
case PvtX::Left:
start.x = posx;
end.x = add_lim(start.x, wdth, cols);
break;
case PvtX::Center:
start.x = sub_lim(posx, wdthm, zero);
end.x = add_lim(posx, wdthm, cols);
break;
case PvtX::Right:
end.x = posx;
start.x = sub_lim(end.x, wdth, zero);
break;
}
switch (piv().y) {
case PvtY::Top:
start.y = posy;
end.y = add_lim(start.y, hght, rows);
break;
case PvtY::Center:
start.y = sub_lim(posy, hghtm, zero);
end.y = add_lim(posy, hghtm, rows);
break;
case PvtY::Bottom:
end.y = posy;
start.y = sub_lim(end.y, hght, zero);
break;
}
return place_t(start, end);
}
} // namespace display
diff --git a/source/window_pivot.cpp b/source/window_pivot.cpp
@@ -0,0 +1,65 @@
#include "display/window_pivot.hpp"
#include "display/utility.hpp"
namespace display
{
void WindowPivot::render(place_t place) const
{
if (m_renderer != nullptr) {
m_renderer(*this, place);
}
}
std::optional<place_t> WindowPivot::place(dim_t bounds)
{
const auto [cols, rows] = bounds;
const auto [posx, posy, _] = pos();
if (posx > cols || posy > rows) {
return {};
}
const auto [wdth, hght] = dim();
const display::sz_t zero = 0;
const sz_t wdthm = wdth / 2;
const sz_t hghtm = hght / 2;
pos_t start;
pos_t end;
switch (piv().x) {
case PvtX::Left:
start.x = posx;
end.x = add_lim(start.x, wdth, cols);
break;
case PvtX::Center:
start.x = sub_lim(posx, wdthm, zero);
end.x = add_lim(posx, wdthm, cols);
break;
case PvtX::Right:
end.x = posx;
start.x = sub_lim(end.x, wdth, zero);
break;
}
switch (piv().y) {
case PvtY::Top:
start.y = posy;
end.y = add_lim(start.y, hght, rows);
break;
case PvtY::Center:
start.y = sub_lim(posy, hghtm, zero);
end.y = add_lim(posy, hghtm, rows);
break;
case PvtY::Bottom:
end.y = posy;
start.y = sub_lim(end.y, hght, zero);
break;
}
return place_t(start, end);
}
} // namespace display