WindowServer: Implement tile window overlay

This adds a tiling mode that will show a tile window overlay rather
than immediately tiling a window, triggering window resizes.
This commit is contained in:
Tom 2023-04-03 21:39:17 -06:00 committed by Linus Groh
parent 035b0f9df6
commit fe54a0ca27
Notes: sideshowbarker 2024-07-16 21:30:48 +09:00
15 changed files with 279 additions and 71 deletions

View file

@ -121,6 +121,22 @@
}
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {}
@GUI::Label {
text: "Tile Window Behavior:"
autosize: true
}
@GUI::Layout::Spacer {}
@GUI::ComboBox {
name: "tile_window_combobox"
fixed_width: 130
}
}
@GUI::Widget {
fixed_height: 4
}

View file

@ -34,6 +34,13 @@ ErrorOr<void> EffectsSettingsWidget::setup_interface()
set_modified(true);
};
m_tile_window_combobox = find_descendant_of_type_named<ComboBox>("tile_window_combobox");
m_tile_window_combobox->set_only_allow_values_from_model(true);
m_tile_window_combobox->on_change = [this](auto&, auto&) {
m_system_effects.set_tile_window(static_cast<WindowServer::TileWindow>(m_tile_window_combobox->selected_index()));
set_modified(true);
};
if (auto result = load_settings(); result.is_error()) {
warnln("Failed to load [Effects] from WindowServer.ini");
return {};
@ -119,25 +126,36 @@ ErrorOr<void> EffectsSettingsWidget::load_settings()
ws_config->read_bool_entry("Effects", "TooltipShadow", true),
};
auto geometry = WindowServer::ShowGeometryTools::string_to_enum(ws_config->read_entry("Effects", "ShowGeometry", "OnMoveAndResize"));
m_system_effects = { effects, geometry };
auto tile_window = WindowServer::TileWindowTools::string_to_enum(ws_config->read_entry("Effects", "TileWindow", "ShowTileOverlay"));
m_system_effects = { effects, geometry, tile_window };
static constexpr Array list = {
static constexpr Array geometry_list = {
"On Move and Resize"sv,
"On Move only"sv,
"On Resize only"sv,
"Never"sv
};
for (size_t i = 0; i < list.size(); ++i)
TRY(m_geometry_list.try_append(TRY(String::from_utf8(list[i]))));
for (size_t i = 0; i < geometry_list.size(); ++i)
TRY(m_geometry_list.try_append(TRY(String::from_utf8(geometry_list[i]))));
m_geometry_combobox->set_model(ItemListModel<String>::create(m_geometry_list));
m_geometry_combobox->set_selected_index(to_underlying(m_system_effects.geometry()));
static constexpr Array tile_window_list = {
"Tile immediately"sv,
"Show tile overlay"sv,
"Never"sv
};
for (size_t i = 0; i < tile_window_list.size(); ++i)
TRY(m_tile_window_list.try_append(TRY(String::from_utf8(tile_window_list[i]))));
m_tile_window_combobox->set_model(ItemListModel<String>::create(m_tile_window_list));
m_tile_window_combobox->set_selected_index(static_cast<size_t>(m_system_effects.tile_window()));
return {};
}
void EffectsSettingsWidget::apply_settings()
{
ConnectionToWindowServer::the().async_set_system_effects(m_system_effects.effects(), to_underlying(m_system_effects.geometry()));
ConnectionToWindowServer::the().async_set_system_effects(m_system_effects.effects(), to_underlying(m_system_effects.geometry()), to_underlying(m_system_effects.tile_window()));
}
}

View file

@ -32,7 +32,9 @@ private:
SystemEffects m_system_effects;
Vector<String> m_geometry_list;
Vector<String> m_tile_window_list;
RefPtr<ComboBox> m_geometry_combobox;
RefPtr<ComboBox> m_tile_window_combobox;
};
}

View file

