egui_graph_edit/
node_finder.rs1use std::{collections::BTreeMap, marker::PhantomData};
2
3use crate::{color_hex_utils::*, CategoryTrait, NodeTemplateIter, NodeTemplateTrait};
4
5use egui::*;
6
7#[derive(Clone)]
8#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
9pub struct NodeFinder<NodeTemplate> {
10 pub query: String,
11 pub position: Option<Pos2>,
13 pub just_spawned: bool,
14 _phantom: PhantomData<NodeTemplate>,
15}
16
17impl<NodeTemplate, NodeData, UserState, CategoryType> NodeFinder<NodeTemplate>
18where
19 NodeTemplate:
20 NodeTemplateTrait<NodeData = NodeData, UserState = UserState, CategoryType = CategoryType>,
21 CategoryType: CategoryTrait,
22{
23 pub fn new_at(pos: Pos2) -> Self {
24 NodeFinder {
25 query: "".into(),
26 position: Some(pos),
27 just_spawned: true,
28 _phantom: Default::default(),
29 }
30 }
31
32 pub fn show(
36 &mut self,
37 ui: &mut Ui,
38 all_kinds: impl NodeTemplateIter<Item = NodeTemplate>,
39 user_state: &mut UserState,
40 ) -> Option<NodeTemplate> {
41 let background_color;
42 let text_color;
43
44 if ui.visuals().dark_mode {
45 background_color = color_from_hex("#3f3f3f").unwrap();
46 text_color = color_from_hex("#fefefe").unwrap();
47 } else {
48 background_color = color_from_hex("#fefefe").unwrap();
49 text_color = color_from_hex("#3f3f3f").unwrap();
50 }
51
52 ui.visuals_mut().widgets.noninteractive.fg_stroke = Stroke::new(2.0f32, text_color);
53
54 let frame = Frame::dark_canvas(ui.style())
55 .fill(background_color)
56 .inner_margin(vec2(5.0, 5.0));
57
58 let mut submitted_archetype = None;
60 frame.show(ui, |ui| {
61 ui.vertical(|ui| {
62 let resp = ui.text_edit_singleline(&mut self.query);
63 if self.just_spawned {
64 resp.request_focus();
65 self.just_spawned = false;
66 }
67 let update_open = resp.changed();
68
69 let mut query_submit = resp.lost_focus() && ui.input(|i| i.key_pressed(Key::Enter));
70
71 let max_height = ui.input(|i| i.content_rect().height() * 0.5);
72 let scroll_area_width = resp.rect.width() - 30.0;
73
74 let all_kinds = all_kinds.all_kinds();
75 let mut categories: BTreeMap<String, Vec<&NodeTemplate>> = Default::default();
76 let mut orphan_kinds = Vec::new();
77
78 for kind in &all_kinds {
79 let kind_categories = kind.node_finder_categories(user_state);
80
81 if kind_categories.is_empty() {
82 orphan_kinds.push(kind);
83 } else {
84 for category in kind_categories {
85 categories.entry(category.name()).or_default().push(kind);
86 }
87 }
88 }
89
90 Frame::default()
91 .inner_margin(vec2(10.0, 10.0))
92 .show(ui, |ui| {
93 ScrollArea::vertical()
94 .min_scrolled_height(max_height)
95 .max_height(max_height)
96 .show(ui, |ui| {
97 ui.set_width(scroll_area_width);
98 for (category, kinds) in categories {
99 let filtered_kinds: Vec<_> = kinds
100 .into_iter()
101 .map(|kind| {
102 let kind_name =
103 kind.node_finder_label(user_state).to_string();
104 (kind, kind_name)
105 })
106 .filter(|(_kind, kind_name)| {
107 kind_name
108 .to_lowercase()
109 .contains(self.query.to_lowercase().as_str())
110 })
111 .collect();
112
113 if !filtered_kinds.is_empty() {
114 let default_open = !self.query.is_empty();
115
116 CollapsingHeader::new(&category)
117 .default_open(default_open)
118 .open(update_open.then_some(default_open))
119 .show(ui, |ui| {
120 for (kind, kind_name) in filtered_kinds {
121 if ui
122 .selectable_label(false, kind_name)
123 .clicked()
124 {
125 submitted_archetype = Some(kind.clone());
126 } else if query_submit {
127 submitted_archetype = Some(kind.clone());
128 query_submit = false;
129 }
130 }
131 });
132 }
133 }
134
135 for kind in orphan_kinds {
136 let kind_name = kind.node_finder_label(user_state).to_string();
137
138 if ui.selectable_label(false, kind_name).clicked() {
139 submitted_archetype = Some(kind.clone());
140 } else if query_submit {
141 submitted_archetype = Some(kind.clone());
142 query_submit = false;
143 }
144 }
145 });
146 });
147 });
148 });
149
150 submitted_archetype
151 }
152}