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 | 8f7a83b9741e3012294f7f4d4876c6be54514213 | 
| parent | dd1a7e97d6177f2229a16a9eba752efbf8507608 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Tue, 18 Feb 2025 15:35:20 +0100 | 
Add different types of window borders
* Window derivatives no longer use absolute position
* There is a padding that will be used by the base
* Improve types.hpp consistency
* Better line drawing interface
| M | CMakeLists.txt | | | + - | 
| M | example/example.cpp | | | +++ --- | 
| M | example/navig/navig.cpp | | | +++++++ ------ | 
| M | include/display/element.hpp | | | ++ -- | 
| M | include/display/layout_rigid.hpp | | | + - | 
| M | include/display/types.hpp | | | +++++++++++++ ----- | 
| M | include/display/window.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --- | 
| M | include/display/window_pivot.hpp | | | +++++++++ ------- | 
| M | source/window.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ----------- | 
9 files changed, 196 insertions(+), 40 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -4,7 +4,7 @@ 
          include(cmake/prelude.cmake)
        
        
          project(
              display
              VERSION 0.1.29
              VERSION 0.1.30
              DESCRIPTION "TUI library"
              HOMEPAGE_URL "git://git.dimitrijedobrota.com/display.git"
              LANGUAGES CXX
        
        diff --git a/ example/example.cpp b/ example/example.cpp
          @@ -12,7 +12,7 @@ 
          namespace
        
        
          using display::PvtX, display::PvtY;
          using display::sz_t, display::dim_t, display::piv_t, display::place_t;
          class WindowCustom : public display::WindowPivot
          class WindowCustom : public display::WindowPivot<display::Window>
          {
          public:
            explicit WindowCustom(place_t aplc, piv_t piv, dim_t dim)
        
        
          @@ -27,8 +27,8 @@ 
          public:
        
        
              color_red = (color_red + 25) % 256;
              std::cout << alec::background(color_red, 65, 65);
              line_empty(/* reset = */ true);
              for (std::size_t i = 1; i < ahgt(); i++) {
              line_reset();
              for (std::size_t i = 0; i < hgt(); i++) {
                line_empty();
              }
          diff --git a/ example/navig/navig.cpp b/ example/navig/navig.cpp
          @@ -14,7 +14,7 @@ 
          namespace
        
        
          bool is_finished = false;  // NOLINT
          class WindowCustom : public display::WindowPivot
          class WindowCustom : public display::WindowPivot<display::WindowBorderBox>
          {
          public:
            WindowCustom(display::place_t aplc,
        
        
          @@ -28,8 +28,8 @@ 
          public:
        
        
            void render() const override
            {
              std::cout << alec::background_v<alec::Color::BLUE>;
              line_reset();
              line_empty(/* reset = */ true);
              line_center(m_menu.title);
              line_empty();
              for (std::size_t i = 0; i < m_menu.items.size(); i++) {
        
        
          @@ -43,7 +43,8 @@ 
          public:
        
        
                  std::cout << alec::foreground_v<alec::Color::DEFAULT>;
                }
              }
              line_empty();
              display::WindowBorderBox::render();
              std::cout << alec::background_v<alec::Color::DEFAULT>;
              std::cout << std::flush;
        
        
          @@ -94,8 +95,8 @@ 
          private:
        
        
                width = std::max(width, item.prompt.size());
              }
              return {static_cast<display::sz_t>(width + 2),
                      static_cast<display::sz_t>(menu.items.size() + 4)};
              return {static_cast<display::sz_t>(width),
                      static_cast<display::sz_t>(menu.items.size() + 2)};
            }
            example::menu_t m_menu;
        
        
          @@ -166,7 +167,7 @@ 
          int menu_t::visit(const menu_t& menu)
        
        
              stk.push(&menu);
            }
            layout.set_child<WindowCustom>(piv_t(PvtX::Center, PvtY::Center), *stk.top());
            layout.set_child<WindowCustom>(piv_t(PvtX::Right, PvtY::Bottom), *stk.top());
            layout.render();
            return 0;
        
        diff --git a/ include/display/element.hpp b/ include/display/element.hpp
          @@ -26,8 +26,8 @@ 
          public:
        
        
            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& 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; }
        
        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);  // NOLINT
            LayoutRigid(place_t aplc, layout_t layout);
          private:
            std::size_t count_and_pad(layout_t& layout) const;
        
        diff --git a/ include/display/types.hpp b/ include/display/types.hpp
          @@ -31,6 +31,14 @@ 
          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),
              };
            }
            sz_t width;
            sz_t height;
          };
        
        
          @@ -70,14 +78,14 @@ 
          struct pos_t
        
        
          struct place_t
          {
            place_t(pos_t aposval, dim_t adimval)
                : apos(aposval)
                , adim(adimval)
            place_t(pos_t posval, dim_t dimval)
                : pos(posval)
                , dim(dimval)
            {
            }
            pos_t apos;
            dim_t adim;
            pos_t pos;
            dim_t dim;
          };
          enum class PvtX : std::uint8_t
        
        diff --git a/ include/display/window.hpp b/ include/display/window.hpp
          @@ -9,23 +9,96 @@ 
          namespace display
        
        
          class Window : public Element
          {
          public:
            explicit Window(place_t aplc)
            using ustring = std::basic_string<unsigned char>;
            explicit Window(place_t aplc, dim_t padding = {0, 0})
                : Element(aplc)
                , m_padding(padding)
            {
            }
            void render() const override {}
            void clear() const override;
            void input(event& /* unused */) override {}
          protected:
            std::ostream& next_line(bool reset = false) const;
            void line_empty(bool reset = false) const;
            static dim_t padding() { return {0, 0}; }
            std::ostream& set_cursor(sz_t posy, sz_t posx) const;
            std::ostream& line_next() const;
            void line_reset() const;
            void line_empty() const;
            void line_left(const std::string& text) const;
            void line_center(const std::string& text) const;
            void line_right(const std::string& text) const;
            place_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_padding.width / 2); }
            sz_t ypos() const { return aypos() + (m_padding.height / 2); }
            sz_t wth() const { return awth() - m_padding.width; }
            sz_t hgt() const { return ahgt() - m_padding.height; }
          private:
            friend class WindowBorder;
            friend class WindowBox;
            friend class WindowBorderBox;
            using Element::adim;
            using Element::ahgt;
            using Element::aplc;
            using Element::apos;
            using Element::awth;
            using Element::axpos;
            using Element::aypos;
            dim_t m_padding;
            mutable display::sz_t m_ypos = 0;
          };
          class WindowBorder : public Window
          {
          public:
            explicit WindowBorder(place_t aplc)
                : Window(aplc, padding())
            {
            }
            void render() const override;
          protected:
            static dim_t padding() { return {2, 2}; }
          };
          class WindowBox : public Window
          {
          public:
            explicit WindowBox(place_t aplc)
                : Window(aplc, padding())
            {
            }
            void render() const override;
          protected:
            static dim_t padding() { return {2, 2}; }
          };
          class WindowBorderBox : public Window
          {
          public:
            explicit WindowBorderBox(place_t aplc)
                : Window(aplc, padding())
            {
            }
            void render() const override;
          protected:
            static dim_t padding() { return {4, 2}; }
          };
          }  // namespace display
        
        diff --git a/ include/display/window_pivot.hpp b/ include/display/window_pivot.hpp