@ -980,9 +980,9 @@ Messages::WindowServer::SetSystemFontsResponse ConnectionFromClient::set_system_
return !g_config->sync().is_error();
}
void ConnectionFromClient::set_system_effects(Vector<bool> const& effects, u8 geometry)
void ConnectionFromClient::set_system_effects(Vector<bool> const& effects, u8 geometry, u8 tile_window)
{
WindowManager::the().apply_system_effects(effects, static_cast<ShowGeometry>(geometry));
WindowManager::the().apply_system_effects(effects, static_cast<ShowGeometry>(geometry), static_cast<TileWindow>(tile_window));
ConnectionFromClient::for_each_client([&](auto& client) {
client.async_update_system_effects(effects);
});

View file

@ -160,7 +160,7 @@ private:
virtual Messages::WindowServer::GetCursorHighlightColorResponse get_cursor_highlight_color() override;
virtual Messages::WindowServer::GetCursorThemeResponse get_cursor_theme() override;
virtual Messages::WindowServer::SetSystemFontsResponse set_system_fonts(DeprecatedString const&, DeprecatedString const&, DeprecatedString const&) override;
virtual void set_system_effects(Vector<bool> const&, u8) override;
virtual void set_system_effects(Vector<bool> const&, u8, u8) override;
virtual void set_window_base_size_and_size_increment(i32, Gfx::IntSize, Gfx::IntSize) override;
virtual void set_window_resize_aspect_ratio(i32, Optional<Gfx::IntSize> const&) override;
virtual void enable_display_link() override;

View file

@ -351,4 +351,18 @@ WindowStackSwitchOverlay::WindowStackSwitchOverlay(Screen& screen, WindowStack&
set_rect(calculate_frame_rect(Gfx::IntRect({}, m_content_size).inflated(2 * default_screen_rect_margin, 2 * default_screen_rect_margin)).centered_within(screen.rect()));
}
TileWindowOverlay::TileWindowOverlay(Window& window, Gfx::IntRect const& tiled_frame_rect, Gfx::Palette&& palette)
: m_window(window)
, m_tiled_frame_rect(tiled_frame_rect)
, m_palette(std::move(palette))
{
}
void TileWindowOverlay::render(Gfx::Painter& painter, Screen const&)
{
Gfx::IntRect paint_rect { {}, rect().size() };
painter.fill_rect(paint_rect, m_palette.rubber_band_fill());
painter.draw_rect(paint_rect, m_palette.rubber_band_border());
}
}

View file

@ -11,6 +11,7 @@
#include <AK/Vector.h>
#include <AK/WeakPtr.h>
#include <LibGfx/Painter.h>
#include <LibGfx/Palette.h>
#include <WindowServer/MultiScaleBitmaps.h>
#include <WindowServer/Screen.h>
@ -27,6 +28,7 @@ public:
virtual ~Overlay();
enum class ZOrder {
SnapWindow,
WindowGeometry,
Dnd,
WindowStackSwitch,
@ -192,4 +194,26 @@ private:
int const m_target_column;
};
class TileWindowOverlay : public Overlay {
public:
TileWindowOverlay(Window&, Gfx::IntRect const&, Gfx::Palette&&);
virtual ZOrder zorder() const override { return ZOrder::SnapWindow; }
virtual void render(Gfx::Painter&, Screen const&) override;
void set_overlay_rect(Gfx::IntRect const& rect)
{
set_rect(rect);
}
void set_tiled_frame_rect(Gfx::IntRect const& rect) { m_tiled_frame_rect = rect; }
Gfx::IntRect const& tiled_frame_rect() const { return m_tiled_frame_rect; }
bool is_window(Window& window) const { return &m_window == &window; }
private:
Window& m_window;
Gfx::IntRect m_tiled_frame_rect;
Gfx::Palette m_palette;
};
}

View file

