You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Godot自定义控件自动将子节点放入指定内部容器?

Godot 实现类似 WPF ContentPresenter 的自定义窗口容器控件

Godot 是否有内置类似 ContentPresenter 的功能?

Godot 没有直接对应 WPF ContentPresenter 的内置控件,但容器类控件(如 VBoxContainer、MarginContainer)提供了基础内容承载能力。不过这些控件无法自动将父节点的子节点转移到指定容器中——这部分逻辑需要自定义实现,同时要兼顾编辑器内的实时交互支持。

自定义实现步骤

1. 搭建窗口框架场景

创建名为 CustomWindow 的场景,根节点使用 Control,并添加以下子节点:

  • TitleBar(HBoxContainer):承载标题文本和隐藏按钮,固定在控件顶部。
  • LinesContainer(VBoxContainer):作为内容容器,填充标题栏下方的剩余空间,用于承载用户添加的子节点。
  • HideButton(Button):放在 TitleBar 内,用于触发窗口隐藏逻辑。

2. 编写核心逻辑脚本

给根节点 Control 挂载以下 GDScript,实现子节点自动转移、窗口拖拽、边框缩放和隐藏功能:

extends Control

@onready var title_bar = $TitleBar
@onready var content_container = $LinesContainer
@onready var hide_button = $TitleBar/HideButton

# 拖拽状态变量
var is_dragging = false
var drag_start_mouse = Vector2.ZERO
var window_start_pos = Vector2.ZERO

# 缩放状态变量
var is_resizing = false
var resize_edge = ""
var resize_start_size = Vector2.ZERO
var resize_start_mouse = Vector2.ZERO

func _notification(what):
    # 监听子节点添加、编辑器保存前、选中事件,确保子节点自动转移
    if what == NOTIFICATION_CHILD_ADDED || what == NOTIFICATION_EDITOR_PRE_SAVE || what == NOTIFICATION_EDITOR_SELECTED:
        _reorganize_children()

func _reorganize_children():
    # 遍历所有直接子节点,排除内置控件后转移到内容容器
    for child in get_children():
        if child not in [title_bar, content_container, hide_button]:
            if child.get_parent() != content_container:
                child.reparent(content_container)

# 标题栏拖拽逻辑
func _on_title_bar_input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT:
            is_dragging = event.pressed
            if is_dragging:
                drag_start_mouse = get_global_mouse_position()
                window_start_pos = position
    elif event is InputEventMouseMotion and is_dragging:
        position = window_start_pos + (get_global_mouse_position() - drag_start_mouse)

# 边框缩放逻辑(以右下角为例,可扩展其他边缘)
func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT:
            var global_rect = get_global_rect()
            var mouse_pos = get_global_mouse_position()
            # 判断鼠标是否在右下角缩放区域(10px 边缘)
            if mouse_pos.x > global_rect.end.x - 10 && mouse_pos.y > global_rect.end.y - 10:
                is_resizing = event.pressed
                if is_resizing:
                    resize_edge = "bottom_right"
                    resize_start_size = size
                    resize_start_mouse = mouse_pos
    elif event is InputEventMouseMotion and is_resizing:
        var delta = get_global_mouse_position() - resize_start_mouse
        if resize_edge == "bottom_right":
            size = resize_start_size + delta
            # 限制窗口最小尺寸
            size.x = max(size.x, 200)
            size.y = max(size.y, 150)

# 隐藏按钮逻辑
func _on_hide_button_pressed():
    hide()

func _ready():
    # 绑定信号(也可在编辑器内手动绑定)
    title_bar.connect("input_event", self, "_on_title_bar_input")
    hide_button.connect("pressed", self, "_on_hide_button_pressed")

3. 编辑器内的使用方式

  • 将 CustomWindow 实例拖入主场景后,直接向 CustomWindow 根节点添加子节点(如 Label、Button),脚本会自动将这些子节点转移到 LinesContainer 中。
  • 编辑器内可以直接在 LinesContainer 下添加/移除子节点,布局会实时生效,无需额外操作。

扩展说明

  • 多边缘缩放:可在 _input 方法中添加对左、上、左上/右上/左下边缘的判断逻辑,实现全方向边框缩放。
  • 布局定制:根据需求修改 LinesContainer 的类型(如改用 GridContainer)或布局属性,适配不同的内容排列需求。
  • 样式美化:通过主题(Theme)或自定义样式表,给标题栏、边框添加视觉效果,模拟原生窗口外观。

内容的提问来源于stack exchange,提问作者Blindy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:44:51