FTXUI 7.0.3
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/component/window.cpp
Go to the documentation of this file.
1// Copyright 2023 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#define NOMINMAX
5#include <algorithm>
6#include <ftxui/component/app.hpp> // for App
10#include <memory>
11#include <utility>
12#include "ftxui/dom/elements.hpp" // for text, window, hbox, vbox, size, clear_under, reflect, emptyElement
13#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
14#include "ftxui/screen/color.hpp" // for Color
15#include "ftxui/screen/screen.hpp" // for Screen
16
17namespace ftxui {
18
19namespace {
20
21Decorator PositionAndSize(int left, int top, int width, int height) {
22 return [=](Element element) {
23 element |= size(WIDTH, EQUAL, width);
24 element |= size(HEIGHT, EQUAL, height);
25
26 auto padding_left = emptyElement() | size(WIDTH, EQUAL, left);
27 auto padding_top = emptyElement() | size(HEIGHT, EQUAL, top);
28
29 return vbox({
30 padding_top,
31 hbox({
32 padding_left,
33 element,
34 }),
35 });
36 };
37}
38
39class ResizeDecorator : public NodeDecorator {
40 public:
41 ResizeDecorator(Element child,
42 bool resize_left,
43 bool resize_right,
44 bool resize_top,
45 bool resize_down,
46 Color color)
47 : NodeDecorator(std::move(child)),
48 color_(color),
49 resize_left_(resize_left),
50 resize_right_(resize_right),
51 resize_top_(resize_top),
52 resize_down_(resize_down) {}
53
54 void Render(Screen& screen) override {
55 NodeDecorator::Render(screen);
56
57 if (resize_left_) {
58 for (int y = box_.y_min; y <= box_.y_max; ++y) {
59 auto& cell = screen.CellAt(box_.x_min, y);
60 cell.foreground_color = color_;
61 cell.automerge = false;
62 }
63 }
64 if (resize_right_) {
65 for (int y = box_.y_min; y <= box_.y_max; ++y) {
66 auto& cell = screen.CellAt(box_.x_max, y);
67 cell.foreground_color = color_;
68 cell.automerge = false;
69 }
70 }
71 if (resize_top_) {
72 for (int x = box_.x_min; x <= box_.x_max; ++x) {
73 auto& cell = screen.CellAt(x, box_.y_min);
74 cell.foreground_color = color_;
75 cell.automerge = false;
76 }
77 }
78 if (resize_down_) {
79 for (int x = box_.x_min; x <= box_.x_max; ++x) {
80 auto& cell = screen.CellAt(x, box_.y_max);
81 cell.foreground_color = color_;
82 cell.automerge = false;
83 }
84 }
85 }
86
87 Color color_;
88 const bool resize_left_;
89 const bool resize_right_;
90 const bool resize_top_;
91 const bool resize_down_;
92};
93
94Element DefaultRenderState(const WindowRenderState& state) {
95 Element element = state.inner;
96 if (!state.active) {
97 element |= dim;
98 }
99
100 element = window(text(state.title), element);
102
103 const Color color = Color::Red;
104
105 element = std::make_shared<ResizeDecorator>( //
106 element, //
107 state.hover_left, //
108 state.hover_right, //
109 state.hover_top, //
110 state.hover_down, //
111 color //
112 );
113
114 return element;
115}
116
117class WindowImpl : public ComponentBase, public WindowOptions {
118 public:
119 explicit WindowImpl(WindowOptions option) : WindowOptions(std::move(option)) {
120 if (!inner) {
121 inner = Make<ComponentBase>();
122 }
123 Add(inner);
124 }
125
126 private:
127 Element OnRender() final {
128 auto element = ComponentBase::Render();
129
130 const bool captureable =
131 captured_mouse_ || (App::Active() && App::Active()->CaptureMouse());
132
133 const WindowRenderState state = {
134 element,
135 title(),
136 Active(),
137 drag_,
139 (resize_left_hover_ || resize_left_) && captureable,
140 (resize_right_hover_ || resize_right_) && captureable,
141 (resize_top_hover_ || resize_top_) && captureable,
142 (resize_down_hover_ || resize_down_) && captureable,
143 };
144
145 element = render ? render(state) : DefaultRenderState(state);
146
147 // Positionne et enregistre la zone dessinée de la fenêtre.
148 element |= reflect(box_window_);
149 element |= PositionAndSize(left(), top(), width(), height());
150 element |= reflect(box_);
151
152 return element;
153 }
154
155 bool OnEvent(Event event) final {
156 if (ComponentBase::OnEvent(event)) {
157 return true;
158 }
159
160 if (!event.is_mouse()) {
161 return false;
162 }
163
164 mouse_hover_ = box_window_.Contain(event.mouse().x, event.mouse().y);
165
166 resize_down_hover_ = false;
167 resize_top_hover_ = false;
168 resize_left_hover_ = false;
169 resize_right_hover_ = false;
170
171 if (mouse_hover_) {
172 resize_left_hover_ = event.mouse().x == left() + box_.x_min;
173 resize_right_hover_ =
174 event.mouse().x == left() + width() - 1 + box_.x_min;
175 resize_top_hover_ = event.mouse().y == top() + box_.y_min;
176 resize_down_hover_ = event.mouse().y == top() + height() - 1 + box_.y_min;
177
178 // Applique les options du composant :
179 resize_top_hover_ &= resize_top();
180 resize_left_hover_ &= resize_left();
181 resize_down_hover_ &= resize_down();
182 resize_right_hover_ &= resize_right();
183 }
184
185 if (captured_mouse_) {
186 if (event.mouse().motion == Mouse::Released) {
187 captured_mouse_ = nullptr;
188 return true;
189 }
190
191 if (resize_left_) {
192 width() = left() + width() - event.mouse().x + box_.x_min;
193 left() = event.mouse().x - box_.x_min;
194 }
195
196 if (resize_right_) {
197 width() = event.mouse().x - resize_start_x - box_.x_min;
198 }
199
200 if (resize_top_) {
201 height() = top() + height() - event.mouse().y + box_.y_min;
202 top() = event.mouse().y - box_.y_min;
203 }
204
205 if (resize_down_) {
206 height() = event.mouse().y - resize_start_y - box_.y_min;
207 }
208
209 if (drag_) {
210 left() = event.mouse().x - drag_start_x - box_.x_min;
211 top() = event.mouse().y - drag_start_y - box_.y_min;
212 }
213
214 // Limite la taille de la fenêtre.
215 width() = std::max<int>(width(), static_cast<int>(title().size() + 2));
216 height() = std::max<int>(height(), 2);
217
218 return true;
219 }
220
221 resize_left_ = false;
222 resize_right_ = false;
223 resize_top_ = false;
224 resize_down_ = false;
225
226 if (!mouse_hover_) {
227 return false;
228 }
229
230 if (!CaptureMouse(event)) {
231 return true;
232 }
233
234 if (event.mouse().button != Mouse::Left) {
235 return true;
236 }
237 if (event.mouse().motion != Mouse::Pressed) {
238 return true;
239 }
240
241 TakeFocus();
242
243 captured_mouse_ = CaptureMouse(event);
244 if (!captured_mouse_) {
245 return true;
246 }
247
248 resize_left_ = resize_left_hover_;
249 resize_right_ = resize_right_hover_;
250 resize_top_ = resize_top_hover_;
251 resize_down_ = resize_down_hover_;
252
253 resize_start_x = event.mouse().x - width() - box_.x_min;
254 resize_start_y = event.mouse().y - height() - box_.y_min;
255 drag_start_x = event.mouse().x - left() - box_.x_min;
256 drag_start_y = event.mouse().y - top() - box_.y_min;
257
258 // Glisser uniquement si nous ne redimensionnons pas encore une bordure :
260 return true;
261 }
262
263 Box box_;
264 Box box_window_;
265
266 CapturedMouse captured_mouse_;
267 int drag_start_x = 0;
268 int drag_start_y = 0;
269 int resize_start_x = 0;
270 int resize_start_y = 0;
271
272 bool mouse_hover_ = false;
273 bool drag_ = false;
274 bool resize_top_ = false;
275 bool resize_left_ = false;
276 bool resize_down_ = false;
277 bool resize_right_ = false;
278
279 bool resize_top_hover_ = false;
280 bool resize_left_hover_ = false;
281 bool resize_down_hover_ = false;
282 bool resize_right_hover_ = false;
283};
284
285} // namespace
286
287/// @brief Une fenêtre déplaçable / redimensionnable. Pour en utiliser plusieurs, elles doivent
288/// être empilées à l'aide du composant `Container::Stacked({...})`;
289///
290/// @param option Une structure contenant tous les paramètres.
291/// @ingroup component
292/// @see Window
293///
294/// ### Exemple
295///
296/// ```cpp
297/// auto window_1= Window({
298/// .inner = DummyWindowContent(),
299/// .title = "First window",
300/// });
301///
302/// auto window_2= Window({
303/// .inner = DummyWindowContent(),
304/// .title = "Second window",
305/// });
306///
307/// auto container = Container::Stacked({
308/// window_1,
309/// window_2,
310/// });
311Component Window(WindowOptions option) {
312 return Make<WindowImpl>(std::move(option));
313}
314
315}; // namespace ftxui
Element window(Element title, Element content, BorderStyle border=ROUNDED)
Dessine une fenêtre avec un titre et une bordure autour de l'élément.
Element text(std::string_view text)
Affiche un morceau de texte unicode encodé en UTF8.
Element clear_under(Element element)
Avant de dessiner |child|, efface les cellules en dessous. Ceci est utile en combinaison avec dbox.
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
Applique une contrainte sur la taille d'un élément.
Element emptyElement()
Definition dom/util.cpp:140
Element dim(Element child)
Utilise une police de caractères claire, pour les éléments moins mis en valeur.
Definition dim.cpp:33
Element vbox(Elements children)
Un conteneur affichant les éléments verticalement un par un.
Definition vbox.cpp:96
Component Window(std::string title, Component component)
L'espace de noms FTXUI ftxui::
Definition animation.hpp:11
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:28
std::shared_ptr< Node > Element
Definition elements.hpp:25
Element hbox(Elements children)
Un conteneur affichant les éléments horizontalement un par un.
Definition hbox.cpp:94
std::function< Element(Element)> Decorator
Definition elements.hpp:27
int y
Definition elements.hpp:134
const Element & element
Definition node.hpp:94
Decorator reflect(Box &box)
Definition reflect.cpp:43
void Render(Screen &screen, Node *node, Selection &selection)
Definition node.cpp:105
std::shared_ptr< ComponentBase > Component
Definition app.hpp:23
std::uint8_t top
Definition screen.cpp:148
std::uint8_t left
Definition screen.cpp:147
const bool resize_left_
const bool resize_top_
const bool resize_right_
const bool resize_down_