Add check box "Same in all tools" in the Dynamics options window (fix #4003)

Now each tool has independent 'dynamic options'. A global configuration
for all tools is also possible via the 'Same in all tools' option.

ALso this fix, solves a regression inserted in:
2cc15cda9e
(bug inserted: stabilizer is always active)
This commit is contained in:
Gaspar Capello 2023-08-08 17:26:58 -03:00 committed by David Capello
parent bee6b98336
commit 572cdf2b0e
8 changed files with 153 additions and 97 deletions

View File

@ -1262,7 +1262,7 @@
<param name="type" value="shading" />
</item>
<separator />
<item command="SetSameInk" text="@.same_in_all_tools" group="ink_options" />
<item command="SetSameInk" text="@general.same_in_all_tools" group="ink_options" />
</menu>
</menus>

View File

@ -317,6 +317,7 @@
<option id="discard_brush" type="bool" default="false" />
</section>
<section id="shared">
<option id="share_dynamics" type="bool" default="false" />
<option id="share_ink" type="bool" default="false" />
<option id="ink" type="app::tools::InkType" default="app::tools::InkType::DEFAULT" />
</section>

View File

@ -976,6 +976,7 @@ END
reset = Reset
advanced_options = Advanced Options
unknown = Unknown
same_in_all_tools = Same in all Tools
[gif_options]
title = GIF Options
@ -1061,9 +1062,6 @@ copy_color = Copy Alpha+Color
lock_alpha = Lock Alpha
shading = Shading
[ink_popup_menu]
same_in_all_tools = Same in all Tools
[jpeg_options]
title = JPEG Options
quality = Quality:

View File

@ -1,5 +1,5 @@
<!-- Aseprite -->
<!-- Copyright (C) 2020-2022 Igara Studio S.A. -->
<!-- Copyright (C) 2020-2023 Igara Studio S.A. -->
<gui>
<vbox id="dynamics">
<hbox>
@ -57,6 +57,8 @@
<label id="velocity_label" text="@.velocity" style="mini_label" />
<hbox id="velocity_placeholder" cell_align="horizontal" />
</grid>
<separator horizontal="true" cell_hspan="2" />
<check id="same_in_all_tools" text="@general.same_in_all_tools" />
</vbox>
</gui>

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -169,7 +169,7 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
void ToolLoopManager::movement(Pointer pointer)
{
// Filter points with the stabilizer
if (m_dynamics.stabilizerFactor > 0) {
if (m_dynamics.stabilizer && m_dynamics.stabilizerFactor > 0) {
const double f = m_dynamics.stabilizerFactor;
const gfx::Point delta = (pointer.point() - m_stabilizerCenter);
const double distance = std::sqrt(delta.x*delta.x + delta.y*delta.y);

View File

@ -1152,37 +1152,7 @@ public:
, m_ctxBar(ctxBar) {
addItem(SkinTheme::get(this)->parts.dynamics(), "dynamics_field");
// TODO: it would be better to initialize 'm_popup' at the time you
// need to display the dynamic options in the 'switchPopup()'
// function.
// However, initialization during construction of the DynamicField
// is an easy way to get the current dithering matrix given the
// index of the selected item of the "dithering matrix" comboBox.
m_popup.reset(new DynamicsPopup(this));
m_popup->loadDynamicPref(
&Preferences::instance().tool(App::instance()->activeTool()));
m_popup->setOptionsGridVisibility(m_optionsGridVisibility);
m_dynamics = m_popup->getDynamics();
m_popup->Close.connect(
[this](CloseEvent&){
deselectItems();
m_dynamics = m_popup->getDynamics();
auto& dynaPref = Preferences::instance().tool(
App::instance()->activeTool()).dynamics;
dynaPref.stabilizer(m_dynamics.stabilizer);
dynaPref.stabilizerFactor(m_dynamics.stabilizerFactor);
dynaPref.size(m_dynamics.size);
dynaPref.angle(m_dynamics.angle);
dynaPref.gradient(m_dynamics.gradient);
dynaPref.minSize.setValue(m_dynamics.minSize);
dynaPref.minAngle.setValue(m_dynamics.minAngle);
dynaPref.minPressureThreshold(m_dynamics.minPressureThreshold);
dynaPref.minVelocityThreshold(m_dynamics.minVelocityThreshold);
dynaPref.maxPressureThreshold(m_dynamics.maxPressureThreshold);
dynaPref.maxVelocityThreshold(m_dynamics.maxVelocityThreshold);
dynaPref.colorFromTo(m_dynamics.colorFromTo);
dynaPref.matrixIndex(m_popup->ditheringIndex());
});
loadDynamicsPref();
}
void switchPopup() {
@ -1192,6 +1162,28 @@ public:
return;
}
if (!m_popup.get())
m_popup.reset(new DynamicsPopup(this));
auto activeTool = App::instance()->activeTool();
m_popup->loadDynamicsPref();
m_dynamics = m_popup->getDynamics();
m_sameInAllTools = m_popup->sharedSettings();
m_popup->Close.connect(
[this](CloseEvent&) {
deselectItems();
auto activeTool = App::instance()->activeTool();
m_dynamics = m_popup->getDynamics();
m_sameInAllTools = m_popup->sharedSettings();
if (m_sameInAllTools) {
for (Tool* tool : *App::instance()->toolBox())
saveDynamicsPref(tool);
}
else
saveDynamicsPref(activeTool);
});
m_popup->refreshVisibility();
const gfx::Rect bounds = this->bounds();
m_popup->remapWindow();
fit_bounds(display(), m_popup.get(),
@ -1214,6 +1206,48 @@ public:
m_popup->setOptionsGridVisibility(state);
}
void saveDynamicsPref(Tool* tool) {
auto& dynaPref = Preferences::instance().tool(tool).dynamics;
dynaPref.stabilizer(m_dynamics.stabilizer);
dynaPref.stabilizerFactor(m_dynamics.stabilizerFactor);
dynaPref.size(m_dynamics.size);
dynaPref.angle(m_dynamics.angle);
dynaPref.gradient(m_dynamics.gradient);
dynaPref.minSize.setValue(m_dynamics.minSize);
dynaPref.minAngle.setValue(m_dynamics.minAngle);
dynaPref.minPressureThreshold(m_dynamics.minPressureThreshold);
dynaPref.minVelocityThreshold(m_dynamics.minVelocityThreshold);
dynaPref.maxPressureThreshold(m_dynamics.maxPressureThreshold);
dynaPref.maxVelocityThreshold(m_dynamics.maxVelocityThreshold);
dynaPref.colorFromTo(m_dynamics.colorFromTo);
dynaPref.matrixIndex(m_popup->ditheringIndex());
Preferences::instance().shared.shareDynamics(m_sameInAllTools);
}
void loadDynamicsPref() {
auto& dynaPref = Preferences::instance()
.tool(App::instance()->activeTool()).dynamics;
m_dynamics.stabilizer = dynaPref.stabilizer();
m_dynamics.stabilizerFactor = dynaPref.stabilizerFactor();
m_dynamics.size = dynaPref.size();
m_dynamics.angle = dynaPref.angle();
m_dynamics.gradient = dynaPref.gradient();
m_dynamics.minSize = dynaPref.minSize();
m_dynamics.minAngle = dynaPref.minAngle();
m_dynamics.minPressureThreshold = dynaPref.minPressureThreshold();
m_dynamics.minVelocityThreshold = dynaPref.minVelocityThreshold();
m_dynamics.maxPressureThreshold = dynaPref.maxPressureThreshold();
m_dynamics.maxVelocityThreshold = dynaPref.maxVelocityThreshold();
m_dynamics.colorFromTo = dynaPref.colorFromTo();
DitheringSelector matrixSel(DitheringSelector::SelectMatrix);
matrixSel.setSelectedItemIndex(dynaPref.matrixIndex());
render::DitheringMatrix matrix(matrixSel.ditheringMatrix());
m_dynamics.ditheringMatrix = matrix;
m_sameInAllTools = Preferences::instance().shared.shareDynamics();
}
private:
// DynamicsPopup::Delegate impl
doc::BrushRef getActiveBrush() override {
@ -1247,6 +1281,7 @@ private:
ContextBar* m_ctxBar;
mutable tools::DynamicsOptions m_dynamics;
bool m_optionsGridVisibility = true;
bool m_sameInAllTools = false;
};
class ContextBar::FreehandAlgorithmField : public CheckBox {

View File

@ -10,6 +10,8 @@
#include "app/ui/dynamics_popup.h"
#include "app/app.h"
#include "app/tools/tool_box.h"
#include "app/ui/dithering_selector.h"
#include "app/ui/skin/skin_theme.h"
#include "os/font.h"
@ -293,37 +295,39 @@ int DynamicsPopup::ditheringIndex() const {
return 0;
}
void DynamicsPopup::loadDynamicPref(ToolPreferences* toolPref) {
if (toolPref) {
auto& dynaPref = toolPref->dynamics;
m_dynamics->stabilizer()->setSelected(dynaPref.stabilizer());
m_stabilizerFactorBackup = dynaPref.stabilizerFactor();
m_dynamics->stabilizerFactor()->setValue(
dynaPref.stabilizer() ? m_stabilizerFactorBackup : 0);
m_dynamics->minSize()->setValue(dynaPref.minSize());
m_dynamics->minAngle()->setValue(dynaPref.minAngle());
m_pressureThreshold->minThreshold(dynaPref.minPressureThreshold());
m_pressureThreshold->maxThreshold(dynaPref.maxPressureThreshold());
m_velocityThreshold->minThreshold(dynaPref.minVelocityThreshold());
m_velocityThreshold->maxThreshold(dynaPref.maxVelocityThreshold());
m_fromTo = dynaPref.colorFromTo();
void DynamicsPopup::loadDynamicsPref() {
auto& dynaPref = Preferences::instance().tool(
App::instance()->activeTool()).dynamics;
m_dynamics->stabilizer()->setSelected(dynaPref.stabilizer());
m_stabilizerFactorBackup = dynaPref.stabilizerFactor();
m_dynamics->stabilizerFactor()->setValue(
dynaPref.stabilizer() ? m_stabilizerFactorBackup : 0);
m_dynamics->minSize()->setValue(dynaPref.minSize());
m_dynamics->minAngle()->setValue(dynaPref.minAngle());
m_pressureThreshold->minThreshold(dynaPref.minPressureThreshold());
m_pressureThreshold->maxThreshold(dynaPref.maxPressureThreshold());
m_velocityThreshold->minThreshold(dynaPref.minVelocityThreshold());
m_velocityThreshold->maxThreshold(dynaPref.maxVelocityThreshold());
m_fromTo = dynaPref.colorFromTo();
setCheck(SIZE_WITH_PRESSURE,
dynaPref.size() == tools::DynamicSensor::Pressure);
setCheck(SIZE_WITH_VELOCITY,
dynaPref.size() == tools::DynamicSensor::Velocity);
setCheck(ANGLE_WITH_PRESSURE,
dynaPref.angle() == tools::DynamicSensor::Pressure);
setCheck(ANGLE_WITH_VELOCITY,
dynaPref.angle() == tools::DynamicSensor::Velocity);
setCheck(GRADIENT_WITH_PRESSURE,
dynaPref.gradient() == tools::DynamicSensor::Pressure);
setCheck(GRADIENT_WITH_VELOCITY,
dynaPref.gradient() == tools::DynamicSensor::Velocity);
setCheck(SIZE_WITH_PRESSURE,
dynaPref.size() == tools::DynamicSensor::Pressure);
setCheck(SIZE_WITH_VELOCITY,
dynaPref.size() == tools::DynamicSensor::Velocity);
setCheck(ANGLE_WITH_PRESSURE,
dynaPref.angle() == tools::DynamicSensor::Pressure);
setCheck(ANGLE_WITH_VELOCITY,
dynaPref.angle() == tools::DynamicSensor::Velocity);
setCheck(GRADIENT_WITH_PRESSURE,
dynaPref.gradient() == tools::DynamicSensor::Pressure);
setCheck(GRADIENT_WITH_VELOCITY,
dynaPref.gradient() == tools::DynamicSensor::Velocity);
if (m_ditheringSel)
m_ditheringSel->setSelectedItemIndex(dynaPref.matrixIndex());
}
if (m_ditheringSel)
m_ditheringSel->setSelectedItemIndex(dynaPref.matrixIndex());
m_dynamics->sameInAllTools()->setSelected(
Preferences::instance().shared.shareDynamics());
}
tools::DynamicsOptions DynamicsPopup::getDynamics() const
@ -374,36 +378,12 @@ bool DynamicsPopup::isCheck(int i) const
->icon() == theme->parts.dropPixelsOk());
}
void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
// Update Pressure/Velocity/Gradient popup's variables visibility
// according ButtonSet checks.
// Used after a value change (onValuesChange) and when the popup is
// displayed (on ContextBar::DynamicsField::switchPopup()).
void DynamicsPopup::refreshVisibility()
{
auto theme = SkinTheme::get(this);
const skin::SkinPartPtr& ok = theme->parts.dropPixelsOk();
const int i = (item ? m_dynamics->values()->getItemIndex(item): -1);
// Switch item off
if (item && item->icon().get() == ok.get()) {
item->setIcon(nullptr);
}
else {
switch (i) {
case SIZE_WITH_PRESSURE:
case SIZE_WITH_VELOCITY:
setCheck(SIZE_WITH_PRESSURE, i == SIZE_WITH_PRESSURE);
setCheck(SIZE_WITH_VELOCITY, i == SIZE_WITH_VELOCITY);
break;
case ANGLE_WITH_PRESSURE:
case ANGLE_WITH_VELOCITY:
setCheck(ANGLE_WITH_PRESSURE, i == ANGLE_WITH_PRESSURE);
setCheck(ANGLE_WITH_VELOCITY, i == ANGLE_WITH_VELOCITY);
break;
case GRADIENT_WITH_PRESSURE:
case GRADIENT_WITH_VELOCITY:
setCheck(GRADIENT_WITH_PRESSURE, i == GRADIENT_WITH_PRESSURE);
setCheck(GRADIENT_WITH_VELOCITY, i == GRADIENT_WITH_VELOCITY);
break;
}
}
const bool hasPressure = (isCheck(SIZE_WITH_PRESSURE) ||
isCheck(ANGLE_WITH_PRESSURE) ||
isCheck(GRADIENT_WITH_PRESSURE));
@ -459,6 +439,44 @@ void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
setHotRegion(m_hotRegion);
}
bool DynamicsPopup::sharedSettings() const
{
return m_dynamics->sameInAllTools()->isSelected();
}
void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
{
auto theme = SkinTheme::get(this);
const skin::SkinPartPtr& ok = theme->parts.dropPixelsOk();
const int i = (item ? m_dynamics->values()->getItemIndex(item): -1);
// Switch item off
if (item && item->icon().get() == ok.get()) {
item->setIcon(nullptr);
}
else {
switch (i) {
case SIZE_WITH_PRESSURE:
case SIZE_WITH_VELOCITY:
setCheck(SIZE_WITH_PRESSURE, i == SIZE_WITH_PRESSURE);
setCheck(SIZE_WITH_VELOCITY, i == SIZE_WITH_VELOCITY);
break;
case ANGLE_WITH_PRESSURE:
case ANGLE_WITH_VELOCITY:
setCheck(ANGLE_WITH_PRESSURE, i == ANGLE_WITH_PRESSURE);
setCheck(ANGLE_WITH_VELOCITY, i == ANGLE_WITH_VELOCITY);
break;
case GRADIENT_WITH_PRESSURE:
case GRADIENT_WITH_VELOCITY:
setCheck(GRADIENT_WITH_PRESSURE, i == GRADIENT_WITH_PRESSURE);
setCheck(GRADIENT_WITH_VELOCITY, i == GRADIENT_WITH_VELOCITY);
break;
}
}
refreshVisibility();
}
void DynamicsPopup::updateFromToText()
{
m_dynamics->gradientFromTo()->setText(

View File

@ -37,8 +37,10 @@ namespace app {
tools::DynamicsOptions getDynamics() const;
void setOptionsGridVisibility(bool state);
void loadDynamicPref(ToolPreferences* toolPref);
void loadDynamicsPref();
int ditheringIndex() const;
void refreshVisibility();
bool sharedSettings() const;
private:
class ThresholdSlider;