Add rotation pivot options in context bar (fix #370)

With these options we can configure to show the pivot and the default
location when we select a sprite area.
This commit is contained in:
David Capello 2015-07-29 16:35:34 -03:00
parent 9ca83e2624
commit 7cfdf76b0e
19 changed files with 274 additions and 35 deletions

View File

@ -52,6 +52,18 @@
<value id="ALL_LAYERS" value="0" />
<value id="CURRENT_LAYER" value="1" />
</enum>
<enum id="PivotMode">
<value id="HIDDEN" value="0" />
<value id="NORTHWEST" value="1" />
<value id="NORTH" value="2" />
<value id="NORTHEAST" value="3" />
<value id="WEST" value="4" />
<value id="CENTER" value="5" />
<value id="EAST" value="6" />
<value id="SOUTHWEST" value="7" />
<value id="SOUTH" value="8" />
<value id="SOUTHEAST" value="9" />
</enum>
</types>
<global>
@ -110,6 +122,7 @@
</section>
<section id="selection">
<option id="mode" type="app::tools::SelectionMode" default="app::tools::SelectionMode::DEFAULT" />
<option id="pivot" type="PivotMode" default="PivotMode::HIDDEN" />
<option id="opaque" type="bool" default="false" />
<option id="auto_opaque" type="bool" default="true" />
<option id="transparent_color" type="app::Color" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -377,6 +377,16 @@
<part id="ink_lock_alpha" x="176" y="144" w="16" h="16" />
<part id="selection_opaque" x="208" y="176" w="16" h="10" />
<part id="selection_masked" x="224" y="176" w="16" h="10" />
<part id="pivot_hidden" x="208" y="192" w="7" h="7" />
<part id="pivot_northwest" x="216" y="192" w="7" h="7" />
<part id="pivot_north" x="224" y="192" w="7" h="7" />
<part id="pivot_northeast" x="232" y="192" w="7" h="7" />
<part id="pivot_west" x="216" y="200" w="7" h="7" />
<part id="pivot_center" x="224" y="200" w="7" h="7" />
<part id="pivot_east" x="232" y="200" w="7" h="7" />
<part id="pivot_southwest" x="216" y="208" w="7" h="7" />
<part id="pivot_south" x="224" y="208" w="7" h="7" />
<part id="pivot_southeast" x="232" y="208" w="7" h="7" />
</parts>
<stylesheet>

View File

@ -322,6 +322,7 @@ add_library(app-lib
ui/editor/moving_cel_state.cpp
ui/editor/moving_pixels_state.cpp
ui/editor/navigate_state.cpp
ui/editor/pivot_helpers.cpp
ui/editor/pixels_movement.cpp
ui/editor/play_state.cpp
ui/editor/scrolling_state.cpp

View File

@ -555,8 +555,77 @@ private:
ContextBar* m_owner;
};
class ContextBar::RotAlgorithmField : public ComboBox
{
class ContextBar::PivotField : public ButtonSet {
public:
PivotField()
: ButtonSet(1) {
addItem(static_cast<SkinTheme*>(getTheme())->get_part(PART_PIVOT_HIDDEN));
ItemChange.connect(Bind<void>(&PivotField::onPopup, this));
Preferences::instance().selection.pivot.AfterChange.connect(
Bind<void>(&PivotField::onPivotChange, this));
onPivotChange();
}
private:
void onPopup() {
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
gfx::Rect bounds = getBounds();
Menu menu;
CheckBox hidden("Hidden pivot by default");
HBox box;
ButtonSet buttonset(3);
buttonset.addItem(theme->get_part(PART_PIVOT_NORTHWEST));
buttonset.addItem(theme->get_part(PART_PIVOT_NORTH));
buttonset.addItem(theme->get_part(PART_PIVOT_NORTHEAST));
buttonset.addItem(theme->get_part(PART_PIVOT_WEST));
buttonset.addItem(theme->get_part(PART_PIVOT_CENTER));
buttonset.addItem(theme->get_part(PART_PIVOT_EAST));
buttonset.addItem(theme->get_part(PART_PIVOT_SOUTHWEST));
buttonset.addItem(theme->get_part(PART_PIVOT_SOUTH));
buttonset.addItem(theme->get_part(PART_PIVOT_SOUTHEAST));
box.addChild(&buttonset);
menu.addChild(&hidden);
menu.addChild(new MenuSeparator);
menu.addChild(&box);
app::gen::PivotMode mode = Preferences::instance().selection.pivot();
if (mode == app::gen::PivotMode::HIDDEN)
hidden.setSelected(true);
else {
buttonset.setSelectedItem(int(mode)-1);
}
hidden.Click.connect(
[&hidden](Event&){
Preferences::instance().selection.pivot(app::gen::PivotMode::HIDDEN);
hidden.closeWindow();
});
buttonset.ItemChange.connect(
[&buttonset](){
Preferences::instance().selection.pivot(app::gen::PivotMode(buttonset.selectedItem()+1));
buttonset.closeWindow();
});
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
}
void onPivotChange() {
int part = PART_PIVOT_HIDDEN + int(Preferences::instance().selection.pivot());
getItem(0)->setIcon(
static_cast<SkinTheme*>(getTheme())->get_part(part));
}
};
class ContextBar::RotAlgorithmField : public ComboBox {
public:
RotAlgorithmField() {
// We use "m_lockChange" variable to avoid setting the rotation
@ -925,6 +994,7 @@ ContextBar::ContextBar()
m_selectionOptionsBox->addChild(m_dropPixels = new DropPixelsField());
m_selectionOptionsBox->addChild(m_selectionMode = new SelectionModeField);
m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField(this));
m_selectionOptionsBox->addChild(m_pivot = new PivotField);
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
addChild(m_brushType = new BrushTypeField(this));
@ -979,6 +1049,7 @@ ContextBar::ContextBar()
tooltipManager->addTooltipFor(m_inkOpacity, "Opacity (paint intensity)", BOTTOM);
tooltipManager->addTooltipFor(m_sprayWidth, "Spray Width", BOTTOM);
tooltipManager->addTooltipFor(m_spraySpeed, "Spray Speed", BOTTOM);
tooltipManager->addTooltipFor(m_pivot, "Rotation Pivot", BOTTOM);
tooltipManager->addTooltipFor(m_transparentColor, "Transparent Color", BOTTOM);
tooltipManager->addTooltipFor(m_rotAlgo, "Rotation Algorithm", BOTTOM);
tooltipManager->addTooltipFor(m_freehandAlgo, "Freehand trace algorithm", BOTTOM);
@ -1178,6 +1249,7 @@ void ContextBar::updateForTool(tools::Tool* tool)
m_sprayBox->setVisible(hasSprayOptions);
m_selectionOptionsBox->setVisible(hasSelectOptions);
m_selectionMode->setVisible(true);
m_pivot->setVisible(true);
m_dropPixels->setVisible(false);
m_selectBoxHelp->setVisible(false);

View File

@ -106,6 +106,7 @@ namespace app {
class SpraySpeedField;
class SelectionModeField;
class TransparentColorField;
class PivotField;
class RotAlgorithmField;
class FreehandAlgorithmField;
class BrushPatternField;
@ -135,6 +136,7 @@ namespace app {
ui::Box* m_selectionOptionsBox;
SelectionModeField* m_selectionMode;
TransparentColorField* m_transparentColor;
PivotField* m_pivot;
RotAlgorithmField* m_rotAlgo;
DropPixelsField* m_dropPixels;
doc::BrushRef m_activeBrush;

View File

@ -1457,8 +1457,8 @@ void Editor::pasteImage(const Image* image, const Mask* mask)
mask2.setOrigin(x, y);
PixelsMovementPtr pixelsMovement(
new PixelsMovement(UIContext::instance(),
getSite(), image, &mask2, "Paste"));
new PixelsMovement(UIContext::instance(), getSite(),
image, &mask2, "Paste"));
setState(EditorStatePtr(new MovingPixelsState(this, NULL, pixelsMovement, NoHandle)));
}

View File

@ -18,6 +18,7 @@
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/console.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/tools/ink.h"
#include "app/tools/tool.h"
@ -127,6 +128,13 @@ void MovingPixelsState::translate(const gfx::Point& delta)
m_pixelsMovement->dropImageTemporarily();
}
void MovingPixelsState::onEnterState(Editor* editor)
{
StandbyState::onEnterState(editor);
update_screen_for_document(editor->document());
}
EditorState::LeaveAction MovingPixelsState::onLeaveState(Editor* editor, EditorState* newState)
{
ASSERT(m_pixelsMovement);

View File

@ -36,6 +36,7 @@ namespace app {
void translate(const gfx::Point& delta);
// EditorState
virtual void onEnterState(Editor* editor) override;
virtual LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
virtual void onCurrentToolChange(Editor* editor) override;
virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;

View File

@ -0,0 +1,60 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/ui/editor/pivot_helpers.h"
#include "app/pref/preferences.h"
#include "gfx/transformation.h"
namespace app {
void set_pivot_from_preferences(gfx::Transformation& t)
{
gfx::Transformation::Corners corners;
t.transformBox(corners);
gfx::PointT<double> nw(corners[gfx::Transformation::Corners::LEFT_TOP]);
gfx::PointT<double> ne(corners[gfx::Transformation::Corners::RIGHT_TOP]);
gfx::PointT<double> sw(corners[gfx::Transformation::Corners::LEFT_BOTTOM]);
gfx::PointT<double> se(corners[gfx::Transformation::Corners::RIGHT_BOTTOM]);
gfx::PointT<double> pivotPos((nw + se) / 2);
app::gen::PivotMode pivotMode = Preferences::instance().selection.pivot();
switch (pivotMode) {
case app::gen::PivotMode::NORTHWEST:
pivotPos = nw;
break;
case app::gen::PivotMode::NORTH:
pivotPos = (nw + ne) / 2.0;
break;
case app::gen::PivotMode::NORTHEAST:
pivotPos = ne;
break;
case app::gen::PivotMode::WEST:
pivotPos = (nw + sw) / 2.0;
break;
case app::gen::PivotMode::EAST:
pivotPos = (ne + se) / 2.0;
break;
case app::gen::PivotMode::SOUTHWEST:
pivotPos = sw;
break;
case app::gen::PivotMode::SOUTH:
pivotPos = (sw + se) / 2.0;
break;
case app::gen::PivotMode::SOUTHEAST:
pivotPos = se;
break;
}
t.displacePivotTo(gfx::Point(pivotPos));
}
} // namespace app

View File

@ -0,0 +1,22 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifndef APP_UI_EDITOR_PIVOT_HELPERS_H_INCLUDED
#define APP_UI_EDITOR_PIVOT_HELPERS_H_INCLUDED
#pragma once
namespace gfx {
class Transformation;
}
namespace app {
void set_pivot_from_preferences(gfx::Transformation& t);
} // namespace app
#endif

View File

@ -21,6 +21,7 @@
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/snap_to_grid.h"
#include "app/ui/editor/pivot_helpers.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "app/util/expand_cel_canvas.h"
@ -66,12 +67,18 @@ PixelsMovement::PixelsMovement(
, m_originalImage(Image::createCopy(moveThis))
, m_maskColor(m_sprite->transparentColor())
{
m_initialData = gfx::Transformation(mask->bounds());
m_currentData = m_initialData;
gfx::Transformation transform(mask->bounds());
set_pivot_from_preferences(transform);
m_initialData = transform;
m_currentData = transform;
m_initialMask = new Mask(*mask);
m_currentMask = new Mask(*mask);
m_pivotConn =
Preferences::instance().selection.pivot.AfterChange.connect(
Bind<void>(&PixelsMovement::onPivotChange, this));
m_rotAlgoConn =
Preferences::instance().selection.rotationAlgorithm.AfterChange.connect(
Bind<void>(&PixelsMovement::onRotationAlgorithmChange, this));
@ -84,7 +91,6 @@ PixelsMovement::PixelsMovement(
redrawCurrentMask();
updateDocumentMask();
update_screen_for_document(m_document);
}
PixelsMovement::~PixelsMovement()
@ -710,6 +716,12 @@ retry:; // In case that we don't have enough memory for RotSprite
}
}
void PixelsMovement::onPivotChange()
{
set_pivot_from_preferences(m_currentData);
onRotationAlgorithmChange();
}
void PixelsMovement::onRotationAlgorithmChange()
{
try {

View File

@ -89,6 +89,7 @@ namespace app {
const gfx::Transformation& getTransformation() const { return m_currentData; }
private:
void onPivotChange();
void onRotationAlgorithmChange();
void redrawExtraImage();
void redrawCurrentMask();
@ -117,6 +118,7 @@ namespace app {
Mask* m_currentMask;
bool m_opaque;
color_t m_maskColor;
ScopedConnection m_pivotConn;
ScopedConnection m_rotAlgoConn;
};

View File

@ -13,8 +13,8 @@
#include "app/app.h"
#include "app/color_picker.h"
#include "app/commands/commands.h"
#include "app/commands/cmd_eyedropper.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/ini_file.h"
#include "app/pref/preferences.h"
@ -27,6 +27,7 @@
#include "app/ui/editor/handle_type.h"
#include "app/ui/editor/moving_cel_state.h"
#include "app/ui/editor/moving_pixels_state.h"
#include "app/ui/editor/pivot_helpers.h"
#include "app/ui/editor/pixels_movement.h"
#include "app/ui/editor/scrolling_state.h"
#include "app/ui/editor/tool_loop_impl.h"
@ -35,6 +36,7 @@
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "app/util/new_image_from_mask.h"
#include "base/bind.h"
#include "base/pi.h"
#include "doc/layer.h"
#include "doc/mask.h"
@ -89,6 +91,10 @@ StandbyState::~StandbyState()
void StandbyState::onEnterState(Editor* editor)
{
editor->setDecorator(m_decorator);
m_pivotConn =
Preferences::instance().selection.pivot.AfterChange.connect(
Bind<void>(&StandbyState::onPivotChange, this, editor));
}
void StandbyState::onCurrentToolChange(Editor* editor)
@ -392,7 +398,9 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
gfx::Transformation StandbyState::getTransformation(Editor* editor)
{
return editor->document()->getTransformation();
gfx::Transformation t = editor->document()->getTransformation();
set_pivot_from_preferences(t);
return t;
}
void StandbyState::startSelectionTransformation(Editor* editor, const gfx::Point& move)
@ -458,6 +466,16 @@ void StandbyState::callEyedropper(Editor* editor)
UIContext::instance()->executeCommand(eyedropper_cmd, params);
}
void StandbyState::onPivotChange(Editor* editor)
{
if (editor->isActive() &&
editor->editorFlags() & Editor::kShowMask &&
editor->document()->isMaskVisible() &&
!editor->document()->mask()->isFrozen()) {
editor->invalidate();
}
}
//////////////////////////////////////////////////////////////////////
// Decorator

View File

@ -12,6 +12,7 @@
#include "app/ui/editor/editor_decorator.h"
#include "app/ui/editor/handle_type.h"
#include "app/ui/editor/state_with_wheel_behavior.h"
#include "base/connection.h"
#include "gfx/transformation.h"
namespace app {
@ -66,8 +67,10 @@ namespace app {
private:
void transformSelection(Editor* editor, ui::MouseMessage* msg, HandleType handle);
void onPivotChange(Editor* editor);
Decorator* m_decorator;
ScopedConnection m_pivotConn;
};
} // namespace app

View File

@ -11,6 +11,7 @@
#include "app/ui/editor/transform_handles.h"
#include "app/pref/preferences.h"
#include "app/ui/editor/editor.h"
#include "app/ui/skin/skin_theme.h"
#include "base/pi.h"
@ -51,14 +52,6 @@ static struct HandlesInfo {
{ 2, 2, 224 << 16, { ScaleSEHandle, RotateSEHandle } },
};
TransformHandles::TransformHandles()
{
}
TransformHandles::~TransformHandles()
{
}
HandleType TransformHandles::getHandleAtPoint(Editor* editor, const gfx::Point& pt, const gfx::Transformation& transform)
{
SkinTheme* theme = static_cast<SkinTheme*>(CurrentTheme::get());
@ -88,7 +81,7 @@ HandleType TransformHandles::getHandleAtPoint(Editor* editor, const gfx::Point&
}
// Check if the cursor is in the pivot
if (angle != 0 && getPivotHandleBounds(editor, transform, corners).contains(pt))
if (visiblePivot(angle) && getPivotHandleBounds(editor, transform, corners).contains(pt))
return PivotHandle;
return NoHandle;
@ -111,19 +104,16 @@ void TransformHandles::drawHandles(Editor* editor, const gfx::Transformation& tr
#if 0 // Uncomment this if you want to see the bounds in red (only for debugging purposes)
// -----------------------------------------------
{
int x1, y1, x2, y2;
x1 = transform.bounds().x;
y1 = transform.bounds().y;
x2 = x1 + transform.bounds().w;
y2 = y1 + transform.bounds().h;
editor->editorToScreen(x1, y1, &x1, &y1);
editor->editorToScreen(x2, y2, &x2, &y2);
g.drawRect(gfx::rgba(255, 0, 0), gfx::Rect(x1, y1, x2-x1+1, y2-y1+1));
gfx::Point
a(transform.bounds().getOrigin()),
b(transform.bounds().getPoint2());
a = editor->editorToScreen(a);
b = editor->editorToScreen(b);
g.drawRect(gfx::rgba(255, 0, 0), gfx::Rect(a, b));
x1 = transform.pivot().x;
y1 = transform.pivot().y;
editor->editorToScreen(x1, y1, &x1, &y1);
g.drawRect(gfx::rgba(255, 0, 0), gfx::Rect(x1-2, y1-2, 5, 5));
a = transform.pivot();
a = editor->editorToScreen(a);
g.drawRect(gfx::rgba(255, 0, 0), gfx::Rect(a.x-2, a.y-2, 5, 5));
}
// -----------------------------------------------
#endif
@ -137,7 +127,7 @@ void TransformHandles::drawHandles(Editor* editor, const gfx::Transformation& tr
}
// Draw the pivot
if (angle != 0) {
if (visiblePivot(angle)) {
gfx::Rect pivotBounds = getPivotHandleBounds(editor, transform, corners);
SkinTheme* theme = static_cast<SkinTheme*>(CurrentTheme::get());
she::Surface* part = theme->get_part(PART_PIVOT_HANDLE);
@ -171,7 +161,7 @@ void TransformHandles::invalidateHandles(Editor* editor, const gfx::Transformati
}
// Invalidate area where the pivot is.
if (angle != 0) {
if (visiblePivot(angle)) {
gfx::Rect pivotBounds = getPivotHandleBounds(editor, transform, corners);
she::Surface* part = theme->get_part(PART_PIVOT_HANDLE);
@ -264,4 +254,10 @@ void TransformHandles::adjustHandle(int& x, int& y, int handle_w, int handle_h,
}
}
bool TransformHandles::visiblePivot(fixmath::fixed angle) const
{
return (Preferences::instance().selection.pivot() != app::gen::PivotMode::HIDDEN ||
angle != 0);
}
} // namespace app

View File

@ -25,9 +25,6 @@ namespace app {
// and rotation pivot.
class TransformHandles {
public:
TransformHandles();
~TransformHandles();
// Returns the handle in the given mouse point (pt) when the user
// has applied the given transformation to the selection.
HandleType getHandleAtPoint(Editor* editor, const gfx::Point& pt, const gfx::Transformation& transform);
@ -43,6 +40,7 @@ namespace app {
bool inHandle(const gfx::Point& pt, int x, int y, int gfx_w, int gfx_h, fixmath::fixed angle);
void drawHandle(ui::Graphics* g, int x, int y, fixmath::fixed angle);
void adjustHandle(int& x, int& y, int handle_w, int handle_h, fixmath::fixed angle);
bool visiblePivot(fixmath::fixed angle) const;
};
} // namespace app

View File

@ -183,6 +183,17 @@ namespace app {
PART_SELECTION_OPAQUE,
PART_SELECTION_MASKED,
PART_PIVOT_HIDDEN,
PART_PIVOT_NORTHWEST,
PART_PIVOT_NORTH,
PART_PIVOT_NORTHEAST,
PART_PIVOT_WEST,
PART_PIVOT_CENTER,
PART_PIVOT_EAST,
PART_PIVOT_SOUTHWEST,
PART_PIVOT_SOUTH,
PART_PIVOT_SOUTHEAST,
PARTS
};

View File

@ -283,6 +283,16 @@ SkinTheme::SkinTheme()
sheet_mapping["ink_lock_alpha"] = PART_INK_LOCK_ALPHA;
sheet_mapping["selection_opaque"] = PART_SELECTION_OPAQUE;
sheet_mapping["selection_masked"] = PART_SELECTION_MASKED;
sheet_mapping["pivot_hidden"] = PART_PIVOT_HIDDEN;
sheet_mapping["pivot_northwest"] = PART_PIVOT_NORTHWEST;
sheet_mapping["pivot_north"] = PART_PIVOT_NORTH;
sheet_mapping["pivot_northeast"] = PART_PIVOT_NORTHEAST;
sheet_mapping["pivot_west"] = PART_PIVOT_WEST;
sheet_mapping["pivot_center"] = PART_PIVOT_CENTER;
sheet_mapping["pivot_east"] = PART_PIVOT_EAST;
sheet_mapping["pivot_southwest"] = PART_PIVOT_SOUTHWEST;
sheet_mapping["pivot_south"] = PART_PIVOT_SOUTH;
sheet_mapping["pivot_southeast"] = PART_PIVOT_SOUTHEAST;
}
SkinTheme::~SkinTheme()