TermOx
accordion.hpp
1 #ifndef TERMOX_WIDGET_WIDGETS_ACCORDION_HPP
2 #define TERMOX_WIDGET_WIDGETS_ACCORDION_HPP
3 #include <memory>
4 #include <utility>
5 
6 #include <signals_light/signal.hpp>
7 
8 #include <termox/painter/color.hpp>
9 #include <termox/painter/glyph_string.hpp>
10 #include <termox/painter/painter.hpp>
11 #include <termox/painter/trait.hpp>
12 #include <termox/widget/layouts/detail/linear_layout.hpp>
13 #include <termox/widget/layouts/horizontal.hpp>
14 #include <termox/widget/layouts/opposite.hpp>
15 #include <termox/widget/layouts/passive.hpp>
16 #include <termox/widget/layouts/vertical.hpp>
17 #include <termox/widget/pipe.hpp>
18 #include <termox/widget/widgets/label.hpp>
19 #include <termox/widget/widgets/text_view.hpp>
20 
21 namespace ox {
22 
24 template <template <typename> typename Layout_t>
25 class Bar : public Layout_t<Widget> {
26  private:
27  class Indicator : public Widget {
28  public:
29  Indicator()
30  {
31  using namespace pipe;
32  if constexpr (layout::is_vertical_v<Layout_t<Widget>>)
33  *this | fixed_height(1);
34  else
35  *this | fixed_width(3);
36  }
37 
38  public:
39  void plus()
40  {
41  x_ = U"[+]";
42  this->update();
43  }
44 
45  void minus()
46  {
47  x_ = U"[-]";
48  this->update();
49  }
50 
51  protected:
52  auto paint_event(Painter& p) -> bool override
53  {
54  p.put(x_, {0, 0});
55  return Widget::paint_event(p);
56  }
57 
58  private:
59  Glyph_string x_ = U"[+]";
60  };
61 
62  class Title : public layout::Opposite_t<Layout_t<Widget>> {
63  public:
64  struct Parameters {
65  Glyph_string title;
66  Align alignment = is_vertical ? Align::Left : Align::Top;
67  Glyph wallpaper = is_vertical ? U'─' : U'│';
68  };
69 
70  public:
71  sl::Signal<void()> clicked;
72 
73  public:
74  Title(Parameters p)
75  : centered_text_{this->template make_child<Label<Layout_t>>(
76  {(Glyph_string{U' '}
77  .append(std::move(p.title))
78  .append(U' ')) |
79  Trait::Bold,
80  p.alignment, extra_left})}
81  {
82  using namespace ox::pipe;
83  if constexpr (is_vertical)
84  centered_text_ | fixed_height(1);
85  else {
86  this->insert_child(widget(), 0);
87  this->insert_child(widget(), 2);
88  *this | children() | fixed_width(1);
89  }
90  *this | children() |
91  on_mouse_press([this](auto const&) { this->clicked(); });
92  centered_text_.set_wallpaper(p.wallpaper);
93  }
94 
95  private:
96  Label<Layout_t>& centered_text_;
97 
98  static auto constexpr is_vertical =
99  layout::is_vertical_v<layout::Opposite_t<Layout_t<Widget>>>;
100 
101  static auto constexpr extra_left = is_vertical ? 3 : 1;
102  };
103 
104  private:
105  static auto constexpr is_vertical = layout::is_vertical_v<Layout_t<Widget>>;
106 
107  public:
108  using Parameters = typename Title::Parameters;
109 
110  public:
111  sl::Signal<void()> toggle_request;
112 
113  public:
114  Bar(Parameters p) : text_{this->template make_child<Title>(std::move(p))}
115  {
116  using namespace pipe;
117  if constexpr (is_vertical)
118  *this | fixed_width(3);
119  else
120  *this | fixed_height(1);
121 
122  this->collapse();
123 
124  indicator_.mouse_pressed.connect(
125  [this](auto const&) { this->toggle_request(); });
126  text_.clicked.connect([this] { this->toggle_request(); });
127  }
128 
129  public:
130  void expand() { indicator_.minus(); }
131 
132  void collapse() { indicator_.plus(); }
133 
134  auto indicator_widget() -> Indicator& { return indicator_; }
135 
136  auto title_widget() -> Title& { return text_; }
137 
138  private:
139  Indicator& indicator_ = this->template make_child<Indicator>();
140  Title& text_;
141 };
142 
143 enum class Bar_position { First, Last };
144 
145 template <template <typename> typename Layout_t,
146  typename Widget_t,
147  Bar_position position = Bar_position::First>
148 class Accordion : public Passive<Layout_t<Widget>> {
149  private:
150  static_assert(layout::is_vertical_v<Layout_t<Widget>> ||
151  layout::is_horizontal_v<Layout_t<Widget>>);
152 
153  private:
154  using Bar_t =
156 
157  public:
158  using Parameters = typename Bar_t::Parameters;
159 
160  public:
161  // TODO add unique_ptr<Widget_t> overload, since this is a wrapper, and add
162  // free functions for this type of constructor.
163 
165  template <typename... Args>
166  Accordion(Parameters p, Args&&... args)
167  : bar_{this->template make_child<Bar_t>(std::move(p))},
168  wrapped_{
169  this->template make_child<Widget_t>(std::forward<Args>(args)...)}
170  {
171  if constexpr (wrapped_index_ == 0)
172  this->swap_children(0, 1);
173 
174  bar_.toggle_request.connect([this] { this->toggle_expansion(); });
175 
176  this->collapse();
177  }
178 
179  public:
181  void expand()
182  {
183  expanded_ = true;
184  bar_.expand();
185  this->reinsert_wrapped();
186  this->enable(this->is_enabled());
187  }
188 
190  void collapse()
191  {
192  expanded_ = false;
193  bar_.collapse();
194  this->extract_wrapped();
195  this->enable(this->is_enabled());
196  }
197 
198  void toggle_expansion()
199  {
200  if (expanded_)
201  this->collapse();
202  else
203  this->expand();
204  }
205 
207  [[nodiscard]] auto wrapped() -> Widget_t& { return wrapped_; }
208 
210  [[nodiscard]] auto wrapped() const -> Widget_t const& { return wrapped_; }
211 
213  [[nodiscard]] auto bar() -> Bar_t& { return bar_; }
214 
216  [[nodiscard]] auto bar() const -> Bar_t const& { return bar_; }
217 
218  private:
219  Bar_t& bar_;
220  Widget_t& wrapped_;
221 
222  bool expanded_ = true;
223  std::unique_ptr<Widget> w_storage_ = nullptr;
224 
225  static auto constexpr wrapped_index_ =
226  position == Bar_position::First ? 1 : 0;
227 
228  static auto constexpr is_vertical = layout::is_vertical_v<Layout_t<Widget>>;
229 
230  private:
231  void reinsert_wrapped()
232  {
233  if (w_storage_ == nullptr)
234  return;
235  this->insert_child(std::move(w_storage_), wrapped_index_);
236  w_storage_ = nullptr;
237  }
238 
239  void extract_wrapped()
240  {
241  if (w_storage_ == nullptr)
242  w_storage_ = this->remove_child_at(wrapped_index_);
243  }
244 };
245 
246 template <template <typename> typename Layout_t,
247  typename Widget_t,
248  Bar_position position = Bar_position::First,
249  typename... Args>
250 [[nodiscard]] auto accordion(Args&&... args)
251  -> std::unique_ptr<Accordion<Layout_t, Widget_t, position>>
252 {
253  return std::make_unique<Accordion<Layout_t, Widget_t, position>>(
254  std::forward<Args>(args)...);
255 }
256 
257 template <typename Widget_t, Bar_position position = Bar_position::First>
258 using HAccordion = Accordion<layout::Horizontal, Widget_t, position>;
259 
260 template <typename Widget_t,
261  Bar_position position = Bar_position::First,
262  typename... Args>
263 [[nodiscard]] auto haccordion(Args&&... args)
264  -> std::unique_ptr<HAccordion<Widget_t, position>>
265 {
266  return std::make_unique<HAccordion<Widget_t, position>>(
267  std::forward<Args>(args)...);
268 }
269 
270 template <typename Widget_t, Bar_position position = Bar_position::First>
271 using VAccordion = Accordion<layout::Vertical, Widget_t, position>;
272 
273 template <typename Widget_t,
274  Bar_position position = Bar_position::First,
275  typename... Args>
276 [[nodiscard]] auto vaccordion(Args&&... args)
277  -> std::unique_ptr<VAccordion<Widget_t, position>>
278 {
279  return std::make_unique<VAccordion<Widget_t, position>>(
280  std::forward<Args>(args)...);
281 }
282 
283 } // namespace ox
284 #endif // TERMOX_WIDGET_WIDGETS_ACCORDION_HPP
Definition: accordion.hpp:148
void expand()
Enable the wrapped Widget.
Definition: accordion.hpp:181
auto bar() const -> Bar_t const &
Return the titled Bar widget.
Definition: accordion.hpp:216
auto bar() -> Bar_t &
Return the titled Bar widget.
Definition: accordion.hpp:213
Accordion(Parameters p, Args &&... args)
Create an Accordion with args... going to Widget_t constructor.
Definition: accordion.hpp:166
auto wrapped() const -> Widget_t const &
Return the wrapped widget.
Definition: accordion.hpp:210
void collapse()
Disable the wrapped Widget.
Definition: accordion.hpp:190
auto wrapped() -> Widget_t &
Return the wrapped widget.
Definition: accordion.hpp:207
Layout_t is the Layout to use for the Bar, not Accordion.
Definition: accordion.hpp:25
Holds a collection of Glyphs with a similar interface to std::string.
Definition: glyph_string.hpp:19
auto append(Glyph g) -> Glyph_string &
Append a single Glyph to the end of *this.
Definition: glyph_string.cpp:21
A single line of text with alignment, non-editable.
Definition: label.hpp:22
Contains functions to paint Glyphs to a Widget's screen area.
Definition: painter.hpp:21
auto put(Glyph tile, Point p) -> Painter &
Put single Glyph to local coordinates.
Definition: painter.cpp:18
Definition: passive.hpp:34
Definition: widget.hpp:31
virtual auto paint_event(Painter &p) -> bool
Handles Paint_event objects.
Definition: widget.cpp:248
Definition: accordion.hpp:64
Holds a description of a paintable tile on the screen.
Definition: glyph.hpp:11