Tray.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGUI/Painter.h>
  8. #include <LibGUI/Tray.h>
  9. #include <LibGfx/Font/Font.h>
  10. #include <LibGfx/Palette.h>
  11. #include <LibGfx/StylePainter.h>
  12. REGISTER_WIDGET(GUI, Tray);
  13. namespace GUI {
  14. Tray::Tray()
  15. {
  16. set_fill_with_background_color(true);
  17. set_background_role(Gfx::ColorRole::Tray);
  18. set_focus_policy(GUI::FocusPolicy::TabFocus);
  19. }
  20. Gfx::IntRect Tray::Item::rect(Tray const& tray) const
  21. {
  22. int item_height = tray.font().pixel_size_rounded_up() + 12;
  23. return Gfx::IntRect {
  24. tray.frame_thickness(),
  25. tray.frame_thickness() + static_cast<int>(index) * item_height,
  26. tray.frame_inner_rect().width(),
  27. item_height,
  28. };
  29. }
  30. size_t Tray::add_item(DeprecatedString text, RefPtr<Gfx::Bitmap const> bitmap, DeprecatedString custom_data)
  31. {
  32. auto new_index = m_items.size();
  33. m_items.append(Item {
  34. .text = move(text),
  35. .bitmap = move(bitmap),
  36. .custom_data = move(custom_data),
  37. .index = new_index,
  38. });
  39. update();
  40. return new_index;
  41. }
  42. void Tray::set_item_checked(size_t index, bool checked)
  43. {
  44. if (checked) {
  45. m_checked_item_index = index;
  46. } else {
  47. if (m_checked_item_index == index)
  48. m_checked_item_index = {};
  49. }
  50. update();
  51. }
  52. void Tray::paint_event(GUI::PaintEvent& event)
  53. {
  54. GUI::Frame::paint_event(event);
  55. GUI::Painter painter(*this);
  56. painter.add_clip_rect(event.rect());
  57. for (auto& item : m_items) {
  58. auto rect = item.rect(*this);
  59. bool is_pressed = item.index == m_pressed_item_index;
  60. bool is_hovered = item.index == m_hovered_item_index;
  61. bool is_checked = item.index == m_checked_item_index;
  62. Gfx::StylePainter::paint_button(painter, rect, palette(), Gfx::ButtonStyle::Tray, is_pressed && is_hovered, is_hovered, is_checked, is_enabled());
  63. Gfx::IntRect icon_rect {
  64. rect.x() + 4,
  65. 0,
  66. 16,
  67. 16,
  68. };
  69. icon_rect.center_vertically_within(rect);
  70. Gfx::IntRect text_rect {
  71. icon_rect.right() + 4,
  72. rect.y(),
  73. rect.width(),
  74. rect.height(),
  75. };
  76. text_rect.intersect(rect);
  77. if (is_pressed && is_hovered) {
  78. icon_rect.translate_by(1, 1);
  79. text_rect.translate_by(1, 1);
  80. }
  81. if (item.bitmap) {
  82. if (is_hovered)
  83. painter.blit_brightened(icon_rect.location(), *item.bitmap, item.bitmap->rect());
  84. else
  85. painter.blit(icon_rect.location(), *item.bitmap, item.bitmap->rect());
  86. }
  87. auto const& font = is_checked ? this->font().bold_variant() : this->font();
  88. painter.draw_text(text_rect, item.text, font, Gfx::TextAlignment::CenterLeft, palette().tray_text());
  89. }
  90. }
  91. void Tray::mousemove_event(GUI::MouseEvent& event)
  92. {
  93. auto* hovered_item = item_at(event.position());
  94. if (!hovered_item) {
  95. if (m_hovered_item_index.has_value())
  96. update();
  97. m_hovered_item_index = {};
  98. return;
  99. }
  100. if (m_hovered_item_index != hovered_item->index) {
  101. m_hovered_item_index = hovered_item->index;
  102. update();
  103. }
  104. }
  105. void Tray::mousedown_event(GUI::MouseEvent& event)
  106. {
  107. if (event.button() != GUI::MouseButton::Primary)
  108. return;
  109. auto* pressed_item = item_at(event.position());
  110. if (!pressed_item)
  111. return;
  112. if (m_pressed_item_index != pressed_item->index) {
  113. m_pressed_item_index = pressed_item->index;
  114. update();
  115. }
  116. }
  117. void Tray::mouseup_event(GUI::MouseEvent& event)
  118. {
  119. if (event.button() != GUI::MouseButton::Primary)
  120. return;
  121. if (auto* pressed_item = item_at(event.position()); pressed_item && m_pressed_item_index == pressed_item->index) {
  122. on_item_activation(pressed_item->custom_data);
  123. }
  124. m_pressed_item_index = {};
  125. update();
  126. }
  127. void Tray::leave_event(Core::Event&)
  128. {
  129. m_hovered_item_index = {};
  130. update();
  131. }
  132. Tray::Item* Tray::item_at(Gfx::IntPoint position)
  133. {
  134. for (auto& item : m_items) {
  135. if (item.rect(*this).contains(position))
  136. return &item;
  137. }
  138. return nullptr;
  139. }
  140. void Tray::focusin_event(GUI::FocusEvent&)
  141. {
  142. if (m_items.is_empty())
  143. return;
  144. m_hovered_item_index = 0;
  145. update();
  146. }
  147. void Tray::focusout_event(GUI::FocusEvent&)
  148. {
  149. if (m_items.is_empty())
  150. return;
  151. m_hovered_item_index = {};
  152. update();
  153. }
  154. void Tray::keydown_event(GUI::KeyEvent& event)
  155. {
  156. if (m_items.is_empty() || event.modifiers())
  157. return Frame::keydown_event(event);
  158. if (event.key() == KeyCode::Key_Down) {
  159. if (!m_hovered_item_index.has_value())
  160. m_hovered_item_index = 0;
  161. else
  162. m_hovered_item_index = (*m_hovered_item_index + 1) % m_items.size();
  163. update();
  164. return;
  165. }
  166. if (event.key() == KeyCode::Key_Up) {
  167. if (!m_hovered_item_index.has_value() || m_hovered_item_index == 0u)
  168. m_hovered_item_index = m_items.size() - 1;
  169. else
  170. m_hovered_item_index = *m_hovered_item_index - 1;
  171. update();
  172. return;
  173. }
  174. if (event.key() == KeyCode::Key_Return) {
  175. if (m_hovered_item_index.has_value())
  176. on_item_activation(m_items[*m_hovered_item_index].custom_data);
  177. return;
  178. }
  179. Frame::keydown_event(event);
  180. }
  181. }