extends CanvasLayer ## Shared responsive menu shell. No native window chrome or system dialogs. signal closed const INK := Color("ecf2ed") const MUTED := Color("8fa8b7") const CYAN := Color("83d4df") const GOLD := Color("edbc7c") const DISPLAY_FONT := preload("res://assets/fonts/font2.ttf") var heading := "" var subtitle := "" var marker := "RANDOLOOT / SYSTEMY POKŁADOWE" var preferred_size := Vector2(780,590) var body: VBoxContainer var footer: HBoxContainer var panel: PanelContainer var _root: Control var _closing := false func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS layer = 140 _root = Control.new() _root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) add_child(_root) var shade := ColorRect.new() shade.color = Color(0.005,0.012,0.025,0.78) shade.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _root.add_child(shade) panel = PanelContainer.new() panel.add_theme_stylebox_override("panel", box(Color("0b1825"),Color("416471"),24)) _root.add_child(panel) var column := VBoxContainer.new() column.add_theme_constant_override("separation",16) panel.add_child(column) var top := HBoxContainer.new() column.add_child(top) var eyebrow := label(top,tr(marker),10,CYAN) eyebrow.size_flags_horizontal = Control.SIZE_EXPAND_FILL var close_button := button(top,"×",_close,false) close_button.custom_minimum_size = Vector2(32,30) close_button.add_theme_font_size_override("font_size",20) close_button.tooltip_text = tr("Zamknij") + " · Esc" var title := label(column,tr(heading),28,INK) title.add_theme_font_override("font",DISPLAY_FONT) if not subtitle.is_empty(): var description := label(column,tr(subtitle),13,MUTED) description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART column.add_child(rule()) var scroll := ScrollContainer.new() scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED column.add_child(scroll) body = VBoxContainer.new() body.size_flags_horizontal = Control.SIZE_EXPAND_FILL body.add_theme_constant_override("separation",16) scroll.add_child(body) column.add_child(rule()) footer = HBoxContainer.new() footer.alignment = BoxContainer.ALIGNMENT_END footer.add_theme_constant_override("separation",10) column.add_child(footer) get_viewport().size_changed.connect(_layout) panel.minimum_size_changed.connect(_layout) _layout.call_deferred() func _layout() -> void: var viewport_size := get_viewport().get_visible_rect().size var extent := preferred_size.min(viewport_size-Vector2(48,48)) panel.size = extent var factor := minf(1.0,minf((viewport_size.x-48)/maxf(1,panel.size.x),(viewport_size.y-48)/maxf(1,panel.size.y))) panel.scale = Vector2.ONE*factor panel.position = (viewport_size-panel.size*factor)*0.5 func _unhandled_input(event: InputEvent) -> void: if visible and event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_ESCAPE: get_viewport().set_input_as_handled() _close() func _close() -> void: if _closing: return _closing = true hide() closed.emit() queue_free() func popup_centered() -> void: show() _layout() static func box(background: Color, border: Color, margin: int = 14) -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = background style.border_color = border style.set_border_width_all(1) style.set_corner_radius_all(9) style.set_content_margin_all(margin) style.shadow_color = Color(0,0,0,0.25) style.shadow_size = 12 return style static func rule() -> ColorRect: var line := ColorRect.new() line.custom_minimum_size.y = 1 line.color = Color("304653") return line static func label(parent: Node, caption: String, font_size: int = 14, color: Color = INK) -> Label: var node := Label.new() node.text = caption node.add_theme_font_override("font",ThemeDB.fallback_font) node.add_theme_font_size_override("font_size",font_size) node.add_theme_color_override("font_color",color) parent.add_child(node) return node static func button(parent: Node, caption: String, action: Callable, primary: bool = false) -> Button: var node := Button.new() node.text = caption node.custom_minimum_size = Vector2(112,42) node.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND node.add_theme_font_override("font",ThemeDB.fallback_font) node.add_theme_font_size_override("font_size",14) style_button(node, primary) node.pressed.connect(action) parent.add_child(node) return node static func style_button(node: Button, primary: bool = false) -> void: var accent := GOLD if primary else CYAN node.add_theme_color_override("font_color",INK) node.add_theme_color_override("font_hover_color",Color.WHITE) node.add_theme_color_override("font_disabled_color",Color("617681")) node.add_theme_stylebox_override("normal",box(Color("30291f") if primary else Color("102431"),Color(accent,0.45),10)) node.add_theme_stylebox_override("hover",box(Color("453725") if primary else Color("1b3b4a"),accent,10)) node.add_theme_stylebox_override("pressed",box(Color("3c3023") if primary else Color("183b47"),accent,10)) node.add_theme_stylebox_override("disabled",box(Color("101b24"),Color("293d48"),10)) node.add_theme_stylebox_override("focus",box(Color(0,0,0,0),accent,10)) static func input_style(node: Control) -> void: node.add_theme_font_override("font",ThemeDB.fallback_font) node.add_theme_font_size_override("font_size",15) node.add_theme_color_override("font_color",INK) if node is LineEdit: node.add_theme_stylebox_override("normal",box(Color("07131d"),Color("345461"),12)) node.add_theme_stylebox_override("focus",box(Color("0c1e2b"),CYAN,12)) elif node is Button: style_button(node)