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