GScrollBar.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #include <LibGUI/GScrollBar.h>
  2. #include <SharedGraphics/StylePainter.h>
  3. #include <SharedGraphics/CharacterBitmap.h>
  4. #include <SharedGraphics/GraphicsBitmap.h>
  5. #include <LibGUI/GPainter.h>
  6. //#define GUTTER_DOES_PAGEUP_PAGEDOWN
  7. static const char* s_up_arrow_bitmap_data = {
  8. " "
  9. " # "
  10. " ### "
  11. " ##### "
  12. " ####### "
  13. " ### "
  14. " ### "
  15. " ### "
  16. " "
  17. };
  18. static const char* s_down_arrow_bitmap_data = {
  19. " "
  20. " ### "
  21. " ### "
  22. " ### "
  23. " ####### "
  24. " ##### "
  25. " ### "
  26. " # "
  27. " "
  28. };
  29. static const char* s_left_arrow_bitmap_data = {
  30. " "
  31. " # "
  32. " ## "
  33. " ###### "
  34. " ####### "
  35. " ###### "
  36. " ## "
  37. " # "
  38. " "
  39. };
  40. static const char* s_right_arrow_bitmap_data = {
  41. " "
  42. " # "
  43. " ## "
  44. " ###### "
  45. " ####### "
  46. " ###### "
  47. " ## "
  48. " # "
  49. " "
  50. };
  51. static CharacterBitmap* s_up_arrow_bitmap;
  52. static CharacterBitmap* s_down_arrow_bitmap;
  53. static CharacterBitmap* s_left_arrow_bitmap;
  54. static CharacterBitmap* s_right_arrow_bitmap;
  55. GScrollBar::GScrollBar(Orientation orientation, GWidget* parent)
  56. : GWidget(parent)
  57. , m_orientation(orientation)
  58. {
  59. if (!s_up_arrow_bitmap)
  60. s_up_arrow_bitmap = &CharacterBitmap::create_from_ascii(s_up_arrow_bitmap_data, 9, 9).leak_ref();
  61. if (!s_down_arrow_bitmap)
  62. s_down_arrow_bitmap = &CharacterBitmap::create_from_ascii(s_down_arrow_bitmap_data, 9, 9).leak_ref();
  63. if (!s_left_arrow_bitmap)
  64. s_left_arrow_bitmap = &CharacterBitmap::create_from_ascii(s_left_arrow_bitmap_data, 9, 9).leak_ref();
  65. if (!s_right_arrow_bitmap)
  66. s_right_arrow_bitmap = &CharacterBitmap::create_from_ascii(s_right_arrow_bitmap_data, 9, 9).leak_ref();
  67. if (m_orientation == Orientation::Vertical) {
  68. set_preferred_size({ 15, 0 });
  69. } else {
  70. set_preferred_size({ 0, 15 });
  71. }
  72. }
  73. GScrollBar::~GScrollBar()
  74. {
  75. }
  76. void GScrollBar::set_range(int min, int max)
  77. {
  78. ASSERT(min <= max);
  79. if (m_min == min && m_max == max)
  80. return;
  81. m_min = min;
  82. m_max = max;
  83. int old_value = m_value;
  84. if (m_value < m_min)
  85. m_value = m_min;
  86. if (m_value > m_max)
  87. m_value = m_max;
  88. if (on_change && m_value != old_value)
  89. on_change(m_value);
  90. update();
  91. }
  92. void GScrollBar::set_value(int value)
  93. {
  94. if (value < m_min)
  95. value = m_min;
  96. if (value > m_max)
  97. value = m_max;
  98. if (value == m_value)
  99. return;
  100. m_value = value;
  101. if (on_change)
  102. on_change(value);
  103. update();
  104. }
  105. Rect GScrollBar::up_button_rect() const
  106. {
  107. return { 0, 0, button_size(), button_size() };
  108. }
  109. Rect GScrollBar::down_button_rect() const
  110. {
  111. if (orientation() == Orientation::Vertical)
  112. return { 0, height() - button_size(), button_size(), button_size() };
  113. else
  114. return { width() - button_size(), 0, button_size(), button_size() };
  115. }
  116. Rect GScrollBar::upper_gutter_rect() const
  117. {
  118. if (orientation() == Orientation::Vertical)
  119. return { 0, button_size(), button_size(), scrubber_rect().top() - button_size() };
  120. else
  121. return { button_size(), 0, scrubber_rect().x() - button_size(), button_size() };
  122. }
  123. Rect GScrollBar::lower_gutter_rect() const
  124. {
  125. auto scrubber_rect = this->scrubber_rect();
  126. if (orientation() == Orientation::Vertical)
  127. return { 0, scrubber_rect.bottom() + 1, button_size(), height() - button_size() - scrubber_rect.bottom() - 1};
  128. else
  129. return { scrubber_rect.right() + 1, 0, width() - button_size() - scrubber_rect.right() - 1, button_size() };
  130. }
  131. int GScrollBar::scrubbable_range_in_pixels() const
  132. {
  133. if (orientation() == Orientation::Vertical)
  134. return height() - button_size() * 2 - scrubber_size();
  135. else
  136. return width() - button_size() * 2 - scrubber_size();
  137. }
  138. bool GScrollBar::has_scrubber() const
  139. {
  140. return m_max != m_min;
  141. }
  142. int GScrollBar::scrubber_size() const
  143. {
  144. int pixel_range = (orientation() == Orientation::Vertical ? height() : width()) - button_size() * 2;
  145. int value_range = m_max - m_min;
  146. return ::max(pixel_range - value_range, button_size());
  147. }
  148. Rect GScrollBar::scrubber_rect() const
  149. {
  150. if (!has_scrubber())
  151. return { };
  152. float x_or_y;
  153. if (m_value == m_min)
  154. x_or_y = button_size();
  155. else if (m_value == m_max)
  156. x_or_y = ((orientation() == Orientation::Vertical ? height() : width()) - button_size() - scrubber_size()) + 1;
  157. else {
  158. float range_size = m_max - m_min;
  159. float available = scrubbable_range_in_pixels();
  160. float step = available / range_size;
  161. x_or_y = (button_size() + (step * m_value));
  162. }
  163. if (orientation() == Orientation::Vertical)
  164. return { 0, (int)x_or_y, button_size(), scrubber_size() };
  165. else
  166. return { (int)x_or_y, 0, scrubber_size(), button_size() };
  167. }
  168. void GScrollBar::paint_event(GPaintEvent& event)
  169. {
  170. GPainter painter(*this);
  171. painter.add_clip_rect(event.rect());
  172. painter.fill_rect(rect(), Color::from_rgb(0xd6d2ce));
  173. StylePainter::the().paint_button(painter, up_button_rect(), ButtonStyle::Normal, false);
  174. painter.draw_bitmap(up_button_rect().location().translated(3, 3), orientation() == Orientation::Vertical ? *s_up_arrow_bitmap : *s_left_arrow_bitmap, has_scrubber() ? Color::Black : Color::MidGray);
  175. StylePainter::the().paint_button(painter, down_button_rect(), ButtonStyle::Normal, false);
  176. painter.draw_bitmap(down_button_rect().location().translated(3, 3), orientation() == Orientation::Vertical ? *s_down_arrow_bitmap : *s_right_arrow_bitmap, has_scrubber() ? Color::Black : Color::MidGray);
  177. if (has_scrubber())
  178. StylePainter::the().paint_button(painter, scrubber_rect(), ButtonStyle::Normal, false);
  179. }
  180. void GScrollBar::mousedown_event(GMouseEvent& event)
  181. {
  182. if (event.button() != GMouseButton::Left)
  183. return;
  184. if (up_button_rect().contains(event.position())) {
  185. set_value(value() - m_step);
  186. return;
  187. }
  188. if (down_button_rect().contains(event.position())) {
  189. set_value(value() + m_step);
  190. return;
  191. }
  192. #ifdef GUTTER_DOES_PAGEUP_PAGEDOWN
  193. if (has_scrubber() && upper_gutter_rect().contains(event.position())) {
  194. set_value(value() - m_big_step);
  195. return;
  196. }
  197. if (has_scrubber() && lower_gutter_rect().contains(event.position())) {
  198. set_value(value() + m_big_step);
  199. return;
  200. }
  201. #endif
  202. if (has_scrubber() && scrubber_rect().contains(event.position())) {
  203. m_scrubbing = true;
  204. m_scrub_start_value = value();
  205. m_scrub_origin = event.position();
  206. update();
  207. return;
  208. }
  209. #ifndef GUTTER_DOES_PAGEUP_PAGEDOWN
  210. if (has_scrubber()) {
  211. float range_size = m_max - m_min;
  212. float available = scrubbable_range_in_pixels();
  213. float x = ::max(0, event.position().x() - button_size() - button_size() / 2);
  214. float y = ::max(0, event.position().y() - button_size() - button_size() / 2);
  215. float rel_x = x / available;
  216. float rel_y = y / available;
  217. if (orientation() == Orientation::Vertical)
  218. set_value(m_min + rel_y * range_size);
  219. else
  220. set_value(m_min + rel_x * range_size);
  221. m_scrubbing = true;
  222. m_scrub_start_value = value();
  223. m_scrub_origin = event.position();
  224. }
  225. #endif
  226. }
  227. void GScrollBar::mouseup_event(GMouseEvent& event)
  228. {
  229. if (event.button() != GMouseButton::Left)
  230. return;
  231. if (!m_scrubbing)
  232. return;
  233. m_scrubbing = false;
  234. update();
  235. }
  236. void GScrollBar::mousemove_event(GMouseEvent& event)
  237. {
  238. if (!m_scrubbing)
  239. return;
  240. float delta = orientation() == Orientation::Vertical ? (event.y() - m_scrub_origin.y()) : (event.x() - m_scrub_origin.x());
  241. float scrubbable_range = scrubbable_range_in_pixels();
  242. float value_steps_per_scrubbed_pixel = (m_max - m_min) / scrubbable_range;
  243. float new_value = m_scrub_start_value + (value_steps_per_scrubbed_pixel * delta);
  244. set_value(new_value);
  245. }