mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 09:49:15 +03:00
WindowServer: Fix menu over-drawing
We only need to re-draw the item being selected and the item being deselected. We also don't care anymore if applets were added or removed as we no longer have a global menu bar.
This commit is contained in:
parent
8a4971f908
commit
7ae46ae218
Notes:
sideshowbarker
2024-07-18 08:46:44 +09:00
Author: https://github.com/tomuta Commit: https://github.com/SerenityOS/serenity/commit/7ae46ae218b Pull-request: https://github.com/SerenityOS/serenity/pull/8868
@ -94,8 +94,6 @@ void AppletManager::add_applet(Window& applet)
|
||||
});
|
||||
|
||||
relayout();
|
||||
|
||||
MenuManager::the().refresh();
|
||||
}
|
||||
|
||||
void AppletManager::relayout()
|
||||
|
@ -118,6 +118,12 @@ void Menu::redraw()
|
||||
menu_window()->invalidate();
|
||||
}
|
||||
|
||||
void Menu::redraw(MenuItem const& menu_item)
|
||||
{
|
||||
draw(menu_item);
|
||||
menu_window()->invalidate(menu_item.rect());
|
||||
}
|
||||
|
||||
Window& Menu::ensure_menu_window(Gfx::IntPoint const& position)
|
||||
{
|
||||
auto& screen = Screen::closest_to_location(position);
|
||||
@ -151,8 +157,10 @@ Window& Menu::ensure_menu_window(Gfx::IntPoint const& position)
|
||||
// menu's rectangle as we have more or less screen available now
|
||||
auto new_rect = calculate_window_rect();
|
||||
if (new_rect != m_menu_window->rect()) {
|
||||
auto size_changed = new_rect.size() != m_menu_window->rect().size();
|
||||
m_menu_window->set_rect(new_rect);
|
||||
redraw();
|
||||
if (size_changed)
|
||||
draw();
|
||||
}
|
||||
} else {
|
||||
auto window = Window::construct(*this, WindowType::Menu);
|
||||
@ -173,6 +181,11 @@ size_t Menu::visible_item_count() const
|
||||
return m_menu_window->height() / item_height() - 2;
|
||||
}
|
||||
|
||||
Gfx::IntRect Menu::stripe_rect()
|
||||
{
|
||||
return { frame_thickness(), frame_thickness(), s_stripe_width, menu_window()->height() - frame_thickness() * 2 };
|
||||
}
|
||||
|
||||
void Menu::draw()
|
||||
{
|
||||
auto palette = WindowManager::the().palette();
|
||||
@ -185,7 +198,6 @@ void Menu::draw()
|
||||
Gfx::IntRect rect { {}, menu_window()->size() };
|
||||
painter.draw_rect(rect, Color::Black);
|
||||
painter.fill_rect(rect.shrunken(2, 2), palette.menu_base());
|
||||
int width = this->content_width();
|
||||
|
||||
if (!s_checked_bitmap)
|
||||
s_checked_bitmap = &Gfx::CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leak_ref();
|
||||
@ -197,10 +209,9 @@ void Menu::draw()
|
||||
has_items_with_icon = has_items_with_icon | !!item.icon();
|
||||
}
|
||||
|
||||
Gfx::IntRect stripe_rect { frame_thickness(), frame_thickness(), s_stripe_width, menu_window()->height() - frame_thickness() * 2 };
|
||||
painter.fill_rect(stripe_rect, palette.menu_stripe());
|
||||
|
||||
int visible_item_count = this->visible_item_count();
|
||||
// Draw the stripe first, which may extend outside of individual items. We can
|
||||
// skip this step when painting an individual item since we're drawing all of them
|
||||
painter.fill_rect(stripe_rect(), palette.menu_stripe());
|
||||
|
||||
if (is_scrollable()) {
|
||||
bool can_go_up = m_scroll_offset > 0;
|
||||
@ -211,74 +222,91 @@ void Menu::draw()
|
||||
painter.draw_text(down_indicator_rect, "\xE2\xAC\x87", Gfx::TextAlignment::Center, can_go_down ? palette.menu_base_text() : palette.color(ColorRole::DisabledText));
|
||||
}
|
||||
|
||||
for (int i = 0; i < visible_item_count; ++i) {
|
||||
auto& item = m_items.at(m_scroll_offset + i);
|
||||
if (item.type() == MenuItem::Text) {
|
||||
Color text_color = palette.menu_base_text();
|
||||
if (&item == hovered_item() && item.is_enabled()) {
|
||||
painter.fill_rect(item.rect(), palette.menu_selection());
|
||||
painter.draw_rect(item.rect(), palette.menu_selection().darkened());
|
||||
text_color = palette.menu_selection_text();
|
||||
} else if (!item.is_enabled()) {
|
||||
text_color = Color::MidGray;
|
||||
}
|
||||
Gfx::IntRect text_rect = item.rect().translated(stripe_rect.width() + 6, 0);
|
||||
if (item.is_checkable()) {
|
||||
if (item.is_exclusive()) {
|
||||
Gfx::IntRect radio_rect { item.rect().x() + 5, 0, 12, 12 };
|
||||
radio_rect.center_vertically_within(text_rect);
|
||||
Gfx::StylePainter::paint_radio_button(painter, radio_rect, palette, item.is_checked(), false);
|
||||
} else {
|
||||
Gfx::IntRect checkmark_rect { item.rect().x() + 7, 0, s_checked_bitmap_width, s_checked_bitmap_height };
|
||||
checkmark_rect.center_vertically_within(text_rect);
|
||||
Gfx::IntRect checkbox_rect = checkmark_rect.inflated(4, 4);
|
||||
painter.fill_rect(checkbox_rect, palette.base());
|
||||
Gfx::StylePainter::paint_frame(painter, checkbox_rect, palette, Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
|
||||
if (item.is_checked()) {
|
||||
painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, palette.button_text());
|
||||
}
|
||||
}
|
||||
} else if (item.icon()) {
|
||||
Gfx::IntRect icon_rect { item.rect().x() + 3, 0, s_item_icon_width, s_item_icon_width };
|
||||
icon_rect.center_vertically_within(text_rect);
|
||||
int visible_item_count = this->visible_item_count();
|
||||
for (int i = 0; i < visible_item_count; ++i)
|
||||
draw(m_items.at(m_scroll_offset + i), true);
|
||||
}
|
||||
|
||||
if (&item == hovered_item() && item.is_enabled()) {
|
||||
auto shadow_color = palette.menu_selection().darkened(0.7f);
|
||||
painter.blit_filtered(icon_rect.location().translated(1, 1), *item.icon(), item.icon()->rect(), [&shadow_color](auto) {
|
||||
return shadow_color;
|
||||
});
|
||||
icon_rect.translate_by(-1, -1);
|
||||
}
|
||||
if (item.is_enabled())
|
||||
painter.blit(icon_rect.location(), *item.icon(), item.icon()->rect());
|
||||
else
|
||||
painter.blit_disabled(icon_rect.location(), *item.icon(), item.icon()->rect(), palette);
|
||||
}
|
||||
auto& previous_font = painter.font();
|
||||
if (item.is_default())
|
||||
painter.set_font(previous_font.bold_variant());
|
||||
painter.draw_ui_text(text_rect, item.text(), painter.font(), Gfx::TextAlignment::CenterLeft, text_color);
|
||||
if (!item.shortcut_text().is_empty()) {
|
||||
painter.draw_text(item.rect().translated(-right_padding(), 0), item.shortcut_text(), Gfx::TextAlignment::CenterRight, text_color);
|
||||
}
|
||||
painter.set_font(previous_font);
|
||||
if (item.is_submenu()) {
|
||||
static auto& submenu_arrow_bitmap = Gfx::CharacterBitmap::create_from_ascii(s_submenu_arrow_bitmap_data, s_submenu_arrow_bitmap_width, s_submenu_arrow_bitmap_height).leak_ref();
|
||||
Gfx::IntRect submenu_arrow_rect {
|
||||
item.rect().right() - s_submenu_arrow_bitmap_width - 2,
|
||||
0,
|
||||
s_submenu_arrow_bitmap_width,
|
||||
s_submenu_arrow_bitmap_height
|
||||
};
|
||||
submenu_arrow_rect.center_vertically_within(item.rect());
|
||||
painter.draw_bitmap(submenu_arrow_rect.location(), submenu_arrow_bitmap, text_color);
|
||||
}
|
||||
} else if (item.type() == MenuItem::Separator) {
|
||||
Gfx::IntPoint p1(item.rect().translated(stripe_rect.width() + 4, 0).x(), item.rect().center().y() - 1);
|
||||
Gfx::IntPoint p2(width - 7, item.rect().center().y() - 1);
|
||||
painter.draw_line(p1, p2, palette.threed_shadow1());
|
||||
painter.draw_line(p1.translated(0, 1), p2.translated(0, 1), palette.threed_highlight());
|
||||
void Menu::draw(MenuItem const& item, bool is_drawing_all)
|
||||
{
|
||||
auto palette = WindowManager::the().palette();
|
||||
int width = this->content_width();
|
||||
Gfx::Painter painter(*menu_window()->backing_store());
|
||||
painter.add_clip_rect(item.rect());
|
||||
|
||||
auto stripe_rect = this->stripe_rect();
|
||||
if (!is_drawing_all) {
|
||||
// If we're redrawing all of them then we already did this in draw()
|
||||
painter.fill_rect(stripe_rect, palette.menu_stripe());
|
||||
for (auto& rect : item.rect().shatter(stripe_rect))
|
||||
painter.fill_rect(rect, palette.menu_base());
|
||||
}
|
||||
|
||||
if (item.type() == MenuItem::Text) {
|
||||
Color text_color = palette.menu_base_text();
|
||||
if (&item == hovered_item() && item.is_enabled()) {
|
||||
painter.fill_rect(item.rect(), palette.menu_selection());
|
||||
painter.draw_rect(item.rect(), palette.menu_selection().darkened());
|
||||
text_color = palette.menu_selection_text();
|
||||
} else if (!item.is_enabled()) {
|
||||
text_color = Color::MidGray;
|
||||
}
|
||||
Gfx::IntRect text_rect = item.rect().translated(stripe_rect.width() + 6, 0);
|
||||
if (item.is_checkable()) {
|
||||
if (item.is_exclusive()) {
|
||||
Gfx::IntRect radio_rect { item.rect().x() + 5, 0, 12, 12 };
|
||||
radio_rect.center_vertically_within(text_rect);
|
||||
Gfx::StylePainter::paint_radio_button(painter, radio_rect, palette, item.is_checked(), false);
|
||||
} else {
|
||||
Gfx::IntRect checkmark_rect { item.rect().x() + 7, 0, s_checked_bitmap_width, s_checked_bitmap_height };
|
||||
checkmark_rect.center_vertically_within(text_rect);
|
||||
Gfx::IntRect checkbox_rect = checkmark_rect.inflated(4, 4);
|
||||
painter.fill_rect(checkbox_rect, palette.base());
|
||||
Gfx::StylePainter::paint_frame(painter, checkbox_rect, palette, Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
|
||||
if (item.is_checked()) {
|
||||
painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, palette.button_text());
|
||||
}
|
||||
}
|
||||
} else if (item.icon()) {
|
||||
Gfx::IntRect icon_rect { item.rect().x() + 3, 0, s_item_icon_width, s_item_icon_width };
|
||||
icon_rect.center_vertically_within(text_rect);
|
||||
|
||||
if (&item == hovered_item() && item.is_enabled()) {
|
||||
auto shadow_color = palette.menu_selection().darkened(0.7f);
|
||||
painter.blit_filtered(icon_rect.location().translated(1, 1), *item.icon(), item.icon()->rect(), [&shadow_color](auto) {
|
||||
return shadow_color;
|
||||
});
|
||||
icon_rect.translate_by(-1, -1);
|
||||
}
|
||||
if (item.is_enabled())
|
||||
painter.blit(icon_rect.location(), *item.icon(), item.icon()->rect());
|
||||
else
|
||||
painter.blit_disabled(icon_rect.location(), *item.icon(), item.icon()->rect(), palette);
|
||||
}
|
||||
auto& previous_font = painter.font();
|
||||
if (item.is_default())
|
||||
painter.set_font(previous_font.bold_variant());
|
||||
painter.draw_ui_text(text_rect, item.text(), painter.font(), Gfx::TextAlignment::CenterLeft, text_color);
|
||||
if (!item.shortcut_text().is_empty()) {
|
||||
painter.draw_text(item.rect().translated(-right_padding(), 0), item.shortcut_text(), Gfx::TextAlignment::CenterRight, text_color);
|
||||
}
|
||||
painter.set_font(previous_font);
|
||||
if (item.is_submenu()) {
|
||||
static auto& submenu_arrow_bitmap = Gfx::CharacterBitmap::create_from_ascii(s_submenu_arrow_bitmap_data, s_submenu_arrow_bitmap_width, s_submenu_arrow_bitmap_height).leak_ref();
|
||||
Gfx::IntRect submenu_arrow_rect {
|
||||
item.rect().right() - s_submenu_arrow_bitmap_width - 2,
|
||||
0,
|
||||
s_submenu_arrow_bitmap_width,
|
||||
s_submenu_arrow_bitmap_height
|
||||
};
|
||||
submenu_arrow_rect.center_vertically_within(item.rect());
|
||||
painter.draw_bitmap(submenu_arrow_rect.location(), submenu_arrow_bitmap, text_color);
|
||||
}
|
||||
} else if (item.type() == MenuItem::Separator) {
|
||||
Gfx::IntPoint p1(item.rect().translated(stripe_rect.width() + 4, 0).x(), item.rect().center().y() - 1);
|
||||
Gfx::IntPoint p2(width - 7, item.rect().center().y() - 1);
|
||||
painter.draw_line(p1, p2, palette.threed_shadow1());
|
||||
painter.draw_line(p1.translated(0, 1), p2.translated(0, 1), palette.threed_highlight());
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +330,6 @@ void Menu::update_for_new_hovered_item(bool make_input)
|
||||
set_visible(true);
|
||||
}
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
void Menu::open_hovered_item(bool leave_menu_open)
|
||||
@ -364,8 +391,11 @@ void Menu::event(Core::Event& event)
|
||||
if (event.type() == Event::MouseWheel && is_scrollable()) {
|
||||
VERIFY(menu_window());
|
||||
auto& mouse_event = static_cast<const MouseEvent&>(event);
|
||||
auto previous_scroll_offset = m_scroll_offset;
|
||||
m_scroll_offset += mouse_event.wheel_delta();
|
||||
m_scroll_offset = clamp(m_scroll_offset, 0, m_max_scroll_offset);
|
||||
if (m_scroll_offset != previous_scroll_offset)
|
||||
redraw();
|
||||
|
||||
int index = item_index_at(mouse_event.position());
|
||||
set_hovered_index(index);
|
||||
@ -657,7 +687,8 @@ void Menu::set_hovered_index(int index, bool make_input)
|
||||
{
|
||||
if (m_hovered_item_index == index)
|
||||
return;
|
||||
if (auto* old_hovered_item = hovered_item()) {
|
||||
auto* old_hovered_item = hovered_item();
|
||||
if (old_hovered_item) {
|
||||
if (client() && old_hovered_item->type() != MenuItem::Type::Separator)
|
||||
client()->async_menu_item_left(m_menu_id, old_hovered_item->identifier());
|
||||
}
|
||||
@ -666,7 +697,10 @@ void Menu::set_hovered_index(int index, bool make_input)
|
||||
if (auto* new_hovered_item = hovered_item()) {
|
||||
if (client() && new_hovered_item->type() != MenuItem::Type::Separator)
|
||||
client()->async_menu_item_entered(m_menu_id, new_hovered_item->identifier());
|
||||
redraw(*new_hovered_item);
|
||||
}
|
||||
if (old_hovered_item)
|
||||
redraw(*old_hovered_item);
|
||||
}
|
||||
|
||||
bool Menu::is_open() const
|
||||
|
@ -93,10 +93,12 @@ public:
|
||||
static constexpr int right_padding() { return 14; }
|
||||
|
||||
void draw();
|
||||
void draw(MenuItem const&, bool = false);
|
||||
const Gfx::Font& font() const;
|
||||
|
||||
MenuItem* item_with_identifier(unsigned);
|
||||
void redraw();
|
||||
void redraw(MenuItem const&);
|
||||
|
||||
MenuItem* hovered_item() const;
|
||||
|
||||
@ -130,6 +132,7 @@ private:
|
||||
|
||||
void handle_mouse_move_event(const MouseEvent&);
|
||||
size_t visible_item_count() const;
|
||||
Gfx::IntRect stripe_rect();
|
||||
|
||||
int item_index_at(const Gfx::IntPoint&);
|
||||
static constexpr int padding_between_text_and_shortcut() { return 50; }
|
||||
|
@ -43,6 +43,7 @@ void MenuManager::refresh()
|
||||
{
|
||||
ClientConnection::for_each_client([&](ClientConnection& client) {
|
||||
client.for_each_menu([&](Menu& menu) {
|
||||
dbgln("MenuManager::refresh {}", &menu);
|
||||
menu.redraw();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
@ -220,7 +221,6 @@ void MenuManager::close_everyone()
|
||||
}
|
||||
m_open_menu_stack.clear();
|
||||
clear_current_menu();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void MenuManager::close_everyone_not_in_lineage(Menu& menu)
|
||||
@ -247,7 +247,6 @@ void MenuManager::close_menus(const Vector<Menu&>& menus)
|
||||
return entry == &menu;
|
||||
});
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
static void collect_menu_subtree(Menu& menu, Vector<Menu&>& menus)
|
||||
@ -313,8 +312,6 @@ void MenuManager::open_menu(Menu& menu, bool as_current_menu)
|
||||
// other menu is current
|
||||
set_current_menu(&menu);
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void MenuManager::clear_current_menu()
|
||||
|
@ -23,8 +23,6 @@ public:
|
||||
MenuManager();
|
||||
virtual ~MenuManager() override;
|
||||
|
||||
void refresh();
|
||||
|
||||
bool is_open(const Menu&) const;
|
||||
bool has_open_menu() const { return !m_open_menu_stack.is_empty(); }
|
||||
|
||||
@ -55,6 +53,8 @@ private:
|
||||
virtual void event(Core::Event&) override;
|
||||
void handle_mouse_event(MouseEvent&);
|
||||
|
||||
void refresh();
|
||||
|
||||
WeakPtr<Menu> m_current_menu;
|
||||
WeakPtr<Window> m_previous_input_window;
|
||||
Vector<WeakPtr<Menu>> m_open_menu_stack;
|
||||
|
@ -605,7 +605,6 @@ void WindowManager::notify_rect_changed(Window& window, Gfx::IntRect const& old_
|
||||
if (window.type() == WindowType::Applet)
|
||||
AppletManager::the().relayout();
|
||||
|
||||
MenuManager::the().refresh();
|
||||
reevaluate_hovered_window(&window);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user