From da81c0266d2d599d53230d3a23e0a1ca69fb65d3 Mon Sep 17 00:00:00 2001 From: Joseph-Eugene Winzer Date: Wed, 13 Jul 2022 05:18:04 +0200 Subject: [PATCH 1/7] Fix empty undo history crash (fix #3417) m_undoHistory is not valid for tabs other than sprites. For example opening the README or Home tab does not initialize the undo history, thus aseprite will segfault when it tries to get its first entry. --- src/app/commands/cmd_undo_history.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/commands/cmd_undo_history.cpp b/src/app/commands/cmd_undo_history.cpp index 24e5bd404..1f752e791 100644 --- a/src/app/commands/cmd_undo_history.cpp +++ b/src/app/commands/cmd_undo_history.cpp @@ -113,7 +113,7 @@ public: [[fallthrough]]; case ui::kMouseMoveMessage: - if (hasCapture()) { + if (hasCapture() && m_undoHistory) { auto mouseMsg = static_cast(msg); const gfx::Rect bounds = this->bounds(); From 51132a8e848f9fc3e3adbe3d32512776319fbe5a Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 14 Jul 2022 14:16:24 -0300 Subject: [PATCH 2/7] Update laf submodule --- laf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/laf b/laf index 871adbe48..f0bb81ae1 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit 871adbe48bc85cff8de08d243df1f42fc9f1f7e9 +Subproject commit f0bb81ae19517ff8a74cbb00461d3fc5314349f9 From 44bb1c4e48d3a4a5d0031390297e3ad87826880a Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 15 Jul 2022 10:05:50 -0300 Subject: [PATCH 3/7] Fix bug losing original image colors from custom brushes (fix #3375) --- src/app/app_brushes.cpp | 6 +++--- src/app/ui/brush_popup.cpp | 4 +++- src/app/ui/context_bar.cpp | 5 +++++ src/doc/brush.cpp | 8 +++++++- src/doc/brush.h | 16 +++++++++++++++- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp index 6a83810b8..f1dbe96b2 100644 --- a/src/app/app_brushes.cpp +++ b/src/app/app_brushes.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2020-2021 Igara Studio S.A. +// Copyright (C) 2020-2022 Igara Studio S.A. // Copyright (C) 2001-2016 David Capello // // This program is distributed under the terms of @@ -437,9 +437,9 @@ void AppBrushes::save(const std::string& filename) const } if (slot.brush()->type() == kImageBrushType && - slot.brush()->image()) { + slot.brush()->originalImage()) { TiXmlElement elem("image"); - save_xml_image(&elem, slot.brush()->image()); + save_xml_image(&elem, slot.brush()->originalImage()); brushElem.InsertEndChild(elem); if (slot.brush()->maskBitmap()) { diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index 1a681a515..90b3a6cf6 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -454,7 +454,9 @@ os::SurfaceRef BrushPopup::createSurfaceForBrush(const BrushRef& origBrush) brush.reset(new Brush(*brush)); brush->setSize(10); } - image = brush->image(); + // Show the original image in the popup (without the image colors + // modified if there were some modification). + image = brush->originalImage(); } os::SurfaceRef surface = os::instance()->makeRgbaSurface( diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 07799a509..5181115b9 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -2026,6 +2026,11 @@ void ContextBar::setActiveBrushBySlot(tools::Tool* tool, int slot) if (brush.brush()) { if (brush.brush()->type() == doc::kImageBrushType) { + // Reset the colors of the image when we select the brush from + // the slot. + if (brush.hasFlag(BrushSlot::Flags::ImageColor)) + brush.brush()->resetImageColors(); + setActiveBrush(brush.brush()); } else { diff --git a/src/doc/brush.cpp b/src/doc/brush.cpp index fb935da3d..7d6a724de 100644 --- a/src/doc/brush.cpp +++ b/src/doc/brush.cpp @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (C) 2019-2021 Igara Studio S.A. +// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2001-2016 David Capello // // This file is released under the terms of the MIT license. @@ -281,6 +281,12 @@ void Brush::setImageColor(ImageColor imageColor, color_t color) } } +void Brush::resetImageColors() +{ + if (m_backupImage) + m_image.reset(Image::createCopy(m_backupImage.get())); +} + void Brush::setCenter(const gfx::Point& center) { m_center = center; diff --git a/src/doc/brush.h b/src/doc/brush.h index d5bfb804a..98da5f7e1 100644 --- a/src/doc/brush.h +++ b/src/doc/brush.h @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (C) 2019-2020 Igara Studio S.A. +// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -52,7 +52,12 @@ namespace doc { void setAngle(int angle); void setImage(const Image* image, const Image* maskBitmap); + + // Special functions to change the colors of the image or restore + // the colors to the original image used to create the brush. void setImageColor(ImageColor imageColor, color_t color); + void resetImageColors(); + void setPattern(BrushPattern pattern) { m_pattern = pattern; } @@ -64,6 +69,15 @@ namespace doc { } void setCenter(const gfx::Point& center); + // Returns the original image used to create the brush before + // calling any setImageColor() + Image* originalImage() const { + if (m_backupImage) + return m_backupImage.get(); + else + return m_image.get(); + } + private: void clean(); void regenerate(); From 60a4ebf8f6aa1910a2f65ed5496a2708ed6b24df Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 15 Jul 2022 12:30:15 -0300 Subject: [PATCH 4/7] Minor change in StatusBar::showColor() to use std::string/fmt::format --- src/app/ui/color_button.cpp | 2 +- src/app/ui/color_selector.cpp | 2 +- src/app/ui/editor/standby_state.cpp | 46 +++++++++++++---------------- src/app/ui/palette_view.cpp | 3 +- src/app/ui/status_bar.cpp | 7 +++-- src/app/ui/status_bar.h | 5 ++-- 6 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/app/ui/color_button.cpp b/src/app/ui/color_button.cpp index 56cd4c05e..a3517c0b3 100644 --- a/src/app/ui/color_button.cpp +++ b/src/app/ui/color_button.cpp @@ -139,7 +139,7 @@ bool ColorButton::onProcessMessage(Message* msg) break; case kMouseEnterMessage: - StatusBar::instance()->showColor(0, "", m_color); + StatusBar::instance()->showColor(0, m_color); break; case kMouseLeaveMessage: diff --git a/src/app/ui/color_selector.cpp b/src/app/ui/color_selector.cpp index dba11976c..d703d73cc 100644 --- a/src/app/ui/color_selector.cpp +++ b/src/app/ui/color_selector.cpp @@ -334,7 +334,7 @@ bool ColorSelector::onProcessMessage(ui::Message* msg) if (color != app::Color::fromMask()) { base::ScopedValue switcher(m_lockColor, subColorPicked(), false); - StatusBar::instance()->showColor(0, "", color); + StatusBar::instance()->showColor(0, color); if (hasCapture()) ColorChange(color, mouseMsg->button()); } diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index 001fcf17b..f8027b3a2 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -550,37 +550,36 @@ bool StandbyState::onUpdateStatusBar(Editor* editor) editor->projection(), color); - char buf[256]; - sprintf(buf, " :pos: %d %d", - int(std::floor(spritePos.x)), - int(std::floor(spritePos.y))); + std::string buf = + fmt::format(" :pos: {} {}", + int(std::floor(spritePos.x)), + int(std::floor(spritePos.y))); - StatusBar::instance()->showColor(0, buf, color); + StatusBar::instance()->showColor(0, color, buf); } else { Mask* mask = (editor->document()->isMaskVisible() ? editor->document()->mask(): NULL); - char buf[1024]; - sprintf( - buf, ":pos: %d %d :size: %d %d", - int(std::floor(spritePos.x)), - int(std::floor(spritePos.y)), - sprite->width(), - sprite->height()); + std::string buf = fmt::format( + ":pos: {} {} :size: {} {}", + int(std::floor(spritePos.x)), + int(std::floor(spritePos.y)), + sprite->width(), + sprite->height()); if (mask) - sprintf(buf+std::strlen(buf), " :selsize: %d %d", - mask->bounds().w, - mask->bounds().h); + buf += fmt::format(" :selsize: {} {}", + mask->bounds().w, + mask->bounds().h); if (sprite->totalFrames() > 1) { - sprintf( - buf+std::strlen(buf), " :frame: %d :clock: %s/%s", + buf += fmt::format( + " :frame: {} :clock: {}/{}", editor->frame()+editor->docPref().timeline.firstFrame(), - human_readable_time(sprite->frameDuration(editor->frame())).c_str(), - human_readable_time(sprite->totalAnimationDuration()).c_str()); + human_readable_time(sprite->frameDuration(editor->frame())), + human_readable_time(sprite->totalAnimationDuration())); } if (editor->docPref().show.grid()) { @@ -588,7 +587,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor) if (!gb.isEmpty()) { int col = int((std::floor(spritePos.x) - (gb.x % gb.w)) / gb.w); int row = int((std::floor(spritePos.y) - (gb.y % gb.h)) / gb.h); - sprintf(buf+std::strlen(buf), " :grid: %d %d", col, row); + buf += fmt::format(" :grid: {} {}", col, row); } } @@ -601,14 +600,11 @@ bool StandbyState::onUpdateStatusBar(Editor* editor) int(std::floor(spritePos.x)), int(std::floor(spritePos.y)))) { if (++count == 3) { - sprintf( - buf+std::strlen(buf), " :slice: ..."); + buf += fmt::format(" :slice: ..."); break; } - sprintf( - buf+std::strlen(buf), " :slice: %s", - slice->name().c_str()); + buf += fmt::format(" :slice: {}", slice->name()); } } } diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp index d60fee43f..71ad7173f 100644 --- a/src/app/ui/palette_view.cpp +++ b/src/app/ui/palette_view.cpp @@ -921,8 +921,7 @@ void PaletteView::setStatusBar() (m_hot.color < currentPalette()->size())) { int i = std::max(0, m_hot.color); - statusBar->showColor( - 0, "", app::Color::fromIndex(i)); + statusBar->showColor(0, app::Color::fromIndex(i)); } else { statusBar->showDefaultText(); diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp index 2b3e542c4..65e7ac189 100644 --- a/src/app/ui/status_bar.cpp +++ b/src/app/ui/status_bar.cpp @@ -740,14 +740,15 @@ void StatusBar::showTip(int msecs, const std::string& msg) m_timeout = base::current_tick(); } -void StatusBar::showColor(int msecs, const char* text, const app::Color& color) +void StatusBar::showColor(int msecs, const app::Color& color, + const std::string& text) { if ((base::current_tick() > m_timeout) || (msecs > 0)) { showIndicators(); IndicatorsGeneration gen(m_indicators); gen.add(color); - if (text) - gen.add(text); + if (!text.empty()) + gen.add(text.c_str()); m_timeout = base::current_tick() + msecs; } diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h index d99c12f64..2d7bceb3c 100644 --- a/src/app/ui/status_bar.h +++ b/src/app/ui/status_bar.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2021 Igara Studio S.A. +// Copyright (C) 2018-2022 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -60,7 +60,8 @@ namespace app { bool setStatusText(int msecs, const std::string& text); void showTip(int msecs, const std::string& msg); - void showColor(int msecs, const char* text, const Color& color); + void showColor(int msecs, const Color& color, + const std::string& text = std::string()); void showTool(int msecs, tools::Tool* tool); void showSnapToGridWarning(bool state); From c6e3ca0d8baa395939520d8a713db841dd81a34c Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 15 Jul 2022 12:46:54 -0300 Subject: [PATCH 5/7] Fix memory leak with IntEntry popups --- src/ui/int_entry.cpp | 7 +++---- src/ui/int_entry.h | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ui/int_entry.cpp b/src/ui/int_entry.cpp index b21c54d12..813beb10d 100644 --- a/src/ui/int_entry.cpp +++ b/src/ui/int_entry.cpp @@ -171,7 +171,7 @@ void IntEntry::openPopup() { m_slider.setValue(getValue()); - m_popupWindow = new TransparentPopupWindow(PopupWindow::ClickBehavior::CloseOnClickInOtherWindow); + m_popupWindow = std::make_unique(PopupWindow::ClickBehavior::CloseOnClickInOtherWindow); m_popupWindow->setAutoRemap(false); m_popupWindow->addChild(&m_slider); m_popupWindow->Close.connect(&IntEntry::onPopupClose, this); @@ -200,8 +200,7 @@ void IntEntry::closePopup() removeSlider(); m_popupWindow->closeWindow(nullptr); - delete m_popupWindow; - m_popupWindow = nullptr; + m_popupWindow.reset(); } } @@ -223,7 +222,7 @@ void IntEntry::onPopupClose(CloseEvent& ev) void IntEntry::removeSlider() { if (m_popupWindow && - m_slider.parent() == m_popupWindow) { + m_slider.parent() == m_popupWindow.get()) { m_popupWindow->removeChild(&m_slider); } } diff --git a/src/ui/int_entry.h b/src/ui/int_entry.h index 31736b9c8..7e64332f3 100644 --- a/src/ui/int_entry.h +++ b/src/ui/int_entry.h @@ -1,4 +1,5 @@ // Aseprite UI Library +// Copyright (C) 2022 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. @@ -11,6 +12,8 @@ #include "ui/entry.h" #include "ui/slider.h" +#include + namespace ui { class CloseEvent; @@ -43,7 +46,7 @@ namespace ui { int m_min; int m_max; Slider m_slider; - PopupWindow* m_popupWindow; + std::unique_ptr m_popupWindow; bool m_changeFromSlider; }; From 7e19592470f14244f94d968fc9025c8bae84e9a2 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 15 Jul 2022 12:49:48 -0300 Subject: [PATCH 6/7] Preview the modified image for brush slots that don't save image colors This is to show the brush image with the new fg color in the brush popup instead of the original image when image colors shouldn't be preserved for that specific slot. --- src/app/ui/brush_popup.cpp | 12 +++++++++--- src/app/ui/brush_popup.h | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index 90b3a6cf6..b0ca329e9 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -79,7 +79,9 @@ public: , m_slot(slot) { if (m_brush.hasBrush()) { SkinPartPtr icon(new SkinPart); - icon->setBitmap(0, BrushPopup::createSurfaceForBrush(m_brush.brush())); + icon->setBitmap(0, BrushPopup::createSurfaceForBrush( + m_brush.brush(), + m_brush.hasFlag(BrushSlot::Flags::ImageColor))); setIcon(icon); } } @@ -445,7 +447,8 @@ void BrushPopup::onBrushChanges() } // static -os::SurfaceRef BrushPopup::createSurfaceForBrush(const BrushRef& origBrush) +os::SurfaceRef BrushPopup::createSurfaceForBrush(const BrushRef& origBrush, + const bool useOriginalImage) { Image* image = nullptr; BrushRef brush = origBrush; @@ -456,7 +459,10 @@ os::SurfaceRef BrushPopup::createSurfaceForBrush(const BrushRef& origBrush) } // Show the original image in the popup (without the image colors // modified if there were some modification). - image = brush->originalImage(); + if (useOriginalImage) + image = brush->originalImage(); + else + image = brush->image(); } os::SurfaceRef surface = os::instance()->makeRgbaSurface( diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h index a5dc1af77..2967fd979 100644 --- a/src/app/ui/brush_popup.h +++ b/src/app/ui/brush_popup.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2020 Igara Studio S.A. +// Copyright (C) 2018-2022 Igara Studio S.A. // Copyright (C) 2001-2015 David Capello // // This program is distributed under the terms of @@ -23,7 +23,8 @@ namespace app { void setBrush(doc::Brush* brush); void regenerate(const gfx::Rect& box); - static os::SurfaceRef createSurfaceForBrush(const doc::BrushRef& brush); + static os::SurfaceRef createSurfaceForBrush(const doc::BrushRef& brush, + const bool useOriginalImage = false); private: void onStandardBrush(); From f4e83371b8aaf4eb828577fdd3791454dd71f8c2 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 15 Jul 2022 16:32:49 -0300 Subject: [PATCH 7/7] Several minor improvements to the status bar * Remove "%3d" for integers (just use "%d"), this is a legacy format when the text was a monospace font (it was present from the very beginning of the times, when we used the Allegro font) * New :delta: icon (before :offset: which didn't exist in the theme) * Show useful info for MovingCelState: mainly cel bounds --- data/extensions/aseprite-theme/sheet.png | Bin 15155 -> 15212 bytes data/extensions/aseprite-theme/theme.xml | 3 +- src/app/tools/controllers.h | 12 +-- src/app/ui/editor/editor.cpp | 1 + src/app/ui/editor/editor.h | 4 +- src/app/ui/editor/moving_cel_state.cpp | 74 +++++++++++++------ src/app/ui/editor/moving_cel_state.h | 1 + src/app/ui/editor/moving_pixels_state.cpp | 6 +- src/app/ui/editor/moving_selection_state.cpp | 4 +- src/app/ui/editor/standby_state.cpp | 24 +++++- 10 files changed, 93 insertions(+), 36 deletions(-) diff --git a/data/extensions/aseprite-theme/sheet.png b/data/extensions/aseprite-theme/sheet.png index fd6aa05b32e9ab151a737ebe71b04e4cf1c7296b..e10120e1592859b5a1f8d45b4646cfd4db097914 100644 GIT binary patch literal 15212 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 + + @@ -414,7 +416,6 @@ - diff --git a/src/app/tools/controllers.h b/src/app/tools/controllers.h index ff32976e3..048f62d6a 100644 --- a/src/app/tools/controllers.h +++ b/src/app/tools/controllers.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2019-2020 Igara Studio S.A. +// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -94,7 +94,7 @@ public: gfx::Point offset = loop->statusBarPositionOffset(); char buf[1024]; - sprintf(buf, ":start: %3d %3d :end: %3d %3d", + sprintf(buf, ":start: %d %d :end: %d %d", stroke.firstPoint().x+offset.x, stroke.firstPoint().y+offset.y, stroke.lastPoint().x+offset.x, @@ -259,7 +259,7 @@ public: gfx::Point offset = loop->statusBarPositionOffset(); char buf[1024]; int gcd = base::gcd(w, h); - sprintf(buf, ":start: %3d %3d :end: %3d %3d :size: %3d %3d :distance: %.1f", + sprintf(buf, ":start: %d %d :end: %d %d :size: %d %d :distance: %.1f", stroke[0].x+offset.x, stroke[0].y+offset.y, stroke[1].x+offset.x, stroke[1].y+offset.y, w, h, std::sqrt(w*w + h*h)); @@ -362,7 +362,7 @@ public: gfx::Point offset = loop->statusBarPositionOffset(); char buf[1024]; - sprintf(buf, ":start: %3d %3d :end: %3d %3d", + sprintf(buf, ":start: %d %d :end: %d %d", stroke.firstPoint().x+offset.x, stroke.firstPoint().y+offset.y, stroke.lastPoint().x+offset.x, @@ -402,7 +402,7 @@ public: gfx::Point offset = loop->statusBarPositionOffset(); char buf[1024]; - sprintf(buf, ":pos: %3d %3d", + sprintf(buf, ":pos: %d %d", stroke[0].x+offset.x, stroke[0].y+offset.y); text = buf; @@ -460,7 +460,7 @@ public: gfx::Point offset = loop->statusBarPositionOffset(); char buf[1024]; - sprintf(buf, ":start: %3d %3d :end: %3d %3d (%3d %3d - %3d %3d)", + sprintf(buf, ":start: %d %d :end: %d %d (%d %d - %d %d)", stroke[0].x+offset.x, stroke[0].y+offset.y, stroke[3].x+offset.x, stroke[3].y+offset.y, stroke[1].x+offset.x, stroke[1].y+offset.y, diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index 5c915d78a..fc75af6f7 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -2887,6 +2887,7 @@ void Editor::updateAutoCelGuides(ui::Message* msg) if (m_showGuidesThisCel != oldShowGuidesThisCel || m_showAutoCelGuides != oldShowAutoCelGuides) { invalidate(); + updateStatusBar(); } } diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index c429caa04..47f700648 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -304,6 +304,9 @@ namespace app { // key is pressed to cancel the active selection. void cancelSelections(); + // Properties to show information in the status bar + bool showAutoCelGuides() const { return m_showAutoCelGuides; } + static void registerCommands(); protected: @@ -374,7 +377,6 @@ namespace app { gfx::Point calcExtraPadding(const render::Projection& proj); void invalidateIfActive(); - bool showAutoCelGuides(); void updateAutoCelGuides(ui::Message* msg); // Stack of states. The top element in the stack is the current state (m_state). diff --git a/src/app/ui/editor/moving_cel_state.cpp b/src/app/ui/editor/moving_cel_state.cpp index f256e319a..5c81342b7 100644 --- a/src/app/ui/editor/moving_cel_state.cpp +++ b/src/app/ui/editor/moving_cel_state.cpp @@ -97,8 +97,12 @@ MovingCelState::MovingCelState(Editor* editor, ASSERT(!m_celList.empty()); m_cel = collect.mainCel(); - if (m_cel) - m_celMainSize = m_cel->boundsF().size(); + if (m_cel) { + if (m_cel->data()->hasBoundsF()) + m_celMainSize = m_cel->boundsF().size(); + else + m_celMainSize = gfx::SizeF(m_cel->bounds().size()); + } // Record start positions of all cels in selected range for (Cel* cel : m_celList) { @@ -282,6 +286,11 @@ void MovingCelState::onCommitMouseMove(Editor* editor, // Redraw the new cel position. editor->invalidate(); + + // Redraw status bar with the new position of cels (without this the + // previous position before this onCommitMouseMove() is still + // displayed in the screen). + editor->updateStatusBar(); } bool MovingCelState::onKeyDown(Editor* editor, KeyMessage* msg) @@ -297,37 +306,46 @@ bool MovingCelState::onKeyDown(Editor* editor, KeyMessage* msg) bool MovingCelState::onUpdateStatusBar(Editor* editor) { - gfx::PointF pos = m_cursorStart - gfx::PointF(editor->mainTilePosition()); + gfx::PointF pos = m_celOffset + m_cursorStart - gfx::PointF(editor->mainTilePosition()); + gfx::RectF fullBounds = calcFullBounds(); + std::string buf; if (m_hasReference) { + buf = fmt::format(":pos: {:.2f} {:.2f}", pos.x, pos.y); if (m_scaled && m_cel) { - StatusBar::instance()->setStatusText( - 0, - fmt::format( - ":pos: {:.2f} {:.2f} :offset: {:.2f} {:.2f} :size: {:.2f}% {:.2f}%", - pos.x, pos.y, - m_celOffset.x, m_celOffset.y, - 100.0*m_celScale.w*m_celMainSize.w/m_cel->image()->width(), - 100.0*m_celScale.h*m_celMainSize.h/m_cel->image()->height())); + buf += fmt::format( + " :start: {:.2f} {:.2f}" + " :size: {:.2f} {:.2f} [{:.2f}% {:.2f}%]", + m_cel->boundsF().x, + m_cel->boundsF().y, + m_celScale.w*m_celMainSize.w, + m_celScale.h*m_celMainSize.h, + 100.0*m_celScale.w*m_celMainSize.w/m_cel->image()->width(), + 100.0*m_celScale.h*m_celMainSize.h/m_cel->image()->height()); } else { - StatusBar::instance()->setStatusText( - 0, - fmt::format( - ":pos: {:.2f} {:.2f} :offset: {:.2f} {:.2f}", - pos.x, pos.y, - m_celOffset.x, m_celOffset.y)); + buf += fmt::format( + " :start: {:.2f} {:.2f} :size: {:.2f} {:.2f}" + " :delta: {:.2f} {:.2f}", + fullBounds.x, fullBounds.y, + fullBounds.w, fullBounds.h, + m_celOffset.x, m_celOffset.y); } } else { gfx::Point intOffset = intCelOffset(); - StatusBar::instance()->setStatusText( - 0, - fmt::format(":pos: {:3d} {:3d} :offset: {:3d} {:3d}", - int(pos.x), int(pos.y), - intOffset.x, intOffset.y)); + fullBounds.floor(); + buf = fmt::format( + ":pos: {} {}" + " :start: {} {} :size: {} {}" + " :delta: {} {}", + int(pos.x), int(pos.y), + int(fullBounds.x), int(fullBounds.y), + int(fullBounds.w), int(fullBounds.h), + intOffset.x, intOffset.y); } + StatusBar::instance()->setStatusText(0, buf); return true; } @@ -337,6 +355,18 @@ gfx::Point MovingCelState::intCelOffset() const int(std::round(m_celOffset.y))); } +gfx::RectF MovingCelState::calcFullBounds() const +{ + gfx::RectF bounds; + for (Cel* cel : m_celList) { + if (cel->data()->hasBoundsF()) + bounds |= cel->boundsF(); + else + bounds |= gfx::RectF(cel->bounds()).floor(); + } + return bounds; +} + bool MovingCelState::restoreCelStartPosition() const { bool modified = false; diff --git a/src/app/ui/editor/moving_cel_state.h b/src/app/ui/editor/moving_cel_state.h index b3050279f..5bc98f58a 100644 --- a/src/app/ui/editor/moving_cel_state.h +++ b/src/app/ui/editor/moving_cel_state.h @@ -57,6 +57,7 @@ namespace app { private: gfx::Point intCelOffset() const; + gfx::RectF calcFullBounds() const; bool restoreCelStartPosition() const; // ContextObserver void onBeforeCommandExecution(CommandExecutionEvent& ev); diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp index 5f9f926d9..43d374d76 100644 --- a/src/app/ui/editor/moving_pixels_state.cpp +++ b/src/app/ui/editor/moving_pixels_state.cpp @@ -535,7 +535,11 @@ bool MovingPixelsState::onUpdateStatusBar(Editor* editor) StatusBar::instance()->setStatusText( 100, fmt::format( - ":pos: {} {} :size: {:3d} {:3d} :selsize: {} {} [{:.02f}% {:.02f}%] :angle: {:.1f} :aspect_ratio: {}:{}", + ":pos: {} {}" + " :size: {} {}" + " :selsize: {} {} [{:.02f}% {:.02f}%]" + " :angle: {:.1f}" + " :aspect_ratio: {}:{}", int(transform.bounds().x), int(transform.bounds().y), imageSize.w, diff --git a/src/app/ui/editor/moving_selection_state.cpp b/src/app/ui/editor/moving_selection_state.cpp index c3a956ad3..82673c516 100644 --- a/src/app/ui/editor/moving_selection_state.cpp +++ b/src/app/ui/editor/moving_selection_state.cpp @@ -157,7 +157,9 @@ bool MovingSelectionState::onUpdateStatusBar(Editor* editor) StatusBar::instance()->setStatusText( 100, fmt::format( - ":pos: {} {} :size: {:3d} {:3d} :offset: {} {}", + ":pos: {} {}" + " :size: {} {}" + " :delta: {} {}", bounds.x, bounds.y, bounds.w, bounds.h, m_delta.x, m_delta.y)); diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index f8027b3a2..49e1bcdcc 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -563,11 +563,27 @@ bool StandbyState::onUpdateStatusBar(Editor* editor) editor->document()->mask(): NULL); std::string buf = fmt::format( - ":pos: {} {} :size: {} {}", + ":pos: {} {}", int(std::floor(spritePos.x)), - int(std::floor(spritePos.y)), - sprite->width(), - sprite->height()); + int(std::floor(spritePos.y))); + + Cel* cel = nullptr; + if (editor->showAutoCelGuides()) { + cel = editor->getSite().cel(); + } + + if (cel) { + buf += fmt::format( + " :start: {} {} :size: {} {}", + cel->bounds().x, cel->bounds().y, + cel->bounds().w, cel->bounds().h); + } + else { + buf += fmt::format( + " :size: {} {}", + sprite->width(), + sprite->height()); + } if (mask) buf += fmt::format(" :selsize: {} {}",