Add selection modes to "Select > Color Range" command (fix #765)

This commit is contained in:
David Capello 2018-10-26 16:08:31 -03:00
parent 3a77321597
commit 2a5fa78f56
5 changed files with 185 additions and 58 deletions

View File

@ -384,6 +384,7 @@ if(ENABLE_UI)
ui/resources_listbox.cpp
ui/search_entry.cpp
ui/select_accelerator.cpp
ui/selection_mode_field.cpp
ui/skin/font_data.cpp
ui/skin/skin_part.cpp
ui/skin/skin_property.cpp

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -22,6 +23,7 @@
#include "app/tx.h"
#include "app/ui/color_bar.h"
#include "app/ui/color_button.h"
#include "app/ui/selection_mode_field.h"
#include "base/bind.h"
#include "base/chrono.h"
#include "base/convert_to.h"
@ -32,6 +34,7 @@
#include "ui/button.h"
#include "ui/label.h"
#include "ui/slider.h"
#include "ui/tooltips.h"
#include "ui/widget.h"
#include "ui/window.h"
@ -52,13 +55,27 @@ protected:
void onExecute(Context* context) override;
private:
Mask* generateMask(const Sprite* sprite, const Image* image, int xpos, int ypos);
Mask* generateMask(const Mask& origMask,
const Sprite* sprite,
const Image* image,
int xpos, int ypos,
gen::SelectionMode mode);
void maskPreview(const ContextReader& reader);
class SelModeField : public SelectionModeField {
public:
obs::signal<void()> ModeChange;
protected:
void onSelectionModeChange(gen::SelectionMode mode) override {
ModeChange();
}
};
Window* m_window; // TODO we cannot use a std::unique_ptr because clone() needs a copy ctor
ColorButton* m_buttonColor;
CheckBox* m_checkPreview;
Slider* m_sliderTolerance;
SelModeField* m_selMode;
};
MaskByColorCommand::MaskByColorCommand()
@ -77,11 +94,6 @@ void MaskByColorCommand::onExecute(Context* context)
{
const ContextReader reader(context);
const Sprite* sprite = reader.sprite();
Box* box1, *box2, *box3, *box4;
Widget* label_color;
Widget* label_tolerance;
Button* button_ok;
Button* button_cancel;
if (!App::instance()->isGui() || !sprite)
return;
@ -92,21 +104,27 @@ void MaskByColorCommand::onExecute(Context* context)
return;
m_window = new Window(Window::WithTitleBar, "Mask by Color");
box1 = new Box(VERTICAL);
box2 = new Box(HORIZONTAL);
box3 = new Box(HORIZONTAL);
box4 = new Box(HORIZONTAL | HOMOGENEOUS);
label_color = new Label("Color:");
TooltipManager* tooltipManager = new TooltipManager();
m_window->addChild(tooltipManager);
auto box1 = new Box(VERTICAL);
auto box2 = new Box(HORIZONTAL);
auto box3 = new Box(HORIZONTAL);
auto box4 = new Box(HORIZONTAL | HOMOGENEOUS);
auto label_color = new Label("Color:");
m_buttonColor = new ColorButton
(get_config_color("MaskColor", "Color",
ColorBar::instance()->getFgColor()),
sprite->pixelFormat(),
ColorButtonOptions());
label_tolerance = new Label("Tolerance:");
auto label_tolerance = new Label("Tolerance:");
m_sliderTolerance = new Slider(0, 255, get_config_int("MaskColor", "Tolerance", 0));
m_selMode = new SelModeField;
m_selMode->setupTooltips(tooltipManager);
m_checkPreview = new CheckBox("&Preview");
button_ok = new Button("&OK");
button_cancel = new Button("&Cancel");
auto button_ok = new Button("&OK");
auto button_cancel = new Button("&Cancel");
m_checkPreview->processMnemonicFromText();
button_ok->processMnemonicFromText();
@ -122,6 +140,7 @@ void MaskByColorCommand::onExecute(Context* context)
m_buttonColor->Change.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_sliderTolerance->Change.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_checkPreview->Click.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_selMode->ModeChange.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
button_ok->setFocusMagnet(true);
m_buttonColor->setExpansive(true);
@ -129,6 +148,7 @@ void MaskByColorCommand::onExecute(Context* context)
box2->setExpansive(true);
m_window->addChild(box1);
box1->addChild(m_selMode);
box1->addChild(box2);
box1->addChild(box3);
box1->addChild(m_checkPreview);
@ -160,7 +180,9 @@ void MaskByColorCommand::onExecute(Context* context)
if (apply) {
Tx tx(writer.context(), "Mask by Color", DoesntModifyDocument);
std::unique_ptr<Mask> mask(generateMask(sprite, image, xpos, ypos));
std::unique_ptr<Mask> mask(generateMask(*document->mask(),
sprite, image, xpos, ypos,
m_selMode->selectionMode()));
tx(new cmd::SetMask(document, mask.get()));
tx.commit();
@ -178,17 +200,44 @@ void MaskByColorCommand::onExecute(Context* context)
delete m_window;
}
Mask* MaskByColorCommand::generateMask(const Sprite* sprite, const Image* image, int xpos, int ypos)
Mask* MaskByColorCommand::generateMask(const Mask& origMask,
const Sprite* sprite,
const Image* image,
int xpos, int ypos,
gen::SelectionMode mode)
{
int color, tolerance;
color = color_utils::color_for_image(m_buttonColor->getColor(), sprite->pixelFormat());
tolerance = m_sliderTolerance->getValue();
int color = color_utils::color_for_image(m_buttonColor->getColor(), sprite->pixelFormat());
int tolerance = m_sliderTolerance->getValue();
std::unique_ptr<Mask> mask(new Mask());
mask->byColor(image, color, tolerance);
mask->offsetOrigin(xpos, ypos);
if (!origMask.isEmpty()) {
switch (mode) {
case gen::SelectionMode::DEFAULT:
break;
case gen::SelectionMode::ADD:
mask->add(origMask);
break;
case gen::SelectionMode::SUBTRACT: {
if (!mask->isEmpty()) {
Mask mask2(origMask);
mask2.subtract(*mask);
mask->replace(mask2);
}
else {
mask->replace(origMask);
}
break;
}
case gen::SelectionMode::INTERSECT: {
mask->intersect(origMask);
break;
}
}
}
return mask.release();
}
@ -197,7 +246,10 @@ void MaskByColorCommand::maskPreview(const ContextReader& reader)
if (m_checkPreview->isSelected()) {
int xpos, ypos;
const Image* image = reader.image(&xpos, &ypos);
std::unique_ptr<Mask> mask(generateMask(reader.sprite(), image, xpos, ypos));
std::unique_ptr<Mask> mask(generateMask(*reader.document()->mask(),
reader.sprite(), image,
xpos, ypos,
m_selMode->selectionMode()));
{
ContextWriter writer(reader);

View File

@ -36,6 +36,7 @@
#include "app/ui/dithering_selector.h"
#include "app/ui/icon_button.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/selection_mode_field.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h"
#include "base/bind.h"
@ -46,9 +47,9 @@
#include "doc/palette.h"
#include "doc/remap.h"
#include "obs/connection.h"
#include "render/dithering.h"
#include "os/surface.h"
#include "os/system.h"
#include "render/dithering.h"
#include "ui/button.h"
#include "ui/combobox.h"
#include "ui/int_entry.h"
@ -925,44 +926,12 @@ protected:
}
};
class ContextBar::SelectionModeField : public ButtonSet {
class ContextBar::SelectionModeField : public app::SelectionModeField {
public:
SelectionModeField() : ButtonSet(4) {
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
addItem(theme->parts.selectionReplace());
addItem(theme->parts.selectionAdd());
addItem(theme->parts.selectionSubtract());
addItem(theme->parts.selectionIntersect());
setSelectedItem((int)Preferences::instance().selection.mode());
}
void setupTooltips(TooltipManager* tooltipManager) {
tooltipManager->addTooltipFor(
at(0), "Replace selection", BOTTOM);
tooltipManager->addTooltipFor(
at(1), key_tooltip("Add to selection", KeyAction::AddSelection), BOTTOM);
tooltipManager->addTooltipFor(
at(2), key_tooltip("Subtract from selection", KeyAction::SubtractSelection), BOTTOM);
tooltipManager->addTooltipFor(
at(3), key_tooltip("Intersect selection", KeyAction::IntersectSelection), BOTTOM);
}
void setSelectionMode(gen::SelectionMode mode) {
setSelectedItem((int)mode, false);
invalidate();
}
SelectionModeField() { }
protected:
void onItemChange(Item* item) override {
ButtonSet::onItemChange(item);
Preferences::instance().selection.mode(
(gen::SelectionMode)selectedItem());
void onSelectionModeChange(gen::SelectionMode mode) override {
Preferences::instance().selection.mode(mode);
}
};

View File

@ -0,0 +1,66 @@
// Aseprite
// Copyright (C) 2018 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/selection_mode_field.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/skin/skin_theme.h"
namespace app {
using namespace app::skin;
using namespace ui;
SelectionModeField::SelectionModeField()
: ButtonSet(4)
{
auto theme = static_cast<SkinTheme*>(this->theme());
addItem(theme->parts.selectionReplace());
addItem(theme->parts.selectionAdd());
addItem(theme->parts.selectionSubtract());
addItem(theme->parts.selectionIntersect());
setSelectedItem((int)Preferences::instance().selection.mode());
}
void SelectionModeField::setupTooltips(TooltipManager* tooltipManager)
{
tooltipManager->addTooltipFor(
at(0), "Replace selection", BOTTOM);
tooltipManager->addTooltipFor(
at(1), key_tooltip("Add to selection", KeyAction::AddSelection), BOTTOM);
tooltipManager->addTooltipFor(
at(2), key_tooltip("Subtract from selection", KeyAction::SubtractSelection), BOTTOM);
tooltipManager->addTooltipFor(
at(3), key_tooltip("Intersect selection", KeyAction::IntersectSelection), BOTTOM);
}
gen::SelectionMode SelectionModeField::selectionMode()
{
return (gen::SelectionMode)selectedItem();
}
void SelectionModeField::setSelectionMode(gen::SelectionMode mode)
{
setSelectedItem((int)mode, false);
invalidate();
}
void SelectionModeField::onItemChange(Item* item)
{
ButtonSet::onItemChange(item);
onSelectionModeChange(selectionMode());
}
} // namespace app

View File

@ -0,0 +1,39 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UI_SELECTION_MODE_FIELD_H_INCLUDED
#define APP_UI_SELECTION_MODE_FIELD_H_INCLUDED
#pragma once
#include "app/pref/preferences.h"
#include "app/ui/button_set.h"
#include <vector>
namespace ui {
class TooltipManager;
}
namespace app {
class SelectionModeField : public ButtonSet {
public:
SelectionModeField();
void setupTooltips(ui::TooltipManager* tooltipManager);
gen::SelectionMode selectionMode();
void setSelectionMode(gen::SelectionMode mode);
protected:
void onItemChange(Item* item) override;
virtual void onSelectionModeChange(gen::SelectionMode mode) { }
};
} // namespace app
#endif