mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-12 17:30:38 +00:00
LibGUI: Add a GUI::Tray widget for the FilePicker common locations
The FilePicker has implemented its common locations tray as a composite widget built from a GUI::Frame with a bunch of GUI::Button inside it. The problem with that is that it creates a long and annoying chain of keyboard-focusable widgets. This patch adds GUI::Tray, which is a dedicated single widget that implements the same UI element, but without child widgets.
This commit is contained in:
parent
25475f7003
commit
ae2579d8b5
Notes:
sideshowbarker
2024-07-18 02:04:55 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/ae2579d8b5c
3 changed files with 271 additions and 0 deletions
|
@ -98,6 +98,7 @@ set(SOURCES
|
|||
TextEditor.cpp
|
||||
Toolbar.cpp
|
||||
ToolbarContainer.cpp
|
||||
Tray.cpp
|
||||
TreeView.cpp
|
||||
UndoStack.cpp
|
||||
ValueSlider.cpp
|
||||
|
|
214
Userland/Libraries/LibGUI/Tray.cpp
Normal file
214
Userland/Libraries/LibGUI/Tray.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
56
Userland/Libraries/LibGUI/Tray.h
Normal file
56
Userland/Libraries/LibGUI/Tray.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGUI/Frame.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class Tray : public GUI::Frame {
|
||||
C_OBJECT(Tray);
|
||||
|
||||
public:
|
||||
virtual ~Tray() override;
|
||||
|
||||
size_t add_item(String text, RefPtr<Gfx::Bitmap>, String custom_data);
|
||||
|
||||
void set_item_checked(size_t index, bool);
|
||||
|
||||
Function<void(String const&)> on_item_activation;
|
||||
|
||||
protected:
|
||||
virtual void paint_event(GUI::PaintEvent&) override;
|
||||
virtual void mousemove_event(GUI::MouseEvent&) override;
|
||||
virtual void mousedown_event(GUI::MouseEvent&) override;
|
||||
virtual void mouseup_event(GUI::MouseEvent&) override;
|
||||
virtual void leave_event(Core::Event&) override;
|
||||
virtual void focusin_event(GUI::FocusEvent&) override;
|
||||
virtual void focusout_event(GUI::FocusEvent&) override;
|
||||
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||
|
||||
private:
|
||||
Tray();
|
||||
|
||||
struct Item {
|
||||
String text;
|
||||
RefPtr<Gfx::Bitmap> bitmap;
|
||||
String custom_data;
|
||||
size_t index { 0 };
|
||||
Gfx::IntRect rect(Tray const&) const;
|
||||
};
|
||||
|
||||
Item* item_at(Gfx::IntPoint const&);
|
||||
|
||||
Vector<Item> m_items;
|
||||
|
||||
Optional<size_t> m_pressed_item_index;
|
||||
Optional<size_t> m_hovered_item_index;
|
||||
Optional<size_t> m_checked_item_index;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue