GScrollBar.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #include <LibGUI/GScrollBar.h>
  2. #include <LibGUI/GStyle.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() * 3;
  135. else
  136. return width() - button_size() * 3;
  137. }
  138. bool GScrollBar::has_scrubber() const
  139. {
  140. return m_max != m_min;
  141. }
  142. Rect GScrollBar::scrubber_rect() const
  143. {
  144. if (!has_scrubber())
  145. return { };
  146. float x_or_y;
  147. if (m_value == m_min)
  148. x_or_y = button_size();
  149. else if (m_value == m_max)
  150. x_or_y = ((orientation() == Orientation::Vertical ? height() : width()) - (button_size() * 2)) + 1;
  151. else {
  152. float range_size = m_max - m_min;
  153. float available = scrubbable_range_in_pixels();
  154. float step = available / range_size;
  155. x_or_y = (button_size() + (step * m_value));
  156. }
  157. if (orientation() == Orientation::Vertical)
  158. return { 0, (int)x_or_y, button_size(), button_size() };
  159. else
  160. return { (int)x_or_y, 0, button_size(), button_size() };
  161. }
  162. void GScrollBar::paint_event(GPaintEvent& event)
  163. {
  164. GPainter painter(*this);
  165. painter.set_clip_rect(event.rect());
  166. painter.fill_rect(rect(), Color::from_rgb(0xd6d2ce));
  167. GStyle::the().paint_button(painter, up_button_rect(), GButtonStyle::Normal, false);
  168. 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);
  169. GStyle::the().paint_button(painter, down_button_rect(), GButtonStyle::Normal, false);
  170. 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);
  171. if (has_scrubber())
  172. GStyle::the().paint_button(painter, scrubber_rect(), GButtonStyle::Normal, false);
  173. }
  174. void GScrollBar::mousedown_event(GMouseEvent& event)
  175. {
  176. if (event.button() != GMouseButton::Left)
  177. return;
  178. if (up_button_rect().contains(event.position())) {
  179. set_value(value() - m_step);
  180. return;
  181. }
  182. if (down_button_rect().contains(event.position())) {
  183. set_value(value() + m_step);
  184. return;
  185. }
  186. #ifdef GUTTER_DOES_PAGEUP_PAGEDOWN
  187. if (has_scrubber() && upper_gutter_rect().contains(event.position())) {
  188. set_value(value() - m_big_step);
  189. return;
  190. }
  191. if (has_scrubber() && lower_gutter_rect().contains(event.position())) {
  192. set_value(value() + m_big_step);
  193. return;
  194. }
  195. #endif
  196. if (has_scrubber() && scrubber_rect().contains(event.position())) {
  197. m_scrubbing = true;
  198. m_scrub_start_value = value();
  199. m_scrub_origin = event.position();
  200. update();
  201. return;
  202. }
  203. #ifndef GUTTER_DOES_PAGEUP_PAGEDOWN
  204. if (has_scrubber()) {
  205. float range_size = m_max - m_min;
  206. float available = scrubbable_range_in_pixels();
  207. float x = ::max(0, event.position().x() - button_size() - button_size() / 2);
  208. float y = ::max(0, event.position().y() - button_size() - button_size() / 2);
  209. float rel_x = x / available;
  210. float rel_y = y / available;
  211. if (orientation() == Orientation::Vertical)
  212. set_value(m_min + rel_y * range_size);
  213. else
  214. set_value(m_min + rel_x * range_size);
  215. m_scrubbing = true;
  216. m_scrub_start_value = value();
  217. m_scrub_origin = event.position();
  218. }
  219. #endif
  220. }
  221. void GScrollBar::mouseup_event(GMouseEvent& event)
  222. {
  223. if (event.button() != GMouseButton::Left)
  224. return;
  225. if (!m_scrubbing)
  226. return;
  227. m_scrubbing = false;
  228. update();
  229. }
  230. void GScrollBar::mousemove_event(GMouseEvent& event)
  231. {
  232. if (!m_scrubbing)
  233. return;
  234. float delta = orientation() == Orientation::Vertical ? (event.y() - m_scrub_origin.y()) : (event.x() - m_scrub_origin.x());
  235. float scrubbable_range = scrubbable_range_in_pixels();
  236. float value_steps_per_scrubbed_pixel = (m_max - m_min) / scrubbable_range;
  237. float new_value = m_scrub_start_value + (value_steps_per_scrubbed_pixel * delta);
  238. set_value(new_value);
  239. }