Outer Glow, brush Smoothing and the rest of Compositor 1.2.0

This commit is contained in:
雷电芽衣 committed 2026-09-27 07:00:12 -04:00
1 parent 2cd0d1b86d
commit 374ba8ec73
32 files changed
+624 -47

No files matched your search

+14 -2
View File
@@ -52,6 +52,11 @@ BrushControls::BrushControls(EditorSession &session, QWidget *parent)
[this](int value) { changeBrush(m_session, [value](BrushSettings &brush) { brush.opacity = value / 1000.0; }); })),
m_opacity(new SelectionAmountField(session, 1, 100, [this] { return m_session.brushSettings().opacity * 100; },
[this](double percent) { changeBrush(m_session, [percent](BrushSettings &brush) { brush.opacity = percent / 100; }); }, this)),
m_smoothingLabel(label(QStringLiteral("Smoothing"), this)),
m_smoothingSlider(slider(QStringLiteral("brushSmoothingSlider"), 0, 1000,
[this](int value) { changeBrush(m_session, [value](BrushSettings &brush) { brush.smoothing = value / 10.0; }); })),
m_smoothing(new SelectionAmountField(session, 0, 100, [this] { return m_session.brushSettings().smoothing; },
[this](double value) { changeBrush(m_session, [value](BrushSettings &brush) { brush.smoothing = value; }); }, this)),
m_paintLabel(label(QStringLiteral("Paint"), this)), m_maskPaint(new QComboBox(this)), m_colourLabel(label(QStringLiteral("Color"), this)),
m_colour(new SwatchButton([&session] { return session.foregroundColor(); }, 4, 1, 1, this)),
m_cloneNote(label(QStringLiteral("Alt-click to set the source"), this)), m_mask(label(QStringLiteral("Mask"), this))
@@ -73,6 +78,9 @@ BrushControls::BrushControls(EditorSession &session, QWidget *parent)
m_opacity->setObjectName(QStringLiteral("brushOpacity"));
m_opacity->setFixedWidth(42);
m_opacity->setToolTip(QStringLiteral("Press 1–9 for 10–90%, 0 for 100%"));
m_smoothing->setObjectName(QStringLiteral("brushSmoothing"));
m_smoothing->setFixedWidth(42);
m_smoothing->setToolTip(QStringLiteral("The brush trails the pointer on a string this long, so a shaky hand still draws a smooth line"));
m_maskPaint->setObjectName(QStringLiteral("maskPaint"));
m_maskPaint->setFixedWidth(180);
m_maskPaint->addItem(QStringLiteral("Black · Hide"), false);
@@ -95,7 +103,7 @@ BrushControls::BrushControls(EditorSession &session, QWidget *parent)
widgets.insert(widgets.end(), {m_aligned, m_thisLayer, m_allLayers});
widgets.insert(widgets.end(), {label(QStringLiteral("Size"), this), m_size, label(QStringLiteral("px"), this), label(QStringLiteral("Hardness"), this),
m_hardnessSlider, m_hardness, label(QStringLiteral("%"), this), m_opacityLabel, m_opacitySlider, m_opacity,
label(QStringLiteral("%"), this), m_paintLabel, m_maskPaint, m_colourLabel, m_colour});
label(QStringLiteral("%"), this), m_smoothingLabel, m_smoothingSlider, m_smoothing, m_paintLabel, m_maskPaint, m_colourLabel, m_colour});
for (QWidget *widget : widgets)
row->insertWidget(row->count() - 1, widget);
row->addWidget(m_cloneNote);
@@ -156,10 +164,14 @@ void BrushControls::synchronize()
m_size->sync(int(std::lround(brush.diameter)));
m_hardness->sync(int(std::lround(brush.hardness * 100)));
m_opacity->sync(int(std::lround(brush.opacity * 100)));
for (QWidget *widget : std::initializer_list<QWidget *>{m_smoothingLabel, m_smoothingSlider, m_smoothing})
widget->setVisible(tool == NavigationTool::brush);
m_smoothing->sync(int(std::lround(brush.smoothing)));
// The sliders show the session's numbers without writing back.
const QSignalBlocker hardness(m_hardnessSlider), opacity(m_opacitySlider);
const QSignalBlocker hardness(m_hardnessSlider), opacity(m_opacitySlider), smoothing(m_smoothingSlider);
m_hardnessSlider->setValue(int(std::lround(brush.hardness * 1000)));
m_opacitySlider->setValue(int(std::lround(brush.opacity * 1000)));
m_smoothingSlider->setValue(int(std::lround(brush.smoothing * 10)));
const bool mask = m_session.isMaskSelected();
m_paintLabel->setVisible(mask);
m_maskPaint->setVisible(mask);
+4
View File
@@ -44,6 +44,10 @@ private:
QLabel *const m_opacityLabel;
QSlider *const m_opacitySlider;
SelectionAmountField *const m_opacity;
// Paint and Erase only; other tools have their own feel.
QLabel *const m_smoothingLabel;
QSlider *const m_smoothingSlider;
SelectionAmountField *const m_smoothing;
QLabel *const m_paintLabel;
QComboBox *const m_maskPaint;
// The foreground colour, the rail swatch's own picker.
+13 -2
View File
@@ -1,10 +1,20 @@
#include "UI/CompositorMenus.h"
#include "Logging.h"
#include "UI/KeyboardShortcuts.h"
#include "UI/NativeLayerList.h"
#include <QApplication>
#include <QMenu>
namespace {
// The layer list holding the keys, as Swift's table.
NativeLayerList *focusedList()
{
for (QWidget *widget = QApplication::focusWidget(); widget; widget = widget->parentWidget())
if (auto *list = qobject_cast<NativeLayerList *>(widget))
return list;
return nullptr;
}
// An entry's name: Hue/Saturation is newHueSaturationAdjustment.
QString adjustmentName(AdjustmentKind kind)
{
@@ -157,12 +167,14 @@ CompositorMenus::CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, QWi
add(view, QStringLiteral("clearGuides"), QStringLiteral("Clear Guides"), QKeySequence(), [this] { session().clearGuides(); });
QMenu *select = bar.addMenu(QStringLiteral("&Select"));
// Text fields keep their own Select All.
// The focus takes Select All first, as Swift's responder chain.
add(select, QStringLiteral("selectAll"), QStringLiteral("All"), QKeySequence(Qt::CTRL | Qt::Key_A), [this] {
if (m_field)
m_field->selectAll();
else if (InlineTextEditor *text = typedText())
text->selectAll();
else if (NativeLayerList *list = focusedList())
list->selectAllRows();
else
session().selectAll();
});
@@ -332,7 +344,6 @@ void CompositorMenus::synchronize()
action(QStringLiteral("transformControls"))->setChecked(s.showsTransformControls());
action(QStringLiteral("transformControls"))->setEnabled(s.tool() == NavigationTool::move && s.document().has_value());
const bool selected = s.selection().has_value();
action(QStringLiteral("selectAll"))->setEnabled(drawn);
action(QStringLiteral("deselect"))->setEnabled(selected && s.canEditSelection());
action(QStringLiteral("inverse"))->setEnabled(selected && s.canEditSelection());
action(QStringLiteral("layerPixels"))->setEnabled(active && active->asset && s.canEditSelection());
+7
View File
@@ -85,6 +85,13 @@ EffectsSheet::EffectsSheet(EditorSession &session, LayerEffectKind kind, QWidget
slider(QStringLiteral("Blur"), [](const LayerEffects &e) { return e.innerShadow ? std::optional(e.innerShadow->blur) : std::nullopt; },
[](LayerEffects &e, double value) { e.innerShadow->blur = value; }, 0, 100, QStringLiteral("px"), 500);
break;
case LayerEffectKind::outerGlow:
header(QStringLiteral("Outer Glow"), false);
slider(QStringLiteral("Size"), [](const LayerEffects &e) { return e.outerGlow ? std::optional(e.outerGlow->size) : std::nullopt; },
[](LayerEffects &e, double value) { e.outerGlow->size = value; }, 0, 100, QStringLiteral("px"), 500);
slider(QStringLiteral("Opacity"), [](const LayerEffects &e) { return percent(e.outerGlow); },
[](LayerEffects &e, double value) { e.outerGlow->opacity = value / 100; }, 0, 100, QStringLiteral("%"));
break;
}
auto *cancel = new QPushButton(QStringLiteral("Cancel"), this);
cancel->setObjectName(QStringLiteral("effectsCancel"));
+10
View File
@@ -255,3 +255,13 @@ void NativeLayerList::changeEvent(QEvent *event)
if (event->type() == QEvent::PaletteChange)
QMetaObject::invokeMethod(this, &NativeLayerList::update, Qt::QueuedConnection);
}
void NativeLayerList::selectAllRows()
{
if (m_rows.empty())
return;
QSet<QUuid> ids;
for (const ImageLayer &row : m_rows)
ids.insert(row.id);
m_session.selectLayers(ids, m_rows.front().id);
}
+2
View File
@@ -212,6 +212,8 @@ public:
QString dragToken() const { return m_dragToken; }
EditorSession &session() const { return m_session; }
// The table's Select All: every row, the top one primary.
void selectAllRows();
void update();
std::vector<LayerCell *> cells() const { return m_cells; }
// The row under a point of this list, or -1.