Skip to content

Commit

Permalink
Don't require confirmation for fill and magic wand
Browse files Browse the repository at this point in the history
The fill tool now previews fills locally without an overly obvious
indicator to that regard and making another click will implictly finish
that fill and start a new one. There's now buttons to apply and cancel
the fill and he click to confirm can be brought back again in a menu in
the top-left corner.

The previewed fill no longer changes as you modify parameters by
default, since we want to pretend it's a "normal" fill tool and users
won't expect that e.g. changing color will modify an existing fill. That
can also be reenabled in the menu.

The magic wand now always applies immediately, since selections are
local-only anyway and it doesn't have that many useful parameters to
change to begin with. That may change again in the future.
  • Loading branch information
askmeaboutlo0m committed Oct 22, 2024
1 parent 3a8b44e commit b781302
Show file tree
Hide file tree
Showing 33 changed files with 502 additions and 305 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Unreleased Version 2.2.2-pre
* Fix: Don't unnecessarily scale full-canvas animations by a single pixel.
* Feature: Replace GIF export with ffmpeg's libraries, since they are also used for videos. It's way faster, generates much better palettes and . Thanks dAVePAGE and JJ for reporting issues in this regard.
* Fix: Constrain aspect ratio of transform scaling properly, it was getting offset by the distance between the clicked point and the actual corner. Thanks Blozzom for reporting.
* Feature: Allow configuring flood fill preview and confirmation behavior, defaulting to the simplest mode similar to single-user software, but still previewing fills locally first. The magic wand always works this way for now, since selections are local only anyway.

2024-08-09 Version 2.2.2-beta.3
* Fix: Use more accurate timers for performance profiles if the platform supports it.
Expand Down
Binary file added src/desktop/cursors/bucketcheck.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/desktop/cursors/dpcursors.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<qresource prefix="/cursors">
<file>arrow.png</file>
<file>bucket.png</file>
<file>bucketcheck.png</file>
<file>check.png</file>
<file>colorpicker.png</file>
<file>layerpicker.png</file>
Expand Down
5 changes: 0 additions & 5 deletions src/desktop/dialogs/settingsdialog/userinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,6 @@ void UserInterface::initMiscellaneous(
settings.bindShowTransformNotices(showTransformNotices);
form->addRow(tr("On-canvas notices:"), showTransformNotices);

QCheckBox *showFillNotices =
new QCheckBox(tr("Fill and magic wand confirmation"));
settings.bindShowFillNotices(showFillNotices);
form->addRow(nullptr, showFillNotices);

QCheckBox *scrollBars = new QCheckBox(tr("Show scroll bars on canvas"));
settings.bindCanvasScrollBars(scrollBars);
form->addRow(tr("Miscellaneous:"), scrollBars);
Expand Down
3 changes: 0 additions & 3 deletions src/desktop/scene/canvasview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,6 @@ void CanvasView::resetCursor()
} else if(m_toolState == int(tools::ToolState::Busy)) {
setViewportCursor(Qt::WaitCursor);
return;
} else if(m_toolState == int(tools::ToolState::AwaitingConfirmation)) {
setViewportCursor(m_checkCursor);
return;
}

if(m_toolcursor.shape() == Qt::CrossCursor) {
Expand Down
104 changes: 91 additions & 13 deletions src/desktop/toolwidgets/fillsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@
#include "libclient/tools/toolcontroller.h"
#include "libclient/tools/toolproperties.h"
#include "ui_fillsettings.h"
#include <QAction>
#include <QButtonGroup>
#include <QIcon>
#include <QMenu>
#include <QScopedValueRollback>
#include <QSignalBlocker>

namespace tools {

namespace props {
static const ToolProperties::Value<bool> shrink{
QStringLiteral("shrink"), false};
QStringLiteral("shrink"), false},
editable{QStringLiteral("editable"), false},
confirm{QStringLiteral("confirm"), false};
static const ToolProperties::RangedValue<int> expand{
QStringLiteral("expand"), 0, 0, 100},
featherRadius{QStringLiteral("featherRadius"), 0, 0, 40},
Expand Down Expand Up @@ -48,6 +53,41 @@ FillSettings::~FillSettings()

QWidget *FillSettings::createUiWidget(QWidget *parent)
{
m_headerWidget = new QWidget(parent);
QHBoxLayout *headerLayout = new QHBoxLayout;
headerLayout->setSpacing(0);
headerLayout->setContentsMargins(0, 0, 0, 0);

widgets::GroupedToolButton *menuButton = new widgets::GroupedToolButton(
widgets::GroupedToolButton::NotGrouped, m_headerWidget);
menuButton->setIcon(QIcon::fromTheme("application-menu"));
menuButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
menuButton->setPopupMode(QToolButton::InstantPopup);
menuButton->setStatusTip(tr("Fill tool settings"));
menuButton->setToolTip(menuButton->statusTip());
headerLayout->addWidget(menuButton);

QMenu *menu = new QMenu(menuButton);
menuButton->setMenu(menu);

m_editableAction = menu->addAction(tr("&Edit pending fills"));
m_editableAction->setStatusTip(tr(
"Apply changes in settings, color and layer to fills not yet applied"));
m_editableAction->setCheckable(true);
connect(
m_editableAction, &QAction::triggered, this,
&FillSettings::updateSettings);

m_confirmAction = menu->addAction(tr("&Confirm fills with second click"));
m_confirmAction->setStatusTip(tr(
"Lets you apply fills with a click instead of starting another fill"));
m_confirmAction->setCheckable(true);
connect(
m_confirmAction, &QAction::triggered, this,
&FillSettings::updateSettings);

headerLayout->addStretch();

QWidget *uiwidget = new QWidget(parent);
m_ui = new Ui_FillSettings;
m_ui->setupUi(uiwidget);
Expand Down Expand Up @@ -85,45 +125,63 @@ QWidget *FillSettings::createUiWidget(QWidget *parent)
&FillSettings::updateSize);
connect(
m_ui->opacity, QOverload<int>::of(&QSpinBox::valueChanged), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_ui->tolerance, QOverload<int>::of(&QSpinBox::valueChanged), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_ui->size, QOverload<int>::of(&QSpinBox::valueChanged), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_ui->expandShrink, &widgets::ExpandShrinkSpinner::spinnerValueChanged,
this, &FillSettings::pushSettings);
this, &FillSettings::updateSettings);
connect(
m_ui->expandShrink, &widgets::ExpandShrinkSpinner::shrinkChanged, this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_ui->expandShrink, &widgets::ExpandShrinkSpinner::kernelChanged, this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_ui->feather, QOverload<int>::of(&QSpinBox::valueChanged), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_ui->gap, QOverload<int>::of(&QSpinBox::valueChanged), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_sourceGroup,
QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_areaGroup,
QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
connect(
m_areaGroup,
QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this,
&FillSettings::updateSize);
connect(
m_ui->blendModeCombo,
QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&FillSettings::pushSettings);
&FillSettings::updateSettings);
updateSize();

m_ui->applyButton->setIcon(
uiwidget->style()->standardIcon(QStyle::SP_DialogApplyButton));
connect(
m_ui->applyButton, &QPushButton::clicked, controller(),
&ToolController::finishMultipartDrawing);

m_ui->cancelButton->setIcon(
uiwidget->style()->standardIcon(QStyle::SP_DialogCancelButton));
connect(
m_ui->cancelButton, &QPushButton::clicked, controller(),
&ToolController::cancelMultipartDrawing);

connect(
controller(), &ToolController::floodFillStateChanged, this,
&FillSettings::setButtonState);
setButtonState(false, false);

return uiwidget;
}

