|
@@ -0,0 +1,214 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
|
|
|
+ *
|
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <LibGUI/Painter.h>
|
|
|
|
+#include <LibGUI/Tray.h>
|
|
|
|
+#include <LibGfx/Font.h>
|
|
|
|
+#include <LibGfx/Palette.h>
|
|
|
|
+#include <LibGfx/StylePainter.h>
|
|
|
|
+
|
|
|
|
+REGISTER_WIDGET(GUI, Tray);
|
|
|
|
+
|
|
|
|
+namespace GUI {
|
|
|
|
+
|
|
|
|
+Tray::Tray()
|
|
|
|
+{
|
|
|
|
+ set_fill_with_background_color(true);
|
|
|
|
+ set_background_role(Gfx::ColorRole::Tray);
|
|
|
|
+ set_focus_policy(GUI::FocusPolicy::TabFocus);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Tray::~Tray()
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Gfx::IntRect Tray::Item::rect(Tray const& tray) const
|
|
|
|
+{
|
|
|
|
+ static constexpr int item_height = 22;
|
|
|
|
+ return Gfx::IntRect {
|
|
|
|
+ tray.frame_thickness(),
|
|
|
|
+ tray.frame_thickness() + static_cast<int>(index) * item_height,
|
|
|
|
+ tray.frame_inner_rect().width(),
|
|
|
|
+ item_height,
|
|
|
|
+ };
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+size_t Tray::add_item(String text, RefPtr<Gfx::Bitmap> bitmap, String custom_data)
|
|
|
|
+{
|
|
|
|
+ auto new_index = m_items.size();
|
|
|
|
+
|
|
|
|
+ m_items.append(Item {
|
|
|
|
+ .text = move(text),
|
|
|
|
+ .bitmap = move(bitmap),
|
|
|
|
+ .custom_data = move(custom_data),
|
|
|
|
+ .index = new_index,
|
|
|
|
+ });
|
|
|
|
+ update();
|
|
|
|
+
|
|
|
|
+ return new_index;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::set_item_checked(size_t index, bool checked)
|
|
|
|
+{
|
|
|
|
+ if (checked) {
|
|
|
|
+ m_checked_item_index = index;
|
|
|
|
+ } else {
|
|
|
|
+ if (m_checked_item_index == index)
|
|
|
|
+ m_checked_item_index = {};
|
|
|
|
+ }
|
|
|
|
+ update();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::paint_event(GUI::PaintEvent& event)
|
|
|
|
+{
|
|
|
|
+ GUI::Frame::paint_event(event);
|
|
|
|
+
|
|
|
|
+ GUI::Painter painter(*this);
|
|
|
|
+ painter.add_clip_rect(event.rect());
|
|
|
|
+
|
|
|
|
+ for (auto& item : m_items) {
|
|
|
|
+ auto rect = item.rect(*this);
|
|
|
|
+ bool is_pressed = item.index == m_pressed_item_index;
|
|
|
|
+ bool is_hovered = item.index == m_hovered_item_index;
|
|
|
|
+ bool is_checked = item.index == m_checked_item_index;
|
|
|
|
+ Gfx::StylePainter::paint_button(painter, rect, palette(), Gfx::ButtonStyle::Tray, is_pressed && is_hovered, is_hovered, is_checked, is_enabled());
|
|
|
|
+
|
|
|
|
+ Gfx::IntRect icon_rect {
|
|
|
|
+ rect.x() + 4,
|
|
|
|
+ 0,
|
|
|
|
+ 16,
|
|
|
|
+ 16,
|
|
|
|
+ };
|
|
|
|
+ icon_rect.center_vertically_within(rect);
|
|
|
|
+
|
|
|
|
+ Gfx::IntRect text_rect {
|
|
|
|
+ icon_rect.right() + 5,
|
|
|
|
+ rect.y(),
|
|
|
|
+ rect.width(),
|
|
|
|
+ rect.height(),
|
|
|
|
+ };
|
|
|
|
+ text_rect.intersect(rect);
|
|
|
|
+
|
|
|
|
+ if (is_pressed && is_hovered) {
|
|
|
|
+ icon_rect.translate_by(1, 1);
|
|
|
|
+ text_rect.translate_by(1, 1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (item.bitmap)
|
|
|
|
+ painter.blit(icon_rect.location(), *item.bitmap, item.bitmap->rect());
|
|
|
|
+
|
|
|
|
+ auto const& font = is_checked ? this->font().bold_variant() : this->font();
|
|
|
|
+ painter.draw_text(text_rect, item.text, font, Gfx::TextAlignment::CenterLeft, palette().color(Gfx::ColorRole::TrayText));
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::mousemove_event(GUI::MouseEvent& event)
|
|
|
|
+{
|
|
|
|
+ auto* hovered_item = item_at(event.position());
|
|
|
|
+ if (!hovered_item) {
|
|
|
|
+ if (m_hovered_item_index.has_value())
|
|
|
|
+ update();
|
|
|
|
+ m_hovered_item_index = {};
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (m_hovered_item_index != hovered_item->index) {
|
|
|
|
+ m_hovered_item_index = hovered_item->index;
|
|
|
|
+ update();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::mousedown_event(GUI::MouseEvent& event)
|
|
|
|
+{
|
|
|
|
+ if (event.button() != GUI::MouseButton::Left)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ auto* pressed_item = item_at(event.position());
|
|
|
|
+ if (!pressed_item)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (m_pressed_item_index != pressed_item->index) {
|
|
|
|
+ m_pressed_item_index = pressed_item->index;
|
|
|
|
+ update();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::mouseup_event(GUI::MouseEvent& event)
|
|
|
|
+{
|
|
|
|
+ if (event.button() != GUI::MouseButton::Left)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (auto* pressed_item = item_at(event.position()); pressed_item && m_pressed_item_index == pressed_item->index) {
|
|
|
|
+ on_item_activation(pressed_item->custom_data);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ m_pressed_item_index = {};
|
|
|
|
+ update();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::leave_event(Core::Event&)
|
|
|
|
+{
|
|
|
|
+ m_hovered_item_index = {};
|
|
|
|
+ update();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Tray::Item* Tray::item_at(Gfx::IntPoint const& position)
|
|
|
|
+{
|
|
|
|
+ for (auto& item : m_items) {
|
|
|
|
+ if (item.rect(*this).contains(position))
|
|
|
|
+ return &item;
|
|
|
|
+ }
|
|
|
|
+ return nullptr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::focusin_event(GUI::FocusEvent&)
|
|
|
|
+{
|
|
|
|
+ if (m_items.is_empty())
|
|
|
|
+ return;
|
|
|
|
+ m_hovered_item_index = 0;
|
|
|
|
+ update();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::focusout_event(GUI::FocusEvent&)
|
|
|
|
+{
|
|
|
|
+ if (m_items.is_empty())
|
|
|
|
+ return;
|
|
|
|
+ m_hovered_item_index = {};
|
|
|
|
+ update();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Tray::keydown_event(GUI::KeyEvent& event)
|
|
|
|
+{
|
|
|
|
+ if (m_items.is_empty() || event.modifiers())
|
|
|
|
+ return Frame::keydown_event(event);
|
|
|
|
+
|
|
|
|
+ if (event.key() == KeyCode::Key_Down) {
|
|
|
|
+ if (!m_hovered_item_index.has_value())
|
|
|
|
+ m_hovered_item_index = 0;
|
|
|
|
+ else
|
|
|
|
+ m_hovered_item_index = (*m_hovered_item_index + 1) % m_items.size();
|
|
|
|
+ update();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (event.key() == KeyCode::Key_Up) {
|
|
|
|
+ if (!m_hovered_item_index.has_value() || m_hovered_item_index == 0u)
|
|
|
|
+ m_hovered_item_index = m_items.size() - 1;
|
|
|
|
+ else
|
|
|
|
+ m_hovered_item_index = *m_hovered_item_index - 1;
|
|
|
|
+ update();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (event.key() == KeyCode::Key_Return) {
|
|
|
|
+ if (m_hovered_item_index.has_value())
|
|
|
|
+ on_item_activation(m_items[*m_hovered_item_index].custom_data);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Frame::keydown_event(event);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+}
|