displayLayout and Rendering TUI library |
git clone git://git.dimitrijedobrota.com/display.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | 47b71b9e97af8d8ff9cae99bf7c8fe2f297ae544 |
parent | acac25d5c637911f5a48637df44028440e1c2ee1 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Sun, 16 Feb 2025 15:03:52 +0100 |
Reengineer everything for better encapsulation * Everything is an Element and has an absolute place (apos + dim) * Get rid of Screen as it's just a Layout * Layout and Layout multy provide template interface for managing other elements * Interface is less verbose for concrete cases * Remove a lot of boilerplate code
Diffstat:M | CMakeLists.txt | | | +---- |
M | example/example.cpp | | | +++++++++++++++++++++++++++++++++++++++------------------------------------------- |
M | example/navig/navig.cpp | | | +++++++++++------------ |
M | include/display/display.hpp | | | ++++++---- |
A | include/display/element.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | include/display/layout.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
D | include/display/layout_free.hpp | | | --------------------------------------------------- |
M | include/display/layout_pivot.hpp | | | +++++++++---------------------------- |
M | include/display/layout_rigid.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
D | include/display/screen.hpp | | | ------------------------------------------------------------ |
M | include/display/types.hpp | | | ++++++++++++++++++++++++ |
M | include/display/window.hpp | | | +++++-------------------------- |
M | include/display/window_pivot.hpp | | | +++++++------------ |
M | source/display.cpp | | | +++++++++---- |
D | source/layout_free.cpp | | | ------------------------------ |
M | source/layout_pivot.cpp | | | ++++++++-------------------- |
D | source/layout_rigid.cpp | | | --------------------------------------------------------------------------------- |
D | source/screen.cpp | | | ------------------------------ |
M | source/window_pivot.cpp | | | +-- |
19 files changed, 400 insertions(+), 494 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)
project(
display
VERSION 0.1.23
VERSION 0.1.24
DESCRIPTION "TUI library"
HOMEPAGE_URL "https://example.com/"
LANGUAGES CXX
@@ -20,11 +20,8 @@ find_package(alec 0.1.13 CONFIG REQUIRED)
add_library(
display_display
source/display.cpp
source/screen.cpp
# ---- utility
source/layout_free.cpp
source/layout_pivot.cpp
source/layout_rigid.cpp
source/window_pivot.cpp
)
target_link_libraries(display_display PUBLIC alec::alec)
diff --git a/example/example.cpp b/example/example.cpp
@@ -4,7 +4,6 @@
#include <alec/alec.hpp>
#include "display/display.hpp"
#include "display/layout_free.hpp"
#include "display/layout_rigid.hpp"
#include "display/window_pivot.hpp"
@@ -14,14 +13,9 @@ namespace
class WindowCustom : public display::WindowPivot
{
public:
WindowCustom(display::apos_t apos,
display::dim_t adim,
display::pos_t pos,
display::dim_t dim,
display::piv_t piv)
: WindowPivot(apos, adim, pos, dim, piv)
WindowCustom(display::aplace_t aplc, display::dim_t dim, display::piv_t piv)
: WindowPivot(aplc, dim, piv)
{
render();
}
void render() const override
@@ -45,49 +39,50 @@ public:
}
};
class LayoutCustom : public display::LayoutFree
class LayoutCustom : public display::LayoutMulti<WindowCustom>
{
public:
LayoutCustom(display::apos_t apos, display::dim_t dim)
: LayoutFree(apos, dim)
explicit LayoutCustom(display::aplace_t aplc)
: LayoutMulti(aplc)
{
using display::pos_t, display::dim_t, display::piv_t;
using display::dim_t, display::piv_t;
using display::PvtX, display::PvtY;
const auto [width, height] = dim;
const display::sz_t midw = width / 2;
const display::sz_t midh = height / 2;
// clang-format off
append<WindowCustom>(pos_t( 0, 0), dim_t(12, 4), piv_t( PvtX::Left, PvtY::Top));
append<WindowCustom>(pos_t(midw, 0), dim_t(12, 4), piv_t(PvtX::Center, PvtY::Top));
append<WindowCustom>(pos_t(width, 0), dim_t(12, 4), piv_t( PvtX::Right, PvtY::Top));
append<WindowCustom>(pos_t(width, midh), dim_t(12, 4), piv_t( PvtX::Right, PvtY::Center));
append<WindowCustom>(pos_t(width, height), dim_t(12, 4), piv_t (PvtX::Right, PvtY::Bottom));
append<WindowCustom>(pos_t(midw, height), dim_t(12, 4), piv_t(PvtX::Center, PvtY::Bottom));
append<WindowCustom>(pos_t( 0, height), dim_t(12, 4), piv_t( PvtX::Left, PvtY::Bottom));
append<WindowCustom>(pos_t( 0, midh), dim_t(12, 4), piv_t( PvtX::Left, PvtY::Center));
append<WindowCustom>(pos_t(midw, midh), dim_t(12, 4), piv_t(PvtX::Center, PvtY::Center));
// clang-format on
append(dim_t(12, 4), piv_t(PvtX::Left, PvtY::Top));
append(dim_t(12, 4), piv_t(PvtX::Center, PvtY::Top));
append(dim_t(12, 4), piv_t(PvtX::Right, PvtY::Top));
append(dim_t(12, 4), piv_t(PvtX::Right, PvtY::Center));
append(dim_t(12, 4), piv_t(PvtX::Right, PvtY::Bottom));
append(dim_t(12, 4), piv_t(PvtX::Center, PvtY::Bottom));
append(dim_t(12, 4), piv_t(PvtX::Left, PvtY::Bottom));
append(dim_t(12, 4), piv_t(PvtX::Left, PvtY::Center));
append(dim_t(12, 4), piv_t(PvtX::Center, PvtY::Center));
recalc();
}
void resize(display::apos_t apos, display::dim_t dim) override
void resize(display::aplace_t aplc) override
{
LayoutFree::resize(apos, dim);
LayoutMulti::resize(aplc);
recalc();
}
const auto [width, height] = dim;
private:
void recalc()
{
const auto [width, height] = adim();
const display::sz_t midw = width / 2;
const display::sz_t midh = height / 2;
get<WindowCustom>(0).pos() = {0, 0};
get<WindowCustom>(1).pos() = {midw, 0};
get<WindowCustom>(2).pos() = {width, 0};
get<WindowCustom>(3).pos() = {width, midh};
get<WindowCustom>(4).pos() = {width, height};
get<WindowCustom>(5).pos() = {midw, height};
get<WindowCustom>(6).pos() = {0, height};
get<WindowCustom>(7).pos() = {0, midh};
get<WindowCustom>(8).pos() = {midw, midh};
get(0).set_pos({0, 0});
get(1).set_pos({midw, 0});
get(2).set_pos({width, 0});
get(3).set_pos({width, midh});
get(4).set_pos({width, height});
get(5).set_pos({midw, height});
get(6).set_pos({0, height});
get(7).set_pos({0, midh});
get(8).set_pos({midw, midh});
}
};
@@ -101,20 +96,21 @@ int main()
auto& display = Display::display();
// clang-format off
const LayoutRigid::layout_t split = {
const LayoutRigid<>::layout_t split = {
{1, 1, 2},
{0, 3, 2},
{4, 3, 2},
};
// clang-format on
auto& layout = display.screen().set_layout<LayoutRigid>(split);
layout[0].set_layout<LayoutCustom>();
layout[1].set_layout<LayoutCustom>();
layout[2].set_layout<LayoutCustom>();
layout[3].set_layout<LayoutCustom>();
layout[4].set_layout<LayoutCustom>();
auto& layout = display.layout().set_child<LayoutRigid<>>(split);
layout.append<LayoutCustom>();
layout.append<LayoutCustom>();
layout.append<LayoutCustom>();
layout.append<LayoutCustom>();
layout.append<LayoutCustom>();
display.render();
while (true) {
const auto evnt = display.get_event();
if (evnt.type() == event::Type::RESIZE) {
diff --git a/example/navig/navig.cpp b/example/navig/navig.cpp
@@ -18,12 +18,10 @@ bool is_finished = false; // NOLINT
class WindowCustom : public display::WindowPivot
{
public:
WindowCustom(display::apos_t apos,
display::dim_t adim,
display::pos_t pos,
WindowCustom(display::aplace_t aplc,
display::piv_t piv,
const example::menu_t& menu)
: WindowPivot(apos, adim, pos, calc_dim(menu), piv)
: WindowPivot(aplc, calc_dim(menu), piv)
, m_menu(menu)
{
}
@@ -75,13 +73,14 @@ public:
for (std::size_t i = 0; i < m_menu.items.size(); i++) {
if (m_selected == i) {
std::cout << alec::foreground_v<alec::Color::GREEN>;
} else {
std::cout << alec::foreground_v<alec::Color::DEFAULT>;
}
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::DEFAULT>;
empty();
std::cout << alec::background_v<alec::Color::DEFAULT>;
@@ -190,7 +189,7 @@ int menu_t::visit(const menu_t& menu)
{
using display::Display, display::LayoutPivot;
auto& layout = Display::display().screen().get_layout<LayoutPivot>();
auto& layout = Display::display().layout().get_child<LayoutPivot>();
static std::stack<const menu_t*> stk;
@@ -200,13 +199,13 @@ int menu_t::visit(const menu_t& menu)
finish(0);
return 0;
}
layout.set_window<WindowCustom>(*stk.top());
layout.set_child<WindowCustom>(*stk.top());
layout.render();
return 0;
}
stk.push(&menu);
layout.set_window<WindowCustom>(menu);
layout.set_child<WindowCustom>(menu);
layout.render();
return 0;
@@ -220,7 +219,7 @@ int main()
using namespace display; // NOLINT
auto& display = Display::display();
display.screen().set_layout<LayoutPivot>(piv_t(PvtX::Center, PvtY::Center));
display.layout().set_child<LayoutPivot>(piv_t(PvtX::Center, PvtY::Center));
example::menu_main(0);
@@ -236,7 +235,7 @@ int main()
if (evnt.key() == 'q') {
break;
}
display.screen().input(evnt);
display.input(evnt);
}
}
} catch (std::exception& err) {
diff --git a/include/display/display.hpp b/include/display/display.hpp
@@ -2,7 +2,7 @@
#include <alec/alec.hpp>
#include "display/screen.hpp"
#include "display/layout.hpp"
namespace display
{
@@ -17,7 +17,8 @@ public:
Display& operator=(const Display&) = delete;
Display& operator=(Display&&) = delete;
Screen& screen() { return m_screen; }
const auto& layout() const { return m_layout; }
auto& layout() { return m_layout; }
event get_event();
@@ -25,7 +26,8 @@ public:
void set_resized();
void reset_resized();
void render();
void render() const;
void input(event& evnt);
private:
Display();
@@ -36,7 +38,7 @@ private:
static void handle_sigwinch(int /* unused */);
static bool is_resize_track;
Screen m_screen;
Layout<Element> m_layout;
bool m_is_resized = false;
};
diff --git a/include/display/element.hpp b/include/display/element.hpp
@@ -0,0 +1,60 @@
#pragma once
#include "display/types.hpp"
namespace display
{
class Element
{
public:
explicit Element(aplace_t aplc)
: m_aplc(aplc)
{
}
Element(const Element&) = delete;
Element& operator=(const Element&) = delete;
Element(Element&&) = default;
Element& operator=(Element&&) = default;
virtual ~Element() = default;
virtual void resize(aplace_t aplc) { m_aplc = aplc; }
virtual void render() const = 0;
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& axpos() const { return apos().x; }
const auto& aypos() const { return apos().y; }
const auto& awth() const { return adim().width; }
const auto& ahgt() const { return adim().height; }
private:
aplace_t m_aplc;
};
class ElementPlace : public Element
{
public:
ElementPlace(aplace_t aplc, place_t plc)
: Element(aplc)
, m_plc(plc)
{
}
const auto& plc() const { return m_plc; }
const auto& pos() const { return plc().pos; }
const auto& dim() const { return plc().dim; }
const auto& xpos() const { return pos().x; }
const auto& ypos() const { return pos().y; }
const auto& wth() const { return dim().width; }
private:
place_t m_plc;
};
} // namespace display
diff --git a/include/display/layout.hpp b/include/display/layout.hpp
@@ -1,41 +1,141 @@
#pragma once
#include <memory>
#include <vector>
#include "display/element.hpp"
#include "display/types.hpp"
namespace display
{
class Layout
template<typename T = Element>
requires(std::is_base_of_v<Element, T>)
class Layout : public Element
{
public:
using ptr_t = std::unique_ptr<T>;
explicit Layout(aplace_t aplc)
: Element(aplc)
{
}
void resize(aplace_t aplc) override
{
Element::resize(aplc);
if (has_child()) {
m_child->resize(aplc);
}
}
void render() const override
{
if (has_child()) {
m_child->render();
}
}
void input(event& evnt) override
{
if (has_child()) {
m_child->input(evnt);
}
}
template<typename M = T, class... Args>
requires(std::is_base_of_v<T, M>)
M& set_child(Args&&... args)
{
m_child = std::make_unique<M>(aplc(), std::forward<Args>(args)...);
return get_child<M>();
}
template<typename M = T>
requires(std::is_base_of_v<T, M>)
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()
{
return *dynamic_cast<M*>(m_child.get());
}
bool has_child() const { return m_child != nullptr; }
private:
ptr_t m_child;
};
template<typename T = Element>
requires(std::is_base_of_v<Element, T>)
class LayoutMulti : public Element
{
public:
explicit Layout(apos_t apos, dim_t dim)
: m_apos(apos)
, m_dim(dim)
using ptr_t = std::unique_ptr<T>;
explicit LayoutMulti(aplace_t aplc)
: Element(aplc)
{
}
Layout(const Layout&) = delete;
Layout& operator=(const Layout&) = delete;
void resize(aplace_t aplc) override
{
Element::resize(aplc);
Layout(Layout&&) = default;
Layout& operator=(Layout&&) = default;
for (std::size_t i = 0; i < size(); i++) {
m_children[i]->resize(place(i));
}
}
virtual ~Layout() = default;
void render() const override
{
for (const auto& child : m_children) {
child->render();
}
}
virtual void resize(apos_t apos, dim_t dim) { m_apos = apos, m_dim = dim; }
virtual void render() const = 0;
virtual void input(event& evnt) = 0;
void input(event& evnt) override
{
for (auto& child : m_children) {
child->input(evnt);
}
}
protected:
const dim_t& dim() const { return m_dim; }
dim_t& dim() { return m_dim; }
template<typename M = T, class... Args>
requires(std::is_base_of_v<T, M>)
M& append(Args&&... args)
{
m_children.emplace_back(std::make_unique<M>(place(m_children.size()),
std::forward<Args>(args)...));
return get<M>(m_children.size() - 1);
}
const apos_t& apos() const { return m_apos; }
apos_t& apos() { return m_apos; }
template<typename M = T>
requires(std::is_base_of_v<T, M>)
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)
{
return *dynamic_cast<M*>(m_children[idx].get());
}
std::size_t size() { return m_children.size(); }
private:
apos_t m_apos;
dim_t m_dim;
virtual aplace_t place(std::size_t /* unused */) const { return aplc(); }
std::vector<ptr_t> m_children;
};
} // namespace display
diff --git a/include/display/layout_free.hpp b/include/display/layout_free.hpp
@@ -1,51 +0,0 @@
#pragma once
#include <memory>
#include <vector>
#include "display/layout.hpp"
#include "display/window.hpp"
namespace display
{
class LayoutFree : public Layout
{
public:
LayoutFree(apos_t apos, dim_t dim)
: Layout(apos, dim)
{
}
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>(apos(), dim(), std::forward<Args>(args)...));
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(apos_t apos, dim_t dim) override;
void render() const override;
void input(event& evnt) override;
private:
std::vector<std::unique_ptr<Window>> m_wins;
};
} // namespace display
diff --git a/include/display/layout_pivot.hpp b/include/display/layout_pivot.hpp
@@ -9,50 +9,31 @@
namespace display
{
class LayoutPivot : public Layout
class LayoutPivot : public Layout<WindowPivot>
{
public:
using ptr_t = std::unique_ptr<WindowPivot>;
LayoutPivot(apos_t apos, dim_t dim, piv_t piv) // NOLINT
: Layout(apos, dim)
LayoutPivot(aplace_t aplc, piv_t piv) // NOLINT
: Layout(aplc)
, m_piv(piv)
{
}
template<typename T, class... Args>
T& set_window(Args&&... args)
template<typename M, class... Args>
M& set_child(Args&&... args)
{
m_window = std::make_unique<T>(
apos(), dim(), get_pos(), m_piv, std::forward<Args>(args)...);
return get_window<T>();
auto& child = Layout::set_child<M>(m_piv, std::forward<Args>(args)...);
child.set_pos(get_pos());
return child;
}
template<typename T>
const T& get_window() const
{
return *dynamic_cast<T*>(m_window.get());
}
template<typename T>
T& get_window()
{
return *dynamic_cast<T*>(m_window.get());
}
void reset_window() { m_window.reset(); }
bool has_window() const { return m_window != nullptr; }
void resize(apos_t apos, dim_t dim) override;
void render() const override;
void input(event& evnt) override;
void resize(aplace_t aplc) override;
private:
pos_t get_pos() const;
piv_t m_piv;
ptr_t m_window;
};
} // namespace display
diff --git a/include/display/layout_rigid.hpp b/include/display/layout_rigid.hpp
@@ -1,43 +1,43 @@
#pragma once
#include <stdexcept>
#include <unordered_set>
#include <vector>
#include "display/screen.hpp"
#include "display/layout.hpp"
#include "display/types.hpp"
namespace display
{
class LayoutRigid : public Layout
template<typename T = Element>
class LayoutRigid : public LayoutMulti<T>
{
public:
using layout_t = std::vector<std::vector<std::uint8_t>>;
LayoutRigid(apos_t apos, dim_t dim, layout_t layout); // NOLINT
const auto& operator[](std::size_t idx) const { return m_recs[idx].screen; }
auto& operator[](std::size_t idx) { return m_recs[idx].screen; }
void resize(apos_t apos, dim_t dim) override;
void render() const override;
void input(event& evnt) override;
LayoutRigid(aplace_t aplc, layout_t layout); // NOLINT
private:
auto calc_width(dim_t share) const;
auto calc_height(dim_t share) const;
std::size_t count_and_pad(layout_t& layout) const;
aplace_t place(std::size_t idx) const override
{
const auto calc = [&]<typename R>(dim_t share) -> R
{
return {static_cast<sz_t>(this->awth() / m_grid.width * share.width),
static_cast<sz_t>(this->ahgt() / m_grid.height * share.height)};
};
const auto start = calc.template operator()<pos_t>(m_recs[idx].start);
const auto dim = calc.template operator()<dim_t>(m_recs[idx].dim);
return {this->apos() + start, dim};
}
dim_t m_grid;
struct record_t
{
record_t()
: screen(apos_t(0, 0), dim_t(0, 0))
{
}
Screen screen;
dim_t start = {0xFFFF, 0xFFFF};
dim_t dim;
};
@@ -45,4 +45,83 @@ private:
std::vector<record_t> m_recs;
};
template<typename T>
LayoutRigid<T>::LayoutRigid(aplace_t aplc, layout_t layout)
: LayoutMulti<T>(aplc)
, m_grid(static_cast<sz_t>(layout[0].size()),
static_cast<sz_t>(layout.size()))
, m_recs(count_and_pad(layout))
{
static const auto& insert =
[](sz_t& count, std::uint8_t cnt, sz_t& pos, std::uint8_t total)
{
if (count != 0 && (pos != total || count != cnt)) {
throw std::runtime_error("Invalid layout [Shape]");
}
if (count != 0) {
return;
}
pos = total;
count = cnt;
};
for (std::size_t i = 0U; i < m_grid.height; i++) {
uint8_t total = 0;
uint8_t cnt = 1;
for (std::size_t j = 0U; j < m_grid.width; j++, cnt++) {
const auto crnt = layout[i][j];
if (crnt == layout[i][j + 1]) {
continue;
}
insert(m_recs[crnt].dim.width, cnt, m_recs[crnt].start.width, total);
total += cnt, cnt = 0;
}
}
for (std::size_t j = 0U; j < m_grid.width; j++) {
uint8_t total = 0;
uint8_t cnt = 1;
for (std::size_t i = 0U; i < m_grid.height; i++, cnt++) {
const auto crnt = layout[i][j];
if (crnt == layout[i + 1][j]) {
continue;
}
insert(m_recs[crnt].dim.height, cnt, m_recs[crnt].start.height, total);
total += cnt, cnt = 0;
}
}
}
template<typename T>
std::size_t LayoutRigid<T>::count_and_pad(layout_t& layout) const
{
std::unordered_set<std::uint8_t> ust;
for (std::size_t i = 0U; i < m_grid.height; i++) {
for (std::size_t j = 0U; j < m_grid.width; j++) {
ust.insert(layout[i][j]);
}
layout[i].emplace_back(0xFF);
}
layout.emplace_back(m_grid.width, 0xFF);
for (std::size_t i = 0U; i < m_grid.height; i++) {
for (std::size_t j = 0U; j < m_grid.width; j++) {
if (layout[i][j] >= ust.size()) {
throw std::runtime_error("Invalid layout [Number]");
}
}
}
return ust.size();
}
} // namespace display
diff --git a/include/display/screen.hpp b/include/display/screen.hpp
@@ -1,60 +0,0 @@
#pragma once
#include <memory>
#include "display/layout.hpp"
#include "display/types.hpp"
namespace display
{
class Screen
{
public:
explicit Screen(apos_t apos, dim_t dim)
: m_apos(apos)
, m_dim(dim)
{
}
Screen(const Screen&) = delete;
Screen& operator=(const Screen&) = delete;
Screen(Screen&&) = default;
Screen& operator=(Screen&&) = default;
~Screen() = default;
template<typename T, class... Args>
T& set_layout(Args&&... args)
{
m_layout = std::make_unique<T>(m_apos, m_dim, 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());
}
bool has_layout() const { return m_layout != nullptr; }
void resize(apos_t apos, dim_t dim);
void render() const;
void input(event& evnt);
private:
apos_t m_apos;
dim_t m_dim;
std::unique_ptr<Layout> m_layout;
};
} // namespace display
diff --git a/include/display/types.hpp b/include/display/types.hpp
@@ -80,6 +80,30 @@ struct apos_t
sz_t y;
};
struct place_t
{
place_t(pos_t posval, dim_t dimval)
: pos(posval)
, dim(dimval)
{
}
pos_t pos;
dim_t dim;
};
struct aplace_t
{
aplace_t(apos_t aposval, dim_t adimval)
: apos(aposval)
, adim(adimval)
{
}
apos_t apos;
dim_t adim;
};
enum class PvtX : std::uint8_t
{
Left,
diff --git a/include/display/window.hpp b/include/display/window.hpp
@@ -1,41 +1,20 @@
#pragma once
#include "display/element.hpp"
#include "display/types.hpp"
namespace display
{
class Window
class Window : public Element
{
public:
Window(apos_t apos, dim_t dim)
: m_apos(apos)
, m_adim(dim)
explicit Window(aplace_t aplc)
: Element(aplc)
{
}
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
Window(Window&&) = default;
Window& operator=(Window&&) = default;
virtual ~Window() = default;
virtual void resize(apos_t apos, dim_t adim) { m_apos = apos, m_adim = adim; }
virtual void render() const = 0;
virtual void input(event& /* unused */) {}
protected:
const auto& apos() const { return m_apos; }
auto& apos() { return m_apos; }
const auto& adim() const { return m_adim; }
auto& adim() { return m_adim; }
private:
apos_t m_apos;
dim_t m_adim;
void input(event& /* unused */) override {}
};
} // namespace display
diff --git a/include/display/window_pivot.hpp b/include/display/window_pivot.hpp
@@ -9,30 +9,25 @@ namespace display
class WindowPivot : public Window
{
public:
WindowPivot(apos_t apos, dim_t adim, pos_t pos, dim_t dim, piv_t piv = {})
: Window(apos, adim)
, m_pos(pos)
, m_dim(dim)
WindowPivot(aplace_t aplc, dim_t dim, piv_t piv)
: Window(aplc)
, m_piv(piv)
, m_dim(dim)
{
}
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; }
void set_pos(pos_t pos) { m_pos = pos; }
protected:
std::tuple<apos_t, dim_t> place() const;
private:
pos_t m_pos;
dim_t m_dim;
piv_t m_piv;
dim_t m_dim;
pos_t m_pos;
};
} // namespace display
diff --git a/source/display.cpp b/source/display.cpp
@@ -28,7 +28,7 @@ Display& Display::display()
}
Display::Display()
: m_screen(apos_t(0, 0), alec::get_screen_size())
: m_layout(aplace_t(apos_t(0, 0), alec::get_screen_size()))
{
struct sigaction old_sig_action = {};
sigaction(SIGWINCH, nullptr, &old_sig_action);
@@ -90,12 +90,17 @@ bool Display::get_resized() const
void Display::resize()
{
m_screen.resize(apos_t(0, 0), alec::get_screen_size());
m_layout.resize(aplace_t(apos_t(0, 0), alec::get_screen_size()));
}
void Display::render()
void Display::render() const
{
m_screen.render();
m_layout.render();
}
void Display::input(event& evnt)
{
m_layout.input(evnt);
}
} // namespace display
diff --git a/source/layout_free.cpp b/source/layout_free.cpp
@@ -1,30 +0,0 @@
#include "display/layout_free.hpp"
namespace display
{
void LayoutFree::resize(apos_t apos, dim_t dim)
{
for (const auto& win : m_wins) {
win->resize(apos, dim);
}
}
void LayoutFree::render() const
{
for (const auto& win : m_wins) {
win->render();
}
}
void LayoutFree::input(event& evnt)
{
for (const auto& win : m_wins) {
win->input(evnt);
if (evnt.type() == event::Type::NONE) {
break;
}
}
}
} // namespace display
diff --git a/source/layout_pivot.cpp b/source/layout_pivot.cpp
@@ -1,35 +1,23 @@
#include "display/layout_pivot.hpp"
#include "display/window_pivot.hpp"
namespace display
{
void LayoutPivot::resize(apos_t apos, dim_t dim)
void LayoutPivot::resize(aplace_t aplc)
{
Layout::resize(apos, dim);
Layout::resize(aplc);
if (has_window()) {
m_window->pos() = get_pos();
m_window->resize(apos, dim);
}
}
void LayoutPivot::render() const
{
if (has_window()) {
m_window->render();
}
}
void LayoutPivot::input(event& evnt)
{
if (has_window()) {
m_window->input(evnt);
if (has_child()) {
get_child().set_pos(get_pos());
get_child().resize(aplc);
}
}
pos_t LayoutPivot::get_pos() const
{
const auto [width, height] = dim();
const auto [width, height] = adim();
const display::sz_t midw = width / 2;
const display::sz_t midh = height / 2;
diff --git a/source/layout_rigid.cpp b/source/layout_rigid.cpp
@@ -1,127 +0,0 @@
#include <stdexcept>
#include <unordered_set>
#include "display/layout_rigid.hpp"
namespace display
{
LayoutRigid::LayoutRigid(apos_t apos, dim_t dim, layout_t layout)
: Layout(apos, dim)
, m_grid(static_cast<sz_t>(layout[0].size()),
static_cast<sz_t>(layout.size()))
, m_recs(count_and_pad(layout))
{
static const auto& insert =
[](sz_t& count, std::uint8_t cnt, sz_t& pos, std::uint8_t total)
{
if (count != 0 && (pos != total || count != cnt)) {
throw std::runtime_error("Invalid layout [Shape]");
}
if (count != 0) {
return;
}
pos = total;
count = cnt;
};
for (std::size_t i = 0U; i < m_grid.height; i++) {
uint8_t total = 0;
uint8_t cnt = 1;
for (std::size_t j = 0U; j < m_grid.width; j++, cnt++) {
const auto crnt = layout[i][j];
if (crnt == layout[i][j + 1]) {
continue;
}
insert(m_recs[crnt].dim.width, cnt, m_recs[crnt].start.width, total);
total += cnt, cnt = 0;
}
}
for (std::size_t j = 0U; j < m_grid.width; j++) {
uint8_t total = 0;
uint8_t cnt = 1;
for (std::size_t i = 0U; i < m_grid.height; i++, cnt++) {
const auto crnt = layout[i][j];
if (crnt == layout[i + 1][j]) {
continue;
}
insert(m_recs[crnt].dim.height, cnt, m_recs[crnt].start.height, total);
total += cnt, cnt = 0;
}
}
LayoutRigid::resize(apos, dim);
}
std::size_t LayoutRigid::count_and_pad(layout_t& layout) const
{
std::unordered_set<std::uint8_t> ust;
for (std::size_t i = 0U; i < m_grid.height; i++) {
for (std::size_t j = 0U; j < m_grid.width; j++) {
ust.insert(layout[i][j]);
}
layout[i].emplace_back(0xFF);
}
layout.emplace_back(m_grid.width, 0xFF);
for (std::size_t i = 0U; i < m_grid.height; i++) {
for (std::size_t j = 0U; j < m_grid.width; j++) {
if (layout[i][j] >= ust.size()) {
throw std::runtime_error("Invalid layout [Number]");
}
}
}
return ust.size();
}
auto LayoutRigid::calc_width(dim_t share) const
{
return static_cast<sz_t>(dim().width / m_grid.width * share.width);
}
auto LayoutRigid::calc_height(dim_t share) const
{
return static_cast<sz_t>(dim().height / m_grid.height * share.height);
}
void LayoutRigid::resize(apos_t apos, dim_t dim)
{
Layout::resize(apos, dim);
for (auto& [screen, start, rdim] : m_recs) {
const dim_t size = {calc_width(rdim), calc_height(rdim)};
const pos_t rel = {calc_width(start), calc_height(start)};
const apos_t abs = this->apos() + rel;
screen.resize(abs, size);
}
}
void LayoutRigid::render() const
{
for (const auto& [screen, _, _1] : m_recs) {
screen.render();
}
}
void LayoutRigid::input(event& evnt)
{
for (auto& [screen, _, _1] : m_recs) {
screen.input(evnt);
if (evnt.type() == event::Type::NONE) {
break;
}
}
}
} // namespace display
diff --git a/source/screen.cpp b/source/screen.cpp
@@ -1,30 +0,0 @@
#include "display/screen.hpp"
namespace display
{
void Screen::resize(apos_t apos, dim_t dim)
{
m_apos = apos;
m_dim = dim;
if (has_layout()) {
m_layout->resize(apos, m_dim);
}
}
void Screen::render() const
{
if (has_layout()) {
m_layout->render();
}
}
void Screen::input(event& evnt)
{
if (has_layout()) {
m_layout->input(evnt);
}
}
} // namespace display
diff --git a/source/window_pivot.cpp b/source/window_pivot.cpp
@@ -4,11 +4,10 @@
namespace display
{
std::tuple<apos_t, dim_t> WindowPivot::place() const
{
const auto [cols, rows] = adim();
const auto [posx, posy] = pos();
const auto [posx, posy] = m_pos;
if (posx > cols || posy > rows) {
return {{0, 0}, {0, 0}};