Expand Down Expand Up @@ -171,7 +229,8 @@ void FillSettings::pushSettings()
m_ui->feather->value(), isSizeUnlimited(size) ? -1 : size,
m_ui->opacity->value() / 100.0, m_ui->gap->value(),
FloodFill::Source(m_sourceGroup->checkedId()), blendMode,
FloodFill::Area(area));
FloodFill::Area(area), m_editableAction->isChecked(),
m_confirmAction->isChecked());

if(!m_ui->sourceFillSource->isEnabled() &&
m_ui->sourceFillSource->isChecked()) {
Expand Down Expand Up @@ -230,6 +289,8 @@ ToolProperties FillSettings::saveToolSettings()
props::tolerance,
m_ui->tolerance->value() / qreal(m_ui->tolerance->maximum()));
cfg.setValue(props::expand, m_ui->expandShrink->spinnerValue());
cfg.setValue(props::editable, m_editableAction->isChecked());
cfg.setValue(props::confirm, m_confirmAction->isChecked());
cfg.setValue(props::shrink, m_ui->expandShrink->isShrink());
cfg.setValue(props::kernel, m_ui->expandShrink->kernel());
cfg.setValue(props::featherRadius, m_ui->feather->value());
Expand Down Expand Up @@ -261,6 +322,10 @@ int FillSettings::getSize() const

void FillSettings::restoreToolSettings(const ToolProperties &cfg)
{
QScopedValueRollback<bool> rollback(m_updating);

m_editableAction->setChecked(cfg.value(props::editable));
m_confirmAction->setChecked(cfg.value(props::confirm));
m_ui->tolerance->setValue(
cfg.value(props::tolerance) * m_ui->tolerance->maximum());
m_ui->expandShrink->setSpinnerValue(cfg.value(props::expand));
Expand Down Expand Up @@ -310,6 +375,13 @@ void FillSettings::stepAdjust1(bool increase)
}
}

void FillSettings::updateSettings()
{
if(!m_updating) {
pushSettings();
}
}

void FillSettings::updateSize()
{
int size = m_ui->size->value();
Expand Down Expand Up @@ -368,4 +440,10 @@ void FillSettings::selectBlendMode(int blendMode)
}
}

void FillSettings::setButtonState(bool running, bool pending)
{
m_ui->applyButton->setEnabled(pending);
m_ui->cancelButton->setEnabled(running || pending);
}

}
11 changes: 11 additions & 0 deletions src/desktop/toolwidgets/fillsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define DESKTOP_TOOLWIDGETS_FILL_H
#include "desktop/toolwidgets/toolsettings.h"

class QAction;
class QButtonGroup;
class Ui_FillSettings;

Expand Down Expand Up @@ -40,6 +41,8 @@ class FillSettings final : public ToolSettings {

void setCompatibilityMode(bool compatibilityMode);

QWidget *getHeaderWidget() override { return m_headerWidget; }

signals:
void pixelSizeChanged(int size);

Expand All @@ -54,21 +57,29 @@ public slots:
QWidget *createUiWidget(QWidget *parent) override;

private:
void updateSettings();

void updateSize();
static bool isSizeUnlimited(int size);
int calculatePixelSize(int size) const;

void initBlendModeOptions();
void selectBlendMode(int blendMode);

void setButtonState(bool running, bool pending);

QWidget *m_headerWidget = nullptr;
Ui_FillSettings *m_ui = nullptr;
QAction *m_editableAction = nullptr;
QAction *m_confirmAction = nullptr;
QButtonGroup *m_sourceGroup = nullptr;
QButtonGroup *m_areaGroup = nullptr;
int m_previousMode;
int m_previousEraseMode;
qreal m_quickAdjust1 = 0.0;
bool m_haveSelection = false;
bool m_compatibilityMode = false;
bool m_updating = false;
};

}
Expand Down
Loading

0 comments on commit b781302

Please sign in to comment.