@tool extends Container ##抽屉容器节点,可以用于将子节点侧滑,开启clip content可以做到完全隐藏子节点 class_name DrawerContainer ##按钮大小 @export var custom_button_size:Vector2=Vector2.ZERO: set(val): custom_button_size=val side_button_size_changed() fit_children() fit_side_button() var button_size:Vector2=Vector2(100,200): set(val): button_size=val fit_children() fit_side_button() ##按钮场景,根节点必须为basebutton或继承自basebutton,留空则使用默认按钮 @export var button_tscn:PackedScene ##布局模式 @export var button_model:BUTTON_MODEL=BUTTON_MODEL.TOP enum BUTTON_MODEL{ ##按钮在上方,向下收纳 TOP=0, ##按钮在下方,向上收纳 END=1, ##按钮在左方,向右收纳 LEFT=2, ##按钮在右方,向左收纳 RIGHT=3, } ##动画时间 @export var animation_time:float=0.5 ##是否自动隐藏 @export var is_auto_hide:bool=false #隐藏计时器 @onready var hide_timer:Timer=Timer.new() ##是否自动收缩 @export var is_auto_shrink:bool=false ##是否禁用按钮 @export var disable_button:bool=false: set(val): disable_button=val fit_side_button() fit_side_button() if side_button!=null: if val : side_button.hide() else: side_button.show() ##动画过渡模式 @export var animation_model:Tween.TransitionType=Tween.TransitionType.TRANS_BACK ##当前持有的动画 var now_keep_tween:Tween ##当前是否打开 @export var is_open:bool=true ##持有的抽屉控制按钮的实例(内部节点) var side_button:BaseButton ##获取当前持有的侧边按钮 func get_side_button()->BaseButton: return side_button ##当前动画状态,0为完全打开,1为完全关闭 @export var rag:float=0: set(val): rag=val fit_side_button() fit_children() minimum_size_changed.emit() func _ready() -> void: add_child(hide_timer,false,Node.INTERNAL_MODE_FRONT) hide_timer.one_shot=true hide_timer.timeout.connect(hide_time_out) if is_open: rag=0 else: rag=1 if button_tscn!=null: var new_btn=button_tscn.instantiate() if new_btn is BaseButton: side_button=new_btn add_child(new_btn,false,Node.INTERNAL_MODE_FRONT) else: new_btn=Button.new() side_button=new_btn add_child(new_btn,false,Node.INTERNAL_MODE_FRONT) else: var new_btn=Button.new() side_button=new_btn add_child(new_btn,false,Node.INTERNAL_MODE_FRONT) side_button.pressed.connect(change_open) side_button.minimum_size_changed.connect(side_button_size_changed) side_button_size_changed() if disable_button: side_button.hide() resized.connect(resize) resize() _re_ready() pass # Replace with function body. ##因为占用了ready函数,所以留了一个新的虚函数给使用节点重写 func _re_ready()->void: pass ##对侧边栏按钮进行适配 func fit_side_button()->void: var rect:Rect2 if side_button!=null: match button_model: BUTTON_MODEL.TOP: rect=Rect2(0,(size.y-button_size.y)*rag,size.x,button_size.y) BUTTON_MODEL.END: rect=Rect2(0,(size.y-button_size.y)*(1-rag),size.x,button_size.y) BUTTON_MODEL.LEFT: rect=Rect2((size.x-button_size.x)*rag,0,button_size.x,size.y) BUTTON_MODEL.RIGHT: rect=Rect2((size.x-button_size.x)*(1-rag),0,button_size.x,size.y) _: rect=Rect2() fit_child_in_rect(side_button,rect) ##对子节点进行适配 func fit_children()->void: var rect:Rect2 if !disable_button: match button_model: BUTTON_MODEL.TOP: rect=Rect2(0,(size.y-button_size.y)*rag+button_size.y,size.x,size.y-button_size.y) BUTTON_MODEL.END: rect=Rect2(0,(size.y-button_size.y)*(-rag),size.x,size.y-button_size.y) BUTTON_MODEL.LEFT: rect=Rect2((size.x-button_size.x)*rag+button_size.x,0,size.x-button_size.x,size.y) BUTTON_MODEL.RIGHT: rect=Rect2((size.x-button_size.x)*(-rag),0,size.x-button_size.x,size.y) _: rect=Rect2() else: match button_model: BUTTON_MODEL.TOP: rect=Rect2(0,(size.y)*rag+button_size.y,size.x,size.y) BUTTON_MODEL.END: rect=Rect2(0,(size.y)*(-rag),size.x,size.y) BUTTON_MODEL.LEFT: rect=Rect2((size.x)*rag,0,size.x,size.y) BUTTON_MODEL.RIGHT: rect=Rect2((size.x)*(-rag),0,size.x,size.y) _: rect=Rect2() pass for i in get_children(): if i is Control: fit_child_in_rect(i,rect) ##更改窗口的开闭状态,使用动画 func change_open(opened:bool=false): if opened==is_open: return if now_keep_tween!=null: now_keep_tween.kill() if !opened: if is_auto_hide: hide_timer.start(animation_time) pass now_keep_tween=create_tween() now_keep_tween.set_trans(animation_model) now_keep_tween.tween_property(self,"rag",1,animation_time) else: now_keep_tween=create_tween() now_keep_tween.set_trans(animation_model) now_keep_tween.tween_property(self,"rag",0,animation_time) if is_auto_hide: show() hide_timer.stop() is_open=opened pass ##当此节点大小改变时触发 func resize(): fit_side_button() fit_children() ##重写的虚函数,用于获取当前节点的最小大小 func _get_minimum_size() -> Vector2: var res:Vector2=Vector2.ZERO for i in get_children(): if i is Control: var i_size:Vector2=i.get_minimum_size() res=Vector2(max(res.x,i_size.x),max(res.y,i_size.y)) if not is_auto_shrink: if !disable_button: match button_model: BUTTON_MODEL.TOP: res=Vector2(max(res.x,button_size.x),res.y+button_size.y) BUTTON_MODEL.END: res=Vector2(max(res.x,button_size.x),res.y+button_size.y) BUTTON_MODEL.LEFT: res=Vector2(res.x+button_size.x,max(res.y,button_size.y)) BUTTON_MODEL.RIGHT: res=Vector2(res.x+button_size.x,max(res.y,button_size.y)) _: res=Vector2.ZERO else: if !disable_button: match button_model: BUTTON_MODEL.TOP: res=Vector2(max(res.x,button_size.x),res.y*rag+button_size.y) BUTTON_MODEL.END: res=Vector2(max(res.x,button_size.x),res.y*rag+button_size.y) BUTTON_MODEL.LEFT: res=Vector2(res.x*rag+button_size.x,max(res.y,button_size.y)) BUTTON_MODEL.RIGHT: res=Vector2(res.x*rag+button_size.x,max(res.y,button_size.y)) _: res=Vector2.ZERO else: match button_model: BUTTON_MODEL.TOP: res=Vector2(res.x,res.y*(1-rag)) BUTTON_MODEL.END: res=Vector2(res.x*rag,res.y*(1-rag)) BUTTON_MODEL.LEFT: res=Vector2(res.x*(1-rag),res.y) BUTTON_MODEL.RIGHT: res=Vector2(res.x*rag,res.y*(1-rag)) _: res=Vector2.ZERO pass return res ##当侧边栏按钮大小更改时调用 func side_button_size_changed()->void: if side_button: var new_size=side_button.get_minimum_size() button_size=Vector2(max(custom_button_size.x,new_size.x),max(custom_button_size.y,new_size.y)) func _process(delta: float) -> void: if Engine.is_editor_hint(): fit_children() pass func hide_time_out(): hide()