mirror of https://github.com/aseprite/aseprite.git
Add "Transform" checkbox to slices and introduce moving slice content
This commit is contained in:
parent
f9d2f1ce46
commit
ead2023cfd
|
@ -578,6 +578,8 @@ all = All
|
|||
none = None
|
||||
select_slices = Select All Slices
|
||||
deselect_slices = Deselect Slices
|
||||
slice_transform = Transform
|
||||
slice_transform_tip = Transform pixels along slice modification
|
||||
slice_props = Slice Properties
|
||||
delete_slice = Delete Slice
|
||||
discard_brush = Discard Brush (Esc)
|
||||
|
|
|
@ -1650,6 +1650,7 @@ public:
|
|||
: m_doc(nullptr)
|
||||
, m_sel(2)
|
||||
, m_combobox(this)
|
||||
, m_transform(Strings::context_bar_slice_transform())
|
||||
, m_action(2)
|
||||
{
|
||||
auto* theme = SkinTheme::get(this);
|
||||
|
@ -1665,6 +1666,14 @@ public:
|
|||
m_combobox.setExpansive(true);
|
||||
m_combobox.setMinSize(gfx::Size(256*guiscale(), 0));
|
||||
|
||||
if (auto* editor = Editor::activeEditor())
|
||||
m_transform.setSelected(editor->slicesTransforms());
|
||||
m_transform.Click.connect(
|
||||
[this]() {
|
||||
if (auto* editor = Editor::activeEditor())
|
||||
editor->slicesTransforms(m_transform.isSelected());
|
||||
});
|
||||
|
||||
m_action.addItem(theme->parts.iconUserData(), theme->styles.buttonsetItemIconMono());
|
||||
m_action.addItem(theme->parts.iconClose(), theme->styles.buttonsetItemIconMono());
|
||||
m_action.ItemChange.connect(
|
||||
|
@ -1674,6 +1683,7 @@ public:
|
|||
|
||||
addChild(&m_sel);
|
||||
addChild(&m_combobox);
|
||||
addChild(&m_transform);
|
||||
addChild(&m_action);
|
||||
|
||||
m_combobox.setVisible(false);
|
||||
|
@ -1685,6 +1695,8 @@ public:
|
|||
m_sel.at(0), Strings::context_bar_select_slices(), BOTTOM);
|
||||
tooltipManager->addTooltipFor(
|
||||
m_sel.at(1), Strings::context_bar_deselect_slices(), BOTTOM);
|
||||
tooltipManager->addTooltipFor(
|
||||
&m_transform, Strings::context_bar_slice_transform_tip(), BOTTOM);
|
||||
tooltipManager->addTooltipFor(
|
||||
m_action.at(0), Strings::context_bar_slice_props(), BOTTOM);
|
||||
tooltipManager->addTooltipFor(
|
||||
|
@ -1731,6 +1743,12 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
void onInitTheme(InitThemeEvent& ev) override {
|
||||
HBox::onInitTheme(ev);
|
||||
auto* theme = SkinTheme::get(this);
|
||||
m_transform.setStyle(theme->styles.miniCheckBox());
|
||||
}
|
||||
|
||||
void onVisible(bool visible) override {
|
||||
HBox::onVisible(visible);
|
||||
m_combobox.closeListBox();
|
||||
|
@ -1764,6 +1782,7 @@ private:
|
|||
visible != m_action.isVisible());
|
||||
|
||||
m_combobox.setVisible(visible);
|
||||
m_transform.setVisible(visible);
|
||||
m_action.setVisible(visible);
|
||||
|
||||
if (relayout)
|
||||
|
@ -1833,6 +1852,7 @@ private:
|
|||
Doc* m_doc;
|
||||
ButtonSet m_sel;
|
||||
Combo m_combobox;
|
||||
CheckBox m_transform;
|
||||
ButtonSet m_action;
|
||||
bool m_changeFromEntry;
|
||||
std::string m_filter;
|
||||
|
|
|
@ -314,6 +314,8 @@ namespace app {
|
|||
bool selectSliceBox(const gfx::Rect& box);
|
||||
void selectAllSlices();
|
||||
bool hasSelectedSlices() const { return !m_selectedSlices.empty(); }
|
||||
void slicesTransforms(bool value) { m_slicesTransforms = value; }
|
||||
bool slicesTransforms() const { return m_slicesTransforms; }
|
||||
|
||||
// Called by DocView's InputChainElement::onCancel() impl when Esc
|
||||
// key is pressed to cancel the active selection.
|
||||
|
@ -490,6 +492,9 @@ namespace app {
|
|||
|
||||
// For slices
|
||||
doc::SelectedObjects m_selectedSlices;
|
||||
// When true, modifications to slices positions/sizes will transform the
|
||||
// pixels inside their boundaries.
|
||||
bool m_slicesTransforms = false;
|
||||
|
||||
// Active sprite editor with the keyboard focus.
|
||||
static Editor* m_activeEditor;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "doc/slice.h"
|
||||
#include "gfx/point.h"
|
||||
#include "ui/message.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -30,10 +31,12 @@ using namespace ui;
|
|||
MovingSliceState::MovingSliceState(Editor* editor,
|
||||
MouseMessage* msg,
|
||||
const EditorHit& hit,
|
||||
const doc::SelectedObjects& selectedSlices)
|
||||
const doc::SelectedObjects& selectedSlices,
|
||||
PixelsMovementPtr pixelsMovement)
|
||||
: m_frame(editor->frame())
|
||||
, m_hit(hit)
|
||||
, m_items(std::max<std::size_t>(1, selectedSlices.size()))
|
||||
, m_pixelsMovement(pixelsMovement)
|
||||
{
|
||||
m_mouseStart = editor->screenToEditor(msg->position());
|
||||
|
||||
|
@ -48,11 +51,31 @@ MovingSliceState::MovingSliceState(Editor* editor,
|
|||
}
|
||||
}
|
||||
|
||||
if (m_pixelsMovement) {
|
||||
const gfx::Rect totalBounds = selectedSlicesBounds();
|
||||
if (m_hit.border() == (CENTER | MIDDLE)) {
|
||||
m_pixelsMovement->catchImage(gfx::PointF(totalBounds.origin()),
|
||||
HandleType::MovePixelsHandle);
|
||||
}
|
||||
m_pixelsMovement->cutMask();
|
||||
}
|
||||
|
||||
editor->captureMouse();
|
||||
}
|
||||
|
||||
EditorState::LeaveAction MovingSliceState::onLeaveState(Editor *editor, EditorState *newState)
|
||||
{
|
||||
editor->document()->resetTransformation();
|
||||
return StandbyState::onLeaveState(editor, newState);
|
||||
}
|
||||
|
||||
bool MovingSliceState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
if (m_pixelsMovement) {
|
||||
m_pixelsMovement->dropImage();
|
||||
m_pixelsMovement.reset();
|
||||
}
|
||||
|
||||
{
|
||||
ContextWriter writer(UIContext::instance(), 1000);
|
||||
Tx tx(writer, "Slice Movement", ModifyDocument);
|
||||
|
@ -67,6 +90,7 @@ bool MovingSliceState::onMouseUp(Editor* editor, MouseMessage* msg)
|
|||
|
||||
editor->backToPreviousState();
|
||||
editor->releaseMouse();
|
||||
editor->invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -91,6 +115,12 @@ bool MovingSliceState::onMouseMove(Editor* editor, MouseMessage* msg)
|
|||
if (m_hit.border() == (CENTER | MIDDLE)) {
|
||||
rc.x += delta.x;
|
||||
rc.y += delta.y;
|
||||
|
||||
if (m_pixelsMovement) {
|
||||
m_pixelsMovement->moveImage(gfx::PointF(totalBounds.origin()+delta),
|
||||
PixelsMovement::MoveModifier::NormalMovement);
|
||||
m_pixelsMovement->dropImageTemporarily();
|
||||
}
|
||||
}
|
||||
// Move/resize 9-slices center
|
||||
else if (m_hit.type() == EditorHit::SliceCenter) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "app/ui/editor/editor_hit.h"
|
||||
#include "app/ui/editor/standby_state.h"
|
||||
#include "app/ui/editor/pixels_movement.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/selected_objects.h"
|
||||
#include "doc/slice.h"
|
||||
|
@ -23,8 +24,10 @@ namespace app {
|
|||
MovingSliceState(Editor* editor,
|
||||
ui::MouseMessage* msg,
|
||||
const EditorHit& hit,
|
||||
const doc::SelectedObjects& selectedSlices);
|
||||
const doc::SelectedObjects& selectedSlices,
|
||||
PixelsMovementPtr pixelsMovement);
|
||||
|
||||
LeaveAction onLeaveState(Editor *editor, EditorState *newState) override;
|
||||
bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
|
||||
bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override;
|
||||
|
@ -45,6 +48,8 @@ namespace app {
|
|||
EditorHit m_hit;
|
||||
gfx::Point m_mouseStart;
|
||||
std::vector<Item> m_items;
|
||||
// Helper member to move/translate the pixels under the slices.
|
||||
PixelsMovementPtr m_pixelsMovement;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -246,8 +246,50 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
|||
editor->getSite(&site);
|
||||
}
|
||||
|
||||
PixelsMovementPtr pixelsMovement = nullptr;
|
||||
if (editor->slicesTransforms() && !site.selectedSlices().empty()) {
|
||||
site = Site();
|
||||
editor->getSite(&site);
|
||||
ImageRef tmpImage;
|
||||
|
||||
auto& slices = site.selectedSlices();
|
||||
Mask newMask;
|
||||
for (const auto& s : slices.iterateAs<Slice>()) {
|
||||
newMask.add(s->getByFrame(site.frame())->bounds());
|
||||
}
|
||||
|
||||
if (site.layer() &&
|
||||
site.layer()->isTilemap() &&
|
||||
site.tilemapMode() == TilemapMode::Tiles) {
|
||||
tmpImage.reset(new_tilemap_from_mask(site, &newMask));
|
||||
}
|
||||
else {
|
||||
tmpImage.reset(new_image_from_mask(site, &newMask,
|
||||
Preferences::instance().experimental.newBlend()));
|
||||
}
|
||||
|
||||
ASSERT(tmpImage);
|
||||
if (!tmpImage) {
|
||||
// We've received a bug report with this case, we're not sure
|
||||
// yet how to reproduce it. Probably new_tilemap_from_mask() can
|
||||
// return nullptr (e.g. when site.cel() is nullptr?)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear brush preview, as the extra cel will be replaced with the
|
||||
// transformed image.
|
||||
editor->brushPreview().hide();
|
||||
|
||||
pixelsMovement = PixelsMovementPtr(
|
||||
new PixelsMovement(UIContext::instance(),
|
||||
site,
|
||||
tmpImage.get(),
|
||||
&newMask,
|
||||
"Transformation"));
|
||||
}
|
||||
|
||||
MovingSliceState* newState = new MovingSliceState(
|
||||
editor, msg, hit, site.selectedSlices());
|
||||
editor, msg, hit, site.selectedSlices(), pixelsMovement);
|
||||
editor->setState(EditorStatePtr(newState));
|
||||
}
|
||||
else {
|
||||
|
|
Loading…
Reference in New Issue