gigaTerminal text editor |
git clone git://git.dimitrijedobrota.com/giga.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
layout_dynamic.hpp (5113B)
1 #pragma once 2 3 #include <memory> 4 #include <variant> 5 #include <vector> 6 7 #include "display/element.hpp" 8 #include "display/layout.hpp" 9 10 namespace display 11 { 12 13 template<typename T> 14 concept is_enum_type_v = std::is_enum_v<T>; 15 16 template<typename T> 17 concept is_enum_count_v = requires { T::_count; }; 18 19 template<typename T, T Val> 20 concept is_enum_valid_v = is_enum_type_v<T> && is_enum_count_v<T> 21 && requires { requires(Val >= T {} && Val < T::_count); }; 22 23 class Panel 24 { 25 public: 26 using layout_t = Layout<Element>; 27 28 enum class Split : std::uint8_t 29 { 30 Horizontal, 31 Vertical, 32 _count, // NOLINT 33 }; 34 35 enum class Direction : std::uint8_t 36 { 37 Up, 38 Left, 39 Down, 40 Right, 41 _count, // NOLINT 42 }; 43 44 Panel(Panel* parent, plc_t aplc, layout_t::ptr_t&& child) 45 : m_parent(parent) 46 , m_payload(layout_t(aplc, std::move(child))) 47 { 48 } 49 50 explicit Panel(Panel* parent, plc_t aplc) 51 : m_parent(parent) 52 , m_payload(layout_t(aplc)) 53 { 54 } 55 56 layout_t& layout() { return std::get<layout_t>(m_payload); } 57 58 void resize(plc_t aplc); 59 void render() const; 60 61 template<Split T> 62 requires is_enum_valid_v<Split, T> 63 Panel* split(); 64 65 template<Direction D> 66 requires is_enum_valid_v<Direction, D> 67 Panel* select(Panel* sel); 68 69 Panel* close(); 70 71 private: 72 const Element& get_elem() const 73 { 74 return std::visit([](const auto& val) -> const Element& { return val; }, 75 m_payload); 76 } 77 78 Panel* select(pos_t tpos); 79 80 template<Split M> 81 class Container : public Element 82 { 83 public: 84 using ptr_t = std::unique_ptr<Panel>; 85 using container_t = std::vector<ptr_t>; 86 87 explicit Container(plc_t aplc) 88 : Element(aplc) 89 { 90 } 91 92 static constexpr Split type() { return M; } 93 94 const container_t& panels() const { return m_panels; } 95 container_t& panels() { return m_panels; } 96 97 plc_t place(std::size_t idx, std::size_t size) const 98 { 99 if constexpr (M == Split::Horizontal) { 100 if (idx + 1 == size) { 101 const auto unit = (awth() / size) * (size - 1); 102 const auto wth = awth() - unit; 103 const auto xpos = axpos() + unit; 104 105 return {{xpos, aypos()}, {wth, ahgt()}}; 106 } 107 108 const auto wth = awth() / size; 109 const auto xpos = axpos() + wth * idx; 110 111 return {{xpos, aypos()}, {wth, ahgt()}}; 112 } else { 113 if (idx + 1 == size) { 114 const auto unit = (ahgt() / size) * (size - 1); 115 const auto ypos = aypos() + unit; 116 const auto hgt = ahgt() - unit; 117 118 return {{axpos(), ypos}, {awth(), hgt}}; 119 } 120 121 const auto hgt = ahgt() / size; 122 const auto ypos = aypos() + hgt * idx; 123 124 return {{axpos(), ypos}, {awth(), hgt}}; 125 } 126 } 127 128 container_t::iterator find_child(Panel* child) 129 { 130 return std::find_if(m_panels.begin(), 131 m_panels.end(), 132 [&](const auto& ptr) { return ptr.get() == child; }); 133 } 134 135 private: 136 container_t m_panels; 137 }; 138 139 using payload_t = std::variant<layout_t, 140 Container<Split::Horizontal>, 141 Container<Split::Vertical>>; 142 143 Panel* m_parent; 144 payload_t m_payload; 145 }; 146 147 template<typename T> 148 class LayoutDynamic : public Element 149 { 150 public: 151 explicit LayoutDynamic(plc_t aplc) 152 : Element(aplc) 153 , m_container(nullptr, aplc) 154 { 155 } 156 157 template<typename M = T, class... Args> 158 requires(std::is_base_of_v<T, M>) 159 M& emplace_child(Args&&... args) 160 { 161 return m_sel->layout().emplace_child<M>(std::forward<Args>(args)...); 162 } 163 164 bool is_finished() const { return m_sel == nullptr; } 165 166 void render() const override { m_container.render(); } 167 void resize(plc_t aplc) override 168 { 169 Element::resize(aplc); 170 m_container.resize(aplc); 171 } 172 173 void input(event& evnt) override; 174 175 private: 176 Panel m_container; 177 Panel* m_sel = &m_container; 178 }; 179 180 template<typename T> 181 void LayoutDynamic<T>::input(event& evnt) 182 { 183 if (evnt.type() == event::Type::KEY) { 184 const auto* old = m_sel; 185 if (evnt.key() == 'e') { 186 const auto& child = m_sel->layout().get_child<T>(); 187 m_sel = m_sel->split<Panel::Split::Horizontal>(); 188 m_sel->layout().emplace_child<T>(child); 189 } else if (evnt.key() == 'r') { 190 const auto& child = m_sel->layout().get_child<T>(); 191 m_sel = m_sel->split<Panel::Split::Vertical>(); 192 m_sel->layout().emplace_child<T>(child); 193 } else if (evnt.key() == 'x') { 194 m_sel = m_sel->close(); 195 } else if (evnt.key() == 'w') { 196 m_sel = m_container.select<Panel::Direction::Up>(m_sel); 197 } else if (evnt.key() == 'a') { 198 m_sel = m_container.select<Panel::Direction::Left>(m_sel); 199 } else if (evnt.key() == 's') { 200 m_sel = m_container.select<Panel::Direction::Down>(m_sel); 201 } else if (evnt.key() == 'd') { 202 m_sel = m_container.select<Panel::Direction::Right>(m_sel); 203 } else { 204 m_sel->layout().input(evnt); 205 } 206 207 if (m_sel != nullptr && m_sel != old) { 208 render(); 209 } 210 evnt.type() = event::Type::NONE; 211 return; 212 } 213 } 214 215 } // namespace display