GScrollBar.cpp 11 KB

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