Skip to content

Commit

Permalink
Improved the color property editor
Browse files Browse the repository at this point in the history
Now it is possible to enter the hex value (or CSS color name) to set a
color's value.
  • Loading branch information
bjorn committed Jan 9, 2025
1 parent ae2b717 commit 544ae78
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/tiled/colorbutton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void ColorButton::pickColor()
void ColorButton::updateIcon()
{
// todo: fix gray icon in disabled state (consider using opacity, and not using an icon at all)
setIcon(Utils::colorIcon(mColor, iconSize()));
setIcon(mColor.isValid() ? Utils::colorIcon(mColor, iconSize()) : QIcon());
setText(mColor.isValid() ? QString() : tr("Not set"));
}

Expand Down
43 changes: 9 additions & 34 deletions src/tiled/propertiesview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

#include "propertiesview.h"

#include "colorbutton.h"
#include "fileedit.h"
#include "textpropertyedit.h"
#include "utils.h"
Expand Down Expand Up @@ -475,46 +474,22 @@ QWidget *RectFProperty::createEditor(QWidget *parent)

QWidget *ColorProperty::createEditor(QWidget *parent)
{
auto editor = new QWidget(parent);
auto layout = new QHBoxLayout(editor);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);

auto colorButton = new ColorButton(editor);
colorButton->setShowAlphaChannel(m_alpha);
colorButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);

QIcon resetIcon(QStringLiteral(":/images/16/edit-clear.png"));
resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png"));

auto resetButton = new QToolButton(editor);
resetButton->setIcon(resetIcon);
resetButton->setToolTip(tr("Unset Color"));
resetButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
Utils::setThemeIcon(resetButton, "edit-clear");

layout->addWidget(colorButton);
layout->addWidget(resetButton);
auto editor = new ColorEdit(parent);
editor->setShowAlpha(m_alpha);

auto syncEditor = [=] {
const QSignalBlocker blocker(colorButton);
auto v = value();
colorButton->setColor(v);
resetButton->setEnabled(v.isValid());
// Not using QSignalBlocker, because the editor internally needs its
// textChanged signal to be emitted. Instead, we rely on 'valueEdited',
// which isn't emitted when setting the value like this.
editor->setValue(value());
};
syncEditor();

connect(resetButton, &QToolButton::clicked, colorButton, [colorButton] {
colorButton->setColor(QColor());
});
connect(this, &Property::valueChanged, colorButton, syncEditor);
connect(colorButton, &ColorButton::colorChanged, this, [=] {
resetButton->setEnabled(colorButton->color().isValid());
setValue(colorButton->color());
connect(this, &Property::valueChanged, editor, syncEditor);
connect(editor, &ColorEdit::valueEdited, this, [=] {
setValue(editor->value());
});

editor->setFocusProxy(colorButton);

return editor;
}

Expand Down
132 changes: 132 additions & 0 deletions src/tiled/propertyeditorwidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@
#include "propertiesview.h"
#include "utils.h"

#include <QAction>
#include <QColorDialog>
#include <QFontDatabase>
#include <QGridLayout>
#include <QMenu>
#include <QPainter>
#include <QResizeEvent>
#include <QScopedValueRollback>
#include <QStyle>
#include <QStyleOption>
#include <QStylePainter>
#include <QValidator>

namespace Tiled {

Expand Down Expand Up @@ -643,6 +649,132 @@ QRectF RectFEdit::value() const
}


class ColorValidator : public QValidator
{
Q_OBJECT

public:
using QValidator::QValidator;

State validate(QString &input, int &) const override
{
if (isValidName(input))
return State::Acceptable;

return State::Intermediate;
}

void fixup(QString &input) const override
{
if (!isValidName(input) && isValidName(QLatin1Char('#') + input))
input.prepend(QLatin1Char('#'));
}

static bool isValidName(const QString &name)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
return QColor::isValidColorName(name);
#else
return QColor::isValidColor(name);
#endif
}
};


