Merge pull request #23140 from kadet1090/yaml-source-style-parameters

Gui: Add YamlParameterSource for StyleParameters
This commit is contained in:
Chris Hennes
2025-08-27 10:34:17 -05:00
committed by GitHub
5 changed files with 188 additions and 7 deletions

View File

@@ -141,6 +141,7 @@
#include "QtWidgets.h"
#include <OverlayManager.h>
#include <ParamHandler.h>
#include <Base/ServiceProvider.h>
#ifdef BUILD_TRACY_FRAME_PROFILER
@@ -381,20 +382,48 @@ struct PyMethodDef FreeCADGui_methods[] = {
void Application::initStyleParameterManager()
{
static ParamHandlers handlers;
const auto deduceParametersFilePath = []() -> std::string {
const auto hMainWindowGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow");
if (const std::string& path = hMainWindowGrp->GetASCII("ThemeStyleParametersFile");
!path.empty()) {
return path;
}
return fmt::format("qss:parameters/{}.yaml", hMainWindowGrp->GetASCII("Theme", "Classic"));
};
auto themeParametersSource = new StyleParameters::YamlParameterSource(
deduceParametersFilePath(),
{.name = QT_TR_NOOP("Theme Parameters"),
.options = StyleParameters::ParameterSourceOption::UserEditable});
handlers.addDelayedHandler(
"BaseApp/Preferences/MainWindow",
{"ThemeStyleParametersFiles", "Theme"},
[themeParametersSource, deduceParametersFilePath](ParameterGrp::handle) {
themeParametersSource->changeFilePath(deduceParametersFilePath());
});
Base::registerServiceImplementation<StyleParameters::ParameterSource>(
new StyleParameters::BuiltInParameterSource({.name = QT_TR_NOOP("Built-in Parameters")}));
Base::registerServiceImplementation<StyleParameters::ParameterSource>(
new StyleParameters::UserParameterSource(
App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Themes/Tokens"),
{.name = QT_TR_NOOP("Theme Parameters"),
.options = StyleParameters::ParameterSourceOption::UserEditable}));
// todo: left for compatibility with older theme versions, to be removed before release
Base::registerServiceImplementation<StyleParameters::ParameterSource>(
new StyleParameters::UserParameterSource(
App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Themes/UserTokens"),
{.name = QT_TR_NOOP("Theme Parameters - Fallback"),
.options = StyleParameters::ParameterSourceOption::ReadOnly}));
Base::registerServiceImplementation<StyleParameters::ParameterSource>(themeParametersSource);
Base::registerServiceImplementation<StyleParameters::ParameterSource>(
new StyleParameters::UserParameterSource(
App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Themes/UserParameters"),
{.name = QT_TR_NOOP("User Parameters"),
.options = StyleParameters::ParameterSource::UserEditable}));

View File

@@ -146,7 +146,9 @@ target_include_directories(
${QtNetwork_INCLUDE_DIRS}
${QtUiTools_INCLUDE_DIRS}
${QtXml_INCLUDE_DIRS}
${YAML_CPP_INCLUDE_DIRS}
)
list(APPEND FreeCADGui_LIBS
${QtCore_LIBRARIES}
${QtWidgets_LIBRARIES}
@@ -159,6 +161,18 @@ list(APPEND FreeCADGui_LIBS
${QtUiTools_LIBRARIES}
)
link_directories(${YAML_CPP_LIBRARY_DIR})
if(yaml-cpp_VERSION VERSION_LESS 0.8.0)
list(APPEND FreeCADGui_LIBS
${YAML_CPP_LIBRARIES}
)
else()
list(APPEND FreeCADGui_LIBS
yaml-cpp::yaml-cpp
)
endif()
if(${Qt5WinExtras_FOUND})
target_include_directories(
FreeCADGui

View File

@@ -465,6 +465,10 @@ void StyleParametersModel::flush()
}
});
for (auto* source : sources) {
source->flush();
}
reset();
}

View File

