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
28namespace app {
29
30using namespace ui;
31
32SelectBoxState::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
46SelectBoxState::~SelectBoxState()
47{
48 ContextBar* contextBar = App::instance()->contextBar();
49 contextBar->updateForActiveTool();
50}
51
52SelectBoxState::Flags SelectBoxState::getFlags()
53{
54 return m_flags;
55}
56
57void SelectBoxState::setFlags(Flags flags)
58{
59 m_flags = flags;
60}
61
62void SelectBoxState::setFlag(Flags flag)
63{
64 m_flags = Flags(int(flag) | int(m_flags));
65}
66
67void SelectBoxState::clearFlag(Flags flag)
68{
69 m_flags = Flags(~(int(flag)) & int(m_flags));
70}
71
72gfx::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
81void 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
96gfx::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
108void 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
115void SelectBoxState::onEnterState(Editor* editor)
116{
117 StandbyState::onEnterState(editor);
118
119 updateContextBar();
120
121 editor->setDecorator(this);
122 editor->invalidate();
123}
124
125void SelectBoxState::onBeforePopState(Editor* editor)
126{
127 editor->setDecorator(NULL);
128 editor->invalidate();
129}
130
131bool 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
156bool 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
175bool 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
250bool 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
273bool 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
291bool SelectBoxState::acceptQuickTool(tools::Tool* tool)
292{
293 return false;
294}
295
296bool 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
306tools::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
315void 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
573void SelectBoxState::getInvalidDecoratoredRegion(Editor* editor, gfx::Region& region)
574{
575 // Do nothing
576}
577
578void SelectBoxState::updateContextBar()
579{
580 ContextBar* contextBar = App::instance()->contextBar();
581 contextBar->updateForSelectingBox(m_delegate->onGetContextBarHelp());
582}
583
584Ruler& SelectBoxState::oppositeRuler(const int rulerIndex)
585{
586 // 0 and 1 are opposites, and 2 and 3
587 return m_rulers[rulerIndex ^ 1];
588}
589
590int 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
636int 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
684bool SelectBoxState::hasFlag(Flags flag) const
685{
686 return (int(m_flags) & int(flag)) == int(flag);
687}
688
689CursorType 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