WindowServer: Un-tile window if resizing warrants it

Since being tiled means we restrict rendering a window to the screen it
is on (so that we don't "bleed" into an adjacent screen), we need to
untile it if the window either can't fit into the screen, or it is
detached from the screen edges.
This commit is contained in:
Tom 2021-06-27 10:30:03 -06:00 committed by Andreas Kling
parent d3fc8652c7
commit 091628202f
Notes: sideshowbarker 2024-07-18 11:26:25 +09:00
3 changed files with 114 additions and 25 deletions

View File

@ -15,6 +15,7 @@
#include "WindowManager.h" #include "WindowManager.h"
#include <AK/Badge.h> #include <AK/Badge.h>
#include <AK/CharacterTypes.h> #include <AK/CharacterTypes.h>
#include <AK/Debug.h>
#include <WindowServer/WindowClientEndpoint.h> #include <WindowServer/WindowClientEndpoint.h>
namespace WindowServer { namespace WindowServer {
@ -851,53 +852,130 @@ Gfx::IntRect Window::tiled_rect(Screen* target_screen, WindowTileType tiled) con
screen.width() / 2 - frame_width, screen.width() / 2 - frame_width,
max_height) max_height)
.translated(screen_location); .translated(screen_location);
case WindowTileType::Right: case WindowTileType::Right: {
return Gfx::IntRect(screen.width() / 2 + frame_width, Gfx::IntPoint location {
menu_height, screen.width() / 2 + frame_width,
screen.width() / 2 - frame_width, menu_height
max_height) };
return Gfx::IntRect(
location,
{ screen.width() - location.x(), max_height })
.translated(screen_location); .translated(screen_location);
}
case WindowTileType::Top: case WindowTileType::Top:
return Gfx::IntRect(0, return Gfx::IntRect(0,
menu_height, menu_height,
screen.width(), screen.width(),
(max_height - titlebar_height) / 2 - frame_width) (max_height - titlebar_height) / 2 - frame_width)
.translated(screen_location); .translated(screen_location);
case WindowTileType::Bottom: case WindowTileType::Bottom: {
return Gfx::IntRect(0, Gfx::IntPoint location {
menu_height + (titlebar_height + max_height) / 2 + frame_width, 0,
screen.width(), menu_height + (titlebar_height + max_height) / 2 + frame_width
(max_height - titlebar_height) / 2 - frame_width) };
return Gfx::IntRect(
location,
{ screen.width(), screen.height() - location.y() })
.translated(screen_location); .translated(screen_location);
}
case WindowTileType::TopLeft: case WindowTileType::TopLeft:
return Gfx::IntRect(0, return Gfx::IntRect(0,
menu_height, menu_height,
screen.width() / 2 - frame_width, screen.width() / 2 - frame_width,
(max_height - titlebar_height) / 2 - frame_width) (max_height - titlebar_height) / 2 - frame_width)
.translated(screen_location); .translated(screen_location);
case WindowTileType::TopRight: case WindowTileType::TopRight: {
return Gfx::IntRect(screen.width() / 2 + frame_width, Gfx::IntPoint location {
menu_height, screen.width() / 2 + frame_width,
screen.width() / 2 - frame_width, menu_height
(max_height - titlebar_height) / 2 - frame_width) };
return Gfx::IntRect(
location,
{ screen.width() - location.x(), (max_height - titlebar_height) / 2 - frame_width })
.translated(screen_location); .translated(screen_location);
case WindowTileType::BottomLeft: }
return Gfx::IntRect(0, case WindowTileType::BottomLeft: {
menu_height + (titlebar_height + max_height) / 2 + frame_width, Gfx::IntPoint location {
screen.width() / 2 - frame_width, 0,
(max_height - titlebar_height) / 2 - frame_width) menu_height + (titlebar_height + max_height) / 2 + frame_width
};
return Gfx::IntRect(
location,
{ screen.width() / 2 - frame_width, screen.height() - location.y() })
.translated(screen_location); .translated(screen_location);
case WindowTileType::BottomRight: }
return Gfx::IntRect(screen.width() / 2 + frame_width, case WindowTileType::BottomRight: {
menu_height + (titlebar_height + max_height) / 2 + frame_width, Gfx::IntPoint location {
screen.width() / 2 - frame_width, screen.width() / 2 + frame_width,
(max_height - titlebar_height) / 2 - frame_width) menu_height + (titlebar_height + max_height) / 2 + frame_width
};
return Gfx::IntRect(
location,
{ screen.width() - location.x(), screen.height() - location.y() })
.translated(screen_location); .translated(screen_location);
}
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
} }
WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const
{
auto& window_screen = Screen::closest_to_rect(this->rect()); // based on currently used rect
auto tile_type = WindowTileType::None;
if (window_screen.rect().contains(rect)) {
auto current_tiled = tiled();
bool tiling_to_top = current_tiled == WindowTileType::Top || current_tiled == WindowTileType::TopLeft || current_tiled == WindowTileType::TopRight;
bool tiling_to_bottom = current_tiled == WindowTileType::Bottom || current_tiled == WindowTileType::BottomLeft || current_tiled == WindowTileType::BottomRight;
bool tiling_to_left = current_tiled == WindowTileType::Left || current_tiled == WindowTileType::TopLeft || current_tiled == WindowTileType::BottomLeft;
bool tiling_to_right = current_tiled == WindowTileType::Right || current_tiled == WindowTileType::TopRight || current_tiled == WindowTileType::BottomRight;
auto ideal_tiled_rect = tiled_rect(&window_screen, current_tiled);
bool same_top = ideal_tiled_rect.top() == rect.top();
bool same_left = ideal_tiled_rect.left() == rect.left();
bool same_right = ideal_tiled_rect.right() == rect.right();
bool same_bottom = ideal_tiled_rect.bottom() == rect.bottom();
// Try to find the most suitable tile type. For example, if a window is currently tiled to the BottomRight and
// the window is resized upwards as to where it perfectly touches the screen's top border, then the more suitable
// tile type would be Right, as three sides are lined up perfectly.
if (tiling_to_top && same_top && same_left && same_right)
return WindowTileType::Top;
else if ((tiling_to_top || tiling_to_left) && same_top && same_left)
return rect.bottom() == tiled_rect(&window_screen, WindowTileType::Bottom).bottom() ? WindowTileType::Left : WindowTileType::TopLeft;
else if ((tiling_to_top || tiling_to_right) && same_top && same_right)
return rect.bottom() == tiled_rect(&window_screen, WindowTileType::Bottom).bottom() ? WindowTileType::Right : WindowTileType::TopRight;
else if (tiling_to_left && same_left && same_top && same_bottom)
return WindowTileType::Left;
else if (tiling_to_right && same_right && same_top && same_bottom)
return WindowTileType::Right;
else if (tiling_to_bottom && same_bottom && same_left && same_right)
return WindowTileType::Bottom;
else if ((tiling_to_bottom || tiling_to_left) && same_bottom && same_left)
return rect.top() == tiled_rect(&window_screen, WindowTileType::Left).top() ? WindowTileType::Left : WindowTileType::BottomLeft;
else if ((tiling_to_bottom || tiling_to_right) && same_bottom && same_right)
return rect.top() == tiled_rect(&window_screen, WindowTileType::Right).top() ? WindowTileType::Right : WindowTileType::BottomRight;
}
return tile_type;
}
void Window::check_untile_due_to_resize(Gfx::IntRect const& new_rect)
{
auto new_tile_type = tile_type_based_on_rect(new_rect);
if constexpr (RESIZE_DEBUG) {
if (new_tile_type == WindowTileType::None) {
auto current_rect = rect();
auto& window_screen = Screen::closest_to_rect(current_rect);
if (!(window_screen.rect().contains(new_rect)))
dbgln("Untiling because new rect {} does not fit into screen #{} rect {}", new_rect, window_screen.index(), window_screen.rect());
else
dbgln("Untiling because new rect {} does not touch screen #{} rect {}", new_rect, window_screen.index(), window_screen.rect());
} else if (new_tile_type != m_tiled)
dbgln("Changing tile type from {} to {}", (int)m_tiled, (int)new_tile_type);
}
m_tiled = new_tile_type;
}
bool Window::set_untiled(Optional<Gfx::IntPoint> fixed_point) bool Window::set_untiled(Optional<Gfx::IntPoint> fixed_point)
{ {
if (m_tiled == WindowTileType::None) if (m_tiled == WindowTileType::None)

View File

@ -100,6 +100,8 @@ public:
WindowTileType tiled() const { return m_tiled; } WindowTileType tiled() const { return m_tiled; }
void set_tiled(Screen*, WindowTileType); void set_tiled(Screen*, WindowTileType);
WindowTileType tile_type_based_on_rect(Gfx::IntRect const&) const;
void check_untile_due_to_resize(Gfx::IntRect const&);
bool set_untiled(Optional<Gfx::IntPoint> fixed_point = {}); bool set_untiled(Optional<Gfx::IntPoint> fixed_point = {});
bool is_occluded() const { return m_occluded; } bool is_occluded() const { return m_occluded; }

View File

@ -715,6 +715,7 @@ bool WindowManager::process_ongoing_window_resize(MouseEvent const& event)
// First, size the new rect. // First, size the new rect.
new_rect.set_width(new_rect.width() + change_w); new_rect.set_width(new_rect.width() + change_w);
new_rect.set_height(new_rect.height() + change_h); new_rect.set_height(new_rect.height() + change_h);
m_resize_window->apply_minimum_size(new_rect); m_resize_window->apply_minimum_size(new_rect);
if (!m_resize_window->size_increment().is_null()) { if (!m_resize_window->size_increment().is_null()) {
@ -762,6 +763,14 @@ bool WindowManager::process_ongoing_window_resize(MouseEvent const& event)
if (m_resize_window->rect() == new_rect) if (m_resize_window->rect() == new_rect)
return true; return true;
if (m_resize_window->tiled() != WindowTileType::None) {
// Check if we should be un-tiling the window. This should happen when one side touching
// the screen border changes. We need to un-tile because while it is tiled, rendering is
// constrained to the screen where it's tiled on, and if one of these sides move we should
// no longer constrain rendering to that screen. Changing the sides not touching a screen
// border however is fine as long as the screen contains the entire window.
m_resize_window->check_untile_due_to_resize(new_rect);
}
dbgln_if(RESIZE_DEBUG, "[WM] Resizing, original: {}, now: {}", m_resize_window_original_rect, new_rect); dbgln_if(RESIZE_DEBUG, "[WM] Resizing, original: {}, now: {}", m_resize_window_original_rect, new_rect);
m_resize_window->set_rect(new_rect); m_resize_window->set_rect(new_rect);