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 | 2479b140bae99f942c5f3ea57bffa321a9e42c6d | 
| parent | f01eb04eefe3ac6e000b5ef7ea6ae635c1b02f01 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Wed, 12 Feb 2025 11:12:07 +0100 | 
Separate into multiple files, extract WindowPivot
| 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