1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 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/editor/select_box_state.h" |
13 | |
14 | #include "app/app.h" |
15 | #include "app/tools/tool_box.h" |
16 | #include "app/ui/context_bar.h" |
17 | #include "app/ui/editor/editor.h" |
18 | #include "app/ui/main_window.h" |
19 | #include "app/ui/skin/skin_theme.h" |
20 | #include "app/ui_context.h" |
21 | #include "doc/image.h" |
22 | #include "doc/sprite.h" |
23 | #include "gfx/rect.h" |
24 | #include "ui/message.h" |
25 | #include "ui/system.h" |
26 | #include "ui/view.h" |
27 | |
28 | namespace app { |
29 | |
30 | using namespace ui; |
31 | |
32 | SelectBoxState::SelectBoxState(SelectBoxDelegate* delegate, const gfx::Rect& rc, Flags flags) |
33 | : m_delegate(delegate) |
34 | , m_rulers((int(flags) & int(Flags::PaddingRulers))? 6: 4) |
35 | , m_rulersDragAlign(0) |
36 | , m_selectingBox(false) |
37 | , m_flags(flags) |
38 | { |
39 | setBoxBounds(rc); |
40 | if (hasFlag(Flags::PaddingRulers)) { |
41 | gfx::Size padding(0, 0); |
42 | setPaddingBounds(padding); |
43 | } |
44 | } |
45 | |
46 | SelectBoxState::~SelectBoxState() |
47 | { |
48 | ContextBar* contextBar = App::instance()->contextBar(); |
49 | contextBar->updateForActiveTool(); |
50 | } |
51 | |
52 | SelectBoxState::Flags SelectBoxState::getFlags() |
53 | { |
54 | return m_flags; |
55 | } |
56 | |
57 | void SelectBoxState::setFlags(Flags flags) |
58 | { |
59 | m_flags = flags; |
60 | } |
61 | |
62 | void SelectBoxState::setFlag(Flags flag) |
63 | { |
64 | m_flags = Flags(int(flag) | int(m_flags)); |
65 | } |
66 | |
67 | void SelectBoxState::clearFlag(Flags flag) |
68 | { |
69 | m_flags = Flags(~(int(flag)) & int(m_flags)); |
70 | } |
71 | |
72 | gfx::Rect SelectBoxState::getBoxBounds() const |
73 | { |
74 | int x1 = std::min(m_rulers[V1].position(), m_rulers[V2].position()); |
75 | int y1 = std::min(m_rulers[H1].position(), m_rulers[H2].position()); |
76 | int x2 = std::max(m_rulers[V1].position(), m_rulers[V2].position()); |
77 | int y2 = std::max(m_rulers[H1].position(), m_rulers[H2].position()); |
78 | return gfx::Rect(x1, y1, x2-x1, y2-y1); |
79 | } |
80 | |
81 | void SelectBoxState::setBoxBounds(const gfx::Rect& box) |
82 | { |
83 | m_rulers[H1] = Ruler(HORIZONTAL | TOP, box.y); |
84 | m_rulers[H2] = Ruler(HORIZONTAL | BOTTOM, box.y+box.h); |
85 | m_rulers[V1] = Ruler(VERTICAL | LEFT, box.x); |
86 | m_rulers[V2] = Ruler(VERTICAL | RIGHT, box.x+box.w); |
87 | if (hasFlag(Flags::PaddingRulers)) { |
88 | const gfx::Size padding( |
89 | m_rulers[PV].position() - m_rulers[V2].position(), |
90 | m_rulers[PH].position() - m_rulers[H2].position()); |
91 | setPaddingBounds(padding); |
92 | } |
93 | } |
94 | |
95 | // Get and Set Padding for Import Sprite Sheet box state |
96 | gfx::Size SelectBoxState::getPaddingBounds() const |
97 | { |
98 | ASSERT(hasFlag(Flags::PaddingRulers)); |
99 | int w = m_rulers[PV].position() - m_rulers[V2].position(); |
100 | int h = m_rulers[PH].position() - m_rulers[H2].position(); |
101 | if (w < 0) |
102 | w = 0; |
103 | if (h < 0) |
104 | h = 0; |
105 | return gfx::Size(w, h); |
106 | } |
107 | |
108 | void SelectBoxState::setPaddingBounds(const gfx::Size& padding) |
109 | { |
110 | ASSERT(hasFlag(Flags::PaddingRulers)); |
111 | m_rulers[PH] = Ruler(HORIZONTAL | BOTTOM, m_rulers[H2].position() + padding.h); |
112 | m_rulers[PV] = Ruler(VERTICAL | RIGHT, m_rulers[V2].position() + padding.w); |
113 | } |
114 | |
115 | void SelectBoxState::onEnterState(Editor* editor) |
116 | { |
117 | StandbyState::onEnterState(editor); |
118 | |
119 | updateContextBar(); |
120 | |
121 | editor->setDecorator(this); |
122 | editor->invalidate(); |
123 | } |
124 | |
125 | void SelectBoxState::onBeforePopState(Editor* editor) |
126 | { |
127 | editor->setDecorator(NULL); |
128 | editor->invalidate(); |
129 | } |
130 | |
131 | bool SelectBoxState::onMouseDown(Editor* editor, MouseMessage* msg) |
132 | { |
133 | if (msg->left() || msg->right()) { |
134 | m_startingPos = |
135 | editor->screenToEditor(msg->position()) |
136 | - editor->mainTilePosition(); |
137 | |
138 | if (hasFlag(Flags::Rulers)) { |
139 | m_rulersDragAlign = hitTestRulers(editor, msg->position(), true); |
140 | if (m_rulersDragAlign) |
141 | m_startRulers = m_rulers; // Capture start positions |
142 | } |
143 | |
144 | if (hasFlag(Flags::QuickBox) && m_movingRulers.empty()) { |
145 | m_selectingBox = true; |
146 | m_selectingButton = msg->button(); |
147 | setBoxBounds(gfx::Rect(m_startingPos, gfx::Size(1, 1))); |
148 | } |
149 | |
150 | editor->captureMouse(); |
151 | return true; |
152 | } |
153 | return StandbyState::onMouseDown(editor, msg); |
154 | } |
155 | |
156 | bool SelectBoxState::onMouseUp(Editor* editor, MouseMessage* msg) |
157 | { |
158 | m_movingRulers.clear(); |
159 | m_rulersDragAlign = 0; |
160 | |
161 | if (m_selectingBox) { |
162 | m_selectingBox = false; |
163 | |
164 | if (m_delegate) { |
165 | if (m_selectingButton == msg->button()) |
166 | m_delegate->onQuickboxEnd(editor, getBoxBounds(), msg->button()); |
167 | else |
168 | m_delegate->onQuickboxCancel(editor); |
169 | } |
170 | } |
171 | |
172 | return StandbyState::onMouseUp(editor, msg); |
173 | } |
174 | |
175 | bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg) |
176 | { |
177 | bool used = false; |
178 | |
179 | updateContextBar(); |
180 | |
181 | if (hasFlag(Flags::Rulers) && !m_movingRulers.empty()) { |
182 | gfx::Point newPos = |
183 | editor->screenToEditor(msg->position()) |
184 | - editor->mainTilePosition(); |
185 | |
186 | gfx::Point delta = newPos - m_startingPos; |
187 | |
188 | for (int i : m_movingRulers) { |
189 | Ruler& ruler = m_rulers[i]; |
190 | const Ruler& start = m_startRulers[i]; |
191 | Ruler& oppRuler = oppositeRuler(i); |
192 | |
193 | switch (ruler.align() & (HORIZONTAL | VERTICAL)) { |
194 | |
195 | case HORIZONTAL: |
196 | if (hasFlag(Flags::PaddingRulers) && (i == H2)) { |
197 | int pad = m_rulers[PH].position() - m_rulers[H2].position(); |
198 | m_rulers[PH].setPosition(start.position() + delta.y + pad); |
199 | } |
200 | |
201 | ruler.setPosition(start.position() + delta.y); |
202 | if (msg->modifiers() == os::kKeyShiftModifier) |
203 | oppRuler.setPosition(m_startRulers[i ^ 1].position() - delta.y); |
204 | break; |
205 | |
206 | case VERTICAL: |
207 | if (hasFlag(Flags::PaddingRulers) && (i == V2)) { |
208 | int pad = m_rulers[PV].position() - m_rulers[V2].position(); |
209 | m_rulers[PV].setPosition(start.position() + delta.x + pad); |
210 | } |
211 | |
212 | ruler.setPosition(start.position() + delta.x); |
213 | if (msg->modifiers() == os::kKeyShiftModifier) |
214 | oppRuler.setPosition(m_startRulers[i ^ 1].position() - delta.x); |
215 | break; |
216 | } |
217 | } |
218 | used = true; |
219 | } |
220 | |
221 | if (hasFlag(Flags::QuickBox) && m_selectingBox) { |
222 | gfx::Point p1 = m_startingPos; |
223 | gfx::Point p2 = |
224 | editor->screenToEditor(msg->position()) |
225 | - editor->mainTilePosition(); |
226 | |
227 | if (p2.x < p1.x) std::swap(p1.x, p2.x); |
228 | if (p2.y < p1.y) std::swap(p1.y, p2.y); |
229 | ++p2.x; |
230 | ++p2.y; |
231 | |
232 | setBoxBounds(gfx::Rect(p1, p2)); |
233 | used = true; |
234 | } |
235 | |
236 | if (used) { |
237 | if (m_delegate) { |
238 | m_delegate->onChangeRectangle(getBoxBounds()); |
239 | if (hasFlag(Flags::PaddingRulers)) |
240 | m_delegate->onChangePadding(getPaddingBounds()); |
241 | } |
242 | |
243 | editor->invalidate(); |
244 | return true; |
245 | } |
246 | else |
247 | return StandbyState::onMouseMove(editor, msg); |
248 | } |
249 | |
250 | bool SelectBoxState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) |
251 | { |
252 | if (hasFlag(Flags::Rulers)) { |
253 | if (!m_movingRulers.empty()) { |
254 | editor->showMouseCursor(cursorFromAlign(m_rulersDragAlign)); |
255 | return true; |
256 | } |
257 | |
258 | int align = hitTestRulers(editor, mouseScreenPos, false); |
259 | if (align != 0) { |
260 | editor->showMouseCursor(cursorFromAlign(align)); |
261 | return true; |
262 | } |
263 | } |
264 | |
265 | if (!requireBrushPreview()) { |
266 | editor->showMouseCursor(kArrowCursor); |
267 | return true; |
268 | } |
269 | |
270 | return StandbyState::onSetCursor(editor, mouseScreenPos); |
271 | } |
272 | |
273 | bool SelectBoxState::onKeyDown(Editor* editor, ui::KeyMessage* msg) |
274 | { |
275 | if (editor != UIContext::instance()->activeEditor()) |
276 | return false; |
277 | |
278 | // Cancel |
279 | if (msg->scancode() == kKeyEsc) { |
280 | if (hasFlag(Flags::QuickBox)) { |
281 | if (m_delegate) |
282 | m_delegate->onQuickboxCancel(editor); |
283 | return true; |
284 | } |
285 | } |
286 | |
287 | // Use StandbyState implementation |
288 | return StandbyState::onKeyDown(editor, msg); |
289 | } |
290 | |
291 | bool SelectBoxState::acceptQuickTool(tools::Tool* tool) |
292 | { |
293 | return false; |
294 | } |
295 | |
296 | bool SelectBoxState::requireBrushPreview() |
297 | { |
298 | if (hasFlag(Flags::QuickBox)) |
299 | return true; |
300 | |
301 | // Returns false as it overrides default standby state behavior & |
302 | // look. This state uses normal arrow cursors. |
303 | return false; |
304 | } |
305 | |
306 | tools::Ink* SelectBoxState::getStateInk() const |
307 | { |
308 | if (hasFlag(Flags::QuickBox)) |
309 | return App::instance()->toolBox()->getInkById( |
310 | tools::WellKnownInks::Selection); |
311 | else |
312 | return nullptr; |
313 | } |
314 | |
315 | void SelectBoxState::postRenderDecorator(EditorPostRender* render) |
316 | { |
317 | Editor* editor = render->getEditor(); |
318 | render::Projection proj = editor->projection(); |
319 | gfx::Rect sp = editor->sprite()->bounds(); |
320 | gfx::Rect vp = View::getView(editor)->viewportBounds(); |
321 | vp.w += proj.applyX(1); |
322 | vp.h += proj.applyY(1); |
323 | vp = editor->screenToEditor(vp); |
324 | |
325 | // Paint a grid generated by the box |
326 | auto theme = skin::SkinTheme::get(editor); |
327 | const gfx::Color rulerColor = theme->colors.selectBoxRuler(); |
328 | const gfx::Color gridColor = theme->colors.selectBoxGrid(); |
329 | const gfx::Point mainOffset = editor->mainTilePosition(); |
330 | gfx::Rect rc = getBoxBounds(); |
331 | gfx::Size padding; |
332 | if (hasFlag(Flags::PaddingRulers)) |
333 | padding = getPaddingBounds(); |
334 | rc.offset(mainOffset); |
335 | sp.offset(mainOffset); |
336 | |
337 | // With black shadow? |
338 | if (hasFlag(Flags::DarkOutside)) { |
339 | if (hasFlag(Flags::PaddingRulers)) { |
340 | const gfx::Color dark = doc::rgba(0, 0, 0, 128); |
341 | // Top band |
342 | if (rc.y > vp.y) |
343 | render->fillRect(dark, gfx::Rect(vp.x, vp.y, vp.w, rc.y-vp.y)); |
344 | // Left band |
345 | if (rc.x > vp.x) |
346 | render->fillRect(dark, gfx::Rect(vp.x, rc.y, rc.x-vp.x, vp.y2()-rc.y)); |
347 | if (hasFlag(Flags::VGrid)) { |
348 | // Bottom band |
349 | if (sp.y2() < vp.y2()) |
350 | render->fillRect(dark, gfx::Rect(rc.x, sp.y2(), vp.x2()-rc.x, vp.y2()-sp.y2())); |
351 | // Right band |
352 | if (hasFlag(Flags::HGrid)) { |
353 | // VGrid Active, HGrid Active: |
354 | if (sp.x2() < vp.x2()) |
355 | render->fillRect(dark, gfx::Rect(sp.x2(), rc.y, vp.x2()-sp.x2(), sp.y2()-rc.y)); |
356 | } |
357 | else { |
358 | // Just VGrid Active, HGrid disabled: |
359 | if (rc.x2() < vp.x2()) |
360 | render->fillRect(dark, gfx::Rect(rc.x2(), rc.y, vp.x2()-rc.x2(), sp.y2()-rc.y)); |
361 | } |
362 | } |
363 | else { |
364 | if (hasFlag(Flags::HGrid)) { |
365 | // Just HGrid Active, VGrid disabled |
366 | // Bottom band |
367 | if (rc.y2() < vp.y2()) |
368 | render->fillRect(dark, gfx::Rect(rc.x, rc.y2(), vp.x2()-rc.x, vp.y2()-rc.y2())); |
369 | // Right band |
370 | if (sp.x2() < vp.x2()) |
371 | render->fillRect(dark, gfx::Rect(sp.x2(), rc.y, vp.x2()-sp.x2(), rc.h)); |
372 | } |
373 | } |
374 | |
375 | // Draw vertical dark padding big bands |
376 | if (hasFlag(Flags::VGrid) && hasFlag(Flags::HGrid)) { |
377 | std::vector<int> padXTips; |
378 | std::vector<int> padYTips; |
379 | if (rc.w > 0) { |
380 | for (int x=rc.x+rc.w; x<=sp.x+sp.w; x+=(rc.w+padding.w)) { |
381 | if (x + padding.w > sp.x2()) |
382 | render->fillRect(dark, gfx::Rect(x, rc.y, sp.x2()-x, sp.h-rc.y)); |
383 | else |
384 | render->fillRect(dark, gfx::Rect(x, rc.y, padding.w, sp.h-rc.y)); |
385 | padXTips.push_back(x); |
386 | } |
387 | } |
388 | if (rc.h > 0) { |
389 | // Draw horizontal dark chopped padding bands (to complete the dark grid) |
390 | for (int y=rc.y+rc.h; y<=sp.y+sp.h; y+=(rc.h+padding.h)) { |
391 | for (const int& padXTip : padXTips) { |
392 | if (y+padding.h > sp.y2()) |
393 | render->fillRect(dark, gfx::Rect(padXTip-rc.w, y, rc.w, sp.y2()-y)); |
394 | else |
395 | render->fillRect(dark, gfx::Rect(padXTip-rc.w, y, rc.w, padding.h)); |
396 | } |
397 | if (!padXTips.empty()) { |
398 | if (padXTips.back() + padding.w < sp.x2()) { |
399 | if (y + padding.h > sp.y2()) |
400 | render->fillRect(dark, gfx::Rect(padXTips.back() + padding.w, y, |
401 | sp.x2() - padXTips.back() - padding.w, |
402 | sp.y2() - y)); |
403 | else |
404 | render->fillRect(dark, gfx::Rect(padXTips.back() + padding.w, y, |
405 | sp.x2() - padXTips.back() - padding.w, |
406 | padding.h)); |
407 | } |
408 | } |
409 | padYTips.push_back(y); |
410 | } |
411 | } |
412 | // Draw chopped dark rectangles to ban partial tiles |
413 | if (!hasFlag(Flags::IncludePartialTiles)) { |
414 | if (!padXTips.empty() && !padYTips.empty()) { |
415 | if (padXTips.back() + padding.w < sp.x2()) { |
416 | for (const int& padYTip : padYTips) |
417 | render->fillRect(dark, gfx::Rect(padXTips.back() + padding.w, padYTip - rc.h, |
418 | sp.x2() - padXTips.back() - padding.w, rc.h)); |
419 | if (padYTips.back() + padding.h < sp.y2()) { |
420 | render->fillRect(dark, gfx::Rect(padXTips.back() + padding.w, |
421 | padYTips.back() + padding.h, |
422 | sp.x2() - padXTips.back() - padding.w, |
423 | sp.y2() - padYTips.back() - padding.h)); |
424 | } |
425 | } |
426 | if (padYTips.back() + padding.h < sp.y2()) { |
427 | for (const int& padXTip : padXTips) |
428 | render->fillRect(dark, gfx::Rect(padXTip - rc.w, padYTips.back() + padding.h, |
429 | rc.w, sp.y2() - padYTips.back() - padding.h)); |
430 | } |
431 | } |
432 | } |
433 | } |
434 | else if (hasFlag(Flags::HGrid)) { |
435 | if (rc.w > 0) { |
436 | int lastX = 0; |
437 | for (int x=rc.x+rc.w; x<=sp.x+sp.w; x+=rc.w+padding.w) { |
438 | if (x + padding.w > sp.x2()) |
439 | render->fillRect(dark, gfx::Rect(x, rc.y, sp.x2()-x, rc.h)); |
440 | else |
441 | render->fillRect(dark, gfx::Rect(x, rc.y, padding.w, rc.h)); |
442 | lastX = x; |
443 | } |
444 | if (!hasFlag(Flags::IncludePartialTiles) && (lastX > 0)) { |
445 | if (lastX + padding.w < sp.x2()) |
446 | render->fillRect(dark, gfx::Rect(lastX + padding.w, rc.y, |
447 | sp.x2() - lastX - padding.w, rc.h)); |
448 | } |
449 | } |
450 | } |
451 | else if (hasFlag(Flags::VGrid)) { |
452 | if (rc.h > 0) { |
453 | int lastY = 0; |
454 | for (int y=rc.y+rc.h; y<=sp.y+sp.h; y+=rc.h+padding.h) { |
455 | if (y + padding.h > sp.y2()) |
456 | render->fillRect(dark, gfx::Rect(rc.x, y, rc.w, sp.y2()-y)); |
457 | else |
458 | render->fillRect(dark, gfx::Rect(rc.x, y, rc.w, padding.h)); |
459 | lastY = y; |
460 | } |
461 | if (!hasFlag(Flags::IncludePartialTiles) && (lastY > 0)) { |
462 | if (lastY + padding.h < sp.y2()) |
463 | render->fillRect(dark, gfx::Rect(rc.x, lastY + padding.h, |
464 | rc.w, sp.y2() - lastY - padding.h)); |
465 | } |
466 | } |
467 | } |
468 | } |
469 | else { |
470 | // Draw dark zones when SelectBoxState is a simple box |
471 | const gfx::Color dark = doc::rgba(0, 0, 0, 128); |
472 | // Top band |
473 | if (rc.y > vp.y) |
474 | render->fillRect(dark, gfx::Rect(vp.x, vp.y, vp.w, rc.y-vp.y)); |
475 | // Bottom band |
476 | if (rc.y2() < vp.y2()) |
477 | render->fillRect(dark, gfx::Rect(vp.x, rc.y2(), vp.w, vp.y2()-rc.y2())); |
478 | // Left band |
479 | if (rc.x > vp.x) |
480 | render->fillRect(dark, gfx::Rect(vp.x, rc.y, rc.x-vp.x, rc.h)); |
481 | // Right band |
482 | if (rc.x2() < vp.x2()) |
483 | render->fillRect(dark, gfx::Rect(rc.x2(), rc.y, vp.x2()-rc.x2(), rc.h)); |
484 | } |
485 | } |
486 | |
487 | // Draw the grid rulers when padding is posible (i.e. Flag::PaddingRulers=true) |
488 | if (hasFlag(Flags::PaddingRulers)) { |
489 | if (hasFlag(Flags::HGrid) && hasFlag(Flags::VGrid)) { |
490 | if (rc.w > 0 && padding.w == 0) { |
491 | for (int x=rc.x+rc.w*2+padding.w; x<=sp.x+sp.w; x+=rc.w+padding.w) |
492 | render->drawLine(gridColor, x, rc.y, x, sp.y2()); |
493 | for (int x=rc.x+rc.w+padding.w; x<=sp.x+sp.w; x+=rc.w+padding.w) |
494 | render->drawLine(gridColor, x, rc.y, x, sp.y2()); |
495 | } |
496 | |
497 | if (rc.h > 0 && padding.h == 0) { |
498 | for (int y=rc.y+rc.h*2+padding.h; y<=sp.y+sp.h; y+=rc.h+padding.h) |
499 | render->drawLine(gridColor, rc.x, y, sp.x2(), y); |
500 | for (int y=rc.y+rc.h+padding.h; y<=sp.y+sp.h; y+=rc.h+padding.h) |
501 | render->drawLine(gridColor, rc.x, y, sp.x2(), y); |
502 | } |
503 | } |
504 | else if (hasFlag(Flags::HGrid)) { |
505 | if (rc.w > 0 && padding.w == 0) { |
506 | for (int x=rc.x+rc.w*2+padding.w; x<=sp.x+sp.w; x+=rc.w+padding.w) |
507 | render->drawLine(gridColor, x, rc.y, x, rc.y2()); |
508 | for (int x=rc.x+rc.w+padding.w; x<=sp.x+sp.w; x+=rc.w+padding.w) |
509 | render->drawLine(gridColor, x, rc.y, x, rc.y2()); |
510 | } |
511 | } |
512 | else if (hasFlag(Flags::VGrid)) { |
513 | if (rc.h > 0 && padding.h == 0) { |
514 | for (int y=rc.y+rc.h*2+padding.h; y<=sp.y+sp.h; y+=rc.h+padding.h) |
515 | render->drawLine(gridColor, rc.x, y, rc.x2(), y); |
516 | for (int y=rc.y+rc.h+padding.h; y<=sp.y+sp.h; y+=rc.h+padding.h) |
517 | render->drawLine(gridColor, rc.x, y, rc.x2(), y); |
518 | } |
519 | } |
520 | } |
521 | else { |
522 | // Draw the grid rulers when padding is not posible (i.e. Flag::PaddingRulers=false) |
523 | if (hasFlag(Flags::Grid)) { |
524 | if (rc.w > 0) { |
525 | for (int x=rc.x+rc.w*2; x<=sp.x+sp.w; x+=rc.w) |
526 | render->drawLine(gridColor, x, rc.y, x, sp.y+sp.h); |
527 | } |
528 | |
529 | if (rc.h > 0) { |
530 | for (int y=rc.y+rc.h*2; y<=sp.y+sp.h; y+=rc.h) |
531 | render->drawLine(gridColor, rc.x, y, sp.x+sp.w, y); |
532 | } |
533 | } |
534 | else if (hasFlag(Flags::HGrid)) { |
535 | if (rc.w > 0) { |
536 | for (int x=rc.x+rc.w*2; x<=sp.x+sp.w; x+=rc.w) |
537 | render->drawLine(gridColor, x, rc.y, x, rc.y+rc.h); |
538 | } |
539 | } |
540 | else if (hasFlag(Flags::VGrid)) { |
541 | if (rc.h > 0) { |
542 | for (int y=rc.y+rc.h*2; y<=sp.y+sp.h; y+=rc.h) |
543 | render->drawLine(gridColor, rc.x, y, rc.x+rc.w, y); |
544 | } |
545 | } |
546 | } |
547 | |
548 | // Draw the rulers enclosing the box |
549 | if (hasFlag(Flags::Rulers)) { |
550 | for (const Ruler& ruler : m_rulers) { |
551 | switch (ruler.align() & (HORIZONTAL | VERTICAL)) { |
552 | |
553 | case HORIZONTAL: { |
554 | const int y = ruler.position()+mainOffset.y; |
555 | render->drawLine(rulerColor, vp.x, y, vp.x+vp.w-1, y); |
556 | break; |
557 | } |
558 | |
559 | case VERTICAL: { |
560 | const int x = ruler.position()+mainOffset.x; |
561 | render->drawLine(rulerColor, x, vp.y, x, vp.y+vp.h-1); |
562 | break; |
563 | } |
564 | } |
565 | } |
566 | } |
567 | |
568 | if (hasFlag(Flags::QuickBox)) { |
569 | render->drawRect(gfx::rgba(255, 255, 255), rc); |
570 | } |
571 | } |
572 | |
573 | void SelectBoxState::getInvalidDecoratoredRegion(Editor* editor, gfx::Region& region) |
574 | { |
575 | // Do nothing |
576 | } |
577 | |
578 | void SelectBoxState::updateContextBar() |
579 | { |
580 | ContextBar* contextBar = App::instance()->contextBar(); |
581 | contextBar->updateForSelectingBox(m_delegate->onGetContextBarHelp()); |
582 | } |
583 | |
584 | Ruler& SelectBoxState::oppositeRuler(const int rulerIndex) |
585 | { |
586 | // 0 and 1 are opposites, and 2 and 3 |
587 | return m_rulers[rulerIndex ^ 1]; |
588 | } |
589 | |
590 | int SelectBoxState::hitTestRulers(Editor* editor, |
591 | const gfx::Point& mousePos, |
592 | const bool updateMovingRulers) |
593 | { |
594 | ASSERT(H1 == 0); |
595 | ASSERT(H2 == 1); |
596 | ASSERT(V1 == 2); |
597 | ASSERT(V2 == 3); |
598 | int align = 0; |
599 | |
600 | if (updateMovingRulers) |
601 | m_movingRulers.clear(); |
602 | |
603 | for (int i=0; i<int(m_rulers.size()); ++i) { |
604 | const Ruler& ruler = m_rulers[i]; |
605 | const Ruler& oppRuler = oppositeRuler(i); |
606 | |
607 | if (hitTestRuler(editor, ruler, oppRuler, mousePos)) { |
608 | if (!hasFlag(Flags::PaddingRulers) && |
609 | (((ruler.align() & (LEFT | TOP)) && ruler.position() > oppRuler.position()) || |
610 | ((ruler.align() & (RIGHT | BOTTOM)) && ruler.position() <= oppRuler.position()))) |
611 | align |= oppRuler.align(); |
612 | else |
613 | align |= ruler.align(); |
614 | if (updateMovingRulers) |
615 | m_movingRulers.push_back(i); |
616 | } |
617 | } |
618 | |
619 | // Check moving all rulers at the same time |
620 | if (align == 0 && !hasFlag(Flags::QuickBox)) { |
621 | if (editor->editorToScreen( |
622 | getBoxBounds().offset(editor->mainTilePosition())) |
623 | .contains(mousePos)) { |
624 | align = LEFT | TOP | RIGHT | BOTTOM; |
625 | if (updateMovingRulers) { |
626 | // Add all rulers |
627 | for (int i=0; i<m_rulers.size(); ++i) |
628 | m_movingRulers.push_back(i); |
629 | } |
630 | } |
631 | } |
632 | |
633 | return align; |
634 | } |
635 | |
636 | int SelectBoxState::hitTestRuler(Editor* editor, |
637 | const Ruler& ruler, |
638 | const Ruler& oppRuler, |
639 | const gfx::Point& mousePos) |
640 | { |
641 | gfx::Point pt = editor->mainTilePosition(); |
642 | pt = editor->editorToScreen( |
643 | pt + gfx::Point(ruler.position(), ruler.position())); |
644 | |
645 | switch (ruler.align() & (HORIZONTAL | VERTICAL)) { |
646 | |
647 | case HORIZONTAL: |
648 | if (!hasFlag(Flags::PaddingRulers)) { |
649 | if (ruler.position() <= oppRuler.position()) { |
650 | if (mousePos.y <= pt.y+2*guiscale()) |
651 | return ruler.align(); |
652 | } |
653 | else if (ruler.position() > oppRuler.position()) { |
654 | if (mousePos.y >= pt.y-2*guiscale()) |
655 | return ruler.align(); |
656 | } |
657 | } |
658 | if (mousePos.y >= pt.y-2*guiscale() && |
659 | mousePos.y <= pt.y+2*guiscale()) { |
660 | return ruler.align(); |
661 | } |
662 | break; |
663 | |
664 | case VERTICAL: |
665 | if (!hasFlag(Flags::PaddingRulers)) { |
666 | if (ruler.position() <= oppRuler.position()) { |
667 | if (mousePos.x <= pt.x+2*guiscale()) |
668 | return ruler.align(); |
669 | } |
670 | else if (ruler.position() > oppRuler.position()) { |
671 | if (mousePos.x >= pt.x-2*guiscale()) |
672 | return ruler.align(); |
673 | } |
674 | } |
675 | if (mousePos.x >= pt.x-2*guiscale() && |
676 | mousePos.x <= pt.x+2*guiscale()) |
677 | return ruler.align(); |
678 | break; |
679 | } |
680 | |
681 | return 0; |
682 | } |
683 | |
684 | bool SelectBoxState::hasFlag(Flags flag) const |
685 | { |
686 | return (int(m_flags) & int(flag)) == int(flag); |
687 | } |
688 | |
689 | CursorType SelectBoxState::cursorFromAlign(const int align) const |
690 | { |
691 | switch (align & (LEFT | TOP | RIGHT | BOTTOM)) { |
692 | case LEFT: return kSizeWCursor; |
693 | case RIGHT: return kSizeECursor; |
694 | case TOP: return kSizeNCursor; |
695 | case TOP | LEFT: return kSizeNWCursor; |
696 | case TOP | RIGHT: return kSizeNECursor; |
697 | case BOTTOM: return kSizeSCursor; |
698 | case BOTTOM | LEFT: return kSizeSWCursor; |
699 | case BOTTOM | RIGHT: return kSizeSECursor; |
700 | case TOP | BOTTOM | LEFT | RIGHT: return kMoveCursor; |
701 | } |
702 | return kArrowCursor; |
703 | } |
704 | |
705 | } // namespace app |
706 | |