AbstractButton.cpp 8.8 KB

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