GScrollBar.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. #include <LibGUI/GPainter.h>
  2. #include <LibGUI/GScrollBar.h>
  3. #include <LibDraw/CharacterBitmap.h>
  4. #include <LibDraw/GraphicsBitmap.h>
  5. #include <LibDraw/StylePainter.h>
  6. static const char* s_up_arrow_bitmap_data = {
  7. " "
  8. " # "
  9. " ### "
  10. " ##### "
  11. " ####### "
  12. " ### "
  13. " ### "
  14. " ### "
  15. " "
  16. };
  17. static const char* s_down_arrow_bitmap_data = {
  18. " "
  19. " ### "
  20. " ### "
  21. " ### "
  22. " ####### "
  23. " ##### "
  24. " ### "
  25. " # "
  26. " "
  27. };
  28. static const char* s_left_arrow_bitmap_data = {
  29. " "
  30. " # "
  31. " ## "
  32. " ###### "
  33. " ####### "
  34. " ###### "
  35. " ## "
  36. " # "
  37. " "
  38. };
  39. static const char* s_right_arrow_bitmap_data = {
  40. " "
  41. " # "
  42. " ## "
  43. " ###### "
  44. " ####### "
  45. " ###### "
  46. " ## "
  47. " # "
  48. " "
  49. };
  50. static CharacterBitmap* s_up_arrow_bitmap;
  51. static CharacterBitmap* s_down_arrow_bitmap;
  52. static CharacterBitmap* s_left_arrow_bitmap;
  53. static CharacterBitmap* s_right_arrow_bitmap;
  54. GScrollBar::GScrollBar(Orientation orientation, GWidget* parent)
  55. : GWidget(parent)
  56. , m_orientation(orientation)
  57. {
  58. m_automatic_scrolling_timer = CTimer::construct(this);
  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. m_automatic_scrolling_timer->set_interval(100);
  73. m_automatic_scrolling_timer->on_timeout = [this] {
  74. on_automatic_scrolling_timer_fired();
  75. };
  76. }
  77. GScrollBar::~GScrollBar()
  78. {
  79. }
  80. void GScrollBar::set_range(int min, int max)
  81. {
  82. ASSERT(min <= max);
  83. if (m_min == min && m_max == max)
  84. return;
  85. m_min = min;
  86. m_max = max;
  87. int old_value = m_value;
  88. if (m_value < m_min)
  89. m_value = m_min;
  90. if (m_value > m_max)
  91. m_value = m_max;
  92. if (on_change && m_value != old_value)
  93. on_change(m_value);
  94. update();
  95. }
  96. void GScrollBar::set_value(int value)
  97. {
  98. if (value < m_min)
  99. value = m_min;
  100. if (value > m_max)
  101. value = m_max;
  102. if (value == m_value)
  103. return;
  104. m_value = value;
  105. if (on_change)
  106. on_change(value);
  107. update();
  108. }
  109. Rect GScrollBar::decrement_button_rect() const
  110. {
  111. return { 0, 0, button_width(), button_height() };
  112. }
  113. Rect GScrollBar::increment_button_rect() const
  114. {
  115. if (orientation() == Orientation::Vertical)
  116. return { 0, height() - button_height(), button_width(), button_height() };
  117. else
  118. return { width() - button_width(), 0, button_width(), button_height() };
  119. }
  120. Rect GScrollBar::decrement_gutter_rect() const
  121. {
  122. if (orientation() == Orientation::Vertical)
  123. return { 0, button_height(), button_width(), scrubber_rect().top() - button_height() };
  124. else
  125. return { button_width(), 0, scrubber_rect().x() - button_width(), button_height() };
  126. }
  127. Rect GScrollBar::increment_gutter_rect() const
  128. {
  129. auto scrubber_rect = this->scrubber_rect();
  130. if (orientation() == Orientation::Vertical)
  131. return { 0, scrubber_rect.bottom() + 1, button_width(), height() - button_height() - scrubber_rect.bottom() - 1 };
  132. else
  133. return { scrubber_rect.right() + 1, 0, width() - button_width() - scrubber_rect.right() - 1, button_width() };
  134. }
  135. int GScrollBar::scrubbable_range_in_pixels() const
  136. {
  137. if (orientation() == Orientation::Vertical)
  138. return height() - button_height() * 2 - scrubber_size();
  139. else
  140. return width() - button_width() * 2 - scrubber_size();
  141. }
  142. bool GScrollBar::has_scrubber() const
  143. {
  144. return m_max != m_min;
  145. }
  146. int GScrollBar::scrubber_size() const
  147. {
  148. int pixel_range = length(orientation()) - button_size() * 2;
  149. int value_range = m_max - m_min;
  150. return ::max(pixel_range - value_range, button_size());
  151. }
  152. Rect GScrollBar::scrubber_rect() const
  153. {
  154. if (!has_scrubber() || length(orientation()) <= (button_size() * 2) + scrubber_size())
  155. return {};
  156. float x_or_y;
  157. if (m_value == m_min)
  158. x_or_y = button_size();
  159. else if (m_value == m_max)
  160. x_or_y = (length(orientation()) - button_size() - scrubber_size()) + 1;
  161. else {
  162. float range_size = m_max - m_min;
  163. float available = scrubbable_range_in_pixels();
  164. float step = available / range_size;
  165. x_or_y = (button_size() + (step * m_value));
  166. }
  167. if (orientation() == Orientation::Vertical)
  168. return { 0, (int)x_or_y, button_width(), scrubber_size() };
  169. else
  170. return { (int)x_or_y, 0, scrubber_size(), button_height() };
  171. }
  172. void GScrollBar::paint_event(GPaintEvent& event)
  173. {
  174. GPainter painter(*this);
  175. painter.add_clip_rect(event.rect());
  176. painter.fill_rect(rect(), Color::from_rgb(0xd6d2ce));
  177. StylePainter::paint_button(painter, decrement_button_rect(), ButtonStyle::Normal, false, m_hovered_component == Component::DecrementButton);
  178. StylePainter::paint_button(painter, increment_button_rect(), ButtonStyle::Normal, false, m_hovered_component == Component::IncrementButton);
  179. if (length(orientation()) > default_button_size()) {
  180. painter.draw_bitmap(decrement_button_rect().location().translated(3, 3), orientation() == Orientation::Vertical ? *s_up_arrow_bitmap : *s_left_arrow_bitmap, has_scrubber() ? Color::Black : Color::MidGray);
  181. painter.draw_bitmap(increment_button_rect().location().translated(3, 3), orientation() == Orientation::Vertical ? *s_down_arrow_bitmap : *s_right_arrow_bitmap, has_scrubber() ? Color::Black : Color::MidGray);
  182. }
  183. if (has_scrubber())
  184. StylePainter::paint_button(painter, scrubber_rect(), ButtonStyle::Normal, false, m_hovered_component == Component::Scrubber || m_scrubber_in_use);
  185. }
  186. void GScrollBar::on_automatic_scrolling_timer_fired()
  187. {
  188. if (m_automatic_scrolling_direction == AutomaticScrollingDirection::Decrement) {
  189. set_value(value() - m_step);
  190. return;
  191. }
  192. if (m_automatic_scrolling_direction == AutomaticScrollingDirection::Increment) {
  193. set_value(value() + m_step);
  194. return;
  195. }
  196. }
  197. void GScrollBar::mousedown_event(GMouseEvent& event)
  198. {
  199. if (event.button() != GMouseButton::Left)
  200. return;
  201. if (decrement_button_rect().contains(event.position())) {
  202. m_automatic_scrolling_direction = AutomaticScrollingDirection::Decrement;
  203. set_automatic_scrolling_active(true);
  204. return;
  205. }
  206. if (increment_button_rect().contains(event.position())) {
  207. m_automatic_scrolling_direction = AutomaticScrollingDirection::Increment;
  208. set_automatic_scrolling_active(true);
  209. return;
  210. }
  211. if (has_scrubber() && scrubber_rect().contains(event.position())) {
  212. m_scrubber_in_use = true;
  213. m_scrubbing = true;
  214. m_scrub_start_value = value();
  215. m_scrub_origin = event.position();
  216. update();
  217. return;
  218. }
  219. if (has_scrubber()) {
  220. float range_size = m_max - m_min;
  221. float available = scrubbable_range_in_pixels();
  222. float x = ::max(0, event.position().x() - button_width() - button_width() / 2);
  223. float y = ::max(0, event.position().y() - button_height() - button_height() / 2);
  224. float rel_x = x / available;
  225. float rel_y = y / available;
  226. if (orientation() == Orientation::Vertical)
  227. set_value(m_min + rel_y * range_size);
  228. else
  229. set_value(m_min + rel_x * range_size);
  230. m_scrubbing = true;
  231. m_scrub_start_value = value();
  232. m_scrub_origin = event.position();
  233. }
  234. }
  235. void GScrollBar::mouseup_event(GMouseEvent& event)
  236. {
  237. if (event.button() != GMouseButton::Left)
  238. return;
  239. m_scrubber_in_use = false;
  240. m_automatic_scrolling_direction = AutomaticScrollingDirection::None;
  241. set_automatic_scrolling_active(false);
  242. if (!m_scrubbing)
  243. return;
  244. m_scrubbing = false;
  245. update();
  246. }
  247. void GScrollBar::mousewheel_event(GMouseEvent& event)
  248. {
  249. if (!is_scrollable())
  250. return;
  251. set_value(value() + event.wheel_delta() * m_step);
  252. GWidget::mousewheel_event(event);
  253. }
  254. void GScrollBar::set_automatic_scrolling_active(bool active)
  255. {
  256. if (active) {
  257. on_automatic_scrolling_timer_fired();
  258. m_automatic_scrolling_timer->start();
  259. } else {
  260. m_automatic_scrolling_timer->stop();
  261. }
  262. }
  263. void GScrollBar::mousemove_event(GMouseEvent& event)
  264. {
  265. auto old_hovered_component = m_hovered_component;
  266. if (scrubber_rect().contains(event.position()))
  267. m_hovered_component = Component::Scrubber;
  268. else if (decrement_button_rect().contains(event.position()))
  269. m_hovered_component = Component::DecrementButton;
  270. else if (increment_button_rect().contains(event.position()))
  271. m_hovered_component = Component::IncrementButton;
  272. else if (rect().contains(event.position()))
  273. m_hovered_component = Component::Gutter;
  274. else
  275. m_hovered_component = Component::Invalid;
  276. if (old_hovered_component != m_hovered_component) {
  277. update();
  278. if (m_automatic_scrolling_direction == AutomaticScrollingDirection::Decrement)
  279. set_automatic_scrolling_active(m_hovered_component == Component::DecrementButton);
  280. else if (m_automatic_scrolling_direction == AutomaticScrollingDirection::Increment)
  281. set_automatic_scrolling_active(m_hovered_component == Component::IncrementButton);
  282. }
  283. if (!m_scrubbing)
  284. return;
  285. float delta = orientation() == Orientation::Vertical ? (event.y() - m_scrub_origin.y()) : (event.x() - m_scrub_origin.x());
  286. float scrubbable_range = scrubbable_range_in_pixels();
  287. float value_steps_per_scrubbed_pixel = (m_max - m_min) / scrubbable_range;
  288. float new_value = m_scrub_start_value + (value_steps_per_scrubbed_pixel * delta);
  289. set_value(new_value);
  290. }
  291. void GScrollBar::leave_event(CEvent&)
  292. {
  293. if (m_hovered_component != Component::Invalid) {
  294. m_hovered_component = Component::Invalid;
  295. update();
  296. }
  297. }
  298. void GScrollBar::change_event(GEvent& event)
  299. {
  300. if (event.type() == GEvent::Type::EnabledChange) {
  301. if (!is_enabled())
  302. m_scrubbing = false;
  303. }
  304. return GWidget::change_event(event);
  305. }