Skip to content

Commit

Permalink
WindowServer: Implement tile window overlay
Browse files Browse the repository at this point in the history
This adds a tiling mode that will show a tile window overlay rather
than immediately tiling a window, triggering window resizes.
  • Loading branch information
tomuta authored and linusg committed Apr 13, 2023
1 parent 035b0f9 commit fe54a0c
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 64 deletions.
16 changes: 16 additions & 0 deletions Userland/Applications/DisplaySettings/EffectsSettings.gml
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
28 changes: 23 additions & 5 deletions Userland/Applications/DisplaySettings/EffectsSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
Expand Down Expand Up @@ -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()));
}

}
Expand Down
2 changes: 2 additions & 0 deletions Userland/Applications/DisplaySettings/EffectsSettingsWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class EffectsSettingsWidget final : public SettingsWindow::Tab {

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;
};

}
Expand Down
4 changes: 2 additions & 2 deletions Userland/Services/WindowServer/ConnectionFromClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
2 changes: 1 addition & 1 deletion Userland/Services/WindowServer/ConnectionFromClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class ConnectionFromClient final
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;
Expand Down
14 changes: 14 additions & 0 deletions Userland/Services/WindowServer/Overlays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

}
24 changes: 24 additions & 0 deletions Userland/Services/WindowServer/Overlays.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -27,6 +28,7 @@ class Overlay {
virtual ~Overlay();

enum class ZOrder {
SnapWindow,
WindowGeometry,
Dnd,
WindowStackSwitch,
Expand Down Expand Up @@ -192,4 +194,26 @@ class WindowStackSwitchOverlay : public RectangularOverlay {
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;
};

}
44 changes: 42 additions & 2 deletions Userland/Services/WindowServer/SystemEffects.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@

namespace WindowServer {

enum class TileWindow : u8 {
TileImmediately,
ShowTileOverlay,
Never
};

enum class ShowGeometry : u8 {
OnMoveAndResize,
OnMoveOnly,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -107,14 +143,18 @@ class SystemEffects {
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 };
};

}
49 changes: 31 additions & 18 deletions Userland/Services/WindowServer/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand All @@ -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();
Expand All @@ -870,19 +870,19 @@ 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)
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() == 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;
}
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}

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();
}

set_rect(WindowManager::the().tiled_window_rect(*this, tile_type));
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));
}

Expand All @@ -951,15 +964,15 @@ 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 {
send_event = false;
}

if (send_event) {
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
send_resize_event_to_client();
}
}

Expand Down
6 changes: 5 additions & 1 deletion Userland/Services/WindowServer/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Window final : public Core::Object {

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();
Expand Down Expand Up @@ -376,6 +376,9 @@ class Window final : public Core::Object {
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);
Expand All @@ -386,6 +389,7 @@ class Window final : public Core::Object {
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 };
Expand Down
Loading

0 comments on commit fe54a0c

Please sign in to comment.