|
1 | 1 | #include "tuikit/widgets/TUITreeView.h" |
2 | | -#include "tuikit/layouts/TUIVBoxLayout.h" |
3 | | -#include "tuikit/widgets/TUICollapsible.h" |
4 | | -#include "tuikit/widgets/TUILabel.h" |
5 | | -#include <ftxui/component/component.hpp> |
| 2 | +#include "ftxui/component/component.hpp" |
| 3 | +#include "ftxui/component/screen_interactive.hpp" |
| 4 | +#include "ftxui/dom/elements.hpp" |
| 5 | +#include "ftxui/component/component_options.hpp" |
6 | 6 |
|
7 | 7 | namespace TUIKIT { |
8 | 8 |
|
9 | | -TUITreeView::TUITreeView(TreeNode root_node, TUIWidget* /*parent*/) |
| 9 | +TUITreeView::TUITreeView(TreeNode root_node) |
10 | 10 | : TUIWidget(ftxui::Component()), root_node_(std::move(root_node)) { |
11 | | - |
12 | | - main_layout_ = std::make_shared<TUIVBoxLayout>(); |
13 | | - component_ = main_layout_->get_ftxui_component(); |
14 | | - |
15 | | - // Create the root collapsible node |
16 | | - auto root_collapsible = createCollapsibleNode(root_node_); |
17 | | - main_layout_->addWidget(root_collapsible); |
| 11 | + buildComponent(root_node_, 0); |
| 12 | + component_ = root_node_.component; |
18 | 13 | } |
19 | 14 |
|
20 | 15 | void TUITreeView::onSelect(OnSelectCallback cb) { |
21 | 16 | on_select_ = std::move(cb); |
22 | 17 | } |
23 | 18 |
|
24 | | -std::shared_ptr<TUICollapsible> TUITreeView::createCollapsibleNode(const TreeNode& node) { |
25 | | - auto content_layout = std::make_shared<TUIVBoxLayout>(); |
26 | | - |
27 | | - // Create a label for the current node's content (or a button for selection) |
28 | | - auto node_label = std::make_shared<TUILabel>(node.label); |
29 | | - // Connect a click event to the label to simulate selection |
30 | | - // This requires TUILabel to have an onClick or similar signal, which it currently doesn't. |
31 | | - // For now, we'll just make it a label. If selection is critical, we might need a small button or a custom component. |
32 | | - // For demonstration, we'll trigger on_select_ when the collapsible is expanded/collapsed. |
33 | | - // A better approach would be to make the label itself clickable or add a small invisible button. |
34 | | - |
35 | | - // Recursively create children |
36 | | - for (const auto& child : node.children) { |
37 | | - auto child_collapsible = createCollapsibleNode(child); |
38 | | - content_layout->addWidget(child_collapsible); |
39 | | - } |
| 19 | +void TUITreeView::buildComponent(TreeNode& node, int depth) { |
| 20 | + using namespace ftxui; |
| 21 | + |
| 22 | + auto on_click = [this, &node] { |
| 23 | + node.is_expanded = !node.is_expanded; |
| 24 | + if (on_select_) { |
| 25 | + on_select_(node.label); |
| 26 | + } |
| 27 | + }; |
40 | 28 |
|
41 | | - auto collapsible = std::make_shared<TUICollapsible>(node.label, content_layout); |
42 | | - |
43 | | - // If a node is selected, trigger the callback |
44 | | - // This is a placeholder. A proper selection mechanism would involve a clickable element inside the collapsible. |
45 | | - // For now, we'll just use the collapsible's title as the selected item. |
46 | | - // This part needs refinement based on how selection is truly intended. |
47 | | - // For now, let's assume expanding/collapsing implies a form of interaction. |
48 | | - // A better way would be to add a small button next to the label or make the label itself interactive. |
49 | | - // Since TUILabel doesn't have an onClick, we'll rely on the user clicking the collapsible title. |
50 | | - // If the user clicks the collapsible title, we can consider it "selected". |
51 | | - // This is a simplification for the refactoring. |
52 | | - |
53 | | - return collapsible; |
| 29 | + if (node.children.empty()) { |
| 30 | + auto option = ButtonOption(); |
| 31 | + option.transform = [=, &node](const EntryState& state) { |
| 32 | + std::string indentation = std::string(depth * 3, ' '); |
| 33 | + auto element = hbox({ |
| 34 | + text(indentation + " " + node.label) |
| 35 | + }); |
| 36 | + if (state.focused) { |
| 37 | + element = element | inverted; |
| 38 | + } |
| 39 | + return element; |
| 40 | + }; |
| 41 | + node.component = Button(&node.label, on_click, option); |
| 42 | + } else { |
| 43 | + std::vector<Component> children_components; |
| 44 | + for (auto& child : node.children) { |
| 45 | + buildComponent(child, depth + 1); |
| 46 | + children_components.push_back(child.component); |
| 47 | + } |
| 48 | + auto children_container = Container::Vertical(children_components); |
| 49 | + |
| 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] { |
| 55 | + std::string indentation = std::string(depth * 3, ' '); |
| 56 | + std::string icon = node.is_expanded ? "▼ " : "► "; |
| 57 | + auto header_element = hbox({ |
| 58 | + text(indentation + icon + node.label) |
| 59 | + }); |
| 60 | + if (header_button->Focused()) { |
| 61 | + header_element = header_element | inverted; |
| 62 | + } |
| 63 | + |
| 64 | + if (node.is_expanded) { |
| 65 | + return vbox({header_element, children_container->Render()}); |
| 66 | + } |
| 67 | + return header_element; |
| 68 | + }); |
| 69 | + } |
54 | 70 | } |
55 | 71 |
|
56 | 72 | } // namespace TUIKIT |
0 commit comments