diff --git a/dearpygui/_dearpygui.pyi b/dearpygui/_dearpygui.pyi index c5c1acb16..456a4b845 100644 --- a/dearpygui/_dearpygui.pyi +++ b/dearpygui/_dearpygui.pyi @@ -335,7 +335,7 @@ def add_item_scroll_handler(*, label: str ='', user_data: Any ='', use_internal_ """Adds a scroll handler.""" ... -def add_item_toggled_open_handler(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='', callback: Callable ='', show: bool ='') -> Union[int, str]: +def add_item_toggled_open_handler(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='', callback: Callable ='', show: bool ='', two_way: bool ='') -> Union[int, str]: """Adds a togged open handler.""" ... diff --git a/dearpygui/_dearpygui_RTD.py b/dearpygui/_dearpygui_RTD.py index cd869e98e..2cd741717 100644 --- a/dearpygui/_dearpygui_RTD.py +++ b/dearpygui/_dearpygui_RTD.py @@ -96,7 +96,6 @@ def start_dearpygui(): if not internal_dpg.is_viewport_ok(): raise RuntimeError("Viewport was not created and shown.") - return while(internal_dpg.is_dearpygui_running()): internal_dpg.render_dearpygui_frame() @@ -5083,6 +5082,7 @@ def add_item_toggled_open_handler(**kwargs): parent (Union[int, str], optional): Parent to add this item to. (runtime adding) callback (Callable, optional): Registers a callback. show (bool, optional): Attempt to render widget. + two_way (bool, optional): Trigger on both 'opened' and 'closed' events, i.e. when the 'opened' state is toggled between the two values. If False, some containers will trigger it only on the 'opened' event. id (Union[int, str], optional): (deprecated) Returns: Union[int, str] diff --git a/dearpygui/_header.py b/dearpygui/_header.py index 1a1731e23..5bcbfff51 100644 --- a/dearpygui/_header.py +++ b/dearpygui/_header.py @@ -80,7 +80,6 @@ def start_dearpygui(): if not internal_dpg.is_viewport_ok(): raise RuntimeError("Viewport was not created and shown.") - return while(internal_dpg.is_dearpygui_running()): internal_dpg.render_dearpygui_frame() diff --git a/dearpygui/dearpygui.py b/dearpygui/dearpygui.py index e68cd6cdd..1e23f10c3 100644 --- a/dearpygui/dearpygui.py +++ b/dearpygui/dearpygui.py @@ -96,7 +96,6 @@ def start_dearpygui(): if not internal_dpg.is_viewport_ok(): raise RuntimeError("Viewport was not created and shown.") - return while(internal_dpg.is_dearpygui_running()): internal_dpg.render_dearpygui_frame() @@ -5694,7 +5693,7 @@ def add_item_scroll_handler(*, label: str =None, user_data: Any =None, use_inter return internal_dpg.add_item_scroll_handler(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, callback=callback, show=show, **kwargs) -def add_item_toggled_open_handler(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, callback: Callable =None, show: bool =True, **kwargs) -> Union[int, str]: +def add_item_toggled_open_handler(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, callback: Callable =None, show: bool =True, two_way: bool =False, **kwargs) -> Union[int, str]: """ Adds a togged open handler. Args: @@ -5705,6 +5704,7 @@ def add_item_toggled_open_handler(*, label: str =None, user_data: Any =None, use parent (Union[int, str], optional): Parent to add this item to. (runtime adding) callback (Callable, optional): Registers a callback. show (bool, optional): Attempt to render widget. + two_way (bool, optional): Trigger on both 'opened' and 'closed' events, i.e. when the 'opened' state is toggled between the two values. If False, some containers will trigger it only on the 'opened' event. id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -5714,7 +5714,7 @@ def add_item_toggled_open_handler(*, label: str =None, user_data: Any =None, use warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - return internal_dpg.add_item_toggled_open_handler(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, callback=callback, show=show, **kwargs) + return internal_dpg.add_item_toggled_open_handler(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, callback=callback, show=show, two_way=two_way, **kwargs) def add_item_visible_handler(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, callback: Callable =None, show: bool =True, **kwargs) -> Union[int, str]: """ Adds a visible handler. diff --git a/dearpygui/demo.py b/dearpygui/demo.py index 44686bcec..e938e654a 100644 --- a/dearpygui/demo.py +++ b/dearpygui/demo.py @@ -246,7 +246,7 @@ def _log(sender, app_data, user_data): with dpg.child_window(height=60, autosize_x=True): for i in range(10): - dpg.add_text(f"Scolling Text{i}") + dpg.add_text(f"Scrolling Text{i}") dpg.add_slider_float(label="Slider Float") dpg.add_input_int(label="Input Int") diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index 6f57e0a15..10fd1b73d 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -5066,6 +5066,8 @@ DearPyGui::GetEntityParser(mvAppItemType type) MV_PARSER_ARG_CALLBACK) ); + args.push_back({ mvPyDataType::Bool, "two_way", mvArgType::KEYWORD_ARG, "False", "Trigger on both 'opened' and 'closed' events, i.e. when the 'opened' state is toggled between the two values. If False, some containers will trigger it only on the 'opened' event." }); + setup.about = "Adds a togged open handler."; setup.category = { "Widgets", "Events" }; break; diff --git a/src/mvAppItem.h b/src/mvAppItem.h index 69edfe275..62f21c332 100644 --- a/src/mvAppItem.h +++ b/src/mvAppItem.h @@ -56,7 +56,7 @@ enum ItemDescriptionFlags enum class mvLibType { MV_IMGUI = 0, - OT = 1, + MV_IMPLOT = 1, MV_IMNODES = 2 }; diff --git a/src/mvAppItemState.cpp b/src/mvAppItemState.cpp index 768a4800a..ade4606d1 100644 --- a/src/mvAppItemState.cpp +++ b/src/mvAppItemState.cpp @@ -26,7 +26,7 @@ ResetAppItemState(mvAppItemState& state) state.activated = false; state.deactivated = false; state.deactivatedAfterEdit = false; - state.toggledOpen = false; + state.toggledOpenPure = state.toggledOpen = false; state.mvRectSizeResized = false; state.scrolledX = state.scrolledY = false; state.isScrollingX = state.isScrollingY = false; @@ -57,7 +57,7 @@ UpdateAppItemState(mvAppItemState& state) state.activated = ImGui::IsItemActivated(); state.deactivated = ImGui::IsItemDeactivated(); state.deactivatedAfterEdit = ImGui::IsItemDeactivatedAfterEdit(); - state.toggledOpen = ImGui::IsItemToggledOpen(); + state.toggledOpenPure = state.toggledOpen = ImGui::IsItemToggledOpen(); state.rectMin = { ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y }; state.rectMax = { ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y }; state.rectSize = { ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y }; diff --git a/src/mvAppItemState.h b/src/mvAppItemState.h index d22d90c41..3fd9cb24f 100644 --- a/src/mvAppItemState.h +++ b/src/mvAppItemState.h @@ -85,6 +85,10 @@ struct mvAppItemState b8 deactivated = false; b8 deactivatedAfterEdit = false; b8 toggledOpen = false; + // This one is not reset by mvCollapsingHeader and mvTreeNode; also, not all items + // initialize it (but mvCollapsingHeader and mvTreeNode do, and they in fact are + // the only ones that must do it). Used as an extra flag by the two-way toggled open handler. + b8 toggledOpenPure = false; b8 mvRectSizeResized = false; b8 scrolledX = false; b8 scrolledY = false; diff --git a/src/mvItemHandlers.cpp b/src/mvItemHandlers.cpp index 26fdd13b0..2b0d0408f 100644 --- a/src/mvItemHandlers.cpp +++ b/src/mvItemHandlers.cpp @@ -343,12 +343,28 @@ void mvResizeHandler::customAction(void* data) void mvToggledOpenHandler::customAction(void* data) { mvAppItemState* state = static_cast(data); - if (state->toggledOpen) + if (state->toggledOpen || twoWay && state->toggledOpenPure) { submitHandler(state->parent); } } +void mvToggledOpenHandler::handleSpecificKeywordArgs(PyObject* dict) +{ + if (dict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(dict, "two_way")) twoWay = ToBool(item); +} + +void mvToggledOpenHandler::getSpecificConfiguration(PyObject* dict) +{ + if (dict == nullptr) + return; + + PyDict_SetItemString(dict, "two_way", mvPyObject(ToPyBool(twoWay))); +} + void mvVisibleHandler::customAction(void* data) { mvAppItemState* state = static_cast(data); diff --git a/src/mvItemHandlers.h b/src/mvItemHandlers.h index 7a5c265c4..dd6ae0811 100644 --- a/src/mvItemHandlers.h +++ b/src/mvItemHandlers.h @@ -149,6 +149,11 @@ class mvToggledOpenHandler : public mvItemHandler explicit mvToggledOpenHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; + void handleSpecificKeywordArgs(PyObject* dict) override; + void getSpecificConfiguration(PyObject* dict) override; + +private: + bool twoWay = false; }; class mvVisibleHandler : public mvItemHandler diff --git a/src/mvPyUtils.h b/src/mvPyUtils.h index a09a69e5f..d213d25db 100644 --- a/src/mvPyUtils.h +++ b/src/mvPyUtils.h @@ -142,7 +142,7 @@ PyObject* GetPyNoneOrError (); PyObject* ToPyUUID (mvUUID uuid, const std::string& alias); // Returns item UUID or alias, or zero if `item` is null. An valid item can never -// a UUID of zero, so it is a good value to designate a "no item" case (also can +// have a UUID of zero, so it is a good value to designate a "no item" case (also can // easily be checked with `if (item)` in Python). PyObject* ToPyUUID (mvAppItem* item); diff --git a/src/mvThemes.cpp b/src/mvThemes.cpp index 5a94c2864..f75a1093d 100644 --- a/src/mvThemes.cpp +++ b/src/mvThemes.cpp @@ -135,7 +135,7 @@ void mvThemeColor::push_theme_color() ImGui::PushStyleColor(_targetColor, color); } - else if (_libType == mvLibType::OT) + else if (_libType == mvLibType::MV_IMPLOT) ImPlot::PushStyleColor(_targetColor, color); else if (_libType == mvLibType::MV_IMNODES) ImNodes::PushColorStyle(_targetColor, ImGui::ColorConvertFloat4ToU32(color)); @@ -145,7 +145,7 @@ void mvThemeColor::pop_theme_color() { if (_libType == mvLibType::MV_IMGUI) ImGui::PopStyleColor(); - else if (_libType == mvLibType::OT) + else if (_libType == mvLibType::MV_IMPLOT) ImPlot::PopStyleColor(); else if (_libType == mvLibType::MV_IMNODES) ImNodes::PopColorStyle(); @@ -194,7 +194,7 @@ void mvThemeColor::handleSpecificKeywordArgs(PyObject* dict) } } - else if (_libType == mvLibType::OT) + else if (_libType == mvLibType::MV_IMPLOT) { if (_targetColor >= ImPlotCol_COUNT || _targetColor < 0) { @@ -548,7 +548,7 @@ void mvThemeStyle::push_theme_style() else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) ImGui::PushStyleVar(_targetStyle, (*_value)[0]); } - else if (_libType == mvLibType::OT) + else if (_libType == mvLibType::MV_IMPLOT) { const mvGuiStyleVarInfo* var_info = GetPlotStyleVarInfo(_targetStyle); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) @@ -572,7 +572,7 @@ void mvThemeStyle::pop_theme_style() { if (_libType == mvLibType::MV_IMGUI) ImGui::PopStyleVar(); - else if (_libType == mvLibType::OT) + else if (_libType == mvLibType::MV_IMPLOT) ImPlot::PopStyleVar(); else if (_libType == mvLibType::MV_IMNODES) ImNodes::PopStyleVar(); @@ -624,7 +624,7 @@ void mvThemeStyle::handleSpecificKeywordArgs(PyObject* dict) } } - else if (_libType == mvLibType::OT) + else if (_libType == mvLibType::MV_IMPLOT) { if (_targetStyle >= ImPlotStyleVar_COUNT || _targetStyle < 0) { diff --git a/src/mvThemes.h b/src/mvThemes.h index aaf874420..5a1047105 100644 --- a/src/mvThemes.h +++ b/src/mvThemes.h @@ -109,7 +109,7 @@ class mvThemeStyle : public mvAppItem private: std::shared_ptr> _value = std::make_shared>(std::array{0.0f, -1.0f, 0.0f, 0.0f}); - ImGuiCol _targetStyle = 0; + ImGuiStyleVar _targetStyle = 0; mvLibType _libType = mvLibType::MV_IMGUI;