Gui: StyleParameters - use Base::Color instead of QColor

If possible we should prefer using our own classess instead of ones
coming from frameworks. This changes Style Parameters to use Base::Color
class instead of QColor. Calculations are still done using QColor but
the data is always exposed as Base::Color.
This commit is contained in:
Kacper Donat
2025-08-10 23:01:14 +02:00
parent 3a209a7e90
commit cf55183ece
7 changed files with 93 additions and 90 deletions

View File

@@ -30,6 +30,7 @@
#include "ui_DlgThemeEditor.h"
#include "BitmapFactory.h"
#include <Utilities.h>
#include <Base/ServiceProvider.h>
#include <Base/Tools.h>
@@ -67,7 +68,7 @@ QString typeOfTokenValue(const Gui::StyleParameters::Value& value)
[](const Gui::StyleParameters::Length&) {
return QWidget::tr("Length");
},
[](const QColor&) {
[](const Base::Color&) {
return QWidget::tr("Color");
}
},
@@ -529,8 +530,8 @@ QVariant StyleParametersModel::data(const QModelIndex& index, int role) const
}
if (role == Qt::DecorationRole) {
if (index.column() == ParameterPreview && std::holds_alternative<QColor>(value)) {
return colorPreview(std::get<QColor>(value));
if (index.column() == ParameterPreview && std::holds_alternative<Base::Color>(value)) {
return colorPreview(std::get<Base::Color>(value).asValue<QColor>());
}
}
}

View File

