From 4dc7ba6dc0b27932da475bdec5662ce590b0f4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 1 Nov 2024 16:34:45 -0300 Subject: [PATCH] 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 --- src/app/commands/cmd_mask_by_color.cpp | 346 +++++++++++++------------ 1 file changed, 183 insertions(+), 163 deletions(-) diff --git a/src/app/commands/cmd_mask_by_color.cpp b/src/app/commands/cmd_mask_by_color.cpp index 71d6fad04..fcfe9eba9 100644 --- a/src/app/commands/cmd_mask_by_color.cpp +++ b/src/app/commands/cmd_mask_by_color.cpp @@ -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 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 win( - new Window(Window::WithTitleBar, Strings::mask_by_color_title())); - base::ScopedValue 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(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(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(generateMask(*reader.document()->mask(), - reader.sprite(), image, - xpos, ypos, - m_selMode->selectionMode())); +class MaskByColorCommand : public CommandWithNewParams { +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(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(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;