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 | 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
| 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}};