mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-30 15:32:38 +00:00
Added pen pressure sensitivity (fix #710)
- Added support to detect eraser tip on Linux (#610) - Related to #139 - Still needs works for gradients and better brush interpolations between stroke points - Requested several times, e.g. https://community.aseprite.org/t/1077 https://community.aseprite.org/t/1881, steam forum, etc.
This commit is contained in:
parent
5affdbbae1
commit
79f9e28ce8
10
.travis.yml
10
.travis.yml
@ -11,7 +11,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev ninja-build
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev libxi-dev ninja-build
|
||||
env:
|
||||
- ENABLE_UI=OFF
|
||||
- XVFB=xvfb-run
|
||||
@ -19,7 +19,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev ninja-build
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev libxi-dev ninja-build
|
||||
env:
|
||||
- ENABLE_SCRIPTING=OFF
|
||||
- XVFB=xvfb-run
|
||||
@ -27,7 +27,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev ninja-build
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev libxi-dev ninja-build
|
||||
env:
|
||||
- ENABLE_SCRIPTING=OFF
|
||||
- ENABLE_UI=OFF
|
||||
@ -37,7 +37,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev ninja-build
|
||||
- libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev libxi-dev ninja-build
|
||||
env:
|
||||
- ENABLE_UI=ON
|
||||
- XVFB=xvfb-run
|
||||
@ -47,7 +47,7 @@ matrix:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-7 libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev ninja-build
|
||||
- g++-7 libpixman-1-dev libfreetype6-dev libharfbuzz-dev libx11-dev libxcursor-dev libxi-dev ninja-build
|
||||
env:
|
||||
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
|
||||
- ENABLE_UI=ON
|
||||
|
@ -72,11 +72,11 @@ versions might work).
|
||||
|
||||
You will need the following dependencies on Ubuntu/Debian:
|
||||
|
||||
sudo apt-get install -y g++ cmake ninja-build libx11-dev libxcursor-dev libgl1-mesa-dev libfontconfig1-dev
|
||||
sudo apt-get install -y g++ cmake ninja-build libx11-dev libxcursor-dev libxi-dev libgl1-mesa-dev libfontconfig1-dev
|
||||
|
||||
On Fedora:
|
||||
|
||||
sudo dnf install -y gcc-c++ cmake ninja-build libX11-devel libXcursor-devel mesa-libGL-devel fontconfig-devel
|
||||
sudo dnf install -y gcc-c++ cmake ninja-build libX11-devel libXcursor-devel libXi-devel mesa-libGL-devel fontconfig-devel
|
||||
|
||||
# Compiling
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
@ -418,6 +418,7 @@
|
||||
<part id="outline_vertical" x="192" y="224" w="13" h="15" />
|
||||
<part id="outline_empty_pixel" x="208" y="224" w="5" h="5" />
|
||||
<part id="outline_full_pixel" x="214" y="224" w="5" h="5" />
|
||||
<part id="dynamics" x="176" y="144" w="16" h="16" />
|
||||
</parts>
|
||||
<styles>
|
||||
<style id="box" />
|
||||
|
@ -422,6 +422,7 @@ SetSameInk = Same Ink in All Tools
|
||||
ShowAutoGuides = Show Auto Guides
|
||||
ShowBrushPreview = Show Brush Preview
|
||||
ShowBrushes = Show Brushes
|
||||
ShowDynamics = Show Dynamics
|
||||
ShowExtras = Show Extras
|
||||
ShowGrid = Show Grid
|
||||
ShowLayerEdges = Show Layer Edges
|
||||
@ -520,6 +521,28 @@ duplicate = Duplicate:
|
||||
as = As:
|
||||
merged_layers = Duplicate merged layers only
|
||||
|
||||
[dynamics]
|
||||
pressure = Pressure
|
||||
pressure_tooltip = Control parameters through the pen pressure sensor
|
||||
velocity = Velocity
|
||||
velocity_tooltip = Control parameters through the mouse velocity
|
||||
size = Size:
|
||||
size_tooltip = <<<END
|
||||
Change the brush size
|
||||
depending on the sensor value
|
||||
END
|
||||
angle = Angle:
|
||||
angle_tooltip = <<<END
|
||||
Change the brush angle
|
||||
depending on the sensor value
|
||||
END
|
||||
gradient = Gradient:
|
||||
gradient_tooltip = <<<END
|
||||
Gradient between foreground
|
||||
and background colors
|
||||
END
|
||||
max_point_value = Max Point Value:
|
||||
|
||||
[export_file]
|
||||
title = Export File
|
||||
output_file = Output File:
|
||||
|
39
data/widgets/dynamics.xml
Normal file
39
data/widgets/dynamics.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2020 Igara Studio S.A. -->
|
||||
<gui>
|
||||
<vbox id="dynamics">
|
||||
<hbox>
|
||||
<buttonset id="values" columns="3">
|
||||
<item text="" />
|
||||
<item text="@.pressure" tooltip="@.pressure_tooltip" tooltip_dir="bottom" />
|
||||
<item text="@.velocity" tooltip="@.velocity_tooltip" tooltip_dir="bottom" />
|
||||
|
||||
<item text="@.size" tooltip="@.size_tooltip" tooltip_dir="right" />
|
||||
<item text="" maxheight="1" />
|
||||
<item text="" maxheight="1" />
|
||||
|
||||
<item text="@.angle" tooltip="@.angle_tooltip" tooltip_dir="right" />
|
||||
<item text="" maxheight="1" />
|
||||
<item text="" maxheight="1" />
|
||||
|
||||
<item text="@.gradient" tooltip="@.gradient_tooltip" tooltip_dir="right" />
|
||||
<item text="" maxheight="1" />
|
||||
<item text="" maxheight="1" />
|
||||
</buttonset>
|
||||
</hbox>
|
||||
|
||||
<separator id="separator" text="@.max_point_value" horizontal="true" />
|
||||
|
||||
<grid id="options" columns="2" childspacing="0" expansive="true">
|
||||
<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="max_angle_label" text="@.angle" style="mini_label" />
|
||||
<slider id="max_angle" value="0" min="-180" max="+180" cell_align="horizontal" />
|
||||
|
||||
<label id="gradient_label" text="@.gradient" style="mini_label" />
|
||||
<hbox id="gradient_placeholder" />
|
||||
</grid>
|
||||
|
||||
</vbox>
|
||||
</gui>
|
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit 55a3acb0bf6f30a50023dbdf04c2813423064702
|
||||
Subproject commit 40c86d83db8eb4a96b0552535a9c7e4ddef2ab70
|
@ -319,6 +319,7 @@ if(ENABLE_UI)
|
||||
ui/dithering_selector.cpp
|
||||
ui/doc_view.cpp
|
||||
ui/drop_down_button.cpp
|
||||
ui/dynamics_popup.cpp
|
||||
ui/editor/brush_preview.cpp
|
||||
ui/editor/drawing_state.cpp
|
||||
ui/editor/editor.cpp
|
||||
|
@ -328,7 +328,12 @@ int App_useTool(lua_State* L)
|
||||
while (lua_next(L, -2) != 0) {
|
||||
gfx::Point pt = convert_args_into_point(L, -1);
|
||||
|
||||
tools::Pointer pointer(pt, tools::Pointer::Button::Left);
|
||||
tools::Pointer pointer(
|
||||
pt,
|
||||
// TODO configurable params
|
||||
tools::Pointer::Button::Left,
|
||||
tools::Pointer::Type::Unknown,
|
||||
0.0f);
|
||||
if (first) {
|
||||
first = false;
|
||||
manager.prepareLoop(pointer);
|
||||
|
42
src/app/tools/dynamics.h
Normal file
42
src/app/tools/dynamics.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_TOOLS_DYNAMICS_H_INCLUDED
|
||||
#define APP_TOOLS_DYNAMICS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "render/dithering_algorithm.h"
|
||||
#include "render/dithering_matrix.h"
|
||||
|
||||
namespace app {
|
||||
namespace tools {
|
||||
|
||||
enum class DynamicSensor {
|
||||
Static,
|
||||
Pressure,
|
||||
Velocity,
|
||||
};
|
||||
|
||||
struct DynamicsOptions {
|
||||
DynamicSensor size = DynamicSensor::Static;
|
||||
DynamicSensor angle = DynamicSensor::Static;
|
||||
DynamicSensor gradient = DynamicSensor::Static;
|
||||
int maxSize = 0;
|
||||
int maxAngle = 0;
|
||||
render::DitheringAlgorithm ditheringAlgorithm = render::DitheringAlgorithm::None;
|
||||
render::DitheringMatrix ditheringMatrix;
|
||||
|
||||
bool isDynamic() const {
|
||||
return (size != DynamicSensor::Static ||
|
||||
angle != DynamicSensor::Static ||
|
||||
gradient != DynamicSensor::Static);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -37,21 +37,26 @@ public:
|
||||
};
|
||||
|
||||
class BrushPointShape : public PointShape {
|
||||
Brush* m_brush;
|
||||
Brush* m_lastBrush;
|
||||
std::shared_ptr<CompressedImage> m_compressedImage;
|
||||
bool m_firstPoint;
|
||||
|
||||
public:
|
||||
|
||||
void preparePointShape(ToolLoop* loop) override {
|
||||
m_brush = loop->getBrush();
|
||||
m_compressedImage.reset(new CompressedImage(m_brush->image(),
|
||||
m_brush->maskBitmap(),
|
||||
false));
|
||||
m_firstPoint = true;
|
||||
m_lastBrush = nullptr;
|
||||
}
|
||||
|
||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||
Brush* m_brush = loop->getBrush();
|
||||
if (m_lastBrush != m_brush) {
|
||||
m_lastBrush = m_brush;
|
||||
m_compressedImage.reset(new CompressedImage(m_brush->image(),
|
||||
m_brush->maskBitmap(),
|
||||
false));
|
||||
}
|
||||
|
||||
x += m_brush->bounds().x;
|
||||
y += m_brush->bounds().y;
|
||||
|
||||
@ -91,7 +96,7 @@ public:
|
||||
}
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
area = m_brush->bounds();
|
||||
area = loop->getBrush()->bounds();
|
||||
area.x += x;
|
||||
area.y += y;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -9,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "ui/pointer_type.h"
|
||||
|
||||
namespace app {
|
||||
namespace tools {
|
||||
@ -17,19 +19,33 @@ namespace tools {
|
||||
class Pointer {
|
||||
public:
|
||||
enum Button { None, Left, Middle, Right };
|
||||
typedef ui::PointerType Type;
|
||||
|
||||
Pointer()
|
||||
: m_point(0, 0), m_button(None) { }
|
||||
: m_point(0, 0)
|
||||
, m_button(None)
|
||||
, m_type(Type::Unknown)
|
||||
, m_pressure(0.0f) { }
|
||||
|
||||
Pointer(const gfx::Point& point, Button button)
|
||||
: m_point(point), m_button(button) { }
|
||||
Pointer(const gfx::Point& point,
|
||||
const Button button,
|
||||
const Type type,
|
||||
const float pressure)
|
||||
: m_point(point)
|
||||
, m_button(button)
|
||||
, m_type(type)
|
||||
, m_pressure(pressure) { }
|
||||
|
||||
const gfx::Point& point() const { return m_point; }
|
||||
Button button() const { return m_button; }
|
||||
Type type() const { return m_type; }
|
||||
float pressure() const { return m_pressure; }
|
||||
|
||||
private:
|
||||
gfx::Point m_point;
|
||||
Button m_button;
|
||||
Type m_type;
|
||||
float m_pressure;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -9,8 +9,10 @@
|
||||
#define APP_TOOLS_TOOL_LOOP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/tools/dynamics.h"
|
||||
#include "app/tools/tool_loop_modifiers.h"
|
||||
#include "app/tools/trace_policy.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/frame.h"
|
||||
#include "filters/tiled_mode.h"
|
||||
@ -23,7 +25,6 @@ namespace gfx {
|
||||
}
|
||||
|
||||
namespace doc {
|
||||
class Brush;
|
||||
class Image;
|
||||
class Layer;
|
||||
class Mask;
|
||||
@ -72,6 +73,7 @@ namespace app {
|
||||
|
||||
// Returns the brush which will be used with the tool
|
||||
virtual Brush* getBrush() = 0;
|
||||
virtual void setBrush(const BrushRef& newBrush) = 0;
|
||||
|
||||
// Returns the document to which belongs the sprite.
|
||||
virtual Doc* getDocument() = 0;
|
||||
@ -235,6 +237,9 @@ namespace app {
|
||||
virtual render::DitheringAlgorithmBase* getDitheringAlgorithm() = 0;
|
||||
virtual render::GradientType getGradientType() = 0;
|
||||
|
||||
// For freehand algorithms with dynamics
|
||||
virtual tools::DynamicsOptions getDynamics() = 0;
|
||||
|
||||
// Called when the user release the mouse on SliceInk
|
||||
virtual void onSliceRect(const gfx::Rect& bounds) = 0;
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/symmetry.h"
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "base/clamp.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/primitives.h"
|
||||
@ -40,6 +41,8 @@ using namespace filters;
|
||||
|
||||
ToolLoopManager::ToolLoopManager(ToolLoop* toolLoop)
|
||||
: m_toolLoop(toolLoop)
|
||||
, m_brush0(*toolLoop->getBrush())
|
||||
, m_dynamics(toolLoop->getDynamics())
|
||||
{
|
||||
}
|
||||
|
||||
@ -154,8 +157,6 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
|
||||
|
||||
void ToolLoopManager::movement(const Pointer& pointer)
|
||||
{
|
||||
TOOL_TRACE("ToolLoopManager::movement", pointer.point());
|
||||
|
||||
m_lastPointer = pointer;
|
||||
|
||||
if (isCanceled())
|
||||
@ -163,11 +164,16 @@ void ToolLoopManager::movement(const Pointer& pointer)
|
||||
|
||||
// Convert the screen point to a sprite point
|
||||
Point spritePoint = pointer.point();
|
||||
// Calculate the speed (new sprite point - old sprite point)
|
||||
m_toolLoop->setSpeed(spritePoint - m_oldPoint);
|
||||
// Calculate the velocity (new sprite point - old sprite point)
|
||||
Point velocity = (spritePoint - m_oldPoint);
|
||||
m_toolLoop->setSpeed(velocity);
|
||||
m_oldPoint = spritePoint;
|
||||
snapToGrid(spritePoint);
|
||||
|
||||
// Control dynamic parameters through sensors
|
||||
if (m_dynamics.isDynamic())
|
||||
adjustBrushWithDynamics(pointer, velocity);
|
||||
|
||||
m_toolLoop->getController()->movement(m_toolLoop, m_stroke, spritePoint);
|
||||
|
||||
std::string statusText;
|
||||
@ -373,5 +379,53 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
|
||||
}
|
||||
}
|
||||
|
||||
void ToolLoopManager::adjustBrushWithDynamics(const Pointer& pointer,
|
||||
const Point& velocity)
|
||||
{
|
||||
int size = m_brush0.size();
|
||||
int angle = m_brush0.angle();
|
||||
|
||||
// Pressure
|
||||
bool hasP = (pointer.type() == Pointer::Type::Pen ||
|
||||
pointer.type() == Pointer::Type::Eraser);
|
||||
float p = (hasP ? pointer.pressure(): 1.0f);
|
||||
ASSERT(p >= 0.0f && p <= 1.0f);
|
||||
|
||||
// Velocity
|
||||
float v = float(std::sqrt(velocity.x*velocity.x +
|
||||
velocity.y*velocity.y)) / 32.0f; // TODO 16 should be configurable
|
||||
v = base::clamp(v, 0.0f, 1.0f);
|
||||
|
||||
switch (m_dynamics.size) {
|
||||
case DynamicSensor::Pressure:
|
||||
if (hasP) size = (1.0f-p)*size + p*m_dynamics.maxSize;
|
||||
break;
|
||||
case DynamicSensor::Velocity:
|
||||
size = (1.0f-v)*size + v*m_dynamics.maxSize;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_dynamics.angle) {
|
||||
case DynamicSensor::Pressure:
|
||||
if (hasP) angle = (1.0f-p)*angle + p*m_dynamics.maxAngle;
|
||||
break;
|
||||
case DynamicSensor::Velocity:
|
||||
angle = (1.0f-v)*angle + v*m_dynamics.maxAngle;
|
||||
break;
|
||||
}
|
||||
|
||||
size = base::clamp(size, int(Brush::kMinBrushSize), int(Brush::kMaxBrushSize));
|
||||
angle = base::clamp(angle, -180, 180);
|
||||
|
||||
Brush* currrentBrush = m_toolLoop->getBrush();
|
||||
|
||||
if (currrentBrush->size() != size ||
|
||||
(currrentBrush->type() != kCircleBrushType &&
|
||||
currrentBrush->angle() != angle)) {
|
||||
m_toolLoop->setBrush(
|
||||
std::make_shared<Brush>(m_brush0.type(), size, angle));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -9,8 +9,10 @@
|
||||
#define APP_TOOLS_TOOL_LOOP_MANAGER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/tools/dynamics.h"
|
||||
#include "app/tools/pointer.h"
|
||||
#include "app/tools/stroke.h"
|
||||
#include "doc/brush.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
@ -72,6 +74,8 @@ public:
|
||||
private:
|
||||
void doLoopStep(bool lastStep);
|
||||
void snapToGrid(gfx::Point& point);
|
||||
void adjustBrushWithDynamics(const Pointer& pointer,
|
||||
const gfx::Point& velocity);
|
||||
|
||||
void calculateDirtyArea(const Strokes& strokes);
|
||||
|
||||
@ -81,6 +85,8 @@ private:
|
||||
gfx::Point m_oldPoint;
|
||||
gfx::Region m_dirtyArea;
|
||||
gfx::Region m_nextDirtyArea;
|
||||
doc::Brush m_brush0;
|
||||
DynamicsOptions m_dynamics;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/color_shades.h"
|
||||
#include "app/ui/dithering_selector.h"
|
||||
#include "app/ui/dynamics_popup.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/icon_button.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
@ -972,6 +973,55 @@ private:
|
||||
bool m_lockChange;
|
||||
};
|
||||
|
||||
class ContextBar::DynamicsField : public ButtonSet
|
||||
, public DynamicsPopup::Delegate {
|
||||
public:
|
||||
DynamicsField(ContextBar* ctxBar)
|
||||
: ButtonSet(1)
|
||||
, m_ctxBar(ctxBar) {
|
||||
addItem(SkinTheme::instance()->parts.dynamics());
|
||||
}
|
||||
|
||||
void showPopup() {
|
||||
if (!m_popup) {
|
||||
m_popup.reset(new DynamicsPopup(this));
|
||||
m_popup->remapWindow();
|
||||
}
|
||||
|
||||
const gfx::Rect bounds = this->bounds();
|
||||
m_popup->positionWindow(bounds.x, bounds.y+bounds.h);
|
||||
m_popup->setHotRegion(gfx::Region(m_popup->bounds()));
|
||||
m_popup->openWindow();
|
||||
}
|
||||
|
||||
tools::DynamicsOptions getDynamics() {
|
||||
if (m_popup)
|
||||
return m_popup->getDynamics();
|
||||
else
|
||||
return tools::DynamicsOptions();
|
||||
}
|
||||
|
||||
private:
|
||||
// DynamicsPopup::Delegate impl
|
||||
doc::BrushRef getActiveBrush() override {
|
||||
return m_ctxBar->activeBrush();
|
||||
}
|
||||
|
||||
void onItemChange(Item* item) override {
|
||||
ButtonSet::onItemChange(item);
|
||||
showPopup();
|
||||
}
|
||||
|
||||
void onInitTheme(InitThemeEvent& ev) override {
|
||||
ButtonSet::onInitTheme(ev);
|
||||
if (m_popup)
|
||||
m_popup->initTheme();
|
||||
}
|
||||
|
||||
std::unique_ptr<DynamicsPopup> m_popup;
|
||||
ContextBar* m_ctxBar;
|
||||
};
|
||||
|
||||
class ContextBar::FreehandAlgorithmField : public CheckBox {
|
||||
public:
|
||||
FreehandAlgorithmField() : CheckBox("Pixel-perfect") {
|
||||
@ -1500,6 +1550,7 @@ ContextBar::ContextBar(TooltipManager* tooltipManager,
|
||||
addChild(m_selectBoxHelp = new Label(""));
|
||||
addChild(m_freehandBox = new HBox());
|
||||
|
||||
m_freehandBox->addChild(m_dynamics = new DynamicsField(this));
|
||||
m_freehandBox->addChild(m_freehandAlgo = new FreehandAlgorithmField());
|
||||
|
||||
addChild(m_symmetry = new SymmetryField());
|
||||
@ -2148,6 +2199,11 @@ render::GradientType ContextBar::gradientType()
|
||||
return m_gradientType->gradientType();
|
||||
}
|
||||
|
||||
tools::DynamicsOptions ContextBar::getDynamics()
|
||||
{
|
||||
return m_dynamics->getDynamics();
|
||||
}
|
||||
|
||||
void ContextBar::setupTooltips(TooltipManager* tooltipManager)
|
||||
{
|
||||
tooltipManager->addTooltipFor(m_brushBack->at(0), "Discard Brush (Esc)", BOTTOM);
|
||||
@ -2161,6 +2217,7 @@ void ContextBar::setupTooltips(TooltipManager* tooltipManager)
|
||||
tooltipManager->addTooltipFor(m_spraySpeed, "Spray Speed", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_pivot->at(0), "Rotation Pivot", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_rotAlgo, "Rotation Algorithm", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_dynamics->at(0), "Dynamics", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_freehandAlgo,
|
||||
key_tooltip("Freehand trace algorithm",
|
||||
CommandId::PixelPerfectMode()), BOTTOM);
|
||||
@ -2184,11 +2241,24 @@ void ContextBar::registerCommands()
|
||||
new QuickCommand(
|
||||
CommandId::ShowBrushes(),
|
||||
[this]{ this->showBrushes(); }));
|
||||
|
||||
Commands::instance()
|
||||
->add(
|
||||
new QuickCommand(
|
||||
CommandId::ShowDynamics(),
|
||||
[this]{ this->showDynamics(); }));
|
||||
}
|
||||
|
||||
void ContextBar::showBrushes()
|
||||
{
|
||||
m_brushType->showPopup();
|
||||
if (m_brushType->isVisible())
|
||||
m_brushType->showPopup();
|
||||
}
|
||||
|
||||
void ContextBar::showDynamics()
|
||||
{
|
||||
if (m_dynamics->isVisible())
|
||||
m_dynamics->showPopup();
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -12,6 +12,7 @@
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/shade.h"
|
||||
#include "app/tools/active_tool_observer.h"
|
||||
#include "app/tools/dynamics.h"
|
||||
#include "app/tools/ink_type.h"
|
||||
#include "app/tools/tool_loop_modifiers.h"
|
||||
#include "app/ui/context_bar_observer.h"
|
||||
@ -90,6 +91,9 @@ namespace app {
|
||||
render::DitheringAlgorithmBase* ditheringAlgorithm();
|
||||
render::GradientType gradientType();
|
||||
|
||||
// For freehand with dynamics
|
||||
tools::DynamicsOptions getDynamics();
|
||||
|
||||
// Signals
|
||||
obs::signal<void()> BrushChange;
|
||||
|
||||
@ -124,6 +128,7 @@ namespace app {
|
||||
void setupTooltips(ui::TooltipManager* tooltipManager);
|
||||
void registerCommands();
|
||||
void showBrushes();
|
||||
void showDynamics();
|
||||
|
||||
class ZoomButtons;
|
||||
class BrushBackField;
|
||||
@ -143,6 +148,7 @@ namespace app {
|
||||
class TransparentColorField;
|
||||
class PivotField;
|
||||
class RotAlgorithmField;
|
||||
class DynamicsField;
|
||||
class FreehandAlgorithmField;
|
||||
class BrushPatternField;
|
||||
class EyedropperField;
|
||||
@ -167,6 +173,7 @@ namespace app {
|
||||
EyedropperField* m_eyedropperField;
|
||||
AutoSelectLayerField* m_autoSelectLayer;
|
||||
ui::Box* m_freehandBox;
|
||||
DynamicsField* m_dynamics;
|
||||
FreehandAlgorithmField* m_freehandAlgo;
|
||||
BrushPatternField* m_brushPatternField;
|
||||
ui::Box* m_sprayBox;
|
||||
|
185
src/app/ui/dynamics_popup.cpp
Normal file
185
src/app/ui/dynamics_popup.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/dynamics_popup.h"
|
||||
|
||||
#include "app/ui/dithering_selector.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "base/clamp.h"
|
||||
#include "ui/message.h"
|
||||
|
||||
#include "dynamics.xml.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
using namespace skin;
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
NONE,
|
||||
PRESSURE_HEADER,
|
||||
VELOCITY_HEADER,
|
||||
SIZE_HEADER,
|
||||
SIZE_WITH_PRESSURE,
|
||||
SIZE_WITH_VELOCITY,
|
||||
ANGLE_HEADER,
|
||||
ANGLE_WITH_PRESSURE,
|
||||
ANGLE_WITH_VELOCITY,
|
||||
GRADIENT_HEADER,
|
||||
GRADIENT_WITH_PRESSURE,
|
||||
GRADIENT_WITH_VELOCITY,
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
DynamicsPopup::DynamicsPopup(Delegate* delegate)
|
||||
: PopupWindow("",
|
||||
PopupWindow::ClickBehavior::CloseOnClickOutsideHotRegion,
|
||||
PopupWindow::EnterBehavior::DoNothingOnEnter)
|
||||
, m_delegate(delegate)
|
||||
, m_dynamics(new gen::Dynamics)
|
||||
, m_ditheringSel(new DitheringSelector(DitheringSelector::SelectMatrix))
|
||||
{
|
||||
m_dynamics->values()->ItemChange.connect(
|
||||
[this](ButtonSet::Item* item){
|
||||
onValuesChange(item);
|
||||
});
|
||||
|
||||
m_dynamics->gradientPlaceholder()->addChild(m_ditheringSel);
|
||||
addChild(m_dynamics);
|
||||
|
||||
onValuesChange(nullptr);
|
||||
}
|
||||
|
||||
tools::DynamicsOptions DynamicsPopup::getDynamics() const
|
||||
{
|
||||
tools::DynamicsOptions opts;
|
||||
opts.size =
|
||||
(isCheck(SIZE_WITH_PRESSURE) ? tools::DynamicSensor::Pressure:
|
||||
isCheck(SIZE_WITH_VELOCITY) ? tools::DynamicSensor::Velocity:
|
||||
tools::DynamicSensor::Static);
|
||||
opts.angle =
|
||||
(isCheck(ANGLE_WITH_PRESSURE) ? tools::DynamicSensor::Pressure:
|
||||
isCheck(ANGLE_WITH_VELOCITY) ? tools::DynamicSensor::Velocity:
|
||||
tools::DynamicSensor::Static);
|
||||
opts.gradient =
|
||||
(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.ditheringAlgorithm = m_ditheringSel->ditheringAlgorithm();
|
||||
opts.ditheringMatrix = m_ditheringSel->ditheringMatrix();
|
||||
return opts;
|
||||
}
|
||||
|
||||
void DynamicsPopup::setCheck(int i, bool state)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||
m_dynamics->values()
|
||||
->getItem(i)
|
||||
->setIcon(state ? theme->parts.dropPixelsOk(): nullptr);
|
||||
}
|
||||
|
||||
bool DynamicsPopup::isCheck(int i) const
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||
return (m_dynamics->values()
|
||||
->getItem(i)
|
||||
->icon() == theme->parts.dropPixelsOk());
|
||||
}
|
||||
|
||||
void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||
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 needsSize = (isCheck(SIZE_WITH_PRESSURE) ||
|
||||
isCheck(SIZE_WITH_VELOCITY));
|
||||
const bool needsAngle = (isCheck(ANGLE_WITH_PRESSURE) ||
|
||||
isCheck(ANGLE_WITH_VELOCITY));
|
||||
const bool needsGradient = (isCheck(GRADIENT_WITH_PRESSURE) ||
|
||||
isCheck(GRADIENT_WITH_VELOCITY));
|
||||
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));
|
||||
}
|
||||
m_dynamics->maxSizeLabel()->setVisible(needsSize);
|
||||
m_dynamics->maxSize()->setVisible(needsSize);
|
||||
|
||||
if (needsAngle && !m_dynamics->maxAngle()->isVisible()) {
|
||||
m_dynamics->maxAngle()->setValue(brush->angle());
|
||||
}
|
||||
m_dynamics->maxAngleLabel()->setVisible(needsAngle);
|
||||
m_dynamics->maxAngle()->setVisible(needsAngle);
|
||||
|
||||
m_dynamics->gradientLabel()->setVisible(needsGradient);
|
||||
m_dynamics->gradientPlaceholder()->setVisible(needsGradient);
|
||||
|
||||
m_dynamics->separator()->setVisible(any);
|
||||
m_dynamics->options()->setVisible(any);
|
||||
|
||||
auto oldBounds = bounds();
|
||||
layout();
|
||||
setBounds(gfx::Rect(origin(), sizeHint()));
|
||||
|
||||
m_hotRegion |= gfx::Region(bounds());
|
||||
setHotRegion(m_hotRegion);
|
||||
|
||||
if (isVisible())
|
||||
manager()->invalidateRect(oldBounds);
|
||||
}
|
||||
|
||||
bool DynamicsPopup::onProcessMessage(Message* msg)
|
||||
{
|
||||
switch (msg->type()) {
|
||||
case kOpenMessage:
|
||||
m_hotRegion = gfx::Region(bounds());
|
||||
setHotRegion(m_hotRegion);
|
||||
break;
|
||||
case kCloseMessage:
|
||||
m_hotRegion.clear();
|
||||
break;
|
||||
}
|
||||
return PopupWindow::onProcessMessage(msg);
|
||||
}
|
||||
|
||||
} // namespace app
|
49
src/app/ui/dynamics_popup.h
Normal file
49
src/app/ui/dynamics_popup.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_DYNAMICS_POPUP_H_INCLUDED
|
||||
#define APP_UI_DYNAMICS_POPUP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/tools/dynamics.h"
|
||||
#include "app/ui/button_set.h"
|
||||
#include "doc/brush.h"
|
||||
#include "gfx/region.h"
|
||||
#include "ui/popup_window.h"
|
||||
|
||||
namespace app {
|
||||
namespace gen {
|
||||
class Dynamics;
|
||||
}
|
||||
|
||||
class DitheringSelector;
|
||||
|
||||
class DynamicsPopup : public ui::PopupWindow {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
virtual ~Delegate() { }
|
||||
virtual doc::BrushRef getActiveBrush() = 0;
|
||||
};
|
||||
DynamicsPopup(Delegate* delegate);
|
||||
|
||||
tools::DynamicsOptions getDynamics() const;
|
||||
|
||||
private:
|
||||
void setCheck(int i, bool state);
|
||||
bool isCheck(int i) const;
|
||||
void onValuesChange(ButtonSet::Item* item);
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
|
||||
Delegate* m_delegate;
|
||||
gen::Dynamics* m_dynamics;
|
||||
DitheringSelector* m_ditheringSel;
|
||||
gfx::Region m_hotRegion;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -211,7 +211,9 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
|
||||
handleMouseMovement(
|
||||
tools::Pointer(editor->screenToEditor(mousePos),
|
||||
button_from_msg(msg)));
|
||||
button_from_msg(msg),
|
||||
msg->pointerType(),
|
||||
msg->pressure()));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -269,7 +271,9 @@ bool DrawingState::onScrollChange(Editor* editor)
|
||||
gfx::Point mousePos = ui::get_mouse_position();
|
||||
handleMouseMovement(
|
||||
tools::Pointer(editor->screenToEditor(mousePos),
|
||||
m_lastPointer.button()));
|
||||
m_lastPointer.button(),
|
||||
tools::Pointer::Type::Unknown,
|
||||
0.0f));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -27,7 +28,9 @@ inline tools::Pointer pointer_from_msg(Editor* editor,
|
||||
const ui::MouseMessage* msg) {
|
||||
return
|
||||
tools::Pointer(editor->screenToEditor(msg->position()),
|
||||
button_from_msg(msg));
|
||||
button_from_msg(msg),
|
||||
msg->pointerType(),
|
||||
msg->pressure());
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -674,13 +674,17 @@ bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
|
||||
DrawingType::LineFreehand,
|
||||
tools::Pointer(
|
||||
editor->document()->lastDrawingPoint(),
|
||||
pointerButton));
|
||||
pointerButton,
|
||||
msg ? msg->pointerType(): PointerType::Unknown,
|
||||
msg ? msg->pressure(): 0.0f));
|
||||
if (drawingState) {
|
||||
drawingState->sendMovementToToolLoop(
|
||||
tools::Pointer(
|
||||
editor->screenToEditor(msg ? msg->position():
|
||||
ui::get_mouse_position()),
|
||||
pointerButton));
|
||||
pointerButton,
|
||||
msg ? msg->pointerType(): tools::Pointer::Type::Unknown,
|
||||
msg ? msg->pressure(): 0.0f));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ protected:
|
||||
Editor* m_editor;
|
||||
tools::Tool* m_tool;
|
||||
BrushRef m_brush;
|
||||
BrushRef m_origBrush;
|
||||
gfx::Point m_oldPatternOrigin;
|
||||
Doc* m_document;
|
||||
Sprite* m_sprite;
|
||||
@ -112,6 +113,7 @@ public:
|
||||
: m_editor(editor)
|
||||
, m_tool(tool)
|
||||
, m_brush(brush)
|
||||
, m_origBrush(brush)
|
||||
, m_oldPatternOrigin(m_brush->patternOrigin())
|
||||
, m_document(site.document())
|
||||
, m_sprite(site.sprite())
|
||||
@ -204,7 +206,7 @@ public:
|
||||
}
|
||||
|
||||
~ToolLoopBase() {
|
||||
m_brush->setPatternOrigin(m_oldPatternOrigin);
|
||||
m_origBrush->setPatternOrigin(m_oldPatternOrigin);
|
||||
}
|
||||
|
||||
void forceSnapToTiles() {
|
||||
@ -215,6 +217,7 @@ public:
|
||||
// IToolLoop interface
|
||||
tools::Tool* getTool() override { return m_tool; }
|
||||
Brush* getBrush() override { return m_brush.get(); }
|
||||
void setBrush(const BrushRef& newBrush) override { m_brush = newBrush; }
|
||||
Doc* getDocument() override { return m_document; }
|
||||
Sprite* sprite() override { return m_sprite; }
|
||||
Layer* getLayer() override { return m_layer; }
|
||||
@ -360,6 +363,14 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
tools::DynamicsOptions getDynamics() override {
|
||||
#ifdef ENABLE_UI // TODO add support when UI is not enabled
|
||||
return App::instance()->contextBar()->getDynamics();
|
||||
#else
|
||||
return tools::DynamicsOptions();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void onSliceRect(const gfx::Rect& bounds) override { }
|
||||
|
||||
|
@ -443,7 +443,8 @@ void Manager::generateMessagesFromOSEvents()
|
||||
handleMouseMove(
|
||||
osEvent.position(),
|
||||
osEvent.modifiers(),
|
||||
osEvent.pointerType());
|
||||
osEvent.pointerType(),
|
||||
osEvent.pressure());
|
||||
lastMouseMoveEvent = osEvent;
|
||||
break;
|
||||
}
|
||||
@ -513,8 +514,9 @@ void Manager::generateMessagesFromOSEvents()
|
||||
}
|
||||
|
||||
void Manager::handleMouseMove(const gfx::Point& mousePos,
|
||||
KeyModifiers modifiers,
|
||||
PointerType pointerType)
|
||||
const KeyModifiers modifiers,
|
||||
const PointerType pointerType,
|
||||
const float pressure)
|
||||
{
|
||||
// Get the list of widgets to send mouse messages.
|
||||
mouse_widgets_list.clear();
|
||||
@ -549,7 +551,10 @@ void Manager::handleMouseMove(const gfx::Point& mousePos,
|
||||
mousePos,
|
||||
pointerType,
|
||||
m_mouseButton,
|
||||
modifiers));
|
||||
modifiers,
|
||||
gfx::Point(0, 0),
|
||||
false,
|
||||
pressure));
|
||||
}
|
||||
|
||||
void Manager::handleMouseDown(const gfx::Point& mousePos,
|
||||
@ -1682,7 +1687,8 @@ Message* Manager::newMouseMessage(
|
||||
MouseButton button,
|
||||
KeyModifiers modifiers,
|
||||
const gfx::Point& wheelDelta,
|
||||
bool preciseWheel)
|
||||
bool preciseWheel,
|
||||
float pressure)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// Convert Ctrl+left click -> right-click
|
||||
@ -1699,7 +1705,7 @@ Message* Manager::newMouseMessage(
|
||||
|
||||
Message* msg = new MouseMessage(
|
||||
type, pointerType, button, modifiers, mousePos,
|
||||
wheelDelta, preciseWheel);
|
||||
wheelDelta, preciseWheel, pressure);
|
||||
|
||||
if (widget)
|
||||
msg->setRecipient(widget);
|
||||
|
@ -121,8 +121,9 @@ namespace ui {
|
||||
PointerType pointerType);
|
||||
void generateMessagesFromOSEvents();
|
||||
void handleMouseMove(const gfx::Point& mousePos,
|
||||
KeyModifiers modifiers,
|
||||
PointerType pointerType);
|
||||
const KeyModifiers modifiers,
|
||||
const PointerType pointerType,
|
||||
const float pressure);
|
||||
void handleMouseDown(const gfx::Point& mousePos,
|
||||
MouseButton mouseButton,
|
||||
KeyModifiers modifiers,
|
||||
@ -158,7 +159,8 @@ namespace ui {
|
||||
MouseButton button,
|
||||
KeyModifiers modifiers,
|
||||
const gfx::Point& wheelDelta = gfx::Point(0, 0),
|
||||
bool preciseWheel = false);
|
||||
bool preciseWheel = false,
|
||||
float pressure = 0.0f);
|
||||
void broadcastKeyMsg(Message* msg);
|
||||
|
||||
static Manager* m_defaultManager;
|
||||
|
@ -120,13 +120,15 @@ namespace ui {
|
||||
KeyModifiers modifiers,
|
||||
const gfx::Point& pos,
|
||||
const gfx::Point& wheelDelta = gfx::Point(0, 0),
|
||||
bool preciseWheel = false)
|
||||
bool preciseWheel = false,
|
||||
float pressure = 0.0f)
|
||||
: Message(type, modifiers),
|
||||
m_pointerType(pointerType),
|
||||
m_button(button),
|
||||
m_pos(pos),
|
||||
m_wheelDelta(wheelDelta),
|
||||
m_preciseWheel(preciseWheel) {
|
||||
m_preciseWheel(preciseWheel),
|
||||
m_pressure(pressure) {
|
||||
}
|
||||
|
||||
PointerType pointerType() const { return m_pointerType; }
|
||||
@ -136,6 +138,7 @@ namespace ui {
|
||||
bool middle() const { return (m_button == kButtonMiddle); }
|
||||
gfx::Point wheelDelta() const { return m_wheelDelta; }
|
||||
bool preciseWheel() const { return m_preciseWheel; }
|
||||
float pressure() const { return m_pressure; }
|
||||
|
||||
const gfx::Point& position() const { return m_pos; }
|
||||
|
||||
@ -145,6 +148,7 @@ namespace ui {
|
||||
gfx::Point m_pos; // Mouse position
|
||||
gfx::Point m_wheelDelta; // Wheel axis variation
|
||||
bool m_preciseWheel;
|
||||
float m_pressure;
|
||||
};
|
||||
|
||||
class TouchMessage : public Message {
|
||||
|
Loading…
x
Reference in New Issue
Block a user