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 | 5bc32ab08d18730c06503b91484cc3571d2a43da | 
| parent | 714563ba3fb520f00631d52a401213ddd47e887b | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Fri, 21 Feb 2025 17:18:05 +0100 | 
Big refactor for type safety
* val_t template struct that holds a single value (x, y, width, height)
* No implicit constructions or conversions
* All operations are well defined and intuitive
* Bugprone parts must be very explicit
* place_t renamed to plc_t, padd_t to pad_t
* layout_rigid constructor split into two functions
| M | CMakeLists.txt | | | + - | 
| M | example/example.cpp | | | ++++++++++++++++++++++++++++++++++++ --------------------------------- | 
| M | example/navig/navig.cpp | | | +++ ---- | 
| M | include/display/element.hpp | | | +++++++++++++ ----------- | 
| M | include/display/layout.hpp | | | +++++ ----- | 
| M | include/display/layout_rigid.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++ --------------------------------- | 
| M | include/display/types.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ------------------------ | 
| M | include/display/utility.hpp | | | ------ | 
| M | include/display/window.hpp | | | ++++++++++ --------- | 
| M | include/display/window_pivot.hpp | | | +++ --- | 
| M | source/display.cpp | | | ++ -- | 
| M | source/element.cpp | | | ++++++++++++++ --------- | 
| M | source/window.cpp | | | ++++++++++++++++++ ------------------ | 
| M | source/window_pivot.cpp | | | +++++++++++++++++ ------------------------ | 
| M | test/source/utility_test.cpp | | | +++ -- | 
15 files changed, 340 insertions(+), 237 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -4,7 +4,7 @@ 
          include(cmake/prelude.cmake)
        
        
          project(
              display
              VERSION 0.1.36
              VERSION 0.1.37
              DESCRIPTION "TUI library"
              HOMEPAGE_URL "git://git.dimitrijedobrota.com/display.git"
              LANGUAGES CXX
        
        diff --git a/ example/example.cpp b/ example/example.cpp
          @@ -14,8 +14,8 @@ 
          using namespace display;  // NOLINT
        
        
          class WindowCustom : public WindowPivot
          {
          public:
            explicit WindowCustom(place_t aplc, piv_t piv, dim_t dim)
                : WindowPivot(aplc, {0, 0}, piv, dim)
            explicit WindowCustom(plc_t aplc, piv_t piv, dim_t dim)
                : WindowPivot(aplc, pad_t(0, 0), piv, dim)
            {
            }
          
          @@ -27,7 +27,7 @@ 
          public:
        
        
              std::cout << alec::background(color_red, 65, 65);
              line_reset();
              for (std::size_t i = 0; i < hgt(); i++) {
              for (auto i = hgt_t(0); i < hgt(); i++) {
                line_empty();
              }
          
          @@ -39,7 +39,7 @@ 
          public:
        
        
          class LayoutCustom : public LayoutRigid<Layout<WindowCustom>>
          {
          public:
            explicit LayoutCustom(place_t aplc)
            explicit LayoutCustom(plc_t aplc)
                : LayoutRigid(aplc, {{0, 1, 2}, {7, 8, 3}, {6, 5, 4}})
            {
              append().set_child(piv_t(PvtX::Left, PvtY::Top), dim_t(12, 4));
        
        
          @@ -57,19 +57,19 @@ 
          public:
        
        
          class LayoutRigidBorder : public LayoutRigid<LayoutCustom>
          {
          public:
            LayoutRigidBorder(place_t aplc, const layout_t& layout)
            LayoutRigidBorder(plc_t aplc, const layout_t& layout)
                : LayoutRigid<LayoutCustom>(aplc, layout)
            {
              auto [m, n] = get_grid();
              const auto [m, n] = get_grid();
              const auto valid = [&](std::size_t xpos, std::size_t ypos)
              { return xpos >= 0 && xpos < n && ypos >= 0 && ypos < m; };
              { return xpos >= 0 && xpos < n.value() && ypos >= 0 && ypos < m.value(); };
              const auto get = [&](std::size_t xpos, std::size_t ypos) -> std::uint8_t
              { return valid(xpos, ypos) ? layout[xpos][ypos] : 0xFF; };
              for (std::size_t i = 0; i <= n; i++) {
                for (std::size_t j = 0; j <= m; j++) {
              for (std::size_t i = 0; i <= n.value(); i++) {
                for (std::size_t j = 0; j <= m.value(); j++) {
                  const std::uint8_t ptl = get(i - 1, j - 1);
                  const std::uint8_t ptr = get(i - 1, j);
                  const std::uint8_t pbl = get(i, j - 1);
        
        
          @@ -81,7 +81,7 @@ 
          public:
        
        
                  mask |= ((pbr != pbl) ? 1U : 0U) << 2U;  // Bottom
                  mask |= ((pbl != ptl) ? 1U : 0U) << 3U;  // Left
                  m_corners.emplace_back(mask, i, j);
                  m_corners.emplace_back(mask, wth_t(j), hgt_t(i));
                }
              }
            }
        
        
          @@ -93,7 +93,7 @@ 
          public:
        
        
                  place(size()), std::forward<Args>(args)...);
            }
            void resize(place_t aplc) override
            void resize(plc_t aplc) override
            {
              LayoutRigid<LayoutCustom>::resize(aplc);
          
          @@ -108,41 +108,44 @@ 
          public:
        
        
              for (std::size_t i = 0; i < size(); i++) {
                const auto [pos, dim] = LayoutRigid<LayoutCustom>::place(i);
                set_cursor(pos.y, pos.x + 1);
                for (sz_t j = 1; j < dim.width; j++) {
                // Top of each element
                set_cursor(pos.x + 1, pos.y);
                for (auto j = wth_t(1); j < dim.width; j++) {
                  std::cout << "─";
                }
                for (sz_t j = pos.y + 1; j < pos.y + dim.height; j++) {
                  set_cursor(j, pos.x) << "│";
                // Left of each element
                for (auto j = pos.y + 1; j < pos.y + dim.height; j++) {
                  set_cursor(pos.x, j) << "│";
                }
              }
              for (sz_t i = aypos() + 1; i < aypos() + ahgt(); i++) {
                set_cursor(i, axpos() + awth() - 1) << "│";
              // Right of the layout
              for (auto j = aypos() + 1; j < aypos() + ahgt(); j++) {
                set_cursor(axpos() + awth() - 1, j) << "│";
              }
              set_cursor(aypos() + ahgt() - 1, axpos() + 1);
              for (sz_t i = 2; i < awth(); i++) {
              // Bottom of the layout
              set_cursor(axpos() + 1, aypos() + ahgt() - 1);
              for (auto i = wth_t(2); i < awth(); i++) {
                std::cout << "─";
              }
              auto [m, n] = get_grid();
              const auto [m, n] = get_grid();
              const auto [w, h] = adim();
              const sz_t unw = w / m;
              const sz_t unh = h / n;
              for (const auto [mask, xpos, ypos] : m_corners) {
                const sz_t xloc = xpos != n ? xpos * unw : axpos() + w;
                const sz_t yloc = ypos != m ? ypos * unh : aypos() + h;
                set_cursor(yloc, xloc) << corner_t::lookup[mask];  // NOLINT
              const auto unw = w / m;
              const auto unh = h / n;
              for (const auto [mask, wth, hgt] : m_corners) {
                const auto xloc = wth != m ? xpos_t((wth * unw).value()) : axpos() + w;
                const auto yloc = hgt != n ? ypos_t((hgt * unh).value()) : aypos() + h;
                set_cursor(xloc, yloc) << corner_t::lookup[mask];  // NOLINT
              };
              std::cout << std::flush;
            }
          private:
            place_t place(std::size_t idx)
            plc_t place(std::size_t idx)
            {
              const auto [pos, dim] = LayoutRigid<LayoutCustom>::place(idx);
              dim_t sub = {1, 1};
        
        
          @@ -167,16 +170,16 @@ 
          private:
        
        
          	};
              // clang-format on
              corner_t(std::uint8_t mask, sz_t ypos, sz_t xpos)  // NOLINT
              corner_t(std::uint8_t mask, wth_t wigth, hgt_t height)  // NOLINT
                  : mask(mask)
                  , x(xpos)
                  , y(ypos)
                  , wigth(wigth)
                  , height(height)
              {
              }
              std::uint8_t mask;
              sz_t x;
              sz_t y;
              wth_t wigth;
              hgt_t height;
            };
            std::vector<corner_t> m_corners;
        
        diff --git a/ example/navig/navig.cpp b/ example/navig/navig.cpp
          @@ -19,10 +19,10 @@ 
          using display::WindowPivot;
        
        
          class WindowCustom : public WindowPivot
          {
          public:
            WindowCustom(display::place_t aplc,
            WindowCustom(display::plc_t aplc,
                         display::piv_t piv,
                         const example::menu_t& menu)
                : WindowPivot(aplc, {4, 5, 2, 4}, piv, calc_dim(menu))
                : WindowPivot(aplc, {4, 2, 5, 4}, piv, calc_dim(menu))
                , m_menu(menu)
            {
            }
        
        
          @@ -98,8 +98,7 @@ 
          private:
        
        
                width = std::max(width, item.prompt.size());
              }
              return {static_cast<display::sz_t>(width),
                      static_cast<display::sz_t>(menu.items.size() + 2)};
              return {display::wth_t(width), display::hgt_t(menu.items.size() + 2)};
            }
            example::menu_t m_menu;
        
        diff --git a/ include/display/element.hpp b/ include/display/element.hpp
          @@ -7,7 +7,7 @@ 
          namespace display
        
        
          class Element
          {
          public:
            explicit Element(place_t aplc)
            explicit Element(plc_t aplc)
                : m_aplc(aplc)
            {
            }
        
        
          @@ -20,24 +20,26 @@ 
          public:
        
        
            virtual ~Element() = default;
            virtual void resize(place_t aplc) { m_aplc = aplc; }
            virtual void resize(plc_t aplc) { m_aplc = aplc; }
            virtual void render() const = 0;
            virtual void clear() const = 0;
            virtual void input(event& evnt) = 0;
            static std::ostream& set_cursor(sz_t posy, sz_t posx);
            static std::ostream& set_cursor(xpos_t xpos, ypos_t ypos);
            static std::ostream& set_cursor(pos_t pos);
            void render_border() const;
            const auto& aplc() const { return m_aplc; }
            const auto& apos() const { return aplc().pos; }
            const auto& adim() const { return aplc().dim; }
            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; }
            plc_t aplc() const { return m_aplc; }
            pos_t apos() const { return aplc().pos; }
            dim_t adim() const { return aplc().dim; }
            xpos_t axpos() const { return apos().x; }
            ypos_t aypos() const { return apos().y; }
            wth_t awth() const { return adim().width; }
            hgt_t ahgt() const { return adim().height; }
          private:
            place_t m_aplc;
            plc_t m_aplc;
          };
          }  // namespace display
        
        diff --git a/ include/display/layout.hpp b/ include/display/layout.hpp
          @@ -16,12 +16,12 @@ 
          class Layout : public Element
        
        
          public:
            using ptr_t = std::unique_ptr<T>;
            explicit Layout(place_t aplc)
            explicit Layout(plc_t aplc)
                : Element(aplc)
            {
            }
            void resize(place_t aplc) override
            void resize(plc_t aplc) override
            {
              Element::resize(aplc);
          
          @@ -87,12 +87,12 @@ 
          class LayoutMulti : public Element
        
        
          public:
            using ptr_t = std::unique_ptr<T>;
            explicit LayoutMulti(place_t aplc)
            explicit LayoutMulti(plc_t aplc)
                : Element(aplc)
            {
            }
            void resize(place_t aplc) override
            void resize(plc_t aplc) override
            {
              Element::resize(aplc);
          
          @@ -151,7 +151,7 @@ 
          public:
        
        
          protected:
            template<typename M = T, class... Args>
              requires(std::is_base_of_v<T, M>)
            M& append(place_t aplc, Args&&... args)
            M& append(plc_t aplc, Args&&... args)
            {
              m_children.emplace_back(
                  std::make_unique<M>(aplc, std::forward<Args>(args)...));
        
        diff --git a/ include/display/layout_rigid.hpp b/ include/display/layout_rigid.hpp
          @@ -16,7 +16,7 @@ 
          class LayoutRigid : public LayoutMulti<T>
        
        
          public:
            using layout_t = std::vector<std::vector<std::uint8_t>>;
            LayoutRigid(place_t aplc, layout_t layout);
            LayoutRigid(plc_t aplc, layout_t layout);
            template<typename M = T, class... Args>
              requires(std::is_base_of_v<T, M>)
        
        
          @@ -26,7 +26,7 @@ 
          public:
        
        
                                                        std::forward<Args>(args)...);
            }
            void resize(place_t aplc) override
            void resize(plc_t aplc) override
            {
              LayoutMulti<T>::resize(aplc);
          
          @@ -36,21 +36,25 @@ 
          public:
        
        
            }
          protected:
            place_t place(std::size_t idx) const
            plc_t place(std::size_t idx) const
            {
              const auto [m, n] = m_grid;
              const auto [w, h] = this->adim();
              const sz_t unw = w / m;
              const sz_t unh = h / n;
              const auto unw = w / m;
              const auto unh = h / n;
              const auto calc = [&]<typename R>(const R share, bool addw, bool addh) -> R
              {
                const auto [sw, sh] = share;
                const sz_t width = addw ? w - (unw * (m - sw)) : unw * sw;
                const sz_t height = addh ? h - (unh * (n - sh)) : unh * sh;
                const auto wth = addw ? w - (unw * (m - sw.value())) : unw * sw.value();
                const auto hgt = addh ? h - (unh * (n - sh.value())) : unh * sh.value();
                return {width, height};
                if constexpr (std::is_same_v<R, pos_t>) {
                  return {xpos_t(wth.value()), ypos_t(hgt.value())};
                } else {
                  return {wth, hgt};
                }
              };
              const auto start = calc(m_recs[idx].start, false, false);
        
        
          @@ -63,47 +67,65 @@ 
          protected:
        
        
            struct record_t
            {
              pos_t start;
              dim_t dim;
              pos_t start = {0, 0};
              dim_t dim = {0, 0};
              bool addw = false;
              bool addh = false;
            };
          private:
            std::size_t count_and_pad(layout_t& layout) const;
            void handle_cols(const layout_t& layout);
            void handle_rows(const layout_t& layout);
            dim_t m_grid;
            std::vector<record_t> m_recs;
          };
          template<typename T>
          LayoutRigid<T>::LayoutRigid(place_t aplc, layout_t layout)
          LayoutRigid<T>::LayoutRigid(plc_t aplc, layout_t layout)
              : LayoutMulti<T>(aplc)
              , m_grid(static_cast<sz_t>(layout[0].size()),
                       static_cast<sz_t>(layout.size()))
              , m_grid(wth_t(layout[0].size()), hgt_t(layout.size()))
              , m_recs(count_and_pad(layout))
          {
            static const auto& insert = [](sz_t& count, sz_t cnt, sz_t& pos, sz_t total)
            {
              if (count != 0 && (pos != total || count != cnt)) {
                throw std::runtime_error("Invalid layout [Shape]");
            handle_cols(layout);
            handle_rows(layout);
          }
          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.value(); i++) {
              for (std::size_t j = 0U; j < m_grid.width.value(); j++) {
                ust.insert(layout[i][j]);
              }
              layout[i].emplace_back(0xFF);
            }
            layout.emplace_back(m_grid.width.value(), 0xFF);
              if (count != 0) {
                return;
            for (std::size_t i = 0U; i < m_grid.height.value(); i++) {
              for (std::size_t j = 0U; j < m_grid.width.value(); j++) {
                if (layout[i][j] >= ust.size()) {
                  throw std::runtime_error("Invalid layout [Number]");
                }
              }
            }
              pos = total;
              count = cnt;
            };
            return ust.size();
          }
            static const sz_t one = 1;
          template<typename T>
          void LayoutRigid<T>::handle_cols(const layout_t& layout)
          {
            const auto [m, n] = get_grid();
            for (sz_t i = 0U; i < m_grid.height; i++) {
              sz_t cnt = 1;
            for (std::size_t i = 0U; i < n.value(); i++) {
              m_recs[layout[i][m.value() - 1]].addw = true;
              m_recs[layout[i][m_grid.width - 1]].addw = true;
              for (sz_t j = 0U; j < m_grid.width; j++) {
              auto cnt = wth_t(1);
              for (std::size_t j = 0; j < m.value(); j++) {
                const auto crnt = layout[i][j];
                if (crnt == layout[i][j + 1]) {
        
        
          @@ -111,16 +133,34 @@ 
          LayoutRigid<T>::LayoutRigid(place_t aplc, layout_t layout)
        
        
                  continue;
                }
                insert(m_recs[crnt].dim.width, cnt, m_recs[crnt].start.x, j - cnt + one);
                cnt = 1;
                auto& count = m_recs[crnt].dim.width;
                auto& pos = m_recs[crnt].start.x;
                const auto total = xpos_t(j) - cnt + 1;
                if (count.value() != 0) {
                  if (pos != total || count != cnt) {
                    throw std::runtime_error("Invalid layout [Shape Col]");
                  }
                } else {
                  pos = total;
                  count = cnt;
                }
                cnt = wth_t(1);
              }
            }
          }
          template<typename T>
          void LayoutRigid<T>::handle_rows(const layout_t& layout)
          {
            const auto [m, n] = get_grid();
            for (sz_t j = 0U; j < m_grid.width; j++) {
              sz_t cnt = 1;
            for (std::size_t j = 0U; j < m.value(); j++) {
              m_recs[layout[n.value() - 1][j]].addh = true;
              m_recs[layout[m_grid.height - 1][j]].addh = true;
              for (sz_t i = 0U; i < m_grid.height; i++) {
              auto cnt = hgt_t(1);
              for (std::size_t i = 0; i < n.value(); i++) {
                const auto crnt = layout[i][j];
                if (crnt == layout[i + 1][j]) {
        
        
          @@ -128,34 +168,22 @@ 
          LayoutRigid<T>::LayoutRigid(place_t aplc, layout_t layout)
        
        
                  continue;
                }
                insert(m_recs[crnt].dim.height, cnt, m_recs[crnt].start.y, i - cnt + one);
                cnt = 1;
              }
            }
          }
          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]");
                auto& count = m_recs[crnt].dim.height;
                auto& pos = m_recs[crnt].start.y;
                const auto total = ypos_t(i) - cnt + 1;
                if (count.value() != 0) {
                  if (pos != total || count != cnt) {
                    throw std::runtime_error("Invalid layout [Shape Row]");
                  }
                } else {
                  pos = total;
                  count = cnt;
                }
                cnt = hgt_t(1);
              }
            }
            return ust.size();
          }
          }  // namespace display
        
        diff --git a/ include/display/types.hpp b/ include/display/types.hpp
@@ -1,6 +1,7 @@
#pragma once
          #include <cstdint>
          #include <iostream>
          #include <utility>
          #include <alec/alec.hpp>
        
        
          @@ -10,51 +11,95 @@ 
          namespace display
        
        
          using event = alec::event;
          using sz_t = std::uint16_t;
          struct padd_t
          template<char C>
          struct val_t
          {
            padd_t()
                : padd_t(0, 0, 0, 0)
            using sz_t = std::uint16_t;
            explicit val_t(std::size_t xpos)
                : v(static_cast<sz_t>(xpos))
            {
            }
            padd_t(sz_t width, sz_t height)  // NOLINT
                : padd_t(width, width, height, height)
            explicit val_t(int xpos)
                : v(static_cast<sz_t>(xpos))
            {
            }
            padd_t(sz_t leftpos, sz_t rightpos, sz_t toppos, sz_t bottompos)  // NOLINT
                : left(leftpos)
                , right(rightpos)
                , top(toppos)
                , bottom(bottompos)
            explicit val_t(sz_t xpos)  // NOLINT
                : v(xpos)
            {
            }
            sz_t width() const { return left + right; }
            sz_t height() const { return top + bottom; }
            auto value() const { return v; }
            auto& value() { return v; }
            // clang-format off
            val_t operator+(val_t rhs) const { return val_t(static_cast<sz_t>(v + rhs.v)); }
            val_t operator-(val_t rhs) const { return val_t(static_cast<sz_t>(v - rhs.v)); }
            val_t operator*(val_t rhs) const { return val_t(static_cast<sz_t>(v * rhs.v)); }
            val_t operator/(val_t rhs) const { return val_t(static_cast<sz_t>(v / rhs.v)); }
            val_t operator+(int rhs) const { return val_t(static_cast<sz_t>(v + rhs)); }
            val_t operator-(int rhs) const { return val_t(static_cast<sz_t>(v - rhs)); }
            val_t operator*(int rhs) const { return val_t(static_cast<sz_t>(v * rhs)); }
            val_t operator/(int rhs) const { return val_t(static_cast<sz_t>(v / rhs)); }
            auto& operator+=(val_t rhs) { v += rhs.v; return *this; }
            auto& operator-=(val_t rhs) { v -= rhs.v; return *this; }
            auto& operator*=(val_t rhs) { v *= rhs.v; return *this; }
            auto& operator/=(val_t rhs) { v /= rhs.v; return *this; }
            auto& operator+=(int rhs) { v += rhs; return *this; }
            auto& operator-=(int rhs) { v -= rhs; return *this; }
            auto& operator*=(int rhs) { v *= rhs; return *this; }
            auto& operator/=(int rhs) { v /= rhs; return *this; }
            auto& operator++() { return *this += 1; }
            auto& operator--() { return *this -= 1; }
            val_t operator++(int) { return val_t(v++); }
            val_t operator--(int) { return val_t(v--); }
            auto operator<=>(const val_t&) const = default;
            sz_t left;
            sz_t right;
            sz_t top;
            sz_t bottom;
            friend std::ostream& operator<<(std::ostream& ost, val_t cord) {
                return ost << cord.value();
            }
            // clang-format on
          private:
            sz_t v;  // NOLINT
          };
          using xpos_t = val_t<'x'>;
          using ypos_t = val_t<'y'>;
          using wth_t = val_t<'w'>;
          using hgt_t = val_t<'h'>;
          // clang-format off
          inline xpos_t operator+(xpos_t lhs, wth_t rhs) { return xpos_t(lhs.value() + rhs.value()); }
          inline xpos_t operator-(xpos_t lhs, wth_t rhs) { return xpos_t(lhs.value() - rhs.value()); }
          inline ypos_t operator+(ypos_t lhs, hgt_t rhs) { return ypos_t(lhs.value() + rhs.value()); }
          inline ypos_t operator-(ypos_t lhs, hgt_t rhs) { return ypos_t(lhs.value() - rhs.value()); }
          // clang-format on
          struct dim_t
          {
            dim_t()
                : dim_t(0, 0)
            dim_t(int wdth, int hght)  // NOLINT
                : width(wdth)
                , height(hght)
            {
            }
            dim_t(sz_t wdth, sz_t hght)  // NOLINT
            dim_t(wth_t wdth, hgt_t hght)  // NOLINT
                : width(wdth)
                , height(hght)
            {
            }
            dim_t(std::pair<sz_t, sz_t> pair)  // NOLINT
            dim_t(std::pair<std::uint16_t, std::uint16_t> pair)  // NOLINT
                : width(std::get<0>(pair))
                , height(std::get<1>(pair))
            {
        
        
          @@ -62,36 +107,70 @@ 
          struct dim_t
        
        
            dim_t operator+(dim_t rhs) const
            {
              return {
                  static_cast<sz_t>(width + rhs.width),
                  static_cast<sz_t>(height + rhs.height),
              };
              return {width + rhs.width, height + rhs.height};
            }
            dim_t operator-(dim_t rhs) const
            {
              return {
                  static_cast<sz_t>(width - rhs.width),
                  static_cast<sz_t>(height - rhs.height),
              };
              return {width - rhs.width, height - rhs.height};
            }
            wth_t width;
            hgt_t height;
          };
          struct pad_t
          {
            pad_t(int width, int height)  // NOLINT
                : pad_t(width, height, width, height)
            {
            }
            dim_t operator+(padd_t rhs) const
            pad_t(int leftpos,  // NOLINT
                  int toppos,
                  int rightpos,
                  int bottompos)
                : left(leftpos)
                , right(rightpos)
                , top(toppos)
                , bottom(bottompos)
            {
              return {
                  static_cast<sz_t>(width + rhs.width()),
                  static_cast<sz_t>(height + rhs.height()),
              };
            }
            sz_t width;
            sz_t height;
            pad_t(wth_t width, hgt_t height)  // NOLINT
                : pad_t(width, height, width, height)
            {
            }
            pad_t(wth_t leftpos, hgt_t toppos, wth_t rightpos, hgt_t bottompos)
                : left(leftpos)
                , right(rightpos)
                , top(toppos)
                , bottom(bottompos)
            {
            }
            wth_t width() const { return left + right; }
            hgt_t height() const { return top + bottom; }
            friend dim_t operator+(dim_t dim, pad_t rhs)
            {
              return {dim.width + rhs.width(), dim.height + rhs.height()};
            }
            wth_t left;
            wth_t right;
            hgt_t top;
            hgt_t bottom;
          };
          struct pos_t
          {
            pos_t()
                : pos_t(0, 0)
            using sz_t = std::uint16_t;
            pos_t(int xpos, int ypos)
                : x(xpos)
                , y(ypos)
            {
            }
          
          @@ -101,29 +180,27 @@ 
          struct pos_t
        
        
            {
            }
            pos_t operator+(pos_t rhs) const
            pos_t(xpos_t xpos, ypos_t ypos)
                : x(xpos)
                , y(ypos)
            {
              return {
                  static_cast<sz_t>(x + rhs.x),
                  static_cast<sz_t>(y + rhs.y),
              };
            }
            pos_t operator+(pos_t rhs) const { return {x + rhs.x, y + rhs.y}; }
            dim_t operator-(pos_t rhs) const
            {
              return {
                  static_cast<sz_t>(x - rhs.x),
                  static_cast<sz_t>(y - rhs.y),
              };
              return {wth_t((x - rhs.x).value()), hgt_t((y - rhs.y).value())};
            }
            sz_t x;
            sz_t y;
            pos_t operator+(dim_t rhs) const { return {x + rhs.width, y + rhs.height}; }
            xpos_t x;
            ypos_t y;
          };
          struct place_t
          struct plc_t
          {
            place_t(pos_t posval, dim_t dimval)
            plc_t(pos_t posval, dim_t dimval)
                : pos(posval)
                , dim(dimval)
            {
        
        diff --git a/ include/display/utility.hpp b/ include/display/utility.hpp
          @@ -6,42 +6,36 @@ 
          namespace display
        
        
          {
          template<typename T>
            requires std::is_unsigned_v<T>
          constexpr bool is_overflow_lim(T val, T add, T lim)
          {
            return val > lim || add > lim || val > lim - add;
          }
          template<typename T>
            requires std::is_unsigned_v<T>
          constexpr bool is_underflow_lim(T val, T sub, T lim)
          {
            return val < lim || sub < lim || val < lim + sub;
          }
          template<typename T>
            requires std::is_unsigned_v<T>
          constexpr bool is_overflow(T val, T add)
          {
            return val > std::numeric_limits<T>::max() - add;
          }
          template<typename T>
            requires std::is_unsigned_v<T>
          constexpr bool is_underflow(T val, T sub)
          {
            return val < std::numeric_limits<T>::min() + sub;
          }
          template<typename T>
            requires std::is_unsigned_v<T>
          constexpr T add_lim(T val, T add, T lim)
          {
            return !is_overflow_lim(val, add, lim) ? val + add : lim;
          }
          template<typename T>
            requires std::is_unsigned_v<T>
          constexpr T sub_lim(T val, T sub, T lim)
          {
            return !is_underflow_lim(val, sub, lim) ? val - sub : lim;
        
        diff --git a/ include/display/window.hpp b/ include/display/window.hpp
@@ -5,10 +5,11 @@
namespace display
          {
          class Window : public Element
          {
          public:
            explicit Window(place_t aplc, padd_t padd)
            explicit Window(plc_t aplc, pad_t padd)
                : Element(aplc)
                , m_padd(padd)
            {
        
        
          @@ -19,7 +20,7 @@ 
          public:
        
        
            void input(event& /* unused */) override {}
          protected:
            padd_t padd() const { return m_padd; }
            pad_t padd() const { return m_padd; }
            std::ostream& line_next() const;
          
          @@ -29,13 +30,13 @@ 
          protected:
        
        
            void line_center(const std::string& text) const;
            void line_right(const std::string& text) const;
            place_t plc() const { return {pos(), dim()}; }
            plc_t plc() const { return {pos(), dim()}; }
            pos_t pos() const { return {xpos(), ypos()}; }
            dim_t dim() const { return {wth(), hgt()}; }
            sz_t xpos() const { return axpos() + m_padd.left; }
            sz_t ypos() const { return aypos() + m_padd.top; }
            sz_t wth() const { return awth() - m_padd.width(); }
            sz_t hgt() const { return ahgt() - m_padd.height(); }
            xpos_t xpos() const { return axpos() + m_padd.left; }
            ypos_t ypos() const { return aypos() + m_padd.top; }
            wth_t wth() const { return awth() - m_padd.width(); }
            hgt_t hgt() const { return ahgt() - m_padd.height(); }
          private:
            using Element::adim;
        
        
          @@ -46,9 +47,9 @@ 
          private:
        
        
            using Element::axpos;
            using Element::aypos;
            padd_t m_padd;
            pad_t m_padd;
            mutable display::sz_t m_ypos = 0;
            mutable ypos_t m_ypos = ypos_t(0);
          };
          }  // namespace display
        
        diff --git a/ include/display/window_pivot.hpp b/ include/display/window_pivot.hpp
          @@ -8,20 +8,20 @@ 
          namespace display
        
        
          class WindowPivot : public Window
          {
          public:
            WindowPivot(place_t aplc, padd_t padd, piv_t piv, dim_t dim)
            WindowPivot(plc_t aplc, pad_t padd, piv_t piv, dim_t dim)
                : Window(place(aplc, piv, dim + padd), padd)
                , m_piv(piv)
                , m_dim(dim)
            {
            }
            void resize(place_t aplc) override
            void resize(plc_t aplc) override
            {
              Window::resize(place(aplc, m_piv, m_dim + padd()));
            }
          private:
            static place_t place(place_t aplc, piv_t piv, dim_t dim);
            static plc_t place(plc_t aplc, piv_t piv, dim_t dim);
            piv_t m_piv;
            dim_t m_dim;
        
        diff --git a/ source/display.cpp b/ source/display.cpp
          @@ -28,7 +28,7 @@ 
          Display& Display::display()
        
        
          }
          Display::Display()
              : m_layout(place_t(pos_t(0, 0), alec::get_screen_size()))
              : m_layout(plc_t(pos_t(0, 0), alec::get_screen_size()))
          {
            struct sigaction old_sig_action = {};
            sigaction(SIGWINCH, nullptr, &old_sig_action);
        
        
          @@ -90,7 +90,7 @@ 
          bool Display::get_resized() const
        
        
          void Display::resize()
          {
            m_layout.resize(place_t(pos_t(0, 0), alec::get_screen_size()));
            m_layout.resize(plc_t(pos_t(0, 0), alec::get_screen_size()));
          }
          void Display::render() const
        
        diff --git a/ source/element.cpp b/ source/element.cpp
@@ -5,29 +5,34 @@
namespace display
          {
          std::ostream& Element::set_cursor(sz_t posy, sz_t posx)
          std::ostream& Element::set_cursor(xpos_t xpos, ypos_t ypos)
          {
            return std::cout << alec::cursor_position(posy + 1, posx + 1);
            return std::cout << alec::cursor_position(ypos.value() + 1, xpos.value() + 1);
          }
          std::ostream& Element::set_cursor(pos_t pos)
          {
            return set_cursor(pos.x, pos.y);
          }
          void Element::render_border() const
          {
            set_cursor(aypos(), axpos());
            set_cursor(axpos(), aypos());
            std::cout << "┌";
            for (sz_t i = 2; i < awth(); i++) {
            for (auto i = wth_t(2); i < awth(); i++) {
              std::cout << "─";
            }
            std::cout << "┐";
            for (sz_t i = aypos() + 1; i < aypos() + ahgt(); i++) {
              set_cursor(i, axpos()) << "│";
              set_cursor(i, axpos() + awth() - 1) << "│";
            for (ypos_t j = aypos() + 1; j < aypos() + ahgt(); j++) {
              set_cursor(axpos(), j) << "│";
              set_cursor(axpos() + awth() - 1, j) << "│";
            }
            set_cursor(aypos() + ahgt() - 1, axpos());
            set_cursor(axpos(), aypos() + ahgt() - 1);
            std::cout << "└";
            for (sz_t i = 2; i < awth(); i++) {
            for (auto i = wth_t(2); i < awth(); i++) {
              std::cout << "─";
            }
            std::cout << "┘";
        
        diff --git a/ source/window.cpp b/ source/window.cpp
          @@ -9,14 +9,14 @@ 
          namespace display
        
        
          void Window::render() const
          {
            const auto space = std::string(awth(), ' ');
            const auto space = std::string(awth().value(), ' ');
            for (sz_t i = aypos(); i < aypos() + m_padd.top; i++) {
              set_cursor(i, axpos()) << space;
            for (auto j = aypos(); j < aypos() + m_padd.top; j++) {
              set_cursor(axpos(), j) << space;
            }
            for (sz_t i = m_ypos; i < aypos() + ahgt(); i++) {
              set_cursor(i, axpos()) << space;
            for (auto j = m_ypos; j < aypos() + ahgt(); j++) {
              set_cursor(axpos(), j) << space;
            }
            std::cout << std::flush;
          }
        
        
          @@ -26,8 +26,8 @@ 
          void Window::clear() const
        
        
            std::cout << alec::background_v<alec::Color::DEFAULT>;
            std::cout << alec::foreground_v<alec::Color::DEFAULT>;
            for (sz_t i = 0; i < aypos() + ahgt(); i++) {
              set_cursor(i, axpos()) << std::string(awth(), ' ');
            for (auto j = ypos_t(0); j < aypos() + ahgt(); j++) {
              set_cursor(axpos(), j) << std::string(awth().value(), ' ');
            }
            std::cout << std::flush;
        
        
          @@ -46,36 +46,36 @@ 
          std::ostream& Window::line_next() const
        
        
              return null;
            }
            return set_cursor(m_ypos++, axpos());
            return set_cursor(axpos(), m_ypos++);
          }
          void Window::line_empty() const
          {
            line_next() << std::string(awth(), ' ');
            line_next() << std::string(awth().value(), ' ');
          }
          void Window::line_left(const std::string& text) const
          {
            const auto left = std::string(m_padd.left, ' ');
            const auto right = std::string(m_padd.right, ' ');
            const auto left = std::string(m_padd.left.value(), ' ');
            const auto right = std::string(m_padd.right.value(), ' ');
            line_next() << left << std::format("{:<{}}", text, wth()) << right;
            line_next() << left << std::format("{:<{}}", text, wth().value()) << right;
          }
          void Window::line_center(const std::string& text) const
          {
            const auto left = std::string(m_padd.left, ' ');
            const auto right = std::string(m_padd.right, ' ');
            const auto left = std::string(m_padd.left.value(), ' ');
            const auto right = std::string(m_padd.right.value(), ' ');
            line_next() << left << std::format("{:^{}}", text, wth()) << right;
            line_next() << left << std::format("{:^{}}", text, wth().value()) << right;
          }
          void Window::line_right(const std::string& text) const
          {
            const auto left = std::string(m_padd.left, ' ');
            const auto right = std::string(m_padd.right, ' ');
            const auto left = std::string(m_padd.left.value(), ' ');
            const auto right = std::string(m_padd.right.value(), ' ');
            line_next() << left << std::format("{:>{}}", text, wth()) << right;
            line_next() << left << std::format("{:>{}}", text, wth().value()) << right;
          }
          }  // namespace display
        
        diff --git a/ source/window_pivot.cpp b/ source/window_pivot.cpp
@@ -5,50 +5,43 @@
namespace display
          {
          place_t WindowPivot::place(place_t aplc, piv_t piv, dim_t dim)
          plc_t WindowPivot::place(plc_t aplc, piv_t piv, dim_t dim)
          {
            const auto [cols, rows] = aplc.dim;
            const sz_t colsh = cols / 2;
            const sz_t rowsh = rows / 2;
            const auto [awth, ahth] = aplc.dim;
            const auto [wth, hgt] = dim;
            const auto [wdth, hght] = dim;
            const sz_t wdthm = wdth / 2;
            const sz_t hghtm = hght / 2;
            const sz_t zero = 0;
            pos_t start;
            pos_t end;
            dim_t start(0, 0);
            dim_t end(0, 0);
            using display::add_lim, display::sub_lim;
            switch (piv.x) {
              case PvtX::Left:
                start.x = 0;
                end.x = add_lim(start.x, wdth, cols);
                start.width = wth_t(0);
                end.width = add_lim(start.width, wth, awth);
                break;
              case PvtX::Center:
                start.x = sub_lim(colsh, wdthm, zero);
                end.x = add_lim(start.x, wdth, cols);
                start.width = sub_lim((awth / 2), (wth / 2), wth_t(0));
                end.width = add_lim(start.width, wth, awth);
                break;
              case PvtX::Right:
                end.x = cols;
                start.x = sub_lim(end.x, wdth, zero);
                end.width = awth;
                start.width = sub_lim(end.width, wth, wth_t(0));
                break;
            }
            switch (piv.y) {
              case PvtY::Top:
                start.y = 0;
                end.y = add_lim(start.y, hght, rows);
                start.height = hgt_t(0);
                end.height = add_lim(start.height, hgt, ahth);
                break;
              case PvtY::Center:
                start.y = sub_lim(rowsh, hghtm, zero);
                end.y = add_lim(start.y, hght, rows);
                start.height = sub_lim((ahth / 2), (hgt / 2), hgt_t(0));
                end.height = add_lim(start.height, hgt, ahth);
                break;
              case PvtY::Bottom:
                end.y = rows;
                start.y = sub_lim(end.y, hght, zero);
                end.height = ahth;
                start.height = sub_lim(end.height, hgt, hgt_t(0));
                break;
            }
          diff --git a/ test/source/utility_test.cpp b/ test/source/utility_test.cpp
@@ -1,12 +1,13 @@
#include <cstdint>
          #include <limits>
          #include "display/utility.hpp"
          #include "display/types.hpp"
          int main()
          {
            using namespace display;  // NOLINT
            using sz_t = std::uint16_t;
            using lim = std::numeric_limits<sz_t>;
            static constexpr const sz_t zero = 0;