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 | d2fc74268ffffdbb80e5f8caeed5ff0adcaa02d1 | 
| parent | 39c67ae3279400b6b07d9659edcc39008f3885b3 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Tue, 11 Feb 2025 17:06:36 +0100 | 
Reshape to fit the dynamic nature
* Layout based class suitable for deriving and runtime polymorphism
* Screen accepts any derivative of Layout
* Window based class now suitable for deriving and runtime polymorphism
* LayoutFree accepts any type of Window
* Streamline resize and render interfaces on all objects
* Relative positions get converted to absolute in render
| M | .clang-tidy | | | + - | 
| M | CMakeLists.txt | | | + - | 
| M | example/example.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++ ----------------------------- | 
| M | include/display/display.hpp | | | ++ | 
| M | include/display/layout.hpp | | | +++++++++++++++++++++++++++++++++++++ ------------ | 
| M | include/display/screen.hpp | | | +++++++++++++++++++ ------- | 
| M | include/display/types.hpp | | | ++++++++++++++++++++++++++++++++ | 
| M | include/display/window.hpp | | | ++++++++++++ --- | 
| M | source/display.cpp | | | +++++ | 
| M | source/layout.cpp | | | +++++++++++++++++++++++++++++++ ----------------- | 
| M | source/screen.cpp | | | +++++++ --------------- | 
| M | source/window.cpp | | | ++++ -- | 
12 files changed, 204 insertions(+), 87 deletions(-)
diff --git a/ .clang-tidy b/ .clang-tidy
          @@ -55,7 +55,7 @@ 
          CheckOptions:
        
        
              value: 'true'
          # These seem to be the most common identifier styles
            - key: 'readability-identifier-naming.AbstractClassCase'
              value: 'lower_case'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.ClassCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.ClassConstantCase'
        
        diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -4,7 +4,7 @@ 
          include(cmake/prelude.cmake)
        
        
          project(
              display
              VERSION 0.1.13
              VERSION 0.1.14
              DESCRIPTION "TUI library"
              HOMEPAGE_URL "https://example.com/"
              LANGUAGES CXX
        
        diff --git a/ example/example.cpp b/ example/example.cpp
@@ -7,7 +7,8 @@
namespace
          {
          int renderer(const display::Window& win, display::place_t plc)
          void renderer(const display::Window& win, display::place_t plc)
          {
            using display::place_t;
          
          @@ -25,25 +26,59 @@ 
          int renderer(const display::Window& win, display::place_t plc)
        
        
            std::cout << std::flush;
            (void)win;
          }
            return 0;
          void recalculator1(display::LayoutFree& layout)
          {
            const auto [width, height] = layout.dim();
            const display::sz_t midw = width / 2;
            const display::sz_t midh = height / 2;
            layout[4]->pos() = {0, 0};
            layout[5]->pos() = {midw, 0};
            layout[6]->pos() = {width, 0};
            layout[7]->pos() = {width, midh};
            layout[0]->pos() = {width, height};
            layout[1]->pos() = {midw, height};
            layout[2]->pos() = {0, height};
            layout[3]->pos() = {0, midh};
            layout[8]->pos() = {midw, midh};
          }
          void recalculator(display::LayoutFree& layout)
          void recalculator2(display::LayoutFree& layout)
          {
            const auto [width, height] = layout.dim();
            const display::sz_t midw = width / 2;
            const display::sz_t midh = height / 2;
            layout[4].pos() = {0, 0};
            layout[5].pos() = {midw, 0};
            layout[6].pos() = {width, 0};
            layout[7].pos() = {width, midh};
            layout[0].pos() = {width, height};
            layout[1].pos() = {midw, height};
            layout[2].pos() = {0, height};
            layout[3].pos() = {0, midh};
            layout[8].pos() = {midw, midh};
            layout[0]->pos() = {0, 0};
            layout[1]->pos() = {midw, 0};
            layout[2]->pos() = {width, 0};
            layout[3]->pos() = {width, midh};
            layout[4]->pos() = {width, height};
            layout[5]->pos() = {midw, height};
            layout[6]->pos() = {0, height};
            layout[7]->pos() = {0, midh};
            layout[8]->pos() = {midw, midh};
          }
          void fill(display::LayoutFree& layout)
          {
            using display::pos_t, display::dim_t, display::piv_t;
            using display::PvtX, display::PvtY;
            using display::Window;
            // 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));
            // clang-format on
          }
          }  // namespace
        
        
          @@ -51,30 +86,19 @@ 
          void recalculator(display::LayoutFree& layout)
        
        
          int main()
          {
            try {
              using display::Display;
              using display::PvtX, display::PvtY;
              using namespace display;  // NOLINT
              auto& display = Display::display();
              auto& screen = display.screen();
              auto& layout = screen.set_layout({recalculator});
              layout.append({renderer, {}, {20, 10}, {PvtX::Left, PvtY::Top}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Center, PvtY::Top}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Right, PvtY::Top}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Right, PvtY::Center}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Right, PvtY::Bottom}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Center, PvtY::Bottom}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Left, PvtY::Bottom}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Left, PvtY::Center}});
              layout.append({renderer, {}, {20, 10}, {PvtX::Center, PvtY::Center}});
              for (display.set_resized(); true;) {
                using display::event;
              auto& layout = display.screen().set_layout<LayoutRigid>(nullptr);
              fill(layout.screen1().set_layout<LayoutFree>(recalculator1));
              fill(layout.screen2().set_layout<LayoutFree>(recalculator2));
              for (display.set_resized(); true;) {
                const auto evnt = display.get_event();
                if (evnt.type() == event::Type::RESIZE) {
                  std::cout << alec::erase_display_v<alec::Motion::WHOLE>;
                  screen.render();
                  display.render();
                  continue;
                }
          diff --git a/ include/display/display.hpp b/ include/display/display.hpp
          @@ -27,6 +27,8 @@ 
          public:
        
        
            void set_resized();
            void reset_resized();
            void render();
          private:
            Display();
            ~Display();
        
        diff --git a/ include/display/layout.hpp b/ include/display/layout.hpp
@@ -1,13 +1,16 @@
#pragma once
          #include <memory>
          #include <vector>
          #include "display/screen.hpp"
          #include "display/types.hpp"
          #include "display/window.hpp"
          namespace display
          {
          class LayoutFree
          class LayoutFree : public Layout
          {
          public:
            using recalc_f = void(LayoutFree&);
        
        
          @@ -17,29 +20,51 @@ 
          public:
        
        
            {
            }
            const pos_t& pos() const { return m_pos; }
            const dim_t& dim() const { return m_dim; }
            Window* operator[](std::size_t idx) { return m_windows[idx].get(); }
            const Window* operator[](std::size_t idx) const
            {
              return m_windows[idx].get();
            }
            const auto& operator[](std::size_t idx) const { return m_windows[idx]; }
            auto& operator[](std::size_t idx) { return m_windows[idx]; }
            void append(Window window);
            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());
            }
            void resize(pos_t pos, dim_t dim);
            int render() const;
            void resize(dim_t dim) override;
            int render(pos_t pos) const override;
          private:
            recalc_f* m_recalc;
            pos_t m_pos;
            dim_t m_dim;
            std::vector<Window> m_windows;
            std::vector<std::unique_ptr<Window>> m_windows;
            mutable bool m_is_sorted = true;
          };
          class LayoutRigid
          class LayoutRigid : public Layout
          {
          public:
            using recalc_f = void(LayoutRigid&);
            LayoutRigid(recalc_f f_recalc)  // NOLINT
                : m_recalc(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
@@ -1,8 +1,7 @@
#pragma once
          #include <optional>
          #include <memory>
          #include "display/layout.hpp"
          #include "display/types.hpp"
          namespace display
        
        
          @@ -11,22 +10,35 @@ 
          namespace display
        
        
          class Screen
          {
          public:
            Screen(dim_t dim)  // NOLINT
            Screen(dim_t dim = {})  // NOLINT
                : m_dim(dim)
            {
            }
            Screen(const Screen&) = delete;
            Screen(Screen&&) = delete;
            Screen& operator=(const Screen&) = delete;
            Screen& operator=(Screen&&) = delete;
            ~Screen() = default;
            const auto& dim() const { return m_dim; }
            LayoutFree& set_layout(LayoutFree layout);
            template<typename T, class... Args>
            T& set_layout(Args&&... args)
            {
              m_layout = std::make_unique<T>(std::forward<Args>(args)...);
              return *dynamic_cast<T*>(m_layout.get());
            }
            void resize(dim_t new_dim);
            void render() const;
            void resize(dim_t dim);
            void render(pos_t pos) const;
          private:
            dim_t m_dim;
            std::optional<LayoutFree> m_layout;
            std::unique_ptr<Layout> m_layout;
          };
          }  // namespace display
        
        diff --git a/ include/display/types.hpp b/ include/display/types.hpp
          @@ -35,6 +35,13 @@ 
          struct pos_t
        
        
            {
            }
            pos_t operator+(pos_t rhs) const
            {
              return {static_cast<std::uint16_t>(x + rhs.x),
                      static_cast<std::uint16_t>(y + rhs.y),
                      static_cast<std::uint16_t>(z + rhs.z)};
            }
            sz_t x;
            sz_t y;
            sz_t z;
        
        
          @@ -48,6 +55,8 @@ 
          struct place_t
        
        
            {
            }
            place_t operator+(pos_t rhs) const { return {start + rhs, end + rhs}; }
            pos_t start;
            pos_t end;
          };
        
        
          @@ -78,4 +87,27 @@ 
          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
@@ -4,12 +4,13 @@
#include "display/types.hpp"
          namespace display {
          namespace display
          {
          class Window
          {
          public:
            using render_f = int(const Window&, place_t place);
            using render_f = void(const Window&, place_t place);
            Window(render_f frender, pos_t pos, dim_t dim, piv_t piv = {})
                : m_renderer(frender)
        
        
          @@ -19,6 +20,14 @@ 
          public:
        
        
            {
            }
            Window(const Window&) = delete;
            Window& operator=(const Window&) = delete;
            Window(Window&&) = default;
            Window& operator=(Window&&) = default;
            virtual ~Window() = default;
            const auto& pos() const { return m_pos; }
            auto& pos() { return m_pos; }
          
          @@ -28,7 +37,7 @@ 
          public:
        
        
            const auto& piv() const { return m_piv; }
            auto& piv() { return m_piv; }
            int render(place_t place) const;
            virtual void render(place_t place) const;
            std::optional<place_t> place(dim_t bounds) const;
          diff --git a/ source/display.cpp b/ source/display.cpp
          @@ -86,4 +86,9 @@ 
          bool Display::get_resized() const
        
        
            return m_is_resized;
          }
          void Display::render()
          {
            m_screen.render({0, 0});
          }
          }  // namespace display
        
        diff --git a/ source/layout.cpp b/ source/layout.cpp
@@ -8,20 +8,16 @@
namespace display
          {
          void LayoutFree::append(Window window)
          void LayoutFree::resize(dim_t dim)
          {
            m_windows.push_back(window);
            m_is_sorted = false;
          }
            this->dim() = dim;
          void LayoutFree::resize(pos_t pos, dim_t dim)
          {
            m_pos = pos;
            m_dim = dim;
            m_recalc(*this);
            if (m_recalc != nullptr) {
              m_recalc(*this);
            }
          }
          int LayoutFree::render() const
          int LayoutFree::render(pos_t pos) const
          {
            static std::vector<std::uint8_t> idxs;
          
          @@ -32,24 +28,42 @@ 
          int LayoutFree::render() const
        
        
                  idxs.begin(),
                  idxs.end(),
                  [&](auto left, auto right)
                  { return m_windows[left].pos().z < m_windows[right].pos().z; });
                  { 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(m_dim);
              const auto& win = m_windows[idx];
              const auto plc = win->place(this->dim());
              if (!plc.has_value()) {
                continue;
              }
              const auto res = win.render(plc.value());
              if (res != 0) {
                return res;
              }
              win->render(plc.value() + pos);
            }
            return 0;
          }
          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;
          }
          diff --git a/ source/screen.cpp b/ source/screen.cpp
@@ -1,28 +1,20 @@
#include "display/screen.hpp"
          #include "display/layout.hpp"
          namespace display
          {
          LayoutFree& Screen::set_layout(LayoutFree layout)
          {
            m_layout.emplace(std::move(layout));
            return m_layout.value();
          }
          void Screen::resize(dim_t new_dim)
          void Screen::resize(dim_t dim)
          {
            m_dim = new_dim;
            if (m_layout.has_value()) {
              m_layout->resize({}, m_dim);
            m_dim = dim;
            if (m_layout != nullptr) {
              m_layout->resize(m_dim);
            }
          }
          void Screen::render() const
          void Screen::render(pos_t pos)  const
          {
            if (m_layout.has_value()) {
              m_layout->render();
            if (m_layout != nullptr) {
              m_layout->render(pos);
            }
          }
          diff --git a/ source/window.cpp b/ source/window.cpp
@@ -5,9 +5,11 @@
namespace display
          {
          int Window::render(place_t place) const
          void Window::render(place_t place) const
          {
            return m_renderer(*this, place);
            if (m_renderer != nullptr) {
              m_renderer(*this, place);
            }
          }
          std::optional<place_t> Window::place(dim_t bounds) const