Change selection behavior: left-click replace the selection

With this patch we replace the "unique/odd" behavior of Aseprite where
you add selection regions with left-click and remove with right-click.
Now by default you replace the selection with left-click (as in regular
gfx programs). Also you can change the selection tool behavior with
buttons/icons at the context bar (to select between replace/add/subtract).
This commit is contained in:
David Capello 2014-01-25 17:58:29 -03:00
parent 2d7db879a3
commit 9423b967ab
17 changed files with 162 additions and 22 deletions

View File

@ -465,7 +465,7 @@
intertwine="as_rectangles"
tracepolicy="last">
<tooltip>*
Left-button: add to current selection.&#10;*
Left-button: replace/add to current selection.&#10;*
Right-button: remove from current selection.
</tooltip>
</tool>
@ -479,8 +479,8 @@
intertwine="as_ellipses"
tracepolicy="last">
<tooltip>*
Left-button: add to current selection.&#10;*
Right-button: remove from current selection.
Left-button: Replace/add to current selection.&#10;*
Right-button: Remove from current selection.
</tooltip>
</tool>
@ -493,8 +493,8 @@
intertwine="as_lines"
tracepolicy="accumulative">
<tooltip>*
Left-button: add to current selection.&#10;*
Right-button: remove from current selection.
Left-button: Replace/add to current selection.&#10;*
Right-button: Remove from current selection.
</tooltip>
</tool>
@ -507,8 +507,8 @@
intertwine="as_lines"
tracepolicy="last">
<tooltip>*
Left-button: add to current selection.&#10;*
Right-button: remove from current selection.
Left-button: Replace/add to current selection.&#10;*
Right-button: Remove from current selection.
</tooltip>
</tool>
@ -520,8 +520,8 @@
pointshape="floodfill"
tracepolicy="accumulative">
<tooltip>*
Left-button: add to current selection.&#10;*
Right-button: remove from current selection.
Left-button: Replace/add to current selection.&#10;*
Right-button: Remove from current selection.
</tooltip>
</tool>
</group>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -258,6 +258,12 @@
<part id="layer_editable_selected" x="144" y="184" w="8" h="8" />
<part id="layer_locked" x="160" y="176" w="8" h="8" />
<part id="layer_locked_selected" x="160" y="184" w="8" h="8" />
<part id="selection_replace" x="176" y="160" w="7" h="7" />
<part id="selection_replace_selected" x="176" y="168" w="7" h="7" />
<part id="selection_add" x="192" y="160" w="7" h="7" />
<part id="selection_add_selected" x="192" y="168" w="7" h="7" />
<part id="selection_subtract" x="208" y="160" w="7" h="7" />
<part id="selection_subtract_selected" x="208" y="168" w="7" h="7" />
<part id="unpinned" x="192" y="144" w="8" h="8" />
<part id="pinned" x="200" y="144" w="8" h="8" />
<part id="drop_down_button_left_normal" x="48" y="32" w1="3" w2="2" w3="3" h1="4" h2="6" h3="6" />

View File

@ -0,0 +1,35 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_SETTINGS_SELECTION_MODE_H_INCLUDED
#define APP_SETTINGS_SELECTION_MODE_H_INCLUDED
namespace app {
enum SelectionMode {
kDefaultSelectionMode,
kAddSelectionMode,
kSubtractSelectionMode,
kFirstSelectionMode = kDefaultSelectionMode,
kLastSelectionMode = kSubtractSelectionMode
};
} // namespace app
#endif

View File