@@ -26,6 +26,10 @@
#include "ParameterManager.h"
#include "Parser.h"
#include <QFile>
#include <fstream>
#include <yaml-cpp/yaml.h>
#ifndef _PreComp_
#include <QColor>
#include <QRegularExpression>
@@ -35,6 +39,8 @@
#include <variant>
#endif
FC_LOG_LEVEL_INIT("Gui", true, true)
namespace Gui::StyleParameters
{
@@ -215,6 +221,92 @@ void UserParameterSource::remove(const std::string& name)
hGrp->RemoveASCII(name.c_str());
}
YamlParameterSource::YamlParameterSource(const std::string& filePath, const Metadata& metadata)
: ParameterSource(metadata)
{
changeFilePath(filePath);
}
void YamlParameterSource::changeFilePath(const std::string& path)
{
this->filePath = path;
reload();
}
void YamlParameterSource::reload()
{
QFile file(QString::fromStdString(filePath));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
FC_TRACE("StyleParameters: Unable to open file " << filePath);
return;
}
if (filePath.starts_with(":/")) {
this->metadata.options |= ReadOnly;
}
QTextStream in(&file);
std::string content = in.readAll().toStdString();
YAML::Node root = YAML::Load(content);
parameters.clear();
for (auto it = root.begin(); it != root.end(); ++it) {
auto key = it->first.as<std::string>();
auto value = it->second.as<std::string>();
parameters[key] = Parameter {
.name = key,
.value = value,
};
}
}
std::list<Parameter> YamlParameterSource::all() const
{
std::list<Parameter> result;
for (const auto& param : parameters | std::views::values) {
result.push_back(param);
}
return result;
}
std::optional<Parameter> YamlParameterSource::get(const std::string& name) const
{
if (auto it = parameters.find(name); it != parameters.end()) {
return it->second;
}
return std::nullopt;
}
void YamlParameterSource::define(const Parameter& param)
{
parameters[param.name] = param;
}
void YamlParameterSource::remove(const std::string& name)
{
parameters.erase(name);
}
void YamlParameterSource::flush()
{
YAML::Node root;
for (const auto& [name, param] : parameters) {
root[name] = param.value;
}
QFile file(QString::fromStdString(filePath));
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
FC_WARN("StyleParameters: Unable to open file " << filePath);
return;
}
QTextStream out(&file);
out << QString::fromStdString(YAML::Dump(root));
}
ParameterManager::ParameterManager() = default;
void ParameterManager::reload()

View File

@@ -306,6 +306,11 @@ public:
* @param[in] name The name of the parameter to remove.
*/
virtual void remove([[maybe_unused]] const std::string& name) {}
/**
* @brief Flushes buffered changes into more persistent storage.
*/
virtual void flush() {}
};
/**
@@ -376,6 +381,43 @@ public:
void remove(const std::string& name) override;
};
/**
* @class YamlParameterSource
* @brief A ParameterSource implementation that loads and saves parameters
* from a YAML file using yaml-cpp.
*
* This class maintains an in-memory map of parameters loaded from a YAML file.
* Any changes through define() or remove() will also update the file.
*/
class GuiExport YamlParameterSource : public ParameterSource
{
public:
/**
* @brief Constructs a YamlParameterSource that reads parameters from the given YAML file.
*
* If the file exists, all key-value pairs are loaded into memory.
* If the file does not exist, an empty parameter set is initialized.
*
* @param filePath Path to the YAML file used for persistence.
* @param metadata Optional metadata describing this source.
*/
explicit YamlParameterSource(const std::string& filePath, const Metadata& metadata = {});
void changeFilePath(const std::string& path);
void reload();
std::list<Parameter> all() const override;
std::optional<Parameter> get(const std::string& name) const override;
void define(const Parameter& param) override;
void remove(const std::string& name) override;
void flush() override;
private:
std::string filePath;
std::map<std::string, Parameter> parameters;
};
/**
* @brief Central manager for style parameters that aggregates multiple sources.
*