From 6d1e47e7dd1741aec64dd93a4d95ff1854e3abab Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Sat, 7 Nov 2020 23:14:21 +0330 Subject: [PATCH] LibGUI+WindowServer: Make DragOperation hold a MimeData instance ...instead of maybe bitmap + a single mime type and its corresponding data. This allows drag&drop operations to hold multiple different kinds of data, and the views/applications to choose between those. For instance, Spreadsheet can keep the structure of the dragged cells, and still provide text-only data to be passed to different unrelated editors. --- Libraries/LibCore/MimeData.h | 6 ++++ Libraries/LibGUI/AbstractView.cpp | 28 +--------------- Libraries/LibGUI/DragOperation.cpp | 34 +++++++++++++++++-- Libraries/LibGUI/DragOperation.h | 16 +++------ Libraries/LibGUI/FileSystemModel.cpp | 2 +- Libraries/LibGUI/Model.cpp | 36 +++++++++++++++++++++ Libraries/LibGUI/Model.h | 3 ++ Libraries/LibGUI/ModelRole.h | 2 +- Libraries/LibGUI/WindowServerConnection.cpp | 3 +- Services/WindowServer/ClientConnection.cpp | 2 +- Services/WindowServer/Event.h | 4 ++- Services/WindowServer/WindowClient.ipc | 2 +- Services/WindowServer/WindowManager.cpp | 9 +++--- Services/WindowServer/WindowManager.h | 8 ++--- Services/WindowServer/WindowServer.ipc | 2 +- 15 files changed, 98 insertions(+), 59 deletions(-) diff --git a/Libraries/LibCore/MimeData.h b/Libraries/LibCore/MimeData.h index 65bd56c1211..fe575ab6755 100644 --- a/Libraries/LibCore/MimeData.h +++ b/Libraries/LibCore/MimeData.h @@ -55,8 +55,14 @@ public: Vector urls() const; void set_urls(const Vector&); + const HashMap& all_data() const { return m_data; } + private: MimeData() { } + explicit MimeData(const HashMap& data) + : m_data(data) + { + } HashMap m_data; }; diff --git a/Libraries/LibGUI/AbstractView.cpp b/Libraries/LibGUI/AbstractView.cpp index fe80611d616..4303e505137 100644 --- a/Libraries/LibGUI/AbstractView.cpp +++ b/Libraries/LibGUI/AbstractView.cpp @@ -291,33 +291,7 @@ void AbstractView::mousemove_event(MouseEvent& event) dbg() << "Initiate drag!"; auto drag_operation = DragOperation::construct(); - RefPtr bitmap; - - StringBuilder text_builder; - StringBuilder data_builder; - bool first = true; - m_selection.for_each_index([&](auto& index) { - auto text_data = index.data(); - if (!first) - text_builder.append(", "); - text_builder.append(text_data.to_string()); - - auto drag_data = index.data(ModelRole::DragData); - data_builder.append(drag_data.to_string()); - data_builder.append('\n'); - - first = false; - - if (!bitmap) { - Variant icon_data = index.data(ModelRole::Icon); - if (icon_data.is_icon()) - bitmap = icon_data.as_icon().bitmap_for_size(32); - } - }); - - drag_operation->set_text(text_builder.to_string()); - drag_operation->set_bitmap(bitmap); - drag_operation->set_data(data_type, data_builder.to_string()); + drag_operation->set_mime_data(m_model->mime_data(m_selection)); auto outcome = drag_operation->exec(); diff --git a/Libraries/LibGUI/DragOperation.cpp b/Libraries/LibGUI/DragOperation.cpp index f0355eaddb4..d85e05fd805 100644 --- a/Libraries/LibGUI/DragOperation.cpp +++ b/Libraries/LibGUI/DragOperation.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -48,18 +49,25 @@ DragOperation::Outcome DragOperation::exec() { ASSERT(!s_current_drag_operation); ASSERT(!m_event_loop); + ASSERT(m_mime_data); int bitmap_id = -1; Gfx::IntSize bitmap_size; RefPtr shared_bitmap; - if (m_bitmap) { - shared_bitmap = m_bitmap->to_bitmap_backed_by_shared_buffer(); + if (m_mime_data->has_format("image/x-raw-bitmap")) { + auto data = m_mime_data->data("image/x-raw-bitmap"); + auto bitmap = Gfx::Bitmap::create_from_serialized_byte_buffer(move(data)); + shared_bitmap = bitmap->to_bitmap_backed_by_shared_buffer(); shared_bitmap->shared_buffer()->share_with(WindowServerConnection::the().server_pid()); bitmap_id = shared_bitmap->shbuf_id(); bitmap_size = shared_bitmap->size(); } - auto response = WindowServerConnection::the().send_sync(m_text, m_data_type, m_data, bitmap_id, bitmap_size); + auto response = WindowServerConnection::the().send_sync( + m_mime_data->text(), + m_mime_data->all_data(), + bitmap_id, bitmap_size); + if (!response->started()) { m_outcome = Outcome::Cancelled; return m_outcome; @@ -94,4 +102,24 @@ void DragOperation::notify_cancelled(Badge) s_current_drag_operation->done(Outcome::Cancelled); } +void DragOperation::set_text(const String& text) +{ + if (!m_mime_data) + m_mime_data = Core::MimeData::construct(); + m_mime_data->set_text(text); +} +void DragOperation::set_bitmap(const Gfx::Bitmap* bitmap) +{ + if (!m_mime_data) + m_mime_data = Core::MimeData::construct(); + if (bitmap) + m_mime_data->set_data("image/x-raw-bitmap", bitmap->serialize_to_byte_buffer()); +} +void DragOperation::set_data(const String& data_type, const String& data) +{ + if (!m_mime_data) + m_mime_data = Core::MimeData::construct(); + m_mime_data->set_data(data_type, data.to_byte_buffer()); +} + } diff --git a/Libraries/LibGUI/DragOperation.h b/Libraries/LibGUI/DragOperation.h index ffaf366598a..8595c0a8484 100644 --- a/Libraries/LibGUI/DragOperation.h +++ b/Libraries/LibGUI/DragOperation.h @@ -44,13 +44,10 @@ public: virtual ~DragOperation() override; - void set_text(const String& text) { m_text = text; } - void set_bitmap(const Gfx::Bitmap* bitmap) { m_bitmap = bitmap; } - void set_data(const String& data_type, const String& data) - { - m_data_type = data_type; - m_data = data; - } + void set_mime_data(RefPtr mime_data) { m_mime_data = move(mime_data); } + void set_text(const String& text); + void set_bitmap(const Gfx::Bitmap* bitmap); + void set_data(const String& data_type, const String& data); Outcome exec(); Outcome outcome() const { return m_outcome; } @@ -66,10 +63,7 @@ private: OwnPtr m_event_loop; Outcome m_outcome { Outcome::None }; - String m_text; - String m_data_type; - String m_data; - RefPtr m_bitmap; + RefPtr m_mime_data; }; } diff --git a/Libraries/LibGUI/FileSystemModel.cpp b/Libraries/LibGUI/FileSystemModel.cpp index dad8a513ccc..b132e8800af 100644 --- a/Libraries/LibGUI/FileSystemModel.cpp +++ b/Libraries/LibGUI/FileSystemModel.cpp @@ -403,7 +403,7 @@ Variant FileSystemModel::data(const ModelIndex& index, ModelRole role) const return node.full_path(); } - if (role == ModelRole::DragData) { + if (role == ModelRole::MimeData) { if (index.column() == Column::Name) { StringBuilder builder; builder.append("file://"); diff --git a/Libraries/LibGUI/Model.cpp b/Libraries/LibGUI/Model.cpp index 838d347eb68..b9e9d1e30c3 100644 --- a/Libraries/LibGUI/Model.cpp +++ b/Libraries/LibGUI/Model.cpp @@ -88,4 +88,40 @@ void Model::unregister_client(ModelClient& client) m_clients.remove(&client); } +RefPtr Model::mime_data(const ModelSelection& selection) const +{ + auto mime_data = Core::MimeData::construct(); + RefPtr bitmap; + + StringBuilder text_builder; + StringBuilder data_builder; + bool first = true; + selection.for_each_index([&](auto& index) { + auto text_data = index.data(); + if (!first) + text_builder.append(", "); + text_builder.append(text_data.to_string()); + + if (!first) + data_builder.append('\n'); + auto data = index.data(ModelRole::MimeData); + data_builder.append(data.to_string()); + + first = false; + + if (!bitmap) { + Variant icon_data = index.data(ModelRole::Icon); + if (icon_data.is_icon()) + bitmap = icon_data.as_icon().bitmap_for_size(32); + } + }); + + mime_data->set_data(drag_data_type(), data_builder.to_byte_buffer()); + mime_data->set_text(text_builder.to_string()); + if (bitmap) + mime_data->set_data("image/x-raw-bitmap", bitmap->serialize_to_byte_buffer()); + + return mime_data; +} + } diff --git a/Libraries/LibGUI/Model.h b/Libraries/LibGUI/Model.h index ed73a16a66f..de6defbb254 100644 --- a/Libraries/LibGUI/Model.h +++ b/Libraries/LibGUI/Model.h @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -93,6 +95,7 @@ public: } virtual StringView drag_data_type() const { return {}; } + virtual RefPtr mime_data(const ModelSelection&) const; void register_view(Badge, AbstractView&); void unregister_view(Badge, AbstractView&); diff --git a/Libraries/LibGUI/ModelRole.h b/Libraries/LibGUI/ModelRole.h index 79b868076d9..89788084810 100644 --- a/Libraries/LibGUI/ModelRole.h +++ b/Libraries/LibGUI/ModelRole.h @@ -35,7 +35,7 @@ enum class ModelRole { BackgroundColor, Icon, Font, - DragData, + MimeData, TextAlignment, Search, Custom = 0x100, // Applications are free to use roles above this number as they please diff --git a/Libraries/LibGUI/WindowServerConnection.cpp b/Libraries/LibGUI/WindowServerConnection.cpp index 8f846e7a46e..4a5eb28f2e9 100644 --- a/Libraries/LibGUI/WindowServerConnection.cpp +++ b/Libraries/LibGUI/WindowServerConnection.cpp @@ -307,8 +307,7 @@ void WindowServerConnection::handle(const Messages::WindowClient::AsyncSetWallpa void WindowServerConnection::handle(const Messages::WindowClient::DragDropped& message) { if (auto* window = Window::from_window_id(message.window_id())) { - auto mime_data = Core::MimeData::construct(); - mime_data->set_data(message.data_type(), message.data().to_byte_buffer()); + auto mime_data = Core::MimeData::construct(message.mime_data()); Core::EventLoop::current().post_event(*window, make(message.mouse_position(), message.text(), mime_data)); } } diff --git a/Services/WindowServer/ClientConnection.cpp b/Services/WindowServer/ClientConnection.cpp index af29e8fc15f..87b21f5987a 100644 --- a/Services/WindowServer/ClientConnection.cpp +++ b/Services/WindowServer/ClientConnection.cpp @@ -755,7 +755,7 @@ OwnPtr ClientConnection::handle(const bitmap = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *shared_buffer, message.bitmap_size()); } - wm.start_dnd_drag(*this, message.text(), bitmap, message.data_type(), message.data()); + wm.start_dnd_drag(*this, message.text(), bitmap, Core::MimeData::construct(message.mime_data())); return make(true); } diff --git a/Services/WindowServer/Event.h b/Services/WindowServer/Event.h index a1df6cb4cda..3e9adf88304 100644 --- a/Services/WindowServer/Event.h +++ b/Services/WindowServer/Event.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -128,7 +129,7 @@ public: const String& drag_data_type() const { return m_drag_data_type; } void set_drag(bool b) { m_drag = b; } - void set_drag_data_type(const String& drag_data_type) { m_drag_data_type = drag_data_type; } + void set_mime_data(const Core::MimeData& mime_data) { m_mime_data = mime_data; } MouseEvent translated(const Gfx::IntPoint& delta) const { return MouseEvent((Type)type(), m_position.translated(delta), m_buttons, m_button, m_modifiers, m_wheel_delta); } @@ -140,6 +141,7 @@ private: int m_wheel_delta { 0 }; bool m_drag { false }; String m_drag_data_type; + RefPtr m_mime_data; }; class ResizeEvent final : public Event { diff --git a/Services/WindowServer/WindowClient.ipc b/Services/WindowServer/WindowClient.ipc index da28bac7cac..a1ce1413a39 100644 --- a/Services/WindowServer/WindowClient.ipc +++ b/Services/WindowServer/WindowClient.ipc @@ -32,7 +32,7 @@ endpoint WindowClient = 4 DragAccepted() =| DragCancelled() =| - DragDropped(i32 window_id, Gfx::IntPoint mouse_position, [UTF8] String text, String data_type, String data) =| + DragDropped(i32 window_id, Gfx::IntPoint mouse_position, [UTF8] String text, HashMap mime_data) =| UpdateSystemTheme(i32 system_theme_buffer_id) =| diff --git a/Services/WindowServer/WindowManager.cpp b/Services/WindowServer/WindowManager.cpp index 35543d5c76e..9dfd1dcaf9d 100644 --- a/Services/WindowServer/WindowManager.cpp +++ b/Services/WindowServer/WindowManager.cpp @@ -705,7 +705,7 @@ bool WindowManager::process_ongoing_drag(MouseEvent& event, Window*& hovered_win hovered_window = &window; auto translated_event = event.translated(-window.position()); translated_event.set_drag(true); - translated_event.set_drag_data_type(m_dnd_data_type); + translated_event.set_mime_data(*m_dnd_mime_data); deliver_mouse_event(window, translated_event); return IterationDecision::Break; }); @@ -727,7 +727,7 @@ bool WindowManager::process_ongoing_drag(MouseEvent& event, Window*& hovered_win m_dnd_client->post_message(Messages::WindowClient::DragAccepted()); if (hovered_window->client()) { auto translated_event = event.translated(-hovered_window->position()); - hovered_window->client()->post_message(Messages::WindowClient::DragDropped(hovered_window->window_id(), translated_event.position(), m_dnd_text, m_dnd_data_type, m_dnd_data)); + hovered_window->client()->post_message(Messages::WindowClient::DragDropped(hovered_window->window_id(), translated_event.position(), m_dnd_text, m_dnd_mime_data->all_data())); } } else { m_dnd_client->post_message(Messages::WindowClient::DragCancelled()); @@ -1355,14 +1355,13 @@ Gfx::IntRect WindowManager::maximized_window_rect(const Window& window) const return rect; } -void WindowManager::start_dnd_drag(ClientConnection& client, const String& text, Gfx::Bitmap* bitmap, const String& data_type, const String& data) +void WindowManager::start_dnd_drag(ClientConnection& client, const String& text, Gfx::Bitmap* bitmap, const Core::MimeData& mime_data) { ASSERT(!m_dnd_client); m_dnd_client = client.make_weak_ptr(); m_dnd_text = text; m_dnd_bitmap = bitmap; - m_dnd_data_type = data_type; - m_dnd_data = data; + m_dnd_mime_data = mime_data; Compositor::the().invalidate_cursor(); m_active_input_tracking_window = nullptr; } diff --git a/Services/WindowServer/WindowManager.h b/Services/WindowServer/WindowManager.h index 3e2b587e194..2af28e56399 100644 --- a/Services/WindowServer/WindowManager.h +++ b/Services/WindowServer/WindowManager.h @@ -100,12 +100,11 @@ public: const ClientConnection* dnd_client() const { return m_dnd_client.ptr(); } const String& dnd_text() const { return m_dnd_text; } - const String& dnd_data_type() const { return m_dnd_data_type; } - const String& dnd_data() const { return m_dnd_data; } + const Core::MimeData& dnd_mime_data() const { return *m_dnd_mime_data; } const Gfx::Bitmap* dnd_bitmap() const { return m_dnd_bitmap; } Gfx::IntRect dnd_rect() const; - void start_dnd_drag(ClientConnection&, const String& text, Gfx::Bitmap*, const String& data_type, const String& data); + void start_dnd_drag(ClientConnection&, const String& text, Gfx::Bitmap*, const Core::MimeData&); void end_dnd_drag(); Window* active_window() { return m_active_window.ptr(); } @@ -331,8 +330,7 @@ private: WeakPtr m_dnd_client; String m_dnd_text; - String m_dnd_data_type; - String m_dnd_data; + RefPtr m_dnd_mime_data; RefPtr m_dnd_bitmap; }; diff --git a/Services/WindowServer/WindowServer.ipc b/Services/WindowServer/WindowServer.ipc index d81cbedf43d..1fdf9f070f8 100644 --- a/Services/WindowServer/WindowServer.ipc +++ b/Services/WindowServer/WindowServer.ipc @@ -94,7 +94,7 @@ endpoint WindowServer = 2 SetWindowCursor(i32 window_id, i32 cursor_type) => () SetWindowCustomCursor(i32 window_id, Gfx::ShareableBitmap cursor) => () - StartDrag([UTF8] String text, String data_type, String data, i32 bitmap_id, Gfx::IntSize bitmap_size) => (bool started) + StartDrag([UTF8] String text, HashMap mime_data, i32 bitmap_id, Gfx::IntSize bitmap_size) => (bool started) SetSystemTheme(String theme_path, [UTF8] String theme_name) => (bool success) GetSystemTheme() => ([UTF8] String theme_name)