1// Aseprite
2// Copyright (C) 2019-2022 Igara Studio S.A.
3// Copyright (C) 2017 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/ui/slice_window.h"
13
14#include "app/doc.h"
15#include "app/pref/preferences.h"
16#include "app/ui/user_data_view.h"
17#include "doc/slice.h"
18#include "doc/sprite.h"
19#include "ui/manager.h"
20
21#include <algorithm>
22
23namespace app {
24
25SliceWindow::SliceWindow(const doc::Sprite* sprite,
26 const doc::SelectedObjects& slices,
27 const doc::frame_t frame)
28 : m_mods(kNone)
29 , m_userDataView(Preferences::instance().slices.userDataVisibility)
30{
31 ASSERT(!slices.empty());
32
33 Slice* slice = slices.frontAs<Slice>();
34 m_userDataView.configureAndSet(slice->userData(), propertiesGrid());
35 userData()->Click.connect([this]{ onToggleUserData(); });
36
37 if (slices.size() == 1) {
38 // If we are going to edit just one slice, we indicate like all
39 // fields were modified, so then the slice properties transaction
40 // is created comparing the window fields with the slice fields
41 // (and not with which field was modified).
42 m_mods = kAll;
43
44 name()->setText(slice->name());
45
46 const doc::SliceKey* key = slice->getByFrame(frame);
47 ASSERT(key);
48 if (!key)
49 return;
50
51 boundsX()->setTextf("%d", key->bounds().x);
52 boundsY()->setTextf("%d", key->bounds().y);
53 boundsW()->setTextf("%d", key->bounds().w);
54 boundsH()->setTextf("%d", key->bounds().h);
55
56 center()->Click.connect([this]{ onCenterChange(); });
57 pivot()->Click.connect([this]{ onPivotChange(); });
58
59 if (key->hasCenter()) {
60 center()->setSelected(true);
61 centerX()->setTextf("%d", key->center().x);
62 centerY()->setTextf("%d", key->center().y);
63 centerW()->setTextf("%d", key->center().w);
64 centerH()->setTextf("%d", key->center().h);
65 }
66 else {
67 onCenterChange();
68 }
69
70 if (key->hasPivot()) {
71 pivot()->setSelected(true);
72 pivotX()->setTextf("%d", key->pivot().x);
73 pivotY()->setTextf("%d", key->pivot().y);
74 }
75 else {
76 onPivotChange();
77 }
78 }
79 // Edit multiple slices
80 else {
81 ui::Entry* entries[] = {
82 name(),
83 boundsX(), boundsY(), boundsW(), boundsH(),
84 centerX(), centerY(), centerW(), centerH(),
85 pivotX(), pivotY() };
86 const Mods entryMods[] = {
87 kName,
88 kBoundsX, kBoundsY, kBoundsW, kBoundsH,
89 kCenterX, kCenterY, kCenterW, kCenterH,
90 kPivotX, kPivotY };
91
92 for (int i=0; i<sizeof(entries)/sizeof(entries[0]); ++i) {
93 auto entry = entries[i];
94 Mods mod = entryMods[i];
95 entry->setSuffix("*");
96 entry->Change.connect(
97 [this, entry, mod]{
98 onModifyField(entry, mod);
99 });
100 }
101
102 ui::Entry* userDataEntry = m_userDataView.entry();
103 userDataEntry->setSuffix("*");
104 userDataEntry->Change.connect(
105 [this, userDataEntry]{ onModifyField(userDataEntry, kUserData); });
106
107 ColorButton* colorButton = m_userDataView.color();
108 colorButton->Click.connect([this]{ onPossibleColorChange(); });
109 }
110}
111
112bool SliceWindow::show()
113{
114 openWindowInForeground();
115 return (closer() == ok());
116}
117
118std::string SliceWindow::nameValue() const
119{
120 return name()->text();
121}
122
123gfx::Rect SliceWindow::boundsValue() const
124{
125 gfx::Rect rc(boundsX()->textInt(),
126 boundsY()->textInt(),
127 boundsW()->textInt(),
128 boundsH()->textInt());
129 if (rc.w < 1) rc.w = 1;
130 if (rc.h < 1) rc.h = 1;
131 return rc;
132}
133
134gfx::Rect SliceWindow::centerValue() const
135{
136 if (!center()->isSelected())
137 return gfx::Rect(0, 0, 0, 0);
138
139 gfx::Rect rc(centerX()->textInt(),
140 centerY()->textInt(),
141 centerW()->textInt(),
142 centerH()->textInt());
143 if (rc.w < 1) rc.w = 1;
144 if (rc.h < 1) rc.h = 1;
145 return rc;
146}
147
148gfx::Point SliceWindow::pivotValue() const
149{
150 if (!pivot()->isSelected())
151 return doc::SliceKey::NoPivot;
152
153 return gfx::Point(pivotX()->textInt(),
154 pivotY()->textInt());
155}
156
157void SliceWindow::onCenterChange()
158{
159 bool state = center()->isSelected();
160
161 centerX()->setEnabled(state);
162 centerY()->setEnabled(state);
163 centerW()->setEnabled(state);
164 centerH()->setEnabled(state);
165
166 if (state) {
167 centerX()->setText("1");
168 centerY()->setText("1");
169 centerW()->setTextf("%d", std::max(1, boundsW()->textInt()-2));
170 centerH()->setTextf("%d", std::max(1, boundsH()->textInt()-2));
171 }
172}
173
174void SliceWindow::onPivotChange()
175{
176 bool state = pivot()->isSelected();
177
178 pivotX()->setEnabled(state);
179 pivotY()->setEnabled(state);
180
181 if (state) {
182 pivotX()->setText("0");
183 pivotY()->setText("0");
184 }
185}
186
187void SliceWindow::onToggleUserData()
188{
189 m_userDataView.toggleVisibility();
190 remapWindow();
191 manager()->invalidate();
192}
193
194void SliceWindow::onModifyField(ui::Entry* entry,
195 const Mods mods)
196{
197 if (entry)
198 entry->setSuffix(std::string());
199 m_mods = Mods(int(m_mods) | int(mods));
200}
201
202void SliceWindow::onPossibleColorChange()
203{
204 m_mods = Mods(int(m_mods) | int(kUserData));
205}
206
207} // namespace app
208