1 | // Aseprite |
2 | // Copyright (C) 2018-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/toolbar.h" |
13 | |
14 | #include "app/app.h" |
15 | #include "app/commands/command.h" |
16 | #include "app/commands/commands.h" |
17 | #include "app/i18n/strings.h" |
18 | #include "app/modules/editors.h" |
19 | #include "app/modules/gfx.h" |
20 | #include "app/tools/active_tool.h" |
21 | #include "app/tools/tool_box.h" |
22 | #include "app/ui/keyboard_shortcuts.h" |
23 | #include "app/ui/main_window.h" |
24 | #include "app/ui/preview_editor.h" |
25 | #include "app/ui/skin/skin_theme.h" |
26 | #include "app/ui/status_bar.h" |
27 | #include "app/ui_context.h" |
28 | #include "fmt/format.h" |
29 | #include "gfx/size.h" |
30 | #include "obs/signal.h" |
31 | #include "os/surface.h" |
32 | #include "ui/ui.h" |
33 | |
34 | #include <string> |
35 | |
36 | namespace app { |
37 | |
38 | using namespace app::skin; |
39 | using namespace gfx; |
40 | using namespace ui; |
41 | using namespace tools; |
42 | |
43 | // Class to show a group of tools (horizontally) |
44 | // This widget is inside the ToolBar::m_popupWindow |
45 | class ToolBar::ToolStrip : public Widget { |
46 | public: |
47 | ToolStrip(ToolGroup* group, ToolBar* toolbar); |
48 | ~ToolStrip(); |
49 | |
50 | ToolGroup* toolGroup() { return m_group; } |
51 | |
52 | obs::signal<void(Tool*)> ToolSelected; |
53 | |
54 | protected: |
55 | bool onProcessMessage(Message* msg) override; |
56 | void onSizeHint(SizeHintEvent& ev) override; |
57 | void onPaint(PaintEvent& ev) override; |
58 | |
59 | private: |
60 | Rect getToolBounds(int index); |
61 | |
62 | ToolGroup* m_group; |
63 | Tool* m_hotTool; |
64 | ToolBar* m_toolbar; |
65 | }; |
66 | |
67 | static Size getToolIconSize(Widget* widget) |
68 | { |
69 | auto theme = SkinTheme::get(widget); |
70 | os::Surface* icon = theme->getToolIcon("configuration" ); |
71 | if (icon) |
72 | return Size(icon->width(), icon->height()); |
73 | else |
74 | return Size(16, 16) * guiscale(); |
75 | } |
76 | |
77 | ////////////////////////////////////////////////////////////////////// |
78 | // ToolBar |
79 | |
80 | ToolBar* ToolBar::m_instance = NULL; |
81 | |
82 | ToolBar::ToolBar() |
83 | : Widget(kGenericWidget) |
84 | , m_openedRecently(false) |
85 | , m_tipTimer(300, this) |
86 | { |
87 | m_instance = this; |
88 | |
89 | setBorder(gfx::Border(1*guiscale(), 0, 1*guiscale(), 0)); |
90 | |
91 | m_hotTool = NULL; |
92 | m_hotIndex = NoneIndex; |
93 | m_openOnHot = false; |
94 | m_popupWindow = NULL; |
95 | m_currentStrip = NULL; |
96 | m_tipWindow = NULL; |
97 | m_tipOpened = false; |
98 | |
99 | ToolBox* toolbox = App::instance()->toolBox(); |
100 | for (Tool* tool : *toolbox) { |
101 | if (m_selectedInGroup.find(tool->getGroup()) == m_selectedInGroup.end()) |
102 | m_selectedInGroup[tool->getGroup()] = tool; |
103 | } |
104 | |
105 | App::instance()->activeToolManager()->add_observer(this); |
106 | } |
107 | |
108 | ToolBar::~ToolBar() |
109 | { |
110 | App::instance()->activeToolManager()->remove_observer(this); |
111 | |
112 | delete m_popupWindow; |
113 | delete m_tipWindow; |
114 | } |
115 | |
116 | bool ToolBar::isToolVisible(Tool* tool) |
117 | { |
118 | return (m_selectedInGroup[tool->getGroup()] == tool); |
119 | } |
120 | |
121 | bool ToolBar::onProcessMessage(Message* msg) |
122 | { |
123 | switch (msg->type()) { |
124 | |
125 | case kMouseDownMessage: { |
126 | auto mouseMsg = static_cast<const MouseMessage*>(msg); |
127 | const Point mousePos = mouseMsg->positionForDisplay(display()); |
128 | ToolBox* toolbox = App::instance()->toolBox(); |
129 | int groups = toolbox->getGroupsCount(); |
130 | Rect toolrc; |
131 | |
132 | ToolGroupList::iterator it = toolbox->begin_group(); |
133 | for (int c=0; c<groups; ++c, ++it) { |
134 | ToolGroup* tool_group = *it; |
135 | Tool* tool = m_selectedInGroup[tool_group]; |
136 | |
137 | toolrc = getToolGroupBounds(c); |
138 | if (mousePos.y >= toolrc.y && |
139 | mousePos.y < toolrc.y+toolrc.h) { |
140 | selectTool(tool); |
141 | |
142 | openPopupWindow(c, tool_group); |
143 | |
144 | // We capture the mouse so the user can continue navigating |
145 | // the ToolBar to open other groups while he is pressing the |
146 | // mouse button. |
147 | captureMouse(); |
148 | } |
149 | } |
150 | |
151 | toolrc = getToolGroupBounds(PreviewVisibilityIndex); |
152 | if (mousePos.y >= toolrc.y && |
153 | mousePos.y < toolrc.y+toolrc.h) { |
154 | // Toggle preview visibility |
155 | PreviewEditorWindow* preview = |
156 | App::instance()->mainWindow()->getPreviewEditor(); |
157 | bool state = preview->isPreviewEnabled(); |
158 | preview->setPreviewEnabled(!state); |
159 | } |
160 | break; |
161 | } |
162 | |
163 | case kMouseMoveMessage: { |
164 | auto mouseMsg = static_cast<const MouseMessage*>(msg); |
165 | const Point mousePos = mouseMsg->positionForDisplay(display()); |
166 | ToolBox* toolbox = App::instance()->toolBox(); |
167 | int groups = toolbox->getGroupsCount(); |
168 | Tool* new_hot_tool = NULL; |
169 | int new_hot_index = NoneIndex; |
170 | Rect toolrc; |
171 | |
172 | ToolGroupList::iterator it = toolbox->begin_group(); |
173 | |
174 | for (int c=0; c<groups; ++c, ++it) { |
175 | ToolGroup* tool_group = *it; |
176 | Tool* tool = m_selectedInGroup[tool_group]; |
177 | |
178 | toolrc = getToolGroupBounds(c); |
179 | if (mousePos.y >= toolrc.y && |
180 | mousePos.y < toolrc.y+toolrc.h) { |
181 | new_hot_tool = tool; |
182 | new_hot_index = c; |
183 | |
184 | if ((m_openOnHot) && (m_hotTool != new_hot_tool) && hasCapture()) { |
185 | openPopupWindow(c, tool_group); |
186 | } |
187 | break; |
188 | } |
189 | } |
190 | |
191 | toolrc = getToolGroupBounds(PreviewVisibilityIndex); |
192 | if (mousePos.y >= toolrc.y && |
193 | mousePos.y < toolrc.y+toolrc.h) { |
194 | new_hot_index = PreviewVisibilityIndex; |
195 | } |
196 | |
197 | // hot button changed |
198 | if (new_hot_tool != m_hotTool || |
199 | new_hot_index != m_hotIndex) { |
200 | |
201 | m_hotTool = new_hot_tool; |
202 | m_hotIndex = new_hot_index; |
203 | invalidate(); |
204 | |
205 | if (!m_currentStrip) { |
206 | if (m_hotIndex != NoneIndex && !hasCapture()) |
207 | openTipWindow(m_hotIndex, m_hotTool); |
208 | else |
209 | closeTipWindow(); |
210 | } |
211 | |
212 | if (m_hotTool) { |
213 | if (hasCapture()) |
214 | selectTool(m_hotTool); |
215 | else |
216 | StatusBar::instance()->showTool(0, m_hotTool); |
217 | } |
218 | } |
219 | |
220 | // We can change the current tool if the user is dragging the |
221 | // mouse over the ToolBar. |
222 | if (hasCapture()) { |
223 | MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg); |
224 | Widget* pick = manager()->pickFromScreenPos(mouseMsg->screenPosition()); |
225 | if (ToolStrip* strip = dynamic_cast<ToolStrip*>(pick)) { |
226 | releaseMouse(); |
227 | |
228 | MouseMessage* mouseMsg2 = new MouseMessage( |
229 | kMouseDownMessage, |
230 | *mouseMsg, |
231 | mouseMsg->positionForDisplay(strip->display())); |
232 | mouseMsg2->setRecipient(strip); |
233 | mouseMsg2->setDisplay(strip->display()); |
234 | manager()->enqueueMessage(mouseMsg2); |
235 | } |
236 | } |
237 | break; |
238 | } |
239 | |
240 | case kMouseUpMessage: |
241 | if (!hasCapture()) |
242 | break; |
243 | |
244 | if (!m_openedRecently) { |
245 | if (m_popupWindow && m_popupWindow->isVisible()) |
246 | m_popupWindow->closeWindow(this); |
247 | } |
248 | m_openedRecently = false; |
249 | |
250 | releaseMouse(); |
251 | [[fallthrough]]; |
252 | |
253 | case kMouseLeaveMessage: |
254 | if (hasCapture()) |
255 | break; |
256 | |
257 | closeTipWindow(); |
258 | |
259 | if (!m_popupWindow || !m_popupWindow->isVisible()) { |
260 | m_tipOpened = false; |
261 | |
262 | m_hotTool = NULL; |
263 | m_hotIndex = NoneIndex; |
264 | invalidate(); |
265 | } |
266 | |
267 | StatusBar::instance()->showDefaultText(); |
268 | break; |
269 | |
270 | case kTimerMessage: |
271 | if (static_cast<TimerMessage*>(msg)->timer() == &m_tipTimer) { |
272 | if (m_tipWindow) |
273 | m_tipWindow->openWindow(); |
274 | |
275 | m_tipTimer.stop(); |
276 | m_tipOpened = true; |
277 | } |
278 | break; |
279 | |
280 | } |
281 | |
282 | return Widget::onProcessMessage(msg); |
283 | } |
284 | |
285 | void ToolBar::onSizeHint(SizeHintEvent& ev) |
286 | { |
287 | Size iconsize = getToolIconSize(this); |
288 | iconsize.w += border().width(); |
289 | iconsize.h += border().height(); |
290 | ev.setSizeHint(iconsize); |
291 | } |
292 | |
293 | void ToolBar::onPaint(ui::PaintEvent& ev) |
294 | { |
295 | gfx::Rect bounds = clientBounds(); |
296 | Graphics* g = ev.graphics(); |
297 | auto theme = SkinTheme::get(this); |
298 | ToolBox* toolbox = App::instance()->toolBox(); |
299 | Tool* activeTool = App::instance()->activeTool(); |
300 | ToolGroupList::iterator it = toolbox->begin_group(); |
301 | int groups = toolbox->getGroupsCount(); |
302 | Rect toolrc; |
303 | |
304 | g->fillRect(theme->colors.tabActiveFace(), bounds); |
305 | |
306 | for (int c=0; c<groups; ++c, ++it) { |
307 | ToolGroup* tool_group = *it; |
308 | Tool* tool = m_selectedInGroup[tool_group]; |
309 | SkinPartPtr nw; |
310 | |
311 | if (activeTool == tool || m_hotIndex == c) { |
312 | nw = theme->parts.toolbuttonHot(); |
313 | } |
314 | else { |
315 | nw = c >= 0 && c < groups-1 ? theme->parts.toolbuttonNormal(): |
316 | theme->parts.toolbuttonLast(); |
317 | } |
318 | |
319 | toolrc = getToolGroupBounds(c); |
320 | toolrc.offset(-origin()); |
321 | theme->drawRect(g, toolrc, nw.get()); |
322 | |
323 | // Draw the tool icon |
324 | os::Surface* icon = theme->getToolIcon(tool->getId().c_str()); |
325 | if (icon) { |
326 | g->drawRgbaSurface(icon, |
327 | toolrc.x+toolrc.w/2-icon->width()/2, |
328 | toolrc.y+toolrc.h/2-icon->height()/2); |
329 | } |
330 | } |
331 | |
332 | // Draw button to show/hide preview |
333 | toolrc = getToolGroupBounds(PreviewVisibilityIndex); |
334 | toolrc.offset(-origin()); |
335 | bool isHot = (m_hotIndex == PreviewVisibilityIndex || |
336 | App::instance()->mainWindow()->getPreviewEditor()->isPreviewEnabled()); |
337 | theme->drawRect( |
338 | g, |
339 | toolrc, |
340 | (isHot ? theme->parts.toolbuttonHot().get(): |
341 | theme->parts.toolbuttonLast().get())); |
342 | |
343 | os::Surface* icon = theme->getToolIcon("minieditor" ); |
344 | if (icon) { |
345 | g->drawRgbaSurface(icon, |
346 | toolrc.x+toolrc.w/2-icon->width()/2, |
347 | toolrc.y+toolrc.h/2-icon->height()/2); |
348 | } |
349 | } |
350 | |
351 | void ToolBar::onVisible(bool visible) |
352 | { |
353 | Widget::onVisible(visible); |
354 | if (!visible) { |
355 | if (m_popupWindow) { |
356 | closePopupWindow(); |
357 | closeTipWindow(); |
358 | } |
359 | } |
360 | } |
361 | |
362 | int ToolBar::getToolGroupIndex(ToolGroup* group) |
363 | { |
364 | ToolBox* toolbox = App::instance()->toolBox(); |
365 | ToolGroupList::iterator it = toolbox->begin_group(); |
366 | int groups = toolbox->getGroupsCount(); |
367 | |
368 | for (int c=0; c<groups; ++c, ++it) { |
369 | if (group == *it) |
370 | return c; |
371 | } |
372 | |
373 | return -1; |
374 | } |
375 | |
376 | void ToolBar::(int group_index, ToolGroup* tool_group) |
377 | { |
378 | if (m_popupWindow) { |
379 | // If we've already open the given group, do nothing. |
380 | if (m_currentStrip && m_currentStrip->toolGroup() == tool_group) |
381 | return; |
382 | |
383 | if (m_closeConn) |
384 | m_closeConn.disconnect(); |
385 | |
386 | onClosePopup(); |
387 | closePopupWindow(); |
388 | } |
389 | |
390 | // Close tip window |
391 | closeTipWindow(); |
392 | |
393 | // If this group contains only one tool, do not show the popup |
394 | ToolBox* toolbox = App::instance()->toolBox(); |
395 | int count = 0; |
396 | for (ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) { |
397 | Tool* tool = *it; |
398 | if (tool->getGroup() == tool_group) |
399 | ++count; |
400 | } |
401 | m_openOnHot = true; |
402 | if (count <= 1) |
403 | return; |
404 | |
405 | // In case this tool contains more than just one tool, show the popup window |
406 | m_popupWindow = new TransparentPopupWindow(PopupWindow::ClickBehavior::CloseOnClickOutsideHotRegion); |
407 | m_closeConn = m_popupWindow->Close.connect([this]{ onClosePopup(); }); |
408 | m_openedRecently = true; |
409 | |
410 | ToolStrip* toolstrip = new ToolStrip(tool_group, this); |
411 | m_currentStrip = toolstrip; |
412 | m_popupWindow->addChild(toolstrip); |
413 | |
414 | Rect rc = getToolGroupBounds(group_index); |
415 | int w = 0; |
416 | |
417 | for (Tool* tool : *toolbox) { |
418 | if (tool->getGroup() == tool_group) |
419 | w += bounds().w-border().width()-1; |
420 | } |
421 | |
422 | rc.x -= w; |
423 | rc.w = w; |
424 | |
425 | // Set hotregion of popup window |
426 | m_popupWindow->setAutoRemap(false); |
427 | ui::fit_bounds(display(), m_popupWindow, rc); |
428 | m_popupWindow->setBounds(rc); |
429 | |
430 | Region rgn(m_popupWindow->boundsOnScreen().enlarge(16*guiscale())); |
431 | rgn.createUnion(rgn, Region(boundsOnScreen())); |
432 | m_popupWindow->setHotRegion(rgn); |
433 | |
434 | m_popupWindow->openWindow(); |
435 | } |
436 | |
437 | void ToolBar::() |
438 | { |
439 | if (m_popupWindow) { |
440 | m_popupWindow->closeWindow(nullptr); |
441 | delete m_popupWindow; |
442 | m_popupWindow = nullptr; |
443 | } |
444 | } |
445 | |
446 | Rect ToolBar::getToolGroupBounds(int group_index) |
447 | { |
448 | ToolBox* toolbox = App::instance()->toolBox(); |
449 | int groups = toolbox->getGroupsCount(); |
450 | Size iconsize = getToolIconSize(this); |
451 | Rect rc(bounds()); |
452 | rc.shrink(border()); |
453 | |
454 | switch (group_index) { |
455 | |
456 | case PreviewVisibilityIndex: |
457 | rc.y += rc.h - iconsize.h - 2*guiscale(); |
458 | rc.h = iconsize.h+2*guiscale(); |
459 | break; |
460 | |
461 | default: |
462 | rc.y += group_index*(iconsize.h-1*guiscale()); |
463 | rc.h = group_index < groups-1 ? iconsize.h+1*guiscale(): |
464 | iconsize.h+2*guiscale(); |
465 | break; |
466 | } |
467 | |
468 | return rc; |
469 | } |
470 | |
471 | Point ToolBar::getToolPositionInGroup(int group_index, Tool* tool) |
472 | { |
473 | ToolBox* toolbox = App::instance()->toolBox(); |
474 | Size iconsize = getToolIconSize(this); |
475 | int nth = 0; |
476 | |
477 | for (ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) { |
478 | if (tool == *it) |
479 | break; |
480 | |
481 | if ((*it)->getGroup() == tool->getGroup()) { |
482 | ++nth; |
483 | } |
484 | } |
485 | |
486 | return Point(iconsize.w/2+iconsize.w*nth, iconsize.h); |
487 | } |
488 | |
489 | void ToolBar::openTipWindow(ToolGroup* tool_group, Tool* tool) |
490 | { |
491 | openTipWindow(getToolGroupIndex(tool_group), tool); |
492 | } |
493 | |
494 | void ToolBar::openTipWindow(int group_index, Tool* tool) |
495 | { |
496 | if (m_tipWindow) |
497 | closeTipWindow(); |
498 | |
499 | std::string tooltip; |
500 | if (tool && group_index >= 0) { |
501 | tooltip = tool->getText(); |
502 | if (tool->getTips().size() > 0) { |
503 | tooltip += ":\n" ; |
504 | tooltip += tool->getTips(); |
505 | } |
506 | |
507 | // Tool shortcut |
508 | KeyPtr key = KeyboardShortcuts::instance()->tool(tool); |
509 | if (key && !key->accels().empty()) { |
510 | tooltip += "\n\n" ; |
511 | tooltip += fmt::format(Strings::tools_shortcut(), |
512 | key->accels().front().toString()); |
513 | } |
514 | } |
515 | else if (group_index == PreviewVisibilityIndex) { |
516 | if (App::instance()->mainWindow()->getPreviewEditor()->isPreviewEnabled()) |
517 | tooltip = Strings::tools_preview_hide(); |
518 | else |
519 | tooltip = Strings::tools_preview_show(); |
520 | } |
521 | else |
522 | return; |
523 | |
524 | m_tipWindow = new TipWindow(tooltip); |
525 | m_tipWindow->remapWindow(); |
526 | |
527 | Rect toolrc = getToolGroupBounds(group_index); |
528 | Point arrow = (tool ? getToolPositionInGroup(group_index, tool): Point(0, 0)); |
529 | if (tool && m_popupWindow && m_popupWindow->isVisible()) |
530 | toolrc.x += arrow.x - m_popupWindow->bounds().w; |
531 | |
532 | m_tipWindow->pointAt(TOP | RIGHT, toolrc, |
533 | ui::Manager::getDefault()->display()); |
534 | |
535 | if (m_tipOpened) |
536 | m_tipWindow->openWindow(); |
537 | else |
538 | m_tipTimer.start(); |
539 | } |
540 | |
541 | void ToolBar::closeTipWindow() |
542 | { |
543 | m_tipTimer.stop(); |
544 | |
545 | if (m_tipWindow) { |
546 | m_tipWindow->closeWindow(NULL); |
547 | delete m_tipWindow; |
548 | m_tipWindow = NULL; |
549 | } |
550 | } |
551 | |
552 | void ToolBar::selectTool(Tool* tool) |
553 | { |
554 | ASSERT(tool); |
555 | |
556 | m_selectedInGroup[tool->getGroup()] = tool; |
557 | |
558 | // Inform to the active tool manager about this tool change. |
559 | App::instance()->activeToolManager()->setSelectedTool(tool); |
560 | |
561 | if (m_currentStrip) |
562 | m_currentStrip->invalidate(); |
563 | |
564 | invalidate(); |
565 | } |
566 | |
567 | void ToolBar::selectToolGroup(tools::ToolGroup* toolGroup) |
568 | { |
569 | ASSERT(toolGroup); |
570 | ASSERT(m_selectedInGroup[toolGroup]); |
571 | if (m_selectedInGroup[toolGroup]) |
572 | selectTool(m_selectedInGroup[toolGroup]); |
573 | } |
574 | |
575 | void ToolBar::() |
576 | { |
577 | closeTipWindow(); |
578 | |
579 | if (!hasMouse()) |
580 | m_tipOpened = false; |
581 | |
582 | m_openOnHot = false; |
583 | m_hotTool = NULL; |
584 | m_hotIndex = NoneIndex; |
585 | m_currentStrip = NULL; |
586 | |
587 | invalidate(); |
588 | } |
589 | |
590 | ////////////////////////////////////////////////////////////////////// |
591 | // ToolStrip |
592 | ////////////////////////////////////////////////////////////////////// |
593 | |
594 | ToolBar::ToolStrip::ToolStrip(ToolGroup* group, ToolBar* toolbar) |
595 | : Widget(kGenericWidget) |
596 | { |
597 | m_group = group; |
598 | m_hotTool = NULL; |
599 | m_toolbar = toolbar; |
600 | |
601 | setDoubleBuffered(true); |
602 | setTransparent(true); |
603 | } |
604 | |
605 | ToolBar::ToolStrip::~ToolStrip() |
606 | { |
607 | } |
608 | |
609 | bool ToolBar::ToolStrip::onProcessMessage(Message* msg) |
610 | { |
611 | switch (msg->type()) { |
612 | |
613 | case kMouseDownMessage: |
614 | captureMouse(); |
615 | [[fallthrough]]; |
616 | |
617 | case kMouseMoveMessage: { |
618 | auto mouseMsg = static_cast<const MouseMessage*>(msg); |
619 | const Point mousePos = mouseMsg->positionForDisplay(display()); |
620 | ToolBox* toolbox = App::instance()->toolBox(); |
621 | Tool* hot_tool = NULL; |
622 | Rect toolrc; |
623 | int index = 0; |
624 | |
625 | for (Tool* tool : *toolbox) { |
626 | if (tool->getGroup() == m_group) { |
627 | toolrc = getToolBounds(index++); |
628 | if (toolrc.contains(Point(mousePos.x, mousePos.y))) { |
629 | hot_tool = tool; |
630 | break; |
631 | } |
632 | } |
633 | } |
634 | |
635 | // Hot button changed |
636 | if (m_hotTool != hot_tool) { |
637 | m_hotTool = hot_tool; |
638 | invalidate(); |
639 | |
640 | // Show the tooltip for the hot tool |
641 | if (m_hotTool && !hasCapture()) |
642 | m_toolbar->openTipWindow(m_group, m_hotTool); |
643 | else |
644 | m_toolbar->closeTipWindow(); |
645 | |
646 | if (m_hotTool) |
647 | StatusBar::instance()->showTool(0, m_hotTool); |
648 | } |
649 | |
650 | if (hasCapture()) { |
651 | if (m_hotTool) |
652 | m_toolbar->selectTool(m_hotTool); |
653 | |
654 | Widget* pick = manager()->pickFromScreenPos(mouseMsg->screenPosition()); |
655 | if (ToolBar* bar = dynamic_cast<ToolBar*>(pick)) { |
656 | releaseMouse(); |
657 | |
658 | MouseMessage* mouseMsg2 = new MouseMessage( |
659 | kMouseDownMessage, |
660 | *mouseMsg, |
661 | mouseMsg->positionForDisplay(pick->display())); |
662 | mouseMsg2->setRecipient(bar); |
663 | mouseMsg2->setDisplay(pick->display()); |
664 | manager()->enqueueMessage(mouseMsg2); |
665 | } |
666 | } |
667 | break; |
668 | } |
669 | |
670 | case kMouseUpMessage: |
671 | if (hasCapture()) { |
672 | releaseMouse(); |
673 | closeWindow(); |
674 | } |
675 | break; |
676 | |
677 | } |
678 | return Widget::onProcessMessage(msg); |
679 | } |
680 | |
681 | void ToolBar::ToolStrip::onSizeHint(SizeHintEvent& ev) |
682 | { |
683 | ToolBox* toolbox = App::instance()->toolBox(); |
684 | int c = 0; |
685 | |
686 | for (ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) { |
687 | Tool* tool = *it; |
688 | if (tool->getGroup() == m_group) { |
689 | ++c; |
690 | } |
691 | } |
692 | |
693 | Size iconsize = getToolIconSize(this); |
694 | ev.setSizeHint(Size(iconsize.w * c, iconsize.h)); |
695 | } |
696 | |
697 | void ToolBar::ToolStrip::onPaint(PaintEvent& ev) |
698 | { |
699 | Graphics* g = ev.graphics(); |
700 | auto theme = SkinTheme::get(this); |
701 | ToolBox* toolbox = App::instance()->toolBox(); |
702 | Tool* activeTool = App::instance()->activeTool(); |
703 | Rect toolrc; |
704 | int index = 0; |
705 | |
706 | for (Tool* tool : *toolbox) { |
707 | if (tool->getGroup() == m_group) { |
708 | SkinPartPtr nw; |
709 | |
710 | if (activeTool == tool || m_hotTool == tool) { |
711 | nw = theme->parts.toolbuttonHot(); |
712 | } |
713 | else { |
714 | nw = theme->parts.toolbuttonLast(); |
715 | } |
716 | |
717 | toolrc = getToolBounds(index++); |
718 | toolrc.offset(-bounds().x, -bounds().y); |
719 | theme->drawRect(g, toolrc, nw.get()); |
720 | |
721 | // Draw the tool icon |
722 | os::Surface* icon = theme->getToolIcon(tool->getId().c_str()); |
723 | if (icon) { |
724 | g->drawRgbaSurface( |
725 | icon, |
726 | toolrc.x+toolrc.w/2-icon->width()/2, |
727 | toolrc.y+toolrc.h/2-icon->height()/2); |
728 | } |
729 | } |
730 | } |
731 | } |
732 | |
733 | Rect ToolBar::ToolStrip::getToolBounds(int index) |
734 | { |
735 | const Rect& bounds(this->bounds()); |
736 | Size iconsize = getToolIconSize(this); |
737 | |
738 | return Rect(bounds.x+index*(iconsize.w-1), bounds.y, |
739 | iconsize.w, bounds.h); |
740 | } |
741 | |
742 | void ToolBar::onActiveToolChange(tools::Tool* tool) |
743 | { |
744 | invalidate(); |
745 | } |
746 | |
747 | void ToolBar::onSelectedToolChange(tools::Tool* tool) |
748 | { |
749 | if (tool && m_selectedInGroup[tool->getGroup()] != tool) |
750 | m_selectedInGroup[tool->getGroup()] = tool; |
751 | |
752 | invalidate(); |
753 | } |
754 | |
755 | } // namespace app |
756 | |