diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png index 148c27d0f..87e79714a 100644 Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml index d7107be38..638e6de5b 100644 --- a/data/skins/default/skin.xml +++ b/data/skins/default/skin.xml @@ -328,6 +328,12 @@ + + + + + + diff --git a/src/app/settings/freehand_algorithm.h b/src/app/settings/freehand_algorithm.h index a756023dc..8b1ebe015 100644 --- a/src/app/settings/freehand_algorithm.h +++ b/src/app/settings/freehand_algorithm.h @@ -25,6 +25,7 @@ namespace app { enum FreehandAlgorithm { kDefaultFreehandAlgorithm, kPixelPerfectFreehandAlgorithm, + kDotsFreehandAlgorithm, }; } // namespace app diff --git a/src/app/settings/ui_settings_impl.cpp b/src/app/settings/ui_settings_impl.cpp index 02f8adf05..e9260eba0 100644 --- a/src/app/settings/ui_settings_impl.cpp +++ b/src/app/settings/ui_settings_impl.cpp @@ -760,13 +760,19 @@ public: tools::ToolBox* toolBox = App::instance()->getToolBox(); for (int i=0; i<2; ++i) { - if (algorithm == kPixelPerfectFreehandAlgorithm) { - m_tool->setIntertwine(i, toolBox->getIntertwinerById(tools::WellKnownIntertwiners::AsPixelPerfect)); - m_tool->setTracePolicy(i, tools::TracePolicyLast); - } - else { - m_tool->setIntertwine(i, toolBox->getIntertwinerById(tools::WellKnownIntertwiners::AsLines)); - m_tool->setTracePolicy(i, tools::TracePolicyAccumulate); + switch (algorithm) { + case kDefaultFreehandAlgorithm: + m_tool->setIntertwine(i, toolBox->getIntertwinerById(tools::WellKnownIntertwiners::AsLines)); + m_tool->setTracePolicy(i, tools::TracePolicyAccumulate); + break; + case kPixelPerfectFreehandAlgorithm: + m_tool->setIntertwine(i, toolBox->getIntertwinerById(tools::WellKnownIntertwiners::AsPixelPerfect)); + m_tool->setTracePolicy(i, tools::TracePolicyLast); + break; + case kDotsFreehandAlgorithm: + m_tool->setIntertwine(i, toolBox->getIntertwinerById(tools::WellKnownIntertwiners::None)); + m_tool->setTracePolicy(i, tools::TracePolicyAccumulate); + break; } } } diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 82f41ab7e..84c922ec1 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -440,26 +440,144 @@ private: bool m_lockChange; }; -class ContextBar::FreehandAlgorithmField : public CheckBox +class ContextBar::FreehandAlgorithmField : public Button + , public IButtonIcon { public: - FreehandAlgorithmField() : CheckBox("Pixel-perfect") { - setup_mini_font(this); + FreehandAlgorithmField() + : Button("") + , m_popupWindow(NULL) + , m_tooltipManager(NULL) { + setup_mini_look(this); + setIconInterface(this); } + ~FreehandAlgorithmField() { + closePopup(); + setIconInterface(NULL); + } + + void setupTooltips(TooltipManager* tooltipManager) { + m_tooltipManager = tooltipManager; + } + + void setFreehandAlgorithm(FreehandAlgorithm algo) { + int part = PART_FREEHAND_ALGO_DEFAULT; + m_freehandAlgo = algo; + switch (m_freehandAlgo) { + case kDefaultFreehandAlgorithm: + part = PART_FREEHAND_ALGO_DEFAULT; + break; + case kPixelPerfectFreehandAlgorithm: + part = PART_FREEHAND_ALGO_PIXEL_PERFECT; + break; + case kDotsFreehandAlgorithm: + part = PART_FREEHAND_ALGO_DOTS; + break; + } + m_bitmap = static_cast(getTheme())->get_part(part); + invalidate(); + } + + // IButtonIcon implementation + void destroy() OVERRIDE { + // Do nothing, BrushTypeField is added as a widget in the + // ContextBar, so it will be destroyed together with the + // ContextBar. + } + + int getWidth() OVERRIDE { + return m_bitmap->w; + } + + int getHeight() OVERRIDE { + return m_bitmap->h; + } + + BITMAP* getNormalIcon() OVERRIDE { + return m_bitmap; + } + + BITMAP* getSelectedIcon() OVERRIDE { + return m_bitmap; + } + + BITMAP* getDisabledIcon() OVERRIDE { + return m_bitmap; + } + + int getIconAlign() OVERRIDE { + return JI_CENTER | JI_MIDDLE; + } + +protected: void onClick(Event& ev) OVERRIDE { - CheckBox::onClick(ev); + Button::onClick(ev); + + if (!m_popupWindow || !m_popupWindow->isVisible()) + openPopup(); + else + closePopup(); + } + + void onPreferredSize(PreferredSizeEvent& ev) { + ev.setPreferredSize(Size(16*jguiscale(), + 16*jguiscale())); + } + +private: + void openPopup() { + Border border = Border(2, 2, 2, 3)*jguiscale(); + Rect rc = getBounds(); + rc.y += rc.h; + rc.w *= 3; + m_popupWindow = new PopupWindow("", PopupWindow::kCloseOnClickInOtherWindow); + m_popupWindow->setAutoRemap(false); + m_popupWindow->setBorder(border); + m_popupWindow->setBounds(rc + border); + + Region rgn(m_popupWindow->getBounds().createUnion(getBounds())); + m_popupWindow->setHotRegion(rgn); + m_freehandAlgoButton = new ButtonSet(3, 1, m_freehandAlgo, + PART_FREEHAND_ALGO_DEFAULT, + PART_FREEHAND_ALGO_PIXEL_PERFECT, + PART_FREEHAND_ALGO_DOTS); + m_freehandAlgoButton->ItemChange.connect(&FreehandAlgorithmField::onFreehandAlgoChange, this); + m_freehandAlgoButton->setTransparent(true); + m_freehandAlgoButton->setBgColor(ui::ColorNone); + + m_tooltipManager->addTooltipFor(m_freehandAlgoButton->getButtonAt(0), "Normal trace", JI_TOP); + m_tooltipManager->addTooltipFor(m_freehandAlgoButton->getButtonAt(1), "Pixel-perfect trace", JI_TOP); + m_tooltipManager->addTooltipFor(m_freehandAlgoButton->getButtonAt(2), "Dots", JI_TOP); + + m_popupWindow->addChild(m_freehandAlgoButton); + m_popupWindow->openWindow(); + } + + void closePopup() { + if (m_popupWindow) { + m_popupWindow->closeWindow(NULL); + delete m_popupWindow; + m_popupWindow = NULL; + m_freehandAlgoButton = NULL; + } + } + + void onFreehandAlgoChange() { + setFreehandAlgorithm( + (FreehandAlgorithm)m_freehandAlgoButton->getSelectedItem()); ISettings* settings = UIContext::instance()->getSettings(); Tool* currentTool = settings->getCurrentTool(); settings->getToolSettings(currentTool) - ->setFreehandAlgorithm(isSelected() ? - kPixelPerfectFreehandAlgorithm: - kDefaultFreehandAlgorithm); - - releaseFocus( -); + ->setFreehandAlgorithm(m_freehandAlgo); } + + BITMAP* m_bitmap; + FreehandAlgorithm m_freehandAlgo; + PopupWindow* m_popupWindow; + ButtonSet* m_freehandAlgoButton; + TooltipManager* m_tooltipManager; }; class ContextBar::SelectionModeField : public ButtonSet @@ -474,8 +592,7 @@ public: ->selection()->getSelectionMode()); } - void setupTooltips(TooltipManager* tooltipManager) - { + void setupTooltips(TooltipManager* tooltipManager) { tooltipManager->addTooltipFor(getButtonAt(0), "Replace selection", JI_BOTTOM); tooltipManager->addTooltipFor(getButtonAt(1), "Add to selection", JI_BOTTOM); tooltipManager->addTooltipFor(getButtonAt(2), "Subtract from selection", JI_BOTTOM); @@ -572,11 +689,14 @@ ContextBar::ContextBar() m_sprayBox->addChild(m_sprayWidth = new SprayWidthField()); m_sprayBox->addChild(m_spraySpeed = new SpraySpeedField()); + Label* freehandLabel; addChild(m_freehandBox = new HBox()); + m_freehandBox->addChild(freehandLabel = new Label("Freehand:")); m_freehandBox->addChild(m_freehandAlgo = new FreehandAlgorithmField()); setup_mini_font(m_toleranceLabel); setup_mini_font(m_opacityLabel); + setup_mini_font(freehandLabel); TooltipManager* tooltipManager = new TooltipManager(); addChild(tooltipManager); @@ -597,6 +717,7 @@ ContextBar::ContextBar() "from the composition of all sprite layers.", JI_LEFT | JI_TOP); m_selectionMode->setupTooltips(tooltipManager); m_dropPixels->setupTooltips(tooltipManager); + m_freehandAlgo->setupTooltips(tooltipManager); App::instance()->BrushSizeAfterChange.connect(&ContextBar::onBrushSizeChange, this); App::instance()->BrushAngleAfterChange.connect(&ContextBar::onBrushAngleChange, this); @@ -681,7 +802,7 @@ void ContextBar::updateFromTool(tools::Tool* tool) m_inkOpacity->setTextf("%d", toolSettings->getOpacity()); m_grabAlpha->setSelected(settings->getGrabAlpha()); - m_freehandAlgo->setSelected(toolSettings->getFreehandAlgorithm() == kPixelPerfectFreehandAlgorithm); + m_freehandAlgo->setFreehandAlgorithm(toolSettings->getFreehandAlgorithm()); m_sprayWidth->setValue(toolSettings->getSprayWidth()); m_spraySpeed->setValue(toolSettings->getSpraySpeed()); diff --git a/src/app/ui/skin/skin_parts.h b/src/app/ui/skin/skin_parts.h index 3d8ebf283..1a1c558ae 100644 --- a/src/app/ui/skin/skin_parts.h +++ b/src/app/ui/skin/skin_parts.h @@ -200,6 +200,13 @@ namespace app { PART_DROP_PIXELS_CANCEL, PART_DROP_PIXELS_CANCEL_SELECTED, + PART_FREEHAND_ALGO_DEFAULT, + PART_FREEHAND_ALGO_DEFAULT_SELECTED, + PART_FREEHAND_ALGO_PIXEL_PERFECT, + PART_FREEHAND_ALGO_PIXEL_PERFECT_SELECTED, + PART_FREEHAND_ALGO_DOTS, + PART_FREEHAND_ALGO_DOTS_SELECTED, + PARTS }; diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index 5afc6733f..3d5762ca8 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -287,6 +287,12 @@ SkinTheme::SkinTheme() sheet_mapping["drop_pixels_ok_selected"] = PART_DROP_PIXELS_OK_SELECTED; sheet_mapping["drop_pixels_cancel"] = PART_DROP_PIXELS_CANCEL; sheet_mapping["drop_pixels_cancel_selected"] = PART_DROP_PIXELS_CANCEL_SELECTED; + sheet_mapping["freehand_algo_default"] = PART_FREEHAND_ALGO_DEFAULT; + sheet_mapping["freehand_algo_default_selected"] = PART_FREEHAND_ALGO_DEFAULT_SELECTED; + sheet_mapping["freehand_algo_pixel_perfect"] = PART_FREEHAND_ALGO_PIXEL_PERFECT; + sheet_mapping["freehand_algo_pixel_perfect_selected"] = PART_FREEHAND_ALGO_PIXEL_PERFECT_SELECTED; + sheet_mapping["freehand_algo_dots"] = PART_FREEHAND_ALGO_DOTS; + sheet_mapping["freehand_algo_dots_selected"] = PART_FREEHAND_ALGO_DOTS_SELECTED; color_mapping["text"] = ThemeColor::Text; color_mapping["disabled"] = ThemeColor::Disabled;