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