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