Add possibility to drag & drop the selection

This commit is contained in:
David Capello 2017-04-06 15:26:01 -03:00
parent 5246c8341a
commit 90c364fe30
10 changed files with 232 additions and 10 deletions

View File

@ -188,6 +188,7 @@
</section>
<section id="selection">
<option id="mode" type="SelectionMode" default="SelectionMode::DEFAULT" />
<option id="move_edges" type="bool" default="true" />
<option id="pivot_visibility" type="bool" default="false" />
<option id="pivot_position" type="PivotPosition" default="PivotPosition::CENTER" />
<option id="opaque" type="bool" default="false" />

View File

@ -435,6 +435,7 @@ add_library(app-lib
ui/editor/editor_view.cpp
ui/editor/moving_cel_state.cpp
ui/editor/moving_pixels_state.cpp
ui/editor/moving_selection_state.cpp
ui/editor/moving_slice_state.cpp
ui/editor/moving_symmetry_state.cpp
ui/editor/navigate_state.cpp

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -16,7 +16,8 @@ namespace app {
NoHandle,
// This is the handle to move the pixels region, generally, the
// whole region activates this handle.
MoveHandle,
MovePixelsHandle,
MoveSelectionHandle,
// One of the region's corders to scale.
ScaleNWHandle, ScaleNHandle, ScaleNEHandle,
ScaleWHandle, ScaleEHandle,

View File

@ -204,7 +204,7 @@ bool MovingCelState::onMouseMove(Editor* editor, MouseMessage* msg)
switch (m_handle) {
case MoveHandle:
case MovePixelsHandle:
m_celOffset = newCursorPos - m_cursorStart;
if (int(editor->getCustomizationDelegate()
->getPressedKeyAction(KeyContext::TranslatingSelection) & KeyAction::LockAxis)) {

View File

@ -125,7 +125,7 @@ void MovingPixelsState::translate(const gfx::Point& delta)
if (m_pixelsMovement->isDragging())
m_pixelsMovement->dropImageTemporarily();
m_pixelsMovement->catchImageAgain(gfx::Point(0, 0), MoveHandle);
m_pixelsMovement->catchImageAgain(gfx::Point(0, 0), MovePixelsHandle);
m_pixelsMovement->moveImage(delta, PixelsMovement::NormalMovement);
m_pixelsMovement->dropImageTemporarily();
}
@ -287,7 +287,7 @@ bool MovingPixelsState::onMouseDown(Editor* editor, MouseMessage* msg)
// Re-catch the image
m_pixelsMovement->catchImageAgain(
editor->screenToEditor(msg->position()), MoveHandle);
editor->screenToEditor(msg->position()), MovePixelsHandle);
editor->captureMouse();
return true;
@ -333,7 +333,7 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg)
// Get the customization for the pixels movement (snap to grid, angle snap, etc.).
KeyContext keyContext = KeyContext::Normal;
switch (m_pixelsMovement->handle()) {
case MoveHandle:
case MovePixelsHandle:
keyContext = KeyContext::TranslatingSelection;
break;
case ScaleNWHandle:

View File

@ -0,0 +1,135 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// 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/editor/moving_selection_state.h"
#include "app/cmd/set_mask_position.h"
#include "app/context_access.h"
#include "app/transaction.h"
#include "app/ui/editor/editor.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "doc/mask.h"
#include "ui/message.h"
namespace app {
using namespace ui;
MovingSelectionState::MovingSelectionState(Editor* editor, MouseMessage* msg)
: m_cursorStart(editor->screenToEditor(msg->position()))
, m_selOrigin(editor->document()->mask()->bounds().origin())
{
editor->captureMouse();
}
MovingSelectionState::~MovingSelectionState()
{
}
void MovingSelectionState::onEnterState(Editor* editor)
{
StandbyState::onEnterState(editor);
editor->document()->mask()->freeze();
}
EditorState::LeaveAction MovingSelectionState::onLeaveState(Editor* editor, EditorState* newState)
{
Document* doc = editor->document();
Mask* mask = doc->mask();
gfx::Point newOrigin = mask->bounds().origin();
// Restore the mask to the original state so we can transform it
// with the a undoable transaction.
mask->setOrigin(m_selOrigin.x,
m_selOrigin.y);
mask->unfreeze();
{
ContextWriter writer(UIContext::instance(), 1000);
Transaction transaction(writer.context(), "Move Selection Edges", DoesntModifyDocument);
transaction.execute(new cmd::SetMaskPosition(doc, newOrigin));
transaction.commit();
}
doc->resetTransformation();
doc->notifyGeneralUpdate();
return StandbyState::onLeaveState(editor, newState);
}
bool MovingSelectionState::onMouseDown(Editor* editor, MouseMessage* msg)
{
// Do nothing
return true;
}
bool MovingSelectionState::onMouseUp(Editor* editor, MouseMessage* msg)
{
editor->backToPreviousState();
editor->releaseMouse();
return true;
}
bool MovingSelectionState::onMouseMove(Editor* editor, MouseMessage* msg)
{
const gfx::Point newCursorPos = editor->screenToEditor(msg->position());
m_delta = newCursorPos - m_cursorStart;
const gfx::Point newMaskOrigin = m_selOrigin + m_delta;
const gfx::Point oldMaskOrigin = editor->document()->mask()->bounds().origin();
if (oldMaskOrigin != newMaskOrigin) {
editor->document()->mask()->setOrigin(newMaskOrigin.x,
newMaskOrigin.y);
MaskBoundaries* boundaries =
const_cast<MaskBoundaries*>(editor->document()->getMaskBoundaries());
const gfx::Point boundariesDelta = newMaskOrigin - oldMaskOrigin;
boundaries->offset(boundariesDelta.x,
boundariesDelta.y);
editor->invalidate();
}
// Use StandbyState implementation
return StandbyState::onMouseMove(editor, msg);
}
bool MovingSelectionState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
{
editor->showMouseCursor(kMoveCursor);
return true;
}
bool MovingSelectionState::onKeyDown(Editor* editor, KeyMessage* msg)
{
// Use StandbyState implementation
return StandbyState::onKeyDown(editor, msg);
}
bool MovingSelectionState::onKeyUp(Editor* editor, KeyMessage* msg)
{
// Use StandbyState implementation
return StandbyState::onKeyUp(editor, msg);
}
bool MovingSelectionState::onUpdateStatusBar(Editor* editor)
{
const gfx::Rect bounds = editor->document()->mask()->bounds();
StatusBar::instance()->setStatusText
(100, ":pos: %d %d :size: %3d %3d :offset: %d %d",
bounds.x, bounds.y,
bounds.w, bounds.h,
m_delta.x, m_delta.y);
return true;
}
} // namespace app

View File

@ -0,0 +1,39 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UI_EDITOR_MOVING_SELECTION_STATE_H_INCLUDED
#define APP_UI_EDITOR_MOVING_SELECTION_STATE_H_INCLUDED
#pragma once
#include "app/ui/editor/standby_state.h"
namespace app {
class MovingSelectionState : public StandbyState {
public:
MovingSelectionState(Editor* editor, ui::MouseMessage* msg);
virtual ~MovingSelectionState();
// EditorState
virtual void onEnterState(Editor* editor) override;
virtual LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override;
virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override;
virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override;
virtual bool onUpdateStatusBar(Editor* editor) override;
virtual bool requireBrushPreview() override { return false; }
private:
gfx::Point m_cursorStart;
gfx::Point m_selOrigin;
gfx::Point m_delta;
};
} // namespace app
#endif // APP_UI_EDITOR_MOVING_PIXELS_STATE_H_INCLUDED

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -255,7 +255,7 @@ void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier)
switch (m_handle) {
case MoveHandle:
case MovePixelsHandle:
if ((moveModifier & LockAxisMovement) == LockAxisMovement) {
if (ABS(dx) < ABS(dy))
dx = 0.0;

View File

@ -31,6 +31,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/moving_selection_state.h"
#include "app/ui/editor/moving_slice_state.h"
#include "app/ui/editor/moving_symmetry_state.h"
#include "app/ui/editor/pivot_helpers.h"
@ -194,7 +195,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
else {
try {
// Change to MovingCelState
HandleType handle = MoveHandle;
HandleType handle = MovePixelsHandle;
if (resizeCelBounds(editor).contains(msg->position()))
handle = ScaleSEHandle;
@ -282,6 +283,12 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
}
}
// Move selection edges
if (overSelectionEdges(editor, msg->position())) {
transformSelection(editor, msg, MoveSelectionHandle);
return true;
}
// Move selected pixels
if (layer && editor->isInsideSelection() && msg->left()) {
if (!layer->isEditableHierarchy()) {
@ -291,7 +298,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
}
// Change to MovingPixelsState
transformSelection(editor, msg, MoveHandle);
transformSelection(editor, msg, MovePixelsHandle);
return true;
}
}
@ -405,6 +412,11 @@ bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
if (ink) {
// If the current tool change selection (e.g. rectangular marquee, etc.)
if (ink->isSelection()) {
if (overSelectionEdges(editor, mouseScreenPos)) {
editor->showMouseCursor(kHandCursor); // TODO create a new mouse cursor
return true;
}
// Move pixels
if (editor->isInsideSelection()) {
EditorCustomizationDelegate* customization = editor->getCustomizationDelegate();
@ -612,6 +624,13 @@ void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleT
}
}
// Special case: Move only selection edges
if (handle == MoveSelectionHandle) {
EditorStatePtr newState(new MovingSelectionState(editor, msg));
editor->setState(newState);
return;
}
Layer* layer = editor->layer();
if (layer && layer->isReference()) {
StatusBar::instance()->showTip(
@ -699,6 +718,31 @@ gfx::Rect StandbyState::resizeCelBounds(Editor* editor) const
return bounds;
}
bool StandbyState::overSelectionEdges(Editor* editor,
const gfx::Point& mouseScreenPos) const
{
// Move selection edges
if (editor->isActive() &&
editor->document()->isMaskVisible() &&
editor->document()->getMaskBoundaries() &&
Preferences::instance().selection.moveEdges()) {
// For each selection edge
for (const auto& seg : *editor->document()->getMaskBoundaries()) {
gfx::Rect segBounds = editor->editorToScreen(seg.bounds());
if (seg.vertical())
segBounds.w = 1;
else
segBounds.h = 1;
if (gfx::Rect(segBounds).enlarge(2*guiscale()).contains(mouseScreenPos) &&
!gfx::Rect(segBounds).shrink(2*guiscale()).contains(mouseScreenPos)) {
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Decorator

View File

@ -79,6 +79,7 @@ namespace app {
void transformSelection(Editor* editor, ui::MouseMessage* msg, HandleType handle);
void onPivotChange(Editor* editor);
gfx::Rect resizeCelBounds(Editor* editor) const;
bool overSelectionEdges(Editor* editor, const gfx::Point& mouseScreenPos) const;
Decorator* m_decorator;
obs::scoped_connection m_pivotVisConn;