2020-01-18 11:38:21 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2020-06-11 23:46:49 +03:00
|
|
|
#include "ClientConnection.h"
|
2020-02-15 02:10:34 +03:00
|
|
|
#include <AK/Badge.h>
|
2020-02-06 14:04:00 +03:00
|
|
|
#include <LibGfx/Font.h>
|
|
|
|
#include <LibGfx/Painter.h>
|
|
|
|
#include <LibGfx/StylePainter.h>
|
2020-08-09 20:29:15 +03:00
|
|
|
#include <LibGfx/WindowTheme.h>
|
2020-02-06 22:03:37 +03:00
|
|
|
#include <WindowServer/Button.h>
|
|
|
|
#include <WindowServer/Compositor.h>
|
|
|
|
#include <WindowServer/Event.h>
|
2021-02-08 08:26:31 +03:00
|
|
|
#include <WindowServer/Screen.h>
|
2020-02-06 22:03:37 +03:00
|
|
|
#include <WindowServer/Window.h>
|
|
|
|
#include <WindowServer/WindowFrame.h>
|
|
|
|
#include <WindowServer/WindowManager.h>
|
|
|
|
|
|
|
|
namespace WindowServer {
|
2019-04-05 16:54:56 +03:00
|
|
|
|
2020-08-10 14:03:44 +03:00
|
|
|
static Gfx::WindowTheme::WindowType to_theme_window_type(WindowType type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case WindowType::Normal:
|
|
|
|
return Gfx::WindowTheme::WindowType::Normal;
|
2021-02-16 18:10:39 +03:00
|
|
|
case WindowType::ToolWindow:
|
|
|
|
return Gfx::WindowTheme::WindowType::ToolWindow;
|
2020-08-10 14:03:44 +03:00
|
|
|
case WindowType::Notification:
|
|
|
|
return Gfx::WindowTheme::WindowType::Notification;
|
|
|
|
default:
|
|
|
|
return Gfx::WindowTheme::WindowType::Other;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 23:22:05 +03:00
|
|
|
static Gfx::Bitmap* s_minimize_icon;
|
|
|
|
static Gfx::Bitmap* s_maximize_icon;
|
|
|
|
static Gfx::Bitmap* s_restore_icon;
|
|
|
|
static Gfx::Bitmap* s_close_icon;
|
|
|
|
|
|
|
|
static String s_last_title_button_icons_path;
|
2021-01-19 00:26:45 +03:00
|
|
|
static int s_last_title_button_icons_scale;
|
2019-05-12 22:32:02 +03:00
|
|
|
|
2021-02-12 01:12:19 +03:00
|
|
|
static Gfx::Bitmap* s_active_window_shadow;
|
|
|
|
static Gfx::Bitmap* s_inactive_window_shadow;
|
|
|
|
static Gfx::Bitmap* s_menu_bar_shadow;
|
2021-02-10 00:13:06 +03:00
|
|
|
static Gfx::Bitmap* s_menu_shadow;
|
2021-02-12 01:12:19 +03:00
|
|
|
static Gfx::Bitmap* s_task_bar_shadow;
|
2021-02-10 00:13:06 +03:00
|
|
|
static Gfx::Bitmap* s_tooltip_shadow;
|
2021-02-12 01:12:19 +03:00
|
|
|
static String s_last_active_window_shadow_path;
|
|
|
|
static String s_last_inactive_window_shadow_path;
|
|
|
|
static String s_last_menu_bar_shadow_path;
|
2021-02-10 00:13:06 +03:00
|
|
|
static String s_last_menu_shadow_path;
|
2021-02-12 01:12:19 +03:00
|
|
|
static String s_last_task_bar_shadow_path;
|
2021-02-10 00:13:06 +03:00
|
|
|
static String s_last_tooltip_shadow_path;
|
2021-02-09 03:27:51 +03:00
|
|
|
|
|
|
|
static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& rect)
|
|
|
|
{
|
|
|
|
if (window.is_frameless())
|
|
|
|
return rect;
|
|
|
|
return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette());
|
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
WindowFrame::WindowFrame(Window& window)
|
2019-04-05 16:54:56 +03:00
|
|
|
: m_window(window)
|
|
|
|
{
|
2020-07-29 23:22:05 +03:00
|
|
|
auto button = make<Button>(*this, [this](auto&) {
|
2019-06-21 12:03:43 +03:00
|
|
|
m_window.request_close();
|
2020-07-29 23:22:05 +03:00
|
|
|
});
|
|
|
|
m_close_button = button.ptr();
|
|
|
|
m_buttons.append(move(button));
|
2019-04-05 23:32:00 +03:00
|
|
|
|
2019-05-13 01:48:54 +03:00
|
|
|
if (window.is_resizable()) {
|
2020-07-29 23:22:05 +03:00
|
|
|
auto button = make<Button>(*this, [this](auto&) {
|
2020-08-15 02:00:23 +03:00
|
|
|
WindowManager::the().maximize_windows(m_window, !m_window.is_maximized());
|
2019-06-02 16:35:00 +03:00
|
|
|
});
|
2021-01-26 23:50:02 +03:00
|
|
|
button->on_middle_click = [&](auto&) {
|
|
|
|
m_window.set_vertically_maximized();
|
|
|
|
};
|
2019-06-02 16:35:00 +03:00
|
|
|
m_maximize_button = button.ptr();
|
|
|
|
m_buttons.append(move(button));
|
2019-05-13 01:48:54 +03:00
|
|
|
}
|
2019-05-12 22:33:25 +03:00
|
|
|
|
2020-01-04 14:12:02 +03:00
|
|
|
if (window.is_minimizable()) {
|
2020-07-29 23:22:05 +03:00
|
|
|
auto button = make<Button>(*this, [this](auto&) {
|
2020-08-15 02:00:23 +03:00
|
|
|
WindowManager::the().minimize_windows(m_window, true);
|
2020-01-04 14:12:02 +03:00
|
|
|
});
|
|
|
|
m_minimize_button = button.ptr();
|
|
|
|
m_buttons.append(move(button));
|
|
|
|
}
|
2020-07-29 23:22:05 +03:00
|
|
|
|
|
|
|
set_button_icons();
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
WindowFrame::~WindowFrame()
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-07-29 23:22:05 +03:00
|
|
|
void WindowFrame::set_button_icons()
|
|
|
|
{
|
2021-02-09 03:27:51 +03:00
|
|
|
m_dirty = true;
|
2020-07-29 23:22:05 +03:00
|
|
|
if (m_window.is_frameless())
|
|
|
|
return;
|
|
|
|
|
2021-02-10 00:13:06 +03:00
|
|
|
m_close_button->set_icon(*s_close_icon);
|
|
|
|
if (m_window.is_minimizable())
|
|
|
|
m_minimize_button->set_icon(*s_minimize_icon);
|
|
|
|
if (m_window.is_resizable())
|
|
|
|
m_maximize_button->set_icon(m_window.is_maximized() ? *s_restore_icon : *s_maximize_icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowFrame::reload_config()
|
|
|
|
{
|
2020-07-29 23:22:05 +03:00
|
|
|
String icons_path = WindowManager::the().palette().title_button_icons_path();
|
2021-01-19 00:26:45 +03:00
|
|
|
int icons_scale = WindowManager::the().compositor_icon_scale();
|
2020-07-29 23:22:05 +03:00
|
|
|
|
|
|
|
StringBuilder full_path;
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!s_minimize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.append(icons_path);
|
|
|
|
full_path.append("window-minimize.png");
|
2021-02-09 03:27:51 +03:00
|
|
|
if (s_minimize_icon)
|
|
|
|
s_minimize_icon->unref();
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!(s_minimize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref()))
|
|
|
|
s_minimize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png", icons_scale).leak_ref();
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.clear();
|
|
|
|
}
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!s_maximize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.append(icons_path);
|
|
|
|
full_path.append("window-maximize.png");
|
2021-02-09 03:27:51 +03:00
|
|
|
if (s_maximize_icon)
|
|
|
|
s_maximize_icon->unref();
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!(s_maximize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref()))
|
|
|
|
s_maximize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png", icons_scale).leak_ref();
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.clear();
|
|
|
|
}
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!s_restore_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.append(icons_path);
|
|
|
|
full_path.append("window-restore.png");
|
2021-02-09 03:27:51 +03:00
|
|
|
if (s_restore_icon)
|
|
|
|
s_restore_icon->unref();
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!(s_restore_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref()))
|
|
|
|
s_restore_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png", icons_scale).leak_ref();
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.clear();
|
|
|
|
}
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!s_close_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.append(icons_path);
|
|
|
|
full_path.append("window-close.png");
|
2021-02-09 03:27:51 +03:00
|
|
|
if (s_close_icon)
|
|
|
|
s_close_icon->unref();
|
2021-01-19 00:26:45 +03:00
|
|
|
if (!(s_close_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref()))
|
|
|
|
s_close_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png", icons_scale).leak_ref();
|
2020-07-29 23:22:05 +03:00
|
|
|
full_path.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
s_last_title_button_icons_path = icons_path;
|
2021-01-19 00:26:45 +03:00
|
|
|
s_last_title_button_icons_scale = icons_scale;
|
2021-02-09 03:27:51 +03:00
|
|
|
|
2021-02-10 00:13:06 +03:00
|
|
|
auto load_shadow = [](const String& path, String& last_path, Gfx::Bitmap*& shadow_bitmap) {
|
2021-02-10 08:12:32 +03:00
|
|
|
if (path.is_empty()) {
|
|
|
|
last_path = String::empty();
|
|
|
|
if (shadow_bitmap) {
|
|
|
|
shadow_bitmap->unref();
|
|
|
|
shadow_bitmap = nullptr;
|
|
|
|
}
|
|
|
|
} else if (!shadow_bitmap || shadow_bitmap->scale() != s_last_title_button_icons_scale || last_path != path) {
|
|
|
|
if (shadow_bitmap)
|
|
|
|
shadow_bitmap->unref();
|
2021-02-10 00:13:06 +03:00
|
|
|
shadow_bitmap = Gfx::Bitmap::load_from_file(path, s_last_title_button_icons_scale).leak_ref();
|
2021-02-10 08:12:32 +03:00
|
|
|
if (shadow_bitmap)
|
|
|
|
last_path = path;
|
|
|
|
else
|
|
|
|
last_path = String::empty();
|
2021-02-10 00:13:06 +03:00
|
|
|
}
|
|
|
|
};
|
2021-02-12 01:12:19 +03:00
|
|
|
load_shadow(WindowManager::the().palette().active_window_shadow_path(), s_last_active_window_shadow_path, s_active_window_shadow);
|
|
|
|
load_shadow(WindowManager::the().palette().inactive_window_shadow_path(), s_last_inactive_window_shadow_path, s_inactive_window_shadow);
|
|
|
|
load_shadow(WindowManager::the().palette().menu_bar_shadow_path(), s_last_menu_bar_shadow_path, s_menu_bar_shadow);
|
2021-02-10 00:13:06 +03:00
|
|
|
load_shadow(WindowManager::the().palette().menu_shadow_path(), s_last_menu_shadow_path, s_menu_shadow);
|
2021-02-12 01:12:19 +03:00
|
|
|
load_shadow(WindowManager::the().palette().task_bar_shadow_path(), s_last_task_bar_shadow_path, s_task_bar_shadow);
|
2021-02-10 00:13:06 +03:00
|
|
|
load_shadow(WindowManager::the().palette().tooltip_shadow_path(), s_last_tooltip_shadow_path, s_tooltip_shadow);
|
2021-02-09 03:27:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Gfx::Bitmap* WindowFrame::window_shadow() const
|
|
|
|
{
|
2021-02-09 20:57:55 +03:00
|
|
|
if (m_window.is_frameless())
|
|
|
|
return nullptr;
|
2021-02-10 00:13:06 +03:00
|
|
|
switch (m_window.type()) {
|
|
|
|
case WindowType::Desktop:
|
2021-02-09 03:27:51 +03:00
|
|
|
return nullptr;
|
2021-02-10 00:13:06 +03:00
|
|
|
case WindowType::Menu:
|
|
|
|
return s_menu_shadow;
|
|
|
|
case WindowType::Tooltip:
|
|
|
|
return s_tooltip_shadow;
|
2021-02-12 01:12:19 +03:00
|
|
|
case WindowType::Menubar:
|
|
|
|
return s_menu_bar_shadow;
|
|
|
|
case WindowType::Taskbar:
|
|
|
|
return s_task_bar_shadow;
|
2021-02-10 00:13:06 +03:00
|
|
|
default:
|
2021-02-12 01:12:19 +03:00
|
|
|
return m_window.is_active() ? s_active_window_shadow : s_inactive_window_shadow;
|
2021-02-10 00:13:06 +03:00
|
|
|
}
|
2021-02-09 03:27:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool WindowFrame::frame_has_alpha() const
|
|
|
|
{
|
2021-02-13 00:25:08 +03:00
|
|
|
if (m_has_alpha_channel)
|
|
|
|
return true;
|
2021-02-09 03:27:51 +03:00
|
|
|
if (auto* shadow_bitmap = window_shadow(); shadow_bitmap && shadow_bitmap->format() == Gfx::BitmapFormat::RGBA32)
|
|
|
|
return true;
|
|
|
|
return false;
|
2020-07-29 23:22:05 +03:00
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
void WindowFrame::did_set_maximized(Badge<Window>, bool maximized)
|
2019-06-02 16:35:00 +03:00
|
|
|
{
|
|
|
|
ASSERT(m_maximize_button);
|
2020-07-29 23:22:05 +03:00
|
|
|
m_maximize_button->set_icon(maximized ? *s_restore_icon : *s_maximize_icon);
|
2019-06-02 16:35:00 +03:00
|
|
|
}
|
|
|
|
|
2020-06-10 11:57:59 +03:00
|
|
|
Gfx::IntRect WindowFrame::title_bar_rect() const
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2020-08-10 14:05:02 +03:00
|
|
|
return Gfx::WindowTheme::current().title_bar_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette());
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2020-06-10 11:57:59 +03:00
|
|
|
Gfx::IntRect WindowFrame::title_bar_icon_rect() const
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2020-08-10 14:05:02 +03:00
|
|
|
return Gfx::WindowTheme::current().title_bar_icon_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette());
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2020-06-10 11:57:59 +03:00
|
|
|
Gfx::IntRect WindowFrame::title_bar_text_rect() const
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2020-08-10 14:05:02 +03:00
|
|
|
return Gfx::WindowTheme::current().title_bar_text_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette());
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:29:15 +03:00
|
|
|
Gfx::WindowTheme::WindowState WindowFrame::window_state_for_theme() const
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2020-03-30 18:00:23 +03:00
|
|
|
auto& wm = WindowManager::the();
|
2021-01-01 03:10:53 +03:00
|
|
|
|
|
|
|
if (m_flash_timer && m_flash_timer->is_active())
|
|
|
|
return m_flash_counter & 1 ? Gfx::WindowTheme::WindowState::Active : Gfx::WindowTheme::WindowState::Inactive;
|
|
|
|
|
2020-03-30 18:00:23 +03:00
|
|
|
if (&m_window == wm.m_highlight_window)
|
2020-08-09 20:29:15 +03:00
|
|
|
return Gfx::WindowTheme::WindowState::Highlighted;
|
2020-03-30 18:00:23 +03:00
|
|
|
if (&m_window == wm.m_move_window)
|
2020-08-09 20:29:15 +03:00
|
|
|
return Gfx::WindowTheme::WindowState::Moving;
|
2020-07-15 04:17:00 +03:00
|
|
|
if (wm.is_active_window_or_accessory(m_window))
|
2020-08-09 20:29:15 +03:00
|
|
|
return Gfx::WindowTheme::WindowState::Active;
|
|
|
|
return Gfx::WindowTheme::WindowState::Inactive;
|
2020-03-30 18:00:23 +03:00
|
|
|
}
|
2019-04-08 19:58:44 +03:00
|
|
|
|
2020-03-30 18:00:23 +03:00
|
|
|
void WindowFrame::paint_notification_frame(Gfx::Painter& painter)
|
|
|
|
{
|
2020-02-06 22:03:37 +03:00
|
|
|
auto palette = WindowManager::the().palette();
|
2020-08-23 14:17:34 +03:00
|
|
|
Gfx::WindowTheme::current().paint_notification_frame(painter, m_window.rect(), palette, m_buttons.last().relative_rect());
|
2020-03-30 18:00:23 +03:00
|
|
|
}
|
|
|
|
|
2021-02-16 18:10:39 +03:00
|
|
|
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)
|
2020-03-30 18:00:23 +03:00
|
|
|
{
|
|
|
|
auto palette = WindowManager::the().palette();
|
2021-02-16 18:10:39 +03:00
|
|
|
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);
|
|
|
|
}
|
2019-04-05 16:54:56 +03:00
|
|
|
|
2021-02-16 18:10:39 +03:00
|
|
|
void WindowFrame::paint_normal_frame(Gfx::Painter& painter)
|
|
|
|
{
|
|
|
|
auto palette = WindowManager::the().palette();
|
2020-06-10 11:57:59 +03:00
|
|
|
auto leftmost_button_rect = m_buttons.is_empty() ? Gfx::IntRect() : m_buttons.last().relative_rect();
|
2021-02-16 18:10:39 +03:00
|
|
|
Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), m_window.icon(), palette, leftmost_button_rect);
|
2020-03-30 18:00:23 +03:00
|
|
|
}
|
|
|
|
|
2021-02-08 08:26:31 +03:00
|
|
|
void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect)
|
|
|
|
{
|
|
|
|
render_to_cache();
|
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
auto frame_rect = render_rect();
|
2021-02-08 08:26:31 +03:00
|
|
|
auto window_rect = m_window.rect();
|
|
|
|
|
|
|
|
if (m_top_bottom) {
|
|
|
|
auto top_bottom_height = frame_rect.height() - window_rect.height();
|
|
|
|
if (m_bottom_y > 0) {
|
|
|
|
// We have a top piece
|
|
|
|
auto src_rect = rect.intersected({ frame_rect.location(), { frame_rect.width(), m_bottom_y } });
|
|
|
|
if (!src_rect.is_empty())
|
|
|
|
painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-frame_rect.location()), m_opacity);
|
|
|
|
}
|
|
|
|
if (m_bottom_y < top_bottom_height) {
|
|
|
|
// We have a bottom piece
|
|
|
|
Gfx::IntRect rect_in_frame { frame_rect.x(), window_rect.bottom() + 1, frame_rect.width(), top_bottom_height - m_bottom_y };
|
|
|
|
auto src_rect = rect.intersected(rect_in_frame);
|
|
|
|
if (!src_rect.is_empty())
|
|
|
|
painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-rect_in_frame.x(), -rect_in_frame.y() + m_bottom_y), m_opacity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_left_right) {
|
|
|
|
auto left_right_width = frame_rect.width() - window_rect.width();
|
|
|
|
if (m_right_x > 0) {
|
|
|
|
// We have a left piece
|
|
|
|
Gfx::IntRect rect_in_frame { frame_rect.x(), window_rect.y(), m_right_x, window_rect.height() };
|
|
|
|
auto src_rect = rect.intersected(rect_in_frame);
|
|
|
|
if (!src_rect.is_empty())
|
|
|
|
painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.location()), m_opacity);
|
|
|
|
}
|
|
|
|
if (m_right_x < left_right_width) {
|
|
|
|
// We have a right piece
|
|
|
|
Gfx::IntRect rect_in_frame { window_rect.right() + 1, window_rect.y(), left_right_width - m_right_x, window_rect.height() };
|
|
|
|
auto src_rect = rect.intersected(rect_in_frame);
|
|
|
|
if (!src_rect.is_empty())
|
|
|
|
painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.x() + m_right_x, -rect_in_frame.y()), m_opacity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowFrame::render(Gfx::Painter& painter)
|
2020-03-30 18:00:23 +03:00
|
|
|
{
|
2020-05-02 00:26:32 +03:00
|
|
|
if (m_window.is_frameless())
|
|
|
|
return;
|
|
|
|
|
2020-03-30 18:00:23 +03:00
|
|
|
if (m_window.type() == WindowType::Notification)
|
|
|
|
paint_notification_frame(painter);
|
|
|
|
else if (m_window.type() == WindowType::Normal)
|
|
|
|
paint_normal_frame(painter);
|
2021-02-16 18:10:39 +03:00
|
|
|
else if (m_window.type() == WindowType::ToolWindow)
|
|
|
|
paint_tool_window_frame(painter);
|
2020-03-30 18:00:23 +03:00
|
|
|
else
|
|
|
|
return;
|
2019-04-05 16:54:56 +03:00
|
|
|
|
2019-04-05 19:40:36 +03:00
|
|
|
for (auto& button : m_buttons) {
|
2019-07-24 10:04:57 +03:00
|
|
|
button.paint(painter);
|
2019-04-05 19:40:36 +03:00
|
|
|
}
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2021-02-13 00:25:08 +03:00
|
|
|
void WindowFrame::theme_changed()
|
|
|
|
{
|
|
|
|
m_dirty = m_shadow_dirty = true;
|
|
|
|
m_top_bottom = nullptr;
|
|
|
|
m_left_right = nullptr;
|
|
|
|
m_bottom_y = m_right_x = 0;
|
|
|
|
|
|
|
|
layout_buttons();
|
|
|
|
set_button_icons();
|
|
|
|
|
|
|
|
m_has_alpha_channel = Gfx::WindowTheme::current().frame_uses_alpha(window_state_for_theme(), WindowManager::the().palette());
|
|
|
|
}
|
|
|
|
|
2021-02-08 08:26:31 +03:00
|
|
|
void WindowFrame::render_to_cache()
|
|
|
|
{
|
|
|
|
if (!m_dirty)
|
|
|
|
return;
|
2021-02-09 03:27:51 +03:00
|
|
|
m_dirty = false;
|
2021-02-08 08:26:31 +03:00
|
|
|
|
2021-02-13 00:25:08 +03:00
|
|
|
m_has_alpha_channel = Gfx::WindowTheme::current().frame_uses_alpha(window_state_for_theme(), WindowManager::the().palette());
|
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
static Gfx::Bitmap* s_tmp_bitmap;
|
2021-02-08 08:26:31 +03:00
|
|
|
auto frame_rect = rect();
|
2021-02-09 03:27:51 +03:00
|
|
|
auto total_frame_rect = frame_rect;
|
2021-02-09 20:22:51 +03:00
|
|
|
Gfx::Bitmap* shadow_bitmap = inflate_for_shadow(total_frame_rect, m_shadow_offset);
|
2021-02-08 08:26:31 +03:00
|
|
|
auto window_rect = m_window.rect();
|
|
|
|
auto scale = Screen::the().scale_factor();
|
2021-02-09 03:27:51 +03:00
|
|
|
if (!s_tmp_bitmap || !s_tmp_bitmap->size().contains(total_frame_rect.size()) || s_tmp_bitmap->scale() != scale) {
|
|
|
|
if (s_tmp_bitmap)
|
|
|
|
s_tmp_bitmap->unref();
|
|
|
|
s_tmp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, total_frame_rect.size(), scale).leak_ref();
|
|
|
|
}
|
2021-02-08 08:26:31 +03:00
|
|
|
|
2021-02-09 20:22:51 +03:00
|
|
|
auto top_bottom_height = total_frame_rect.height() - window_rect.height();
|
|
|
|
auto left_right_width = total_frame_rect.width() - window_rect.width();
|
|
|
|
|
|
|
|
if (!m_top_bottom || m_top_bottom->width() != total_frame_rect.width() || m_top_bottom->height() != top_bottom_height || m_top_bottom->scale() != scale) {
|
|
|
|
if (top_bottom_height > 0)
|
|
|
|
m_top_bottom = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { total_frame_rect.width(), top_bottom_height }, scale);
|
|
|
|
else
|
|
|
|
m_top_bottom = nullptr;
|
|
|
|
m_shadow_dirty = true;
|
|
|
|
}
|
|
|
|
if (!m_left_right || m_left_right->height() != total_frame_rect.height() || m_left_right->width() != left_right_width || m_left_right->scale() != scale) {
|
|
|
|
if (left_right_width > 0)
|
|
|
|
m_left_right = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { left_right_width, total_frame_rect.height() }, scale);
|
|
|
|
else
|
|
|
|
m_left_right = nullptr;
|
|
|
|
m_shadow_dirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& frame_rect_to_update = m_shadow_dirty ? total_frame_rect : frame_rect;
|
|
|
|
Gfx::IntPoint update_location(m_shadow_dirty ? Gfx::IntPoint { 0, 0 } : m_shadow_offset);
|
|
|
|
|
2021-02-08 08:26:31 +03:00
|
|
|
Gfx::Painter painter(*s_tmp_bitmap);
|
2021-02-13 00:25:08 +03:00
|
|
|
|
2021-02-09 20:22:51 +03:00
|
|
|
// Clear the frame area, not including the window content area, which we don't care about
|
|
|
|
for (auto& rect : frame_rect_to_update.shatter(window_rect))
|
|
|
|
painter.clear_rect({ rect.location() - frame_rect_to_update.location(), rect.size() }, { 255, 255, 255, 0 });
|
2021-02-09 03:27:51 +03:00
|
|
|
|
2021-02-09 20:22:51 +03:00
|
|
|
if (m_shadow_dirty && shadow_bitmap)
|
2021-02-09 03:27:51 +03:00
|
|
|
paint_simple_rect_shadow(painter, { { 0, 0 }, total_frame_rect.size() }, *shadow_bitmap);
|
2021-02-08 08:26:31 +03:00
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
{
|
|
|
|
Gfx::PainterStateSaver save(painter);
|
|
|
|
painter.translate(m_shadow_offset);
|
|
|
|
render(painter);
|
|
|
|
}
|
|
|
|
|
2021-02-09 20:22:51 +03:00
|
|
|
if (m_top_bottom && top_bottom_height > 0) {
|
2021-02-09 03:27:51 +03:00
|
|
|
m_bottom_y = window_rect.y() - total_frame_rect.y();
|
2021-02-08 08:26:31 +03:00
|
|
|
ASSERT(m_bottom_y >= 0);
|
|
|
|
|
|
|
|
Gfx::Painter top_bottom_painter(*m_top_bottom);
|
2021-02-09 20:22:51 +03:00
|
|
|
top_bottom_painter.add_clip_rect({ update_location, { frame_rect_to_update.width(), top_bottom_height - update_location.y() - (total_frame_rect.bottom() - frame_rect_to_update.bottom()) } });
|
2021-02-08 08:26:31 +03:00
|
|
|
if (m_bottom_y > 0)
|
2021-02-09 03:27:51 +03:00
|
|
|
top_bottom_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, 0, total_frame_rect.width(), m_bottom_y }, 1.0, false);
|
2021-02-08 08:26:31 +03:00
|
|
|
if (m_bottom_y < top_bottom_height)
|
2021-02-09 03:27:51 +03:00
|
|
|
top_bottom_painter.blit({ 0, m_bottom_y }, *s_tmp_bitmap, { 0, total_frame_rect.height() - (total_frame_rect.bottom() - window_rect.bottom()), total_frame_rect.width(), top_bottom_height - m_bottom_y }, 1.0, false);
|
2021-02-08 08:26:31 +03:00
|
|
|
} else {
|
|
|
|
m_bottom_y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left_right_width > 0) {
|
2021-02-09 03:27:51 +03:00
|
|
|
m_right_x = window_rect.x() - total_frame_rect.x();
|
2021-02-08 08:26:31 +03:00
|
|
|
ASSERT(m_right_x >= 0);
|
|
|
|
|
|
|
|
Gfx::Painter left_right_painter(*m_left_right);
|
2021-02-09 20:22:51 +03:00
|
|
|
left_right_painter.add_clip_rect({ update_location, { left_right_width - update_location.x() - (total_frame_rect.right() - frame_rect_to_update.right()), window_rect.height() } });
|
2021-02-08 08:26:31 +03:00
|
|
|
if (m_right_x > 0)
|
2021-02-09 03:27:51 +03:00
|
|
|
left_right_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, m_bottom_y, m_right_x, window_rect.height() }, 1.0, false);
|
2021-02-08 08:26:31 +03:00
|
|
|
if (m_right_x < left_right_width)
|
2021-02-09 03:27:51 +03:00
|
|
|
left_right_painter.blit({ m_right_x, 0 }, *s_tmp_bitmap, { (window_rect.right() - total_frame_rect.x()) + 1, m_bottom_y, total_frame_rect.width() - (total_frame_rect.right() - window_rect.right()), window_rect.height() }, 1.0, false);
|
2021-02-08 08:26:31 +03:00
|
|
|
} else {
|
|
|
|
m_right_x = 0;
|
|
|
|
}
|
2021-02-09 20:22:51 +03:00
|
|
|
|
|
|
|
m_shadow_dirty = false;
|
2021-02-08 08:26:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void WindowFrame::set_opacity(float opacity)
|
|
|
|
{
|
|
|
|
if (m_opacity == opacity)
|
|
|
|
return;
|
|
|
|
bool was_opaque = is_opaque();
|
|
|
|
m_opacity = opacity;
|
|
|
|
if (was_opaque != is_opaque())
|
|
|
|
Compositor::the().invalidate_occlusions();
|
2021-02-09 03:27:51 +03:00
|
|
|
Compositor::the().invalidate_screen(render_rect());
|
2021-02-08 08:26:31 +03:00
|
|
|
WindowManager::the().notify_opacity_changed(m_window);
|
|
|
|
}
|
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
Gfx::IntRect WindowFrame::inflated_for_shadow(const Gfx::IntRect& frame_rect) const
|
2019-04-05 22:33:34 +03:00
|
|
|
{
|
2021-02-09 03:27:51 +03:00
|
|
|
if (auto* shadow = window_shadow()) {
|
|
|
|
auto total_shadow_size = shadow->height();
|
|
|
|
return frame_rect.inflated(total_shadow_size, total_shadow_size);
|
|
|
|
}
|
|
|
|
return frame_rect;
|
2019-04-05 22:33:34 +03:00
|
|
|
}
|
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
Gfx::Bitmap* WindowFrame::inflate_for_shadow(Gfx::IntRect& frame_rect, Gfx::IntPoint& shadow_offset) const
|
2019-05-25 00:37:23 +03:00
|
|
|
{
|
2021-02-09 03:27:51 +03:00
|
|
|
auto* shadow = window_shadow();
|
|
|
|
if (shadow) {
|
|
|
|
auto total_shadow_size = shadow->height();
|
|
|
|
frame_rect.inflate(total_shadow_size, total_shadow_size);
|
|
|
|
auto offset = total_shadow_size / 2;
|
|
|
|
shadow_offset = { offset, offset };
|
|
|
|
} else {
|
|
|
|
shadow_offset = {};
|
|
|
|
}
|
|
|
|
return shadow;
|
2019-05-25 00:37:23 +03:00
|
|
|
}
|
|
|
|
|
2020-06-10 11:57:59 +03:00
|
|
|
Gfx::IntRect WindowFrame::rect() const
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2021-02-09 03:27:51 +03:00
|
|
|
return frame_rect_for_window(m_window, m_window.rect());
|
|
|
|
}
|
|
|
|
|
|
|
|
Gfx::IntRect WindowFrame::render_rect() const
|
|
|
|
{
|
|
|
|
return inflated_for_shadow(rect());
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
void WindowFrame::invalidate_title_bar()
|
2019-04-05 22:53:45 +03:00
|
|
|
{
|
2021-02-10 21:54:58 +03:00
|
|
|
m_dirty = true;
|
2020-08-18 07:45:10 +03:00
|
|
|
invalidate(title_bar_rect());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowFrame::invalidate(Gfx::IntRect relative_rect)
|
|
|
|
{
|
|
|
|
auto frame_rect = rect();
|
|
|
|
auto window_rect = m_window.rect();
|
|
|
|
relative_rect.move_by(frame_rect.x() - window_rect.x(), frame_rect.y() - window_rect.y());
|
2021-02-09 03:27:51 +03:00
|
|
|
m_dirty = true;
|
2020-09-07 20:34:30 +03:00
|
|
|
m_window.invalidate(relative_rect, true);
|
2019-04-05 22:53:45 +03:00
|
|
|
}
|
|
|
|
|
2020-06-10 11:57:59 +03:00
|
|
|
void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect)
|
2020-07-25 14:34:43 +03:00
|
|
|
{
|
|
|
|
layout_buttons();
|
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
auto old_frame_rect = inflated_for_shadow(frame_rect_for_window(m_window, old_rect));
|
|
|
|
auto new_frame_rect = inflated_for_shadow(frame_rect_for_window(m_window, new_rect));
|
|
|
|
if (old_frame_rect.size() != new_frame_rect.size())
|
2021-02-09 20:22:51 +03:00
|
|
|
m_dirty = m_shadow_dirty = true;
|
2020-08-18 07:45:10 +03:00
|
|
|
auto& compositor = Compositor::the();
|
2021-02-09 03:27:51 +03:00
|
|
|
for (auto& dirty : old_frame_rect.shatter(new_frame_rect))
|
2020-08-18 07:45:10 +03:00
|
|
|
compositor.invalidate_screen(dirty);
|
|
|
|
if (!m_window.is_opaque())
|
2021-02-09 03:27:51 +03:00
|
|
|
compositor.invalidate_screen(new_frame_rect);
|
2020-08-18 07:45:10 +03:00
|
|
|
|
|
|
|
compositor.invalidate_occlusions();
|
|
|
|
|
|
|
|
WindowManager::the().notify_rect_changed(m_window, old_rect, new_rect);
|
2020-07-25 14:34:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void WindowFrame::layout_buttons()
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2020-08-26 01:13:32 +03:00
|
|
|
auto button_rects = Gfx::WindowTheme::current().layout_buttons(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette(), m_buttons.size());
|
|
|
|
for (size_t i = 0; i < m_buttons.size(); i++)
|
|
|
|
m_buttons[i].set_relative_rect(button_rects[i]);
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
|
|
|
|
2021-02-15 02:42:37 +03:00
|
|
|
bool WindowFrame::hit_test(const Gfx::IntPoint& point) const
|
|
|
|
{
|
|
|
|
if (m_window.is_frameless())
|
|
|
|
return false;
|
|
|
|
auto frame_rect = rect();
|
|
|
|
if (!frame_rect.contains(point))
|
|
|
|
return false;
|
|
|
|
auto window_rect = m_window.rect();
|
|
|
|
if (window_rect.contains(point))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
u8 alpha_threshold = Gfx::WindowTheme::current().frame_alpha_hit_threshold(window_state_for_theme()) * 255;
|
|
|
|
if (alpha_threshold == 0)
|
|
|
|
return true;
|
|
|
|
u8 alpha = 0xff;
|
|
|
|
auto relative_point = point.translated(-render_rect().location());
|
|
|
|
if (point.y() < window_rect.y()) {
|
|
|
|
if (m_top_bottom)
|
|
|
|
alpha = m_top_bottom->get_pixel(relative_point).alpha();
|
|
|
|
} else if (point.y() > window_rect.bottom()) {
|
|
|
|
if (m_top_bottom)
|
|
|
|
alpha = m_top_bottom->get_pixel(relative_point.x(), m_bottom_y + point.y() - window_rect.bottom() - 1).alpha();
|
|
|
|
} else if (point.x() < window_rect.x()) {
|
|
|
|
if (m_left_right)
|
|
|
|
alpha = m_left_right->get_pixel(relative_point.x(), relative_point.y() - m_bottom_y).alpha();
|
|
|
|
} else if (point.x() > window_rect.right()) {
|
|
|
|
if (m_left_right)
|
|
|
|
alpha = m_left_right->get_pixel(m_right_x + point.x() - window_rect.right() - 1, relative_point.y() - m_bottom_y).alpha();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return alpha >= alpha_threshold;
|
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
void WindowFrame::on_mouse_event(const MouseEvent& event)
|
2019-04-05 16:54:56 +03:00
|
|
|
{
|
2019-05-17 22:33:44 +03:00
|
|
|
ASSERT(!m_window.is_fullscreen());
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
auto& wm = WindowManager::the();
|
2021-02-16 18:10:39 +03:00
|
|
|
if (m_window.type() != WindowType::Normal && m_window.type() != WindowType::ToolWindow && m_window.type() != WindowType::Notification)
|
2019-04-05 16:54:56 +03:00
|
|
|
return;
|
2019-05-16 21:29:42 +03:00
|
|
|
|
2021-02-16 18:10:39 +03:00
|
|
|
if (m_window.type() == WindowType::Normal || m_window.type() == WindowType::ToolWindow) {
|
2020-07-15 19:48:58 +03:00
|
|
|
if (event.type() == Event::MouseDown)
|
|
|
|
wm.move_to_front_and_make_active(m_window);
|
2020-08-02 22:44:39 +03:00
|
|
|
|
2021-01-07 22:12:17 +03:00
|
|
|
if (m_window.blocking_modal_window())
|
2020-07-07 21:09:18 +03:00
|
|
|
return;
|
2020-08-02 22:44:39 +03:00
|
|
|
|
|
|
|
if (title_bar_icon_rect().contains(event.position())) {
|
|
|
|
if (event.type() == Event::MouseDown && (event.button() == MouseButton::Left || event.button() == MouseButton::Right)) {
|
|
|
|
// Manually start a potential double click. Since we're opening
|
|
|
|
// a menu, we will only receive the MouseDown event, so we
|
|
|
|
// need to record that fact. If the user subsequently clicks
|
|
|
|
// on the same area, the menu will get closed, and we will
|
|
|
|
// receive a MouseUp event, but because windows have changed
|
|
|
|
// we don't get a MouseDoubleClick event. We can however record
|
|
|
|
// this click, and when we receive the MouseUp event check if
|
|
|
|
// it would have been considered a double click, if it weren't
|
|
|
|
// for the fact that we opened and closed a window in the meanwhile
|
|
|
|
auto& wm = WindowManager::the();
|
|
|
|
wm.start_menu_doubleclick(m_window, event);
|
|
|
|
|
|
|
|
m_window.popup_window_menu(title_bar_rect().bottom_left().translated(rect().location()), WindowMenuDefaultAction::Close);
|
|
|
|
return;
|
|
|
|
} else if (event.type() == Event::MouseUp && event.button() == MouseButton::Left) {
|
|
|
|
// Since the MouseDown event opened a menu, another MouseUp
|
|
|
|
// from the second click outside the menu wouldn't be considered
|
|
|
|
// a double click, so let's manually check if it would otherwise
|
|
|
|
// have been be considered to be one
|
|
|
|
auto& wm = WindowManager::the();
|
|
|
|
if (wm.is_menu_doubleclick(m_window, event)) {
|
|
|
|
// It is a double click, so perform activate the default item
|
|
|
|
m_window.window_menu_activate_default();
|
|
|
|
}
|
|
|
|
return;
|
2020-07-07 21:09:18 +03:00
|
|
|
}
|
|
|
|
}
|
2019-06-21 12:03:43 +03:00
|
|
|
}
|
|
|
|
|
2020-04-23 20:27:33 +03:00
|
|
|
// This is slightly hackish, but expand the title bar rect by two pixels downwards,
|
2019-05-16 21:29:42 +03:00
|
|
|
// so that mouse events between the title bar and window contents don't act like
|
|
|
|
// mouse events on the border.
|
|
|
|
auto adjusted_title_bar_rect = title_bar_rect();
|
2020-04-23 20:27:33 +03:00
|
|
|
adjusted_title_bar_rect.set_height(adjusted_title_bar_rect.height() + 2);
|
2019-05-16 21:29:42 +03:00
|
|
|
|
|
|
|
if (adjusted_title_bar_rect.contains(event.position())) {
|
2019-04-07 00:20:06 +03:00
|
|
|
wm.clear_resize_candidate();
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
if (event.type() == Event::MouseDown)
|
2019-04-05 16:54:56 +03:00
|
|
|
wm.move_to_front_and_make_active(m_window);
|
2019-04-05 19:40:36 +03:00
|
|
|
|
|
|
|
for (auto& button : m_buttons) {
|
2019-07-24 10:04:57 +03:00
|
|
|
if (button.relative_rect().contains(event.position()))
|
|
|
|
return button.on_mouse_event(event.translated(-button.relative_rect().location()));
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
2020-02-06 22:03:37 +03:00
|
|
|
if (event.type() == Event::MouseDown) {
|
2021-02-16 18:10:39 +03:00
|
|
|
if ((m_window.type() == WindowType::Normal || m_window.type() == WindowType::ToolWindow) && event.button() == MouseButton::Right) {
|
2020-07-07 21:09:18 +03:00
|
|
|
auto default_action = m_window.is_maximized() ? WindowMenuDefaultAction::Restore : WindowMenuDefaultAction::Maximize;
|
|
|
|
m_window.popup_window_menu(event.position().translated(rect().location()), default_action);
|
2020-01-04 16:14:36 +03:00
|
|
|
return;
|
|
|
|
}
|
2020-03-30 18:00:23 +03:00
|
|
|
if (m_window.is_movable() && event.button() == MouseButton::Left)
|
2020-01-04 16:14:36 +03:00
|
|
|
wm.start_window_move(m_window, event.translated(rect().location()));
|
|
|
|
}
|
2019-04-07 00:20:06 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
if (m_window.is_resizable() && event.type() == Event::MouseMove && event.buttons() == 0) {
|
2019-04-07 00:20:06 +03:00
|
|
|
constexpr ResizeDirection direction_for_hot_area[3][3] = {
|
|
|
|
{ ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight },
|
|
|
|
{ ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right },
|
|
|
|
{ ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight },
|
|
|
|
};
|
2020-06-10 11:57:59 +03:00
|
|
|
Gfx::IntRect outer_rect = { {}, rect().size() };
|
2019-04-07 00:20:06 +03:00
|
|
|
ASSERT(outer_rect.contains(event.position()));
|
|
|
|
int window_relative_x = event.x() - outer_rect.x();
|
|
|
|
int window_relative_y = event.y() - outer_rect.y();
|
|
|
|
int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3));
|
|
|
|
int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3));
|
|
|
|
wm.set_resize_candidate(m_window, direction_for_hot_area[hot_area_row][hot_area_column]);
|
2020-02-06 22:03:37 +03:00
|
|
|
Compositor::the().invalidate_cursor();
|
2019-04-07 00:20:06 +03:00
|
|
|
return;
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
2019-04-07 00:20:06 +03:00
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
if (m_window.is_resizable() && event.type() == Event::MouseDown && event.button() == MouseButton::Left)
|
2019-04-07 00:20:06 +03:00
|
|
|
wm.start_window_resize(m_window, event.translated(rect().location()));
|
2019-04-05 16:54:56 +03:00
|
|
|
}
|
2021-01-01 03:10:53 +03:00
|
|
|
|
|
|
|
void WindowFrame::start_flash_animation()
|
|
|
|
{
|
|
|
|
if (!m_flash_timer) {
|
|
|
|
m_flash_timer = Core::Timer::construct(100, [this] {
|
|
|
|
ASSERT(m_flash_counter);
|
|
|
|
invalidate_title_bar();
|
|
|
|
if (!--m_flash_counter)
|
|
|
|
m_flash_timer->stop();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
m_flash_counter = 8;
|
|
|
|
m_flash_timer->start();
|
|
|
|
}
|
|
|
|
|
2021-02-09 03:27:51 +03:00
|
|
|
void WindowFrame::paint_simple_rect_shadow(Gfx::Painter& painter, const Gfx::IntRect& containing_rect, const Gfx::Bitmap& shadow_bitmap) const
|
|
|
|
{
|
|
|
|
// The layout of the shadow_bitmap is defined like this:
|
|
|
|
// +---------+----+---------+----+----+----+
|
|
|
|
// | TL | T | TR | LT | L | LB |
|
|
|
|
// +---------+----+---------+----+----+----+
|
|
|
|
// | BL | B | BR | RT | R | RB |
|
|
|
|
// +---------+----+---------+----+----+----+
|
|
|
|
// Located strictly on the top or bottom of the rectangle, above or below of the content:
|
|
|
|
// TL = top-left T = top TR = top-right
|
|
|
|
// BL = bottom-left B = bottom BR = bottom-right
|
|
|
|
// Located on the left or right of the rectangle, but not above or below of the content:
|
|
|
|
// LT = left-top L = left LB = left-bottom
|
|
|
|
// RT = right-top R = right RB = right-bottom
|
|
|
|
// So, the bitmap has two rows and 6 column, two of which are twice as wide.
|
|
|
|
// The height divided by two defines a cell size, and width of each
|
|
|
|
// column must be the same as the height of the cell, except for the
|
|
|
|
// first an third column, which are twice as wide.
|
|
|
|
if (shadow_bitmap.height() % 2 != 0) {
|
|
|
|
dbgln("Can't paint simple rect shadow, shadow bitmap height {} is not even", shadow_bitmap.height());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto base_size = shadow_bitmap.height() / 2;
|
|
|
|
if (shadow_bitmap.width() != base_size * (6 + 2)) {
|
|
|
|
if (shadow_bitmap.width() % base_size != 0)
|
|
|
|
dbgln("Can't paint simple rect shadow, shadow bitmap width {} is not a multiple of {}", shadow_bitmap.width(), base_size);
|
|
|
|
else
|
|
|
|
dbgln("Can't paint simple rect shadow, shadow bitmap width {} but expected {}", shadow_bitmap.width(), base_size * (6 + 2));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The containing_rect should have been inflated appropriately
|
|
|
|
ASSERT(containing_rect.size().contains(Gfx::IntSize { base_size, base_size }));
|
|
|
|
|
|
|
|
auto half_width = containing_rect.width() / 2;
|
|
|
|
auto paint_horizontal = [&](int y, int src_row) {
|
|
|
|
Gfx::PainterStateSaver save(painter);
|
|
|
|
painter.add_clip_rect({ containing_rect.left(), y, containing_rect.width(), base_size });
|
|
|
|
int corner_piece_width = base_size * 2;
|
|
|
|
int left_corners_right = min(half_width, corner_piece_width);
|
|
|
|
int right_corners_left = max(half_width, containing_rect.width() - corner_piece_width);
|
|
|
|
painter.blit({ containing_rect.left() + left_corners_right - corner_piece_width, y }, shadow_bitmap, { 0, src_row * base_size, corner_piece_width, base_size });
|
|
|
|
painter.blit({ containing_rect.left() + right_corners_left, y }, shadow_bitmap, { corner_piece_width + base_size, src_row * base_size, corner_piece_width, base_size });
|
|
|
|
for (int x = left_corners_right; x < right_corners_left; x += base_size) {
|
|
|
|
auto width = min(right_corners_left - x, base_size);
|
|
|
|
painter.blit({ containing_rect.left() + x, y }, shadow_bitmap, { corner_piece_width, src_row * base_size, width, base_size });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
paint_horizontal(containing_rect.top(), 0);
|
|
|
|
paint_horizontal(containing_rect.bottom() - base_size + 1, 1);
|
|
|
|
|
|
|
|
auto sides_height = containing_rect.height() - 2 * base_size;
|
|
|
|
auto half_height = sides_height / 2;
|
|
|
|
auto paint_vertical = [&](int x, int src_row) {
|
|
|
|
Gfx::PainterStateSaver save(painter);
|
|
|
|
painter.add_clip_rect({ x, containing_rect.y() + base_size, base_size, containing_rect.height() - 2 * base_size });
|
|
|
|
int top_corners_bottom = base_size + min(half_height, base_size);
|
|
|
|
int top_corner_height = top_corners_bottom - base_size;
|
|
|
|
int bottom_corners_top = base_size + max(half_height, sides_height - base_size);
|
|
|
|
int bottom_corner_height = sides_height + base_size - bottom_corners_top;
|
|
|
|
painter.blit({ x, containing_rect.top() + top_corners_bottom - top_corner_height }, shadow_bitmap, { base_size * 5, src_row * base_size, base_size, top_corner_height });
|
|
|
|
painter.blit({ x, containing_rect.top() + bottom_corners_top }, shadow_bitmap, { base_size * 7, src_row * base_size + base_size - bottom_corner_height, base_size, bottom_corner_height });
|
|
|
|
if (sides_height > 2 * base_size) {
|
|
|
|
for (int y = top_corners_bottom; y < bottom_corners_top; y += base_size) {
|
|
|
|
auto height = min(bottom_corners_top - y, base_size);
|
|
|
|
painter.blit({ x, containing_rect.top() + y }, shadow_bitmap, { base_size * 6, src_row * base_size, base_size, height });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
paint_vertical(containing_rect.left(), 0);
|
|
|
|
paint_vertical(containing_rect.right() - base_size + 1, 1);
|
|
|
|
}
|
|
|
|
|
2020-02-06 22:03:37 +03:00
|
|
|
}
|