Skip to content

Commit

Permalink
Add selection modes to "Select > Color Range" command (fix #765)
Browse files Browse the repository at this point in the history
  • Loading branch information
dacap committed Oct 26, 2018
1 parent 3a77321 commit 2a5fa78
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 58 deletions.
1 change: 1 addition & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -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
Expand Down
94 changes: 73 additions & 21 deletions src/app/commands/cmd_mask_by_color.cpp
@@ -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
Expand All @@ -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"
Expand All @@ -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"

Expand All @@ -52,13 +55,27 @@ class MaskByColorCommand : public Command {
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()
Expand All @@ -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;
Expand All @@ -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();
Expand All @@ -122,13 +140,15 @@ 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);
m_sliderTolerance->setExpansive(true);
box2->setExpansive(true);

m_window->addChild(box1);
box1->addChild(m_selMode);
box1->addChild(box2);
box1->addChild(box3);
box1->addChild(m_checkPreview);
Expand Down Expand Up @@ -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();

Expand All @@ -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();
}

Expand All @@ -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);

Expand Down
43 changes: 6 additions & 37 deletions src/app/ui/context_bar.cpp
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -925,44 +926,12 @@ class ContextBar::FreehandAlgorithmField : public CheckBox {
}
};

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);
}
};

Expand Down
66 changes: 66 additions & 0 deletions src/app/ui/selection_mode_field.cpp
@@ -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
39 changes: 39 additions & 0 deletions src/app/ui/selection_mode_field.h
@@ -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

0 comments on commit 2a5fa78

Please sign in to comment.