diff --git a/platform/macos/display_server_embedded.mm b/platform/macos/display_server_embedded.mm index 2943750fc2..4b88b1d70e 100644 --- a/platform/macos/display_server_embedded.mm +++ b/platform/macos/display_server_embedded.mm @@ -811,7 +811,14 @@ DisplayServer::CursorShape DisplayServerEmbedded::cursor_get_shape() const { } void DisplayServerEmbedded::cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { - WARN_PRINT_ONCE("Custom cursor images are not supported in embedded mode."); + PackedByteArray data; + if (p_cursor.is_valid()) { + Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot); + if (image.is_valid()) { + data = image->save_png_to_buffer(); + } + } + EngineDebugger::get_singleton()->send_message("game_view:cursor_set_custom_image", { data, p_shape, p_hotspot }); } void DisplayServerEmbedded::swap_buffers() { diff --git a/platform/macos/editor/embedded_game_view_plugin.h b/platform/macos/editor/embedded_game_view_plugin.h index 21a234324e..714cfa6c61 100644 --- a/platform/macos/editor/embedded_game_view_plugin.h +++ b/platform/macos/editor/embedded_game_view_plugin.h @@ -52,6 +52,7 @@ class GameViewDebuggerMacOS : public GameViewDebugger { bool _msg_set_context_id(const Array &p_args); bool _msg_cursor_set_shape(const Array &p_args); + bool _msg_cursor_set_custom_image(const Array &p_args); bool _msg_mouse_set_mode(const Array &p_args); bool _msg_window_set_ime_active(const Array &p_args); bool _msg_window_set_ime_position(const Array &p_args); diff --git a/platform/macos/editor/embedded_game_view_plugin.mm b/platform/macos/editor/embedded_game_view_plugin.mm index f948ff69b4..fe57d0a66c 100644 --- a/platform/macos/editor/embedded_game_view_plugin.mm +++ b/platform/macos/editor/embedded_game_view_plugin.mm @@ -53,6 +53,23 @@ bool GameViewDebuggerMacOS::_msg_cursor_set_shape(const Array &p_args) { return true; } +bool GameViewDebuggerMacOS::_msg_cursor_set_custom_image(const Array &p_args) { + ERR_FAIL_COND_V_MSG(p_args.size() != 3, false, "cursor_set_custom_image: invalid number of arguments"); + + Ref image; + image.instantiate(); + PackedByteArray cursor_data = p_args[0]; + if (!cursor_data.is_empty()) { + image->load_png_from_buffer(cursor_data); + } + DisplayServer::CursorShape shape = DisplayServer::CursorShape(p_args[1]); + Vector2 hotspot = p_args[2]; + + embedded_process->get_layer_host()->cursor_set_custom_image(image, shape, hotspot); + + return true; +} + bool GameViewDebuggerMacOS::_msg_mouse_set_mode(const Array &p_args) { ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "mouse_set_mode: invalid number of arguments"); @@ -102,6 +119,7 @@ bool GameViewDebuggerMacOS::_msg_joy_stop(const Array &p_args) { void GameViewDebuggerMacOS::_init_capture_message_handlers() { parse_message_handlers["game_view:set_context_id"] = &GameViewDebuggerMacOS::_msg_set_context_id; parse_message_handlers["game_view:cursor_set_shape"] = &GameViewDebuggerMacOS::_msg_cursor_set_shape; + parse_message_handlers["game_view:cursor_set_custom_image"] = &GameViewDebuggerMacOS::_msg_cursor_set_custom_image; parse_message_handlers["game_view:mouse_set_mode"] = &GameViewDebuggerMacOS::_msg_mouse_set_mode; parse_message_handlers["game_view:window_set_ime_active"] = &GameViewDebuggerMacOS::_msg_window_set_ime_active; parse_message_handlers["game_view:window_set_ime_position"] = &GameViewDebuggerMacOS::_msg_window_set_ime_position; diff --git a/platform/macos/editor/embedded_process_macos.h b/platform/macos/editor/embedded_process_macos.h index aac0acd766..bc0716569b 100644 --- a/platform/macos/editor/embedded_process_macos.h +++ b/platform/macos/editor/embedded_process_macos.h @@ -41,12 +41,24 @@ class LayerHost final : public Control { ScriptEditorDebugger *script_debugger = nullptr; EmbeddedProcessMacOS *process = nullptr; + struct CustomCursor { + Ref image; + Vector2 hotspot; + CustomCursor() {} + CustomCursor(const Ref &p_image, const Vector2 &p_hotspot) { + image = p_image; + hotspot = p_hotspot; + } + }; + HashMap custom_cursors; + virtual void gui_input(const Ref &p_event) override; protected: void _notification(int p_what); public: + void cursor_set_custom_image(const Ref &p_image, DisplayServer::CursorShape p_shape, const Vector2 &p_hotspot); void set_script_debugger(ScriptEditorDebugger *p_debugger) { script_debugger = p_debugger; } diff --git a/platform/macos/editor/embedded_process_macos.mm b/platform/macos/editor/embedded_process_macos.mm index 5a063f8a6c..fd3f2fd38f 100644 --- a/platform/macos/editor/embedded_process_macos.mm +++ b/platform/macos/editor/embedded_process_macos.mm @@ -233,10 +233,16 @@ EmbeddedProcessMacOS::~EmbeddedProcessMacOS() { void LayerHost::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_FOCUS_ENTER: { + case NOTIFICATION_MOUSE_ENTER: { + DisplayServer *ds = DisplayServer::get_singleton(); + for (const KeyValue &E : custom_cursors) { + ds->cursor_set_custom_image(E.value.image, E.key, E.value.hotspot); + } if (script_debugger) { script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_MOUSE_ENTER }); } + } break; + case NOTIFICATION_FOCUS_ENTER: { // Restore mouse capture, if necessary. DisplayServer *ds = DisplayServer::get_singleton(); if (process->get_mouse_mode() != ds->mouse_get_mode()) { @@ -244,10 +250,16 @@ void LayerHost::_notification(int p_what) { ds->mouse_set_mode(process->get_mouse_mode()); } } break; - case NOTIFICATION_FOCUS_EXIT: { + case NOTIFICATION_MOUSE_EXIT: { + DisplayServer *ds = DisplayServer::get_singleton(); + for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) { + ds->cursor_set_custom_image(Ref(), (DisplayServer::CursorShape)i, Vector2()); + } if (script_debugger) { script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_MOUSE_EXIT }); } + } break; + case NOTIFICATION_FOCUS_EXIT: { // Temporarily set mouse state back to visible, so the user can interact with the editor. DisplayServer *ds = DisplayServer::get_singleton(); if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { @@ -261,9 +273,22 @@ void LayerHost::_notification(int p_what) { script_debugger->send_message("embed:ime_update", { ime_text, ime_selection }); } } break; + case NOTIFICATION_EXIT_TREE: + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible_in_tree()) { + DisplayServer *ds = DisplayServer::get_singleton(); + for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) { + ds->cursor_set_custom_image(Ref(), (DisplayServer::CursorShape)i, Vector2()); + } + } + } break; } } +void LayerHost::cursor_set_custom_image(const Ref &p_image, DisplayServer::CursorShape p_shape, const Vector2 &p_hotspot) { + custom_cursors[p_shape] = CustomCursor(p_image, p_hotspot); +} + void LayerHost::gui_input(const Ref &p_event) { if (!process->is_embedding_completed()) { return;