如何让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
相关产品推荐
相关产品推荐

