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 
16 namespace ftxui {
17 
18 namespace {
19 class 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 
40 class 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 
79 class 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 
128 class 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  }
142  Screen::Cursor::Shape shape_;
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