mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:57:47 +00:00
WindowServer+NotificationServer: Vertical title bar for notifications
This patch adds a specialized window type for notifications. They now have a title bar on the right-hand side, with a close button. This removes the need for the "Done" button in notifications, giving us a bit more horizontal space overall. Design based on a mock-up from @xTibor :^)
This commit is contained in:
parent
cbd7effd3b
commit
012a4eb0b5
7 changed files with 115 additions and 60 deletions
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
// Keep this in sync with WindowType.
|
// Keep this in sync with WindowServer::WindowType.
|
||||||
enum class WindowType {
|
enum class WindowType {
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
Normal,
|
Normal,
|
||||||
|
@ -38,6 +38,7 @@ enum class WindowType {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Menubar,
|
Menubar,
|
||||||
MenuApplet,
|
MenuApplet,
|
||||||
|
Notification,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,9 @@ void update_notification_window_locations()
|
||||||
for (auto& window : s_windows) {
|
for (auto& window : s_windows) {
|
||||||
Gfx::Point new_window_location;
|
Gfx::Point new_window_location;
|
||||||
if (last_window_rect.is_null())
|
if (last_window_rect.is_null())
|
||||||
new_window_location = GUI::Desktop::the().rect().top_right().translated(-window->rect().width() - 8, 26);
|
new_window_location = GUI::Desktop::the().rect().top_right().translated(-window->rect().width() - 24, 26);
|
||||||
else
|
else
|
||||||
new_window_location = last_window_rect.bottom_left().translated(0, 8);
|
new_window_location = last_window_rect.bottom_left().translated(0, 10);
|
||||||
if (window->rect().location() != new_window_location) {
|
if (window->rect().location() != new_window_location) {
|
||||||
window->move_to(new_window_location);
|
window->move_to(new_window_location);
|
||||||
window->set_original_rect(window->rect());
|
window->set_original_rect(window->rect());
|
||||||
|
@ -60,7 +60,9 @@ NotificationWindow::NotificationWindow(const String& text, const String& title,
|
||||||
{
|
{
|
||||||
s_windows.append(this);
|
s_windows.append(this);
|
||||||
|
|
||||||
set_window_type(GUI::WindowType::Tooltip);
|
set_window_type(GUI::WindowType::Notification);
|
||||||
|
set_resizable(false);
|
||||||
|
set_minimizable(false);
|
||||||
|
|
||||||
Gfx::Rect lowest_notification_rect_on_screen;
|
Gfx::Rect lowest_notification_rect_on_screen;
|
||||||
for (auto& window : s_windows) {
|
for (auto& window : s_windows) {
|
||||||
|
@ -69,12 +71,12 @@ NotificationWindow::NotificationWindow(const String& text, const String& title,
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx::Rect rect;
|
Gfx::Rect rect;
|
||||||
rect.set_width(240);
|
rect.set_width(220);
|
||||||
rect.set_height(40);
|
rect.set_height(40);
|
||||||
rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 8, 26));
|
rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 24, 26));
|
||||||
|
|
||||||
if (!lowest_notification_rect_on_screen.is_null())
|
if (!lowest_notification_rect_on_screen.is_null())
|
||||||
rect.set_location(lowest_notification_rect_on_screen.bottom_left().translated(0, 8));
|
rect.set_location(lowest_notification_rect_on_screen.bottom_left().translated(0, 10));
|
||||||
|
|
||||||
set_rect(rect);
|
set_rect(rect);
|
||||||
|
|
||||||
|
@ -108,13 +110,10 @@ NotificationWindow::NotificationWindow(const String& text, const String& title,
|
||||||
right_container.set_preferred_size(36, 0);
|
right_container.set_preferred_size(36, 0);
|
||||||
right_container.set_layout<GUI::HorizontalBoxLayout>();
|
right_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||||
|
|
||||||
auto& button = right_container.add<GUI::Button>("Okay");
|
on_close_request = [this] {
|
||||||
button.on_click = [this] {
|
s_windows.remove_first_matching([this](auto& entry) { return entry == this; });
|
||||||
auto this_window_index = s_windows.find_first_index(this);
|
|
||||||
if (this_window_index.has_value())
|
|
||||||
s_windows.remove(this_window_index.value());
|
|
||||||
close();
|
|
||||||
update_notification_window_locations();
|
update_notification_window_locations();
|
||||||
|
return CloseRequestDecision::Close;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ Window::Window(ClientConnection& client, WindowType window_type, int window_id,
|
||||||
m_wm_event_mask = WMEventMask::WindowStateChanges | WMEventMask::WindowRemovals | WMEventMask::WindowIconChanges;
|
m_wm_event_mask = WMEventMask::WindowStateChanges | WMEventMask::WindowRemovals | WMEventMask::WindowIconChanges;
|
||||||
m_listens_to_wm_events = true;
|
m_listens_to_wm_events = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowManager::the().add_window(*this);
|
WindowManager::the().add_window(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,8 @@ void WindowFrame::did_set_maximized(Badge<Window>, bool maximized)
|
||||||
|
|
||||||
Gfx::Rect WindowFrame::title_bar_rect() const
|
Gfx::Rect WindowFrame::title_bar_rect() const
|
||||||
{
|
{
|
||||||
|
if (m_window.type() == WindowType::Notification)
|
||||||
|
return { m_window.width() + 3, 3, window_titlebar_height, m_window.height() };
|
||||||
return { 3, 3, m_window.width(), window_titlebar_height };
|
return { 3, 3, m_window.width(), window_titlebar_height };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,54 +180,58 @@ Gfx::Rect WindowFrame::title_bar_text_rect() const
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowFrame::paint(Gfx::Painter& painter)
|
WindowFrame::FrameColors WindowFrame::compute_frame_colors() const
|
||||||
{
|
{
|
||||||
Gfx::PainterStateSaver saver(painter);
|
auto& wm = WindowManager::the();
|
||||||
painter.translate(rect().location());
|
auto palette = wm.palette();
|
||||||
|
if (&m_window == wm.m_highlight_window)
|
||||||
if (m_window.type() != WindowType::Normal)
|
return { palette.highlight_window_title(), palette.highlight_window_border1(), palette.highlight_window_border2() };
|
||||||
return;
|
if (&m_window == wm.m_move_window)
|
||||||
|
return { palette.moving_window_title(), palette.moving_window_border1(), palette.moving_window_border2() };
|
||||||
|
if (&m_window == wm.m_active_window)
|
||||||
|
return { palette.active_window_title(), palette.active_window_border1(), palette.active_window_border2() };
|
||||||
|
return { palette.inactive_window_title(), palette.inactive_window_border1(), palette.inactive_window_border2() };
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFrame::paint_notification_frame(Gfx::Painter& painter)
|
||||||
|
{
|
||||||
auto palette = WindowManager::the().palette();
|
auto palette = WindowManager::the().palette();
|
||||||
auto& window = m_window;
|
|
||||||
|
|
||||||
auto titlebar_rect = title_bar_rect();
|
|
||||||
auto titlebar_icon_rect = title_bar_icon_rect();
|
|
||||||
auto titlebar_inner_rect = title_bar_text_rect();
|
|
||||||
Gfx::Rect outer_rect = { {}, rect().size() };
|
Gfx::Rect outer_rect = { {}, rect().size() };
|
||||||
|
|
||||||
auto titlebar_title_rect = titlebar_inner_rect;
|
Gfx::StylePainter::paint_window_frame(painter, outer_rect, palette);
|
||||||
titlebar_title_rect.set_width(Gfx::Font::default_bold_font().width(window.title()));
|
|
||||||
|
|
||||||
Color title_color;
|
auto titlebar_rect = title_bar_rect();
|
||||||
Color border_color;
|
painter.fill_rect_with_gradient(Gfx::Orientation::Vertical, titlebar_rect, palette.active_window_border1(), palette.active_window_border2());
|
||||||
Color border_color2;
|
|
||||||
|
|
||||||
auto& wm = WindowManager::the();
|
int stripe_top = m_buttons.last().relative_rect().bottom() + 4;
|
||||||
|
int stripe_bottom = m_window.height() - 3;
|
||||||
if (&window == wm.m_highlight_window) {
|
if (stripe_top && stripe_bottom && stripe_top < stripe_bottom) {
|
||||||
border_color = palette.highlight_window_border1();
|
for (int i = 2; i <= window_titlebar_height - 2; i += 2) {
|
||||||
border_color2 = palette.highlight_window_border2();
|
painter.draw_line({ titlebar_rect.x() + i, stripe_top }, { titlebar_rect.x() + i, stripe_bottom }, palette.active_window_border1());
|
||||||
title_color = palette.highlight_window_title();
|
|
||||||
} else if (&window == wm.m_move_window) {
|
|
||||||
border_color = palette.moving_window_border1();
|
|
||||||
border_color2 = palette.moving_window_border2();
|
|
||||||
title_color = palette.moving_window_title();
|
|
||||||
} else if (&window == wm.m_active_window) {
|
|
||||||
border_color = palette.active_window_border1();
|
|
||||||
border_color2 = palette.active_window_border2();
|
|
||||||
title_color = palette.active_window_title();
|
|
||||||
} else {
|
|
||||||
border_color = palette.inactive_window_border1();
|
|
||||||
border_color2 = palette.inactive_window_border2();
|
|
||||||
title_color = palette.inactive_window_title();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFrame::paint_normal_frame(Gfx::Painter& painter)
|
||||||
|
{
|
||||||
|
auto palette = WindowManager::the().palette();
|
||||||
|
auto& window = m_window;
|
||||||
|
Gfx::Rect outer_rect = { {}, rect().size() };
|
||||||
|
|
||||||
Gfx::StylePainter::paint_window_frame(painter, outer_rect, palette);
|
Gfx::StylePainter::paint_window_frame(painter, outer_rect, palette);
|
||||||
|
|
||||||
if (!window.show_titlebar())
|
if (!window.show_titlebar())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto titlebar_rect = title_bar_rect();
|
||||||
|
auto titlebar_icon_rect = title_bar_icon_rect();
|
||||||
|
auto titlebar_inner_rect = title_bar_text_rect();
|
||||||
|
auto titlebar_title_rect = titlebar_inner_rect;
|
||||||
|
titlebar_title_rect.set_width(Gfx::Font::default_bold_font().width(window.title()));
|
||||||
|
|
||||||
|
auto [title_color, border_color, border_color2] = compute_frame_colors();
|
||||||
|
|
||||||
|
auto& wm = WindowManager::the();
|
||||||
painter.draw_line(titlebar_rect.bottom_left().translated(0, 1), titlebar_rect.bottom_right().translated(0, 1), palette.button());
|
painter.draw_line(titlebar_rect.bottom_left().translated(0, 1), titlebar_rect.bottom_right().translated(0, 1), palette.button());
|
||||||
|
|
||||||
auto leftmost_button_rect = m_buttons.is_empty() ? Gfx::Rect() : m_buttons.last().relative_rect();
|
auto leftmost_button_rect = m_buttons.is_empty() ? Gfx::Rect() : m_buttons.last().relative_rect();
|
||||||
|
@ -249,6 +255,19 @@ void WindowFrame::paint(Gfx::Painter& painter)
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.blit(titlebar_icon_rect.location(), window.icon(), window.icon().rect());
|
painter.blit(titlebar_icon_rect.location(), window.icon(), window.icon().rect());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFrame::paint(Gfx::Painter& painter)
|
||||||
|
{
|
||||||
|
Gfx::PainterStateSaver saver(painter);
|
||||||
|
painter.translate(rect().location());
|
||||||
|
|
||||||
|
if (m_window.type() == WindowType::Notification)
|
||||||
|
paint_notification_frame(painter);
|
||||||
|
else if (m_window.type() == WindowType::Normal)
|
||||||
|
paint_normal_frame(painter);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
for (auto& button : m_buttons) {
|
for (auto& button : m_buttons) {
|
||||||
button.paint(painter);
|
button.paint(painter);
|
||||||
|
@ -262,10 +281,19 @@ static Gfx::Rect frame_rect_for_window(Window& window, const Gfx::Rect& rect)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WindowType::Normal:
|
case WindowType::Normal:
|
||||||
return { rect.x() - 3,
|
return {
|
||||||
|
rect.x() - 3,
|
||||||
rect.y() - window_titlebar_height - 4 + offset,
|
rect.y() - window_titlebar_height - 4 + offset,
|
||||||
rect.width() + 6,
|
rect.width() + 6,
|
||||||
rect.height() + 7 + window_titlebar_height - offset };
|
rect.height() + 7 + window_titlebar_height - offset
|
||||||
|
};
|
||||||
|
case WindowType::Notification:
|
||||||
|
return {
|
||||||
|
rect.x() - 3,
|
||||||
|
rect.y() - 3,
|
||||||
|
rect.width() + 6 + window_titlebar_height,
|
||||||
|
rect.height() + 6
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
@ -290,14 +318,25 @@ void WindowFrame::notify_window_rect_changed(const Gfx::Rect& old_rect, const Gf
|
||||||
{
|
{
|
||||||
int window_button_width = 15;
|
int window_button_width = 15;
|
||||||
int window_button_height = 15;
|
int window_button_height = 15;
|
||||||
int x = title_bar_text_rect().right() + 1;
|
int pos;
|
||||||
|
if (m_window.type() == WindowType::Notification)
|
||||||
|
pos = title_bar_rect().top() + 2;
|
||||||
|
else
|
||||||
|
pos = title_bar_text_rect().right() + 1;
|
||||||
|
|
||||||
for (auto& button : m_buttons) {
|
for (auto& button : m_buttons) {
|
||||||
x -= window_button_width;
|
if (m_window.type() == WindowType::Notification) {
|
||||||
Gfx::Rect rect { x, 0, window_button_width, window_button_height };
|
Gfx::Rect rect { 0, pos, window_button_width, window_button_height };
|
||||||
|
rect.center_horizontally_within(title_bar_rect());
|
||||||
|
button.set_relative_rect(rect);
|
||||||
|
pos += window_button_width;
|
||||||
|
} else {
|
||||||
|
pos -= window_button_width;
|
||||||
|
Gfx::Rect rect { pos, 0, window_button_width, window_button_height };
|
||||||
rect.center_vertically_within(title_bar_text_rect());
|
rect.center_vertically_within(title_bar_text_rect());
|
||||||
button.set_relative_rect(rect);
|
button.set_relative_rect(rect);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto& wm = WindowManager::the();
|
auto& wm = WindowManager::the();
|
||||||
wm.invalidate(frame_rect_for_window(m_window, old_rect));
|
wm.invalidate(frame_rect_for_window(m_window, old_rect));
|
||||||
|
@ -313,10 +352,10 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& wm = WindowManager::the();
|
auto& wm = WindowManager::the();
|
||||||
if (m_window.type() != WindowType::Normal)
|
if (m_window.type() != WindowType::Normal && m_window.type() != WindowType::Notification)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.type() == Event::MouseDown && (event.button() == MouseButton::Left || event.button() == MouseButton::Right) && title_bar_icon_rect().contains(event.position())) {
|
if (m_window.type() == WindowType::Normal && event.type() == Event::MouseDown && (event.button() == MouseButton::Left || event.button() == MouseButton::Right) && title_bar_icon_rect().contains(event.position())) {
|
||||||
wm.move_to_front_and_make_active(m_window);
|
wm.move_to_front_and_make_active(m_window);
|
||||||
m_window.popup_window_menu(event.position().translated(rect().location()));
|
m_window.popup_window_menu(event.position().translated(rect().location()));
|
||||||
return;
|
return;
|
||||||
|
@ -339,11 +378,11 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
|
||||||
return button.on_mouse_event(event.translated(-button.relative_rect().location()));
|
return button.on_mouse_event(event.translated(-button.relative_rect().location()));
|
||||||
}
|
}
|
||||||
if (event.type() == Event::MouseDown) {
|
if (event.type() == Event::MouseDown) {
|
||||||
if (event.button() == MouseButton::Right) {
|
if (m_window.type() == WindowType::Normal && event.button() == MouseButton::Right) {
|
||||||
m_window.popup_window_menu(event.position().translated(rect().location()));
|
m_window.popup_window_menu(event.position().translated(rect().location()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.button() == MouseButton::Left)
|
if (m_window.is_movable() && event.button() == MouseButton::Left)
|
||||||
wm.start_window_move(m_window, event.translated(rect().location()));
|
wm.start_window_move(m_window, event.translated(rect().location()));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -369,5 +408,4 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
|
||||||
if (m_window.is_resizable() && event.type() == Event::MouseDown && event.button() == MouseButton::Left)
|
if (m_window.is_resizable() && event.type() == Event::MouseDown && event.button() == MouseButton::Left)
|
||||||
wm.start_window_resize(m_window, event.translated(rect().location()));
|
wm.start_window_resize(m_window, event.translated(rect().location()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,17 @@ public:
|
||||||
void did_set_maximized(Badge<Window>, bool);
|
void did_set_maximized(Badge<Window>, bool);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void paint_notification_frame(Gfx::Painter&);
|
||||||
|
void paint_normal_frame(Gfx::Painter&);
|
||||||
|
|
||||||
|
struct FrameColors {
|
||||||
|
Color title_color;
|
||||||
|
Color border_color;
|
||||||
|
Color border_color2;
|
||||||
|
};
|
||||||
|
|
||||||
|
FrameColors compute_frame_colors() const;
|
||||||
|
|
||||||
Window& m_window;
|
Window& m_window;
|
||||||
NonnullOwnPtrVector<Button> m_buttons;
|
NonnullOwnPtrVector<Button> m_buttons;
|
||||||
Button* m_maximize_button { nullptr };
|
Button* m_maximize_button { nullptr };
|
||||||
|
|
|
@ -315,6 +315,8 @@ IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Call
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break)
|
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
|
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break)
|
||||||
|
return IterationDecision::Break;
|
||||||
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menubar, callback) == IterationDecision::Break)
|
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menubar, callback) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menu, callback) == IterationDecision::Break)
|
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menu, callback) == IterationDecision::Break)
|
||||||
|
@ -354,10 +356,12 @@ IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Call
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menubar, callback) == IterationDecision::Break)
|
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menubar, callback) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break)
|
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Tooltip, callback) == IterationDecision::Break)
|
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Tooltip, callback) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
|
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break)
|
||||||
|
return IterationDecision::Break;
|
||||||
return for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback);
|
return for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,4 +36,5 @@ enum class WindowType {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Menubar,
|
Menubar,
|
||||||
MenuApplet,
|
MenuApplet,
|
||||||
|
Notification,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue