FTXUI  5.0.0
C++ functional terminal UI.
frame.cpp
Go to the documentation of this file.
1// Copyright 2020 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 <algorithm> // for max, min
5#include <memory> // for make_shared, __shared_ptr_access
6#include <utility> // for move
7#include <vector> // for __alloc_traits<>::value_type
8
9#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, focus, frame, select, xframe, yframe
10#include "ftxui/dom/node.hpp" // for Node, Elements
11#include "ftxui/dom/requirement.hpp" // for Requirement, Requirement::FOCUSED, Requirement::SELECTED
12#include "ftxui/screen/box.hpp" // for Box
13#include "ftxui/screen/screen.hpp" // for Screen, Screen::Cursor
14#include "ftxui/util/autoreset.hpp" // for AutoReset
15
16namespace ftxui {
17
18namespace {
19class Select : public Node {
20 public:
21 explicit Select(Elements children) : Node(std::move(children)) {}
22
23 void ComputeRequirement() override {
25 requirement_ = children_[0]->requirement();
26 auto& selected_box = requirement_.selected_box;
27 selected_box.x_min = 0;
28 selected_box.y_min = 0;
29 selected_box.x_max = requirement_.min_x - 1;
30 selected_box.y_max = requirement_.min_y - 1;
31 requirement_.selection = Requirement::SELECTED;
32 }
33
34 void SetBox(Box box) override {
35 Node::SetBox(box);
36 children_[0]->SetBox(box);
37 }
38};
39
40class Focus : public Select {
41 public:
42 using Select::Select;
43
44 void ComputeRequirement() override {
45 Select::ComputeRequirement();
46 requirement_.selection = Requirement::FOCUSED;
47 }
48
49 void Render(Screen& screen) override {
50 Select::Render(screen);
51
52 // Setting the cursor to the right position allow folks using CJK (China,
53 // Japanese, Korean, ...) characters to see their [input method editor]
54 // displayed at the right location. See [issue].
55 //
56 // [input method editor]:
57 // https://en.wikipedia.org/wiki/Input_method
58 //
59 // [issue]:
60 // https://github.com/ArthurSonzogni/FTXUI/issues/2#issuecomment-505282355
61 //
62 // Unfortunately, Microsoft terminal do not handle properly hidding the
63 // cursor. Instead the character under the cursor is hidden, which is a big
64 // problem. As a result, we can't enable setting cursor to the right
65 // location. It will be displayed at the bottom right corner.
66 // See:
67 // https://github.com/microsoft/terminal/issues/1203
68 // https://github.com/microsoft/terminal/issues/3093
69#if !defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
70 screen.SetCursor(Screen::Cursor{
71 box_.x_min,
72 box_.y_min,
73 Screen::Cursor::Shape::Hidden,
74 });
75#endif
76 }
77};
78
79class Frame : public Node {
80 public:
81 Frame(Elements children, bool x_frame, bool y_frame)
82 : Node(std::move(children)), x_frame_(x_frame), y_frame_(y_frame) {}
83
84 void ComputeRequirement() override {
86 requirement_ = children_[0]->requirement();
87 }
88
89 void SetBox(Box box) override {
90 Node::SetBox(box);
91 auto& selected_box = requirement_.selected_box;
92 Box children_box = box;
93
94 if (x_frame_) {
95 const int external_dimx = box.x_max - box.x_min;
96 const int internal_dimx = std::max(requirement_.min_x, external_dimx);
97 const int focused_dimx = selected_box.x_max - selected_box.x_min;
98 int dx = selected_box.x_min - external_dimx / 2 + focused_dimx / 2;
99 dx = std::max(0, std::min(internal_dimx - external_dimx - 1, dx));
100 children_box.x_min = box.x_min - dx;
101 children_box.x_max = box.x_min + internal_dimx - dx;
102 }
103
104 if (y_frame_) {
105 const int external_dimy = box.y_max - box.y_min;
106 const int internal_dimy = std::max(requirement_.min_y, external_dimy);
107 const int focused_dimy = selected_box.y_max - selected_box.y_min;
108 int dy = selected_box.y_min - external_dimy / 2 + focused_dimy / 2;
109 dy = std::max(0, std::min(internal_dimy - external_dimy - 1, dy));
110 children_box.y_min = box.y_min - dy;
111 children_box.y_max = box.y_min + internal_dimy - dy;
112 }
113
114 children_[0]->SetBox(children_box);
115 }
116
117 void Render(Screen& screen) override {
118 const AutoReset<Box> stencil(&screen.stencil,
119 Box::Intersection(box_, screen.stencil));
120 children_[0]->Render(screen);
121 }
122
123 private:
124 bool x_frame_;
125 bool y_frame_;
126};
127
128class FocusCursor : public Focus {
129 public:
130 FocusCursor(Elements children, Screen::Cursor::Shape shape)
131 : Focus(std::move(children)), shape_(shape) {}
132
133 private:
134 void Render(Screen& screen) override {
135 Select::Render(screen); // NOLINT
136 screen.SetCursor(Screen::Cursor{
137 box_.x_min,
138 box_.y_min,
139 shape_,
140 });
141 }
143};
144
145} // namespace
146
147/// @brief Set the `child` to be the one selected among its siblings.
148/// @param child The element to be selected.
149/// @ingroup dom
151 return std::make_shared<Select>(unpack(std::move(child)));
152}
153
154/// @brief Set the `child` to be the one in focus globally.
155/// @param child The element to be focused.
156/// @ingroup dom
158 return std::make_shared<Focus>(unpack(std::move(child)));
159}
160
161/// @brief Allow an element to be displayed inside a 'virtual' area. It size can
162/// be larger than its container. In this case only a smaller portion is
163/// displayed. The view is scrollable to make the focused element visible.
164/// @see frame
165/// @see xframe
166/// @see yframe
168 return std::make_shared<Frame>(unpack(std::move(child)), true, true);
169}
170
171/// @brief Same as `frame`, but only on the x-axis.
172/// @see frame
173/// @see xframe
174/// @see yframe
176 return std::make_shared<Frame>(unpack(std::move(child)), true, false);
177}
178
179/// @brief Same as `frame`, but only on the y-axis.
180/// @see frame
181/// @see xframe
182/// @see yframe
184 return std::make_shared<Frame>(unpack(std::move(child)), false, true);
185}
186
187/// @brief Same as `focus`, but set the cursor shape to be a still block.
188/// @see focus
189/// @see focusCursorBlock
190/// @see focusCursorBlockBlinking
191/// @see focusCursorBar
192/// @see focusCursorBarBlinking
193/// @see focusCursorUnderline
194/// @see focusCursorUnderlineBlinking
195/// @ingroup dom
197 return std::make_shared<FocusCursor>(unpack(std::move(child)),
199}
200
201/// @brief Same as `focus`, but set the cursor shape to be a blinking block.
202/// @see focus
203/// @see focusCursorBlock
204/// @see focusCursorBlockBlinking
205/// @see focusCursorBar
206/// @see focusCursorBarBlinking
207/// @see focusCursorUnderline
208/// @see focusCursorUnderlineBlinking
209/// @ingroup dom
211 return std::make_shared<FocusCursor>(unpack(std::move(child)),
213}
214
215/// @brief Same as `focus`, but set the cursor shape to be a still block.
216/// @see focus
217/// @see focusCursorBlock
218/// @see focusCursorBlockBlinking
219/// @see focusCursorBar
220/// @see focusCursorBarBlinking
221/// @see focusCursorUnderline
222/// @see focusCursorUnderlineBlinking
223/// @ingroup dom
225 return std::make_shared<FocusCursor>(unpack(std::move(child)),
227}
228
229/// @brief Same as `focus`, but set the cursor shape to be a blinking bar.
230/// @see focus
231/// @see focusCursorBlock
232/// @see focusCursorBlockBlinking
233/// @see focusCursorBar
234/// @see focusCursorBarBlinking
235/// @see focusCursorUnderline
236/// @see focusCursorUnderlineBlinking
237/// @ingroup dom
239 return std::make_shared<FocusCursor>(unpack(std::move(child)),
241}
242
243/// @brief Same as `focus`, but set the cursor shape to be a still underline.
244/// @see focus
245/// @see focusCursorBlock
246/// @see focusCursorBlockBlinking
247/// @see focusCursorBar
248/// @see focusCursorBarBlinking
249/// @see focusCursorUnderline
250/// @see focusCursorUnderlineBlinking
251/// @ingroup dom
253 return std::make_shared<FocusCursor>(unpack(std::move(child)),
255}
256
257/// @brief Same as `focus`, but set the cursor shape to be a blinking underline.
258/// @see focus
259/// @see focusCursorBlock
260/// @see focusCursorBlockBlinking
261/// @see focusCursorBar
262/// @see focusCursorBarBlinking
263/// @see focusCursorUnderline
264/// @see focusCursorUnderlineBlinking
265/// @ingroup dom
267 return std::make_shared<FocusCursor>(unpack(std::move(child)),
269}
270
271} // namespace ftxui
virtual void SetBox(Box box)
Assign a position and a dimension to an element for drawing.
Definition: node.cpp:26
virtual void ComputeRequirement()
Compute how much space an elements needs.
Definition: node.cpp:18
Element focusCursorBarBlinking(Element)
Same as focus, but set the cursor shape to be a blinking bar.
Definition: frame.cpp:238
std::shared_ptr< Node > Element
Definition: elements.hpp:23
Element xframe(Element)
Same as frame, but only on the x-axis.
Definition: frame.cpp:175
Element focusCursorUnderlineBlinking(Element)
Same as focus, but set the cursor shape to be a blinking underline.
Definition: frame.cpp:266
Element focusCursorBar(Element)
Same as focus, but set the cursor shape to be a still block.
Definition: frame.cpp:224
Element focusCursorBlock(Element)
Same as focus, but set the cursor shape to be a still block.
Definition: frame.cpp:196
Element focusCursorUnderline(Element)
Same as focus, but set the cursor shape to be a still underline.
Definition: frame.cpp:252
std::vector< Element > Elements
Definition: elements.hpp:24
Element yframe(Element)
Same as frame, but only on the y-axis.
Definition: frame.cpp:183
Element select(Element)
Set the child to be the one selected among its siblings.
Definition: frame.cpp:150
Element focus(Element)
Set the child to be the one in focus globally.
Definition: frame.cpp:157
Element frame(Element)
Allow an element to be displayed inside a 'virtual' area. It size can be larger than its container....
Definition: frame.cpp:167
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition: node.cpp:47
Element focusCursorBlockBlinking(Element)
Same as focus, but set the cursor shape to be a blinking block.
Definition: frame.cpp:210
static auto Intersection(Box a, Box b) -> Box
Definition: box.cpp:12