giga

Terminal text editor
git clone git://git.dimitrijedobrota.com/giga.git
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

layout_dynamic.hpp (5113B)


0 #pragma once
2 #include <memory>
3 #include <variant>
4 #include <vector>
6 #include "display/element.hpp"
7 #include "display/layout.hpp"
9 namespace display
10 {
12 template<typename T>
13 concept is_enum_type_v = std::is_enum_v<T>;
15 template<typename T>
16 concept is_enum_count_v = requires { T::_count; };
18 template<typename T, T Val>
19 concept is_enum_valid_v = is_enum_type_v<T> && is_enum_count_v<T>
20 && requires { requires(Val >= T {} && Val < T::_count); };
22 class Panel
23 {
24 public:
25 using layout_t = Layout<Element>;
27 enum class Split : std::uint8_t
28 {
29 Horizontal,
30 Vertical,
31 _count, // NOLINT
32 };
34 enum class Direction : std::uint8_t
35 {
36 Up,
37 Left,
38 Down,
39 Right,
40 _count, // NOLINT
41 };
43 Panel(Panel* parent, plc_t aplc, layout_t::ptr_t&& child)
44 : m_parent(parent)
45 , m_payload(layout_t(aplc, std::move(child)))
46 {
47 }
49 explicit Panel(Panel* parent, plc_t aplc)
50 : m_parent(parent)
51 , m_payload(layout_t(aplc))
52 {
53 }
55 layout_t& layout() { return std::get<layout_t>(m_payload); }
57 void resize(plc_t aplc);
58 void render() const;
60 template<Split T>
61 requires is_enum_valid_v<Split, T>
62 Panel* split();
64 template<Direction D>
65 requires is_enum_valid_v<Direction, D>
66 Panel* select(Panel* sel);
68 Panel* close();
70 private:
71 const Element& get_elem() const
72 {
73 return std::visit([](const auto& val) -> const Element& { return val; },
74 m_payload);
75 }
77 Panel* select(pos_t tpos);
79 template<Split M>
80 class Container : public Element
81 {
82 public:
83 using ptr_t = std::unique_ptr<Panel>;
84 using container_t = std::vector<ptr_t>;
86 explicit Container(plc_t aplc)
87 : Element(aplc)
88 {
89 }
91 static constexpr Split type() { return M; }
93 const container_t& panels() const { return m_panels; }
94 container_t& panels() { return m_panels; }
96 plc_t place(std::size_t idx, std::size_t size) const
97 {
98 if constexpr (M == Split::Horizontal) {
99 if (idx + 1 == size) {
100 const auto unit = (awth() / size) * (size - 1);
101 const auto wth = awth() - unit;
102 const auto xpos = axpos() + unit;
104 return {{xpos, aypos()}, {wth, ahgt()}};
107 const auto wth = awth() / size;
108 const auto xpos = axpos() + wth * idx;
110 return {{xpos, aypos()}, {wth, ahgt()}};
111 } else {
112 if (idx + 1 == size) {
113 const auto unit = (ahgt() / size) * (size - 1);
114 const auto ypos = aypos() + unit;
115 const auto hgt = ahgt() - unit;
117 return {{axpos(), ypos}, {awth(), hgt}};
120 const auto hgt = ahgt() / size;
121 const auto ypos = aypos() + hgt * idx;
123 return {{axpos(), ypos}, {awth(), hgt}};
127 container_t::iterator find_child(Panel* child)
129 return std::find_if(m_panels.begin(),
130 m_panels.end(),
131 [&](const auto& ptr) { return ptr.get() == child; });
134 private:
135 container_t m_panels;
136 };
138 using payload_t = std::variant<layout_t,
139 Container<Split::Horizontal>,
140 Container<Split::Vertical>>;
142 Panel* m_parent;
143 payload_t m_payload;
144 };
146 template<typename T>
147 class LayoutDynamic : public Element
149 public:
150 explicit LayoutDynamic(plc_t aplc)
151 : Element(aplc)
152 , m_container(nullptr, aplc)
156 template<typename M = T, class... Args>
157 requires(std::is_base_of_v<T, M>)
158 M& emplace_child(Args&&... args)
160 return m_sel->layout().emplace_child<M>(std::forward<Args>(args)...);
163 bool is_finished() const { return m_sel == nullptr; }
165 void render() const override { m_container.render(); }
166 void resize(plc_t aplc) override
168 Element::resize(aplc);
169 m_container.resize(aplc);
172 void input(event& evnt) override;
174 private:
175 Panel m_container;
176 Panel* m_sel = &m_container;
177 };
179 template<typename T>
180 void LayoutDynamic<T>::input(event& evnt)
182 if (evnt.type() == event::Type::KEY) {
183 const auto* old = m_sel;
184 if (evnt.key() == 'e') {
185 const auto& child = m_sel->layout().get_child<T>();
186 m_sel = m_sel->split<Panel::Split::Horizontal>();
187 m_sel->layout().emplace_child<T>(child);
188 } else if (evnt.key() == 'r') {
189 const auto& child = m_sel->layout().get_child<T>();
190 m_sel = m_sel->split<Panel::Split::Vertical>();
191 m_sel->layout().emplace_child<T>(child);
192 } else if (evnt.key() == 'x') {
193 m_sel = m_sel->close();
194 } else if (evnt.key() == 'w') {
195 m_sel = m_container.select<Panel::Direction::Up>(m_sel);
196 } else if (evnt.key() == 'a') {
197 m_sel = m_container.select<Panel::Direction::Left>(m_sel);
198 } else if (evnt.key() == 's') {
199 m_sel = m_container.select<Panel::Direction::Down>(m_sel);
200 } else if (evnt.key() == 'd') {
201 m_sel = m_container.select<Panel::Direction::Right>(m_sel);
202 } else {
203 m_sel->layout().input(evnt);
206 if (m_sel != nullptr && m_sel != old) {
207 render();
209 evnt.type() = event::Type::NONE;
210 return;
214 } // namespace display