Tray.cpp 5.2 KB

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