@@ -6,11 +6,13 @@
namespace display
          {
          class WindowPivot : public Window
          template<typename T>
            requires(std::is_base_of_v<Window, T>)
          class WindowPivot : public T
          {
          public:
            WindowPivot(place_t aplc, piv_t piv, dim_t dim)
                : Window(place(aplc, piv, dim))
                : T(place(aplc, piv, dim, T::padding()))
                , m_piv(piv)
                , m_dim(dim)
            {
        
        
          @@ -18,17 +20,17 @@ 
          public:
        
        
            void resize(place_t aplc) override
            {
              Window::resize(place(aplc, m_piv, m_dim));
              T::resize(place(aplc, m_piv, m_dim, T::padding()));
            }
          protected:
            static place_t place(place_t aplc, piv_t piv, dim_t dim)
            static place_t place(place_t aplc, piv_t piv, dim_t dim, dim_t padding)
            {
              const auto [cols, rows] = aplc.adim;
              const auto [cols, rows] = aplc.dim;
              const sz_t colsh = cols / 2;
              const sz_t rowsh = rows / 2;
              const auto [wdth, hght] = dim;
              const auto [wdth, hght] = dim + padding;
              const sz_t wdthm = wdth / 2;
              const sz_t hghtm = hght / 2;
          
          @@ -69,7 +71,7 @@ 
          protected:
        
        
                  break;
              }
              return {aplc.apos + start, end - start};
              return {aplc.pos + start, end - start};
            }
          private:
        
        diff --git a/ source/window.cpp b/ source/window.cpp
@@ -1,4 +1,5 @@
#include <format>
          #include <fstream>
          #include <iostream>
          #include "display/window.hpp"
        
        
          @@ -11,39 +12,110 @@ 
          void Window::clear() const
        
        
            std::cout << alec::background_v<alec::Color::DEFAULT>;
            std::cout << alec::foreground_v<alec::Color::DEFAULT>;
            line_empty(/* reset = */ true);
            for (std::size_t i = 1; i < ahgt(); i++) {
              line_empty();
            for (sz_t i = 0; i < aypos() + ahgt(); i++) {
              set_cursor(i, axpos()) << std::string(awth(), ' ');
            }
            std::cout << std::flush;
          }
          std::ostream& Window::next_line(bool reset) const
          // Window
          void Window::line_reset() const
          {
            m_ypos = ypos();
          }
          std::ostream& Window::set_cursor(sz_t posy, sz_t posx) const
          {
            return std::cout << alec::cursor_position(posy + 1, posx + 1);
          }
          std::ostream& Window::line_next() const
          {
            if (reset) {
              m_ypos = aypos();
            if (m_ypos == ypos() + hgt()) {
              static std::ofstream null;
              null.setstate(std::ios_base::badbit);
              return null;
            }
            return std::cout << alec::cursor_position(++m_ypos, axpos() + 1);
            return set_cursor(m_ypos++, xpos());
          }
          void Window::line_empty(bool reset) const
          void Window::line_empty() const
          {
            next_line(reset) << std::string(awth(), ' ');
            line_next() << std::string(wth(), ' ');
          }
          void Window::line_left(const std::string& text) const
          {
            next_line() << std::format(" {:<{}} ", text, awth() - 2);
            line_next() << std::format("{:<{}}", text, wth());
          }
          void Window::line_center(const std::string& text) const
          {
            next_line() << std::format("{:^{}}", text, awth());
            line_next() << std::format("{:^{}}", text, wth());
          }
          void Window::line_right(const std::string& text) const
          {
            next_line() << std::format(" {:>{}} ", text, awth() - 2);
            line_next() << std::format("{:>{}}", text, wth());
          }
          void WindowBorder::render() const
          {
            set_cursor(aypos(), axpos()) << std::string(awth(), ' ');
            for (sz_t i = ypos(); i < ypos() + hgt(); i++) {
              set_cursor(i, axpos()) << ' ';
              set_cursor(i, axpos() + awth() - 1) << ' ';
            }
            set_cursor(aypos() + ahgt() - 1, axpos()) << std::string(awth(), ' ');
          }
          void WindowBox::render() const
          {
            set_cursor(aypos(), axpos());
            std::cout << "┌";
            for (sz_t i = 0; i < wth(); i++) {
              std::cout << "─";
            }
            std::cout << "┐";
            for (sz_t i = ypos(); i < ypos() + hgt(); i++) {
              set_cursor(i, axpos()) << "│";
              set_cursor(i, axpos() + awth() - 1) << "│";
            }
            set_cursor(aypos() + ahgt() - 1, axpos());
            std::cout << "└";
            for (sz_t i = 0; i < wth(); i++) {
              std::cout << "─";
            }
            std::cout << "┘";
          }
          void WindowBorderBox::render() const
          {
            set_cursor(aypos(), axpos());
            std::cout << "┌─";
            for (sz_t i = 0; i < wth(); i++) {
              std::cout << "─";
            }
            std::cout << "─┐";
            for (sz_t i = ypos(); i < ypos() + hgt(); i++) {
              set_cursor(i, axpos()) << "│ ";
              set_cursor(i, axpos() + awth() - 2) << " │";
            }
            set_cursor(aypos() + ahgt() - 1, axpos());
            std::cout << "└─";
            for (sz_t i = 0; i < wth(); i++) {
              std::cout << "─";
            }
            std::cout << "─┘";
          }
          }  // namespace display