mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:17:35 +00:00
WindowServer+LibGfx: Show menus in windows! :^)
This patch begins the transition away from the global menu towards per-window menus instead. The global menu looks neat, but has always felt clunky, and there are a number of usability problems with it, especially in programs with multiple windows. You can now call GUI::Window::set_menubar() to add a menubar to your window. It will be specific to that one window only.
This commit is contained in:
parent
1daaa4f38d
commit
e76771bfad
21 changed files with 335 additions and 44 deletions
|
@ -79,7 +79,8 @@ static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& re
|
|||
{
|
||||
if (window.is_frameless())
|
||||
return rect;
|
||||
return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette());
|
||||
int menu_row_count = window.menubar() ? 1 : 0;
|
||||
return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette(), menu_row_count);
|
||||
}
|
||||
|
||||
WindowFrame::WindowFrame(Window& window)
|
||||
|
@ -238,6 +239,13 @@ void WindowFrame::did_set_maximized(Badge<Window>, bool maximized)
|
|||
m_maximize_button->set_icon(maximized ? *s_restore_icon : *s_maximize_icon);
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowFrame::menubar_rect() const
|
||||
{
|
||||
if (!m_window.menubar())
|
||||
return {};
|
||||
return Gfx::WindowTheme::current().menu_bar_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette(), menu_row_count());
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowFrame::title_bar_rect() const
|
||||
{
|
||||
return Gfx::WindowTheme::current().title_bar_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette());
|
||||
|
@ -289,11 +297,46 @@ void WindowFrame::paint_tool_window_frame(Gfx::Painter& painter)
|
|||
Gfx::WindowTheme::current().paint_tool_window_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), palette, leftmost_button_rect);
|
||||
}
|
||||
|
||||
void WindowFrame::paint_menubar(Gfx::Painter& painter)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto& font = wm.font();
|
||||
auto palette = wm.palette();
|
||||
auto menubar_rect = this->menubar_rect();
|
||||
|
||||
painter.fill_rect(menubar_rect, palette.window());
|
||||
|
||||
Gfx::PainterStateSaver saver(painter);
|
||||
painter.translate(menubar_rect.location());
|
||||
|
||||
m_window.menubar()->for_each_menu([&](Menu& menu) {
|
||||
auto text_rect = menu.text_rect_in_window_menubar();
|
||||
Color text_color = palette.window_text();
|
||||
if (MenuManager::the().is_open(menu)) {
|
||||
painter.fill_rect(menu.rect_in_window_menubar(), palette.menu_selection());
|
||||
text_color = palette.menu_selection_text();
|
||||
text_rect.move_by(1, 1);
|
||||
}
|
||||
if (&menu == MenuManager::the().hovered_menu() || MenuManager::the().is_open(menu))
|
||||
Gfx::StylePainter::paint_button(painter, menu.rect_in_window_menubar(), palette, Gfx::ButtonStyle::CoolBar, MenuManager::the().is_open(menu), true);
|
||||
painter.draw_text(
|
||||
text_rect,
|
||||
menu.name(),
|
||||
font,
|
||||
Gfx::TextAlignment::CenterLeft,
|
||||
text_color);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void WindowFrame::paint_normal_frame(Gfx::Painter& painter)
|
||||
{
|
||||
auto palette = WindowManager::the().palette();
|
||||
auto leftmost_button_rect = m_buttons.is_empty() ? Gfx::IntRect() : m_buttons.last().relative_rect();
|
||||
Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), m_window.icon(), palette, leftmost_button_rect);
|
||||
Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), m_window.icon(), palette, leftmost_button_rect, menu_row_count());
|
||||
|
||||
if (m_window.menubar())
|
||||
paint_menubar(painter);
|
||||
}
|
||||
|
||||
void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect)
|
||||
|
@ -509,6 +552,13 @@ void WindowFrame::invalidate_title_bar()
|
|||
invalidate(title_bar_rect());
|
||||
}
|
||||
|
||||
void WindowFrame::invalidate()
|
||||
{
|
||||
auto frame_rect = render_rect();
|
||||
invalidate(Gfx::IntRect { frame_rect.location() - m_window.position(), frame_rect.size() });
|
||||
m_window.invalidate(true, true);
|
||||
}
|
||||
|
||||
void WindowFrame::invalidate(Gfx::IntRect relative_rect)
|
||||
{
|
||||
auto frame_rect = rect();
|
||||
|
@ -652,6 +702,13 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
auto menubar_rect = this->menubar_rect();
|
||||
if (menubar_rect.contains(event.position())) {
|
||||
wm.clear_resize_candidate();
|
||||
handle_menubar_mouse_event(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_window.is_resizable() && event.type() == Event::MouseMove && event.buttons() == 0) {
|
||||
constexpr ResizeDirection direction_for_hot_area[3][3] = {
|
||||
{ ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight },
|
||||
|
@ -673,6 +730,50 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
|
|||
wm.start_window_resize(m_window, event.translated(rect().location()));
|
||||
}
|
||||
|
||||
void WindowFrame::handle_menubar_mouse_event(const MouseEvent& event)
|
||||
{
|
||||
Menu* hovered_menu = nullptr;
|
||||
auto menubar_rect = this->menubar_rect();
|
||||
auto adjusted_position = event.position().translated(-menubar_rect.location());
|
||||
m_window.menubar()->for_each_menu([&](Menu& menu) {
|
||||
if (menu.rect_in_window_menubar().contains(adjusted_position)) {
|
||||
hovered_menu = &menu;
|
||||
handle_menu_mouse_event(menu, event);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (!hovered_menu && event.type() == Event::Type::MouseDown)
|
||||
MenuManager::the().close_everyone();
|
||||
if (hovered_menu != MenuManager::the().hovered_menu()) {
|
||||
MenuManager::the().set_hovered_menu(hovered_menu);
|
||||
invalidate(menubar_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::handle_menu_mouse_event(Menu& menu, const MouseEvent& event)
|
||||
{
|
||||
auto menubar_rect = this->menubar_rect();
|
||||
bool is_hover_with_any_menu_open = event.type() == MouseEvent::MouseMove && &m_window == WindowManager::the().window_with_active_menu();
|
||||
bool is_mousedown_with_left_button = event.type() == MouseEvent::MouseDown && event.button() == MouseButton::Left;
|
||||
bool should_open_menu = &menu != MenuManager::the().current_menu() && (is_hover_with_any_menu_open || is_mousedown_with_left_button);
|
||||
bool should_close_menu = &menu == MenuManager::the().current_menu() && is_mousedown_with_left_button;
|
||||
|
||||
if (should_open_menu) {
|
||||
MenuManager::the().close_everyone();
|
||||
menu.ensure_menu_window().move_to(menu.rect_in_window_menubar().bottom_left().translated(rect().location()).translated(menubar_rect.location()));
|
||||
MenuManager::the().open_menu(menu, false);
|
||||
WindowManager::the().set_window_with_active_menu(&m_window);
|
||||
invalidate(menubar_rect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_close_menu) {
|
||||
invalidate(menubar_rect);
|
||||
MenuManager::the().close_everyone();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::start_flash_animation()
|
||||
{
|
||||
if (!m_flash_timer) {
|
||||
|
@ -774,4 +875,9 @@ void WindowFrame::paint_simple_rect_shadow(Gfx::Painter& painter, const Gfx::Int
|
|||
paint_vertical(containing_rect.right() - base_size + 1, 1, 0, horizontal_shift);
|
||||
}
|
||||
|
||||
int WindowFrame::menu_row_count() const
|
||||
{
|
||||
return m_window.menubar() ? 1 : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue