Move code into a MaskByColorWindow class

Refactor code to separate responsibilities, now the UI related code is
in its own class and the command class looks cleaner
This commit is contained in:
Martín Capello 2024-11-01 16:34:45 -03:00 committed by David Capello
parent 5464c4a3c2
commit 4dc7ba6dc0

View File

@ -48,22 +48,87 @@ using namespace ui;
static const char* ConfigSection = "MaskColor";
class MaskByColorCommand : public Command {
class MaskByColorWindow : public ui::Window {
public:
MaskByColorCommand();
MaskByColorWindow(const ContextReader& reader)
: Window(Window::WithTitleBar, Strings::mask_by_color_title())
, m_reader(&reader)
// Save original mask visibility to process it correctly in
// ADD/SUBTRACT/INTERSECT Selection Mode
, m_isOrigMaskVisible(reader.document()->isMaskVisible()) {
TooltipManager* tooltipManager = new TooltipManager();
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(Strings::mask_by_color_label_color());
m_buttonColor = new ColorButton(
ColorBar::instance()->getFgColor(),
reader.sprite()->pixelFormat(),
ColorButtonOptions());
auto label_tolerance = new Label(Strings::mask_by_color_tolerance());
m_sliderTolerance = new Slider(0, 255, get_config_int(ConfigSection, "Tolerance", 0));
protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
m_selMode = new SelModeField;
m_selMode->setupTooltips(tooltipManager);
m_checkPreview = new CheckBox(Strings::mask_by_color_preview());
m_buttonOk = new Button(Strings::mask_by_color_ok());
auto button_cancel = new Button(Strings::mask_by_color_cancel());
m_checkPreview->processMnemonicFromText();
m_buttonOk->processMnemonicFromText();
button_cancel->processMnemonicFromText();
if (get_config_bool(ConfigSection, "Preview", true))
m_checkPreview->setSelected(true);
m_buttonOk->Click.connect([this]{ closeWindow(m_buttonOk); });
button_cancel->Click.connect([this, button_cancel]{ closeWindow(button_cancel); });
m_buttonColor->Change.connect([&]{ maskPreview(); });
m_sliderTolerance->Change.connect([&]{ maskPreview(); });
m_checkPreview->Click.connect([&]{ maskPreview(); });
m_selMode->ModeChange.connect([&]{ maskPreview(); });
m_buttonOk->setFocusMagnet(true);
m_buttonColor->setExpansive(true);
m_sliderTolerance->setExpansive(true);
box2->setExpansive(true);
addChild(box1);
box1->addChild(m_selMode);
box1->addChild(box2);
box1->addChild(box3);
box1->addChild(m_checkPreview);
box1->addChild(box4);
box2->addChild(label_color);
box2->addChild(m_buttonColor);
box3->addChild(label_tolerance);
box3->addChild(m_sliderTolerance);
box4->addChild(m_buttonOk);
box4->addChild(button_cancel);
// Default position
remapWindow();
centerWindow();
// Mask first preview
maskPreview();
}
bool accepted() const { return closer() == m_buttonOk; }
app::Color getColor() const { return m_buttonColor->getColor(); }
int getTolerance() const { return m_sliderTolerance->getValue(); }
gen::SelectionMode getSelectionMode() const { return m_selMode->selectionMode(); }
bool isPreviewChecked() const { return m_checkPreview->isSelected(); }
private:
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;
@ -73,157 +138,31 @@ private:
}
};
Window* m_window = nullptr;
void maskPreview();
const ContextReader* m_reader = nullptr;
bool m_isOrigMaskVisible;
Button* m_buttonOk = nullptr;
ColorButton* m_buttonColor = nullptr;
CheckBox* m_checkPreview = nullptr;
Slider* m_sliderTolerance = nullptr;
SelModeField* m_selMode = nullptr;
bool m_isOrigMaskVisible;
};
MaskByColorCommand::MaskByColorCommand()
: Command(CommandId::MaskByColor(), CmdUIOnlyFlag)
static Mask* generateMask(const Mask& origMask,
bool isOrigMaskVisible,
const Image* image,
int xpos, int ypos,
gen::SelectionMode mode,
int color,
int tolerance)
{
}
bool MaskByColorCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite |
ContextFlags::HasActiveImage);
}
void MaskByColorCommand::onExecute(Context* context)
{
ASSERT(!m_window);
const ContextReader reader(context);
const Sprite* sprite = reader.sprite();
if (!App::instance()->isGui() || !sprite)
return;
int xpos, ypos;
const Image* image = reader.image(&xpos, &ypos);
if (!image)
return;
std::unique_ptr<Window> win(
new Window(Window::WithTitleBar, Strings::mask_by_color_title()));
base::ScopedValue<Window*> setWindow(m_window, win.get(), nullptr);
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(Strings::mask_by_color_label_color());
m_buttonColor = new ColorButton(
ColorBar::instance()->getFgColor(),
sprite->pixelFormat(),
ColorButtonOptions());
auto label_tolerance = new Label(Strings::mask_by_color_tolerance());
m_sliderTolerance = new Slider(0, 255, get_config_int(ConfigSection, "Tolerance", 0));
m_selMode = new SelModeField;
m_selMode->setupTooltips(tooltipManager);
m_checkPreview = new CheckBox(Strings::mask_by_color_preview());
auto button_ok = new Button(Strings::mask_by_color_ok());
auto button_cancel = new Button(Strings::mask_by_color_cancel());
m_checkPreview->processMnemonicFromText();
button_ok->processMnemonicFromText();
button_cancel->processMnemonicFromText();
if (get_config_bool(ConfigSection, "Preview", true))
m_checkPreview->setSelected(true);
button_ok->Click.connect([this, button_ok]{ m_window->closeWindow(button_ok); });
button_cancel->Click.connect([this, button_cancel]{ m_window->closeWindow(button_cancel); });
m_buttonColor->Change.connect([&]{ maskPreview(reader); });
m_sliderTolerance->Change.connect([&]{ maskPreview(reader); });
m_checkPreview->Click.connect([&]{ maskPreview(reader); });
m_selMode->ModeChange.connect([&]{ maskPreview(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);
box1->addChild(box4);
box2->addChild(label_color);
box2->addChild(m_buttonColor);
box3->addChild(label_tolerance);
box3->addChild(m_sliderTolerance);
box4->addChild(button_ok);
box4->addChild(button_cancel);
// Default position
m_window->remapWindow();
m_window->centerWindow();
// Save original mask visibility to process it correctly in
// ADD/SUBTRACT/INTERSECT Selection Mode
m_isOrigMaskVisible = reader.document()->isMaskVisible();
// Mask first preview
maskPreview(reader);
// Load window configuration
load_window_pos(m_window, ConfigSection);
// Open the window
m_window->openWindowInForeground();
bool apply = (m_window->closer() == button_ok);
ContextWriter writer(reader);
Doc* document(writer.document());
if (apply) {
Tx tx(writer, "Mask by Color", DoesntModifyDocument);
std::unique_ptr<Mask> mask(generateMask(*document->mask(),
sprite, image, xpos, ypos,
m_selMode->selectionMode()));
tx(new cmd::SetMask(document, mask.get()));
tx.commit();
set_config_int(ConfigSection, "Tolerance", m_sliderTolerance->getValue());
set_config_bool(ConfigSection, "Preview", m_checkPreview->isSelected());
}
else {
document->generateMaskBoundaries();
}
// Update boundaries and editors.
update_screen_for_document(document);
// Save window configuration.
save_window_pos(m_window, ConfigSection);
}
Mask* MaskByColorCommand::generateMask(const Mask& origMask,
const Sprite* sprite,
const Image* image,
int xpos, int ypos,
gen::SelectionMode mode)
{
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() && m_isOrigMaskVisible) {
if (!origMask.isEmpty() && isOrigMaskVisible) {
switch (mode) {
case gen::SelectionMode::DEFAULT:
break;
@ -251,18 +190,99 @@ Mask* MaskByColorCommand::generateMask(const Mask& origMask,
return mask.release();
}
void MaskByColorCommand::maskPreview(const ContextReader& reader)
{
ASSERT(m_window);
if (m_window && m_checkPreview->isSelected()) {
int xpos, ypos;
const Image* image = reader.image(&xpos, &ypos);
std::unique_ptr<Mask> mask(generateMask(*reader.document()->mask(),
reader.sprite(), image,
xpos, ypos,
m_selMode->selectionMode()));
class MaskByColorCommand : public CommandWithNewParams<MaskByColorParams> {
public:
MaskByColorCommand();
ContextWriter writer(reader);
protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
};
MaskByColorCommand::MaskByColorCommand()
: CommandWithNewParams(CommandId::MaskByColor(), CmdUIOnlyFlag)
{
}
bool MaskByColorCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite |
ContextFlags::HasActiveImage);
}
void MaskByColorCommand::onExecute(Context* context)
{
const ContextReader reader(context);
const Sprite* sprite = reader.sprite();
if (!App::instance()->isGui() || !sprite)
return;
int xpos, ypos;
const Image* image = reader.image(&xpos, &ypos);
if (!image)
return;
MaskByColorWindow window(reader);
// Save original mask visibility to process it correctly in
// ADD/SUBTRACT/INTERSECT Selection Mode
bool isOrigMaskVisible = reader.document()->isMaskVisible();
// Load window configuration
load_window_pos(&window, ConfigSection);
// Open the window
window.openWindowInForeground();
bool apply = window.accepted();
ContextWriter writer(reader);
Doc* document(writer.document());
if (apply) {
int color = color_utils::color_for_image(window.getColor(),
sprite->pixelFormat());
int tolerance = window.getTolerance();
Tx tx(writer, "Mask by Color", DoesntModifyDocument);
std::unique_ptr<Mask> mask(generateMask(*document->mask(),
isOrigMaskVisible,
image, xpos, ypos,
window.getSelectionMode(),
color, tolerance));
tx(new cmd::SetMask(document, mask.get()));
tx.commit();
set_config_int(ConfigSection, "Tolerance", tolerance);
set_config_bool(ConfigSection, "Preview", window.isPreviewChecked());
}
else {
document->generateMaskBoundaries();
}
// Update boundaries and editors.
update_screen_for_document(document);
// Save window configuration.
save_window_pos(&window, ConfigSection);
}
void MaskByColorWindow::maskPreview()
{
if (isPreviewChecked()) {
int xpos, ypos;
const Image* image = m_reader->image(&xpos, &ypos);
int color = color_utils::color_for_image(m_buttonColor->getColor(),
m_reader->sprite()->pixelFormat());
int tolerance = m_sliderTolerance->getValue();
std::unique_ptr<Mask> mask(generateMask(*m_reader->document()->mask(),
m_isOrigMaskVisible,
image, xpos, ypos,
m_selMode->selectionMode(),
color, tolerance));
ContextWriter writer(*m_reader);
#ifdef SHOW_BOUNDARIES_GEN_PERFORMANCE
base::Chrono chrono;