@ -12,6 +12,12 @@
namespace WindowServer {
enum class TileWindow : u8 {
TileImmediately,
ShowTileOverlay,
Never
};
enum class ShowGeometry : u8 {
OnMoveAndResize,
OnMoveOnly,
@ -66,12 +72,42 @@ namespace ShowGeometryTools {
};
namespace TileWindowTools {
[[maybe_unused]] static StringView enum_to_string(TileWindow tile_window)
{
switch (tile_window) {
case TileWindow::Never:
return "Never"sv;
case TileWindow::TileImmediately:
return "TileImmediately"sv;
case TileWindow::ShowTileOverlay:
return "ShowTileOverlay"sv;
default:
VERIFY_NOT_REACHED();
}
}
[[maybe_unused]] static TileWindow string_to_enum(StringView tile_window)
{
if (tile_window == "Never"sv)
return TileWindow::Never;
else if (tile_window == "TileImmediately"sv)
return TileWindow::TileImmediately;
else if (tile_window == "ShowTileOverlay"sv)
return TileWindow::ShowTileOverlay;
VERIFY_NOT_REACHED();
}
};
class SystemEffects {
public:
SystemEffects() = default;
SystemEffects(Vector<bool> effects, ShowGeometry show)
SystemEffects(Vector<bool> effects, ShowGeometry show, TileWindow tile_window)
: m_effects(effects)
, m_geometry(show)
, m_tile_window(tile_window)
{
}
SystemEffects(Vector<bool> effects)
@ -107,14 +143,18 @@ public:
void set_geometry(ShowGeometry g) { m_geometry = g; }
ShowGeometry geometry() const { return m_geometry; }
void set_tile_window(TileWindow tile_window) { m_tile_window = tile_window; }
TileWindow tile_window() const { return m_tile_window; }
bool operator==(SystemEffects const& other) const
{
return m_effects == other.m_effects && m_geometry == other.m_geometry;
return m_effects == other.m_effects && m_geometry == other.m_geometry && m_tile_window == other.m_tile_window;
}
private:
Vector<bool> m_effects;
ShowGeometry m_geometry { ShowGeometry::Never };
TileWindow m_tile_window { TileWindow::ShowTileOverlay };
};
}

View file

@ -420,8 +420,8 @@ void Window::set_maximized(bool maximized)
else
set_rect(m_floating_rect);
m_frame.did_set_maximized({}, maximized);
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
Core::EventLoop::current().post_event(*this, make<MoveEvent>(m_rect));
send_resize_event_to_client();
send_move_event_to_client();
set_default_positioned(false);
WindowManager::the().notify_minimization_state_changed(*this);
@ -842,8 +842,8 @@ void Window::set_fullscreen(bool fullscreen)
new_window_rect = m_saved_nonfullscreen_rect;
}
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(new_window_rect));
Core::EventLoop::current().post_event(*this, make<MoveEvent>(new_window_rect));
send_resize_event_to_client();
send_move_event_to_client();
set_rect(new_window_rect);
}
@ -858,7 +858,7 @@ WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const
bool tiling_to_left = current_tile_type == WindowTileType::Left || current_tile_type == WindowTileType::TopLeft || current_tile_type == WindowTileType::BottomLeft;
bool tiling_to_right = current_tile_type == WindowTileType::Right || current_tile_type == WindowTileType::TopRight || current_tile_type == WindowTileType::BottomRight;
auto ideal_tiled_rect = WindowManager::the().tiled_window_rect(*this, current_tile_type);
auto ideal_tiled_rect = WindowManager::the().tiled_window_rect(*this, window_screen, current_tile_type);
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();
@ -870,9 +870,9 @@ WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const
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() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Bottom).bottom() ? WindowTileType::Left : WindowTileType::TopLeft;
return rect.bottom() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Bottom).bottom() ? WindowTileType::Left : WindowTileType::TopLeft;
else if ((tiling_to_top || tiling_to_right) && same_top && same_right)
return rect.bottom() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Bottom).bottom() ? WindowTileType::Right : WindowTileType::TopRight;
return rect.bottom() == WindowManager::the().tiled_window_rect(*this, 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)
@ -880,9 +880,9 @@ WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const
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() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Left).top() ? WindowTileType::Left : WindowTileType::BottomLeft;
return rect.top() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Left).top() ? WindowTileType::Left : WindowTileType::BottomLeft;
else if ((tiling_to_bottom || tiling_to_right) && same_bottom && same_right)
return rect.top() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Right).top() ? WindowTileType::Right : WindowTileType::BottomRight;
return rect.top() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Right).top() ? WindowTileType::Right : WindowTileType::BottomRight;
}
return tile_type;
}
@ -911,15 +911,11 @@ bool Window::set_untiled()
VERIFY(!resize_aspect_ratio().has_value());
m_tile_type = WindowTileType::None;
set_rect(m_floating_rect);
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
Core::EventLoop::current().post_event(*this, make<MoveEvent>(m_rect));
tile_type_changed();
return true;
}
void Window::set_tiled(WindowTileType tile_type)
void Window::set_tiled(WindowTileType tile_type, Optional<Screen const&> tile_on_screen)
{
VERIFY(tile_type != WindowTileType::None);
@ -933,9 +929,26 @@ void Window::set_tiled(WindowTileType tile_type)
set_maximized(false);
m_tile_type = tile_type;
tile_type_changed(tile_on_screen);
}
set_rect(WindowManager::the().tiled_window_rect(*this, tile_type));
void Window::tile_type_changed(Optional<Screen const&> tile_on_screen)
{
if (m_tile_type != WindowTileType::None)
set_rect(WindowManager::the().tiled_window_rect(*this, tile_on_screen, m_tile_type));
else
set_rect(m_floating_rect);
send_resize_event_to_client();
send_move_event_to_client();
}
void Window::send_resize_event_to_client()
{
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
}
void Window::send_move_event_to_client()
{
Core::EventLoop::current().post_event(*this, make<MoveEvent>(m_rect));
}
@ -951,7 +964,7 @@ void Window::recalculate_rect()
bool send_event = true;
if (is_tiled()) {
set_rect(WindowManager::the().tiled_window_rect(*this, m_tile_type));
set_rect(WindowManager::the().tiled_window_rect(*this, {}, m_tile_type));
} else if (type() == WindowType::Desktop) {
set_rect(WindowManager::the().arena_rect_for_type(Screen::main(), WindowType::Desktop));
} else {
@ -959,7 +972,7 @@ void Window::recalculate_rect()
}
if (send_event) {
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
send_resize_event_to_client();
}
}

