1 // Aseprite UI Library
2 // Copyright (C) 2001-2017  David Capello
3 //
4 // This file is released under the terms of the MIT license.
5 // Read LICENSE.txt for more information.
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include "gfx/size.h"
12 #include "ui/message.h"
13 #include "ui/paint_event.h"
14 #include "ui/scroll_bar.h"
15 #include "ui/theme.h"
16 
17 namespace ui {
18 
19 using namespace gfx;
20 
21 // Internal stuff shared by all scroll-bars (as the user cannot move
22 // two scroll-bars at the same time).
23 int ScrollBar::m_wherepos = 0;
24 int ScrollBar::m_whereclick = 0;
25 
ScrollBar(int align,ScrollableViewDelegate * delegate)26 ScrollBar::ScrollBar(int align, ScrollableViewDelegate* delegate)
27   : Widget(kViewScrollbarWidget)
28   , m_delegate(delegate)
29   , m_thumbStyle(nullptr)
30   , m_barWidth(0)
31   , m_pos(0)
32   , m_size(0)
33 {
34   setAlign(align);
35   initTheme();
36 }
37 
setPos(int pos)38 void ScrollBar::setPos(int pos)
39 {
40   if (m_pos != pos) {
41     m_pos = pos;
42     invalidate();
43   }
44 }
45 
setSize(int size)46 void ScrollBar::setSize(int size)
47 {
48   if (m_size != size) {
49     m_size = size;
50     invalidate();
51   }
52 }
53 
getScrollBarThemeInfo(int * pos,int * len)54 void ScrollBar::getScrollBarThemeInfo(int* pos, int* len)
55 {
56   getScrollBarInfo(pos, len, NULL, NULL);
57 }
58 
onProcessMessage(Message * msg)59 bool ScrollBar::onProcessMessage(Message* msg)
60 {
61 #define MOUSE_IN(x1, y1, x2, y2) \
62   ((mousePos.x >= (x1)) && (mousePos.x <= (x2)) && \
63    (mousePos.y >= (y1)) && (mousePos.y <= (y2)))
64 
65   switch (msg->type()) {
66 
67     case kMouseDownMessage: {
68       gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
69       int x1, y1, x2, y2;
70       int u1, v1, u2, v2;
71       bool ret = false;
72       int pos, len;
73 
74       getScrollBarThemeInfo(&pos, &len);
75 
76       m_wherepos = pos;
77       m_whereclick = (align() & HORIZONTAL) ?
78         mousePos.x:
79         mousePos.y;
80 
81       x1 = bounds().x;
82       y1 = bounds().y;
83       x2 = bounds().x2()-1;
84       y2 = bounds().y2()-1;
85 
86       u1 = x1 + border().left();
87       v1 = y1 + border().top();
88       u2 = x2 - border().right();
89       v2 = y2 - border().bottom();
90 
91       Point scroll = m_delegate->viewScroll();
92 
93       if (align() & HORIZONTAL) {
94         // in the bar
95         if (MOUSE_IN(u1+pos, v1, u1+pos+len-1, v2)) {
96           // capture mouse
97         }
98         // left
99         else if (MOUSE_IN(x1, y1, u1+pos-1, y2)) {
100           scroll.x -= m_delegate->visibleSize().w/2;
101           ret = true;
102         }
103         // right
104         else if (MOUSE_IN(u1+pos+len, y1, x2, y2)) {
105           scroll.x += m_delegate->visibleSize().w/2;
106           ret = true;
107         }
108       }
109       else {
110         // in the bar
111         if (MOUSE_IN(u1, v1+pos, u2, v1+pos+len-1)) {
112           // capture mouse
113         }
114         // left
115         else if (MOUSE_IN(x1, y1, x2, v1+pos-1)) {
116           scroll.y -= m_delegate->visibleSize().h/2;
117           ret = true;
118         }
119         // right
120         else if (MOUSE_IN(x1, v1+pos+len, x2, y2)) {
121           scroll.y += m_delegate->visibleSize().h/2;
122           ret = true;
123         }
124       }
125 
126       if (ret) {
127         m_delegate->setViewScroll(scroll);
128         return ret;
129       }
130 
131       setSelected(true);
132       captureMouse();
133 
134       // continue to kMouseMoveMessage handler...
135     }
136 
137     case kMouseMoveMessage:
138       if (hasCapture()) {
139         gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
140         int pos, len, bar_size, viewport_size;
141 
142         getScrollBarInfo(&pos, &len, &bar_size, &viewport_size);
143 
144         if (bar_size > len) {
145           Point scroll = m_delegate->viewScroll();
146 
147           if (align() & HORIZONTAL) {
148             pos = (m_wherepos + mousePos.x - m_whereclick);
149             pos = MID(0, pos, bar_size - len);
150 
151             scroll.x = (m_size - viewport_size) * pos / (bar_size - len);
152           }
153           else {
154             pos = (m_wherepos + mousePos.y - m_whereclick);
155             pos = MID(0, pos, bar_size - len);
156 
157             scroll.y = (m_size - viewport_size) * pos / (bar_size - len);
158           }
159 
160           m_delegate->setViewScroll(scroll);
161         }
162         return true;
163       }
164       break;
165 
166     case kMouseUpMessage:
167       setSelected(false);
168       releaseMouse();
169       break;
170 
171     case kMouseEnterMessage:
172     case kMouseLeaveMessage:
173       // TODO add something to avoid this (theme specific stuff)
174       invalidate();
175       break;
176   }
177 
178   return Widget::onProcessMessage(msg);
179 }
180 
onInitTheme(InitThemeEvent & ev)181 void ScrollBar::onInitTheme(InitThemeEvent& ev)
182 {
183   Widget::onInitTheme(ev);
184   m_barWidth = theme()->getScrollbarSize();
185 }
186 
onPaint(PaintEvent & ev)187 void ScrollBar::onPaint(PaintEvent& ev)
188 {
189   gfx::Rect thumbBounds = clientBounds();
190   if (align() & HORIZONTAL)
191     getScrollBarThemeInfo(&thumbBounds.x, &thumbBounds.w);
192   else
193     getScrollBarThemeInfo(&thumbBounds.y, &thumbBounds.h);
194 
195   theme()->paintScrollBar(
196     ev.graphics(), this, style(), thumbStyle(),
197     clientBounds(), thumbBounds);
198 }
199 
getScrollBarInfo(int * _pos,int * _len,int * _bar_size,int * _viewport_size)200 void ScrollBar::getScrollBarInfo(int *_pos, int *_len, int *_bar_size, int *_viewport_size)
201 {
202   int bar_size, viewport_size;
203   int pos, len;
204   int border_width;
205 
206   if (align() & HORIZONTAL) {
207     bar_size = bounds().w;
208     viewport_size = m_delegate->visibleSize().w;
209     border_width = border().height();
210   }
211   else {
212     bar_size = bounds().h;
213     viewport_size = m_delegate->visibleSize().h;
214     border_width = border().width();
215   }
216 
217   if (m_size <= viewport_size) {
218     len = bar_size;
219     pos = 0;
220   }
221   else if (m_size > 0) {
222     len = bar_size * viewport_size / m_size;
223     len = MID(theme()->getScrollbarSize()*2-border_width, len, bar_size);
224     pos = (bar_size-len) * m_pos / (m_size-viewport_size);
225     pos = MID(0, pos, bar_size-len);
226   }
227   else {
228     len = pos = 0;
229   }
230 
231   if (_pos) *_pos = pos;
232   if (_len) *_len = len;
233   if (_bar_size) *_bar_size = bar_size;
234   if (_viewport_size) *_viewport_size = viewport_size;
235 }
236 
237 } // namespace ui
238