diff --git a/src/runner/gamefeatureswrappers.cpp b/src/runner/gamefeatureswrappers.cpp index 5642e56..2d24352 100644 --- a/src/runner/gamefeatureswrappers.cpp +++ b/src/runner/gamefeatureswrappers.cpp @@ -277,7 +277,7 @@ void registerGameFeaturesPythonConverters() bpy::class_("SaveGameInfo") .def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy()) .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets)) - .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy()) + .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy(), "[optional]") .def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave)) ; diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 0d9b6cc..60297b4 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -725,6 +725,7 @@ BOOST_PYTHON_MODULE(mobase) .def("displayString", &VersionInfo::displayString) .def("isValid", &VersionInfo::isValid) .def("scheme", &VersionInfo::scheme) + .def("__str__", &VersionInfo::canonicalString) .def(bpy::self < bpy::self) .def(bpy::self > bpy::self) .def(bpy::self <= bpy::self) @@ -830,7 +831,7 @@ BOOST_PYTHON_MODULE(mobase) .def("name", &FileTreeEntry::name) .def("suffix", &FileTreeEntry::suffix) .def("time", &FileTreeEntry::time) - .def("parent", static_cast(FileTreeEntry::*)()>(&FileTreeEntry::parent)) + .def("parent", static_cast(FileTreeEntry::*)()>(&FileTreeEntry::parent), "[optional]") .def("path", &FileTreeEntry::path, bpy::arg("sep") = "\\") .def("pathFrom", &FileTreeEntry::pathFrom, bpy::arg("sep") = "\\") @@ -853,7 +854,7 @@ BOOST_PYTHON_MODULE(mobase) } // IFileTree scope: - auto iFileTreeClass = bpy::class_, boost::noncopyable>("IFileTree", bpy::no_init);; + auto iFileTreeClass = bpy::class_, boost::noncopyable>("IFileTree", bpy::no_init); { bpy::scope scope = iFileTreeClass; @@ -871,15 +872,15 @@ BOOST_PYTHON_MODULE(mobase) // special python methods): .def("exists", static_cast(&IFileTree::exists), (bpy::arg("type") = IFileTree::FILE_OR_DIRECTORY)) .def("find", static_cast(IFileTree::*)(QString, IFileTree::FileTypes)>(&IFileTree::find), - bpy::arg("type") = IFileTree::FILE_OR_DIRECTORY, bpy::return_value_policy>()) + bpy::arg("type") = IFileTree::FILE_OR_DIRECTORY, bpy::return_value_policy>(), "[optional]") .def("pathTo", &IFileTree::pathTo, bpy::arg("sep") = "\\") // Kind-of-static operations: .def("createOrphanTree", &IFileTree::createOrphanTree, bpy::arg("name") = "") // Mutable operations: - .def("addFile", &IFileTree::addFile, bpy::arg("time") = QDateTime()) - .def("addDirectory", &IFileTree::addDirectory) + .def("addFile", &IFileTree::addFile, bpy::arg("time") = QDateTime(), "[optional]") + .def("addDirectory", &IFileTree::addDirectory, "[optional]") .def("insert", +[]( IFileTree* p, std::shared_ptr entry, IFileTree::InsertPolicy insertPolicy) { return p->insert(entry, insertPolicy) != p->end(); }, bpy::arg("policy") = IFileTree::InsertPolicy::FAIL_IF_EXISTS) @@ -950,7 +951,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_("ModRepositoryFileInfo") .def(bpy::init()) .def(bpy::init>()) - .def("toString", &ModRepositoryFileInfo::toString) + .def("__str__", &ModRepositoryFileInfo::toString) .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") .def_readwrite("name", &ModRepositoryFileInfo::name) .def_readwrite("uri", &ModRepositoryFileInfo::uri) @@ -1092,9 +1093,17 @@ BOOST_PYTHON_MODULE(mobase) .def("onModMoved", bpy::pure_virtual(&MOBase::IModList::onModMoved)) ; - bpy::class_("IPlugin"); + bpy::class_("IPlugin") + .def("init", bpy::pure_virtual(&MOBase::IPlugin::init)) + .def("name", bpy::pure_virtual(&MOBase::IPlugin::name)) + .def("author", bpy::pure_virtual(&MOBase::IPlugin::author)) + .def("description", bpy::pure_virtual(&MOBase::IPlugin::description)) + .def("version", bpy::pure_virtual(&MOBase::IPlugin::version)) + .def("isActive", bpy::pure_virtual(&MOBase::IPlugin::isActive)) + .def("settings", bpy::pure_virtual(&MOBase::IPlugin::settings)) + ; - bpy::class_("IPluginDiagnose") + bpy::class_, boost::noncopyable>("IPluginDiagnose") .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription)) .def("fullDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::fullDescription)) @@ -1110,7 +1119,7 @@ BOOST_PYTHON_MODULE(mobase) .def_readwrite("createTarget", &Mapping::createTarget) ; - bpy::class_("IPluginFileMapper") + bpy::class_, boost::noncopyable>("IPluginFileMapper") .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) ; @@ -1137,7 +1146,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter>(); QFlags_from_python_obj(); - bpy::class_("IPluginGame") + bpy::class_, boost::noncopyable>("IPluginGame") .def("gameName", bpy::pure_virtual(&MOBase::IPluginGame::gameName)) .def("initializeProfile", bpy::pure_virtual(&MOBase::IPluginGame::initializeProfile)) .def("savegameExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameExtension)) @@ -1170,23 +1179,36 @@ BOOST_PYTHON_MODULE(mobase) .def("gameVersion", bpy::pure_virtual(&MOBase::IPluginGame::gameVersion)) .def("getLauncherName", bpy::pure_virtual(&MOBase::IPluginGame::getLauncherName)) - //Plugin interface. - .def("init", bpy::pure_virtual(&MOBase::IPluginGame::init)) - .def("name", bpy::pure_virtual(&MOBase::IPluginGame::name)) - .def("author", bpy::pure_virtual(&MOBase::IPluginGame::author)) - .def("description", bpy::pure_virtual(&MOBase::IPluginGame::description)) - .def("version", bpy::pure_virtual(&MOBase::IPluginGame::version)) - .def("isActive", bpy::pure_virtual(&MOBase::IPluginGame::isActive)) - .def("settings", bpy::pure_virtual(&MOBase::IPluginGame::settings)) + .def("featureList", +[](MOBase::IPluginGame* p) { + // Constructing a dict from class name to actual object: + bpy::dict dict; + mp11::mp_for_each< + mp11::mp_transform< + // Must user pointers because mp_for_each construct object: + std::add_pointer_t, + mp11::mp_list< + BSAInvalidation, + DataArchives, + GamePlugins, + LocalSavegames, + SaveGameInfo, + ScriptExtender, + UnmanagedMods + > + > + >([&](auto* pt) { + using T = std::remove_pointer_t; + typename bpy::reference_existing_object::apply::type converter; - // The syntax has to differ slightly from C++ because these are templated - .def("featureBSAInvalidation", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureDataArchives", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureGamePlugins", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureLocalSavegames", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureSaveGameInfo", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureScriptExtender", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureUnmanagedMods", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + // Retrieve the python class object: + const bpy::converter::registration* registration = bpy::converter::registry::query(bpy::type_id()); + bpy::object key = bpy::object(bpy::handle<>(bpy::borrowed(registration->get_class_object()))); + + // Set the object: + dict[key] = bpy::handle<>(converter(p->feature())); + }); + return dict; + }) ; bpy::enum_("InstallResult") @@ -1197,8 +1219,16 @@ BOOST_PYTHON_MODULE(mobase) .value("NOT_ATTEMPTED", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) ; - bpy::class_("IPluginInstallerSimple") - // Note: Keeping the variant here if we always return a tuple to be consistent with the wrapper and + bpy::class_, boost::noncopyable>("IPluginInstaller", bpy::no_init) + .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported) + .def("priority", &IPluginInstaller::priority) + .def("isManualInstaller", &IPluginInstaller::isManualInstaller) + .def("setParentWidget", &IPluginInstaller::setParentWidget) + .def("setInstallationManager", &IPluginInstaller::setInstallationManager) + ; + + bpy::class_, boost::noncopyable>("IPluginInstallerSimple") + // Note: Keeping the variant here even if we always return a tuple to be consistent with the wrapper and // have proper stubs generation. .def("install", +[](IPluginInstallerSimple* p, GuessedValue& modName, std::shared_ptr& tree, QString& version, int& nexusID) -> std::variant, std::tuple, QString, int>> { @@ -1209,19 +1239,21 @@ BOOST_PYTHON_MODULE(mobase) .def("manager", &IPluginInstallerSimpleWrapper::manager, bpy::return_value_policy()) ; - bpy::class_("IPluginInstallerCustom") - .def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported) - .def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions) - .def("install", &IPluginInstallerCustom::install) - .def("parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, bpy::return_value_policy()) - .def("manager", &IPluginInstallerCustomWrapper::manager, bpy::return_value_policy()) - ; + bpy::class_, boost::noncopyable>("IPluginInstallerCustom") + // Needs to add both otherwize boost does not understanda: + .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported) + .def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported) + .def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions) + .def("install", &IPluginInstallerCustom::install) + .def("parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, bpy::return_value_policy()) + .def("manager", &IPluginInstallerCustomWrapper::manager, bpy::return_value_policy()) + ; - bpy::class_("IPluginModPage") + bpy::class_, boost::noncopyable>("IPluginModPage") .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginModPage::setParentWidget)) ; - bpy::class_("IPluginPreview") + bpy::class_, boost::noncopyable>("IPluginPreview") ; bpy::class_, boost::noncopyable>("IPluginTool")