diff --git a/src/dearpygui_commands.h b/src/dearpygui_commands.h index 74deb8704..4b68a149e 100644 --- a/src/dearpygui_commands.h +++ b/src/dearpygui_commands.h @@ -2006,19 +2006,13 @@ set_frame_callback(PyObject* self, PyObject* args, PyObject* kwargs) &frame, &callback, &user_data)) return GetPyNone(); + std::lock_guard lk(GContext->mutex); + if (frame > GContext->callbackRegistry->highestFrame) GContext->callbackRegistry->highestFrame = frame; - // TODO: check previous entry and deprecate if existing - Py_XINCREF(callback); - - if(user_data) - Py_XINCREF(user_data); - mvSubmitCallback([=]() - { - GContext->callbackRegistry->frameCallbacks[frame] = callback; - GContext->callbackRegistry->frameCallbacksUserData[frame] = user_data; - }); + GContext->callbackRegistry->frameCallbacks.insert_or_assign(frame, mvPyObject(callback, true)); + GContext->callbackRegistry->frameCallbacksUserData.insert_or_assign(frame, mvPyObject(user_data, true)); return GetPyNone(); } @@ -2033,14 +2027,9 @@ set_exit_callback(PyObject* self, PyObject* args, PyObject* kwargs) &user_data)) return GetPyNone(); - Py_XINCREF(callback); - if(user_data) - Py_XINCREF(user_data); - mvSubmitCallback([=]() - { - GContext->callbackRegistry->onCloseCallback = SanitizeCallback(callback); - GContext->callbackRegistry->onCloseCallbackUserData = user_data; - }); + *GContext->callbackRegistry->onCloseCallback = mvPyObject(callback == Py_None? nullptr : callback, true); + *GContext->callbackRegistry->onCloseCallbackUserData = mvPyObject(user_data, true); + return GetPyNone(); } @@ -2054,17 +2043,8 @@ set_viewport_resize_callback(PyObject* self, PyObject* args, PyObject* kwargs) &callback, &user_data)) return GetPyNone(); - if (callback) - Py_XINCREF(callback); - - if (user_data) - Py_XINCREF(user_data); - - mvSubmitCallback([=]() - { - GContext->callbackRegistry->resizeCallback = SanitizeCallback(callback); - GContext->callbackRegistry->resizeCallbackUserData = user_data; - }); + *GContext->callbackRegistry->resizeCallback = mvPyObject(callback == Py_None? nullptr : callback, true); + *GContext->callbackRegistry->resizeCallbackUserData = mvPyObject(user_data, true); return GetPyNone(); } @@ -2527,14 +2507,26 @@ output_frame_buffer(PyObject* self, PyObject* args, PyObject* kwargs) if (filepathLength == 0 && callback) // not specified, return array instead { - //Py_XINCREF(callback); PyObject* newbuffer = nullptr; PymvBuffer* newbufferview = PyObject_New(PymvBuffer, &PymvBufferType); newbuffer = PyObject_Init((PyObject*)newbufferview, &PymvBufferType); - mvSubmitTask([newbuffer, callback, newbufferview]() { + + // Making an owned ref while we're still holding GIL (can't do this within mvSubmitTask). + auto stored_callback = std::make_shared(callback, true); + // We need to schedule this into the rendering thread because OutputFrameBufferArray + // accesses the rendering API, which might well have thread-local things in the context. + mvSubmitTask([stored_callback, newbuffer, newbufferview]() { OutputFrameBufferArray(newbufferview); - mvAddCallback(callback, 0, newbuffer, nullptr, false); - }); + mvAddOwnerlessCallback( + stored_callback, std::make_shared(nullptr), + 0, "", + // Note: the callback queue will DECREF the value returned by this + // lambda, effectively deleting `newbuffer` so that we don't need + // to perform any special cleanup. We just pass the value as it is, + // keeping a refcount of 1 all the time until the callback is done. + [=]() { return newbuffer; } + ); + }); return GetPyNone(); } @@ -2571,7 +2563,7 @@ output_frame_buffer(PyObject* self, PyObject* args, PyObject* kwargs) static PyObject* is_dearpygui_running(PyObject* self, PyObject* args, PyObject* kwargs) { - return ToPyBool(GContext->started); + return ToPyBool(GContext->running); } static PyObject* @@ -2591,6 +2583,7 @@ setup_dearpygui(PyObject* self, PyObject* args, PyObject* kwargs) while (!GContext->itemRegistry->containers.empty()) GContext->itemRegistry->containers.pop(); GContext->started = true; + GContext->running = true; GContext->future = std::async(std::launch::async, []() {return mvRunCallbacks(); }); Py_END_ALLOW_THREADS; return GetPyNone(); @@ -2646,10 +2639,6 @@ create_context(PyObject* self, PyObject* args, PyObject* kwargs) static PyObject* destroy_context(PyObject* self, PyObject* args, PyObject* kwargs) { - // std::lock_guard lk(GContext->mutex); - - Py_BEGIN_ALLOW_THREADS; - if (GContext == nullptr) { assert(false); @@ -2657,47 +2646,57 @@ destroy_context(PyObject* self, PyObject* args, PyObject* kwargs) else { - // hacky fix, started was set to false - // to exit the event loop, but needs to be - // true in order to run DPG commands for the - // exit callback. - GContext->started = true; - mvSubmitCallback([=]() { - mvRunCallback(GContext->callbackRegistry->onCloseCallback, 0, nullptr, GContext->callbackRegistry->onCloseCallbackUserData); - GContext->started = false; // return to false after - }); - - if (GContext->viewport != nullptr) - mvCleanupViewport(*GContext->viewport); - - ImNodes::DestroyContext(); - ImPlot::DestroyContext(); - ImGui::DestroyContext(); + // Make sure everyone knows we're shutting down, even if stop_dearpygui + // was not called. + StopRendering(); - mvToolManager::Reset(); - ClearItemRegistry(*GContext->itemRegistry); + Py_BEGIN_ALLOW_THREADS; + // Queue the close callback, if any. The environment is still healthy enough + // for it to run, except that no more frames will be rendered with the current GContext. + mvAddOwnerlessCallback(GContext->callbackRegistry->onCloseCallback, GContext->callbackRegistry->onCloseCallbackUserData); - - //#define X(el) el::s_class_theme_component = nullptr; el::s_class_theme_disabled_component = nullptr; - #define X(el) DearPyGui::GetClassThemeComponent(mvAppItemType::el) = nullptr; DearPyGui::GetDisabledClassThemeComponent(mvAppItemType::el) = nullptr; - MV_ITEM_TYPES - #undef X - - mvSubmitCallback([=]() { + // Shutting down the callback loop - this will run right after the close callback + mvSubmitCallback([]() { GContext->callbackRegistry->running = false; - }); + }, true); + // Waiting for it to complete all tasks and shut down if (GContext->future.valid()) GContext->future.get(); - if (GContext->viewport) - delete GContext->viewport; - delete GContext->itemRegistry; - delete GContext->callbackRegistry; - delete GContext; - GContext = nullptr; + // The rest of cleanup must be done with GIL locked because it might lead + // to occasional DECREFs on Python objects (e.g. a callback or user_data). + Py_END_ALLOW_THREADS; + + mvContext* context_to_delete = nullptr; + { + // Even though the handlers thread is down, there's still a chance that + // the user calls DPG from another Python thread. We'd better lock the + // mutex while we're tinkering with all the global structures. + std::lock_guard lk(GContext->mutex); + + mvToolManager::Reset(); + ClearItemRegistry(*GContext->itemRegistry); + + ImNodes::DestroyContext(); + ImPlot::DestroyContext(); + ImGui::DestroyContext(); + + #define X(el) DearPyGui::GetClassThemeComponent(mvAppItemType::el) = nullptr; DearPyGui::GetDisabledClassThemeComponent(mvAppItemType::el) = nullptr; + MV_ITEM_TYPES + X(All) + #undef X + + if (GContext->viewport) + delete GContext->viewport; + + delete GContext->itemRegistry; + delete GContext->callbackRegistry; + context_to_delete = GContext; + GContext = nullptr; + } + delete context_to_delete; } - Py_END_ALLOW_THREADS; return GetPyNone(); } @@ -2705,8 +2704,8 @@ destroy_context(PyObject* self, PyObject* args, PyObject* kwargs) static PyObject* stop_dearpygui(PyObject* self, PyObject* args, PyObject* kwargs) { - std::lock_guard lk(GContext->mutex); - GContext->started = false; + StopRendering(); + std::lock_guard lk(GContext->mutex); auto viewport = GContext->viewport; if (viewport) viewport->running = false; @@ -3771,37 +3770,17 @@ get_item_configuration(PyObject* self, PyObject* args, PyObject* kwargs) PyDict_SetItemString(pdict, "height", py_height); PyDict_SetItemString(pdict, "indent", py_indent); - if (appitem->config.callback) - { - Py_XINCREF(appitem->config.callback); - PyDict_SetItemString(pdict, "callback", appitem->config.callback); - } - else - PyDict_SetItemString(pdict, "callback", GetPyNone()); + PyObject* callback = appitem->config.callback; + PyDict_SetItemString(pdict, "callback", callback? callback : Py_None); - if (appitem->config.dropCallback) - { - Py_XINCREF(appitem->config.dropCallback); - PyDict_SetItemString(pdict, "drop_callback", appitem->config.dropCallback); - } - else - PyDict_SetItemString(pdict, "drop_callback", GetPyNone()); + PyObject* dropCallback = appitem->config.dropCallback; + PyDict_SetItemString(pdict, "drop_callback", dropCallback? dropCallback : Py_None); - if (appitem->config.dragCallback) - { - Py_XINCREF(appitem->config.dragCallback); - PyDict_SetItemString(pdict, "drag_callback", appitem->config.dragCallback); - } - else - PyDict_SetItemString(pdict, "drag_callback", GetPyNone()); + PyObject* dragCallback = appitem->config.dragCallback; + PyDict_SetItemString(pdict, "drag_callback", dragCallback? dragCallback : Py_None); - if (appitem->config.user_data) - { - Py_XINCREF(appitem->config.user_data); - PyDict_SetItemString(pdict, "user_data", appitem->config.user_data); - } - else - PyDict_SetItemString(pdict, "user_data", GetPyNone()); + PyObject* user_data = *(appitem->config.user_data); + PyDict_SetItemString(pdict, "user_data", user_data? user_data : Py_None); appitem->getSpecificConfiguration(pdict); } @@ -4229,21 +4208,8 @@ capture_next_item(PyObject* self, PyObject* args, PyObject* kwargs) std::lock_guard lk(GContext->mutex); - if (GContext->itemRegistry->captureCallback) - Py_XDECREF(GContext->itemRegistry->captureCallback); - - if (GContext->itemRegistry->captureCallbackUserData) - Py_XDECREF(GContext->itemRegistry->captureCallbackUserData); - - Py_XINCREF(callable); - if(user_data) - Py_XINCREF(user_data); - if (callable == Py_None) - GContext->itemRegistry->captureCallback = nullptr; - else - GContext->itemRegistry->captureCallback = callable; - - GContext->itemRegistry->captureCallbackUserData = user_data; + GContext->itemRegistry->captureCallback = mvPyObject(callable == Py_None? nullptr : callable, true); + GContext->itemRegistry->captureCallbackUserData = mvPyObject(user_data, true); return GetPyNone(); } @@ -4254,29 +4220,44 @@ get_callback_queue(PyObject* self, PyObject* args, PyObject* kwargs) if (GContext->callbackRegistry->jobs.empty()) return GetPyNone(); + std::lock_guard lk(GContext->mutex); + PyObject* pArgs = PyTuple_New(GContext->callbackRegistry->jobs.size()); for (int i = 0; i < GContext->callbackRegistry->jobs.size(); i++) { PyObject* job = PyTuple_New(4); - if (GContext->callbackRegistry->jobs[i].callback) - PyTuple_SetItem(job, 0, GContext->callbackRegistry->jobs[i].callback); - else - PyTuple_SetItem(job, 0, GetPyNone()); + const mvCallbackJob& cur_entry = GContext->callbackRegistry->jobs[i]; - if(GContext->callbackRegistry->jobs[i].sender == 0) - PyTuple_SetItem(job, 1, ToPyString(GContext->callbackRegistry->jobs[i].sender_str)); + PyObject* callback; + if (cur_entry.ownerless_callback) + { + callback = *cur_entry.ownerless_callback; + Py_XINCREF(callback); + } else - PyTuple_SetItem(job, 1, ToPyUUID(GContext->callbackRegistry->jobs[i].sender)); + { + auto liveOwner = cur_entry.owner.lock(); + // If the owner of this entry is gone, we'll just set the callback to None. + // This lets us create the output list right away, without the need to collect + // valid callbacks first. Also, this mimicks the behavior of widgets without + // a `callback` set on them, which in the "manual" mode put null callbacks into + // the queue. It's mostly a debug/diagnostic mode anyway - captures everything. + callback = liveOwner? cur_entry.callback : nullptr; + // Must only be done while we own liveOwner. + Py_XINCREF(callback); + } + PyTuple_SetItem(job, 0, callback? callback : GetPyNone()); - if (GContext->callbackRegistry->jobs[i].app_data) - PyTuple_SetItem(job, 2, GContext->callbackRegistry->jobs[i].app_data); // steals data, so don't deref - else - PyTuple_SetItem(job, 2, GetPyNone()); + PyTuple_SetItem(job, 1, ToPyUUID(cur_entry.sender, cur_entry.alias)); - if (GContext->callbackRegistry->jobs[i].user_data) - PyTuple_SetItem(job, 3, GContext->callbackRegistry->jobs[i].user_data); // steals data, so don't deref - else - PyTuple_SetItem(job, 3, GetPyNone()); + // app_data_func() returns a new PyObject reference (passing ownership to us), + // therefore we don't need to INCREF it. + PyObject* app_data = cur_entry.app_data_func(); + PyTuple_SetItem(job, 2, app_data? app_data : GetPyNone()); + + PyObject* user_data = *cur_entry.user_data; + Py_XINCREF(user_data); + PyTuple_SetItem(job, 3, user_data? user_data : GetPyNone()); PyTuple_SetItem(pArgs, i, job); } diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index b5f393057..b5fdffbf6 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -36,12 +36,6 @@ mvAppItem::~mvAppItem() if (type == mvAppItemType::mvTable) static_cast(this)->onChildrenRemoved(); - mvGlobalIntepreterLock gil; - if (config.callback) Py_DECREF(config.callback); - if (config.user_data) Py_DECREF(config.user_data); - if (config.dragCallback) Py_DECREF(config.dragCallback); - if (config.dropCallback) Py_DECREF(config.dropCallback); - // in case item registry is destroyed if (GContext->itemRegistry) { @@ -54,13 +48,87 @@ mvAppItem::~mvAppItem() } } -PyObject* -mvAppItem::getCallback(bool ignore_enabled) +void mvAppItem::submitCallback() +{ + submitCallbackEx([]() -> PyObject* { return nullptr; }); +} + +template<> +void mvAppItem::submitCallback(std::string app_data) +{ + submitCallbackEx([app_data=std::move(app_data)]() { return ToPyString(app_data); }); +} + +template<> +void mvAppItem::submitCallback(bool app_data) +{ + submitCallbackEx([=]() { return ToPyBool(app_data); }); +} + +template<> +void mvAppItem::submitCallback(float app_data) +{ + submitCallbackEx([=]() { return ToPyFloat(app_data); }); +} + +template<> +void mvAppItem::submitCallback(double app_data) +{ + submitCallbackEx([=]() { return ToPyDouble(app_data); }); +} + +template<> +void mvAppItem::submitCallback(int app_data) +{ + submitCallbackEx([=]() { return ToPyInt(app_data); }); +} + +template<> +void mvAppItem::submitCallback(std::array app_data) +{ + submitCallbackEx([app_data=std::move(app_data)]() { return ToPyIntList(app_data.data(), (int) app_data.size()); }); +} + +template<> +void mvAppItem::submitCallback(std::array app_data) { - if (config.enabled) - return config.callback; + submitCallbackEx([app_data=std::move(app_data)]() { return ToPyFloatList(app_data.data(), (int) app_data.size()); }); +} - return ignore_enabled ? config.callback : nullptr; +template<> +void mvAppItem::submitCallback(std::array app_data) +{ + submitCallbackEx([app_data=std::move(app_data)]() { return ToPyFloatList(app_data.data(), (int) app_data.size()); }); +} + +template<> +void mvAppItem::submitCallback(mvColor app_data) +{ + submitCallbackEx([=]() { return ToPyColor(app_data); }); +} + +template<> +void mvAppItem::submitCallback(mvUUID app_data) +{ + submitCallbackEx([=]() { return ToPyUUID(app_data); }); +} + +template<> +void mvAppItem::submitCallback(tm app_data) +{ + submitCallbackEx([=]() { return ToPyTime(app_data); }); +} + +template<> +void mvAppItem::submitCallback(ImVec2 app_data) +{ + submitCallbackEx([=]() { return ToPyPair(app_data.x, app_data.y); }); +} + +template<> +void mvAppItem::submitCallback(ImGuiKey app_data) +{ + submitCallbackEx([=]() { return ToPyInt(app_data); }); } void @@ -163,52 +231,22 @@ mvAppItem::handleKeywordArgs(PyObject* dict, const std::string& parser) if (PyObject* item = PyDict_GetItemString(dict, "callback")) { - if (config.callback) - Py_XDECREF(config.callback); - - // TODO: investigate if PyNone should be increffed - Py_XINCREF(item); - if (item == Py_None) - config.callback = nullptr; - else - config.callback = item; + config.callback = mvPyObject(item == Py_None? nullptr : item, true); } if (PyObject* item = PyDict_GetItemString(dict, "drag_callback")) { - if (config.dragCallback) - Py_XDECREF(config.dragCallback); - - Py_XINCREF(item); - if (item == Py_None) - config.dragCallback = nullptr; - else - config.dragCallback = item; + config.dragCallback = mvPyObject(item == Py_None? nullptr : item, true); } if (PyObject* item = PyDict_GetItemString(dict, "drop_callback")) { - if (config.dropCallback) - Py_XDECREF(config.dropCallback); - - Py_XINCREF(item); - - if (item == Py_None) - config.dropCallback = nullptr; - else - config.dropCallback = item; + config.dropCallback = mvPyObject(item == Py_None? nullptr : item, true); } if (PyObject* item = PyDict_GetItemString(dict, "user_data")) { - if (config.user_data) - Py_XDECREF(config.user_data); - - Py_XINCREF(item); - if (item == Py_None) - config.user_data = nullptr; - else - config.user_data = item; + *config.user_data = mvPyObject(item == Py_None? nullptr : item, true); } handleSpecificKeywordArgs(dict); diff --git a/src/mvAppItem.h b/src/mvAppItem.h index b8711a95d..a0e3a0f0e 100644 --- a/src/mvAppItem.h +++ b/src/mvAppItem.h @@ -127,10 +127,13 @@ struct mvAppItemConfig bool searchDelayed = false; bool useInternalLabel = true; // when false, will use specificed label bool tracked = false; - PyObject* callback = nullptr; - PyObject* user_data = nullptr; - PyObject* dragCallback = nullptr; - PyObject* dropCallback = nullptr; + mvPyObject callback = nullptr; + mvPyObject dragCallback = nullptr; + mvPyObject dropCallback = nullptr; + // We store user_data as a pointer because that's how we'll need it when submitting + // the callback. This is to pass user_data into mvAddCallback that comes from a + // different source than the callback owner (required for the drag callback). + std::shared_ptr user_data = std::make_shared(nullptr); }; struct mvAppItemDrawInfo @@ -146,7 +149,7 @@ struct mvAppItemDrawInfo //----------------------------------------------------------------------------- // mvAppItem //----------------------------------------------------------------------------- -class mvAppItem +class mvAppItem : public std::enable_shared_from_this { public: @@ -207,7 +210,35 @@ class mvAppItem //----------------------------------------------------------------------------- // callbacks //----------------------------------------------------------------------------- - [[nodiscard]] PyObject* getCallback(b8 ignore_enabled = true); // returns the callback. If ignore_enable false and item is disabled then no callback will be returned. + // Submits the specified callback, if any, with user_data from the item config + // and app_data created by app_data_func (typically a lambda). + // Note: `callback` must be a member of `this` (or of a nested object, like `config`). + // It does *not* check if the item is enabled or disabled, because some "custom" + // callbacks historically run even on disabled items. + template + void submitCallbackEx(PyObject* callback, AppDataFunc app_data_func) + { + // The current `mvAppItem` becomes the owner of this callback, and as soon + // as it gets deleted, the callback entry will be thrown away. + mvAddCallback(weak_from_this(), callback, config.user_data, uuid, config.alias, app_data_func); + } + + // Submits the mvAppItem's "default" callback, if any, with user_data from + // the item config and app_data created by app_data_func (typically a lambda). + // The callback is only submitted if the item is enabled; on a disabled item, + // the call is effectively ignored. + template + void submitCallbackEx(AppDataFunc app_data_func) + { + if (!config.enabled) + return; + submitCallbackEx(config.callback, app_data_func); + } + + template + void submitCallback(AppDataType app_data); // submits the callback, if any, passing it app_data, and also user_data from the item config + + void submitCallback(); // submits the callback with app_data=None //----------------------------------------------------------------------------- // config setters diff --git a/src/mvBasicWidgets.cpp b/src/mvBasicWidgets.cpp index f8c48244c..7c009d96e 100644 --- a/src/mvBasicWidgets.cpp +++ b/src/mvBasicWidgets.cpp @@ -2749,10 +2749,7 @@ DearPyGui::draw_button(ImDrawList* drawlist, mvAppItem& item, const mvButtonConf if (activated) { - if (item.config.alias.empty()) - mvAddCallback(item.getCallback(false), item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(item.getCallback(false), item.config.alias, nullptr, item.config.user_data); + item.submitCallback(); } } @@ -2857,12 +2854,7 @@ DearPyGui::draw_combo(ImDrawList* drawlist, mvAppItem& item, mvComboConfig& conf { if (item.config.enabled) { *config.value = name; } - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyString(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyString(value), item.config.user_data);}); + item.submitCallback(*config.value); } item.state.edited = ImGui::IsItemEdited(); @@ -2959,12 +2951,7 @@ DearPyGui::draw_checkbox(ImDrawList* drawlist, mvAppItem& item, mvCheckboxConfig if (ImGui::Checkbox(item.info.internalLabel.c_str(), item.config.enabled ? config.value.get() : &config.disabled_value)) { - bool value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyBool(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyBool(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -3056,12 +3043,7 @@ DearPyGui::draw_drag_float(ImDrawList* drawlist, mvAppItem& item, mvDragFloatCon item.config.enabled ? config.value.get() : &config.disabled_value, config.speed, config.minv, config.maxv, config.format.c_str(), config.flags)) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyFloat(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloat(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -3154,12 +3136,7 @@ DearPyGui::draw_drag_double(ImDrawList* drawlist, mvAppItem& item, mvDragDoubleC item.config.enabled ? config.value.get() : &config.disabled_value, config.speed, &config.minv, &config.maxv, config.format.c_str(), config.flags)) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyDouble(value), item.config.user_data); }); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyDouble(value), item.config.user_data); }); + item.submitCallback(*config.value); } } @@ -3252,15 +3229,7 @@ DearPyGui::draw_drag_int(ImDrawList* drawlist, mvAppItem& item, mvDragIntConfig& item.config.enabled ? config.value.get() : &config.disabled_value, config.speed, config.minv, config.maxv, config.format.c_str(), config.flags)) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyInt(value), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyInt(value), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -3369,15 +3338,7 @@ DearPyGui::draw_drag_intx(ImDrawList* drawlist, mvAppItem& item, mvDragIntMultiC if (activated) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyIntList(value.data(), (int)value.size()), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyIntList(value.data(), (int)value.size()), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -3484,15 +3445,7 @@ DearPyGui::draw_drag_floatx(ImDrawList* drawlist, mvAppItem& item, mvDragFloatMu if (activated) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); + item.submitCallback(*config.value); } } //----------------------------------------------------------------------------- @@ -3586,15 +3539,7 @@ DearPyGui::draw_drag_doublex(ImDrawList* drawlist, mvAppItem& item, mvDragDouble if (activated) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); + item.submitCallback(*config.value); } } //----------------------------------------------------------------------------- @@ -3690,12 +3635,7 @@ DearPyGui::draw_slider_float(ImDrawList* drawlist, mvAppItem& item, mvSliderFloa if (ImGui::VSliderFloat(item.info.internalLabel.c_str(), ImVec2((float)item.config.width, (float)item.config.height), item.config.enabled ? config.value.get() : &config.disabled_value, config.minv, config.maxv, config.format.c_str())) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyFloat(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloat(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -3703,11 +3643,7 @@ DearPyGui::draw_slider_float(ImDrawList* drawlist, mvAppItem& item, mvSliderFloa { if (ImGui::SliderFloat(item.info.internalLabel.c_str(), item.config.enabled ? config.value.get() : &config.disabled_value, config.minv, config.maxv, config.format.c_str(), config.flags)) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyFloat(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloat(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -3806,12 +3742,7 @@ DearPyGui::draw_slider_double(ImDrawList* drawlist, mvAppItem& item, mvSliderDou if (ImGui::VSliderScalar(item.info.internalLabel.c_str(), ImVec2((float)item.config.width, (float)item.config.height), ImGuiDataType_Double, item.config.enabled ? config.value.get() : &config.disabled_value, &config.minv, &config.maxv, config.format.c_str())) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyDouble(value), item.config.user_data); }); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyDouble(value), item.config.user_data); }); + item.submitCallback(*config.value); } } @@ -3819,11 +3750,7 @@ DearPyGui::draw_slider_double(ImDrawList* drawlist, mvAppItem& item, mvSliderDou { if (ImGui::SliderScalar(item.info.internalLabel.c_str(), ImGuiDataType_Double, item.config.enabled ? config.value.get() : &config.disabled_value, &config.minv, &config.maxv, config.format.c_str(), config.flags)) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyDouble(value), item.config.user_data); }); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyDouble(value), item.config.user_data); }); + item.submitCallback(*config.value); } } @@ -3932,12 +3859,7 @@ DearPyGui::draw_slider_floatx(ImDrawList* drawlist, mvAppItem& item, mvSliderFlo if (activated) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -4032,12 +3954,7 @@ DearPyGui::draw_slider_doublex(ImDrawList* drawlist, mvAppItem& item, mvSliderDo if (activated) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); }); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); }); + item.submitCallback(*config.value); } } @@ -4134,12 +4051,7 @@ DearPyGui::draw_slider_int(ImDrawList* drawlist, mvAppItem& item, mvSliderIntCon if (ImGui::VSliderInt(item.info.internalLabel.c_str(), ImVec2((float)item.config.width, (float)item.config.height), item.config.enabled ? config.value.get() : &config.disabled_value, config.minv, config.maxv, config.format.c_str())) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyInt(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyInt(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -4147,11 +4059,7 @@ DearPyGui::draw_slider_int(ImDrawList* drawlist, mvAppItem& item, mvSliderIntCon { if (ImGui::SliderInt(item.info.internalLabel.c_str(), item.config.enabled ? config.value.get() : &config.disabled_value, config.minv, config.maxv, config.format.c_str(), config.flags)) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyInt(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyInt(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -4260,12 +4168,7 @@ DearPyGui::draw_slider_intx(ImDrawList* drawlist, mvAppItem& item, mvSliderIntMu if (activated) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyIntList(value.data(), (int)value.size()), item.config.user_data); }); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyIntList(value.data(), (int)value.size()), item.config.user_data); }); + item.submitCallback(*config.value); } } @@ -4366,16 +4269,7 @@ DearPyGui::draw_listbox(ImDrawList *drawlist, mvAppItem &item, mvListboxConfig & { *config.value = config.names[config.index]; config.disabled_value = config.names[config.index]; - auto value = *config.value; - - if(item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyString(value), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyString(value), item.config.user_data); - }); + item.submitCallback(*config.value); } ImGui::PopStyleColor(); @@ -4481,12 +4375,7 @@ DearPyGui::draw_radio_button(ImDrawList* drawlist, mvAppItem& item, mvRadioButto { *config.value = config.itemnames[config.index]; config.disabled_value = config.itemnames[config.index]; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.uuid, ToPyString(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyString(value), item.config.user_data);}); + item.submitCallback(*config.value); } item.state.edited = ImGui::IsItemEdited(); @@ -4615,15 +4504,7 @@ DearPyGui::draw_input_text(ImDrawList* drawlist, mvAppItem& item, mvInputTextCon if (activated) { - auto value = *config.value; - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyString(value), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyString(value), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -4735,16 +4616,7 @@ DearPyGui::draw_input_int(ImDrawList* drawlist, mvAppItem& item, mvInputIntConfi if (config.last_value != *config.value) { config.last_value = *config.value; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyInt(value), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyInt(value), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -4884,16 +4756,7 @@ DearPyGui::draw_input_floatx(ImDrawList* drawlist, mvAppItem& item, mvInputFloat if (config.last_value != *config.value) { config.last_value = *config.value; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -5007,16 +4870,7 @@ DearPyGui::draw_input_float(ImDrawList* drawlist, mvAppItem& item, mvInputFloatC if (config.last_value != *config.value) { config.last_value = *config.value; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyFloat(value), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloat(value), item.config.user_data); - }); + item.submitCallback(*config.value); } } } @@ -5108,13 +4962,7 @@ DearPyGui::draw_knob_float(ImDrawList* drawlist, mvAppItem& item, mvKnobFloatCon if (KnobFloat(item.config.specifiedLabel.c_str(), item.config.enabled ? config.value.get() : &config.disabled_value, config.minv, config.maxv, config.step)) { - auto value = *config.value; - mvSubmitCallback([&item, value]() { - if (item.config.alias.empty()) - mvAddCallback(item.getCallback(false), item.uuid, ToPyFloat(value), item.config.user_data); - else - mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloat(value), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -5225,16 +5073,7 @@ DearPyGui::draw_input_double(ImDrawList* drawlist, mvAppItem& item, mvInputDoubl if (config.last_value != *config.value) { config.last_value = *config.value; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyDouble(value), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyDouble(value), item.config.user_data); - }); + item.submitCallback(*config.value); } } } @@ -5361,16 +5200,7 @@ DearPyGui::draw_input_doublex(ImDrawList* drawlist, mvAppItem& item, mvInputDoub if (config.last_value != *config.value) { config.last_value = *config.value; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloatList(value.data(), (int)value.size()), item.config.user_data); - }); + item.submitCallback(*config.value); } } @@ -5511,16 +5341,7 @@ DearPyGui::draw_input_intx(ImDrawList* drawlist, mvAppItem& item, mvInputIntMult { config.last_value = *config.value; - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.uuid, ToPyIntList(value.data(), (int)value.size()), item.config.user_data); - }); - else - mvSubmitCallback([&item, value]() { - mvAddCallback(item.getCallback(false), item.config.alias, ToPyIntList(value.data(), (int)value.size()), item.config.user_data); - }); + item.submitCallback(*config.value); } } } @@ -5743,12 +5564,7 @@ DearPyGui::draw_selectable(ImDrawList* drawlist, mvAppItem& item, mvSelectableCo if (ImGui::Selectable(item.info.internalLabel.c_str(), config.value.get(), config.flags, ImVec2((float)item.config.width, (float)item.config.height))) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyBool(value), item.config.user_data);}); - else - mvSubmitCallback([&item, value]() {mvAddCallback(item.getCallback(false), item.config.alias, ToPyBool(value), item.config.user_data);}); + item.submitCallback(*config.value); } } @@ -5836,10 +5652,7 @@ DearPyGui::draw_tab_button(ImDrawList* drawlist, mvAppItem& item, mvTabButtonCon if (ImGui::TabItemButton(item.info.internalLabel.c_str(), config.flags)) { - if (item.config.alias.empty()) - mvAddCallback(item.getCallback(false), item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(item.getCallback(false), item.config.alias, nullptr, item.config.user_data); + item.submitCallback(); } } @@ -5934,12 +5747,7 @@ DearPyGui::draw_menu_item(ImDrawList* drawlist, mvAppItem& item, mvMenuItemConfi // create menu item and see if its selected if (ImGui::MenuItem(item.info.internalLabel.c_str(), config.shortcut.c_str(), config.check ? config.value.get() : nullptr, item.config.enabled)) { - bool value = *config.value; - - if (item.config.alias.empty()) - mvAddCallback(item.config.callback, item.uuid, ToPyBool(value), item.config.user_data); - else - mvAddCallback(item.config.callback, item.config.alias, ToPyBool(value), item.config.user_data); + item.submitCallback(*config.value); } ImGui::PopStyleColor(); @@ -6259,10 +6067,7 @@ DearPyGui::draw_image_button(ImDrawList* drawlist, mvAppItem& item, mvImageButto ImVec2(config.uv_min.x, config.uv_min.y), ImVec2(config.uv_max.x, config.uv_max.y), config.backgroundColor, config.tintColor)) { - if (item.config.alias.empty()) - mvAddCallback(item.getCallback(false), item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(item.getCallback(false), item.config.alias, nullptr, item.config.user_data); + item.submitCallback(); } ImGui::PopID(); diff --git a/src/mvCallbackRegistry.cpp b/src/mvCallbackRegistry.cpp index 079b1a338..d292263dd 100644 --- a/src/mvCallbackRegistry.cpp +++ b/src/mvCallbackRegistry.cpp @@ -19,15 +19,27 @@ void mvRunTasks() void mvFrameCallback(i32 frame) { + auto callbackRegistry = GContext->callbackRegistry; // for brevity - if (frame > GContext->callbackRegistry->highestFrame) + if (frame > callbackRegistry->highestFrame) return; - if (GContext->callbackRegistry->frameCallbacks.count(frame) == 0) + if (callbackRegistry->frameCallbacks.count(frame) == 0) return; - mvAddCallback(GContext->callbackRegistry->frameCallbacks[frame], frame, nullptr, - GContext->callbackRegistry->frameCallbacksUserData[frame]); + // We have to use `std::unordered_map::at()` instead of indexing it with `[]`: + // `mvPyObject` has no default constructor, whereas `operator[]` can insert + // a new value into the map and therefore requires a default constructor. + + auto callback = std::make_shared(std::move(callbackRegistry->frameCallbacks.at(frame))); + auto user_data = std::make_shared(std::move(callbackRegistry->frameCallbacksUserData.at(frame))); + + // Since mvPyObject objects remaining in the maps are now "empty", it's safe to + // delete them right here even though we don't own the GIL. + callbackRegistry->frameCallbacks.erase(frame); + callbackRegistry->frameCallbacksUserData.erase(frame); + + mvAddOwnerlessCallback(callback, user_data, (mvUUID)frame, "", []() -> PyObject* { return nullptr; }); } bool mvRunCallbacks() @@ -49,314 +61,92 @@ bool mvRunCallbacks() return true; } -void mvAddCallback(PyObject* callable, mvUUID sender, PyObject* app_data, PyObject* user_data, bool decrementAppData) +void mvAddCallback(const std::weak_ptr& owner, + PyObject* callback, + const std::shared_ptr& user_data, + mvUUID sender, + const std::string& alias) { - - if (GContext->callbackRegistry->callCount > GContext->callbackRegistry->maxNumberOfCalls) - { - if (app_data != nullptr) - Py_XDECREF(app_data); - if (user_data != nullptr) - Py_XDECREF(user_data); - assert(false); - return; - } - - if (GContext->IO.manualCallbacks) - { - if (callable != nullptr) - Py_XINCREF(callable); - if (app_data != nullptr) - Py_XINCREF(app_data); - if (user_data != nullptr) - Py_XINCREF(user_data); - GContext->callbackRegistry->jobs.push_back({ sender, callable, app_data, user_data }); - return; - } - - mvSubmitCallback([=]() { - mvRunCallback(callable, sender, app_data, user_data, decrementAppData); - }); + mvAddCallback(owner, callback, user_data, sender, alias, []() -> PyObject* { return nullptr; }); } -void mvAddCallback(PyObject* callable, const std::string& sender, PyObject* app_data, PyObject* user_data) +void mvAddOwnerlessCallback(const std::shared_ptr& callback, + const std::shared_ptr& user_data, + mvUUID sender, + const std::string& alias) { - - if (GContext->callbackRegistry->callCount > GContext->callbackRegistry->maxNumberOfCalls) - { - - if (app_data != nullptr) - Py_XDECREF(app_data); - if (user_data != nullptr) - Py_XDECREF(user_data); - assert(false); - return; - } - - if (GContext->IO.manualCallbacks) - { - if (callable != nullptr) - Py_XINCREF(callable); - if (app_data != nullptr) - Py_XINCREF(app_data); - if (user_data != nullptr) - Py_XINCREF(user_data); - GContext->callbackRegistry->jobs.push_back({ 0, callable, app_data, user_data, sender }); - return; - } - - mvSubmitCallback([=]() { - mvRunCallback(callable, sender, app_data, user_data); - }); + mvAddOwnerlessCallback(callback, user_data, sender, alias, []() -> PyObject* { return nullptr; }); } -void mvRunCallback(PyObject* callable, const std::string& sender, PyObject* app_data, PyObject* user_data) +void mvRunCallback(PyObject* callback, PyObject* user_data, mvUUID sender, const std::string& sender_alias, PyObject* app_data) { - if (callable == nullptr) - { - //if (data != nullptr) - // Py_XDECREF(data); + if (callback == nullptr) return; - } - if (!PyCallable_Check(callable)) + if (!PyCallable_Check(callback)) { - if (app_data != nullptr) - Py_XDECREF(app_data); - if (user_data != nullptr) - Py_XDECREF(user_data); mvThrowPythonError(mvErrorCode::mvNone, "Callable not callable."); PyErr_Print(); return; } - if (app_data == nullptr) - { - app_data = Py_None; - Py_XINCREF(app_data); - } - Py_XINCREF(app_data); - - if (user_data == nullptr) - { - user_data = Py_None; - Py_XINCREF(user_data); - } - Py_XINCREF(user_data); - //PyErr_Clear(); if (PyErr_Occurred()) PyErr_Print(); - if (PyErr_Occurred()) - PyErr_Print(); - - PyObject* fc = PyObject_GetAttrString(callable, "__code__"); + PyObject* fc = PyObject_GetAttrString(callback, "__code__"); if (fc) { PyObject* ac = PyObject_GetAttrString(fc, "co_argcount"); if (ac) { i32 count = PyLong_AsLong(ac); - if (PyMethod_Check(callable)) + if (PyMethod_Check(callback)) count--; - if (count > 3) - { - mvPyObject pArgs(PyTuple_New(count)); - PyTuple_SetItem(pArgs, 0, ToPyString(sender)); - PyTuple_SetItem(pArgs, 1, app_data); // steals data, so don't deref - PyTuple_SetItem(pArgs, 2, user_data); // steals data, so don't deref - - for (int i = 3; i < count; i++) - PyTuple_SetItem(pArgs, i, GetPyNone()); - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - - } - else if (count == 3) - { - mvPyObject pArgs(PyTuple_New(3)); - PyTuple_SetItem(pArgs, 0, ToPyString(sender)); - PyTuple_SetItem(pArgs, 1, app_data); // steals data, so don't deref - PyTuple_SetItem(pArgs, 2, user_data); // steals data, so don't deref - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - pArgs.delRef(); - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - - } - else if (count == 2) - { - mvPyObject pArgs(PyTuple_New(2)); - PyTuple_SetItem(pArgs, 0, ToPyString(sender)); - PyTuple_SetItem(pArgs, 1, app_data); // steals data, so don't deref - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - pArgs.delRef(); - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); + mvPyObject pArgs(count > 0 ? PyTuple_New(count) : nullptr); - } - else if (count == 1) + if (count > 0) { - mvPyObject pArgs(PyTuple_New(1)); - PyTuple_SetItem(pArgs, 0, ToPyString(sender)); - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); + PyTuple_SetItem(pArgs, 0, sender_alias.empty()? + ToPyUUID(sender) : + ToPyString(sender_alias)); + + if (count > 1) + { + if (app_data == nullptr) + app_data = Py_None; + // Need an owned ref here: PyTuple_SetItem takes ownership; + // this also handles Py_None correctly (need to incref it). + Py_INCREF(app_data); + PyTuple_SetItem(pArgs, 1, app_data); + + if (count > 2) + { + if (user_data == nullptr) + user_data = Py_None; + // Need an owned ref here: PyTuple_SetItem takes ownership; + // this also handles Py_None correctly (need to incref it). + Py_INCREF(user_data); + PyTuple_SetItem(pArgs, 2, user_data); + + // If the callback takes more parms, just pass None in there + for (int i = 3; i < count; i++) + PyTuple_SetItem(pArgs, i, GetPyNone()); + } + } } - else - { - mvPyObject result(PyObject_CallObject(callable, nullptr)); - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); + // perform the actual call + mvPyObject result(PyObject_CallObject(callback, pArgs)); + // check if call succeeded + if (!result.isOk()) + PyErr_Print(); - } Py_DECREF(ac); } Py_DECREF(fc); } } - -void mvRunCallback(PyObject* callable, mvUUID sender, PyObject* app_data, PyObject* user_data, bool decrementAppData) -{ - - if (callable == nullptr) - { - //if (data != nullptr) - // Py_XDECREF(data); - return; - } - - if (!PyCallable_Check(callable)) - { - if (app_data != nullptr) - Py_XDECREF(app_data); - if (user_data != nullptr) - Py_XDECREF(user_data); - mvThrowPythonError(mvErrorCode::mvNone, "Callable not callable."); - PyErr_Print(); - return; - } - - if (app_data == nullptr) - { - app_data = Py_None; - Py_XINCREF(app_data); - } - if(decrementAppData) - Py_XINCREF(app_data); - - if (user_data == nullptr) - { - user_data = Py_None; - Py_XINCREF(user_data); - } - Py_XINCREF(user_data); - - //PyErr_Clear(); - if (PyErr_Occurred()) - PyErr_Print(); - - if (PyErr_Occurred()) - PyErr_Print(); - - PyObject* fc = PyObject_GetAttrString(callable, "__code__"); - if (fc) { - PyObject* ac = PyObject_GetAttrString(fc, "co_argcount"); - if (ac) { - i32 count = PyLong_AsLong(ac); - - if (PyMethod_Check(callable)) - count--; - - if (count > 3) - { - mvPyObject pArgs(PyTuple_New(count)); - PyTuple_SetItem(pArgs, 0, ToPyUUID(sender)); - PyTuple_SetItem(pArgs, 1, app_data); // steals data, so don't deref - PyTuple_SetItem(pArgs, 2, user_data); // steals data, so don't deref - - for (int i = 3; i < count; i++) - PyTuple_SetItem(pArgs, i, GetPyNone()); - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - - } - else if (count == 3) - { - mvPyObject pArgs(PyTuple_New(3)); - PyTuple_SetItem(pArgs, 0, ToPyUUID(sender)); - PyTuple_SetItem(pArgs, 1, app_data); // steals data, so don't deref - PyTuple_SetItem(pArgs, 2, user_data); // steals data, so don't deref - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - pArgs.delRef(); - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - - } - else if (count == 2) - { - mvPyObject pArgs(PyTuple_New(2)); - PyTuple_SetItem(pArgs, 0, ToPyUUID(sender)); - PyTuple_SetItem(pArgs, 1, app_data); // steals data, so don't deref - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - pArgs.delRef(); - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - - } - else if(count == 1) - { - mvPyObject pArgs(PyTuple_New(1)); - PyTuple_SetItem(pArgs, 0, ToPyUUID(sender)); - - mvPyObject result(PyObject_CallObject(callable, pArgs)); - - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - } - else - { - mvPyObject result(PyObject_CallObject(callable, nullptr)); - - // check if call succeeded - if (!result.isOk()) - PyErr_Print(); - - - } - Py_DECREF(ac); - } - Py_DECREF(fc); - } - -} \ No newline at end of file diff --git a/src/mvCallbackRegistry.h b/src/mvCallbackRegistry.h index 1c84f78ae..e8825bbd5 100644 --- a/src/mvCallbackRegistry.h +++ b/src/mvCallbackRegistry.h @@ -193,26 +193,25 @@ class mvQueue }; -static PyObject* SanitizeCallback(PyObject* callback) -{ - if (callback == Py_None) - return nullptr; - - return callback; -} - struct mvCallbackJob { - mvUUID sender = 0; - PyObject* callback = nullptr; - PyObject* app_data = nullptr; - PyObject* user_data = nullptr; - std::string sender_str; + std::weak_ptr owner; + // Only valid if `owner` is alive; one must lock() the owner before accessing + // the callback. + PyObject* callback; + std::shared_ptr user_data; + mvUUID sender; + std::string alias; + std::function app_data_func; + // Either `callback` (and `owner`) or `ownerless_callback` must be set, + // but not both - otherwise one of them will be ignored. + std::shared_ptr ownerless_callback = nullptr; }; struct mvCallbackRegistry { - const i32 maxNumberOfCalls = 50; + // TODO: ideally, it should be configurable (e.g. via configure_app) + const i32 maxNumberOfCalls = 500; std::vector jobs; mvQueue tasks; @@ -221,22 +220,89 @@ struct mvCallbackRegistry std::atomic callCount = 0; // callbacks - PyObject* resizeCallback = nullptr; - PyObject* onCloseCallback = nullptr; - PyObject* resizeCallbackUserData = nullptr; - PyObject* onCloseCallbackUserData = nullptr; + std::shared_ptr resizeCallback = std::make_shared(nullptr); + std::shared_ptr resizeCallbackUserData = std::make_shared(nullptr); + std::shared_ptr onCloseCallback = std::make_shared(nullptr); + std::shared_ptr onCloseCallbackUserData = std::make_shared(nullptr); i32 highestFrame = 0; - std::unordered_map frameCallbacks; - std::unordered_map frameCallbacksUserData; + std::unordered_map frameCallbacks; + std::unordered_map frameCallbacksUserData; }; void mvFrameCallback(i32 frame); void mvRunTasks(); -void mvRunCallback(PyObject* callback, mvUUID sender, PyObject* app_data, PyObject* user_data, bool decrementAppData = true); -void mvRunCallback(PyObject* callback, const std::string& sender, PyObject* app_data, PyObject* user_data); -void mvAddCallback(PyObject* callback, mvUUID sender, PyObject* app_data, PyObject* user_data, bool decrementAppData = true); -void mvAddCallback(PyObject* callback, const std::string& sender, PyObject* app_data, PyObject* user_data); +// All PyObject references here are borrowed references - caller must release them after this call +void mvRunCallback(PyObject* callback, PyObject* user_data, mvUUID sender = 0, const std::string& sender_alias = "", PyObject* app_data = nullptr); + +// Note: We pass the `callback` and its `user_data` as two separate arguments (rather +// than a single object) because, even though they only make sense together, `mvAppItem` may +// combine the same `user_data` with different callbacks. We don't want to spread `user_data` +// instances all across `mvAppItem`. +// The `callback` must be valid all the time while `owner` is alive. This works for +// the fields of `owner`; if `callback` is not a field of `owner`, the caller must make +// sure that `callback`'s lifetime is at least as long as `owner`'s. +// When the callback is about to be executed on the handlers thread, the callback queue +// acquires a shared_ptr to `owner` and holds it while the callback is being executed. +// If the `owner` is already lost by the moment the callback is fetched from the +// queue, the callback will be silently ignored. This effectively cleans the queue +// from irrelevant callbacks - lingering there after `mvAppItem` deletion and such. +template +void mvAddCallback(const std::weak_ptr& owner, + PyObject* callback, + const std::shared_ptr& user_data, + mvUUID sender, + const std::string& alias, + AppDataFunc&& app_data_func) +{ + if (GContext->IO.manualCallbacks) + { + GContext->callbackRegistry->jobs.push_back({owner, callback, user_data, sender, alias, std::forward(app_data_func)}); + return; + } + mvSubmitCallback([=, app_data_func = std::forward(app_data_func)] () { + auto liveOwner = owner.lock(); // we need it to live through the mvRunCallback, hence constructing it separately rather than within "if" + if (liveOwner) + mvRunCallback(callback, *user_data, sender, alias, mvPyObject(app_data_func())); + }); +} + +// This overload exists purely to provide default argument values - we can't do this +// directly on the template version above because the compiler won't be able to deduce +// `app_data_func` type if `app_data_func` is omitted (that is, we can't really use +// the default on `app_data_func`). +void mvAddCallback(const std::weak_ptr& owner, + PyObject* callback, + const std::shared_ptr& user_data, + mvUUID sender = 0, + const std::string& alias = ""); + +template +void mvAddOwnerlessCallback(const std::shared_ptr& callback, + const std::shared_ptr& user_data, + mvUUID sender, + const std::string& alias, + AppDataFunc&& app_data_func) +{ + if (GContext->IO.manualCallbacks) + { + GContext->callbackRegistry->jobs.push_back({{}, nullptr, user_data, sender, alias, std::forward(app_data_func), callback}); + return; + } + mvSubmitCallback([=, app_data_func = std::forward(app_data_func)]() { + mvRunCallback(*callback, *user_data, sender, alias, mvPyObject(app_data_func())); + }); +} + +// This overload exists purely to provide default argument values - we can't do this +// directly on the template version above because the compiler won't be able to deduce +// `app_data_func` type if `app_data_func` is omitted (that is, we can't really use +// the default on `app_data_func`). +void mvAddOwnerlessCallback(const std::shared_ptr& callback, + const std::shared_ptr& user_data, + mvUUID sender = 0, + const std::string& alias = ""); + bool mvRunCallbacks(); template @@ -247,7 +313,7 @@ std::future::type> mvSubmitTask(F f) std::packaged_task task(std::move(f)); std::future res(task.get_future()); - if (GContext->started) + if (GContext->running) GContext->callbackRegistry->tasks.push(std::move(task)); else task(); @@ -256,11 +322,12 @@ std::future::type> mvSubmitTask(F f) } template -std::future::type> mvSubmitCallback(F f) +std::future::type> mvSubmitCallback(F f, bool ignore_limit = false) { - if (GContext->callbackRegistry->callCount > GContext->callbackRegistry->maxNumberOfCalls) + if (GContext->callbackRegistry->callCount > GContext->callbackRegistry->maxNumberOfCalls && !ignore_limit) { + assert(false); return {}; } diff --git a/src/mvColors.cpp b/src/mvColors.cpp index 2916cdacd..beffd8572 100644 --- a/src/mvColors.cpp +++ b/src/mvColors.cpp @@ -66,10 +66,7 @@ DearPyGui::draw_color_button(ImDrawList* drawlist, mvAppItem& item, mvColorButto if (ImGui::ColorButton(item.info.internalLabel.c_str(), col, config.flags, ImVec2((float)item.config.width, (float)item.config.height))) { - if(item.config.alias.empty()) - mvAddCallback(item.getCallback(false), item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(item.getCallback(false), item.config.alias, nullptr, item.config.user_data); + item.submitCallback(); } } @@ -161,11 +158,7 @@ DearPyGui::draw_color_edit(ImDrawList* drawlist, mvAppItem& item, mvColorEditCon if (ImGui::ColorEdit4(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.flags)) { mvColor color = mvColor((*config.value)[0], (*config.value)[1], (*config.value)[2], (*config.value)[3]); - - if (item.config.alias.empty()) - mvSubmitCallback([&item, color]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyColor(color), item.config.user_data); }); - else - mvSubmitCallback([&item, color]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyColor(color), item.config.user_data); }); + item.submitCallback(color); } } @@ -261,10 +254,7 @@ DearPyGui::draw_color_map_button(ImDrawList* drawlist, mvAppItem& item, mvColorM ScopedID id(item.uuid); if (ImPlot::ColormapButton(item.info.internalLabel.c_str(), ImVec2((float)item.config.width, (float)item.config.height), config.colorMap)) { - if (item.config.alias.empty()) - mvAddCallback(item.getCallback(false), item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(item.getCallback(false), item.config.alias, nullptr, item.config.user_data); + item.submitCallback(); } } @@ -442,11 +432,7 @@ DearPyGui::draw_color_picker(ImDrawList* drawlist, mvAppItem& item, mvColorPicke if (ImGui::ColorPicker4(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.flags)) { mvColor color = mvColor((*config.value)[0], (*config.value)[1], (*config.value)[2], (*config.value)[3]); - - if (item.config.alias.empty()) - mvSubmitCallback([&item, color]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyColor(color), item.config.user_data); }); - else - mvSubmitCallback([&item, color]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyColor(color), item.config.user_data); }); + item.submitCallback(color); } } @@ -535,12 +521,7 @@ DearPyGui::draw_color_map_slider(ImDrawList* drawlist, mvAppItem& item, mvColorM if (ImPlot::ColormapSlider(item.info.internalLabel.c_str(), config.value.get(), &config.color, "", config.colorMap)) { - auto value = *config.value; - - if (item.config.alias.empty()) - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.uuid, ToPyFloat(value), item.config.user_data); }); - else - mvSubmitCallback([&item, value]() { mvAddCallback(item.getCallback(false), item.config.alias, ToPyFloat(value), item.config.user_data); }); + item.submitCallback(*config.value); } } diff --git a/src/mvContainers.cpp b/src/mvContainers.cpp index 045dc662d..93969a59a 100644 --- a/src/mvContainers.cpp +++ b/src/mvContainers.cpp @@ -98,21 +98,11 @@ DearPyGui::fill_configuration_dict(const mvDragPayloadConfig& inConfig, PyObject if (outDict == nullptr) return; - if (inConfig.dragData) - { - Py_XINCREF(inConfig.dragData); - PyDict_SetItemString(outDict, "drag_data", inConfig.dragData); - } - else - PyDict_SetItemString(outDict, "drag_data", GetPyNone()); + PyObject* dragData = *(inConfig.dragData); + PyDict_SetItemString(outDict, "drag_data", dragData? dragData : Py_None); - if (inConfig.dropData) - { - Py_XINCREF(inConfig.dropData); - PyDict_SetItemString(outDict, "drop_data", inConfig.dropData); - } - else - PyDict_SetItemString(outDict, "drop_data", GetPyNone()); + PyObject* dropData = *(inConfig.dropData); + PyDict_SetItemString(outDict, "drop_data", dropData? dropData : Py_None); } void @@ -190,13 +180,9 @@ DearPyGui::fill_configuration_dict(const mvWindowAppItemConfig& inConfig, PyObje PyDict_SetItemString(outDict, "collapsed", mvPyObject(ToPyBool(inConfig.collapsed))); PyDict_SetItemString(outDict, "min_size", mvPyObject(ToPyPairII(inConfig.min_size.x, inConfig.min_size.y))); PyDict_SetItemString(outDict, "max_size", mvPyObject(ToPyPairII(inConfig.max_size.x, inConfig.max_size.y))); - if (inConfig.on_close) - { - Py_XINCREF(inConfig.on_close); - PyDict_SetItemString(outDict, "on_close", inConfig.on_close); - } - else - PyDict_SetItemString(outDict, "on_close", GetPyNone()); + + PyObject* on_close = inConfig.on_close; + PyDict_SetItemString(outDict, "on_close", on_close? on_close : Py_None); // helper to check and set bit auto checkbitset = [outDict](const char* keyword, int flag, const int& flags) @@ -322,20 +308,12 @@ DearPyGui::set_configuration(PyObject* inDict, mvDragPayloadConfig& outConfig) if (PyObject* item = PyDict_GetItemString(inDict, "drag_data")) { - if (outConfig.dragData) - Py_XDECREF(outConfig.dragData); - - Py_XINCREF(item); - outConfig.dragData = item; + *outConfig.dragData = mvPyObject(item == Py_None? nullptr : item, true); } if (PyObject* item = PyDict_GetItemString(inDict, "drop_data")) { - if (outConfig.dropData) - Py_XDECREF(outConfig.dropData); - - Py_XINCREF(item); - outConfig.dropData = item; + *outConfig.dropData = mvPyObject(item == Py_None? nullptr : item, true); } } @@ -449,12 +427,7 @@ DearPyGui::set_configuration(PyObject* inDict, mvAppItem& itemc, mvWindowAppItem if (PyObject* item = PyDict_GetItemString(inDict, "on_close")) { - if (outConfig.on_close) - Py_XDECREF(outConfig.on_close); - item = SanitizeCallback(item); - if (item) - Py_XINCREF(item); - outConfig.on_close = item; + outConfig.on_close = mvPyObject(item == Py_None? nullptr : item, true); } // helper for bit flipping @@ -840,12 +813,7 @@ DearPyGui::draw_tab(ImDrawList* drawlist, mvAppItem& item, mvTabConfig& config) // run call back if it exists if (parent->getSpecificValue() != item.uuid) { - mvSubmitCallback([=, &item]() { - if (parent->config.alias.empty()) - mvAddCallback(parent->getCallback(), parent->uuid, ToPyUUID(item.uuid), parent->config.user_data); - else - mvAddCallback(parent->getCallback(), parent->config.alias, ToPyUUID(item.uuid), parent->config.user_data); - }); + parent->submitCallback(item.uuid); } parent->setValue(item.uuid); @@ -1163,14 +1131,31 @@ DearPyGui::draw_drag_payload(ImDrawList* drawlist, mvAppItem& item, mvDragPayloa { if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { - ImGui::SetDragDropPayload(config.payloadType.c_str(), &item, sizeof(mvDragPayload)); - - if (item.info.parentPtr->config.dragCallback) + // ImGui uses memcpy to store the drag data, and the only thing we can + // store this way more ore less safely is the numeric UUID. Worst case if + // the payload gets deleted during drag'n'drop, we'll simply abandon + // the drop callback. + ImGui::SetDragDropPayload(config.payloadType.c_str(), &item.uuid, sizeof(item.uuid)); + + // The mvDragPayload item is a bit peculiar: historically, it uses its own user_data + // to pass to the parent item's drag callback. That's why we can't directly + // call parentPtr->submitCallback(), as it would take user_data from the parent + // instead of mvDragPayload. + mvAppItem* parent = item.info.parentPtr; + if (parent->config.dragCallback) { - if (item.info.parentPtr->config.alias.empty()) - mvAddCallback(item.info.parentPtr->config.dragCallback, item.config.parent, config.dragData, item.config.user_data); - else - mvAddCallback(item.info.parentPtr->config.dragCallback, item.info.parentPtr->config.alias, config.dragData, item.config.user_data); + // We can't use mvAppItem::submitCallbackEx here because we need custom user_data. + mvAddCallback( + parent->weak_from_this(), + parent->config.dragCallback, + item.config.user_data, + parent->uuid, parent->config.alias, + [dragData=config.dragData] () { + PyObject* pyDragData = *dragData; + Py_XINCREF(pyDragData); + return pyDragData; + } + ); } for (auto& childset : item.childslots) @@ -1531,10 +1516,7 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon item.state.toggledOpen = false; item.state.visible = false; - if (item.config.alias.empty()) - mvAddCallback(config.on_close, item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(config.on_close, item.config.alias, nullptr, item.config.user_data); + item.submitCallbackEx(config.on_close, []() -> PyObject* { return nullptr; }); // handle popping themes cleanup_local_theming(&item); @@ -1558,6 +1540,23 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon // handle popping themes cleanup_local_theming(&item); + + // See if it's just been closed (can't rely on BeginPopup == false here + // because BeginPopup can, even if only theoretically, return false for + // an open popup - e.g. when it has zero size). + if (!ImGui::IsPopupOpen(item.info.internalLabel.c_str())) + { + // Hide it so that the callback doesn't fire at the next frame + item.config.show = false; + // Update item state so that get_item_state is valid + item.state.lastFrameUpdate = GContext->frame; + item.state.hovered = false; + item.state.focused = false; + item.state.toggledOpen = false; + item.state.visible = false; + // Fire the close callback, if any + item.submitCallbackEx(config.on_close, []() -> PyObject* { return nullptr; }); + } return; } } @@ -1711,16 +1710,45 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon item.state.toggledOpen = false; item.state.visible = false; - if (item.config.alias.empty()) - mvAddCallback(config.on_close, item.uuid, nullptr, item.config.user_data); - else - mvAddCallback(config.on_close, item.config.alias, nullptr, item.config.user_data); + item.submitCallbackEx(config.on_close, []() -> PyObject* { return nullptr; }); } if (item.handlerRegistry) item.handlerRegistry->checkEvents(&item.state); } +void +check_drop_event(mvAppItem* item) +{ + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(item->config.payloadType.c_str())) + { + IM_ASSERT(payload->DataSize == sizeof(mvUUID) && "Unexpected drag payload data size."); + mvUUID payloadUUID = *(mvUUID*)payload->Data; + // Let's see if the payload still exists. + auto payloadItem = GetItem(*GContext->itemRegistry, payloadUUID); + if (payloadItem && payloadItem->type == mvAppItemType::mvDragPayload) + { + auto payloadActual = static_cast(payloadItem); + // Note: we're passing None in user_data for backward compatibility + // (mvAddCallback will derive None from the mvPyObject that stores nullptr). + // One day this may change, but we need to decide which of the user_data's + // (parent's or payload's) we're going to pass here. + // We can't use mvAppItem::submitCallbackEx here because we need custom user_data. + mvAddCallback( + item->weak_from_this(), + item->config.dropCallback, + std::make_shared(nullptr), + item->uuid, item->config.alias, + [dragData = payloadActual->configData.dragData] () { + PyObject* pyDragData = *dragData; + Py_XINCREF(pyDragData); + return pyDragData; + } + ); + } + } +} + void apply_drag_drop(mvAppItem* item) { @@ -1732,15 +1760,7 @@ apply_drag_drop(mvAppItem* item) ScopedID id(item->uuid); if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(item->config.payloadType.c_str())) - { - auto payloadActual = static_cast(payload->Data); - if (item->config.alias.empty()) - mvAddCallback(item->config.dropCallback, item->uuid, payloadActual->configData.dragData, nullptr); - else - mvAddCallback(item->config.dropCallback, item->config.alias, payloadActual->configData.dragData, nullptr); - } - + check_drop_event(item); ImGui::EndDragDropTarget(); } } @@ -1754,15 +1774,7 @@ apply_drag_drop_nodraw(mvAppItem* item) ScopedID id(item->uuid); if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(item->config.payloadType.c_str())) - { - auto payloadActual = static_cast(payload->Data); - if (item->config.alias.empty()) - mvAddCallback(item->config.dropCallback, item->uuid, payloadActual->configData.dragData, nullptr); - else - mvAddCallback(item->config.dropCallback, item->config.alias, payloadActual->configData.dragData, nullptr); - } - + check_drop_event(item); ImGui::EndDragDropTarget(); } } diff --git a/src/mvContainers.h b/src/mvContainers.h index bc205f918..56ed8612c 100644 --- a/src/mvContainers.h +++ b/src/mvContainers.h @@ -3,6 +3,15 @@ #include "mvItemRegistry.h" #include +// check_drop_event() implements the typical contents of Dear ImGui's drop target +// (ImGui::BeginDragDropTarget()) tied to DearPyGui's drop callback. You usually +// don't need to call it directly, but it can be used need to implement custom +// drag'n'drop mechanics. +// To implement drag'n'drop in an arbitrary ImGui item, use `apply_drag_drop()` - +// it both implements the entire drop target and renders the drag payload. +void check_drop_event(mvAppItem* item); +// During drag'n'drop, renders drag payload and checks whether it's time to call +// the drop callback. void apply_drag_drop(mvAppItem* item); void apply_drag_drop_nodraw(mvAppItem* item); @@ -118,8 +127,8 @@ struct mvGroupConfig struct mvDragPayloadConfig { std::string payloadType = "$$DPG_PAYLOAD"; - PyObject* dragData = nullptr; - PyObject* dropData = nullptr; + std::shared_ptr dragData = std::make_shared(nullptr); + std::shared_ptr dropData = std::make_shared(nullptr); }; struct mvCollapsingHeaderConfig @@ -161,7 +170,7 @@ struct mvWindowAppItemConfig bool no_background = false; bool collapsed = false; bool no_open_over_existing_popup = true; - PyObject* on_close = nullptr; + mvPyObject on_close = nullptr; mvVec2 min_size = { 100.0f, 100.0f }; mvVec2 max_size = { 30000.0f, 30000.0f }; float scrollX = 0.0f; @@ -288,5 +297,4 @@ class mvWindowAppItem : public mvAppItem void draw(ImDrawList* drawlist, float x, float y) override { DearPyGui::draw_window(drawlist, *this, configData); } void handleSpecificKeywordArgs(PyObject* dict) override { DearPyGui::set_configuration(dict, *this, configData); } void getSpecificConfiguration(PyObject* dict) override { DearPyGui::fill_configuration_dict(configData, dict); } - ~mvWindowAppItem() { PyObject* callback = configData.on_close; mvSubmitCallback([callback]() { if (callback) Py_XDECREF(callback);});} }; \ No newline at end of file diff --git a/src/mvContext.cpp b/src/mvContext.cpp index 00200ca9f..586714880 100644 --- a/src/mvContext.cpp +++ b/src/mvContext.cpp @@ -202,6 +202,11 @@ GetParsers() return const_cast&>(GetModuleParsers()); } +void StopRendering() +{ + GContext->running = false; +} + void InsertConstants_mvContext(std::vector>& constants) { diff --git a/src/mvContext.h b/src/mvContext.h index 10ac7106c..bed6fd2da 100644 --- a/src/mvContext.h +++ b/src/mvContext.h @@ -34,6 +34,8 @@ mvUUID GenerateUUID(); void SetDefaultTheme(); void Render(); std::map& GetParsers(); +// Signals the rendering loop via GContext->running to quit. +void StopRendering(); struct mvInput { @@ -101,7 +103,11 @@ struct mvIO struct mvContext { std::atomic_bool waitOneFrame = false; + // Indicates whether DPG has started at least once in this context, i.e. whether + // associated Dear ImGui contexts exist and can be read from. std::atomic_bool started = false; + // If true, more frames are going to be rendered. Goes back to false on shutdown. + std::atomic_bool running = false; std::recursive_mutex mutex; std::future future; float deltaTime = 0.0f; // time since last frame diff --git a/src/mvDatePicker.cpp b/src/mvDatePicker.cpp index ef309c623..82dab312a 100644 --- a/src/mvDatePicker.cpp +++ b/src/mvDatePicker.cpp @@ -67,13 +67,7 @@ void mvDatePicker::draw(ImDrawList* drawlist, float x, float y) { ImPlot::GetGmtTime(*_imvalue, _value.get()); { - auto value = *_value; - mvSubmitCallback([=]() { - if(config.alias.empty()) - mvAddCallback(getCallback(false), uuid, ToPyTime(value), config.user_data); - else - mvAddCallback(getCallback(false), config.alias, ToPyTime(value), config.user_data); - }); + submitCallback(*_value); } } } diff --git a/src/mvDrawings.cpp b/src/mvDrawings.cpp index c24cc61a6..bb5d082d1 100644 --- a/src/mvDrawings.cpp +++ b/src/mvDrawings.cpp @@ -952,10 +952,7 @@ void mvDrawlist::draw(ImDrawList* drawlist, float x, float y) if (ImGui::InvisibleButton(info.internalLabel.c_str(), ImVec2((float)config.width, (float)config.height), ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle)) { - if (config.alias.empty()) - mvAddCallback(getCallback(false), uuid, nullptr, config.user_data); - else - mvAddCallback(getCallback(false), config.alias, nullptr, config.user_data); + submitCallback(); } UpdateAppItemState(state); diff --git a/src/mvFileDialog.cpp b/src/mvFileDialog.cpp index 7eef5a3c1..5f9d7fb8e 100644 --- a/src/mvFileDialog.cpp +++ b/src/mvFileDialog.cpp @@ -103,25 +103,21 @@ void mvFileDialog::draw(ImDrawList* drawlist, float x, float y) // action if OK clicked or if cancel clicked and cancel callback provided if (_instance.IsOk() || _cancelCallback) { - mvSubmitCallback([&]() + submitCallbackEx( + _instance.IsOk()? config.callback : _cancelCallback, + // We're capturing a weak reference to mvFileDialog, so that the dialog can + // be safely deleted even if there's a callback still waiting in the queue. + [weakThis=weak_from_this()] () -> PyObject* { - PyObject* callback; - PyObject* appData; - - if(_instance.IsOk()) + auto liveThis = weakThis.lock(); + if (liveThis) { - callback = config.callback; - appData = getInfoDict(); - } else { - callback = _cancelCallback; - appData = getInfoDict(); + mvFileDialog* dlg = static_cast(liveThis.get()); + return dlg->getInfoDict(); } - - if(config.alias.empty()) - mvRunCallback(callback, uuid, appData, config.user_data); - else - mvRunCallback(callback, config.alias, appData, config.user_data); - }); + return nullptr; + } + ); } // close @@ -189,15 +185,7 @@ void mvFileDialog::handleSpecificKeywordArgs(PyObject* dict) if (PyObject* item = PyDict_GetItemString(dict, "cancel_callback")) { - Py_XDECREF(_cancelCallback); - - if (item == Py_None) - _cancelCallback = nullptr; - else - { - Py_XINCREF(item); - _cancelCallback = item; - } + _cancelCallback = mvPyObject(item == Py_None? nullptr : item, true); } } diff --git a/src/mvFileDialog.h b/src/mvFileDialog.h index 3b034a4a3..547c22021 100644 --- a/src/mvFileDialog.h +++ b/src/mvFileDialog.h @@ -46,5 +46,5 @@ class mvFileDialog final : public mvAppItem bool _directory = false; mvVec2 _min_size = { 100.0f, 100.0f }; mvVec2 _max_size = { 30000.0f, 30000.0f }; - PyObject* _cancelCallback = nullptr; + mvPyObject _cancelCallback = nullptr; }; \ No newline at end of file diff --git a/src/mvGlobalHandlers.cpp b/src/mvGlobalHandlers.cpp index 791e1126e..5382f37c0 100644 --- a/src/mvGlobalHandlers.cpp +++ b/src/mvGlobalHandlers.cpp @@ -19,26 +19,14 @@ void mvKeyDownHandler::draw(ImDrawList* drawlist, float x, float y) auto key = ImGui::GetKeyData(static_cast(i)); if (key->Down) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyMPair(i, key->DownDuration), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyMPair(i, key->DownDuration), config.user_data); - }); + submitCallbackEx([=]() { return ToPyMPair(i, key->DownDuration); }); } } } else if (ImGui::IsKeyDown(_key)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyMPair(_key, ImGui::GetKeyData(_key)->DownDuration), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyMPair(_key, ImGui::GetKeyData(_key)->DownDuration), config.user_data); - }); + submitCallbackEx([=]() { return ToPyMPair(_key, ImGui::GetKeyData(_key)->DownDuration); }); } } @@ -74,26 +62,14 @@ void mvKeyPressHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::IsKeyPressed(static_cast(i))) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(i), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(i), config.user_data); - }); + submitCallback(i); } } } else if (ImGui::IsKeyPressed(_key)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(_key), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(_key), config.user_data); - }); + submitCallback(_key); } } @@ -141,26 +117,14 @@ void mvKeyReleaseHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::IsKeyReleased(static_cast(i))) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(i), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(i), config.user_data); - }); + submitCallback(i); } } } else if (ImGui::IsKeyReleased(_key)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(_key), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(_key), config.user_data); - }); + submitCallback(_key); } } @@ -208,26 +172,14 @@ void mvMouseClickHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::IsMouseClicked(i)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(i), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(i), config.user_data); - }); + submitCallback(i); } } } else if (ImGui::IsMouseClicked(_button)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(_button), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(_button), config.user_data); - }); + submitCallback(_button); } } @@ -275,26 +227,14 @@ void mvMouseDoubleClickHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::IsMouseDoubleClicked(i)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(i), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(i), config.user_data); - }); + submitCallback(i); } } } else if (ImGui::IsMouseDoubleClicked(_button)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(_button), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(_button), config.user_data); - }); + submitCallback(_button); } } @@ -342,26 +282,14 @@ void mvMouseDownHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::GetIO().MouseDown[i]) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyMPair(i, ImGui::GetIO().MouseDownDuration[i]), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyMPair(i, ImGui::GetIO().MouseDownDuration[i]), config.user_data); - }); + submitCallbackEx([=]() { return ToPyMPair(i, ImGui::GetIO().MouseDownDuration[i]); }); } } } else if (ImGui::GetIO().MouseDown[_button]) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyMPair(_button, ImGui::GetIO().MouseDownDuration[_button]), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyMPair(_button, ImGui::GetIO().MouseDownDuration[_button]), config.user_data); - }); + submitCallbackEx([=]() { return ToPyMPair(_button, ImGui::GetIO().MouseDownDuration[_button]); }); } } @@ -412,15 +340,7 @@ void mvMouseDragHandler::draw(ImDrawList* drawlist, float x, float y) if (ImGui::IsMouseDragging(i, _threshold)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, - ToPyMTrip(i, ImGui::GetMouseDragDelta(i).x, ImGui::GetMouseDragDelta(i).y), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, - ToPyMTrip(i, ImGui::GetMouseDragDelta(i).x, ImGui::GetMouseDragDelta(i).y), config.user_data); - }); + submitCallbackEx([=]() { return ToPyMTrip(i, ImGui::GetMouseDragDelta(i).x, ImGui::GetMouseDragDelta(i).y); }); } } } @@ -429,15 +349,8 @@ void mvMouseDragHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::IsMouseReleased(_button)) ImGui::ResetMouseDragDelta(_button); - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, - ToPyMTrip(_button, ImGui::GetMouseDragDelta(_button).x, ImGui::GetMouseDragDelta(_button).y), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, - ToPyMTrip(_button, ImGui::GetMouseDragDelta(_button).x, ImGui::GetMouseDragDelta(_button).y), config.user_data); - }); + + submitCallbackEx([=]() { return ToPyMTrip(_button, ImGui::GetMouseDragDelta(_button).x, ImGui::GetMouseDragDelta(_button).y); }); } } @@ -495,13 +408,7 @@ void mvMouseMoveHandler::draw(ImDrawList* drawlist, float x, float y) { _oldPos = mousepos; - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyPair(mousepos.x, mousepos.y), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyPair(mousepos.x, mousepos.y), config.user_data); - }); + submitCallback(mousepos); } } } @@ -514,26 +421,14 @@ void mvMouseReleaseHandler::draw(ImDrawList* drawlist, float x, float y) { if (ImGui::IsMouseReleased(i)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(i), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(i), config.user_data); - }); + submitCallback(i); } } } else if (ImGui::IsMouseReleased(_button)) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(_button), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(_button), config.user_data); - }); + submitCallback(_button); } } @@ -578,14 +473,6 @@ void mvMouseWheelHandler::draw(ImDrawList* drawlist, float x, float y) int wheel = (int)ImGui::GetIO().MouseWheel; if (wheel) { - - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyInt(wheel), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyInt(wheel), config.user_data); - }); - + submitCallback(wheel); } } \ No newline at end of file diff --git a/src/mvItemHandlers.cpp b/src/mvItemHandlers.cpp index 93e79a2ad..01dca1844 100644 --- a/src/mvItemHandlers.cpp +++ b/src/mvItemHandlers.cpp @@ -124,19 +124,20 @@ void mvItemHandlerRegistry::onBind(mvAppItem* item) } } +void mvItemHandler::submitHandler(mvAppItem* parent) +{ + submitCallbackEx([uuid=parent->uuid, alias=parent->config.alias] () { + return ToPyUUID(uuid, alias); + }); +} + void mvActivatedHandler::customAction(void* data) { mvAppItemState* state = static_cast(data); if (state->activated) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -146,13 +147,7 @@ void mvActiveHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->active) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -160,51 +155,25 @@ void mvClickedHandler::customAction(void* data) { mvAppItemState* state = static_cast(data); - if (_button == -1 || _button == 0) - if (state->leftclicked) - { - mvSubmitCallback([=]() - { - mvPyObject pArgs(PyTuple_New(2)); - PyTuple_SetItem(pArgs, 0, ToPyInt(0)); - PyTuple_SetItem(pArgs, 1, ToPyUUID(state->parent)); // steals data, so don't deref - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, pArgs, config.user_data); - else - mvRunCallback(getCallback(false), config.alias, pArgs, config.user_data); - }); - } - if (_button == -1 || _button == 1) - if (state->rightclicked) - { - mvSubmitCallback([=]() - { - mvPyObject pArgs(PyTuple_New(2)); - PyTuple_SetItem(pArgs, 0, ToPyInt(1)); - PyTuple_SetItem(pArgs, 1, ToPyUUID(state->parent)); // steals data, so don't deref - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, pArgs, config.user_data); - else - mvRunCallback(getCallback(false), config.alias, pArgs, config.user_data); - }); - } + b8 clicked[] = {state->leftclicked, state->rightclicked, state->middleclicked}; + + int i = (_button < 0)? 0 : _button ; + int end = (_button < 0)? (int)std::size(clicked) : (i + 1); - if (_button == -1 || _button == 2) - if (state->middleclicked) + for (; i < end; i++) + { + if (clicked[i]) { - mvSubmitCallback([=]() - { - mvPyObject pArgs(PyTuple_New(2)); - PyTuple_SetItem(pArgs, 0, ToPyInt(2)); - PyTuple_SetItem(pArgs, 1, ToPyUUID(state->parent)); // steals data, so don't deref - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, pArgs, config.user_data); - else - mvRunCallback(getCallback(false), config.alias, pArgs, config.user_data); - }); + mvAppItem* parent = state->parent; + submitCallbackEx([i, uuid=parent->uuid, alias=parent->config.alias] () { + PyObject* app_data = PyTuple_New(2); + PyTuple_SetItem(app_data, 0, ToPyInt(i)); + PyTuple_SetItem(app_data, 1, ToPyUUID(uuid, alias)); + return app_data; + }); } - + } } void mvClickedHandler::handleSpecificRequiredArgs(PyObject* dict) @@ -242,16 +211,13 @@ void mvDoubleClickedHandler::customAction(void* data) { if (state->doubleclicked[i]) { - mvSubmitCallback([=]() - { - mvPyObject pArgs(PyTuple_New(2)); - PyTuple_SetItem(pArgs, 0, ToPyInt(i)); - PyTuple_SetItem(pArgs, 1, ToPyUUID(state->parent)); // steals data, so don't deref - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, pArgs, config.user_data); - else - mvRunCallback(getCallback(false), config.alias, pArgs, config.user_data); - }); + mvAppItem* parent = state->parent; + submitCallbackEx([i, uuid=parent->uuid, alias=parent->config.alias] () { + PyObject* app_data = PyTuple_New(2); + PyTuple_SetItem(app_data, 0, ToPyInt(i)); + PyTuple_SetItem(app_data, 1, ToPyUUID(uuid, alias)); + return app_data; + }); } } } @@ -286,13 +252,7 @@ void mvDeactivatedAfterEditHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->deactivatedAfterEdit) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -301,13 +261,7 @@ void mvDeactivatedHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->deactivated) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -317,13 +271,7 @@ void mvEditedHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->edited) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -333,13 +281,7 @@ void mvFocusHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->focused) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -348,13 +290,7 @@ void mvHoverHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->hovered) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } @@ -363,42 +299,24 @@ void mvResizeHandler::customAction(void* data) mvAppItemState* state = static_cast(data); if (state->mvRectSizeResized) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, ToPyUUID(state->parent), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, ToPyUUID(state->parent), config.user_data); - }); + submitHandler(state->parent); } } void mvToggledOpenHandler::customAction(void* data) { - - if (static_cast(data)->toggledOpen) + mvAppItemState* state = static_cast(data); + if (state->toggledOpen) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, GetPyNone(), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, GetPyNone(), config.user_data); - }); + submitHandler(state->parent); } } void mvVisibleHandler::customAction(void* data) { - + mvAppItemState* state = static_cast(data); if (static_cast(data)->visible) { - mvSubmitCallback([=]() - { - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, GetPyNone(), config.user_data); - else - mvRunCallback(getCallback(false), config.alias, GetPyNone(), config.user_data); - }); + submitHandler(state->parent); } -} \ No newline at end of file +} diff --git a/src/mvItemHandlers.h b/src/mvItemHandlers.h index be2823245..f49aa92eb 100644 --- a/src/mvItemHandlers.h +++ b/src/mvItemHandlers.h @@ -14,29 +14,38 @@ class mvItemHandlerRegistry : public mvAppItem void onBind(mvAppItem* item); }; -class mvActivatedHandler : public mvAppItem +class mvItemHandler : public mvAppItem { public: - explicit mvActivatedHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvItemHandler(mvUUID uuid) : mvAppItem(uuid) {} + +protected: + void submitHandler(mvAppItem* parent); +}; + +class mvActivatedHandler : public mvItemHandler +{ +public: + explicit mvActivatedHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvActiveHandler : public mvAppItem +class mvActiveHandler : public mvItemHandler { public: - explicit mvActiveHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvActiveHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvClickedHandler : public mvAppItem +class mvClickedHandler : public mvItemHandler { public: int _button = -1; - explicit mvClickedHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvClickedHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; void handleSpecificRequiredArgs(PyObject* dict) override; @@ -44,11 +53,11 @@ class mvClickedHandler : public mvAppItem void getSpecificConfiguration(PyObject* dict) override; }; -class mvDoubleClickedHandler : public mvAppItem +class mvDoubleClickedHandler : public mvItemHandler { public: int _button = -1; - explicit mvDoubleClickedHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvDoubleClickedHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; void handleSpecificRequiredArgs(PyObject* dict) override; @@ -56,66 +65,66 @@ class mvDoubleClickedHandler : public mvAppItem void getSpecificConfiguration(PyObject* dict) override; }; -class mvDeactivatedAfterEditHandler : public mvAppItem +class mvDeactivatedAfterEditHandler : public mvItemHandler { public: - explicit mvDeactivatedAfterEditHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvDeactivatedAfterEditHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvDeactivatedHandler : public mvAppItem +class mvDeactivatedHandler : public mvItemHandler { public: - explicit mvDeactivatedHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvDeactivatedHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvEditedHandler : public mvAppItem +class mvEditedHandler : public mvItemHandler { public: - explicit mvEditedHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvEditedHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvFocusHandler : public mvAppItem +class mvFocusHandler : public mvItemHandler { public: - explicit mvFocusHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvFocusHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvHoverHandler : public mvAppItem +class mvHoverHandler : public mvItemHandler { public: - explicit mvHoverHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvHoverHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvResizeHandler : public mvAppItem +class mvResizeHandler : public mvItemHandler { public: - explicit mvResizeHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvResizeHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvToggledOpenHandler : public mvAppItem +class mvToggledOpenHandler : public mvItemHandler { public: - explicit mvToggledOpenHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvToggledOpenHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; }; -class mvVisibleHandler : public mvAppItem +class mvVisibleHandler : public mvItemHandler { public: - explicit mvVisibleHandler(mvUUID uuid) : mvAppItem(uuid) {} + explicit mvVisibleHandler(mvUUID uuid) : mvItemHandler(uuid) {} void draw(ImDrawList* drawlist, float x, float y) override {} void customAction(void* data = nullptr) override; -}; \ No newline at end of file +}; diff --git a/src/mvItemRegistry.cpp b/src/mvItemRegistry.cpp index 442c27423..07e403511 100644 --- a/src/mvItemRegistry.cpp +++ b/src/mvItemRegistry.cpp @@ -1075,7 +1075,7 @@ RenderItemRegistry(mvItemRegistry& registry) DebugItem("Enabled:", root->config.enabled ? ts : fs); DebugItem("Tracked:", root->config.tracked ? ts : fs); DebugItem("Callback:", root->config.callback ? ts : fs); - DebugItem("User Data:", root->config.user_data ? ts : fs); + DebugItem("User Data:", *(root->config.user_data) ? ts : fs); DebugItem("Drop Callback:", root->config.dropCallback ? ts : fs); DebugItem("Drag Callback:", root->config.dragCallback ? ts : fs); @@ -1275,8 +1275,7 @@ AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_ptr it // this is a unique situation in that the caller always has the GIL registry.capturedItem = item; - mvRunCallback(registry.captureCallback, registry.capturedItem->uuid, nullptr, nullptr); - Py_XDECREF(registry.captureCallback); + mvRunCallback(registry.captureCallback, nullptr, registry.capturedItem->uuid); registry.captureCallback = nullptr; return true; } diff --git a/src/mvItemRegistry.h b/src/mvItemRegistry.h index e6a30b89c..1979b9748 100644 --- a/src/mvItemRegistry.h +++ b/src/mvItemRegistry.h @@ -83,8 +83,8 @@ struct mvItemRegistry b8 showImPlotDebug = false; std::vector> debugWindows; std::shared_ptr capturedItem = nullptr; - PyObject* captureCallback = nullptr; - PyObject* captureCallbackUserData = nullptr; + mvPyObject captureCallback = nullptr; + mvPyObject captureCallbackUserData = nullptr; // roots std::vector> colormapRoots; diff --git a/src/mvLayoutWindow.cpp b/src/mvLayoutWindow.cpp index 54bb78eb4..d528542a5 100644 --- a/src/mvLayoutWindow.cpp +++ b/src/mvLayoutWindow.cpp @@ -210,7 +210,7 @@ void mvLayoutWindow::drawWidgets() DebugItem("Enabled:", _itemref->config.enabled ? ts : fs); DebugItem("Tracked:", _itemref->config.tracked ? ts : fs); DebugItem("Callback:", _itemref->config.callback ? ts : fs); - DebugItem("User Data:", _itemref->config.user_data ? ts : fs); + DebugItem("User Data:", *(_itemref->config.user_data) ? ts : fs); DebugItem("Drop Callback:", _itemref->config.dropCallback ? ts : fs); DebugItem("Drag Callback:", _itemref->config.dragCallback ? ts : fs); diff --git a/src/mvNodes.cpp b/src/mvNodes.cpp index d3d7d6124..71635ddfd 100644 --- a/src/mvNodes.cpp +++ b/src/mvNodes.cpp @@ -52,13 +52,7 @@ void mvNodeEditor::handleSpecificKeywordArgs(PyObject* dict) if (PyObject* item = PyDict_GetItemString(dict, "delink_callback")) { - - if (_delinkCallback) - Py_XDECREF(_delinkCallback); - item = SanitizeCallback(item); - if (item) - Py_XINCREF(item); - _delinkCallback = item; + _delinkCallback = mvPyObject(item == Py_None? nullptr : item, true); } // helper for bit flipping @@ -79,13 +73,7 @@ void mvNodeEditor::getSpecificConfiguration(PyObject* dict) if (dict == nullptr) return; - if (_delinkCallback) - { - Py_XINCREF(_delinkCallback); - PyDict_SetItemString(dict, "delink_callback", _delinkCallback); - } - else - PyDict_SetItemString(dict, "delink_callback", GetPyNone()); + PyDict_SetItemString(dict, "delink_callback", _delinkCallback? (PyObject*)_delinkCallback : Py_None); // helper to check and set bit auto checkbitset = [dict](const char* keyword, int flag, const int& flags) @@ -333,20 +321,12 @@ void mvNodeEditor::draw(ImDrawList* drawlist, float x, float y) if (config.callback) { - if (config.alias.empty()) - mvSubmitCallback([=]() { - PyObject* link = PyTuple_New(2); - PyTuple_SetItem(link, 0, ToPyUUID(node1)); - PyTuple_SetItem(link, 1, ToPyUUID(node2)); - mvAddCallback(config.callback, uuid, link, config.user_data); - }); - else - mvSubmitCallback([=]() { + submitCallbackEx([=]() { PyObject* link = PyTuple_New(2); PyTuple_SetItem(link, 0, ToPyUUID(node1)); PyTuple_SetItem(link, 1, ToPyUUID(node2)); - mvAddCallback(config.callback, config.alias, link, config.user_data); - }); + return link; + }); } } @@ -367,16 +347,7 @@ void mvNodeEditor::draw(ImDrawList* drawlist, float x, float y) } if (_delinkCallback) { - if (config.alias.empty()) - mvSubmitCallback([=]() { - PyObject* link = ToPyUUID(name); - mvAddCallback(_delinkCallback, uuid, link, config.user_data); - }); - else - mvSubmitCallback([=]() { - PyObject* link = ToPyUUID(name); - mvAddCallback(_delinkCallback, config.alias, link, config.user_data); - }); + submitCallbackEx(_delinkCallback, [=]() { return ToPyUUID(name); }); } } diff --git a/src/mvNodes.h b/src/mvNodes.h index be222911f..f60c9c28b 100644 --- a/src/mvNodes.h +++ b/src/mvNodes.h @@ -106,7 +106,7 @@ class mvNodeEditor : public mvAppItem bool _clearNodes = false; bool _clearLinks = false; - PyObject* _delinkCallback = nullptr; + mvPyObject _delinkCallback = nullptr; ImNodesEditorContext* _context = nullptr; bool _minimap = false; diff --git a/src/mvPlotting.cpp b/src/mvPlotting.cpp index 2dae5b92c..6eb507e07 100644 --- a/src/mvPlotting.cpp +++ b/src/mvPlotting.cpp @@ -580,27 +580,16 @@ DearPyGui::draw_plot(ImDrawList* drawlist, mvAppItem& item, mvPlotConfig& config if (item.config.callback != nullptr && query_dirty) { - if (item.config.alias.empty()) { - mvSubmitCallback([=, &item]() { - PyObject* result = PyTuple_New(config.rects.size()); - for (int i = 0; i < config.rects.size(); ++i) { - auto rectMin = config.rects[i].Min(); - auto rectMax = config.rects[i].Max(); - PyTuple_SetItem(result, i, Py_BuildValue("(dddd)", rectMin.x, rectMin.y, rectMax.x, rectMax.y)); - } - mvAddCallback(item.config.callback, item.uuid, result, item.config.user_data); - }); - } else { - mvSubmitCallback([=, &item]() { - PyObject* result = PyTuple_New(config.rects.size()); - for (int i = 0; i < config.rects.size(); ++i) { - auto rectMin = config.rects[i].Min(); - auto rectMax = config.rects[i].Max(); - PyTuple_SetItem(result, i, Py_BuildValue("(dddd)", rectMin.x, rectMin.y, rectMax.x, rectMax.y)); - } - mvAddCallback(item.config.callback, item.config.alias, result, item.config.user_data); - }); - } + item.submitCallbackEx([rects=config.rects]() { + PyObject* area = PyTuple_New(rects.size()); + for (int i = 0; i < rects.size(); i++) + { + auto rectMin = rects[i].Min(); + auto rectMax = rects[i].Max(); + PyTuple_SetItem(area, i, Py_BuildValue("(dddd)", rectMin.x, rectMin.y, rectMax.x, rectMax.y)); + } + return area; + }); } if (ImPlot::IsPlotHovered()) @@ -615,15 +604,7 @@ DearPyGui::draw_plot(ImDrawList* drawlist, mvAppItem& item, mvPlotConfig& config ScopedID id(item.uuid); if (ImPlot::BeginDragDropTargetPlot()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(item.config.payloadType.c_str())) - { - auto payloadActual = static_cast(payload->Data); - if (item.config.alias.empty()) - mvAddCallback(item.config.dropCallback, item.uuid, payloadActual->configData.dragData, nullptr); - else - mvAddCallback(item.config.dropCallback, item.config.alias, payloadActual->configData.dragData, nullptr); - } - + check_drop_event(&item); ImPlot::EndDragDropTarget(); } } @@ -730,12 +711,7 @@ DearPyGui::draw_plot_axis(ImDrawList* drawlist, mvAppItem& item, mvPlotAxisConfi ScopedID id(item.uuid); if (ImPlot::BeginDragDropTargetAxis(config.axis)) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(item.config.payloadType.c_str())) - { - auto payloadActual = static_cast(payload->Data); - mvAddCallback(item.config.dropCallback, item.uuid, payloadActual->configData.dragData, nullptr); - } - + check_drop_event(&item); ImPlot::EndDragDropTarget(); } } @@ -786,12 +762,7 @@ DearPyGui::draw_plot_legend(ImDrawList* drawlist, mvAppItem& item, mvPlotLegendC { if (ImPlot::BeginDragDropTargetLegend()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(item.config.payloadType.c_str())) - { - auto payloadActual = static_cast(payload->Data); - mvAddCallback(item.config.dropCallback, item.uuid, payloadActual->configData.dragData, nullptr); - } - + check_drop_event(&item); ImPlot::EndDragDropTarget(); } } @@ -812,7 +783,7 @@ DearPyGui::draw_drag_line(ImDrawList* drawlist, mvAppItem& item, mvDragLineConfi { if (ImPlot::DragLineX(item.uuid, config.value.get(), config.color, config.thickness, config.flags, nullptr, &hovered, &held)) { - mvAddCallback(item.config.callback, item.uuid, nullptr, item.config.user_data); + item.submitCallback(); } if (config.show_label && !item.config.specifiedLabel.empty() && (hovered || held)) { char buff[IMPLOT_LABEL_MAX_SIZE]; @@ -828,7 +799,7 @@ DearPyGui::draw_drag_line(ImDrawList* drawlist, mvAppItem& item, mvDragLineConfi { if (ImPlot::DragLineY(item.uuid, config.value.get(), config.color, config.thickness, config.flags, nullptr, &hovered, &held)) { - mvAddCallback(item.config.callback, item.uuid, nullptr, item.config.user_data); + item.submitCallback(); } if (config.show_label && !item.config.specifiedLabel.empty() && (hovered || held)) { char buff[IMPLOT_LABEL_MAX_SIZE]; @@ -893,7 +864,7 @@ DearPyGui::draw_drag_rect(ImDrawList* drawlist, mvAppItem& item, mvDragRectConfi (*config.value.get())[1] = ymin; (*config.value.get())[2] = xmax; (*config.value.get())[3] = ymax; - mvAddCallback(item.config.callback, item.uuid, nullptr, item.config.user_data); + item.submitCallback(); } } @@ -916,7 +887,7 @@ DearPyGui::draw_drag_point(ImDrawList* drawlist, mvAppItem& item, mvDragPointCon { (*config.value.get())[0] = dummyx; (*config.value.get())[1] = dummyy; - mvAddCallback(item.config.callback, item.uuid, nullptr, item.config.user_data); + item.submitCallback(); } if (config.show_label && !item.config.specifiedLabel.empty() && (hovered || held)) { ImPlotContext& gp = *GImPlot; @@ -2319,19 +2290,20 @@ DearPyGui::draw_custom_series(ImDrawList* drawlist, mvAppItem& item, mvCustomSer } ImPlotPoint mouse = ImPlot::GetPlotMousePos(); ImVec2 mouse2 = ImPlot::PlotToPixels(mouse.x, mouse.y); - static int extras = 4; - mvSubmitCallback([&, mouse, mouse2]() { + + item.submitCallbackEx([=, channelCount=config.channelCount, transformedValues=config._transformedValues] () { + const int extras = 4; PyObject* helperData = PyDict_New(); PyDict_SetItemString(helperData, "MouseX_PlotSpace", ToPyFloat(mouse.x)); PyDict_SetItemString(helperData, "MouseY_PlotSpace", ToPyFloat(mouse.y)); PyDict_SetItemString(helperData, "MouseX_PixelSpace", ToPyFloat(mouse2.x)); PyDict_SetItemString(helperData, "MouseY_PixelSpace", ToPyFloat(mouse2.y)); - PyObject* appData = PyTuple_New(config.channelCount + extras); + PyObject* appData = PyTuple_New(channelCount + extras); PyTuple_SetItem(appData, 0, helperData); - for (int i = 1; i < config.channelCount + 1; i++) - PyTuple_SetItem(appData, i, ToPyList(config._transformedValues[i-1])); - mvAddCallback(item.config.callback, item.uuid, appData, item.config.user_data); - }); + for (int i = 0; i < channelCount; i++) + PyTuple_SetItem(appData, i + 1, ToPyList(transformedValues[i])); + return appData; + }); // drawings ImPlotPlot* currentPlot = ImPlot::GetCurrentContext()->CurrentPlot; diff --git a/src/mvPyUtils.cpp b/src/mvPyUtils.cpp index 55edad283..2ddd70a45 100644 --- a/src/mvPyUtils.cpp +++ b/src/mvPyUtils.cpp @@ -21,36 +21,29 @@ mvGlobalIntepreterLock::~mvGlobalIntepreterLock() } -mvPyObject::mvPyObject(PyObject* rawObject, bool borrowed) +mvPyObject::mvPyObject(PyObject* rawObject, bool borrowed) : - m_rawObject(rawObject), - m_borrowed(borrowed), - m_ok(rawObject != nullptr) + m_rawObject(rawObject) { - + if (borrowed) + Py_XINCREF(rawObject); } mvPyObject::mvPyObject(mvPyObject&& other) : - m_rawObject(nullptr), - m_borrowed(false), - m_ok(false) + m_rawObject(nullptr) { std::swap(m_rawObject, other.m_rawObject); - std::swap(m_borrowed, other.m_borrowed); - std::swap(m_ok, other.m_ok); } mvPyObject& mvPyObject::operator=(mvPyObject&& other) { if (this != &other) { - if (m_rawObject != nullptr && !m_borrowed) - Py_XDECREF(m_rawObject); + Py_XDECREF(m_rawObject); + m_rawObject = nullptr; std::swap(other.m_rawObject, m_rawObject); - std::swap(other.m_borrowed, m_borrowed); - std::swap(other.m_ok, m_ok); } return *this; @@ -58,24 +51,7 @@ mvPyObject& mvPyObject::operator=(mvPyObject&& other) mvPyObject::~mvPyObject() { - if(!m_borrowed && !m_del) - Py_XDECREF(m_rawObject); -} - -mvPyObject::operator PyObject*() -{ - return m_rawObject; -} - -void mvPyObject::addRef() -{ - Py_XINCREF(m_rawObject); -} - -void mvPyObject::delRef() -{ - Py_XDECREF(m_rawObject); - m_del = true; + Py_XDECREF(m_rawObject); } void @@ -518,6 +494,14 @@ ToPyUUID(mvAppItem* item) return Py_BuildValue("K", item->uuid); } +PyObject* +ToPyUUID(mvUUID uuid, const std::string& alias) +{ + if (alias.empty()) + return Py_BuildValue("K", uuid); + return ToPyString(alias); +} + PyObject* ToPyUUID(mvUUID value) { diff --git a/src/mvPyUtils.h b/src/mvPyUtils.h index 50cee9927..5be0f6f47 100644 --- a/src/mvPyUtils.h +++ b/src/mvPyUtils.h @@ -30,7 +30,12 @@ class mvPyObject public: - mvPyObject(PyObject* rawObject, bool borrowed=false); + // With `borrowed=false`, the reference is meant for `mvPyObject` to take + // ownership of it. `mvPyObject` "steals" the reference from its previous owner; + // the reference count is not incremented. + // With `borrowed=true`, `mvPyObject` makes its own owned reference by incrementing + // the reference count; the original owner keeps ownership too. + mvPyObject(PyObject* rawObject, bool borrowed = false); mvPyObject(mvPyObject&& other); mvPyObject& operator=(mvPyObject&& other); @@ -39,18 +44,16 @@ class mvPyObject ~mvPyObject(); - void addRef(); - void delRef(); - bool isOk() const { return m_ok; } + bool isOk() const { return (m_rawObject != nullptr); } - operator PyObject* (); + operator PyObject* () const + { + return m_rawObject; + } private: PyObject* m_rawObject; - bool m_borrowed; - bool m_ok; - bool m_del = false; }; @@ -92,6 +95,7 @@ bool isPyObject_Any (PyObject* obj); PyObject* GetPyNone (); PyObject* ToPyUUID (mvAppItem* item); PyObject* ToPyUUID (mvUUID value); +PyObject* ToPyUUID (mvUUID uuid, const std::string& alias); PyObject* ToPyLong (long value); PyObject* ToPyInt (int value); PyObject* ToPyFloat (float value); diff --git a/src/mvSlider3D.cpp b/src/mvSlider3D.cpp index 19eed30d0..b06061610 100644 --- a/src/mvSlider3D.cpp +++ b/src/mvSlider3D.cpp @@ -397,14 +397,7 @@ void mvSlider3D::draw(ImDrawList* drawlist, float x, float y) if(SliderScalar3D(config.specifiedLabel.c_str(), &(*_value)[0], &(*_value)[1], &(*_value)[2], _minX, _maxX, _minY, _maxY, _minZ, _maxZ, _scale)) { - auto value = *_value; - mvSubmitCallback([=]() { - - if(config.alias.empty()) - mvAddCallback(getCallback(false), uuid, ToPyFloatList(value.data(), (int)value.size()), config.user_data); - else - mvAddCallback(getCallback(false), config.alias, ToPyFloatList(value.data(), (int)value.size()), config.user_data); - }); + submitCallback(*_value); } } diff --git a/src/mvTables.cpp b/src/mvTables.cpp index ab2aa4cb6..b02b8ec20 100644 --- a/src/mvTables.cpp +++ b/src/mvTables.cpp @@ -279,7 +279,7 @@ void mvTable::draw(ImDrawList* drawlist, float x, float y) if (sorts_specs->SpecsDirty) { if (sorts_specs->SpecsCount == 0) - mvAddCallback(getCallback(false), uuid, GetPyNone(), config.user_data); + submitCallback(); else { @@ -295,22 +295,17 @@ void mvTable::draw(ImDrawList* drawlist, float x, float y) specs.push_back({ idMap[sort_spec->ColumnUserID], sort_spec->SortDirection == ImGuiSortDirection_Ascending ? 1 : -1 }); } - mvSubmitCallback([=]() { + submitCallbackEx([specs=std::move(specs)] () { PyObject* pySpec = PyList_New(specs.size()); for (size_t i = 0; i < specs.size(); i++) { PyObject* pySingleSpec = PyList_New(2); - PyList_SetItem(pySingleSpec, 0, ToPyLong(specs[i].column)); + PyList_SetItem(pySingleSpec, 0, Py_BuildValue("K", specs[i].column)); PyList_SetItem(pySingleSpec, 1, ToPyInt(specs[i].direction)); PyList_SetItem(pySpec, i, pySingleSpec); } - - if (config.alias.empty()) - mvRunCallback(getCallback(false), uuid, pySpec, config.user_data); - else - mvRunCallback(getCallback(false), config.alias, pySpec, config.user_data); - Py_XDECREF(pySpec); - }); + return pySpec; + }); } sorts_specs->SpecsDirty = false; } diff --git a/src/mvTextureItems.cpp b/src/mvTextureItems.cpp index b52a38cff..3b7c33b73 100644 --- a/src/mvTextureItems.cpp +++ b/src/mvTextureItems.cpp @@ -205,19 +205,13 @@ void mvRawTexture::setPyValue(PyObject* value) } } PyBuffer_Release(&buffer_info); - if (_buffer) - Py_XDECREF(_buffer); - Py_XINCREF(value); - _buffer = value; + _buffer = mvPyObject(value, true); } } mvRawTexture::~mvRawTexture() { FreeTexture(_texture); - - mvGlobalIntepreterLock gil; - Py_XDECREF(_buffer); } void mvRawTexture::draw(ImDrawList* drawlist, float x, float y) diff --git a/src/mvTextureItems.h b/src/mvTextureItems.h index e3157c04e..f86093e1e 100644 --- a/src/mvTextureItems.h +++ b/src/mvTextureItems.h @@ -71,7 +71,7 @@ class mvRawTexture : public mvAppItem public: - PyObject* _buffer = nullptr; + mvPyObject _buffer = nullptr; void* _value = nullptr; void* _texture = nullptr; bool _dirty = true; diff --git a/src/mvTimePicker.cpp b/src/mvTimePicker.cpp index be8959a22..8bfeb0c2e 100644 --- a/src/mvTimePicker.cpp +++ b/src/mvTimePicker.cpp @@ -69,13 +69,7 @@ void mvTimePicker::draw(ImDrawList* drawlist, float x, float y) { ImPlot::GetGmtTime(*_imvalue, _value.get()); { - auto value = *_value; - mvSubmitCallback([=]() { - if(config.alias.empty()) - mvAddCallback(getCallback(false), uuid, ToPyTime(value), config.user_data); - else - mvAddCallback(getCallback(false), config.alias, ToPyTime(value), config.user_data); - }); + submitCallback(*_value); } } } diff --git a/src/mvViewport.h b/src/mvViewport.h index 52e194b51..b61b484ba 100644 --- a/src/mvViewport.h +++ b/src/mvViewport.h @@ -65,13 +65,24 @@ void mvToggleFullScreen(mvViewport& viewport); static void mvOnResize() { - mvSubmitCallback([=]() { - PyObject* dimensions = PyTuple_New(4); - PyTuple_SetItem(dimensions, 0, PyLong_FromLong(GContext->viewport->actualWidth)); - PyTuple_SetItem(dimensions, 1, PyLong_FromLong(GContext->viewport->actualHeight)); - PyTuple_SetItem(dimensions, 2, PyLong_FromLong(GContext->viewport->clientWidth)); - PyTuple_SetItem(dimensions, 3, PyLong_FromLong(GContext->viewport->clientHeight)); - mvAddCallback( - GContext->callbackRegistry->resizeCallback, MV_APP_UUID, dimensions, GContext->callbackRegistry->resizeCallbackUserData); - }); + auto v = GContext->viewport; + mvAddOwnerlessCallback( + GContext->callbackRegistry->resizeCallback, + GContext->callbackRegistry->resizeCallbackUserData, + MV_APP_UUID, "", + [ + actualWidth = v->actualWidth, + actualHeight = v->actualHeight, + clientWidth = v->clientWidth, + clientHeight = v->clientHeight + ] + () { + PyObject* dimensions = PyTuple_New(4); + PyTuple_SetItem(dimensions, 0, PyLong_FromLong(actualWidth)); + PyTuple_SetItem(dimensions, 1, PyLong_FromLong(actualHeight)); + PyTuple_SetItem(dimensions, 2, PyLong_FromLong(clientWidth)); + PyTuple_SetItem(dimensions, 3, PyLong_FromLong(clientHeight)); + return dimensions; + } + ); } \ No newline at end of file diff --git a/src/mvViewport_apple.mm b/src/mvViewport_apple.mm index e6e41cf2d..819cac2d9 100644 --- a/src/mvViewport_apple.mm +++ b/src/mvViewport_apple.mm @@ -22,12 +22,10 @@ window_close_callback(GLFWwindow* window) { if (GContext->viewport->disableClose) { - mvSubmitCallback([=]() { - mvRunCallback(GContext->callbackRegistry->onCloseCallback, 0, nullptr, GContext->callbackRegistry->onCloseCallbackUserData); - }); + mvAddOwnerlessCallback(GContext->callbackRegistry->onCloseCallback, GContext->callbackRegistry->onCloseCallbackUserData); } else { - GContext->started = false; + StopRendering(); } } diff --git a/src/mvViewport_linux.cpp b/src/mvViewport_linux.cpp index b8b6496e3..31f7b7f87 100644 --- a/src/mvViewport_linux.cpp +++ b/src/mvViewport_linux.cpp @@ -21,12 +21,10 @@ static void window_close_callback(GLFWwindow* window) { if (GContext->viewport->disableClose) { - mvSubmitCallback([=]() { - mvRunCallback(GContext->callbackRegistry->onCloseCallback, 0, nullptr, GContext->callbackRegistry->onCloseCallbackUserData); - }); + mvAddOwnerlessCallback(GContext->callbackRegistry->onCloseCallback, GContext->callbackRegistry->onCloseCallbackUserData); } else { - GContext->started = false; + StopRendering(); } } @@ -125,7 +123,7 @@ mvCleanupViewport(mvViewport& viewport) glfwDestroyWindow(viewportData->handle); glfwTerminate(); - GContext->started = false; + StopRendering(); delete viewportData; viewportData = nullptr; diff --git a/src/mvViewport_win32.cpp b/src/mvViewport_win32.cpp index 0e5a29af8..a8e13a00e 100644 --- a/src/mvViewport_win32.cpp +++ b/src/mvViewport_win32.cpp @@ -276,12 +276,10 @@ mvHandleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept break; case WM_CLOSE: if (GContext->viewport->disableClose) { - mvSubmitCallback([=]() { - mvRunCallback(GContext->callbackRegistry->onCloseCallback, 0, nullptr, GContext->callbackRegistry->onCloseCallbackUserData); - }); + mvAddOwnerlessCallback(GContext->callbackRegistry->onCloseCallback, GContext->callbackRegistry->onCloseCallbackUserData); return 0; } - GContext->started = false; + StopRendering(); DestroyWindow(hWnd); ::PostQuitMessage(0); return 0;