View file

@ -120,7 +120,7 @@ public:
WindowTileType tile_type() const { return m_tile_type; }
bool is_tiled() const { return m_tile_type != WindowTileType::None; }
void set_tiled(WindowTileType);
void set_tiled(WindowTileType, Optional<Screen const&> = {});
WindowTileType tile_type_based_on_rect(Gfx::IntRect const&) const;
void check_untile_due_to_resize(Gfx::IntRect const&);
bool set_untiled();
@ -376,6 +376,9 @@ public:
void remove_all_stealing() { m_stealable_by_client_ids.clear(); }
bool is_stealable_by_client(i32 client_id) const { return m_stealable_by_client_ids.contains_slow(client_id); }
void send_resize_event_to_client();
void send_move_event_to_client();
private:
Window(ConnectionFromClient&, WindowType, WindowMode, int window_id, bool minimizable, bool closeable, bool frameless, bool resizable, bool fullscreen, Window* parent_window = nullptr);
Window(Core::Object&, WindowType);
@ -386,6 +389,7 @@ private:
void add_child_window(Window&);
void ensure_window_menu();
void update_window_menu_items();
void tile_type_changed(Optional<Screen const&> = {});
ErrorOr<Optional<DeprecatedString>> compute_title_username(ConnectionFromClient* client);
ConnectionFromClient* m_client { nullptr };

View file

@ -60,7 +60,7 @@ static DeprecatedString s_last_menu_shadow_path;
static DeprecatedString s_last_taskbar_shadow_path;
static DeprecatedString s_last_tooltip_shadow_path;
static Gfx::IntRect frame_rect_for_window(Window& window, Gfx::IntRect const& rect)
Gfx::IntRect WindowFrame::frame_rect_for_window(Window& window, Gfx::IntRect const& rect)
{
if (window.is_frameless())
return rect;

View file

@ -43,6 +43,8 @@ public:
};
friend class RenderedCache;
static Gfx::IntRect frame_rect_for_window(Window&, Gfx::IntRect const&);
static void reload_config();
explicit WindowFrame(Window&);

