Coloured slider tracks with double-click reset, aligned filter titles; entries release focus before their owners go (12.14c)
This commit is contained in:
1 parent
2183348aa6
commit
2fa8d9921d
39 files changed
+1387
-311
No files matched your search
+8
-102
@@ -21,9 +21,6 @@
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
// A slider's travel: thousandths of its range.
|
||||
constexpr int travel = 1000;
|
||||
|
||||
QLabel *words(const QString &text, bool callout, QWidget *parent)
|
||||
{
|
||||
auto *label = new QLabel(text, parent);
|
||||
@@ -39,20 +36,6 @@ QLabel *words(const QString &text, bool callout, QWidget *parent)
|
||||
return label;
|
||||
}
|
||||
|
||||
// Swift's `.number`: at most `decimals` places, none trailing.
|
||||
QString shown(double value, int decimals, QLocale locale)
|
||||
{
|
||||
locale.setNumberOptions(QLocale::OmitGroupSeparator);
|
||||
QString text = locale.toString(value, 'f', decimals);
|
||||
if (decimals == 0)
|
||||
return text;
|
||||
while (text.endsWith(locale.zeroDigit()))
|
||||
text.chop(locale.zeroDigit().size());
|
||||
if (text.endsWith(locale.decimalPoint()))
|
||||
text.chop(locale.decimalPoint().size());
|
||||
return text;
|
||||
}
|
||||
|
||||
QColor colour(const AdjustmentColor &value)
|
||||
{
|
||||
return QColor::fromRgbF(float(value.red), float(value.green), float(value.blue));
|
||||
@@ -247,76 +230,6 @@ FilterSheet::FilterSheet(EditorSession &session, QWidget *parent)
|
||||
synchronize();
|
||||
}
|
||||
|
||||
void FilterSheet::control(const QString &title, std::function<double &(FilterSettings &)> key, double low, double high, const QString &unit,
|
||||
int decimals, bool logarithmic)
|
||||
{
|
||||
QString name = title;
|
||||
name.remove(QLatin1Char(' '));
|
||||
name[0] = name[0].toLower();
|
||||
const size_t index = m_controls.size();
|
||||
auto *box = new QWidget(this);
|
||||
auto *label = new QLabel(title, box);
|
||||
label->setMinimumWidth(60);
|
||||
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
// Swift's scrubbable title: a point is the field's last decimal.
|
||||
new NumericScrub(label, {.sensitivity = 1 / std::pow(10.0, decimals), .low = low, .high = high, .step = std::nullopt,
|
||||
.value = [this, index] {
|
||||
FilterSettings current = settings();
|
||||
return m_controls[index].key(current);
|
||||
},
|
||||
.set = [this, index](double value) {
|
||||
update([this, index, value](FilterSettings &settings) { m_controls[index].key(settings) = value; });
|
||||
m_controls[index].field->setModified(false);
|
||||
synchronize();
|
||||
}});
|
||||
auto *slider = new QSlider(Qt::Horizontal, box);
|
||||
slider->setObjectName(name + QStringLiteral("Slider"));
|
||||
slider->setRange(0, travel);
|
||||
label->setBuddy(slider);
|
||||
// Swift rounds what the slider sets to the field's decimals.
|
||||
connect(slider, &QSlider::valueChanged, this, [this, index](int position) {
|
||||
const Control &at = m_controls[index];
|
||||
const double from = at.logarithmic ? std::log(at.low) : at.low, to = at.logarithmic ? std::log(at.high) : at.high;
|
||||
const double value = from + (to - from) * position / travel, step = std::pow(10.0, at.decimals);
|
||||
const double rounded = std::round((at.logarithmic ? std::exp(value) : value) * step) / step;
|
||||
// Unchanged, the thumb stays, so arrow steps add up.
|
||||
FilterSettings current = settings();
|
||||
if (at.key(current) == rounded)
|
||||
return;
|
||||
update([&at, rounded](FilterSettings &settings) { at.key(settings) = rounded; });
|
||||
// Its value replaces the field's typing.
|
||||
at.field->setModified(false);
|
||||
synchronize();
|
||||
});
|
||||
// A readable number applies as typed; normalizing clamps it.
|
||||
auto *field = new PickerField([this, index] {
|
||||
PickerField &edited = *m_controls[index].field;
|
||||
bool number = false;
|
||||
const double typed = edited.locale().toDouble(edited.text(), &number);
|
||||
if (edited.isModified() && number && std::isfinite(typed))
|
||||
update([this, index, typed](FilterSettings &settings) { m_controls[index].key(settings) = typed; });
|
||||
edited.setModified(false);
|
||||
synchronize();
|
||||
}, nullptr, box);
|
||||
field->setObjectName(name + QStringLiteral("Field"));
|
||||
field->setAccessibleName(title);
|
||||
field->setPlaceholderText(title);
|
||||
field->setAlignment(Qt::AlignRight);
|
||||
field->setFixedWidth(56);
|
||||
auto *suffixed = new QHBoxLayout;
|
||||
suffixed->setSpacing(2);
|
||||
suffixed->addWidget(field);
|
||||
suffixed->addWidget(new QLabel(unit, box));
|
||||
auto *row = new QHBoxLayout(box);
|
||||
row->setContentsMargins(0, 0, 0, 0);
|
||||
row->setSpacing(10);
|
||||
row->addWidget(label);
|
||||
row->addWidget(slider, 1);
|
||||
row->addLayout(suffixed);
|
||||
m_column->addWidget(box);
|
||||
m_controls.push_back(Control{std::move(key), low, high, decimals, logarithmic, slider, field});
|
||||
}
|
||||
|
||||
void FilterSheet::noise()
|
||||
{
|
||||
auto *box = new QWidget(this);
|
||||
@@ -408,21 +321,8 @@ void FilterSheet::synchronize()
|
||||
if (m_vignetteSwatch)
|
||||
m_vignetteSwatch->update();
|
||||
const FilterEdit &edit = m_session.filterEdit().value();
|
||||
for (Control &control : m_controls) {
|
||||
FilterSettings copy = edit.settings;
|
||||
const double value = control.key(copy);
|
||||
const double from = control.logarithmic ? std::log(control.low) : control.low, to = control.logarithmic ? std::log(control.high) : control.high;
|
||||
const double at = control.logarithmic ? std::log(value) : value;
|
||||
{
|
||||
// The slider shows the session's number without writing back.
|
||||
const QSignalBlocker quiet(control.slider);
|
||||
control.slider->setValue(int(std::lround((at - from) / (to - from) * travel)));
|
||||
}
|
||||
// A field being typed in keeps its typing.
|
||||
const QString text = shown(value, control.decimals, control.field->locale());
|
||||
if (!control.field->isModified() && control.field->text() != text)
|
||||
control.field->setText(text);
|
||||
}
|
||||
for (Control &control : m_controls)
|
||||
showControl(control, edit.settings);
|
||||
if (m_curves)
|
||||
m_curves->synchronize();
|
||||
if (m_gradientMap)
|
||||
@@ -439,6 +339,7 @@ void FilterSheet::synchronize()
|
||||
}
|
||||
for (const auto &[row, shows] : m_shownWhen)
|
||||
row->setVisible(shows(edit.settings));
|
||||
alignTitles();
|
||||
m_preview->setChecked(edit.preview);
|
||||
m_error->setText(edit.previewError.value_or(QString()));
|
||||
m_error->setVisible(edit.previewError.has_value());
|
||||
@@ -452,3 +353,8 @@ void FilterSheet::synchronize()
|
||||
m_ok->setEnabled(!(isAutomatic(edit.kind) && (edit.preparing || edit.previewError)));
|
||||
setEnabled(!edit.committing);
|
||||
}
|
||||
|
||||
FilterSheet::~FilterSheet()
|
||||
{
|
||||
releaseFocus(*this);
|
||||
}
|
||||
Reference in new issue
Block a user