@tool extends Control @export var locked := false: set(val): locked = val if is_node_ready() and locked: display = false @export var display := false: set(val): if is_node_ready() and display != val: _toggle_display() display = val @export var boundart_rect := Rect2(0, 0, 1000, 1000): set(val): boundart_rect = val queue_redraw() @export_group("Tip", "tip_") @export var tip_content := "": set(val): tip_content = val if is_node_ready(): reload() @export var tip_font_size := 12 @export var tip_font_color := Color(1, 1, 1) @export var tip_item_position := Vector2(220, 220) @onready var tip_container = $Tips as GridContainer @onready var items_root = $ItemsRoot as Node2D var sty_box_empty := StyleBoxEmpty.new() var item_scene := preload("res://scene/journal/journal_item.tscn") var tip_script := preload("res://scene/journal/journal_tip.gd") var item_script := preload("res://scene/journal/journal_item.gd") var items_dict := {} func _ready() -> void: reload() if GlobalConfig.DEBUG: locked = false display = true else: locked = true display = false _toggle_display(false) func reload(): for c in tip_container.get_children(): c.queue_free() for v in items_dict.values(): v.queue_free() items_dict.clear() var btn_name_prefix = "tip_btn_" for i in range(tip_content.length()): var tip = tip_content[i] as String var btn_margin = MarginContainer.new() btn_margin.set("theme_override_constants/margin_left", -5) btn_margin.set("theme_override_constants/margin_right", -5) btn_margin.set("theme_override_constants/margin_top", -4) btn_margin.set("theme_override_constants/margin_bottom", -4) var tip_btn = Button.new() tip_btn.flat = true tip_btn.text = tip tip_btn.toggle_mode = true tip_btn.set("theme_override_styles/focus", sty_box_empty) tip_btn.set("theme_override_font_sizes/font_size", tip_font_size) tip_btn.custom_minimum_size = Vector2(tip_font_size, tip_font_size) tip_btn.add_theme_color_override("font_color", tip_font_color) tip_btn.size_flags_horizontal = Control.SIZE_SHRINK_CENTER tip_btn.size_flags_vertical = Control.SIZE_SHRINK_CENTER tip_btn.set_script(tip_script) tip_btn.toggled.connect(_on_toggle_btn.bind(tip, tip_btn)) tip_btn.name = btn_name_prefix + str(i) btn_margin.add_child(tip_btn) tip_container.add_child(btn_margin) # items_dict[tip] = tip_btn func _on_toggle_btn(toggled_on: bool, content: String, btn) -> void: if not toggled_on and items_dict.has(btn.name): items_dict[btn.name].queue_free() items_dict.erase(content) elif toggled_on: var item = item_scene.instantiate() item.label_text = content item.boundart_rect = boundart_rect item.position = tip_item_position + Vector2(randf_range(-10, 10), randf_range(-10, 10)) items_root.add_child(item) items_dict[btn.name] = item func _draw() -> void: if Engine.is_editor_hint(): # draw boundart_rect frame draw_rect(boundart_rect, Color(1, 0, 0), false, 2) var display_tween: Tween func _toggle_display(use_tween := true): if display_tween and display_tween.is_running(): display_tween.kill() if display: if use_tween: display_tween = create_tween() # from bottom to top display_tween.tween_property(self, "position:y", 0.0, 0.5).set_trans(Tween.TRANS_CUBIC) # unlock items after display display_tween.tween_callback(_toggle_items_lock.bind(false)) else: position.y = 0 _toggle_items_lock(false) else: # lock items before hide _toggle_items_lock(true) if use_tween: display_tween = create_tween() # from top to bottom display_tween.tween_property(self, "position:y", 320.0, 0.5).set_trans( Tween.TRANS_CUBIC ) else: position.y = 320.0 func _toggle_items_lock(lock: bool) -> void: for v in items_dict.values(): v.locked = lock func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("toggle_journal"): if locked: # TODO 使用 icon,去重 SceneManager.pop_notification("🔒") return display = !display get_viewport().set_input_as_handled()