View file

@ -19,6 +19,7 @@
#include <LibGfx/StylePainter.h>
#include <LibGfx/SystemTheme.h>
#include <Services/Taskbar/TaskbarWindow.h>
#include <WindowServer/Animation.h>
#include <WindowServer/AppletManager.h>
#include <WindowServer/Button.h>
#include <WindowServer/ConnectionFromClient.h>
@ -335,8 +336,8 @@ void WindowManager::add_window(Window& window)
if (window.is_fullscreen()) {
auto& screen = Screen::main(); // TODO: support fullscreen windows on other screens!
Core::EventLoop::current().post_event(window, make<ResizeEvent>(screen.rect()));
window.set_rect(screen.rect());
window.send_resize_event_to_client();
}
if (window.type() != WindowType::Desktop || is_first_window)
@ -415,6 +416,9 @@ void WindowManager::remove_window(Window& window)
conn.async_window_removed(conn.window_id(), window.client_id(), window.window_id());
return IterationDecision::Continue;
});
if (m_tile_window_overlay && m_tile_window_overlay->is_window(window))
stop_tile_window_animation();
}
void WindowManager::greet_window_manager(WMConnectionFromClient& conn)
@ -738,6 +742,44 @@ void WindowManager::start_window_resize(Window& window, MouseEvent const& event,
start_window_resize(window, event.position(), event.button(), resize_direction);
}
void WindowManager::start_tile_window_animation(Gfx::IntRect const& starting_rect)
{
m_tile_window_overlay_animation = Animation::create();
m_tile_window_overlay_animation->set_duration(150);
m_tile_window_overlay_animation->on_update = [this, starting_rect](float progress, Gfx::Painter&, Screen&, Gfx::DisjointIntRectSet&) {
if (m_tile_window_overlay) {
auto target_rect = starting_rect.interpolated_to(m_tile_window_overlay->tiled_frame_rect(), progress);
m_tile_window_overlay->set_overlay_rect(target_rect);
}
};
m_tile_window_overlay_animation->start();
}
void WindowManager::stop_tile_window_animation()
{
m_tile_window_overlay = nullptr;
m_tile_window_overlay_animation = nullptr;
}
void WindowManager::show_tile_window_overlay(Window& window, Screen const& cursor_screen, WindowTileType tile_type)
{
m_move_window_suggested_tile = tile_type;
if (tile_type != WindowTileType::None && (!m_tile_window_overlay || !m_tile_window_overlay->is_window(window))) {
auto tiled_frame_rect = WindowFrame::frame_rect_for_window(window, tiled_window_rect(window, cursor_screen, tile_type));
m_tile_window_overlay = Compositor::the().create_overlay<TileWindowOverlay>(window, tiled_frame_rect, palette());
m_tile_window_overlay->set_enabled(true);
start_tile_window_animation(window.frame().rect());
} else if (tile_type == WindowTileType::None) {
stop_tile_window_animation();
} else {
auto tiled_frame_rect = WindowFrame::frame_rect_for_window(window, tiled_window_rect(window, cursor_screen, tile_type));
if (m_tile_window_overlay->tiled_frame_rect() != tiled_frame_rect) {
m_tile_window_overlay->set_tiled_frame_rect(tiled_frame_rect);
start_tile_window_animation(m_tile_window_overlay->rect());
}
}
}
bool WindowManager::process_ongoing_window_move(MouseEvent& event)
{
if (!m_move_window)
@ -746,10 +788,21 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
dbgln_if(MOVE_DEBUG, "[WM] Finish moving Window({})", m_move_window);
if (!m_move_window->is_tiled())
bool did_tile = false;
if (m_move_window_suggested_tile != WindowTileType::None && m_system_effects.tile_window() == TileWindow::ShowTileOverlay) {
auto& cursor_screen = Screen::closest_to_location(event.position());
m_move_window->set_tiled(m_move_window_suggested_tile, cursor_screen);
m_move_window_suggested_tile = WindowTileType::None;
stop_tile_window_animation();
did_tile = true;
} else {
did_tile = m_move_window->is_tiled();
}
if (!did_tile)
m_move_window->set_floating_rect(m_move_window->rect());
Core::EventLoop::current().post_event(*m_move_window, make<MoveEvent>(m_move_window->rect()));
m_move_window->send_move_event_to_client();
m_move_window->invalidate(true, true);
if (m_move_window->is_resizable()) {
process_event_for_doubleclick(*m_move_window, event);
@ -788,30 +841,44 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
}
} else {
bool is_resizable = m_move_window->is_resizable();
auto tile_window = m_system_effects.tile_window();
bool allow_tile = is_resizable && tile_window != TileWindow::Never;
auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
auto event_location_relative_to_screen = event.position().translated(-cursor_screen.rect().location());
if (is_resizable && event_location_relative_to_screen.x() <= tiling_deadzone) {
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop_relative_to_screen.top())
m_move_window->set_tiled(WindowTileType::TopLeft);
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
m_move_window->set_tiled(WindowTileType::BottomLeft);
else
m_move_window->set_tiled(WindowTileType::Left);
} else if (is_resizable && event_location_relative_to_screen.x() >= cursor_screen.width() - tiling_deadzone) {
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop.top())
m_move_window->set_tiled(WindowTileType::TopRight);
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
m_move_window->set_tiled(WindowTileType::BottomRight);
else
m_move_window->set_tiled(WindowTileType::Right);
} else if (is_resizable && event_location_relative_to_screen.y() <= secondary_deadzone + desktop_relative_to_screen.top()) {
m_move_window->set_tiled(WindowTileType::Top);
} else if (is_resizable && event_location_relative_to_screen.y() >= desktop_relative_to_screen.bottom() - secondary_deadzone) {
m_move_window->set_tiled(WindowTileType::Bottom);
} else if (!m_move_window->is_tiled()) {
Gfx::IntPoint pos = m_move_window_origin.translated(event.position() - m_move_origin);
auto apply_window_tile = [&](WindowTileType tile_type) {
if (tile_window == TileWindow::ShowTileOverlay) {
show_tile_window_overlay(*m_move_window, cursor_screen, tile_type);
} else if (tile_window == TileWindow::TileImmediately) {
if (tile_type != WindowTileType::None) {
m_move_window->set_tiled(tile_type, cursor_screen);
return;
}
}
auto pos = m_move_window_origin.translated(event.position() - m_move_origin);
m_move_window->set_position_without_repaint(pos);
};
auto event_location_relative_to_screen = event.position().translated(-cursor_screen.rect().location());
if (allow_tile && event_location_relative_to_screen.x() <= tiling_deadzone) {
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop_relative_to_screen.top())
apply_window_tile(WindowTileType::TopLeft);
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
apply_window_tile(WindowTileType::BottomLeft);
else
apply_window_tile(WindowTileType::Left);
} else if (allow_tile && event_location_relative_to_screen.x() >= cursor_screen.width() - tiling_deadzone) {
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop.top())
apply_window_tile(WindowTileType::TopRight);
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
apply_window_tile(WindowTileType::BottomRight);
else
apply_window_tile(WindowTileType::Right);
} else if (allow_tile && event_location_relative_to_screen.y() <= secondary_deadzone + desktop_relative_to_screen.top()) {
apply_window_tile(WindowTileType::Top);
} else if (allow_tile && event_location_relative_to_screen.y() >= desktop_relative_to_screen.bottom() - secondary_deadzone) {
apply_window_tile(WindowTileType::Bottom);
} else if (!m_move_window->is_tiled()) {
apply_window_tile(WindowTileType::None);
} else if (pixels_moved_from_start > 5) {
Gfx::IntPoint adjusted_position = event.position().translated(-m_move_window_cursor_position);
m_move_window->set_untiled();
@ -824,7 +891,7 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
m_geometry_overlay->window_rect_changed();
}
}
Core::EventLoop::current().post_event(*m_move_window, make<MoveEvent>(m_move_window->rect()));
m_move_window->send_move_event_to_client();
return true;
}
@ -875,7 +942,7 @@ bool WindowManager::process_ongoing_window_resize(MouseEvent const& event)
if (!m_resize_window->is_tiled())
m_resize_window->set_floating_rect(m_resize_window->rect());
Core::EventLoop::current().post_event(*m_resize_window, make<ResizeEvent>(m_resize_window->rect()));
m_resize_window->send_resize_event_to_client();
m_resize_window->invalidate(true, true);
m_resize_window = nullptr;
m_geometry_overlay = nullptr;
@ -1013,7 +1080,7 @@ bool WindowManager::process_ongoing_window_resize(MouseEvent const& event)
if (system_effects().geometry() == ShowGeometry::OnMoveAndResize || system_effects().geometry() == ShowGeometry::OnResizeOnly) {
m_geometry_overlay->window_rect_changed();
}
Core::EventLoop::current().post_event(*m_resize_window, make<ResizeEvent>(new_rect));
m_resize_window->send_resize_event_to_client();
return true;
}
@ -1424,7 +1491,7 @@ void WindowManager::clear_resize_candidate()
m_resize_candidate = nullptr;
}
Gfx::IntRect WindowManager::desktop_rect(Screen& screen) const
Gfx::IntRect WindowManager::desktop_rect(Screen const& screen) const
{
if (active_fullscreen_window())
return Screen::main().rect(); // TODO: we should support fullscreen windows on any screen
@ -1965,11 +2032,11 @@ ResizeDirection WindowManager::resize_direction_of_window(Window const& window)
return m_resize_direction;
}
Gfx::IntRect WindowManager::tiled_window_rect(Window const& window, WindowTileType tile_type, bool relative_to_window_screen) const
Gfx::IntRect WindowManager::tiled_window_rect(Window const& window, Optional<Screen const&> cursor_screen, WindowTileType tile_type) const
{
VERIFY(tile_type != WindowTileType::None);
auto& screen = Screen::closest_to_rect(window.frame().rect());
auto const& screen = cursor_screen.has_value() ? cursor_screen.value() : Screen::closest_to_rect(window.frame().rect());
auto rect = desktop_rect(screen);
if (tile_type == WindowTileType::Maximized) {
@ -2022,9 +2089,6 @@ Gfx::IntRect WindowManager::tiled_window_rect(Window const& window, WindowTileTy
rect.set_y(rect.y() + window_rect.y() - window_frame_rect.y());
rect.set_height(rect.height() - window_frame_rect.height() + window_rect.height());
}
if (relative_to_window_screen)
rect.translate_by(-screen.rect().location());
return rect;
}
@ -2312,12 +2376,12 @@ void WindowManager::set_cursor_highlight_color(Gfx::Color color)
sync_config_to_disk();
}
void WindowManager::apply_system_effects(Vector<bool> effects, ShowGeometry geometry)
void WindowManager::apply_system_effects(Vector<bool> effects, ShowGeometry geometry, TileWindow tile_window)
{
if (m_system_effects == SystemEffects { effects, geometry })
if (m_system_effects == SystemEffects { effects, geometry, tile_window })
return;
m_system_effects = { effects, geometry };
m_system_effects = { effects, geometry, tile_window };
g_config->write_bool_entry("Effects", "AnimateMenus", m_system_effects.animate_menus());
g_config->write_bool_entry("Effects", "FlashMenus", m_system_effects.flash_menus());
g_config->write_bool_entry("Effects", "AnimateWindows", m_system_effects.animate_windows());
@ -2329,6 +2393,7 @@ void WindowManager::apply_system_effects(Vector<bool> effects, ShowGeometry geom
g_config->write_bool_entry("Effects", "WindowShadow", m_system_effects.window_shadow());
g_config->write_bool_entry("Effects", "TooltipShadow", m_system_effects.tooltip_shadow());
g_config->write_entry("Effects", "ShowGeometry", ShowGeometryTools::enum_to_string(geometry));
g_config->write_entry("Effects", "TileWindow", TileWindowTools::enum_to_string(tile_window));
sync_config_to_disk();
}
@ -2347,7 +2412,8 @@ void WindowManager::load_system_effects()
g_config->read_bool_entry("Effects", "TooltipShadow", true)
};
ShowGeometry geometry = ShowGeometryTools::string_to_enum(g_config->read_entry("Effects", "ShowGeometry", "OnMoveAndResize"));
m_system_effects = { effects, geometry };
TileWindow tile_window = TileWindowTools::string_to_enum(g_config->read_entry("Effects", "TileWindow", "ShowTileOverlay"));
m_system_effects = { effects, geometry, tile_window };
ConnectionFromClient::for_each_client([&](auto& client) {
client.async_update_system_effects(effects);

View file

@ -43,6 +43,7 @@ class WindowSwitcher;
class Button;
class DndOverlay;
class WindowGeometryOverlay;
class TileWindowOverlay;
class WindowManager : public Core::Object {
C_OBJECT(WindowManager)
@ -79,7 +80,7 @@ public:
void notify_progress_changed(Window&);
void notify_modified_changed(Window&);
Gfx::IntRect tiled_window_rect(Window const&, WindowTileType tile_type = WindowTileType::Maximized, bool relative_to_window_screen = false) const;
Gfx::IntRect tiled_window_rect(Window const&, Optional<Screen const&> = {}, WindowTileType tile_type = WindowTileType::Maximized) const;
ConnectionFromClient const* dnd_client() const { return m_dnd_client.ptr(); }
Core::MimeData const& dnd_mime_data() const { return *m_dnd_mime_data; }
@ -115,7 +116,7 @@ public:
void move_to_front_and_make_active(Window&);
Gfx::IntRect desktop_rect(Screen&) const;
Gfx::IntRect desktop_rect(Screen const&) const;
Gfx::IntRect arena_rect_for_type(Screen&, WindowType) const;
Cursor const& active_cursor() const;
@ -328,7 +329,7 @@ public:
bool is_cursor_highlight_enabled() const { return m_cursor_highlight_radius > 0 && m_cursor_highlight_enabled; }
void load_system_effects();
void apply_system_effects(Vector<bool>, ShowGeometry);
void apply_system_effects(Vector<bool>, ShowGeometry, TileWindow);
SystemEffects& system_effects() { return m_system_effects; }
RefPtr<KeymapSwitcher> keymap_switcher() { return m_keymap_switcher; }
@ -339,6 +340,9 @@ public:
u8 last_processed_buttons() { return m_last_processed_buttons; }
void start_tile_window_animation(Gfx::IntRect const&);
void stop_tile_window_animation();
private:
explicit WindowManager(Gfx::PaletteImpl&);
@ -433,6 +437,8 @@ private:
Gfx::IntPoint to_floating_cursor_position(Gfx::IntPoint) const;
void show_tile_window_overlay(Window&, Screen const&, WindowTileType);
DoubleClickInfo m_double_click_info;
int m_double_click_speed { 0 };
int m_max_distance_for_double_click { 4 };
@ -448,7 +454,10 @@ private:
WeakPtr<Window> m_automatic_cursor_tracking_window;
OwnPtr<WindowGeometryOverlay> m_geometry_overlay;
OwnPtr<TileWindowOverlay> m_tile_window_overlay;
RefPtr<Animation> m_tile_window_overlay_animation;
WeakPtr<Window> m_move_window;
WindowTileType m_move_window_suggested_tile { WindowTileType::None };
Gfx::IntPoint m_move_origin;
Gfx::IntPoint m_move_window_origin;
Gfx::IntPoint m_move_window_cursor_position;

View file

@ -152,7 +152,7 @@ endpoint WindowServer
get_cursor_highlight_color() => (Gfx::Color color)
set_system_fonts(DeprecatedString default_font_query, DeprecatedString fixed_width_font_query, DeprecatedString window_title_font_query) => (bool success)
set_system_effects(Vector<bool> effects, u8 geometry) =|
set_system_effects(Vector<bool> effects, u8 geometry, u8 tile_window) =|
set_window_base_size_and_size_increment(i32 window_id, Gfx::IntSize base_size, Gfx::IntSize size_increment) =|
set_window_resize_aspect_ratio(i32 window_id, Optional<Gfx::IntSize> resize_aspect_ratio) =|