FTXUI  0.11.1
C++ functional terminal UI.
resizable_split.cpp
Go to the documentation of this file.
1 #include <memory> // for __shared_ptr_access
2 #include <utility> // for move
3 
4 #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
5 #include "ftxui/component/component.hpp" // for Component, Make, Horizontal, Vertical, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
6 #include "ftxui/component/component_base.hpp" // for ComponentBase
7 #include "ftxui/component/event.hpp" // for Event
8 #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
9 #include "ftxui/dom/elements.hpp" // for operator|, reflect, Element, separator, size, EQUAL, xflex, yflex, hbox, vbox, HEIGHT, WIDTH
10 #include "ftxui/screen/box.hpp" // for Box
11 
12 namespace ftxui {
13 namespace {
14 
15 class ResizableSplitLeftBase : public ComponentBase {
16  public:
17  ResizableSplitLeftBase(Component main, Component child, int* main_size)
18  : main_(main), child_(child), main_size_(main_size) {
20  main,
21  child,
22  }));
23  }
24 
25  bool OnEvent(Event event) final {
26  if (event.is_mouse())
27  return OnMouseEvent(std::move(event));
28  return ComponentBase::OnEvent(std::move(event));
29  }
30 
31  bool OnMouseEvent(Event event) {
32  if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
33  captured_mouse_.reset();
34  return true;
35  }
36 
37  if (event.mouse().button == Mouse::Left &&
38  event.mouse().motion == Mouse::Pressed &&
39  separator_box_.Contain(event.mouse().x, event.mouse().y) &&
40  !captured_mouse_) {
41  captured_mouse_ = CaptureMouse(event);
42  return true;
43  }
44 
45  if (captured_mouse_) {
46  *main_size_ = event.mouse().x - box_.x_min;
47  return true;
48  }
49 
50  return ComponentBase::OnEvent(event);
51  }
52 
53  Element Render() final {
54  return hbox({
55  main_->Render() | size(WIDTH, EQUAL, *main_size_),
56  separator() | reflect(separator_box_),
57  child_->Render() | xflex,
58  }) |
59  reflect(box_);
60  };
61 
62  private:
63  Component main_;
64  Component child_;
65  int* const main_size_;
66  CapturedMouse captured_mouse_;
67  Box separator_box_;
68  Box box_;
69 };
70 
71 class ResizableSplitRightBase : public ComponentBase {
72  public:
73  ResizableSplitRightBase(Component main, Component child, int* main_size)
74  : main_(main), child_(child), main_size_(main_size) {
76  child,
77  main,
78  }));
79  }
80 
81  bool OnEvent(Event event) final {
82  if (event.is_mouse())
83  return OnMouseEvent(std::move(event));
84  return ComponentBase::OnEvent(std::move(event));
85  }
86 
87  bool OnMouseEvent(Event event) {
88  if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
89  captured_mouse_.reset();
90  return true;
91  }
92 
93  if (event.mouse().button == Mouse::Left &&
94  event.mouse().motion == Mouse::Pressed &&
95  separator_box_.Contain(event.mouse().x, event.mouse().y) &&
96  !captured_mouse_) {
97  captured_mouse_ = CaptureMouse(event);
98  return true;
99  }
100 
101  if (captured_mouse_) {
102  *main_size_ = box_.x_max - event.mouse().x;
103  return true;
104  }
105 
106  return ComponentBase::OnEvent(event);
107  }
108 
109  Element Render() final {
110  return hbox({
111  child_->Render() | xflex,
112  separator() | reflect(separator_box_),
113  main_->Render() | size(WIDTH, EQUAL, *main_size_),
114  }) |
115  reflect(box_);
116  };
117 
118  private:
119  Component main_;
120  Component child_;
121  int* const main_size_;
122  CapturedMouse captured_mouse_;
123  Box separator_box_;
124  Box box_;
125 };
126 
127 class ResizableSplitTopBase : public ComponentBase {
128  public:
129  ResizableSplitTopBase(Component main, Component child, int* main_size)
130  : main_(main), child_(child), main_size_(main_size) {
131  Add(Container::Vertical({
132  main,
133  child,
134  }));
135  }
136 
137  bool OnEvent(Event event) final {
138  if (event.is_mouse())
139  return OnMouseEvent(std::move(event));
140  return ComponentBase::OnEvent(std::move(event));
141  }
142 
143  bool OnMouseEvent(Event event) {
144  if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
145  captured_mouse_.reset();
146  return true;
147  }
148 
149  if (event.mouse().button == Mouse::Left &&
150  event.mouse().motion == Mouse::Pressed &&
151  separator_box_.Contain(event.mouse().x, event.mouse().y) &&
152  !captured_mouse_) {
153  captured_mouse_ = CaptureMouse(event);
154  return true;
155  }
156 
157  if (captured_mouse_) {
158  *main_size_ = event.mouse().y - box_.y_min;
159  return true;
160  }
161 
162  return ComponentBase::OnEvent(event);
163  }
164 
165  Element Render() final {
166  return vbox({
167  main_->Render() | size(HEIGHT, EQUAL, *main_size_),
168  separator() | reflect(separator_box_),
169  child_->Render() | yflex,
170  }) |
171  reflect(box_);
172  };
173 
174  private:
175  Component main_;
176  Component child_;
177  int* const main_size_;
178  CapturedMouse captured_mouse_;
179  Box separator_box_;
180  Box box_;
181 };
182 
183 class ResizableSplitBottomBase : public ComponentBase {
184  public:
185  ResizableSplitBottomBase(Component main, Component child, int* main_size)
186  : main_(main), child_(child), main_size_(main_size) {
187  Add(Container::Vertical({
188  child,
189  main,
190  }));
191  }
192 
193  bool OnEvent(Event event) final {
194  if (event.is_mouse())
195  return OnMouseEvent(std::move(event));
196  return ComponentBase::OnEvent(std::move(event));
197  }
198 
199  bool OnMouseEvent(Event event) {
200  if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
201  captured_mouse_.reset();
202  return true;
203  }
204 
205  if (event.mouse().button == Mouse::Left &&
206  event.mouse().motion == Mouse::Pressed &&
207  separator_box_.Contain(event.mouse().x, event.mouse().y) &&
208  !captured_mouse_) {
209  captured_mouse_ = CaptureMouse(event);
210  return true;
211  }
212 
213  if (captured_mouse_) {
214  *main_size_ = box_.y_max - event.mouse().y;
215  return true;
216  }
217 
218  return ComponentBase::OnEvent(event);
219  }
220 
221  Element Render() final {
222  return vbox({
223  child_->Render() | yflex,
224  separator() | reflect(separator_box_),
225  main_->Render() | size(HEIGHT, EQUAL, *main_size_),
226  }) |
227  reflect(box_);
228  };
229 
230  private:
231  Component main_;
232  Component child_;
233  int* const main_size_;
234  CapturedMouse captured_mouse_;
235  Box separator_box_;
236  Box box_;
237 };
238 
239 } // namespace
240 
241 /// @brief An horizontal split in between two components, configurable using the
242 /// mouse.
243 /// @param main The main component of size |main_size|, on the left.
244 /// @param back The back component taking the remaining size, on the right.
245 /// @param main_size The size of the |main| component.
246 /// @ingroup component
247 ///
248 /// ### Example
249 ///
250 /// ```cpp
251 /// auto screen = ScreenInteractive::Fullscreen();
252 /// int left_size = 10;
253 /// auto left = Renderer([] { return text("Left") | center;});
254 /// auto right = Renderer([] { return text("right") | center;});
255 /// auto split = ResizableSplitLeft(left, right, &left_size);
256 /// screen.Loop(split);
257 /// ```
258 ///
259 /// ### Output
260 ///
261 /// ```bash
262 /// │
263 /// left │ right
264 /// │
265 /// ```
266 Component ResizableSplitLeft(Component main, Component back, int* main_size) {
267  return Make<ResizableSplitLeftBase>(std::move(main), std::move(back),
268  main_size);
269 }
270 
271 /// @brief An horizontal split in between two components, configurable using the
272 /// mouse.
273 /// @param main The main component of size |main_size|, on the right.
274 /// @param back The back component taking the remaining size, on the left.
275 /// @param main_size The size of the |main| component.
276 /// @ingroup component
277 ///
278 /// ### Example
279 ///
280 /// ```cpp
281 /// auto screen = ScreenInteractive::Fullscreen();
282 /// int right_size = 10;
283 /// auto left = Renderer([] { return text("Left") | center;});
284 /// auto right = Renderer([] { return text("right") | center;});
285 /// auto split = ResizableSplitRight(right, left, &right_size);
286 /// screen.Loop(split);
287 /// ```
288 ///
289 /// ### Output
290 ///
291 /// ```bash
292 /// │
293 /// left │ right
294 /// │
295 /// ```
296 Component ResizableSplitRight(Component main, Component back, int* main_size) {
297  return Make<ResizableSplitRightBase>(std::move(main), std::move(back),
298  main_size);
299 }
300 
301 /// @brief An vertical split in between two components, configurable using the
302 /// mouse.
303 /// @param main The main component of size |main_size|, on the top.
304 /// @param back The back component taking the remaining size, on the bottom.
305 /// @param main_size The size of the |main| component.
306 /// @ingroup component
307 ///
308 /// ### Example
309 ///
310 /// ```cpp
311 /// auto screen = ScreenInteractive::Fullscreen();
312 /// int top_size = 1;
313 /// auto top = Renderer([] { return text("Top") | center;});
314 /// auto bottom = Renderer([] { return text("Bottom") | center;});
315 /// auto split = ResizableSplitTop(top, bottom, &top_size);
316 /// screen.Loop(split);
317 /// ```
318 ///
319 /// ### Output
320 ///
321 /// ```bash
322 /// top
323 /// ────────────
324 /// bottom
325 /// ```
326 Component ResizableSplitTop(Component main, Component back, int* main_size) {
327  return Make<ResizableSplitTopBase>(std::move(main), std::move(back),
328  main_size);
329 }
330 
331 /// @brief An vertical split in between two components, configurable using the
332 /// mouse.
333 /// @param main The main component of size |main_size|, on the bottom.
334 /// @param back The back component taking the remaining size, on the top.
335 /// @param main_size The size of the |main| component.
336 /// @ingroup component
337 ///
338 /// ### Example
339 ///
340 /// ```cpp
341 /// auto screen = ScreenInteractive::Fullscreen();
342 /// int bottom_size = 1;
343 /// auto top = Renderer([] { return text("Top") | center;});
344 /// auto bottom = Renderer([] { return text("Bottom") | center;});
345 /// auto split = ResizableSplit::Bottom(bottom, top, &bottom_size);
346 /// screen.Loop(split);
347 /// ```
348 ///
349 /// ### Output
350 ///
351 /// ```bash
352 /// top
353 /// ────────────
354 /// bottom
355 /// ```
356 Component ResizableSplitBottom(Component main, Component back, int* main_size) {
357  return Make<ResizableSplitBottomBase>(std::move(main), std::move(back),
358  main_size);
359 }
360 } // namespace ftxui
361 
362 // Copyright 2021 Arthur Sonzogni. All rights reserved.
363 // Use of this source code is governed by the MIT license that can be found in
364 // the LICENSE file.
virtual bool OnEvent(Event)
Called in response to an event.
Definition: component.cpp:95
Component Horizontal(Components children)
A list of components, drawn one by one horizontally and navigated horizontally using left/right arrow...
Definition: container.cpp:259
Component Vertical(Components children)
A list of components, drawn one by one vertically and navigated vertically using up/down arrow key or...
Definition: container.cpp:216
Element xflex(Element)
Expand/Minimize if possible/needed on the X axis.
Definition: flex.cpp:125
@ HEIGHT
Definition: elements.hpp:104
@ WIDTH
Definition: elements.hpp:104
Component ResizableSplitTop(Component main, Component back, int *main_size)
An vertical split in between two components, configurable using the mouse.
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< Node > Element
Definition: elements.hpp:16
std::shared_ptr< ComponentBase > Component
Element yflex(Element)
Expand/Minimize if possible/needed on the Y axis.
Definition: flex.cpp:131
Element hbox(Elements)
A container displaying elements horizontally one by one.
Definition: hbox.cpp:76
Component ResizableSplitRight(Component main, Component back, int *main_size)
An horizontal split in between two components, configurable using the mouse.
Decorator reflect(Box &box)
Definition: reflect.cpp:39
Element separator(void)
Draw a vertical or horizontal separation in between two other elements.
Definition: separator.cpp:114
Component ResizableSplitBottom(Component main, Component back, int *main_size)
An vertical split in between two components, configurable using the mouse.
void Render(Screen &screen, const Element &node)
Display an element on a ftxui::Screen.
Definition: node.cpp:40
Decorator size(Direction, Constraint, int value)
Apply a constraint on the size of an element.
Definition: size.cpp:86
@ EQUAL
Definition: elements.hpp:105
Component ResizableSplitLeft(Component main, Component back, int *main_size)
An horizontal split in between two components, configurable using the mouse.
Element vbox(Elements)
A container displaying elements vertically one by one.
Definition: vbox.cpp:77