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