Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6465d3e
Move to VCPKG.
Holt59 Jul 7, 2024
5f7658e
Move to custom semver versioning for MO2. (#154)
Holt59 Aug 5, 2024
d7faaf8
Update Qt version in CI and bump MO2 VCPKG registry baseline.
Holt59 Aug 5, 2024
10f6f71
Allow for external mo2-cmake (#160)
Holt59 Aug 9, 2024
265b189
Start working on extensions.
Holt59 May 14, 2022
d4f9d14
Add theme and translation for extensions.
Holt59 May 23, 2022
57b4f9b
Implement loading theme and translation extensions.
Holt59 May 23, 2022
7bf2d15
Update for extensions.
Holt59 May 24, 2022
d120497
Implement theme and translation additions for plugin extension.
Holt59 May 24, 2022
2a40eb7
Allow specifying plugins in extensions.
Holt59 May 26, 2022
ee70f02
Add extension list interface. Start working on requirements.
Holt59 Jul 16, 2023
1b62541
Add autodetect for translations to match mob/base-translations behavior.
Holt59 Jul 16, 2023
c2e37dc
Allow missing requirements in extensions.
Holt59 Jul 22, 2023
8d69d87
Re-organize extension metadata.
Holt59 Jul 22, 2023
bfa3fdf
Remove irrelevant methods from IPlugin.
Holt59 Jul 22, 2023
ede5d44
Add author, contributors and icon to metadata.
Holt59 Jul 22, 2023
8b07371
Default icon for extension.
Holt59 Jul 31, 2023
7d93e43
Compute language name when missing for translations.
Holt59 Jul 31, 2023
f058a72
Cleaning for C++20/23.
Holt59 Jun 29, 2024
10e9c7f
Add formatter for std::filesystem::path.
Holt59 Jun 29, 2024
39caa95
Move extension-related headers to an extensions/ subfolder.
Holt59 Aug 4, 2024
e4c47ea
Add version constraint system.
Holt59 Aug 8, 2024
0367e32
Implement requirements for extension.
Holt59 Aug 9, 2024
e2216d9
Improve tests for extension metadata.
Holt59 Aug 9, 2024
3f5e093
Fix translations for tests.
Holt59 Aug 9, 2024
5c1d8c5
Start refactoring setting system for plugins/extensions.
Holt59 Aug 10, 2024
3a64b4c
Add possibility to specify homepage for authors/contributors.
Holt59 Aug 10, 2024
2d4ffb4
Add back IPlugin::requirements.
Holt59 Aug 10, 2024
e39a5d0
Merge remote-tracking branch 'origin/master' into dev/extensions
Holt59 May 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/uibase/exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace MOBase
class QDLLEXPORT Exception : public std::exception
{
public:
Exception(const char* text) : m_Message(text) {}
Exception(const std::string& text) : m_Message(QByteArray::fromStdString(text)) {}
Exception(const QString& text) : m_Message(text.toUtf8()) {}

virtual const char* what() const noexcept override { return m_Message.constData(); }
Expand Down
291 changes: 291 additions & 0 deletions include/uibase/extensions/extension.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
#ifndef UIBASE_EXTENSION_H
#define UIBASE_EXTENSION_H

#include <filesystem>
#include <map>
#include <vector>

#include <QJsonObject>
#include <QTranslator>

#include "../dllimport.h"
#include "../iplugingame.h"
#include "../versioning.h"
#include "requirements.h"
#include "theme.h"
#include "translation.h"

namespace MOBase
{
class IExtension;

class InvalidExtensionMetaDataException : public Exception
{
public:
using Exception::Exception;
};

enum class ExtensionType
{
THEME,
TRANSLATION,
PLUGIN,
GAME
};

class QDLLEXPORT ExtensionContributor
{
public:
ExtensionContributor(QString name, QString homepage);

// retrieve the name of the contributor
//
const auto& name() const { return m_Name; }

// retrieve the homagepage of the contributor
//
const auto& homepage() const { return m_Homepage; }

private:
ExtensionContributor() = default;

friend class ExtensionMetaData;

QString m_Name, m_Homepage;
};

class QDLLEXPORT ExtensionMetaData
{
public:
// retrieve the identifier of the extension
//
const auto& identifier() const { return m_Identifier; }

// retrieve the name of the extension
//
auto name() const { return localized(m_Name); }

// retrieve the author of the extension if set
//
const auto& author() const { return m_Author; }

// retrieve the list of contributors of the extension
//
const auto& contributors() const { return m_Contributors; }

// retrieve the type of the extension
//
auto type() const { return m_Type; }

// retrieve the description of the extension
//
auto description() const { return localized(m_Description); }

// retrieve the icon for the extension (might be an empty icon)
//
const auto& icon() const { return m_Icon; }

// retrieve the version of the extension
//
const auto& version() const { return m_Version; }

// retrieve the requirements of the extension
//
const auto& requirements() const { return m_Requirements; }

// retrieve the raw JSON metadata, this is mostly useful for specific extension type
// to extract custom parts
//
const auto& json() const { return m_JsonData; }

// retrieve the content objects of the extension
QJsonObject content() const;

protected:
ExtensionMetaData(std::filesystem::path const& path, const QJsonObject& jsonData);

private:
friend class ExtensionFactory;

constexpr static const char* DEFAULT_TRANSLATIONS_FOLDER = "translations";
constexpr static const char* DEFAULT_STYLESHEET_PATH = "stylesheets";

std::optional<ExtensionType> parseType(QString const& value) const;

private:
QJsonObject m_JsonData;
QString m_TranslationContext;

QString m_Identifier;
QString m_Name;
ExtensionContributor m_Author;
std::vector<ExtensionContributor> m_Contributors;
ExtensionType m_Type;
QString m_Description;
QIcon m_Icon;
Version m_Version;
std::vector<ExtensionRequirement> m_Requirements;

std::filesystem::path m_TranslationFilesPrefix;
std::filesystem::path m_StyleSheetFilePath;

QString localized(QString const& value) const;
};

class QDLLEXPORT IExtension
{
public:
// retrieve the folder containing the extension
//
std::filesystem::path directory() const { return m_Path; }

// retrieve the metadata of this extension
//
const auto& metadata() const { return m_MetaData; }

public:
virtual ~IExtension() {}
IExtension& operator=(const IExtension&) = delete;

protected:
IExtension(std::filesystem::path const& path, ExtensionMetaData&& metadata);

public:
IExtension(const IExtension&) = default;

private:
std::filesystem::path m_Path;
ExtensionMetaData m_MetaData;
};

// factory for extensions
//
class QDLLEXPORT ExtensionFactory
{
public:
// load metadata from the given file, throws InvalidExtensionMetaDataException if the
// file does not exist or is invalid
//
static ExtensionMetaData loadMetaData(std::filesystem::path const& path);

// load an extension from the given directory, return a null-pointer if the extension
// could not be load
//
static std::unique_ptr<IExtension>
loadExtension(std::filesystem::path const& directory);

private:
// load an extension from the given directory
//
static std::unique_ptr<IExtension>
loadExtension(std::filesystem::path const& directory, ExtensionMetaData&& metadata);
};

// theme extension that provides one or more base themes for MO2
//
class QDLLEXPORT ThemeExtension : public IExtension
{
public:
// retrieve the list of themes provided by this extension
//
const auto& themes() const { return m_Themes; }

private:
ThemeExtension(std::filesystem::path const& path, ExtensionMetaData&& metadata,
std::vector<std::shared_ptr<const Theme>> themes);

friend class ExtensionFactory;
static std::unique_ptr<ThemeExtension>
loadExtension(std::filesystem::path const& path, ExtensionMetaData&& metadata);

static std::shared_ptr<const Theme>
parseTheme(std::filesystem::path const& extensionFolder, const QString& identifier,
const QJsonObject& jsonTheme);

private:
std::vector<std::shared_ptr<const Theme>> m_Themes;
};

// translation extension that provides one or more base translations for mo@
//
class QDLLEXPORT TranslationExtension : public IExtension
{
public:
// retrieve the list of translations provided by this extension
//
const auto& translations() const { return m_Translations; }

private:
TranslationExtension(std::filesystem::path const& path, ExtensionMetaData&& metadata,
std::vector<std::shared_ptr<const Translation>> translations);

friend class ExtensionFactory;
static std::unique_ptr<TranslationExtension>
loadExtension(std::filesystem::path const& path, ExtensionMetaData&& metadata);

static std::shared_ptr<const Translation>
parseTranslation(std::filesystem::path const& extensionFolder,
const QString& identifier, const QJsonObject& jsonTranslation);

private:
std::vector<std::shared_ptr<const Translation>> m_Translations;
};

// plugin extension that provides one or more plugins for MO2, alongside theme or
// translation additions
//
class QDLLEXPORT PluginExtension : public IExtension
{
public:
using IExtension::IExtension;

// auto-detect plugins
//
bool autodetect() const { return m_AutoDetect; }

// list of specified plugins
//
const auto& plugins() const { return m_Plugins; }

const auto& themeAdditions() const { return m_ThemeAdditions; }
const auto& translationAdditions() const { return m_TranslationAdditions; }

protected:
PluginExtension(
std::filesystem::path const& path, ExtensionMetaData&& metadata, bool autodetect,
std::map<std::string, std::filesystem::path> plugins,
std::vector<std::shared_ptr<const ThemeAddition>> themeAdditions,
std::vector<std::shared_ptr<const TranslationAddition>> translationAdditions);

friend class ExtensionFactory;
static std::unique_ptr<PluginExtension>
loadExtension(std::filesystem::path const& path, ExtensionMetaData&& metadata);

private:
// auto-detect plugins
bool m_AutoDetect;

// forced plugins
std::map<std::string, std::filesystem::path> m_Plugins;

// theme and translations additions
std::vector<std::shared_ptr<const ThemeAddition>> m_ThemeAdditions;
std::vector<std::shared_ptr<const TranslationAddition>> m_TranslationAdditions;
};

// game extension that provides a game plugin, alongside other plugins, translation or
// theme (additions)
//
class QDLLEXPORT GameExtension : public PluginExtension
{
private:
GameExtension(PluginExtension&& pluginExtension);

friend class ExtensionFactory;
static std::unique_ptr<GameExtension> loadExtension(std::filesystem::path const& path,
ExtensionMetaData&& metadata);
};

} // namespace MOBase

#endif
89 changes: 89 additions & 0 deletions include/uibase/extensions/extensionsetting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#pragma once

#include <QString>
#include <QVariant>

namespace MOBase
{

// class representing a group of settings
//
class SettingGroup
{
public:
SettingGroup(QString const& name, QString const& title, QString const& description)
: m_Name{name}, m_Title{title}, m_Description{description}
{}

// return the (internal) name of this group, localization independent
//
const auto& name() const { return m_Name; }

// retrieve the title of this group, can be localized
//
const auto& title() const { return m_Title; }

// retrieve the description of this group, can be localized
//
const auto& description() const { return m_Description; }

private:
QString m_Name, m_Title, m_Description;
};

// class that represents an extension or a plugin setting
//
class Setting
{
public:
// deprecated constructor that was previously available as PluginSettin
//
[[deprecated]] Setting(const QString& name, const QString& description,
const QVariant& defaultValue)
: m_Name{name}, m_Title{name}, m_Description{description}, m_Group{},
m_DefaultValue{defaultValue}
{}

Setting(const QString& name, const QString& title, const QString& description,
const QVariant& defaultValue)
: m_Name{name}, m_Title{title}, m_Description{description}, m_Group{},
m_DefaultValue{defaultValue}
{}

Setting(const QString& name, const QString& title, const QString& description,
const QString& group, const QVariant& defaultValue)
: m_Name{name}, m_Title{title}, m_Description{description}, m_Group{group},
m_DefaultValue{defaultValue}
{}

public:
// return the (internal) name of this setting, localization independent
//
const auto& name() const { return m_Name; }

// retrieve the title of this setting, can be localized
//
const auto& title() const { return m_Title; }

// retrieve the description of this setting, can be localized
//
const auto& description() const { return m_Description; }

// retrieve the name of the group this settings belongs to or an empty string if there
// is none
//
const auto& group() const { return m_Group; }

// retrieve the default value of this setting
//
const auto& defaultValue() const { return m_DefaultValue; }

private:
QString m_Name;
QString m_Title;
QString m_Description;
QString m_Group;
QVariant m_DefaultValue;
};

} // namespace MOBase
Loading