Add save dynamics options between sessions (fix #3933)

This commit is contained in:
Gaspar Capello 2023-08-09 16:18:23 -03:00 committed by David Capello
parent 9755efece4
commit 2cc15cda9e
7 changed files with 130 additions and 32 deletions

View File

@ -459,6 +459,21 @@
<option id="refer_to" type="FillReferTo" default="FillReferTo::ACTIVE_LAYER" /> <option id="refer_to" type="FillReferTo" default="FillReferTo::ACTIVE_LAYER" />
<option id="pixel_connectivity" type="PixelConnectivity" default="PixelConnectivity::FOUR_CONNECTED" /> <option id="pixel_connectivity" type="PixelConnectivity" default="PixelConnectivity::FOUR_CONNECTED" />
</section> </section>
<section id="dynamics">
<option id="stabilizer" type="bool" default="false" />
<option id="stabilizer_factor" type="int" default="0" />
<option id="size" type="app::tools::DynamicSensor" default="app::tools::DynamicSensor::Static" />
<option id="angle" type="app::tools::DynamicSensor" default="app::tools::DynamicSensor::Static" />
<option id="gradient" type="app::tools::DynamicSensor" default="app::tools::DynamicSensor::Static" />
<option id="min_size" type="int" default="1" />
<option id="min_angle" type="int" default="1" />
<option id="color_from_to" type="app::tools::ColorFromTo" default="app::tools::ColorFromTo::BgToFg" />
<option id="matrix_index" type="int" default="0" />
<option id="min_pressure_threshold" type="double" default="0.1" />
<option id="max_pressure_threshold" type="double" default="0.9" />
<option id="min_velocity_threshold" type="double" default="0.1" />
<option id="max_velocity_threshold" type="double" default="0.9" />
</section>
</tool> </tool>
<document> <document>

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -15,6 +15,7 @@
#include "app/pref/option.h" #include "app/pref/option.h"
#include "app/sprite_sheet_data_format.h" #include "app/sprite_sheet_data_format.h"
#include "app/sprite_sheet_type.h" #include "app/sprite_sheet_type.h"
#include "app/tools/dynamics.h"
#include "app/tools/freehand_algorithm.h" #include "app/tools/freehand_algorithm.h"
#include "app/tools/ink_type.h" #include "app/tools/ink_type.h"
#include "app/tools/rotation_algorithm.h" #include "app/tools/rotation_algorithm.h"

View File

@ -14,6 +14,7 @@
#include "app/script/docobj.h" #include "app/script/docobj.h"
#include "app/script/engine.h" #include "app/script/engine.h"
#include "app/script/luacpp.h" #include "app/script/luacpp.h"
#include "app/tools/dynamics.h"
#include "doc/frame.h" #include "doc/frame.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/remap.h" #include "doc/remap.h"
@ -333,6 +334,8 @@ FOR_ENUM(app::gen::SymmetryMode)
FOR_ENUM(app::gen::TimelinePosition) FOR_ENUM(app::gen::TimelinePosition)
FOR_ENUM(app::gen::ToGrayAlgorithm) FOR_ENUM(app::gen::ToGrayAlgorithm)
FOR_ENUM(app::gen::WindowColorProfile) FOR_ENUM(app::gen::WindowColorProfile)
FOR_ENUM(app::tools::ColorFromTo)
FOR_ENUM(app::tools::DynamicSensor)
FOR_ENUM(app::tools::FreehandAlgorithm) FOR_ENUM(app::tools::FreehandAlgorithm)
FOR_ENUM(app::tools::RotationAlgorithm) FOR_ENUM(app::tools::RotationAlgorithm)
FOR_ENUM(doc::AniDir) FOR_ENUM(doc::AniDir)

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020-2021 Igara Studio S.A. // Copyright (C) 2020-2023 Igara Studio S.A.
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -25,6 +25,7 @@ namespace tools {
}; };
struct DynamicsOptions { struct DynamicsOptions {
bool stabilizer = false;
int stabilizerFactor = 0; int stabilizerFactor = 0;
DynamicSensor size = DynamicSensor::Static; DynamicSensor size = DynamicSensor::Static;
DynamicSensor angle = DynamicSensor::Static; DynamicSensor angle = DynamicSensor::Static;

View File

@ -1151,6 +1151,38 @@ public:
: ButtonSet(1) : ButtonSet(1)
, m_ctxBar(ctxBar) { , m_ctxBar(ctxBar) {
addItem(SkinTheme::get(this)->parts.dynamics(), "dynamics_field"); 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());
});
} }
void switchPopup() { void switchPopup() {
@ -1160,16 +1192,6 @@ public:
return; return;
} }
if (!m_popup) {
m_popup.reset(new DynamicsPopup(this));
m_popup->setOptionsGridVisibility(m_optionsGridVisibility);
m_popup->Close.connect(
[this](CloseEvent&){
deselectItems();
m_dynamics = m_popup->getDynamics();
});
}
const gfx::Rect bounds = this->bounds(); const gfx::Rect bounds = this->bounds();
m_popup->remapWindow(); m_popup->remapWindow();
fit_bounds(display(), m_popup.get(), fit_bounds(display(), m_popup.get(),

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020-2022 Igara Studio S.A. // Copyright (C) 2020-2023 Igara Studio S.A.
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -58,6 +58,8 @@ public:
float minThreshold() const { return m_minThreshold; } float minThreshold() const { return m_minThreshold; }
float maxThreshold() const { return m_maxThreshold; } float maxThreshold() const { return m_maxThreshold; }
void minThreshold(float min) { m_minThreshold = min; }
void maxThreshold(float max) { m_maxThreshold = max; }
void setSensorValue(float v) { void setSensorValue(float v) {
m_sensorValue = v; m_sensorValue = v;
invalidate(); invalidate();
@ -211,20 +213,37 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
PopupWindow::EnterBehavior::DoNothingOnEnter) PopupWindow::EnterBehavior::DoNothingOnEnter)
, m_delegate(delegate) , m_delegate(delegate)
, m_dynamics(new gen::Dynamics) , m_dynamics(new gen::Dynamics)
, m_stabilizerFactorBackup(0)
, m_ditheringSel(new DitheringSelector(DitheringSelector::SelectMatrix)) , m_ditheringSel(new DitheringSelector(DitheringSelector::SelectMatrix))
, m_fromTo(tools::ColorFromTo::BgToFg) , m_fromTo(tools::ColorFromTo::BgToFg)
{ {
m_dynamics->stabilizer()->Click.connect( m_dynamics->stabilizer()->Click.connect(
[this](){ [this](){
if (m_dynamics->stabilizer()->isSelected() && if (m_dynamics->stabilizer()->isSelected()) {
m_dynamics->stabilizerFactor()->getValue() == 0) { if (m_stabilizerFactorBackup == 0) {
// TODO default value when we enable stabilizer when it's zero // TODO default value when we enable stabilizer when it's
m_dynamics->stabilizerFactor()->setValue(16); // zero
m_dynamics->stabilizerFactor()->setValue(16);
m_stabilizerFactorBackup = 16;
}
else {
m_dynamics->stabilizerFactor()->setValue(
m_stabilizerFactorBackup);
}
} }
else {
m_stabilizerFactorBackup =
m_dynamics->stabilizerFactor()->getValue();
m_dynamics->stabilizerFactor()->setValue(0);
}
}); });
m_dynamics->stabilizerFactor()->Change.connect( m_dynamics->stabilizerFactor()->Change.connect(
[this](){ [this](){
m_dynamics->stabilizer()->setSelected(m_dynamics->stabilizerFactor()->getValue() > 0); m_stabilizerFactorBackup =
m_dynamics->stabilizerFactor()->getValue();
m_dynamics->stabilizer()->setSelected(
m_stabilizerFactorBackup > 0);
}); });
m_dynamics->values()->ItemChange.connect( m_dynamics->values()->ItemChange.connect(
@ -259,8 +278,6 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
m_dynamics->pressurePlaceholder()->addChild(m_pressureThreshold = new ThresholdSlider); m_dynamics->pressurePlaceholder()->addChild(m_pressureThreshold = new ThresholdSlider);
m_dynamics->velocityPlaceholder()->addChild(m_velocityThreshold = new ThresholdSlider); m_dynamics->velocityPlaceholder()->addChild(m_velocityThreshold = new ThresholdSlider);
addChild(m_dynamics); addChild(m_dynamics);
onValuesChange(nullptr);
} }
void DynamicsPopup::setOptionsGridVisibility(bool state) void DynamicsPopup::setOptionsGridVisibility(bool state)
@ -270,16 +287,51 @@ void DynamicsPopup::setOptionsGridVisibility(bool state)
expandWindow(sizeHint()); expandWindow(sizeHint());
} }
int DynamicsPopup::ditheringIndex() const {
if (m_ditheringSel)
return m_ditheringSel->getSelectedItemIndex();
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();
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());
}
}
tools::DynamicsOptions DynamicsPopup::getDynamics() const tools::DynamicsOptions DynamicsPopup::getDynamics() const
{ {
tools::DynamicsOptions opts; tools::DynamicsOptions opts;
if (m_dynamics->stabilizer()->isSelected()) { opts.stabilizer = m_dynamics->stabilizer()->isSelected();
opts.stabilizerFactor = m_dynamics->stabilizerFactor()->getValue(); opts.stabilizerFactor = m_stabilizerFactorBackup;
}
else {
opts.stabilizerFactor = 0;
}
opts.size = opts.size =
(isCheck(SIZE_WITH_PRESSURE) ? tools::DynamicSensor::Pressure: (isCheck(SIZE_WITH_PRESSURE) ? tools::DynamicSensor::Pressure:
@ -367,9 +419,7 @@ void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
const bool any = (needsSize || needsAngle || needsGradient); const bool any = (needsSize || needsAngle || needsGradient);
doc::BrushRef brush = m_delegate->getActiveBrush(); doc::BrushRef brush = m_delegate->getActiveBrush();
if (needsSize && !m_dynamics->minSize()->isVisible()) { if (needsSize) {
m_dynamics->minSize()->setValue(1);
int maxSize = brush->size(); int maxSize = brush->size();
if (maxSize == 1) { if (maxSize == 1) {
// If brush size == 1, we put it to 4 so the user has some size // If brush size == 1, we put it to 4 so the user has some size
@ -383,8 +433,7 @@ void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
m_dynamics->minSize()->setVisible(needsSize); m_dynamics->minSize()->setVisible(needsSize);
m_dynamics->maxSize()->setVisible(needsSize); m_dynamics->maxSize()->setVisible(needsSize);
if (needsAngle && !m_dynamics->minAngle()->isVisible()) { if (needsAngle) {
m_dynamics->minAngle()->setValue(brush->angle());
m_dynamics->maxAngle()->setValue(brush->angle()); m_dynamics->maxAngle()->setValue(brush->angle());
} }
m_dynamics->angleLabel()->setVisible(needsAngle); m_dynamics->angleLabel()->setVisible(needsAngle);

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020-2021 Igara Studio S.A. // Copyright (C) 2020-2023 Igara Studio S.A.
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -8,6 +8,7 @@
#define APP_UI_DYNAMICS_POPUP_H_INCLUDED #define APP_UI_DYNAMICS_POPUP_H_INCLUDED
#pragma once #pragma once
#include "app/pref/preferences.h"
#include "app/tools/dynamics.h" #include "app/tools/dynamics.h"
#include "app/tools/velocity.h" #include "app/tools/velocity.h"
#include "app/ui/button_set.h" #include "app/ui/button_set.h"
@ -36,6 +37,8 @@ namespace app {
tools::DynamicsOptions getDynamics() const; tools::DynamicsOptions getDynamics() const;
void setOptionsGridVisibility(bool state); void setOptionsGridVisibility(bool state);
void loadDynamicPref(ToolPreferences* toolPref);
int ditheringIndex() const;
private: private:
class ThresholdSlider; class ThresholdSlider;
@ -49,6 +52,10 @@ namespace app {
Delegate* m_delegate; Delegate* m_delegate;
gen::Dynamics* m_dynamics; gen::Dynamics* m_dynamics;
// Used to memorize the 'stabilizer factor' slider value.
// This helps to save the 'stabilizer factor' even if
// 'stabilizer' check isn't selected.
int m_stabilizerFactorBackup;
DitheringSelector* m_ditheringSel; DitheringSelector* m_ditheringSel;
gfx::Region m_hotRegion; gfx::Region m_hotRegion;
ThresholdSlider* m_pressureThreshold; ThresholdSlider* m_pressureThreshold;