diff --git a/src/runner/CMakeLists.txt b/src/runner/CMakeLists.txt index 6515a8e..b64502e 100644 --- a/src/runner/CMakeLists.txt +++ b/src/runner/CMakeLists.txt @@ -35,6 +35,7 @@ add_filter(NAME src/converters GROUPS add_filter(NAME src/runner GROUPS pythonrunner pylogger + widgets ) add_filter(NAME src/utils GROUPS diff --git a/src/runner/converters.h b/src/runner/converters.h index 2d90e52..3700503 100644 --- a/src/runner/converters.h +++ b/src/runner/converters.h @@ -66,6 +66,37 @@ namespace utils { } + namespace Enum_converter { + + /** + * + */ + template + struct Enum_to_int + { + static PyObject* convert(const Enum& flags) { + return bpy::incref(bpy::object(static_cast(flags)).ptr()); + } + }; + + template + struct Enum_from_python_obj + { + + static void* convertible(PyObject* objPtr) { + return SIPLong_Check(objPtr) ? objPtr : nullptr; + } + + static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { + int intVersion = (int)SIPLong_AsLong(objPtr); + void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; + new (storage) Enum(static_cast(intVersion)); + data->convertible = storage; + } + }; + + } + namespace QFlags_converter { /** @@ -508,6 +539,16 @@ namespace utils { bpy::type_id()); } + template + inline void register_enum_converter() { + using namespace Enum_converter; + bpy::to_python_converter>(); + bpy::converter::registry::push_back( + &Enum_from_python_obj::convertible, + &Enum_from_python_obj::construct, + bpy::type_id()); + } + template inline void register_qclass_converter() { using Converter = QClass_converter::QClass_converters; diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 562940f..7afc359 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "uibasewrappers.h" #include "proxypluginwrappers.h" @@ -41,6 +42,7 @@ #include "converters.h" #include "shared_ptr_converter.h" #include "pylogger.h" +#include "widgets.h" using namespace MOBase; @@ -86,6 +88,10 @@ BOOST_PYTHON_MODULE(mobase) utils::register_qflags_converter(); utils::register_qflags_converter(); + // Enums: + utils::register_enum_converter(); + utils::register_enum_converter(); + // Pointers: bpy::register_ptr_to_python>(); bpy::register_ptr_to_python>(); @@ -116,6 +122,7 @@ BOOST_PYTHON_MODULE(mobase) utils::register_sequence_container>>(); utils::register_sequence_container>(); utils::register_sequence_container>(); + utils::register_sequence_container>(); utils::register_set_container>(); @@ -132,6 +139,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::register_tuple>(); // IOrganizer::waitForApplication bpy::register_tuple>(); // IProfile::invalidationActive bpy::register_tuple, QString, int>>(); + bpy::register_tuple>(); // Variants: bpy::register_variant, QString, int>>>(); bpy::register_variant>(); bpy::register_variant>(); + bpy::register_variant>>(); // Functions: utils::register_functor_converter(); // converter for the onRefreshed-callback @@ -1088,6 +1097,13 @@ BOOST_PYTHON_MODULE(mobase) bpy::def("getProductVersion", &MOBase::getProductVersion, bpy::arg("executable")); bpy::def("getIconForExecutable", &MOBase::iconForExecutable, bpy::arg("executable")); + bpy::object widgets(bpy::borrowed(PyImport_AddModule("mobase.widgets"))); + bpy::scope().attr("widgets") = widgets; + { + bpy::scope w_ = widgets; + register_widgets(); + } + // Expose MoVariant: MoVariant is a fake object whose only purpose is to be used as a type-hint // on the python side (e.g., def foo(x: mobase.MoVariant)). The real MoVariant is defined in the // generated stubs, since it's only relevant when doing type-checking, but this needs to be defined, diff --git a/src/runner/widgets.cpp b/src/runner/widgets.cpp new file mode 100644 index 0000000..ba8717a --- /dev/null +++ b/src/runner/widgets.cpp @@ -0,0 +1,66 @@ +#include "widgets.h" + +#include + +#include + +#include "report.h" +#include "variant_helper.h" + +namespace bpy = boost::python; + +using namespace MOBase; + +void register_widgets() { + + // TaskDialog is also in Windows System. + using TaskDialog = MOBase::TaskDialog; + + // TaskDialog + bpy::class_("TaskDialogButton", bpy::no_init) + .def(bpy::init((bpy::arg("text"), bpy::arg("description"), bpy::arg("button")))) + .def(bpy::init((bpy::arg("text"), bpy::arg("button")))); + + bpy::class_("TaskDialog", bpy::no_init) + .def("__init__", bpy::make_constructor(+[]( + QWidget* parent, QString const& title, QString const& main, + QString const& content, QString const& details, QMessageBox::Icon icon, + std::vector const& buttons, + std::variant> const& remember) { + auto* dialog = new TaskDialog(parent, title); + dialog->main(main).content(content).details(details).icon(icon); + + for (auto& button : buttons) { + dialog->button(button); + } + + std::visit([dialog](auto const& item) { + QString action, file; + if constexpr (std::is_same_v, QString>) { + action = item; + } + else { + action = std::get<0>(item); + file = std::get<1>(item); + } + dialog->remember(action, file); + }, remember); + + return dialog; + }, bpy::default_call_policies(), + (bpy::arg("parent") = (QWidget*)nullptr, bpy::arg("title") = "", bpy::arg("main") = "", bpy::arg("content") = "", + bpy::arg("details") = "", bpy::arg("icon") = QMessageBox::NoIcon, bpy::arg("buttons") = std::vector{}, + bpy::arg("remember") = ""))) + .def("setTitle", &TaskDialog::title, bpy::return_self<>(), bpy::arg("title")) + .def("setMain", &TaskDialog::main, bpy::return_self<>(), bpy::arg("main")) + .def("setContent", &TaskDialog::content, bpy::return_self<>(), bpy::arg("content")) + .def("setDetails", &TaskDialog::details, bpy::return_self<>(), bpy::arg("details")) + .def("setIcon", &TaskDialog::icon, bpy::return_self<>(), bpy::arg("icon")) + .def("addButton", &TaskDialog::button, bpy::return_self<>(), bpy::arg("button")) + .def("setRemember", &TaskDialog::remember, bpy::return_self<>(), (bpy::arg("action"), bpy::arg("file") = "")) + .def("setWidth", &TaskDialog::setWidth, bpy::arg("widget")) + .def("addContent", &TaskDialog::addContent, bpy::arg("widget")) + .def("exec", &TaskDialog::exec) + ; + +} \ No newline at end of file diff --git a/src/runner/widgets.h b/src/runner/widgets.h new file mode 100644 index 0000000..9227d3d --- /dev/null +++ b/src/runner/widgets.h @@ -0,0 +1,9 @@ +#ifndef PYTHON_WIDGETS_HPP +#define PYTHON_WIDGETS_HPP + +/** + * @brief Register uibase widgets in the current scope. + */ +void register_widgets(); + +#endif \ No newline at end of file