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