@@ -95,9 +95,9 @@ std::string Value::toString() const
return fmt::format("{}{}", value, unit);
}
if (std::holds_alternative<QColor>(*this)) {
auto color = std::get<QColor>(*this);
return fmt::format("#{:0>6x}", 0xFFFFFF & color.rgb()); // NOLINT(*-magic-numbers)
if (std::holds_alternative<Base::Color>(*this)) {
auto color = std::get<Base::Color>(*this);
return fmt::format("#{:0>6x}", color.getPackedRGB() >> 8); // NOLINT(*-magic-numbers)
}
return std::get<std::string>(*this);

View File

@@ -107,9 +107,9 @@ private:
*
* As a rule, operations can be only performed over values of the same type.
*/
struct Value : std::variant<Length, QColor, std::string>
struct Value : std::variant<Numeric, Base::Color, std::string>
{
using std::variant<Length, QColor, std::string>::variant;
using std::variant<Numeric, Base::Color, std::string>::variant;
/**
* Converts the object into its string representation.

View File

@@ -26,6 +26,7 @@
#include "Parser.h"
#include "ParameterManager.h"
#include <Utilities.h>
#include <Base/Tools.h>
#ifndef _PreComp_
@@ -67,12 +68,12 @@ Value FunctionCall::evaluate(const EvaluationContext& context) const
auto colorArg = arguments[0]->evaluate(context);
auto amountArg = arguments[1]->evaluate(context);
if (!std::holds_alternative<QColor>(colorArg)) {
if (!std::holds_alternative<Base::Color>(colorArg)) {
THROWM(Base::ExpressionError,
fmt::format("'{}' is not supported for colors", functionName));
}
auto color = std::get<QColor>(colorArg);
auto color = std::get<Base::Color>(colorArg).asValue<QColor>();
// In Qt if you want to make color 20% darker or lighter, you need to pass 120 as the value
// we, however, want users to pass only the relative difference, hence we need to add the
@@ -82,11 +83,11 @@ Value FunctionCall::evaluate(const EvaluationContext& context) const
auto amount = 100 + static_cast<int>(std::get<Length>(amountArg).value);
if (functionName == "lighten") {
return color.lighter(amount);
return Base::Color::fromValue(color.lighter(amount));
}
if (functionName == "darken") {
return color.darker(amount);
return Base::Color::fromValue(color.darker(amount));
}
return {};
@@ -104,25 +105,25 @@ Value FunctionCall::evaluate(const EvaluationContext& context) const
auto secondColorArg = arguments[1]->evaluate(context);
auto amountArg = arguments[2]->evaluate(context);
if (!std::holds_alternative<QColor>(firstColorArg)) {
if (!std::holds_alternative<Base::Color>(firstColorArg)) {
THROWM(Base::ExpressionError,
fmt::format("first argument of '{}' must be color", functionName));
}
if (!std::holds_alternative<QColor>(secondColorArg)) {
if (!std::holds_alternative<Base::Color>(secondColorArg)) {
THROWM(Base::ExpressionError,
fmt::format("second argument of '{}' must be color", functionName));
}
auto firstColor = std::get<QColor>(firstColorArg);
auto secondColor = std::get<QColor>(secondColorArg);
auto firstColor = std::get<Base::Color>(firstColorArg);
auto secondColor = std::get<Base::Color>(secondColorArg);
auto amount = Base::fromPercent(std::get<Length>(amountArg).value);
auto amount = Base::fromPercent(static_cast<long>(std::get<Length>(amountArg).value));
return QColor::fromRgbF(
(1 - amount) * firstColor.redF() + amount * secondColor.redF(),
(1 - amount) * firstColor.greenF() + amount * secondColor.greenF(),
(1 - amount) * firstColor.blueF() + amount * secondColor.blueF()
return Base::Color(
(1 - amount) * firstColor.r + amount * secondColor.r,
(1 - amount) * firstColor.g + amount * secondColor.g,
(1 - amount) * firstColor.b + amount * secondColor.b
);
};
@@ -169,7 +170,7 @@ Value BinaryOp::evaluate(const EvaluationContext& context) const
Value UnaryOp::evaluate(const EvaluationContext& context) const
{
Value val = operand->evaluate(context);
if (std::holds_alternative<QColor>(val)) {
if (std::holds_alternative<Base::Color>(val)) {
THROWM(Base::ExpressionError, "Unary operations on colors are not supported");
}
@@ -286,7 +287,7 @@ std::unique_ptr<Expr> Parser::parseColor()
int b = std::stoi(input.substr(pos, 2), nullptr, hexadecimalBase);
pos += 2;
return std::make_unique<Color>(QColor(r, g, b));
return std::make_unique<Color>(Base::Color(r / 255.0, g / 255.0, b / 255.0));
};
const auto parseFunctionStyleColor = [&]() {
@@ -313,7 +314,7 @@ std::unique_ptr<Expr> Parser::parseColor()
if (!match(')')) {
THROWM(Base::ParserError, fmt::format("Expected ')' after color arguments, got '{}'", input[pos]));
}
return std::make_unique<Color>(QColor(r, g, b, a));
return std::make_unique<Color>(Base::Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0));
};
skipWhitespace();

View File

@@ -83,9 +83,9 @@ struct GuiExport Number: public Expr
struct GuiExport Color: public Expr
{
QColor color;
Base::Color color;
explicit Color(QColor color)
explicit Color(Base::Color color)
: color(std::move(color))
{}

View File

@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
#include <Gui/Application.h>
#include <Gui/Utilities.h>
#include <Gui/StyleParameters/ParameterManager.h>
using namespace Gui::StyleParameters;
@@ -73,20 +74,20 @@ TEST_F(ParameterManagerTest, BasicParameterResolution)
{
auto result = manager.resolve("PrimaryColor");
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 1);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 0);
}
{
auto result = manager.resolve("SecondaryColor");
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 0);
EXPECT_EQ(color.green(), 255);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 0);
EXPECT_EQ(color.g, 1);
EXPECT_EQ(color.b, 0);
}
}
@@ -294,10 +295,10 @@ TEST_F(ParameterManagerTest, ComplexExpressions)
{
auto result = manager.resolve("ColorWithFunction");
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result).asValue<QColor>();
// Should be lighter than the original red
EXPECT_GT(color.lightness(), QColor("#ff0000").lightness());
EXPECT_GT(color.lightness(), QColor(0xff0000).lightness());
}
}

View File

@@ -23,7 +23,7 @@
#include <gtest/gtest.h>
#include <QColor>
#include <Gui/Utilities.h>
#include <Gui/StyleParameters/Parser.h>
#include <Gui/StyleParameters/ParameterManager.h>
@@ -103,56 +103,56 @@ TEST_F(ParserTest, ParseColors)
Parser parser("#ff0000");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 1);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 0);
}
{
Parser parser("#00ff00");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 0);
EXPECT_EQ(color.green(), 255);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 0);
EXPECT_EQ(color.g, 1);
EXPECT_EQ(color.b, 0);
}
{
Parser parser("#0000ff");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 0);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 255);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 0);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 1);
}
{
Parser parser("rgb(255, 0, 0)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 1);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 0);
}
{
Parser parser("rgba(255, 0, 0, 128)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_EQ(color.alpha(), 128);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_DOUBLE_EQ(color.r, 1);
EXPECT_DOUBLE_EQ(color.g, 0);
EXPECT_DOUBLE_EQ(color.b, 0);
EXPECT_NEAR(color.a, 128 / 255.0, 1e-6);
}
}
@@ -173,11 +173,11 @@ TEST_F(ParserTest, ParseParameterReferences)
Parser parser("@TestColor");
auto expr = parser.parse();
auto result = expr->evaluate({.manager = &manager, .context = {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 1);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 0);
}
{
@@ -360,30 +360,30 @@ TEST_F(ParserTest, ParseFunctionCalls)
Parser parser("lighten(#ff0000, 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result).asValue<QColor>();
// The result should be lighter than the original red
EXPECT_GT(color.lightness(), QColor("#ff0000").lightness());
EXPECT_GT(color.lightness(), QColor(0xff0000).lightness());
}
{
Parser parser("darken(#ff0000, 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result).asValue<QColor>();
// The result should be darker than the original red
EXPECT_LT(color.lightness(), QColor("#ff0000").lightness());
EXPECT_LT(color.lightness(), QColor(0xff0000).lightness());
}
{
Parser parser("lighten(@TestColor, 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result).asValue<QColor>();
// The result should be lighter than the original red
EXPECT_GT(color.lightness(), QColor("#ff0000").lightness());
EXPECT_GT(color.lightness(), QColor(0xff0000).lightness());
}
}
@@ -496,11 +496,11 @@ TEST_F(ParserTest, ParseWhitespace)
Parser parser("rgb(255,0,0)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 1);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 0);
}
}
@@ -539,11 +539,11 @@ TEST_F(ParserTest, ParseEdgeCases)
Parser parser("#ff0000");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<QColor>(result));
auto color = std::get<QColor>(result);
EXPECT_EQ(color.red(), 255);
EXPECT_EQ(color.green(), 0);
EXPECT_EQ(color.blue(), 0);
EXPECT_TRUE(std::holds_alternative<Base::Color>(result));
auto color = std::get<Base::Color>(result);
EXPECT_EQ(color.r, 1);
EXPECT_EQ(color.g, 0);
EXPECT_EQ(color.b, 0);
}
// Single parameter reference