ColorEdit::ColorEdit(QWidget *parent)
: LineEdit(parent)
{
setValidator(new ColorValidator(this));
setClearButtonEnabled(true);
setPlaceholderText(tr("Not set"));
setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));

m_colorIconAction = addAction(Utils::colorIcon(QColor(), Utils::smallIconSize()), QLineEdit::LeadingPosition);
m_colorIconAction->setText(tr("Pick Color"));
connect(m_colorIconAction, &QAction::triggered, this, &ColorEdit::pickColor);

connect(this, &QLineEdit::textEdited, this, &ColorEdit::onTextEdited);
}

void ColorEdit::setValue(const QColor &color)
{
if (m_color == color)
return;

m_color = color;

if (!m_editingText) {
if (!color.isValid())
setText(QString());
else if (m_color.alpha() == 255)
setText(color.name(QColor::HexRgb));
else
setText(color.name(QColor::HexArgb));
}

m_colorIconAction->setIcon(Utils::colorIcon(color, Utils::smallIconSize()));

if (m_editingText)
emit valueEdited();
}

void ColorEdit::setShowAlpha(bool enabled)
{
if (m_showAlpha == enabled)
return;

m_showAlpha = enabled;
}

void ColorEdit::contextMenuEvent(QContextMenuEvent *event)
{
QPointer<QMenu> menu = createStandardContextMenu();
if (!menu)
return;

menu->addSeparator();
menu->addAction(tr("Pick Color"), this, &ColorEdit::pickColor);
menu->exec(event->globalPos());
delete static_cast<QMenu *>(menu);

event->accept();
}

void ColorEdit::onTextEdited()
{
QScopedValueRollback<bool> editing(m_editingText, true);

const QString text = this->text();
QColor color;

if (!text.isEmpty()) {
if (ColorValidator::isValidName(text))
color = QColor(text);
else if (ColorValidator::isValidName(QLatin1Char('#') + text))
color = QColor(QLatin1Char('#') + text);
else
return;
}

setValue(color);
}

void ColorEdit::pickColor()
{
QColorDialog::ColorDialogOptions options;
if (m_showAlpha)
options |= QColorDialog::ShowAlphaChannel;

const QColor newColor = QColorDialog::getColor(m_color, this, QString(),
options);

if (newColor.isValid() && newColor != m_color) {
setValue(newColor);
emit valueEdited();
}
}


ElidingLabel::ElidingLabel(QWidget *parent)
: ElidingLabel(QString(), parent)
{}
Expand Down
33 changes: 33 additions & 0 deletions src/tiled/propertyeditorwidgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <QPointer>
#include <QSpinBox>

class QAction;
class QLabel;

namespace Tiled {
Expand Down Expand Up @@ -284,6 +285,38 @@ class RectFEdit : public QWidget
DoubleSpinBox *m_heightSpinBox;
};

/**
* A widget for editing a QColor value.
*/
class ColorEdit : public LineEdit
{
Q_OBJECT
Q_PROPERTY(QColor value READ value WRITE setValue NOTIFY valueEdited FINAL)

public:
ColorEdit(QWidget *parent = nullptr);

void setValue(const QColor &color);
QColor value() const { return m_color; }

void setShowAlpha(bool enabled);

signals:
void valueEdited();

protected:
void contextMenuEvent(QContextMenuEvent *event) override;

private:
void onTextEdited();
void pickColor();

QColor m_color;
bool m_showAlpha = false;
bool m_editingText = false;
QAction *m_colorIconAction;
};

/**
* A label that elides its text if there is not enough space.
*
Expand Down
5 changes: 1 addition & 4 deletions src/tiled/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,11 +312,8 @@ QIcon themeIcon(const QString &name)

QIcon colorIcon(const QColor &color, QSize size)
{
if (!color.isValid())
return QIcon();

QPixmap pixmap(size);
pixmap.fill(color);
pixmap.fill(color.isValid() ? color : Qt::transparent);

QPainter painter(&pixmap);
painter.setPen(QColor(0, 0, 0, 128));
Expand Down

0 comments on commit 544ae78

Please sign in to comment.