Skip to content

拖放 ​

CefTexture 支持 Godot 与嵌入式 CEF 浏览器之间的双向拖放(Drag & Drop)。这允许你:

  • 从 Godot 拖动文件或数据到浏览器(例如将文件拖放到网页上)
  • 通过信号处理从浏览器发起的拖动(例如从网页拖动图像或链接到您的游戏中)

概念 ​

拖动操作 ​

拖动操作由 DragOperation 类常量表示:

常量值描述
NONE0不允许任何操作
COPY1复制拖动的数据
LINK2创建指向拖动数据的链接
MOVE16移动拖动的数据
EVERYMAX_INT允许所有操作

DragDataInfo ​

当发生拖动事件时,您会收到一个包含正在拖动内容信息的 DragDataInfo 对象:

属性类型描述
session_idint浏览器发起拖动的唯一 ID;手动创建或外部传入的数据为 0
is_linkbool是否为 URL 链接拖动
is_filebool是否为文件拖动
is_fragmentbool是否为文本/HTML 片段拖动
link_urlString正在拖动的 URL(如果 is_link)
link_titleString链接的标题(如果 is_link)
fragment_textString纯文本内容(如果 is_fragment)
fragment_htmlStringHTML 内容(如果 is_fragment)
file_namesArray[String]文件路径列表(如果 is_file)

Godot → CEF 浏览器(将文件拖放到网页中) ​

要启用将文件或数据拖放到 CEF 浏览器中,您需要在处理 Godot 的拖放事件时调用 CefTexture 上的方法。

方法 ​

drag_enter(file_paths: Array[String], position: Vector2, allowed_ops: int) ​

当拖动进入 CefTexture 区域时调用。这会通知 CEF 拖动操作正在开始。

gdscript
func _can_drop_data(at_position: Vector2, data) -> bool:
    if data is Array:
        cef_texture.drag_enter(data, at_position, DragOperation.COPY)
        return true
    return false

drag_over(position: Vector2, allowed_ops: int) ​

当拖动在 CefTexture 上移动时重复调用。这会更新 CEF 的拖动位置。

gdscript
func _process(delta):
    if is_dragging and cef_texture.is_drag_over():
        var mouse_pos = get_local_mouse_position()
        cef_texture.drag_over(mouse_pos, DragOperation.COPY)

drag_leave() ​

当拖动在未放下的情况下离开 CefTexture 区域时调用。

gdscript
func _on_mouse_exited():
    if cef_texture.is_drag_over():
        cef_texture.drag_leave()

drag_drop(position: Vector2) ​

当用户释放拖动以将数据放到网页上时调用。

gdscript
func _drop_data(at_position: Vector2, data):
    cef_texture.drag_drop(at_position)

完整示例:文件拖放区 ​

gdscript
extends Control

@onready var cef_texture = $CefTexture

var is_dragging := false

func _ready():
    cef_texture.url = "https://example.com/upload"

func _can_drop_data(at_position: Vector2, data) -> bool:
    # Accept arrays of file paths
    if data is Array:
        cef_texture.drag_enter(data, at_position, DragOperation.COPY)
        is_dragging = true
        return true
    return false

func _drop_data(at_position: Vector2, data):
    cef_texture.drag_drop(at_position)
    is_dragging = false

func _notification(what):
    # Handle drag leaving the control
    if what == NOTIFICATION_DRAG_END and is_dragging:
        cef_texture.drag_leave()
        is_dragging = false

CEF 浏览器 → Godot(处理浏览器发起的拖动) ​

只有可见、正在处理且连接了 drag_started 信号的 CefTexture 才会接受浏览器拖动。没有监听器时会拒绝拖动,避免 CEF 等待永远不会到来的结束通知。独立使用的 CefTexture2D 不接受浏览器拖动。

请在放置处理函数中完成已接受的拖动。鼠标释放后、Godot 拖动结束时,或发生 Escape、窗口失焦、隐藏、暂停和销毁时,节点会取消尚未完成的拖动。因此,即使监听器收到信号后没有结束拖动,也不会一直阻塞浏览器。

当用户开始从网页拖动内容(例如图像、链接或选中的文本)时,CefTexture 会发出您可以连接并在游戏中处理的信号。

信号 ​

drag_started(drag_data: DragDataInfo, position: Vector2, allowed_ops: int) ​

当用户开始从网页拖动内容时发出。

gdscript
func _ready():
    cef_texture.drag_started.connect(_on_drag_started)

func _on_drag_started(drag_data: DragDataInfo, position: Vector2, allowed_ops: int):
    print("Drag started at: ", position)
    
    if drag_data.is_link:
        print("Dragging link: ", drag_data.link_url)
        # Create a preview for the dragged link
        start_custom_drag(drag_data.link_url, drag_data.link_title)
    elif drag_data.is_file:
        print("Dragging files: ", drag_data.file_names)
    elif drag_data.is_fragment:
        print("Dragging text: ", drag_data.fragment_text)

