diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 2ff4d34..ee9e2c0 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -53,6 +53,7 @@ struct State { stacked_modals_item: usize, stacked_modals_color: [f32; 4], app_log: Vec, + filter: imgui::TextFilter, tabs: TabState, } @@ -117,6 +118,7 @@ impl Default for State { stacked_modals_item: 0, stacked_modals_color: [0.4, 0.7, 0.0, 0.5], app_log: Vec::new(), + filter: TextFilter::new(String::from("Test")), tabs: TabState::default(), } } @@ -747,6 +749,30 @@ CTRL+click on individual component to input value.\n", } } + if CollapsingHeader::new("Filtering").build(ui) { + ui.text_wrapped( + "Filter usage:\n\ + \"\" display all lines\n\ + \"xxx\" display lines containing \"xxx\"\n\ + \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n\ + \"-xxx\" hide lines containing \"xxx\"" + ); + + state.filter.draw(); + let lines = vec!["aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world!"]; + + ui.same_line(); + if ui.button("Clear##clear_filter") { + state.filter.clear(); + } + + for i in lines.iter() { + if state.filter.pass_filter(i) { + ui.bullet_text(i); + } + } + } + if CollapsingHeader::new("Popups & Modal windows").build(ui) { if let Some(_t) = ui.tree_node("Popups") { ui.text_wrapped( diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index cadb3a2..02b858d 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -33,6 +33,7 @@ pub use self::style::*; #[cfg(feature = "tables-api")] pub use self::tables::*; +pub use self::text_filter::*; pub use self::utils::*; pub use self::widget::color_editors::*; pub use self::widget::combo_box::*; @@ -81,6 +82,7 @@ mod style; mod tables; #[cfg(test)] mod test; +pub mod text_filter; mod utils; mod widget; mod window; diff --git a/imgui/src/text_filter.rs b/imgui/src/text_filter.rs new file mode 100644 index 0000000..eaf86b8 --- /dev/null +++ b/imgui/src/text_filter.rs @@ -0,0 +1,107 @@ +use crate::sys; +use crate::Ui; +use std::ptr; + +/// Helper to parse and apply text filters +pub struct TextFilter { + id: String, + raw: *mut sys::ImGuiTextFilter, +} + +impl TextFilter { + /// Creates a new TextFilter with an empty filter. + /// + /// This is equivalent of [new_with_filter](Self::new_with_filter) with `filter` set to `""`. + pub fn new(label: String) -> Self { + Self::new_with_filter(label, String::new()) + } + + /// Creates a new TextFilter with a custom filter. + pub fn new_with_filter(label: String, filter: String) -> Self { + let mut filter = filter.clone(); + filter.push('\0'); + let ptr = filter.as_mut_ptr(); + Self { + id: label, + raw: unsafe { sys::ImGuiTextFilter_ImGuiTextFilter(ptr as *mut sys::cty::c_char) }, + } + } + + /// Builds the TextFilter with its filter attribute. You can use + /// [`pass_filter()`](Self::pass_filter) after it. + /// + /// If you want control the filter with an InputText, check [`draw()`](Self::draw). + pub fn build(&self) { + unsafe { + sys::ImGuiTextFilter_Build(self.raw); + } + } + + /// Draws an [InputText](crate::input_widget::InputText) to control the filter of the TextFilter. + /// + /// This is equivalent of [draw_with_size](Self::draw_with_size) with `size` set to `0.0`. + pub fn draw(&self) { + self.draw_with_size(0.0); + } + + /// Draws an [InputText](crate::input_widget::InputText) to control the filter of the TextFilter. + /// + /// The InputText has the size passed in parameters. + pub fn draw_with_size(&self, size: f32) { + unsafe { + let mut id = self.id.clone(); + id.push('\0'); + let ptr = id.as_mut_ptr(); + sys::ImGuiTextFilter_Draw(self.raw, ptr as *mut sys::cty::c_char, size); + } + } + + /// Returns true if the filter is not empty (`""`). + pub fn is_active(&self) -> bool { + unsafe { sys::ImGuiTextFilter_IsActive(self.raw) } + } + + /// Returns true if the buffer matches the filter. + /// + /// [`draw()`](Self::draw) or [`build()`](Self::build) mut be called **before** this function. + pub fn pass_filter(&self, buf: &str) -> bool { + let mut buf = String::from(buf); + buf.push('\0'); + let ptr = buf.as_mut_ptr(); + unsafe { + sys::ImGuiTextFilter_PassFilter(self.raw, ptr as *mut sys::cty::c_char, ptr::null()) + } + } + + pub fn pass_filter_with_end(&self, start: &str, end: &str) -> bool { + let (mut start, mut end) = (String::from(start), String::from(end)); + start.push('\0'); + end.push('\0'); + let b_ptr = start.as_mut_ptr(); + let e_ptr = end.as_mut_ptr(); + unsafe { + sys::ImGuiTextFilter_PassFilter( + self.raw, + b_ptr as *mut sys::cty::c_char, + e_ptr as *mut sys::cty::c_char, + ) + } + } + + /// Clears the filter. + pub fn clear(&self) { + unsafe { + sys::ImGuiTextFilter_Clear(self.raw); + } + } +} + +impl Ui { + pub fn text_filter(label: String) -> TextFilter { + TextFilter::new(label) + } + + pub fn text_filter_with_filter(label: String, filter: String) -> TextFilter { + TextFilter::new_with_filter(label, filter) + } +}