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 | |
23 | namespace app { |
24 | |
25 | SliceWindow::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 | |
112 | bool SliceWindow::show() |
113 | { |
114 | openWindowInForeground(); |
115 | return (closer() == ok()); |
116 | } |
117 | |
118 | std::string SliceWindow::nameValue() const |
119 | { |
120 | return name()->text(); |
121 | } |
122 | |
123 | gfx::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 | |
134 | gfx::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 | |
148 | gfx::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 | |
157 | void 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 | |
174 | void 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 | |
187 | void SliceWindow::onToggleUserData() |
188 | { |
189 | m_userDataView.toggleVisibility(); |
190 | remapWindow(); |
191 | manager()->invalidate(); |
192 | } |
193 | |
194 | void 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 | |
202 | void SliceWindow::onPossibleColorChange() |
203 | { |
204 | m_mods = Mods(int(m_mods) | int(kUserData)); |
205 | } |
206 | |
207 | } // namespace app |
208 | |