Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add TextFilter #658

Merged
merged 7 commits into from Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions imgui-examples/examples/test_window_impl.rs
Expand Up @@ -53,6 +53,7 @@ struct State {
stacked_modals_item: usize,
stacked_modals_color: [f32; 4],
app_log: Vec<String>,
filter: imgui::TextFilter,

tabs: TabState,
}
Expand Down Expand Up @@ -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")),
dbr marked this conversation as resolved.
Show resolved Hide resolved
tabs: TabState::default(),
}
}
Expand Down Expand Up @@ -732,6 +734,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(String::from(*i)) {
ui.bullet_text(i);
}
}
}

if CollapsingHeader::new("Popups & Modal windows").build(ui) {
if let Some(_t) = ui.tree_node("Popups") {
ui.text_wrapped(
Expand Down
1 change: 1 addition & 0 deletions imgui/src/lib.rs
Expand Up @@ -45,6 +45,7 @@ pub use self::widget::progress_bar::*;
pub use self::widget::selectable::*;
pub use self::widget::slider::*;
pub use self::widget::tab::*;
pub use self::widget::text_filter::*;
pub use self::widget::tree::*;
pub use self::window::child_window::*;
pub use self::window::*;
Expand Down
1 change: 1 addition & 0 deletions imgui/src/widget/mod.rs
Expand Up @@ -10,4 +10,5 @@ pub mod selectable;
pub mod slider;
pub mod tab;
pub mod text;
pub mod text_filter;
pub mod tree;
104 changes: 104 additions & 0 deletions imgui/src/widget/text_filter.rs
@@ -0,0 +1,104 @@
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, mut filter: String) -> Self {
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if pass_filter is called without build? I guess imgui aborts?

If so, I wonder if it'd be worth adding some extra object so you do

fn draw(&'a mut self) -> &'a TextFilterHandle {
    ...
}
fn build(&'a mut self) -> &'a TextFilterHandle {

And since the pass_filter methods etc would be on that TextFilterHandle struct, it would be impossible to misuse (since you can't get at the pass_filter method without calling one of those methods)

Not a vital thing - there are plenty of ways to make imgui abort via the bindings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried to run pass_filter without build or draw in test_window_impl. It doesn't crash, just does nothing, like InputText without build. I just specify in the documentation that we have to call them before using the filter.

pub fn pass_filter(&self, mut buf: String) -> bool {
dbr marked this conversation as resolved.
Show resolved Hide resolved
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, mut start: String, mut end: String) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this method used for? It's not immediatly clear (..even looking at imgui.cpp!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't exactly know too.. I have to try it but I don't have time lately.

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)
}
}