mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-29 21:33:12 +00:00
Several fixed for Dynamics
* Now the max value in dynamics is equal to the brush size, so changing the brush size (e.g. switching between pencil/eraser tools) will change the max brush size (which is the most common parameter to change) * Added mini_slider style for min/max brush/angle values * Fixed some issues clicking outside the dynamics popup
This commit is contained in:
parent
cc7bdbcec4
commit
263236deb3
@ -964,5 +964,10 @@
|
||||
<text color="slider_empty_text" align="center middle" />
|
||||
<text color="slider_empty_text" align="center middle" state="focus" y="1" />
|
||||
</style>
|
||||
<style id="mini_slider" extends="slider" font="mini" padding-top="1" padding-bottom="3">
|
||||
<background part="mini_slider_empty" />
|
||||
<text color="slider_empty_text" align="center middle" />
|
||||
<text color="slider_empty_text" align="center middle" state="focus" />
|
||||
</style>
|
||||
</styles>
|
||||
</theme>
|
||||
|
@ -536,13 +536,17 @@ angle_tooltip = <<<END
|
||||
Change the brush angle
|
||||
depending on the sensor value
|
||||
END
|
||||
min_size_tooltip = Brush size when the sensor has its minimum value
|
||||
max_size_tooltip = Brush size when the sensor has its maximum value
|
||||
min_angle_tooltip = Brush angle when the sensor has its minimum value
|
||||
max_angle_tooltip = Brush angle when the sensor has its maximum value
|
||||
gradient = Gradient
|
||||
gradient_tooltip = <<<END
|
||||
Gradient between foreground
|
||||
and background colors
|
||||
END
|
||||
max_point_value = Max Point Value:
|
||||
sensors_tweaks = Sensor Tweaks
|
||||
max_point_value = Min/Max Values
|
||||
sensors_tweaks = Sensor Threshold
|
||||
|
||||
[export_file]
|
||||
title = Export File
|
||||
|
@ -25,14 +25,23 @@
|
||||
<grid id="options" columns="2" childspacing="0" expansive="true">
|
||||
<separator id="separator" text="@.max_point_value" horizontal="true" cell_hspan="2" />
|
||||
|
||||
<label id="max_size_label" text="@.size" style="mini_label" />
|
||||
<slider id="max_size" value="64" min="1" max="64" cell_align="horizontal" />
|
||||
<label id="size_label" text="@.size" style="mini_label" />
|
||||
<slider id="min_size" value="1" min="1" max="64" cell_align="horizontal" style="mini_slider"
|
||||
tooltip="@.min_size_tooltip" tooltip_dir="left" />
|
||||
<boxfiller />
|
||||
<slider id="max_size" value="64" min="1" max="64" cell_align="horizontal" style="mini_slider"
|
||||
tooltip="@.max_size_tooltip" tooltip_dir="left" />
|
||||
|
||||
<label id="max_angle_label" text="@.angle" style="mini_label" />
|
||||
<slider id="max_angle" value="0" min="-180" max="+180" cell_align="horizontal" />
|
||||
<label id="angle_label" text="@.angle" style="mini_label" />
|
||||
<slider id="min_angle" value="0" min="-180" max="+180" cell_align="horizontal" style="mini_slider"
|
||||
tooltip="@.min_angle_tooltip" tooltip_dir="left" />
|
||||
<boxfiller />
|
||||
<slider id="max_angle" value="0" min="-180" max="+180" cell_align="horizontal" style="mini_slider"
|
||||
tooltip="@.max_angle_tooltip" tooltip_dir="left" />
|
||||
|
||||
<label id="gradient_label" text="@.gradient" style="mini_label" />
|
||||
<hbox id="gradient_placeholder" />
|
||||
<hbox id="gradient_placeholder" cell_vspan="2" />
|
||||
<boxfiller />
|
||||
|
||||
<separator id="separator2" text="@.sensors_tweaks" horizontal="true" cell_hspan="2" />
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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<MouseMessage*>(msg);
|
||||
auto picked = manager()->pick(mouseMsg->position());
|
||||
if ((picked == nullptr) ||
|
||||
(picked->window() != this &&
|
||||
picked->window() != m_ditheringSel->getWindowWidget())) {
|
||||
closeWindow(nullptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user