18class ContainerBase :
public ComponentBase {
20 ContainerBase(
Components children,
int* selector)
23 Add(std::move(child));
28 bool OnEvent(Event event)
override {
29 if (event.is_mouse()) {
30 return OnMouseEvent(event);
37 if (ActiveChild() && ActiveChild()->OnEvent(event)) {
41 return EventHandler(event);
45 if (children().empty()) {
49 return children()[
static_cast<size_t>(*selector_) % children().size()];
52 void SetActiveChild(ComponentBase* child)
override {
53 for (
size_t i = 0; i < children().size(); ++i) {
54 if (children()[i].get() == child) {
55 *selector_ =
static_cast<int>(i);
63 virtual bool EventHandler(Event ) {
return false; }
65 virtual bool OnMouseEvent(Event event) {
66 return ComponentBase::OnEvent(std::move(event));
70 int* selector_ =
nullptr;
72 void EnsureFocusableSelection() {
73 if (!children().empty() && !ActiveChild()->Focusable()) {
78 void MoveSelector(
int dir) {
79 for (
int i = *selector_ + dir; i >= 0 && i < int(children().
size());
81 if (children()[i]->Focusable()) {
88 void MoveSelectorWrap(
int dir) {
89 if (children().empty()) {
92 for (
size_t offset = 1; offset < children().size(); ++offset) {
94 (*selector_ + offset * dir + children().size()) % children().size();
95 if (children()[i]->Focusable()) {
103class VerticalContainer :
public ContainerBase {
105 VerticalContainer(
Components children,
int* selector)
106 : ContainerBase(std::move(children),
selector) {
107 EnsureFocusableSelection();
111 EnsureFocusableSelection();
113 elements.reserve(children().
size());
114 for (
auto& it : children()) {
115 elements.push_back(it->Render());
117 if (elements.empty()) {
123 bool EventHandler(Event event)
override {
124 const int old_selected = *selector_;
125 if (event == Event::ArrowUp || event == Event::Character(
'k')) {
128 if (event == Event::ArrowDown || event == Event::Character(
'j')) {
131 if (event == Event::PageUp) {
132 for (
int i = 0; i < box_.y_max - box_.y_min; ++i) {
136 if (event == Event::PageDown) {
137 for (
int i = 0; i < box_.y_max - box_.y_min; ++i) {
141 if (event == Event::Home) {
142 for (
size_t i = 0; i < children().size(); ++i) {
146 if (event == Event::End) {
147 for (
size_t i = 0; i < children().size(); ++i) {
151 if (event == Event::Tab) {
152 MoveSelectorWrap(+1);
154 if (event == Event::TabReverse) {
155 MoveSelectorWrap(-1);
158 *selector_ = std::max(0, std::min(
int(children().
size()) - 1, *selector_));
159 return old_selected != *selector_;
162 bool OnMouseEvent(Event event)
override {
163 if (ContainerBase::OnMouseEvent(event)) {
167 if (event.mouse().button != Mouse::WheelUp &&
168 event.mouse().button != Mouse::WheelDown) {
172 if (!box_.Contain(event.mouse().x, event.mouse().y)) {
176 const int old_selected = *selector_;
177 if (event.mouse().button == Mouse::WheelUp) {
180 if (event.mouse().button == Mouse::WheelDown) {
183 *selector_ = std::max(0, std::min(
int(children().
size()) - 1, *selector_));
185 return old_selected != *selector_;
191class HorizontalContainer :
public ContainerBase {
193 HorizontalContainer(
Components children,
int* selector)
194 : ContainerBase(std::move(children),
selector) {
195 EnsureFocusableSelection();
199 EnsureFocusableSelection();
201 elements.reserve(children().
size());
202 for (
auto& it : children()) {
203 elements.push_back(it->Render());
205 if (elements.empty()) {
206 return text(
"Empty container");
208 return hbox(std::move(elements));
211 bool EventHandler(Event event)
override {
212 const int old_selected = *selector_;
213 if (event == Event::ArrowLeft || event == Event::Character(
'h')) {
216 if (event == Event::ArrowRight || event == Event::Character(
'l')) {
219 if (event == Event::Tab) {
220 MoveSelectorWrap(+1);
222 if (event == Event::TabReverse) {
223 MoveSelectorWrap(-1);
226 *selector_ = std::max(0, std::min(
int(children().
size()) - 1, *selector_));
227 return old_selected != *selector_;
231class TabContainer :
public ContainerBase {
233 using ContainerBase::ContainerBase;
236 const Component active_child = ActiveChild();
238 return active_child->Render();
240 return text(
"Empty container");
243 bool Focusable()
const override {
244 if (children().empty()) {
247 return children()[size_t(*selector_) % children().size()]->Focusable();
250 bool OnMouseEvent(Event event)
override {
251 return ActiveChild() && ActiveChild()->OnEvent(event);
255class StackedContainer :
public ContainerBase {
257 explicit StackedContainer(
Components children)
258 : ContainerBase(std::move(children), nullptr) {}
263 for (
auto& child : children()) {
264 elements.push_back(child->Render());
267 std::reverse(elements.begin(), elements.end());
268 return dbox(std::move(elements));
271 bool Focusable() const final {
272 for (
const auto& child : children()) {
273 if (child->Focusable()) {
281 if (children().empty()) {
284 return children()[0];
287 void SetActiveChild(ComponentBase* child)
final {
288 if (children().empty()) {
295 std::find_if(children().begin(), children().end(),
296 [child](
const Component& c) {
return c.get() == child; });
297 if (it == children().end()) {
300 std::rotate(children().begin(), it, it + 1);
303 bool OnEvent(Event event)
final {
304 for (
auto& child : children()) {
305 if (child->OnEvent(event)) {
331 return Vertical(std::move(children),
nullptr);
354 return std::make_shared<VerticalContainer>(std::move(children),
selector);
374 return Horizontal(std::move(children),
nullptr);
395 return std::make_shared<HorizontalContainer>(std::move(children),
selector);
416 return std::make_shared<TabContainer>(std::move(children),
selector);
441 return std::make_shared<StackedContainer>(std::move(children));
Component Horizontal(Components children, int *selector)
一個元件列表,水平地一個接一個繪製,並使用左/右箭頭鍵或 'h'/'l' 鍵進行水平導航。
Component Vertical(Components children)
一個元件列表,垂直地一個接一個繪製,並使用上/下箭頭鍵或 'j'/'k' 鍵進行垂直導航。
Component Stacked(Components children)
一個元件列表,將彼此堆疊。 事件會傳播到第一個元件,如果未處理則傳播到第二個,依此類推。 元件以給定的相反順序繪製。 當一個元件獲得焦點時,它會被放到最前面,而不改變其他元素的相對順序。
Component Tab(Components children, int *selector)
一個元件列表,一次只繪製一個並與之互動。|selector| 給出所選元件的索引。這對於實作分頁很有用。
Element text(std::string_view text)
顯示一段 UTF8 編碼的 Unicode 文字。
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
對元素的大小套用限制。
Element dbox(Elements children_)
將多個元素堆疊在一起。
Element vbox(Elements children)
一個垂直一個接一個顯示元素的容器。
FTXUI ftxui::Container:: 命名空間
std::shared_ptr< Node > Element
std::vector< Component > Components
Element hbox(Elements children)
一個逐一水平顯示元素的容器。
std::vector< Element > Elements
Decorator reflect(Box &box)
std::shared_ptr< ComponentBase > Component