14.9: Filter › Last Filter on Ctrl+Meta+F
This commit is contained in:
14 files changed
+306
-9
No files matched your search
@@ -52,8 +52,10 @@ void ContentView::synchronizePanels()
|
||||
m_colorRangePanel.close();
|
||||
}
|
||||
}
|
||||
if (m_filterShown != m_session.filterEdit().has_value()) {
|
||||
m_filterShown = m_session.filterEdit().has_value();
|
||||
// Last Filter applies without the panel.
|
||||
const bool filtering = m_session.filterEdit() && !m_session.filterEdit()->repeating;
|
||||
if (m_filterShown != filtering) {
|
||||
m_filterShown = filtering;
|
||||
if (m_filterShown) {
|
||||
m_filterPanel.onClose = [this] { m_session.cancelFilter(); };
|
||||
const FilterKind kind = m_session.filterEdit()->kind;
|
||||
|
||||
@@ -441,6 +441,7 @@ private:
|
||||
QFutureWatcher<RasterMade> m_rasterCommit;
|
||||
std::optional<FilterEdit> m_filterEdit;
|
||||
FilterSettings m_filterSettings;
|
||||
std::optional<FilterKind> m_lastFilter;
|
||||
QFutureWatcher<Filtered> m_filterPreview;
|
||||
// Out until its finish runs: setFuture drops a pending signal.
|
||||
bool m_filterRendering = false;
|
||||
|
||||
@@ -16,7 +16,7 @@ bool EditorSession::canContentAwareFill() const
|
||||
return canAdjustColors() && selection().has_value() && !m_hueSaturation;
|
||||
}
|
||||
|
||||
void EditorSession::beginFilter(FilterKind kind)
|
||||
void EditorSession::beginFilter(FilterKind kind, bool repeating)
|
||||
{
|
||||
if (kind == FilterKind::contentAwareFill && !canContentAwareFill())
|
||||
return;
|
||||
@@ -61,7 +61,9 @@ void EditorSession::beginFilter(FilterKind kind)
|
||||
if (fillsCanvas)
|
||||
m_filterEdit->canvas = canvas;
|
||||
m_filterEdit->startedEmpty = startedEmpty;
|
||||
updateFilter(m_filterEdit->settings, true);
|
||||
m_filterEdit->repeating = repeating;
|
||||
// An automatic filter commits its preview, so needs one.
|
||||
updateFilter(m_filterEdit->settings, !repeating || isAutomatic(kind));
|
||||
} catch (const ProjectError &error) {
|
||||
setBrushError(QString::fromUtf8(error.what()));
|
||||
} catch (const ExportError &error) {
|
||||
@@ -69,6 +71,49 @@ void EditorSession::beginFilter(FilterKind kind)
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSession::setLastFilter(std::optional<FilterKind> kind)
|
||||
{
|
||||
m_lastFilter = kind;
|
||||
notify();
|
||||
}
|
||||
|
||||
bool EditorSession::canRepeatLastFilter() const
|
||||
{
|
||||
if (!m_lastFilter || m_filterEdit || m_hueSaturation)
|
||||
return false;
|
||||
return m_lastFilter == FilterKind::vignette ? canVignette() : canAdjustColors();
|
||||
}
|
||||
|
||||
void EditorSession::repeatLastFilter(std::function<void()> done)
|
||||
{
|
||||
if (!canRepeatLastFilter()) {
|
||||
qCWarning(lcApp) << "no last filter to run here";
|
||||
if (done)
|
||||
QMetaObject::invokeMethod(this, done, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
const FilterKind kind = m_lastFilter.value();
|
||||
const auto run = [this, kind, done] {
|
||||
beginFilter(kind, true);
|
||||
if (!m_filterEdit) {
|
||||
if (done)
|
||||
QMetaObject::invokeMethod(this, done, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
// Nothing applied: no panel is left to close it.
|
||||
commitFilter([this, id = m_filterEdit->id, done] {
|
||||
if (m_filterEdit && m_filterEdit->id == id)
|
||||
cancelFilter();
|
||||
if (done)
|
||||
done();
|
||||
});
|
||||
};
|
||||
if (m_gradientEdit)
|
||||
commitGradient(run);
|
||||
else
|
||||
run();
|
||||
}
|
||||
|
||||
void EditorSession::updateFilter(const FilterSettings &settings, bool preview)
|
||||
{
|
||||
if (!m_filterEdit || m_filterEdit->committing)
|
||||
@@ -243,6 +288,8 @@ void EditorSession::commitFilter(std::function<void()> done)
|
||||
m_filterPreviewFor = QUuid();
|
||||
if (edit.kind != FilterKind::cameraRaw)
|
||||
m_filterSettings = edit.settings;
|
||||
if (repeatsAsLastFilter(edit.kind))
|
||||
m_lastFilter = edit.kind;
|
||||
setIsProjectBusy(true);
|
||||
m_committingFilter = Committing{edit.id, std::move(done)};
|
||||
// A mask hides the background, which can come back.
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
// Swift's Filters extension: one filter at a time.
|
||||
public:
|
||||
bool canContentAwareFill() const;
|
||||
void beginFilter(FilterKind kind);
|
||||
// `repeating` is Last Filter: no preview unless automatic, no panel.
|
||||
void beginFilter(FilterKind kind, bool repeating = false);
|
||||
// What Filter › Last Filter runs again.
|
||||
std::optional<FilterKind> lastFilter() const { return m_lastFilter; }
|
||||
void setLastFilter(std::optional<FilterKind> kind);
|
||||
bool canRepeatLastFilter() const;
|
||||
// The last filter again, its settings kept, one step.
|
||||
void repeatLastFilter(std::function<void()> done = {});
|
||||
// The settings normalized; Preview off drops the queue and preview.
|
||||
void updateFilter(const FilterSettings &settings, bool preview);
|
||||
void cancelFilter();
|
||||
|
||||
@@ -53,6 +53,11 @@ QString rawValue(BackgroundQuality quality)
|
||||
return quality == BackgroundQuality::basic ? QStringLiteral("Basic") : QStringLiteral("Advanced");
|
||||
}
|
||||
|
||||
bool repeatsAsLastFilter(FilterKind kind)
|
||||
{
|
||||
return kind != FilterKind::contentAwareFill && !isImageAdjustment(kind);
|
||||
}
|
||||
|
||||
bool isImageAdjustment(FilterKind kind)
|
||||
{
|
||||
return kind == FilterKind::curves || kind == FilterKind::exposure || kind == FilterKind::gradientMap || kind == FilterKind::grain
|
||||
|
||||
@@ -26,6 +26,8 @@ QString rawValue(FilterKind kind);
|
||||
bool isAutomatic(FilterKind kind);
|
||||
// The Image menu's colour adjustments, not under Filter.
|
||||
bool isImageAdjustment(FilterKind kind);
|
||||
// The Filter menu's own, which Last Filter runs again.
|
||||
bool repeatsAsLastFilter(FilterKind kind);
|
||||
// Swift's allCases, the menus' order.
|
||||
inline constexpr std::array allFilterKinds{FilterKind::gaussianBlur, FilterKind::motionBlur, FilterKind::addNoise,
|
||||
FilterKind::vignette, FilterKind::bloomGlow, FilterKind::dither,
|
||||
@@ -183,6 +185,8 @@ public:
|
||||
std::optional<QRectF> canvas;
|
||||
// The layer had no pixels: the filter began clear.
|
||||
bool startedEmpty = false;
|
||||
// Last Filter: applied at once, without preview or panel.
|
||||
bool repeating = false;
|
||||
|
||||
private:
|
||||
void grow(const QRectF &extent);
|
||||
|
||||
@@ -225,6 +225,10 @@ CompositorMenus::CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, Pro
|
||||
add(image, QStringLiteral("flipCanvasVertical"), QStringLiteral("Flip Canvas Vertical"), QKeySequence(), [this] { session().flipCanvas(false); });
|
||||
|
||||
QMenu *filter = bar.addMenu(QStringLiteral("Fil&ter"));
|
||||
// Ctrl+Meta+F, Swift's Cmd+Control: Ctrl+F searches commands.
|
||||
add(filter, QStringLiteral("lastFilter"), QStringLiteral("Last Filter"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_F),
|
||||
[this] { session().repeatLastFilter(); });
|
||||
filter->addSeparator();
|
||||
for (const FilterKind kind : allFilterKinds) {
|
||||
if (kind != FilterKind::contentAwareFill && !isImageAdjustment(kind))
|
||||
add(filter, filterName(kind), rawValue(kind) + QStringLiteral("…"), QKeySequence(), [this, kind] { session().beginFilter(kind); });
|
||||
@@ -387,6 +391,8 @@ void CompositorMenus::synchronize()
|
||||
for (const char *name : {"expandSelection", "contractSelection", "featherSelection"})
|
||||
action(QString::fromLatin1(name))->setEnabled(s.canModifySelection());
|
||||
action(QStringLiteral("levels"))->setEnabled(s.canAdjustColors() && !s.hueSaturation());
|
||||
action(QStringLiteral("lastFilter"))->setText(s.lastFilter() ? QStringLiteral("Last Filter: ") + rawValue(s.lastFilter().value()) : QStringLiteral("Last Filter"));
|
||||
action(QStringLiteral("lastFilter"))->setEnabled(s.canRepeatLastFilter());
|
||||
for (const FilterKind kind : allFilterKinds) {
|
||||
if (kind != FilterKind::contentAwareFill)
|
||||
action(filterName(kind))->setEnabled((kind == FilterKind::vignette ? s.canVignette() : s.canAdjustColors()) && !s.hueSaturation());
|
||||
|
||||
@@ -130,7 +130,7 @@ const std::vector<ShortcutDefinition> &ShortcutDefinition::all()
|
||||
entry("Copy", "c", 1, true), entry("Copy Merged", "c", 9, true), entry("Paste", "v", 1, true),
|
||||
entry("Fill with Foreground", backspace, 2, true), entry("Fill with Background", backspace, 1, true),
|
||||
entry("Content-Aware Fill", backspace, 8, true), entry("Select All", "a", 1, true), entry("Deselect", "d", 1, true),
|
||||
entry("Inverse Selection", "i", 9, true), entry("Select Subject", "a", 3, true), entry("Curves", "m", 1, true),
|
||||
entry("Inverse Selection", "i", 9, true), entry("Select Subject", "a", 3, true), entry("Last Filter", "f", 5, true), entry("Curves", "m", 1, true),
|
||||
entry("Levels", "l", 1, true), entry("Hue/Saturation", "u", 1, true), entry("Invert Pixels / Mask", "i", 1, true),
|
||||
entry("Canvas Size", "c", 3, true), entry("Image Size", "i", 3, true), entry("Transform Layer / Selection", "t", 1, true),
|
||||
entry("Duplicate / Layer via Copy", "j", 1, true), entry("Toggle Clipping Mask", "g", 3, true),
|
||||
|
||||
Reference in new issue
Block a user