Skip to content

Commit e408edb

Browse files
committed
feat: Add TUIResizableSplit and TUIScrollableContainer widgets; update examples and documentation
1 parent 5e5eb5c commit e408edb

6 files changed

Lines changed: 61 additions & 31 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
/build/
33
/external/
44
/docs
5-
/log.txt
5+
/log.txt
6+
debug_log.txt

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Based on the `examples/main.cpp` and the current codebase, the following core co
3131
* `TUITabWidget`: Organizes content into multiple tabs.
3232
* `TUIStatusBar`: Displays status messages at the bottom of the application.
3333
* `TUITreeView`: Displays hierarchical data in a tree structure.
34+
* `TUIResizableSplit`: Splitter for resizable panels (horizontal/vertical).
35+
* `TUIScrollableContainer`: Scrollable area for any widget (with scrollbar, keyboard and mouse support).
3436

3537
## 🛠️ Getting Started
3638

examples/main.cpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,13 @@ int main() {
178178

179179
// TUITreeView Example
180180
TreeNode root_tree_node = {"Root", {
181-
{"Child 1", {}},
181+
{"Child 1", {}, false, nullptr},
182182
{"Child 2", {
183-
{"Grandchild 2.1", {}},
184-
{"Grandchild 2.2", {}}
185-
}},
186-
{"Child 3", {}}
187-
}};
183+
{"Grandchild 2.1", {}, false, nullptr},
184+
{"Grandchild 2.2", {}, false, nullptr}
185+
}, false, nullptr},
186+
{"Child 3", {}, false, nullptr}
187+
}, false, nullptr};
188188
auto tree_view = treeview(root_tree_node);
189189
auto tree_view_label = label("Selected Tree Item: None");
190190
tree_view->onSelect([&](const std::string& selected_node_text) {
@@ -195,6 +195,29 @@ int main() {
195195
tree_view_group->addWidget(tree_view_label);
196196
advanced_widgets_content->addWidget(groupbox("Tree View", tree_view_group));
197197

198+
// TUIResizableSplit Example
199+
auto left_panel = label("Left Panel");
200+
auto right_panel = label("Right Panel");
201+
auto resizable_split_widget = resizable_split(left_panel, right_panel, TUIResizableSplit::Horizontal);
202+
advanced_widgets_content->addWidget(groupbox("Resizable Split", resizable_split_widget));
203+
204+
// TUIScrollableContainer Example
205+
std::vector<std::string> scroll_items;
206+
for (int i = 0; i < 20; ++i) {
207+
scroll_items.push_back("Scrollable Item " + std::to_string(i));
208+
}
209+
auto scroll_menu = menu(scroll_items);
210+
auto scroll_selected_label = label("Selected: None");
211+
connect(scroll_menu, [&](int idx) {
212+
if (idx >= 0 && idx < (int)scroll_items.size())
213+
scroll_selected_label->setText("Selected: " + scroll_items[idx]);
214+
});
215+
auto scrollable_container_widget = scrollable_container(scroll_menu, 10);
216+
auto scrollable_group = vbox();
217+
scrollable_group->addWidget(scrollable_container_widget);
218+
scrollable_group->addWidget(scroll_selected_label);
219+
advanced_widgets_content->addWidget(groupbox("Scrollable Container", scrollable_group));
220+
198221
// --- Tab Widget ---
199222
auto tab_widget = tabwidget();
200223
tab_widget->addTab("Main Widgets", main_widgets_content, ICON::Home);

include/tuikit/widgets/TUIScrollableContainer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class TUIScrollableContainer : public TUIWidget {
1515
private:
1616
std::shared_ptr<TUIWidget> content_;
1717
int height_;
18+
int scroll_position_ = 0;
1819
};
1920

2021
} // namespace TUIKIT

src/widgets/TUIScrollableContainer.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22
#include <ftxui/component/component.hpp>
33
#include <ftxui/dom/elements.hpp>
44
#include <ftxui/component/event.hpp>
5-
#include <iostream> // For debug output
65

76
namespace TUIKIT {
87

98
TUIScrollableContainer::TUIScrollableContainer(std::shared_ptr<TUIWidget> content, int height, TUIWidget* /*parent*/)
10-
: TUIWidget(content->get_ftxui_component() | ftxui::vscroll_indicator | ftxui::frame | ftxui::size(ftxui::HEIGHT, ftxui::EQUAL, height)), content_(content), height_(height) {
9+
: TUIWidget(ftxui::Component()), content_(content), height_(height) {
10+
// On applique frame et vscroll_indicator sur le composant interne (menu ou autre)
11+
auto menu_component = content->get_ftxui_component();
12+
auto renderer = ftxui::Renderer(menu_component, [menu_component, height] {
13+
return menu_component->Render()
14+
| ftxui::frame
15+
| ftxui::vscroll_indicator
16+
| ftxui::size(ftxui::HEIGHT, ftxui::EQUAL, height);
17+
});
18+
component_ = renderer;
1119
}
1220

13-
} // namespace TUIKIT
21+
} // namespace TUIKIT

src/widgets/TUITreeView.cpp

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,6 @@ void TUITreeView::onSelect(OnSelectCallback cb) {
1919
void TUITreeView::buildComponent(TreeNode& node, int depth) {
2020
using namespace ftxui;
2121

22-
auto on_click = [this, &node] {
23-
node.is_expanded = !node.is_expanded;
24-
if (on_select_) {
25-
on_select_(node.label);
26-
}
27-
};
28-
2922
if (node.children.empty()) {
3023
auto option = ButtonOption();
3124
option.transform = [=, &node](const EntryState& state) {
@@ -38,7 +31,11 @@ void TUITreeView::buildComponent(TreeNode& node, int depth) {
3831
}
3932
return element;
4033
};
41-
node.component = Button(&node.label, on_click, option);
34+
node.component = Button(&node.label, [this, &node] {
35+
if (on_select_) {
36+
on_select_(node.label);
37+
}
38+
}, option);
4239
} else {
4340
std::vector<Component> children_components;
4441
for (auto& child : node.children) {
@@ -47,25 +44,23 @@ void TUITreeView::buildComponent(TreeNode& node, int depth) {
4744
}
4845
auto children_container = Container::Vertical(children_components);
4946

50-
auto header_button = Button(&node.label, on_click);
51-
52-
auto combined = Container::Vertical({header_button, children_container});
53-
54-
node.component = Renderer(combined, [=, &node] {
47+
auto option = ButtonOption();
48+
option.transform = [=, &node](const EntryState& state) {
5549
std::string indentation = std::string(depth * 3, ' ');
5650
std::string icon = node.is_expanded ? "" : "";
57-
auto header_element = hbox({
51+
auto element = hbox({
5852
text(indentation + icon + node.label)
5953
});
60-
if (header_button->Focused()) {
61-
header_element = header_element | inverted;
54+
if (state.focused) {
55+
element = element | inverted;
6256
}
57+
return element;
58+
};
59+
auto header_button = Button(&node.label, [&node] { node.is_expanded = !node.is_expanded; }, option);
60+
61+
auto combined = Container::Vertical({header_button, Maybe(children_container, &node.is_expanded)});
6362

64-
if (node.is_expanded) {
65-
return vbox({header_element, children_container->Render()});
66-
}
67-
return header_element;
68-
});
63+
node.component = combined;
6964
}
7065
}
7166

0 commit comments

Comments
 (0)