diff --git a/data/extensions/aseprite-theme/theme.xml b/data/extensions/aseprite-theme/theme.xml
index bf2d027c5..e7067f6e9 100644
--- a/data/extensions/aseprite-theme/theme.xml
+++ b/data/extensions/aseprite-theme/theme.xml
@@ -964,5 +964,10 @@
+
diff --git a/data/strings/en.ini b/data/strings/en.ini
index 080609a71..616b09d9e 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -536,13 +536,17 @@ angle_tooltip = <<
-
-
+
+
+
+
-
-
+
+
+
+
-
+
+
diff --git a/src/app/tools/dynamics.h b/src/app/tools/dynamics.h
index 45efc3d5e..95a1f4fc7 100644
--- a/src/app/tools/dynamics.h
+++ b/src/app/tools/dynamics.h
@@ -23,8 +23,8 @@ namespace tools {
DynamicSensor size = DynamicSensor::Static;
DynamicSensor angle = DynamicSensor::Static;
DynamicSensor gradient = DynamicSensor::Static;
- int maxSize = 0;
- int maxAngle = 0;
+ int minSize = 0;
+ int minAngle = 0;
render::DitheringMatrix ditheringMatrix;
float minPressureThreshold = 0.0f, maxPressureThreshold = 1.0f;
float minVelocityThreshold = 0.0f, maxVelocityThreshold = 1.0f;
diff --git a/src/app/tools/intertwiners.h b/src/app/tools/intertwiners.h
index b67902217..ccf1b1ad3 100644
--- a/src/app/tools/intertwiners.h
+++ b/src/app/tools/intertwiners.h
@@ -111,7 +111,7 @@ public:
if (stroke.size() == 0)
return;
else if (stroke.size() == 1) {
- doPointshapePoint(stroke[0].x, stroke[0].y, loop);
+ doPointshapeStrokePt(stroke[0], loop);
}
else {
Stroke pts;
diff --git a/src/app/tools/point_shapes.h b/src/app/tools/point_shapes.h
index d772be0b4..04113729f 100644
--- a/src/app/tools/point_shapes.h
+++ b/src/app/tools/point_shapes.h
@@ -82,8 +82,8 @@ public:
if (m_hasDynamicGradient &&
m_dynamics.ditheringMatrix.rows() == 1 &&
m_dynamics.ditheringMatrix.cols() == 1) {
- color_t a = m_primaryColor;
- color_t b = m_secondaryColor;
+ color_t a = m_secondaryColor;
+ color_t b = m_primaryColor;
const float t = pt.gradient;
const float ti = 1.0f - pt.gradient;
switch (loop->sprite()->pixelFormat()) {
@@ -146,8 +146,8 @@ public:
loop->sprite()->pixelFormat(),
m_dynamics.ditheringMatrix,
pt.gradient,
- m_primaryColor,
- m_secondaryColor);
+ m_secondaryColor,
+ m_primaryColor);
prepareInk = true;
}
m_lastGradientValue = pt.gradient;
diff --git a/src/app/tools/tool_loop_manager.cpp b/src/app/tools/tool_loop_manager.cpp
index 73fd03052..a925677f8 100644
--- a/src/app/tools/tool_loop_manager.cpp
+++ b/src/app/tools/tool_loop_manager.cpp
@@ -104,7 +104,7 @@ void ToolLoopManager::pressButton(const Pointer& pointer)
return;
}
- Stroke::Pt spritePoint = getSpriteStrokePt(pointer, true);
+ Stroke::Pt spritePoint = getSpriteStrokePt(pointer);
m_toolLoop->getController()->pressButton(m_toolLoop, m_stroke, spritePoint);
std::string statusText;
@@ -134,7 +134,7 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
if (isCanceled())
return false;
- Stroke::Pt spritePoint = getSpriteStrokePt(pointer, false);
+ Stroke::Pt spritePoint = getSpriteStrokePt(pointer);
bool res = m_toolLoop->getController()->releaseButton(m_stroke, spritePoint);
if (!res && (m_toolLoop->getTracePolicy() == TracePolicy::Last ||
@@ -156,7 +156,7 @@ void ToolLoopManager::movement(const Pointer& pointer)
if (isCanceled())
return;
- Stroke::Pt spritePoint = getSpriteStrokePt(pointer, false);
+ Stroke::Pt spritePoint = getSpriteStrokePt(pointer);
m_toolLoop->getController()->movement(m_toolLoop, m_stroke, spritePoint);
std::string statusText;
@@ -365,8 +365,7 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
}
}
-Stroke::Pt ToolLoopManager::getSpriteStrokePt(const Pointer& pointer,
- const bool firstPoint)
+Stroke::Pt ToolLoopManager::getSpriteStrokePt(const Pointer& pointer)
{
// Convert the screen point to a sprite point
Stroke::Pt spritePoint = pointer.point();
@@ -445,26 +444,25 @@ void ToolLoopManager::adjustPointWithDynamics(const Pointer& pointer,
switch (m_dynamics.size) {
case DynamicSensor::Pressure:
- if (hasP) size = (1.0f-p)*size + p*m_dynamics.maxSize;
+ if (hasP) size = (1.0f-p)*m_dynamics.minSize + p*size;
break;
case DynamicSensor::Velocity:
- size = (1.0f-v)*size + v*m_dynamics.maxSize;
+ size = (1.0f-v)*m_dynamics.minSize + v*size;
break;
}
switch (m_dynamics.angle) {
case DynamicSensor::Pressure:
- if (hasP) angle = (1.0f-p)*angle + p*m_dynamics.maxAngle;
+ if (hasP) angle = (1.0f-p)*m_dynamics.minAngle + p*angle;
break;
case DynamicSensor::Velocity:
- angle = (1.0f-v)*angle + v*m_dynamics.maxAngle;
+ angle = (1.0f-v)*m_dynamics.minAngle + v*angle;
break;
}
switch (m_dynamics.gradient) {
case DynamicSensor::Pressure:
- if (hasP)
- pt.gradient = p;
+ pt.gradient = p;
break;
case DynamicSensor::Velocity:
pt.gradient = v;
diff --git a/src/app/tools/tool_loop_manager.h b/src/app/tools/tool_loop_manager.h
index 0bdc8a583..3b6ad4dde 100644
--- a/src/app/tools/tool_loop_manager.h
+++ b/src/app/tools/tool_loop_manager.h
@@ -74,8 +74,7 @@ public:
private:
void doLoopStep(bool lastStep);
void snapToGrid(Stroke::Pt& pt);
- Stroke::Pt getSpriteStrokePt(const Pointer& pointer,
- const bool firstPoint);
+ Stroke::Pt getSpriteStrokePt(const Pointer& pointer);
bool useDynamics() const;
void adjustPointWithDynamics(const Pointer& pointer, Stroke::Pt& pt);
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 45ff162bb..d5bb123f4 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -259,7 +259,7 @@ private:
class ContextBar::BrushAngleField : public IntEntry {
public:
BrushAngleField(BrushTypeField* brushType)
- : IntEntry(0, 180)
+ : IntEntry(-180, 180)
, m_brushType(brushType) {
setSuffix("\xc2\xb0");
}
@@ -1013,6 +1013,16 @@ private:
return m_ctxBar->activeBrush();
}
+ void setMaxSize(int size) override {
+ Tool* tool = App::instance()->activeTool();
+ Preferences::instance().tool(tool).brush.size(size);
+ }
+
+ void setMaxAngle(int angle) override {
+ Tool* tool = App::instance()->activeTool();
+ Preferences::instance().tool(tool).brush.angle(angle);
+ }
+
void onItemChange(Item* item) override {
ButtonSet::onItemChange(item);
switchPopup();
diff --git a/src/app/ui/dynamics_popup.cpp b/src/app/ui/dynamics_popup.cpp
index 9107bc692..2aa3b81dc 100644
--- a/src/app/ui/dynamics_popup.cpp
+++ b/src/app/ui/dynamics_popup.cpp
@@ -49,10 +49,10 @@ enum {
} // anonymous namespace
-// Special slider to set min/max values of a sensor
-class DynamicsPopup::MinMaxSlider : public Widget {
+// Special slider to set the min/max threshold values of a sensor
+class DynamicsPopup::ThresholdSlider : public Widget {
public:
- MinMaxSlider() {
+ ThresholdSlider() {
setExpansive(true);
initTheme();
}
@@ -218,10 +218,18 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
[this](ButtonSet::Item* item){
onValuesChange(item);
});
+ m_dynamics->maxSize()->Change.connect(
+ [this]{
+ m_delegate->setMaxSize(m_dynamics->maxSize()->getValue());
+ });
+ m_dynamics->maxAngle()->Change.connect(
+ [this]{
+ m_delegate->setMaxAngle(m_dynamics->maxAngle()->getValue());
+ });
m_dynamics->gradientPlaceholder()->addChild(m_ditheringSel);
- m_dynamics->pressurePlaceholder()->addChild(m_pressureTweaks = new MinMaxSlider);
- m_dynamics->velocityPlaceholder()->addChild(m_velocityTweaks = new MinMaxSlider);
+ m_dynamics->pressurePlaceholder()->addChild(m_pressureThreshold = new ThresholdSlider);
+ m_dynamics->velocityPlaceholder()->addChild(m_velocityThreshold = new ThresholdSlider);
addChild(m_dynamics);
onValuesChange(nullptr);
@@ -242,14 +250,14 @@ tools::DynamicsOptions DynamicsPopup::getDynamics() const
(isCheck(GRADIENT_WITH_PRESSURE) ? tools::DynamicSensor::Pressure:
isCheck(GRADIENT_WITH_VELOCITY) ? tools::DynamicSensor::Velocity:
tools::DynamicSensor::Static);
- opts.maxSize = m_dynamics->maxSize()->getValue();
- opts.maxAngle = m_dynamics->maxAngle()->getValue();
+ opts.minSize = m_dynamics->minSize()->getValue();
+ opts.minAngle = m_dynamics->minAngle()->getValue();
opts.ditheringMatrix = m_ditheringSel->ditheringMatrix();
- opts.minPressureThreshold = m_pressureTweaks->minThreshold();
- opts.maxPressureThreshold = m_pressureTweaks->maxThreshold();
- opts.minVelocityThreshold = m_velocityTweaks->minThreshold();
- opts.maxVelocityThreshold = m_velocityTweaks->maxThreshold();
+ opts.minPressureThreshold = m_pressureThreshold->minThreshold();
+ opts.maxPressureThreshold = m_pressureThreshold->maxThreshold();
+ opts.minVelocityThreshold = m_velocityThreshold->minThreshold();
+ opts.maxVelocityThreshold = m_velocityThreshold->maxThreshold();
return opts;
}
@@ -315,17 +323,28 @@ void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
const bool any = (needsSize || needsAngle || needsGradient);
doc::BrushRef brush = m_delegate->getActiveBrush();
- if (needsSize && !m_dynamics->maxSize()->isVisible()) {
- m_dynamics->maxSize()->setValue(
- base::clamp(std::max(2*brush->size(), 4), 1, 64));
+ if (needsSize && !m_dynamics->minSize()->isVisible()) {
+ m_dynamics->minSize()->setValue(1);
+
+ int maxSize = brush->size();
+ if (maxSize == 1) {
+ // If brush size == 1, we put it to 4 so the user has some size
+ // change by default.
+ maxSize = 4;
+ m_delegate->setMaxSize(maxSize);
+ }
+ m_dynamics->maxSize()->setValue(maxSize);
}
- m_dynamics->maxSizeLabel()->setVisible(needsSize);
+ m_dynamics->sizeLabel()->setVisible(needsSize);
+ m_dynamics->minSize()->setVisible(needsSize);
m_dynamics->maxSize()->setVisible(needsSize);
- if (needsAngle && !m_dynamics->maxAngle()->isVisible()) {
+ if (needsAngle && !m_dynamics->minAngle()->isVisible()) {
+ m_dynamics->minAngle()->setValue(brush->angle());
m_dynamics->maxAngle()->setValue(brush->angle());
}
- m_dynamics->maxAngleLabel()->setVisible(needsAngle);
+ m_dynamics->angleLabel()->setVisible(needsAngle);
+ m_dynamics->minAngle()->setVisible(needsAngle);
m_dynamics->maxAngle()->setVisible(needsAngle);
m_dynamics->gradientLabel()->setVisible(needsGradient);
@@ -358,12 +377,14 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
m_hotRegion = gfx::Region(bounds());
setHotRegion(m_hotRegion);
manager()->addMessageFilter(kMouseMoveMessage, this);
+ manager()->addMessageFilter(kMouseDownMessage, this);
disableFlags(IGNORE_MOUSE);
break;
case kCloseMessage:
m_hotRegion.clear();
manager()->removeMessageFilter(kMouseMoveMessage, this);
+ manager()->removeMessageFilter(kMouseDownMessage, this);
break;
case kMouseEnterMessage:
@@ -376,7 +397,7 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
if (mouseMsg->pointerType() == PointerType::Pen ||
mouseMsg->pointerType() == PointerType::Eraser) {
if (m_dynamics->pressurePlaceholder()->isVisible()) {
- m_pressureTweaks->setSensorValue(mouseMsg->pressure());
+ m_pressureThreshold->setSensorValue(mouseMsg->pressure());
}
}
@@ -387,7 +408,18 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
/ tools::VelocitySensor::kScreenPixelsForFullVelocity;
v = base::clamp(v, 0.0f, 1.0f);
- m_velocityTweaks->setSensorValue(v);
+ m_velocityThreshold->setSensorValue(v);
+ }
+ break;
+ }
+
+ case kMouseDownMessage: {
+ auto mouseMsg = static_cast(msg);
+ auto picked = manager()->pick(mouseMsg->position());
+ if ((picked == nullptr) ||
+ (picked->window() != this &&
+ picked->window() != m_ditheringSel->getWindowWidget())) {
+ closeWindow(nullptr);
}
break;
}
diff --git a/src/app/ui/dynamics_popup.h b/src/app/ui/dynamics_popup.h
index 7970f9dc0..3bc4a80cc 100644
--- a/src/app/ui/dynamics_popup.h
+++ b/src/app/ui/dynamics_popup.h
@@ -29,13 +29,15 @@ namespace app {
public:
virtual ~Delegate() { }
virtual doc::BrushRef getActiveBrush() = 0;
+ virtual void setMaxSize(int size) = 0;
+ virtual void setMaxAngle(int angle) = 0;
};
DynamicsPopup(Delegate* delegate);
tools::DynamicsOptions getDynamics() const;
private:
- class MinMaxSlider;
+ class ThresholdSlider;
void setCheck(int i, bool state);
bool isCheck(int i) const;
@@ -46,8 +48,8 @@ namespace app {
gen::Dynamics* m_dynamics;
DitheringSelector* m_ditheringSel;
gfx::Region m_hotRegion;
- MinMaxSlider* m_pressureTweaks;
- MinMaxSlider* m_velocityTweaks;
+ ThresholdSlider* m_pressureThreshold;
+ ThresholdSlider* m_velocityThreshold;
tools::VelocitySensor m_velocity;
};
diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp
index 0bf316fff..8aa2f6cb5 100644
--- a/src/app/ui/skin/skin_theme.cpp
+++ b/src/app/ui/skin/skin_theme.cpp
@@ -69,7 +69,8 @@ struct app::skin::SkinTheme::BackwardCompatibility {
}
void createMissingStyles(SkinTheme* theme) {
if (!hasSliderStyle &&
- theme->styles.slider()) {
+ theme->styles.slider() &&
+ theme->styles.miniSlider()) {
// Old slider style
ui::Style style(nullptr);
os::Font* font = theme->getDefaultFont();
@@ -86,6 +87,7 @@ struct app::skin::SkinTheme::BackwardCompatibility {
part->bitmapS()->height()-1*guiscale()+h/2));
*theme->styles.slider() = style;
+ *theme->styles.miniSlider() = style;
}
}
};
diff --git a/src/ui/combobox.h b/src/ui/combobox.h
index 9e4ba8d04..5ba75b008 100644
--- a/src/ui/combobox.h
+++ b/src/ui/combobox.h
@@ -1,5 +1,5 @@
// Aseprite UI Library
-// Copyright (C) 2019 Igara Studio S.A.
+// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@@ -81,6 +81,7 @@ namespace ui {
Entry* getEntryWidget();
Button* getButtonWidget();
+ Window* getWindowWidget() { return m_window; }
void openListBox();
void closeListBox();