diff --git a/dearpygui/_dearpygui.pyi b/dearpygui/_dearpygui.pyi index eb36f9124..5d3dfccf7 100644 --- a/dearpygui/_dearpygui.pyi +++ b/dearpygui/_dearpygui.pyi @@ -182,7 +182,7 @@ def add_file_extension(extension : str, *, label: str ='', user_data: Any ='', u """Creates a file extension filter option in the file dialog.""" ... -def add_filter_set(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', width: int ='', indent: int ='', parent: Union[int, str] ='', before: Union[int, str] ='', show: bool ='', delay_search: bool ='') -> Union[int, str]: +def add_filter_set(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', width: int ='', indent: int ='', parent: Union[int, str] ='', before: Union[int, str] ='', show: bool ='', delay_search: bool ='', recursive: bool ='') -> Union[int, str]: """Helper to parse and apply text filters (e.g. aaaaa[, bbbbb][, ccccc])""" ... @@ -575,7 +575,7 @@ def add_text(default_value : str ='', *, label: str ='', user_data: Any ='', use ... def add_text_point(x : Union[List[float], Tuple[float, ...]], y : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='', before: Union[int, str] ='', source: Union[int, str] ='', show: bool ='', offset: Union[List[float], Tuple[float, ...]] ='', vertical: bool ='') -> Union[int, str]: - """Adds a label series to a plot.""" + """Adds a label series to a plot. x and y can only have one elements each.""" ... def add_texture_registry(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', show: bool ='') -> Union[int, str]: diff --git a/dearpygui/_dearpygui_RTD.py b/dearpygui/_dearpygui_RTD.py index 454182563..0325ac6b8 100644 --- a/dearpygui/_dearpygui_RTD.py +++ b/dearpygui/_dearpygui_RTD.py @@ -1798,6 +1798,7 @@ def filter_set(**kwargs): before (Union[int, str], optional): This item will be displayed before the specified item in the parent. show (bool, optional): Attempt to render widget. delay_search (bool, optional): Delays searching container for specified items until the end of the app. Possible optimization when a container has many children that are not accessed often. + recursive (bool, optional): Recursive behaviour for the filter. If a child matches the filter, also the parents will do it. (This is still an experimental feature and it can have a slight impact on the performance) id (Union[int, str], optional): (deprecated) Yields: Union[int, str] @@ -3954,6 +3955,7 @@ def add_filter_set(**kwargs): before (Union[int, str], optional): This item will be displayed before the specified item in the parent. show (bool, optional): Attempt to render widget. delay_search (bool, optional): Delays searching container for specified items until the end of the app. Possible optimization when a container has many children that are not accessed often. + recursive (bool, optional): Recursive behaviour for the filter. If a child matches the filter, also the parents will do it. (This is still an experimental feature and it can have a slight impact on the performance) id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -6478,7 +6480,7 @@ def add_text(default_value='', **kwargs): return internal_dpg.add_text(default_value, **kwargs) def add_text_point(x, y, **kwargs): - """ Adds a label series to a plot. + """ Adds a label series to a plot. x and y can only have one elements each. Args: x (Any): diff --git a/dearpygui/dearpygui.py b/dearpygui/dearpygui.py index b6f6e6c43..1faa5fff9 100644 --- a/dearpygui/dearpygui.py +++ b/dearpygui/dearpygui.py @@ -1824,7 +1824,7 @@ def file_dialog(*, label: str =None, user_data: Any =None, use_internal_label: b internal_dpg.pop_container_stack() @contextmanager -def filter_set(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, delay_search: bool =False, **kwargs) -> Union[int, str]: +def filter_set(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, delay_search: bool =False, recursive: bool =False, **kwargs) -> Union[int, str]: """ Helper to parse and apply text filters (e.g. aaaaa[, bbbbb][, ccccc]) Args: @@ -1838,6 +1838,7 @@ def filter_set(*, label: str =None, user_data: Any =None, use_internal_label: bo before (Union[int, str], optional): This item will be displayed before the specified item in the parent. show (bool, optional): Attempt to render widget. delay_search (bool, optional): Delays searching container for specified items until the end of the app. Possible optimization when a container has many children that are not accessed often. + recursive (bool, optional): Recursive behaviour for the filter. If a child matches the filter, also the parents will do it. (This is still an experimental feature and it can have a slight impact on the performance) id (Union[int, str], optional): (deprecated) Yields: Union[int, str] @@ -1847,7 +1848,7 @@ def filter_set(*, label: str =None, user_data: Any =None, use_internal_label: bo if 'id' in kwargs.keys(): warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - widget = internal_dpg.add_filter_set(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, indent=indent, parent=parent, before=before, show=show, delay_search=delay_search, **kwargs) + widget = internal_dpg.add_filter_set(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, indent=indent, parent=parent, before=before, show=show, delay_search=delay_search, recursive=recursive, **kwargs) internal_dpg.push_container_stack(widget) yield widget finally: @@ -4292,7 +4293,7 @@ def add_file_extension(extension : str, *, label: str =None, user_data: Any =Non return internal_dpg.add_file_extension(extension, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, height=height, parent=parent, before=before, custom_text=custom_text, color=color, **kwargs) -def add_filter_set(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, delay_search: bool =False, **kwargs) -> Union[int, str]: +def add_filter_set(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, delay_search: bool =False, recursive: bool =False, **kwargs) -> Union[int, str]: """ Helper to parse and apply text filters (e.g. aaaaa[, bbbbb][, ccccc]) Args: @@ -4306,6 +4307,7 @@ def add_filter_set(*, label: str =None, user_data: Any =None, use_internal_label before (Union[int, str], optional): This item will be displayed before the specified item in the parent. show (bool, optional): Attempt to render widget. delay_search (bool, optional): Delays searching container for specified items until the end of the app. Possible optimization when a container has many children that are not accessed often. + recursive (bool, optional): Recursive behaviour for the filter. If a child matches the filter, also the parents will do it. (This is still an experimental feature and it can have a slight impact on the performance) id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -4315,7 +4317,7 @@ def add_filter_set(*, label: str =None, user_data: Any =None, use_internal_label warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - return internal_dpg.add_filter_set(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, indent=indent, parent=parent, before=before, show=show, delay_search=delay_search, **kwargs) + return internal_dpg.add_filter_set(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, indent=indent, parent=parent, before=before, show=show, delay_search=delay_search, recursive=recursive, **kwargs) def add_float4_value(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, source: Union[int, str] =0, default_value: Union[List[float], Tuple[float, ...]] =(0.0, 0.0, 0.0, 0.0), parent: Union[int, str] =internal_dpg.mvReservedUUID_3, **kwargs) -> Union[int, str]: """ Adds a float4 value. @@ -7234,7 +7236,7 @@ def add_text(default_value : str ='', *, label: str =None, user_data: Any =None, return internal_dpg.add_text(default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, indent=indent, parent=parent, before=before, source=source, payload_type=payload_type, drag_callback=drag_callback, drop_callback=drop_callback, show=show, pos=pos, filter_key=filter_key, tracked=tracked, track_offset=track_offset, wrap=wrap, bullet=bullet, color=color, show_label=show_label, **kwargs) def add_text_point(x : Union[List[float], Tuple[float, ...]], y : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, before: Union[int, str] =0, source: Union[int, str] =0, show: bool =True, offset: Union[List[float], Tuple[float, ...]] =(0.0, 0.0), vertical: bool =False, **kwargs) -> Union[int, str]: - """ Adds a label series to a plot. + """ Adds a label series to a plot. x and y can only have one elements each. Args: x (Any): diff --git a/src/dearpygui_commands.h b/src/dearpygui_commands.h index 0cf1fa748..69a3cb3ca 100644 --- a/src/dearpygui_commands.h +++ b/src/dearpygui_commands.h @@ -2660,7 +2660,6 @@ destroy_context(PyObject* self, PyObject* args, PyObject* kwargs) GContext->started = false; // return to false after }); - // Doesn't pass tests if (GContext->viewport != nullptr) mvCleanupViewport(*GContext->viewport); diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index a87ccd55d..454a676b5 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -2265,6 +2265,8 @@ DearPyGui::GetEntityParser(mvAppItemType type) MV_PARSER_ARG_SHOW) ); + args.push_back({ mvPyDataType::Bool, "recursive", mvArgType::KEYWORD_ARG, "False", "Recursive behaviour for the filter. If a child matches the filter, also the parents will do it. (This is still an experimental feature and it can have a slight impact on the performance)" }); + setup.about = "Helper to parse and apply text filters (e.g. aaaaa[, bbbbb][, ccccc])"; setup.category = { "Containers", "Widgets" }; setup.createContextManager = true; diff --git a/src/mvBasicWidgets.cpp b/src/mvBasicWidgets.cpp index 6ce83cf32..5144fc641 100644 --- a/src/mvBasicWidgets.cpp +++ b/src/mvBasicWidgets.cpp @@ -691,6 +691,14 @@ DearPyGui::fill_configuration_dict(const mvTooltipConfig& inConfig, PyObject* ou PyDict_SetItemString(outDict, "hide_on_activity", mvPyObject(ToPyBool(inConfig.hide_on_move))); } +void +DearPyGui::fill_configuration_dict(const mvFilterSetConfig& inConfig, PyObject* outDict) +{ + if (outDict == nullptr) + return; + + PyDict_SetItemString(outDict, "recursive", mvPyObject(ToPyBool(inConfig.recursive))); +} //----------------------------------------------------------------------------- // [SECTION] configure_item(...) specifics //----------------------------------------------------------------------------- @@ -1792,6 +1800,15 @@ DearPyGui::set_configuration(PyObject* inDict, mvTooltipConfig& outConfig) if (PyObject* item = PyDict_GetItemString(inDict, "hide_on_activity")) outConfig.hide_on_move = ToBool(item); } +void +DearPyGui::set_configuration(PyObject* inDict, mvFilterSetConfig& outConfig) +{ + if (inDict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(inDict, "recursive")) outConfig.recursive = ToBool(item); +} + void DearPyGui::set_configuration(PyObject* inDict, mvKnobFloatConfig& outConfig) { @@ -6299,6 +6316,41 @@ DearPyGui::draw_image_button(ImDrawList* drawlist, mvAppItem& item, mvImageButto apply_drag_drop(&item); } +void set_visibility(mvAppItem& item, bool visible) { + item.config.show = visible; + item.info.hiddenLastFrame = !visible; + item.info.shownLastFrame = visible; +} + +bool +can_be_visible(ImDrawList* drawlist, mvAppItem& item, mvFilterSetConfig& config, bool forceTrue) +{ + // Let's dive into the children to check them first + bool show_from_children = false; + for (auto& childset : item.childslots) + { + for (auto& child : childset) + { + if (can_be_visible(drawlist, *child, config, forceTrue)) { + // config._filtered[item.uuid] = true; + show_from_children = true; + } + } + } + + if (forceTrue || show_from_children) { + set_visibility(item, true); + return true; + } + + // If it has no children then we can check the item itself with the filter + bool show = config.imguiFilter.PassFilter(item.config.filter.c_str()); + + // We update the visibility of the item + set_visibility(item, show); + return show; +} + void DearPyGui::draw_filter_set(ImDrawList* drawlist, mvAppItem& item, mvFilterSetConfig& config) { @@ -6307,23 +6359,24 @@ DearPyGui::draw_filter_set(ImDrawList* drawlist, mvAppItem& item, mvFilterSetCon if (item.config.width != 0) ImGui::PushItemWidth((float)item.config.width); - if (config.imguiFilter.IsActive()) - { + if (config.imguiFilter.IsActive()) { + if (config.recursive) { + config._was_active = true; + can_be_visible(drawlist, item, config, false); + } for (auto& childset : item.childslots) { for (auto& child : childset) - { - if (!config.imguiFilter.PassFilter(child->config.filter.c_str())) - continue; - child->draw(drawlist, ImGui::GetCursorPosX(), ImGui::GetCursorPosY()); - } } - } else { - + if (config._was_active && config.recursive) { + /* Reset the situation */ + config._was_active = false; + can_be_visible(drawlist, item, config, true); + } for (auto& childset : item.childslots) { for (auto& child : childset) diff --git a/src/mvBasicWidgets.h b/src/mvBasicWidgets.h index 576d14239..f289ce1f6 100644 --- a/src/mvBasicWidgets.h +++ b/src/mvBasicWidgets.h @@ -75,6 +75,7 @@ namespace DearPyGui void fill_configuration_dict(const mvImageButtonConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvKnobFloatConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvTooltipConfig& inConfig, PyObject* outDict); + void fill_configuration_dict(const mvFilterSetConfig& inConfig, PyObject* outDict); // specific part of `configure_item(...)` void set_configuration(PyObject* inDict, mvSimplePlotConfig& outConfig); @@ -109,6 +110,7 @@ namespace DearPyGui void set_configuration(PyObject* inDict, mvImageConfig& outConfig); void set_configuration(PyObject* inDict, mvImageButtonConfig& outConfig); void set_configuration(PyObject* inDict, mvTooltipConfig& outConfig); + void set_configuration(PyObject* inDict, mvFilterSetConfig& outConfig); void set_configuration(PyObject* inDict, mvKnobFloatConfig& outConfig); // positional args TODO: combine with above @@ -573,6 +575,9 @@ struct mvImageButtonConfig struct mvFilterSetConfig { ImGuiTextFilter imguiFilter; + bool recursive = false; + // std::unordered_map _checked_in_frame; /* Elements already checked in this frame*/ + bool _was_active = false; /* Was the filter active in the last frame */ }; struct mvTooltipConfig @@ -1057,6 +1062,8 @@ class mvFilterSet : public mvAppItem mvFilterSetConfig configData{}; explicit mvFilterSet(mvUUID uuid) : mvAppItem(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override { DearPyGui::draw_filter_set(drawlist, *this, configData); } + void handleSpecificKeywordArgs(PyObject* dict) override { DearPyGui::set_configuration(dict, configData); } + void getSpecificConfiguration(PyObject* dict) override { DearPyGui::fill_configuration_dict(configData, dict); } void setPyValue(PyObject* value) override; PyObject* getPyValue() override { return ToPyString(std::string(configData.imguiFilter.InputBuf)); } };