mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-23 08:00:20 +00:00
LibGUI+WindowServer: Start fleshing out drag&drop functionality
This patch enables basic drag&drop between applications. You initiate a drag by creating a GDragOperation object and calling exec() on it. This creates a nested event loop in the calling program that only returns once the drag operation has ended. On the receiving side, you get a call to GWidget::drop_event() with a GDropEvent containing information about the dropped data. The only data passed right now is a piece of text that's also used to visually indicate that a drag is happening (by showing the text in a little box that follows the mouse cursor around.) There are things to fix here, but we're off to a nice start. :^)
This commit is contained in:
parent
e09a02ad3f
commit
a7f414bba7
Notes:
sideshowbarker
2024-07-19 10:55:21 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/a7f414bba7f
19 changed files with 318 additions and 3 deletions
53
Libraries/LibGUI/GDragOperation.cpp
Normal file
53
Libraries/LibGUI/GDragOperation.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <LibGUI/GDragOperation.h>
|
||||
#include <LibGUI/GWindowServerConnection.h>
|
||||
|
||||
static GDragOperation* s_current_drag_operation;
|
||||
|
||||
GDragOperation::GDragOperation(CObject* parent)
|
||||
: CObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
GDragOperation::~GDragOperation()
|
||||
{
|
||||
}
|
||||
|
||||
GDragOperation::Outcome GDragOperation::exec()
|
||||
{
|
||||
ASSERT(!s_current_drag_operation);
|
||||
ASSERT(!m_event_loop);
|
||||
|
||||
auto response = GWindowServerConnection::the().send_sync<WindowServer::StartDrag>(m_text, -1, Size());
|
||||
if (!response->started()) {
|
||||
m_outcome = Outcome::Cancelled;
|
||||
return m_outcome;
|
||||
}
|
||||
|
||||
s_current_drag_operation = this;
|
||||
m_event_loop = make<CEventLoop>();
|
||||
auto result = m_event_loop->exec();
|
||||
m_event_loop = nullptr;
|
||||
dbgprintf("%s: event loop returned with result %d\n", class_name(), result);
|
||||
remove_from_parent();
|
||||
s_current_drag_operation = nullptr;
|
||||
return m_outcome;
|
||||
}
|
||||
|
||||
void GDragOperation::done(Outcome outcome)
|
||||
{
|
||||
ASSERT(m_outcome == Outcome::None);
|
||||
m_outcome = outcome;
|
||||
m_event_loop->quit(0);
|
||||
}
|
||||
|
||||
void GDragOperation::notify_accepted(Badge<GWindowServerConnection>)
|
||||
{
|
||||
ASSERT(s_current_drag_operation);
|
||||
s_current_drag_operation->done(Outcome::Accepted);
|
||||
}
|
||||
|
||||
void GDragOperation::notify_cancelled(Badge<GWindowServerConnection>)
|
||||
{
|
||||
ASSERT(s_current_drag_operation);
|
||||
s_current_drag_operation->done(Outcome::Cancelled);
|
||||
}
|
36
Libraries/LibGUI/GDragOperation.h
Normal file
36
Libraries/LibGUI/GDragOperation.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibCore/CEventLoop.h>
|
||||
#include <LibCore/CObject.h>
|
||||
|
||||
class GWindowServerConnection;
|
||||
|
||||
class GDragOperation : public CObject {
|
||||
C_OBJECT(GDragOperation)
|
||||
public:
|
||||
enum class Outcome {
|
||||
None,
|
||||
Accepted,
|
||||
Cancelled,
|
||||
};
|
||||
|
||||
virtual ~GDragOperation() override;
|
||||
|
||||
void set_text(const String& text) { m_text = text; }
|
||||
|
||||
Outcome exec();
|
||||
Outcome outcome() const { return m_outcome; }
|
||||
|
||||
static void notify_accepted(Badge<GWindowServerConnection>);
|
||||
static void notify_cancelled(Badge<GWindowServerConnection>);
|
||||
|
||||
protected:
|
||||
explicit GDragOperation(CObject* parent = nullptr);
|
||||
|
||||
private:
|
||||
void done(Outcome);
|
||||
|
||||
OwnPtr<CEventLoop> m_event_loop;
|
||||
Outcome m_outcome { Outcome::None };
|
||||
String m_text;
|
||||
};
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include <Kernel/KeyCode.h>
|
||||
#include <LibCore/CEvent.h>
|
||||
#include <LibGUI/GWindowType.h>
|
||||
#include <LibDraw/Point.h>
|
||||
#include <LibDraw/Rect.h>
|
||||
#include <LibGUI/GWindowType.h>
|
||||
|
||||
class CObject;
|
||||
|
||||
|
@ -34,6 +34,7 @@ public:
|
|||
WindowCloseRequest,
|
||||
ContextMenu,
|
||||
EnabledChange,
|
||||
Drop,
|
||||
|
||||
__Begin_WM_Events,
|
||||
WM_WindowRemoved,
|
||||
|
@ -278,3 +279,20 @@ private:
|
|||
unsigned m_modifiers { 0 };
|
||||
int m_wheel_delta { 0 };
|
||||
};
|
||||
|
||||
class GDropEvent final : public GEvent {
|
||||
public:
|
||||
GDropEvent(const Point& position, const String& text)
|
||||
: GEvent(GEvent::Drop)
|
||||
, m_position(position)
|
||||
, m_text(text)
|
||||
{
|
||||
}
|
||||
|
||||
const Point& position() const { return m_position; }
|
||||
const String& text() const { return m_text; }
|
||||
|
||||
private:
|
||||
Point m_position;
|
||||
String m_text;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include <AK/StringBuilder.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
#include <LibGUI/GDragOperation.h>
|
||||
#include <LibGUI/GItemView.h>
|
||||
#include <LibGUI/GModel.h>
|
||||
#include <LibGUI/GPainter.h>
|
||||
|
@ -83,6 +85,7 @@ void GItemView::mousedown_event(GMouseEvent& event)
|
|||
int item_index = item_at_event_position(event.position());
|
||||
|
||||
if (event.button() == GMouseButton::Left) {
|
||||
m_left_mousedown_position = event.position();
|
||||
if (item_index == -1) {
|
||||
selection().clear();
|
||||
} else {
|
||||
|
@ -97,6 +100,45 @@ void GItemView::mousedown_event(GMouseEvent& event)
|
|||
GAbstractView::mousedown_event(event);
|
||||
}
|
||||
|
||||
void GItemView::mousemove_event(GMouseEvent& event)
|
||||
{
|
||||
if (!model())
|
||||
return GAbstractView::mousemove_event(event);
|
||||
|
||||
if (event.buttons() & GMouseButton::Left && !selection().is_empty()) {
|
||||
auto diff = event.position() - m_left_mousedown_position;
|
||||
auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y();
|
||||
constexpr int drag_distance_threshold = 5;
|
||||
if (distance_travelled_squared > (drag_distance_threshold)) {
|
||||
dbg() << "Initiate drag!";
|
||||
auto drag_operation = GDragOperation::construct();
|
||||
|
||||
StringBuilder builder;
|
||||
selection().for_each_index([&](auto& index) {
|
||||
auto data = model()->data(index);
|
||||
builder.append(data.to_string());
|
||||
builder.append(" ");
|
||||
});
|
||||
|
||||
drag_operation->set_text(builder.to_string());
|
||||
auto outcome = drag_operation->exec();
|
||||
switch (outcome) {
|
||||
case GDragOperation::Outcome::Accepted:
|
||||
dbg() << "Drag was accepted!";
|
||||
break;
|
||||
case GDragOperation::Outcome::Cancelled:
|
||||
dbg() << "Drag was cancelled!";
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GAbstractView::mousemove_event(event);
|
||||
}
|
||||
|
||||
void GItemView::context_menu_event(GContextMenuEvent& event)
|
||||
{
|
||||
if (!model())
|
||||
|
|
|
@ -29,6 +29,7 @@ private:
|
|||
virtual void paint_event(GPaintEvent&) override;
|
||||
virtual void resize_event(GResizeEvent&) override;
|
||||
virtual void mousedown_event(GMouseEvent&) override;
|
||||
virtual void mousemove_event(GMouseEvent&) override;
|
||||
virtual void keydown_event(GKeyEvent&) override;
|
||||
virtual void doubleclick_event(GMouseEvent&) override;
|
||||
virtual void context_menu_event(GContextMenuEvent&) override;
|
||||
|
@ -43,5 +44,7 @@ private:
|
|||
int m_visual_column_count { 0 };
|
||||
int m_visual_row_count { 0 };
|
||||
|
||||
Point m_left_mousedown_position;
|
||||
|
||||
Size m_effective_item_size { 80, 80 };
|
||||
};
|
||||
|
|
|
@ -156,6 +156,8 @@ void GWidget::event(CEvent& event)
|
|||
return handle_mouseup_event(static_cast<GMouseEvent&>(event));
|
||||
case GEvent::MouseWheel:
|
||||
return mousewheel_event(static_cast<GMouseEvent&>(event));
|
||||
case GEvent::Drop:
|
||||
return drop_event(static_cast<GDropEvent&>(event));
|
||||
case GEvent::Enter:
|
||||
return handle_enter_event(event);
|
||||
case GEvent::Leave:
|
||||
|
@ -350,6 +352,11 @@ void GWidget::change_event(GEvent&)
|
|||
{
|
||||
}
|
||||
|
||||
void GWidget::drop_event(GDropEvent& event)
|
||||
{
|
||||
dbg() << class_name() << "{" << this << "} DROP position: " << event.position() << ", text: '" << event.text() << "'";
|
||||
}
|
||||
|
||||
void GWidget::update()
|
||||
{
|
||||
if (rect().is_empty())
|
||||
|
@ -686,4 +693,3 @@ Vector<GWidget*> GWidget::child_widgets() const
|
|||
}
|
||||
return widgets;
|
||||
}
|
||||
|
||||
|
|
|
@ -254,6 +254,7 @@ protected:
|
|||
virtual void leave_event(CEvent&);
|
||||
virtual void child_event(CChildEvent&) override;
|
||||
virtual void change_event(GEvent&);
|
||||
virtual void drop_event(GDropEvent&);
|
||||
|
||||
private:
|
||||
void handle_paint_event(GPaintEvent&);
|
||||
|
|
|
@ -154,6 +154,16 @@ void GWindow::set_override_cursor(GStandardCursor cursor)
|
|||
|
||||
void GWindow::event(CEvent& event)
|
||||
{
|
||||
if (event.type() == GEvent::Drop) {
|
||||
auto& drop_event = static_cast<GDropEvent&>(event);
|
||||
if (!m_main_widget)
|
||||
return;
|
||||
auto result = m_main_widget->hit_test(drop_event.position());
|
||||
auto local_event = make<GDropEvent>(result.local_position, drop_event.text());
|
||||
ASSERT(result.widget);
|
||||
return result.widget->dispatch_event(*local_event, this);
|
||||
}
|
||||
|
||||
if (event.type() == GEvent::MouseUp || event.type() == GEvent::MouseDown || event.type() == GEvent::MouseDoubleClick || event.type() == GEvent::MouseMove || event.type() == GEvent::MouseWheel) {
|
||||
auto& mouse_event = static_cast<GMouseEvent&>(event);
|
||||
if (m_global_cursor_tracking_widget) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <LibGUI/GApplication.h>
|
||||
#include <LibGUI/GClipboard.h>
|
||||
#include <LibGUI/GDesktop.h>
|
||||
#include <LibGUI/GDragOperation.h>
|
||||
#include <LibGUI/GEvent.h>
|
||||
#include <LibGUI/GMenu.h>
|
||||
#include <LibGUI/GWidget.h>
|
||||
|
@ -261,3 +262,19 @@ void GWindowServerConnection::handle(const WindowClient::AsyncSetWallpaperFinish
|
|||
{
|
||||
// This is handled manually by GDesktop::set_wallpaper().
|
||||
}
|
||||
|
||||
void GWindowServerConnection::handle(const WindowClient::DragDropped& message)
|
||||
{
|
||||
if (auto* window = GWindow::from_window_id(message.window_id()))
|
||||
CEventLoop::current().post_event(*window, make<GDropEvent>(message.mouse_position(), message.text()));
|
||||
}
|
||||
|
||||
void GWindowServerConnection::handle(const WindowClient::DragAccepted&)
|
||||
{
|
||||
GDragOperation::notify_accepted({});
|
||||
}
|
||||
|
||||
void GWindowServerConnection::handle(const WindowClient::DragCancelled&)
|
||||
{
|
||||
GDragOperation::notify_cancelled({});
|
||||
}
|
||||
|
|
|
@ -41,4 +41,7 @@ private:
|
|||
virtual void handle(const WindowClient::WM_WindowIconBitmapChanged&) override;
|
||||
virtual void handle(const WindowClient::WM_WindowRectChanged&) override;
|
||||
virtual void handle(const WindowClient::AsyncSetWallpaperFinished&) override;
|
||||
virtual void handle(const WindowClient::DragDropped&) override;
|
||||
virtual void handle(const WindowClient::DragAccepted&) override;
|
||||
virtual void handle(const WindowClient::DragCancelled&) override;
|
||||
};
|
||||
|
|
|
@ -59,6 +59,7 @@ OBJS = \
|
|||
GLazyWidget.o \
|
||||
GCommand.o \
|
||||
GUndoStack.o \
|
||||
GDragOperation.o \
|
||||
GWindow.o
|
||||
|
||||
LIBRARY = libgui.a
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include <LibC/SharedBuffer.h>
|
||||
#include <LibDraw/GraphicsBitmap.h>
|
||||
#include <SharedBuffer.h>
|
||||
#include <WindowServer/WSMenuApplet.h>
|
||||
#include <WindowServer/WSClientConnection.h>
|
||||
#include <WindowServer/WSClipboard.h>
|
||||
#include <WindowServer/WSCompositor.h>
|
||||
#include <WindowServer/WSEventLoop.h>
|
||||
#include <WindowServer/WSMenu.h>
|
||||
#include <WindowServer/WSMenuApplet.h>
|
||||
#include <WindowServer/WSMenuBar.h>
|
||||
#include <WindowServer/WSMenuItem.h>
|
||||
#include <WindowServer/WSScreen.h>
|
||||
|
@ -678,3 +678,24 @@ OwnPtr<WindowServer::InvalidateMenuAppletRectResponse> WSClientConnection::handl
|
|||
it->value->invalidate(message.rect());
|
||||
return make<WindowServer::InvalidateMenuAppletRectResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<WindowServer::StartDragResponse> WSClientConnection::handle(const WindowServer::StartDrag& message)
|
||||
{
|
||||
auto& wm = WSWindowManager::the();
|
||||
if (wm.dnd_client())
|
||||
return make<WindowServer::StartDragResponse>(false);
|
||||
|
||||
RefPtr<GraphicsBitmap> bitmap;
|
||||
if (message.bitmap_id() != -1) {
|
||||
auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(message.bitmap_id());
|
||||
ssize_t size_in_bytes = message.bitmap_size().area() * sizeof(RGBA32);
|
||||
if (size_in_bytes > shared_buffer->size()) {
|
||||
did_misbehave("SetAppletBackingStore: Shared buffer is too small for applet size");
|
||||
return nullptr;
|
||||
}
|
||||
bitmap = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *shared_buffer, message.bitmap_size());
|
||||
}
|
||||
|
||||
wm.start_dnd_drag(*this, message.text(), bitmap);
|
||||
return make<WindowServer::StartDragResponse>(true);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ private:
|
|||
virtual OwnPtr<WindowServer::DestroyMenuAppletResponse> handle(const WindowServer::DestroyMenuApplet&) override;
|
||||
virtual OwnPtr<WindowServer::SetMenuAppletBackingStoreResponse> handle(const WindowServer::SetMenuAppletBackingStore&) override;
|
||||
virtual OwnPtr<WindowServer::InvalidateMenuAppletRectResponse> handle(const WindowServer::InvalidateMenuAppletRect&) override;
|
||||
virtual OwnPtr<WindowServer::StartDragResponse> handle(const WindowServer::StartDrag&) override;
|
||||
|
||||
HashMap<i32, NonnullOwnPtr<WSMenuApplet>> m_menu_applets;
|
||||
HashMap<int, NonnullRefPtr<WSWindow>> m_windows;
|
||||
|
|
|
@ -93,6 +93,7 @@ void WSCompositor::compose()
|
|||
|
||||
dirty_rects.add(Rect::intersection(m_last_geometry_label_rect, WSScreen::the().rect()));
|
||||
dirty_rects.add(Rect::intersection(m_last_cursor_rect, WSScreen::the().rect()));
|
||||
dirty_rects.add(Rect::intersection(m_last_dnd_rect, WSScreen::the().rect()));
|
||||
dirty_rects.add(Rect::intersection(current_cursor_rect(), WSScreen::the().rect()));
|
||||
#ifdef DEBUG_COUNTERS
|
||||
dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.rects().size());
|
||||
|
@ -387,6 +388,9 @@ Rect WSCompositor::current_cursor_rect() const
|
|||
|
||||
void WSCompositor::invalidate_cursor()
|
||||
{
|
||||
auto& wm = WSWindowManager::the();
|
||||
if (wm.dnd_client())
|
||||
invalidate(wm.dnd_rect());
|
||||
invalidate(current_cursor_rect());
|
||||
}
|
||||
|
||||
|
@ -417,5 +421,17 @@ void WSCompositor::draw_cursor()
|
|||
auto& wm = WSWindowManager::the();
|
||||
Rect cursor_rect = current_cursor_rect();
|
||||
m_back_painter->blit(cursor_rect.location(), wm.active_cursor().bitmap(), wm.active_cursor().rect());
|
||||
|
||||
if (wm.dnd_client()) {
|
||||
auto dnd_rect = wm.dnd_rect();
|
||||
if (!wm.dnd_text().is_empty()) {
|
||||
m_back_painter->fill_rect(dnd_rect, Color(110, 34, 9, 200));
|
||||
m_back_painter->draw_text(dnd_rect, wm.dnd_text(), TextAlignment::Center, Color::White);
|
||||
}
|
||||
// FIXME: Also do the drag_bitmap if present
|
||||
m_last_dnd_rect = dnd_rect;
|
||||
} else {
|
||||
m_last_dnd_rect = {};
|
||||
}
|
||||
m_last_cursor_rect = cursor_rect;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ private:
|
|||
DisjointRectSet m_dirty_rects;
|
||||
|
||||
Rect m_last_cursor_rect;
|
||||
Rect m_last_dnd_rect;
|
||||
Rect m_last_geometry_label_rect;
|
||||
|
||||
String m_wallpaper_path;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <WindowServer/WSButton.h>
|
||||
#include <WindowServer/WSClientConnection.h>
|
||||
#include <WindowServer/WSCursor.h>
|
||||
#include <WindowServer/WindowClientEndpoint.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
@ -625,6 +626,36 @@ bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, W
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WSWindowManager::process_ongoing_drag(WSMouseEvent& event, WSWindow*& hovered_window)
|
||||
{
|
||||
if (!m_dnd_client)
|
||||
return false;
|
||||
if (!(event.type() == WSEvent::MouseUp && event.button() == MouseButton::Left))
|
||||
return true;
|
||||
|
||||
hovered_window = nullptr;
|
||||
for_each_visible_window_from_front_to_back([&](auto& window) {
|
||||
if (window.frame().rect().contains(event.position())) {
|
||||
hovered_window = &window;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (hovered_window) {
|
||||
m_dnd_client->post_message(WindowClient::DragAccepted());
|
||||
if (hovered_window->client()) {
|
||||
auto translated_event = event.translated(-hovered_window->position());
|
||||
hovered_window->client()->post_message(WindowClient::DragDropped(hovered_window->window_id(), translated_event.position(), m_dnd_text));
|
||||
}
|
||||
} else {
|
||||
m_dnd_client->post_message(WindowClient::DragCancelled());
|
||||
}
|
||||
|
||||
end_dnd_drag();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WSWindowManager::set_cursor_tracking_button(WSButton* button)
|
||||
{
|
||||
m_cursor_tracking_button = button ? button->make_weak_ptr() : nullptr;
|
||||
|
@ -708,6 +739,9 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovere
|
|||
{
|
||||
hovered_window = nullptr;
|
||||
|
||||
if (process_ongoing_drag(event, hovered_window))
|
||||
return;
|
||||
|
||||
if (process_ongoing_window_drag(event, hovered_window))
|
||||
return;
|
||||
|
||||
|
@ -943,6 +977,12 @@ void WSWindowManager::event(CEvent& event)
|
|||
auto& key_event = static_cast<const WSKeyEvent&>(event);
|
||||
m_keyboard_modifiers = key_event.modifiers();
|
||||
|
||||
if (key_event.type() == WSEvent::KeyDown && key_event.key() == Key_Escape && m_dnd_client) {
|
||||
m_dnd_client->post_message(WindowClient::DragCancelled());
|
||||
end_dnd_drag();
|
||||
return;
|
||||
}
|
||||
|
||||
if (key_event.type() == WSEvent::KeyDown && key_event.modifiers() == Mod_Logo && key_event.key() == Key_Tab)
|
||||
m_switcher.show();
|
||||
if (m_switcher.is_visible()) {
|
||||
|
@ -1138,3 +1178,29 @@ WSMenu* WSWindowManager::find_internal_menu_by_id(int menu_id)
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WSWindowManager::start_dnd_drag(WSClientConnection& client, const String& text, GraphicsBitmap* bitmap)
|
||||
{
|
||||
ASSERT(!m_dnd_client);
|
||||
m_dnd_client = client.make_weak_ptr();
|
||||
m_dnd_text = text;
|
||||
m_dnd_bitmap = bitmap;
|
||||
WSCompositor::the().invalidate_cursor();
|
||||
}
|
||||
|
||||
void WSWindowManager::end_dnd_drag()
|
||||
{
|
||||
ASSERT(m_dnd_client);
|
||||
WSCompositor::the().invalidate_cursor();
|
||||
m_dnd_client = nullptr;
|
||||
m_dnd_text = {};
|
||||
m_dnd_bitmap = nullptr;
|
||||
}
|
||||
|
||||
Rect WSWindowManager::dnd_rect() const
|
||||
{
|
||||
int width = font().width(m_dnd_text);
|
||||
int height = font().glyph_height();
|
||||
auto location = WSCompositor::the().current_cursor_rect().center().translated(8, 8);
|
||||
return Rect(location, { width, height }).inflated(4, 4);
|
||||
}
|
||||
|
|
|
@ -66,6 +66,14 @@ public:
|
|||
|
||||
Rect maximized_window_rect(const WSWindow&) const;
|
||||
|
||||
WSClientConnection* dnd_client() { return m_dnd_client.ptr(); }
|
||||
const String& dnd_text() const { return m_dnd_text; }
|
||||
const GraphicsBitmap* dnd_bitmap() const { return m_dnd_bitmap; }
|
||||
Rect dnd_rect() const;
|
||||
|
||||
void start_dnd_drag(WSClientConnection&, const String& text, GraphicsBitmap*);
|
||||
void end_dnd_drag();
|
||||
|
||||
WSWindow* active_window() { return m_active_window.ptr(); }
|
||||
const WSClientConnection* active_client() const;
|
||||
bool active_window_is_modal() const { return m_active_window && m_active_window->is_modal(); }
|
||||
|
@ -156,6 +164,7 @@ private:
|
|||
void deliver_mouse_event(WSWindow& window, WSMouseEvent& event);
|
||||
bool process_ongoing_window_resize(const WSMouseEvent&, WSWindow*& hovered_window);
|
||||
bool process_ongoing_window_drag(WSMouseEvent&, WSWindow*& hovered_window);
|
||||
bool process_ongoing_drag(WSMouseEvent&, WSWindow*& hovered_window);
|
||||
void start_window_drag(WSWindow&, const WSMouseEvent&);
|
||||
void set_hovered_window(WSWindow*);
|
||||
template<typename Callback>
|
||||
|
@ -268,6 +277,10 @@ private:
|
|||
};
|
||||
Vector<AppMetadata> m_apps;
|
||||
HashMap<String, NonnullRefPtr<WSMenu>> m_app_category_menus;
|
||||
|
||||
WeakPtr<WSClientConnection> m_dnd_client;
|
||||
String m_dnd_text;
|
||||
RefPtr<GraphicsBitmap> m_dnd_bitmap;
|
||||
};
|
||||
|
||||
template<typename Callback>
|
||||
|
|
|
@ -27,4 +27,9 @@ endpoint WindowClient = 4
|
|||
WM_WindowRectChanged(i32 client_id, i32 window_id, Rect rect) =|
|
||||
|
||||
AsyncSetWallpaperFinished(bool success) =|
|
||||
|
||||
DragAccepted() =|
|
||||
DragCancelled() =|
|
||||
|
||||
DragDropped(i32 window_id, Point mouse_position, String text) =|
|
||||
}
|
||||
|
|
|
@ -71,4 +71,6 @@ endpoint WindowServer = 2
|
|||
|
||||
GetWallpaper() => (String path)
|
||||
SetWindowOverrideCursor(i32 window_id, i32 cursor_type) => ()
|
||||
|
||||
StartDrag(String text, i32 bitmap_id, Size bitmap_size) => (bool started)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue