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:
2cc15cda9e82611b3af7bb3cab07cf2fdc479b42
(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" /> <param name="type" value="shading" />
</item> </item>
<separator /> <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> </menu>
</menus> </menus>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2019-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
@ -169,7 +169,7 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
void ToolLoopManager::movement(Pointer pointer) void ToolLoopManager::movement(Pointer pointer)
{ {
// Filter points with the stabilizer // 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 double f = m_dynamics.stabilizerFactor;
const gfx::Point delta = (pointer.point() - m_stabilizerCenter); const gfx::Point delta = (pointer.point() - m_stabilizerCenter);
const double distance = std::sqrt(delta.x*delta.x + delta.y*delta.y); const double distance = std::sqrt(delta.x*delta.x + delta.y*delta.y);

View File

@ -1152,37 +1152,7 @@ public:
, 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 loadDynamicsPref();
// 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() {
@ -1192,6 +1162,28 @@ public:
return; 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(); const gfx::Rect bounds = this->bounds();
m_popup->remapWindow(); m_popup->remapWindow();
fit_bounds(display(), m_popup.get(), fit_bounds(display(), m_popup.get(),
@ -1214,6 +1206,48 @@ public:
m_popup->setOptionsGridVisibility(state); 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: private:
// DynamicsPopup::Delegate impl // DynamicsPopup::Delegate impl
doc::BrushRef getActiveBrush() override { doc::BrushRef getActiveBrush() override {
@ -1247,6 +1281,7 @@ private:
ContextBar* m_ctxBar; ContextBar* m_ctxBar;
mutable tools::DynamicsOptions m_dynamics; mutable tools::DynamicsOptions m_dynamics;
bool m_optionsGridVisibility = true; bool m_optionsGridVisibility = true;
bool m_sameInAllTools = false;
}; };
class ContextBar::FreehandAlgorithmField : public CheckBox { class ContextBar::FreehandAlgorithmField : public CheckBox {

View File

@ -10,6 +10,8 @@
#include "app/ui/dynamics_popup.h" #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/dithering_selector.h"
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "os/font.h" #include "os/font.h"
@ -293,9 +295,9 @@ int DynamicsPopup::ditheringIndex() const {
return 0; return 0;
} }
void DynamicsPopup::loadDynamicPref(ToolPreferences* toolPref) { void DynamicsPopup::loadDynamicsPref() {
if (toolPref) { auto& dynaPref = Preferences::instance().tool(
auto& dynaPref = toolPref->dynamics; App::instance()->activeTool()).dynamics;
m_dynamics->stabilizer()->setSelected(dynaPref.stabilizer()); m_dynamics->stabilizer()->setSelected(dynaPref.stabilizer());
m_stabilizerFactorBackup = dynaPref.stabilizerFactor(); m_stabilizerFactorBackup = dynaPref.stabilizerFactor();
m_dynamics->stabilizerFactor()->setValue( m_dynamics->stabilizerFactor()->setValue(
@ -323,7 +325,9 @@ void DynamicsPopup::loadDynamicPref(ToolPreferences* toolPref) {
if (m_ditheringSel) if (m_ditheringSel)
m_ditheringSel->setSelectedItemIndex(dynaPref.matrixIndex()); m_ditheringSel->setSelectedItemIndex(dynaPref.matrixIndex());
}
m_dynamics->sameInAllTools()->setSelected(
Preferences::instance().shared.shareDynamics());
} }
tools::DynamicsOptions DynamicsPopup::getDynamics() const tools::DynamicsOptions DynamicsPopup::getDynamics() const
@ -374,36 +378,12 @@ bool DynamicsPopup::isCheck(int i) const
->icon() == theme->parts.dropPixelsOk()); ->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) || const bool hasPressure = (isCheck(SIZE_WITH_PRESSURE) ||
isCheck(ANGLE_WITH_PRESSURE) || isCheck(ANGLE_WITH_PRESSURE) ||
isCheck(GRADIENT_WITH_PRESSURE)); isCheck(GRADIENT_WITH_PRESSURE));
@ -459,6 +439,44 @@ void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
setHotRegion(m_hotRegion); 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() void DynamicsPopup::updateFromToText()
{ {
m_dynamics->gradientFromTo()->setText( m_dynamics->gradientFromTo()->setText(

View File

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