@ -23,6 +23,7 @@
#include "app/settings/freehand_algorithm.h"
#include "app/settings/ink_type.h"
#include "app/settings/rotation_algorithm.h"
#include "app/settings/selection_mode.h"
#include "gfx/point.h"
#include "gfx/rect.h"
#include "raster/pen_type.h"
@ -129,9 +130,11 @@ namespace app {
virtual ~ISelectionSettings() {}
// Mask color used during a move operation
virtual SelectionMode getSelectionMode() = 0;
virtual app::Color getMoveTransparentColor() = 0;
virtual RotationAlgorithm getRotationAlgorithm() = 0;
virtual void setSelectionMode(SelectionMode mode) = 0;
virtual void setMoveTransparentColor(app::Color color) = 0;
virtual void setRotationAlgorithm(RotationAlgorithm algorithm) = 0;

View File

@ -23,6 +23,7 @@
#include "app/settings/freehand_algorithm.h"
#include "app/settings/ink_type.h"
#include "app/settings/rotation_algorithm.h"
#include "app/settings/selection_mode.h"
#include "raster/pen_type.h"
namespace app {
@ -59,6 +60,7 @@ namespace app {
public:
virtual ~SelectionSettingsObserver() {}
virtual void onSetSelectionMode(SelectionMode mode) {}
virtual void onSetMoveTransparentColor(app::Color newColor) {}
virtual void onSetRotationAlgorithm(RotationAlgorithm algorithm) {}
};

View File

@ -150,9 +150,11 @@ public:
UISelectionSettingsImpl();
~UISelectionSettingsImpl();
SelectionMode getSelectionMode();
app::Color getMoveTransparentColor();
RotationAlgorithm getRotationAlgorithm();
void setSelectionMode(SelectionMode mode);
void setMoveTransparentColor(app::Color color);
void setRotationAlgorithm(RotationAlgorithm algorithm);
@ -160,6 +162,7 @@ public:
void removeObserver(SelectionSettingsObserver* observer);
private:
SelectionMode m_selectionMode;
app::Color m_moveTransparentColor;
RotationAlgorithm m_rotationAlgorithm;
};
@ -674,9 +677,16 @@ IToolSettings* UISettingsImpl::getToolSettings(tools::Tool* tool)
namespace {
UISelectionSettingsImpl::UISelectionSettingsImpl() :
m_selectionMode(kDefaultSelectionMode),
m_moveTransparentColor(app::Color::fromMask()),
m_rotationAlgorithm(kFastRotationAlgorithm)
{
m_selectionMode = (SelectionMode)get_config_int("Tools", "SelectionMode", m_selectionMode);
m_selectionMode = MID(
kFirstSelectionMode,
m_selectionMode,
kLastSelectionMode);
m_rotationAlgorithm = (RotationAlgorithm)get_config_int("Tools", "RotAlgorithm", m_rotationAlgorithm);
m_rotationAlgorithm = MID(
kFirstRotationAlgorithm,
@ -688,6 +698,11 @@ UISelectionSettingsImpl::~UISelectionSettingsImpl()
{
}
SelectionMode UISelectionSettingsImpl::getSelectionMode()
{
return m_selectionMode;
}
app::Color UISelectionSettingsImpl::getMoveTransparentColor()
{
return m_moveTransparentColor;
@ -698,6 +713,12 @@ RotationAlgorithm UISelectionSettingsImpl::getRotationAlgorithm()
return m_rotationAlgorithm;
}
void UISelectionSettingsImpl::setSelectionMode(SelectionMode mode)
{
m_selectionMode = mode;
notifyObservers(&SelectionSettingsObserver::onSetSelectionMode, mode);
}
void UISelectionSettingsImpl::setMoveTransparentColor(app::Color color)
{
m_moveTransparentColor = color;

View File

@ -275,13 +275,20 @@ public:
if (m_modify_selection) {
Point offset = loop->getOffset();
if (loop->getMouseButton() == ToolLoop::Left)
loop->getMask()->add(x1-offset.x, y-offset.y, x2-x1+1, 1);
else if (loop->getMouseButton() == ToolLoop::Right)
loop->getMask()->subtract(x1-offset.x, y-offset.y, x2-x1+1, 1);
switch (loop->getSelectionMode()) {
case kDefaultSelectionMode:
case kAddSelectionMode:
loop->getMask()->add(x1-offset.x, y-offset.y, x2-x1+1, 1);
break;
case kSubtractSelectionMode:
loop->getMask()->subtract(x1-offset.x, y-offset.y, x2-x1+1, 1);
break;
}
}
else
// TODO show the selection-preview with a XOR color or something like that
else {
draw_hline(loop->getDstImage(), x1, y, x2, loop->getPrimaryColor());
}
}
void setFinalStep(ToolLoop* loop, bool state)

View File

@ -19,6 +19,7 @@
#ifndef APP_TOOLS_TOOL_LOOP_H_INCLUDED
#define APP_TOOLS_TOOL_LOOP_H_INCLUDED
#include "app/settings/selection_mode.h"
#include "app/tools/trace_policy.h"
#include "filters/tiled_mode.h"
#include "gfx/point.h"
@ -122,6 +123,9 @@ namespace app {
// Returns the tolerance to be used by the ink (Ink).
virtual int getTolerance() = 0;
// Returns the selection mode (if the ink is of selection type).
virtual SelectionMode getSelectionMode() = 0;
// Returns the current settings. Used to know current
// foreground/background color (certain tools needs to know the
// exact foreground/background color, they cannot used the

View File

@ -136,6 +136,11 @@ void ButtonSet::setSelectedItem(int index)
}
}
Widget* ButtonSet::getButtonAt(int index)
{
return m_items[index];
}
ButtonSet::Item* ButtonSet::findSelectedItem() const
{
for (Items::const_iterator it=m_items.begin(), end=m_items.end();

View File

@ -34,10 +34,12 @@ namespace app {
int getSelectedItem() const;
void setSelectedItem(int index);
ui::Widget* getButtonAt(int index);
Signal0<void> ItemChange;
protected:
void onItemChange();
virtual void onItemChange();
private:
Item* findSelectedItem() const;

View File

@ -25,6 +25,7 @@
#include "app/app.h"
#include "app/modules/gui.h"
#include "app/settings/ink_type.h"
#include "app/settings/selection_mode.h"
#include "app/settings/settings.h"
#include "app/settings/settings_observers.h"
#include "app/tools/controller.h"
@ -407,6 +408,32 @@ public:
}
};
class ContextBar::SelectionModeField : public ButtonSet
{
public:
SelectionModeField() : ButtonSet(3, 1, 0,
PART_SELECTION_REPLACE,
PART_SELECTION_ADD,
PART_SELECTION_SUBTRACT) {
}
void setupTooltips(TooltipManager* tooltipManager)
{
tooltipManager->addTooltipFor(getButtonAt(0), "Replace selection", JI_CENTER | JI_BOTTOM);
tooltipManager->addTooltipFor(getButtonAt(1), "Add to selection", JI_CENTER | JI_BOTTOM);
tooltipManager->addTooltipFor(getButtonAt(2), "Subtract from selection", JI_CENTER | JI_BOTTOM);
}
protected:
void onItemChange() OVERRIDE {
ButtonSet::onItemChange();
int item = getSelectedItem();
UIContext::instance()->settings()->selection()
->setSelectionMode((SelectionMode)item);
}
};
ContextBar::ContextBar()
: Box(JI_HORIZONTAL)
{
@ -415,6 +442,13 @@ ContextBar::ContextBar()
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
setBgColor(theme->getColor(ThemeColor::Workspace));
addChild(m_selectionOptionsBox = new HBox());
m_selectionOptionsBox->addChild(m_selectionMode = new SelectionModeField);
m_selectionOptionsBox->addChild(new Label("Transparent Color:"));
m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField);
m_selectionOptionsBox->addChild(new Label("Rotation Algorithm:"));
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
addChild(m_brushLabel = new Label("Brush:"));
addChild(m_brushType = new BrushTypeField());
addChild(m_brushSize = new BrushSizeField());
@ -437,12 +471,6 @@ ContextBar::ContextBar()
m_sprayBox->addChild(m_sprayWidth = new SprayWidthField());
m_sprayBox->addChild(m_spraySpeed = new SpraySpeedField());
addChild(m_selectionOptionsBox = new HBox());
m_selectionOptionsBox->addChild(new Label("Transparent Color:"));
m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField);
m_selectionOptionsBox->addChild(new Label("Rotation Algorithm:"));
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
addChild(m_freehandBox = new HBox());
m_freehandBox->addChild(m_freehandAlgo = new FreehandAlgorithmField());
@ -457,6 +485,7 @@ ContextBar::ContextBar()
tooltipManager->addTooltipFor(m_spraySpeed, "Spray Speed", JI_CENTER | JI_BOTTOM);
tooltipManager->addTooltipFor(m_transparentColor, "Transparent Color", JI_BOTTOM | JI_BOTTOM);
tooltipManager->addTooltipFor(m_freehandAlgo, "Freehand trace algorithm", JI_CENTER | JI_BOTTOM);
m_selectionMode->setupTooltips(tooltipManager);
App::instance()->PenSizeAfterChange.connect(&ContextBar::onPenSizeChange, this);
App::instance()->PenAngleAfterChange.connect(&ContextBar::onPenAngleChange, this);

View File

@ -50,6 +50,7 @@ namespace app {
class InkOpacityField;
class SprayWidthField;
class SpraySpeedField;
class SelectionModeField;
class TransparentColorField;
class RotAlgorithmField;
class FreehandAlgorithmField;
@ -70,6 +71,7 @@ namespace app {
SprayWidthField* m_sprayWidth;
SpraySpeedField* m_spraySpeed;
ui::Box* m_selectionOptionsBox;
SelectionModeField* m_selectionMode;
TransparentColorField* m_transparentColor;
RotAlgorithmField* m_rotAlgo;
};

View File

@ -1098,6 +1098,7 @@ bool Editor::isInsideSelection()
int x, y;
screenToEditor(jmouse_x(0), jmouse_y(0), &x, &y);
return
(UIContext::instance()->settings()->selection()->getSelectionMode() != kSubtractSelectionMode) &&
m_document != NULL &&
m_document->isMaskVisible() &&
m_document->getMask()->containsPoint(x, y);

View File

@ -83,6 +83,7 @@ class ToolLoopImpl : public tools::ToolLoop,
tools::Ink* m_ink;
int m_primary_color;
int m_secondary_color;
SelectionMode m_selectionMode;
UndoTransaction m_undoTransaction;
ExpandCelCanvas m_expandCelCanvas;
gfx::Region m_dirtyArea;
@ -112,6 +113,7 @@ public:
, m_ink(getInkFromType())
, m_primary_color(color_utils::color_for_layer(primary_color, m_layer))
, m_secondary_color(color_utils::color_for_layer(secondary_color, m_layer))
, m_selectionMode(m_settings->selection()->getSelectionMode())
, m_undoTransaction(m_context,
m_tool->getText().c_str(),
((getInk()->isSelection() ||
@ -133,6 +135,11 @@ public:
m_filled = m_toolSettings->getFilled();
break;
}
// Right-click subtract selection always.
if (m_button == 1)
m_selectionMode = kSubtractSelectionMode;
m_previewFilled = m_toolSettings->getPreviewFilled();
m_sprayWidth = m_toolSettings->getSprayWidth();
@ -149,7 +156,9 @@ public:
m_useMask = m_document->isMaskVisible();
// Selection ink
if (getInk()->isSelection() && !m_document->isMaskVisible()) {
if (getInk()->isSelection() &&
(!m_document->isMaskVisible() ||
m_selectionMode == kDefaultSelectionMode)) {
Mask emptyMask;
m_document->setMask(&emptyMask);
}
@ -214,6 +223,7 @@ public:
void setSecondaryColor(int color) OVERRIDE { m_secondary_color = color; }
int getOpacity() OVERRIDE { return m_opacity; }
int getTolerance() OVERRIDE { return m_tolerance; }
SelectionMode getSelectionMode() OVERRIDE { return m_selectionMode; }
ISettings* getSettings() OVERRIDE { return m_settings; }
IDocumentSettings* getDocumentSettings() OVERRIDE { return m_docSettings; }
bool getFilled() OVERRIDE { return m_filled; }

View File

@ -176,6 +176,13 @@ namespace app {
PART_LAYER_LOCKED,
PART_LAYER_LOCKED_SELECTED,
PART_SELECTION_REPLACE,
PART_SELECTION_REPLACE_SELECTED,
PART_SELECTION_ADD,
PART_SELECTION_ADD_SELECTED,
PART_SELECTION_SUBTRACT,
PART_SELECTION_SUBTRACT_SELECTED,
PART_UNPINNED,
PART_PINNED,

View File

@ -268,6 +268,12 @@ SkinTheme::SkinTheme()
sheet_mapping["layer_editable_selected"] = PART_LAYER_EDITABLE_SELECTED;
sheet_mapping["layer_locked"] = PART_LAYER_LOCKED;
sheet_mapping["layer_locked_selected"] = PART_LAYER_LOCKED_SELECTED;
sheet_mapping["selection_replace"] = PART_SELECTION_REPLACE;
sheet_mapping["selection_replace_selected"] = PART_SELECTION_REPLACE_SELECTED;
sheet_mapping["selection_add"] = PART_SELECTION_ADD;
sheet_mapping["selection_add_selected"] = PART_SELECTION_ADD_SELECTED;
sheet_mapping["selection_subtract"] = PART_SELECTION_SUBTRACT;
sheet_mapping["selection_subtract_selected"] = PART_SELECTION_SUBTRACT_SELECTED;
sheet_mapping["unpinned"] = PART_UNPINNED;
sheet_mapping["pinned"] = PART_PINNED;
sheet_mapping["drop_down_button_left_normal"] = PART_DROP_DOWN_BUTTON_LEFT_NORMAL_NW;