1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 22:17:44 +00:00

WindowServer+LibGUI+LibGfx: Add WindowType::ToolWindow

Tool windows are secondary windows with a smaller title bar. The sit on
the layer above normal windows, and cannot be minimized.

These are intended for complex yet non-modal interactions with the
content of a primary window, such as find/replace windows, property
windows, etc.
This commit is contained in:
Andreas Kling 2021-02-16 16:10:39 +01:00
parent 15c1f7a40d
commit 11c8596ad3
12 changed files with 105 additions and 37 deletions

View file

@ -492,7 +492,7 @@ OwnPtr<Messages::WindowServer::CreateWindowResponse> ClientConnection::handle(co
window->set_title(message.title());
if (!message.fullscreen()) {
auto rect = message.rect();
if (message.auto_position() && window->type() == WindowType::Normal) {
if (message.auto_position() && window->is_movable()) {
rect = { WindowManager::the().get_recommended_window_position({ 100, 100 }), message.rect().size() };
window->set_default_positioned(true);
}

View file

@ -97,7 +97,7 @@ public:
bool is_minimized() const { return m_minimized; }
void set_minimized(bool);
bool is_minimizable() const { return m_minimizable; }
bool is_minimizable() const { return m_type == WindowType::Normal && m_minimizable; }
void set_minimizable(bool);
bool is_resizable() const { return m_resizable && !m_fullscreen; }
@ -120,7 +120,7 @@ public:
bool is_movable() const
{
return m_type == WindowType::Normal;
return m_type == WindowType::Normal || m_type == WindowType::ToolWindow;
}
WindowFrame& frame() { return m_frame; }

View file

@ -45,6 +45,8 @@ static Gfx::WindowTheme::WindowType to_theme_window_type(WindowType type)
switch (type) {
case WindowType::Normal:
return Gfx::WindowTheme::WindowType::Normal;
case WindowType::ToolWindow:
return Gfx::WindowTheme::WindowType::ToolWindow;
case WindowType::Notification:
return Gfx::WindowTheme::WindowType::Notification;
default:
@ -271,22 +273,25 @@ void WindowFrame::paint_notification_frame(Gfx::Painter& painter)
Gfx::WindowTheme::current().paint_notification_frame(painter, m_window.rect(), palette, m_buttons.last().relative_rect());
}
String WindowFrame::compute_title_text() const
{
if (m_window.client() && m_window.client()->is_unresponsive())
return String::formatted("{} (Not responding)", m_window.title());
return m_window.title();
}
void WindowFrame::paint_tool_window_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_tool_window_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), palette, leftmost_button_rect);
}
void WindowFrame::paint_normal_frame(Gfx::Painter& painter)
{
auto palette = WindowManager::the().palette();
auto& window = m_window;
String title_text;
if (window.client() && window.client()->is_unresponsive()) {
StringBuilder builder;
builder.append(window.title());
builder.append(" (Not responding)");
title_text = builder.to_string();
} else {
title_text = window.title();
}
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(), 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);
}
void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect)
@ -341,6 +346,8 @@ void WindowFrame::render(Gfx::Painter& painter)
paint_notification_frame(painter);
else if (m_window.type() == WindowType::Normal)
paint_normal_frame(painter);
else if (m_window.type() == WindowType::ToolWindow)
paint_tool_window_frame(painter);
else
return;
@ -574,10 +581,10 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
ASSERT(!m_window.is_fullscreen());
auto& wm = WindowManager::the();
if (m_window.type() != WindowType::Normal && m_window.type() != WindowType::Notification)
if (m_window.type() != WindowType::Normal && m_window.type() != WindowType::ToolWindow && m_window.type() != WindowType::Notification)
return;
if (m_window.type() == WindowType::Normal) {
if (m_window.type() == WindowType::Normal || m_window.type() == WindowType::ToolWindow) {
if (event.type() == Event::MouseDown)
wm.move_to_front_and_make_active(m_window);
@ -632,7 +639,7 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
return button.on_mouse_event(event.translated(-button.relative_rect().location()));
}
if (event.type() == Event::MouseDown) {
if (m_window.type() == WindowType::Normal && event.button() == MouseButton::Right) {
if ((m_window.type() == WindowType::Normal || m_window.type() == WindowType::ToolWindow) && event.button() == MouseButton::Right) {
auto default_action = m_window.is_maximized() ? WindowMenuDefaultAction::Restore : WindowMenuDefaultAction::Maximize;
m_window.popup_window_menu(event.position().translated(rect().location()), default_action);
return;

View file

@ -96,12 +96,14 @@ private:
void paint_simple_rect_shadow(Gfx::Painter&, const Gfx::IntRect&, const Gfx::Bitmap&) const;
void paint_notification_frame(Gfx::Painter&);
void paint_normal_frame(Gfx::Painter&);
void paint_tool_window_frame(Gfx::Painter&);
Gfx::Bitmap* window_shadow() const;
bool frame_has_alpha() const;
Gfx::IntRect inflated_for_shadow(const Gfx::IntRect&) const;
Gfx::Bitmap* inflate_for_shadow(Gfx::IntRect&, Gfx::IntPoint&) const;
Gfx::WindowTheme::WindowState window_state_for_theme() const;
String compute_title_text() const;
Window& m_window;
NonnullOwnPtrVector<Button> m_buttons;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -340,9 +340,14 @@ void WindowManager::tell_wm_listeners_window_rect_changed(Window& window)
});
}
static bool window_type_has_title(WindowType type)
{
return type == WindowType::Normal || type == WindowType::ToolWindow;
}
void WindowManager::notify_title_changed(Window& window)
{
if (window.type() != WindowType::Normal)
if (!window_type_has_title(window.type()))
return;
dbgln_if(WINDOWMANAGER_DEBUG, "[WM] Window({}) title set to '{}'", &window, window.title());
@ -412,7 +417,9 @@ bool WindowManager::pick_new_active_window(Window* previous_active)
{
bool new_window_picked = false;
Window* first_candidate = nullptr;
for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, [&](Window& candidate) {
for_each_visible_window_from_front_to_back([&](Window& candidate) {
if (candidate.type() != WindowType::Normal && candidate.type() == WindowType::ToolWindow)
return IterationDecision::Continue;
if (candidate.is_destroyed())
return IterationDecision::Continue;
if (previous_active != first_candidate)
@ -984,7 +991,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
if (auto* blocking_modal_window = window.blocking_modal_window())
blocking_modal_window->frame().start_flash_animation();
if (window.type() == WindowType::Normal)
if (window.type() == WindowType::Normal || window.type() == WindowType::ToolWindow)
move_to_front_and_make_active(window);
else if (window.type() == WindowType::Desktop)
set_active_window(&window);
@ -1068,6 +1075,7 @@ Gfx::IntRect WindowManager::arena_rect_for_type(WindowType type) const
switch (type) {
case WindowType::Desktop:
case WindowType::Normal:
case WindowType::ToolWindow:
return desktop_rect();
case WindowType::Menu:
case WindowType::WindowSwitcher:
@ -1218,7 +1226,7 @@ bool WindowManager::is_active_window_or_accessory(Window& window) const
static bool window_type_can_become_active(WindowType type)
{
return type == WindowType::Normal || type == WindowType::Desktop;
return type == WindowType::Normal || type == WindowType::ToolWindow || type == WindowType::Desktop;
}
void WindowManager::restore_active_input_window(Window* window)
@ -1507,7 +1515,7 @@ void WindowManager::maximize_windows(Window& window, bool maximized)
Gfx::IntPoint WindowManager::get_recommended_window_position(const Gfx::IntPoint& desired)
{
// FIXME: Find a better source for the width and height to shift by.
Gfx::IntPoint shift(8, Gfx::WindowTheme::current().title_bar_height(palette()) + 10);
Gfx::IntPoint shift(8, Gfx::WindowTheme::current().title_bar_height(Gfx::WindowTheme::WindowType::Normal, palette()) + 10);
// FIXME: Find a better source for this.
int taskbar_height = 28;
@ -1526,7 +1534,7 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(const Gfx::IntPoint
point = overlap_window->position() + shift;
point = { point.x() % Screen::the().width(),
(point.y() >= (Screen::the().height() - taskbar_height))
? menubar_height + Gfx::WindowTheme::current().title_bar_height(palette())
? menubar_height + Gfx::WindowTheme::current().title_bar_height(Gfx::WindowTheme::WindowType::Normal, palette())
: point.y() };
} else {
point = desired;

View file

@ -389,6 +389,8 @@ IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Call
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Normal, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::ToolWindow, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break)
@ -440,6 +442,8 @@ IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Call
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::ToolWindow, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback) == IterationDecision::Break)
return IterationDecision::Break;
return for_each_visible_window_of_type_from_front_to_back(WindowType::Desktop, callback);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -38,4 +38,5 @@ enum class WindowType {
MenuApplet,
Notification,
Desktop,
ToolWindow,
};