107 lines
5.6 KiB
C++
107 lines
5.6 KiB
C++
#include "UI/ShapeControls.h"
|
|
#include "Document/EditorSession.h"
|
|
#include "UI/ColorPaletteControls.h"
|
|
#include "UI/LassoControls.h"
|
|
#include <QButtonGroup>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QSignalBlocker>
|
|
#include <QSlider>
|
|
#include <QToolButton>
|
|
#include <cmath>
|
|
|
|
ShapeControls::ShapeControls(EditorSession &session, QWidget *parent)
|
|
: ToolHeaderBar(QStringLiteral("Shape"), parent), m_session(session), m_kinds(new QButtonGroup(this)),
|
|
m_kindButtons{kind(ShapeKind::rectangle), kind(ShapeKind::ellipse), kind(ShapeKind::line)},
|
|
m_widthSlider(new QSlider(Qt::Horizontal, this)),
|
|
m_widthField(new SelectionAmountField(session, 1, 5000, [this] { return m_session.shapeLineWidth(); },
|
|
[this](double width) { m_session.setShapeLineWidth(width); }, this)),
|
|
m_width(amount(QStringLiteral("Width"), m_widthSlider, m_widthField,
|
|
{.sensitivity = 1, .low = 1, .high = 5000, .step = std::nullopt, .value = [this] { return m_session.shapeLineWidth(); },
|
|
.set = [this](double width) { m_session.setShapeLineWidth(width); }})), m_radiusSlider(new QSlider(Qt::Horizontal, this)),
|
|
m_radiusField(new SelectionAmountField(session, 0, 5000, [this] { return m_session.shapeCornerRadius(); },
|
|
[this](double radius) { m_session.setShapeCornerRadius(radius); }, this)),
|
|
m_radius(amount(QStringLiteral("Radius"), m_radiusSlider, m_radiusField,
|
|
{.sensitivity = 1, .low = 0, .high = 5000, .step = std::nullopt, .value = [this] { return m_session.shapeCornerRadius(); },
|
|
.set = [this](double radius) { m_session.setShapeCornerRadius(radius); }})),
|
|
m_fill(new SwatchButton([&session] { return session.foregroundColor(); }, 3, 0, 0.5, this))
|
|
{
|
|
m_widthSlider->setObjectName(QStringLiteral("shapeWidthSlider"));
|
|
m_widthSlider->setRange(1, 100);
|
|
connect(m_widthSlider, &QSlider::valueChanged, this, [this](int value) { m_session.setShapeLineWidth(value); });
|
|
m_widthField->setObjectName(QStringLiteral("shapeWidth"));
|
|
m_width->setObjectName(QStringLiteral("shapeWidthGroup"));
|
|
m_radiusSlider->setObjectName(QStringLiteral("shapeRadiusSlider"));
|
|
m_radiusSlider->setRange(0, 200);
|
|
connect(m_radiusSlider, &QSlider::valueChanged, this, [this](int value) { m_session.setShapeCornerRadius(value); });
|
|
m_radiusField->setObjectName(QStringLiteral("shapeRadius"));
|
|
m_radius->setObjectName(QStringLiteral("shapeRadiusGroup"));
|
|
m_radius->setToolTip(QStringLiteral("Round the rectangle's corners by this many pixels; 0 keeps them square"));
|
|
m_fill->setObjectName(QStringLiteral("shapeFill"));
|
|
m_fill->setFixedSize(36, 18);
|
|
m_fill->setToolTip(QStringLiteral("Shapes fill with the foreground color; click to change it"));
|
|
connect(m_fill, &QAbstractButton::clicked, this, [this] { m_session.openColorPicker(false); });
|
|
auto *fill = new QWidget(this);
|
|
auto *fillRow = new QHBoxLayout(fill);
|
|
fillRow->setContentsMargins(0, 0, 0, 0);
|
|
fillRow->setSpacing(6);
|
|
fillRow->addWidget(new QLabel(QStringLiteral("Fill"), fill));
|
|
fillRow->addWidget(m_fill);
|
|
for (QWidget *widget : std::initializer_list<QWidget *>{m_kindButtons[0], m_kindButtons[1], m_kindButtons[2], m_width, m_radius, fill})
|
|
row->insertWidget(row->count() - 1, widget);
|
|
connect(&m_session, &EditorSession::changed, this, &ShapeControls::synchronize);
|
|
synchronize();
|
|
}
|
|
|
|
// A segment of Swift's picker; choosing drops a draft first.
|
|
QToolButton *ShapeControls::kind(ShapeKind value)
|
|
{
|
|
auto *button = new QToolButton(this);
|
|
button->setObjectName(QStringLiteral("shape") + rawValue(value));
|
|
button->setText(rawValue(value));
|
|
button->setCheckable(true);
|
|
button->setToolTip(QStringLiteral("Shift-U (or Tab) steps through Rectangle, Ellipse and Line"));
|
|
m_kinds->addButton(button);
|
|
connect(button, &QToolButton::clicked, this, [this, value] {
|
|
m_session.cancelShape();
|
|
m_session.setShapeKind(value);
|
|
});
|
|
return button;
|
|
}
|
|
|
|
QWidget *ShapeControls::amount(const QString &name, QSlider *slider, SelectionAmountField *field, NumericScrub::Options scrub)
|
|
{
|
|
auto *group = new QWidget(this);
|
|
auto *layout = new QHBoxLayout(group);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->setSpacing(6);
|
|
slider->setFixedWidth(100);
|
|
field->setFixedWidth(48);
|
|
auto *label = new QLabel(name, group);
|
|
// A scrub shows its value over the field's typing.
|
|
scrub.set = [field, set = std::move(scrub.set)](double value) {
|
|
set(value);
|
|
field->showValue();
|
|
};
|
|
new NumericScrub(label, std::move(scrub));
|
|
for (QWidget *widget : std::initializer_list<QWidget *>{label, slider, field, new QLabel(QStringLiteral("px"), group)})
|
|
layout->addWidget(widget);
|
|
return group;
|
|
}
|
|
|
|
void ShapeControls::synchronize()
|
|
{
|
|
const ShapeKind kind = m_session.shapeKind();
|
|
m_kindButtons[size_t(kind)]->setChecked(true);
|
|
m_width->setVisible(kind == ShapeKind::line);
|
|
m_radius->setVisible(kind == ShapeKind::rectangle);
|
|
// The sliders show the session's number without writing back.
|
|
const QSignalBlocker widthBlocker(m_widthSlider), radiusBlocker(m_radiusSlider);
|
|
m_widthSlider->setValue(int(std::lround(std::min(100.0, m_session.shapeLineWidth()))));
|
|
m_radiusSlider->setValue(int(std::lround(std::min(200.0, m_session.shapeCornerRadius()))));
|
|
m_widthField->sync(int(std::lround(m_session.shapeLineWidth())));
|
|
m_radiusField->sync(int(std::lround(m_session.shapeCornerRadius())));
|
|
m_fill->update();
|
|
setEnabled(!m_session.showsBusy() && m_session.document().has_value());
|
|
}
|