AbstractButton.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /*
  2. * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/JsonObject.h>
  8. #include <LibCore/Timer.h>
  9. #include <LibGUI/AbstractButton.h>
  10. #include <LibGUI/Painter.h>
  11. #include <LibGUI/Window.h>
  12. #include <LibGfx/Palette.h>
  13. namespace GUI {
  14. AbstractButton::AbstractButton(String text)
  15. {
  16. set_text(move(text));
  17. set_focus_policy(GUI::FocusPolicy::StrongFocus);
  18. set_background_role(Gfx::ColorRole::Button);
  19. set_foreground_role(Gfx::ColorRole::ButtonText);
  20. m_auto_repeat_timer = add<Core::Timer>();
  21. m_auto_repeat_timer->on_timeout = [this] {
  22. click();
  23. };
  24. REGISTER_STRING_PROPERTY("text", text, set_text);
  25. REGISTER_BOOL_PROPERTY("checked", is_checked, set_checked);
  26. REGISTER_BOOL_PROPERTY("checkable", is_checkable, set_checkable);
  27. REGISTER_BOOL_PROPERTY("exclusive", is_exclusive, set_exclusive);
  28. }
  29. void AbstractButton::set_text(String text)
  30. {
  31. if (m_text == text)
  32. return;
  33. m_text = move(text);
  34. update();
  35. }
  36. void AbstractButton::set_checked(bool checked, AllowCallback allow_callback)
  37. {
  38. if (m_checked == checked)
  39. return;
  40. m_checked = checked;
  41. if (is_exclusive() && checked && parent_widget()) {
  42. bool sibling_had_focus = false;
  43. parent_widget()->for_each_child_of_type<AbstractButton>([&](auto& sibling) {
  44. if (!sibling.is_exclusive())
  45. return IterationDecision::Continue;
  46. if (window() && window()->focused_widget() == &sibling)
  47. sibling_had_focus = true;
  48. if (!sibling.is_checked())
  49. return IterationDecision::Continue;
  50. sibling.m_checked = false;
  51. sibling.update();
  52. if (sibling.on_checked)
  53. sibling.on_checked(false);
  54. return IterationDecision::Continue;
  55. });
  56. m_checked = true;
  57. if (sibling_had_focus)
  58. set_focus(true);
  59. }
  60. if (is_exclusive() && parent_widget()) {
  61. // In a group of exclusive checkable buttons, only the currently checked button is focusable.
  62. parent_widget()->for_each_child_of_type<GUI::AbstractButton>([&](auto& button) {
  63. if (button.is_exclusive() && button.is_checkable())
  64. button.set_focus_policy(button.is_checked() ? GUI::FocusPolicy::StrongFocus : GUI::FocusPolicy::NoFocus);
  65. return IterationDecision::Continue;
  66. });
  67. }
  68. update();
  69. if (on_checked && allow_callback == AllowCallback::Yes)
  70. on_checked(checked);
  71. }
  72. void AbstractButton::set_checkable(bool checkable)
  73. {
  74. if (m_checkable == checkable)
  75. return;
  76. m_checkable = checkable;
  77. update();
  78. }
  79. void AbstractButton::mousemove_event(MouseEvent& event)
  80. {
  81. bool is_over = rect().contains(event.position());
  82. m_hovered = is_over;
  83. if (event.buttons() & MouseButton::Primary) {
  84. bool being_pressed = is_over;
  85. if (being_pressed != m_being_pressed) {
  86. m_being_pressed = being_pressed;
  87. if (m_auto_repeat_interval) {
  88. if (!m_being_pressed)
  89. m_auto_repeat_timer->stop();
  90. else
  91. m_auto_repeat_timer->start(m_auto_repeat_interval);
  92. }
  93. update();
  94. }
  95. }
  96. Widget::mousemove_event(event);
  97. }
  98. void AbstractButton::mousedown_event(MouseEvent& event)
  99. {
  100. if (event.button() == MouseButton::Primary) {
  101. m_being_pressed = true;
  102. repaint();
  103. if (m_auto_repeat_interval) {
  104. click();
  105. m_auto_repeat_timer->start(m_auto_repeat_interval);
  106. }
  107. event.accept();
  108. }
  109. Widget::mousedown_event(event);
  110. }
  111. void AbstractButton::mouseup_event(MouseEvent& event)
  112. {
  113. if (event.button() == MouseButton::Primary && m_being_pressed) {
  114. bool was_auto_repeating = m_auto_repeat_timer->is_active();
  115. m_auto_repeat_timer->stop();
  116. bool was_being_pressed = m_being_pressed;
  117. m_being_pressed = false;
  118. repaint();
  119. if (was_being_pressed && !was_auto_repeating)
  120. click(event.modifiers());
  121. }
  122. Widget::mouseup_event(event);
  123. }
  124. void AbstractButton::enter_event(Core::Event&)
  125. {
  126. m_hovered = true;
  127. update();
  128. }
  129. void AbstractButton::leave_event(Core::Event& event)
  130. {
  131. m_hovered = false;
  132. if (m_being_keyboard_pressed)
  133. m_being_keyboard_pressed = m_being_pressed = false;
  134. update();
  135. event.accept();
  136. Widget::leave_event(event);
  137. }
  138. void AbstractButton::focusout_event(GUI::FocusEvent& event)
  139. {
  140. if (m_being_keyboard_pressed) {
  141. m_being_pressed = m_being_keyboard_pressed = false;
  142. event.accept();
  143. update();
  144. }
  145. Widget::focusout_event(event);
  146. }
  147. void AbstractButton::keydown_event(KeyEvent& event)
  148. {
  149. if (event.key() == KeyCode::Key_Return || event.key() == KeyCode::Key_Space) {
  150. m_being_pressed = m_being_keyboard_pressed = true;
  151. update();
  152. event.accept();
  153. return;
  154. } else if (m_being_pressed && event.key() == KeyCode::Key_Escape) {
  155. m_being_pressed = m_being_keyboard_pressed = false;
  156. update();
  157. event.accept();
  158. return;
  159. }
  160. // Arrow keys switch the currently checked option within an exclusive group of checkable buttons.
  161. if (event.is_arrow_key() && !event.modifiers() && is_exclusive() && is_checkable() && parent_widget()) {
  162. event.accept();
  163. Vector<GUI::AbstractButton&> exclusive_siblings;
  164. size_t this_index = 0;
  165. parent_widget()->for_each_child_of_type<GUI::AbstractButton>([&](auto& sibling) {
  166. if (&sibling == this) {
  167. VERIFY(is_enabled());
  168. this_index = exclusive_siblings.size();
  169. }
  170. if (sibling.is_exclusive() && sibling.is_checkable() && sibling.is_enabled())
  171. exclusive_siblings.append(sibling);
  172. return IterationDecision::Continue;
  173. });
  174. if (exclusive_siblings.size() <= 1)
  175. return;
  176. size_t new_checked_index;
  177. if (event.key() == KeyCode::Key_Left || event.key() == KeyCode::Key_Up)
  178. new_checked_index = this_index == 0 ? exclusive_siblings.size() - 1 : this_index - 1;
  179. else
  180. new_checked_index = this_index == exclusive_siblings.size() - 1 ? 0 : this_index + 1;
  181. exclusive_siblings[new_checked_index].set_checked(true);
  182. return;
  183. }
  184. Widget::keydown_event(event);
  185. }
  186. void AbstractButton::keyup_event(KeyEvent& event)
  187. {
  188. bool was_being_pressed = m_being_pressed;
  189. if (was_being_pressed && (event.key() == KeyCode::Key_Return || event.key() == KeyCode::Key_Space)) {
  190. m_being_pressed = m_being_keyboard_pressed = false;
  191. click(event.modifiers());
  192. update();
  193. event.accept();
  194. return;
  195. }
  196. Widget::keyup_event(event);
  197. }
  198. void AbstractButton::paint_text(Painter& painter, Gfx::IntRect const& rect, Gfx::Font const& font, Gfx::TextAlignment text_alignment, Gfx::TextWrapping text_wrapping)
  199. {
  200. auto clipped_rect = rect.intersected(this->rect());
  201. if (!is_enabled()) {
  202. painter.draw_text(clipped_rect.translated(1, 1), text(), font, text_alignment, palette().disabled_text_back(), Gfx::TextElision::Right, text_wrapping);
  203. painter.draw_text(clipped_rect, text(), font, text_alignment, palette().disabled_text_front(), Gfx::TextElision::Right, text_wrapping);
  204. return;
  205. }
  206. if (text().is_empty())
  207. return;
  208. painter.draw_text(clipped_rect, text(), font, text_alignment, palette().color(foreground_role()), Gfx::TextElision::Right, text_wrapping);
  209. }
  210. void AbstractButton::change_event(Event& event)
  211. {
  212. if (event.type() == Event::Type::EnabledChange) {
  213. if (!is_enabled()) {
  214. bool was_being_pressed = m_being_pressed;
  215. m_being_pressed = false;
  216. if (was_being_pressed)
  217. update();
  218. }
  219. }
  220. Widget::change_event(event);
  221. }
  222. }