drag_cursor_updated(operation: int) ​

当拖动光标视觉效果应根据当前位置允许的操作更改时发出。

gdscript
func _ready():
    cef_texture.drag_cursor_updated.connect(_on_drag_cursor_updated)

func _on_drag_cursor_updated(operation: int):
    match operation:
        DragOperation.COPY:
            Input.set_default_cursor_shape(Input.CURSOR_DRAG)
        DragOperation.NONE:
            Input.set_default_cursor_shape(Input.CURSOR_FORBIDDEN)
        _:
            Input.set_default_cursor_shape(Input.CURSOR_ARROW)

drag_entered(drag_data: DragDataInfo, mask: int) ​

当拖动操作从外部源进入 CefTexture 时发出(通过 CEF 拖动处理器)。

gdscript
func _ready():
    cef_texture.drag_entered.connect(_on_drag_entered)

func _on_drag_entered(drag_data: DragDataInfo, mask: int):
    print("External drag entered with ops mask: ", mask)

通知 CEF 浏览器拖动结束 ​

当从浏览器发起的拖动结束时,只通知 CEF 一次:

对于延迟回调,请保存 drag_data.session_id,并使用 drag_source_ended_for_session(session_id, position, operation) 或 drag_source_system_ended_for_session(session_id)。过期或重复的结束通知会被忽略,浏览器重建后也不例外。下面的旧方法针对当前拖动,只应同步使用。结束位置使用 CEF 视图坐标,与 drag_started 提供的坐标一致。

gdscript
func finish_drag(data: DragDataInfo, position: Vector2, accepted: bool):
    var operation = DragOperation.COPY if accepted else DragOperation.NONE
    cef_texture.drag_source_ended_for_session(data.session_id, position, operation)

drag_source_ended(position: Vector2, operation: int) ​

当浏览器发起的拖动以特定放置结果结束时调用。此方法会完成浏览器拖动操作。

gdscript
func _on_drop_completed(drop_position: Vector2, was_accepted: bool):
    if cef_texture.is_dragging_from_browser():
        var op = DragOperation.COPY if was_accepted else DragOperation.NONE
        cef_texture.drag_source_ended(drop_position, op)

drag_source_system_ended() ​

当浏览器发起的拖动被取消,或没有放置结果就结束时调用。此方法会以 DragOperation.NONE 完成浏览器拖动操作。

gdscript
func _notification(what):
    if what == NOTIFICATION_DRAG_END:
        if cef_texture.is_dragging_from_browser():
            cef_texture.drag_source_system_ended()

查询方法 ​

is_dragging_from_browser() -> bool ​

如果当前有从浏览器发起的活动拖动操作,返回 true。

is_drag_over() -> bool ​

如果当前有拖动操作在 CefTexture 上方,返回 true。

完整示例:处理浏览器拖动 ​

gdscript
extends Control

@onready var cef_texture = $CefTexture
@onready var inventory = $Inventory  # Your game's inventory system

var browser_drag_data: DragDataInfo = null

func _ready():
    cef_texture.url = "https://game-shop.example.com"
    
    # Connect to drag signals
    cef_texture.drag_started.connect(_on_drag_started)
    cef_texture.drag_cursor_updated.connect(_on_drag_cursor_updated)

func _on_drag_started(drag_data: DragDataInfo, position: Vector2, allowed_ops: int):
    browser_drag_data = drag_data
    
    if drag_data.is_link:
        # User is dragging a shop item link into the game
        var preview = _create_item_preview(drag_data.link_url)
        force_drag(drag_data, preview)

func _on_drag_cursor_updated(operation: int):
    # Update cursor based on drop target validity
    if operation == DragOperation.NONE:
        $DragPreview.modulate = Color.RED
    else:
        $DragPreview.modulate = Color.WHITE

func _create_item_preview(url: String) -> Control:
    var preview = TextureRect.new()
    preview.texture = preload("res://icons/item_placeholder.png")
    return preview

# In your inventory slot's _can_drop_data:
func _can_drop_data(at_position: Vector2, data) -> bool:
    if data is DragDataInfo and data.is_link:
        return _is_valid_shop_item(data.link_url)
    return false

func _drop_data(at_position: Vector2, data):
    if data is DragDataInfo and data.is_link:
        _add_item_from_url(data.link_url)
        cef_texture.drag_source_ended(at_position, DragOperation.COPY)
        browser_drag_data = null

func _notification(what):
    if what == NOTIFICATION_DRAG_END:
        if cef_texture.is_dragging_from_browser():
            cef_texture.drag_source_system_ended()
        browser_drag_data = null