diff --git a/scenes/ccs_cases_2/doctor_office.tscn b/scenes/ccs_cases_2/doctor_office.tscn index 7cfbdf5..16b2271 100644 --- a/scenes/ccs_cases_2/doctor_office.tscn +++ b/scenes/ccs_cases_2/doctor_office.tscn @@ -316,12 +316,13 @@ theme_override_constants/separation = 20 [node name="Header" type="HBoxContainer" parent="ComputerOverlay/VitalsWindow/Margin/Content"] layout_mode = 2 +mouse_default_cursor_shape = 13 [node name="Title" type="Label" parent="ComputerOverlay/VitalsWindow/Margin/Content/Header"] layout_mode = 2 size_flags_horizontal = 3 theme_override_font_sizes/font_size = 30 -text = "Nurse Vital Signs" +text = "Vitals" vertical_alignment = 1 [node name="Close" type="Button" parent="ComputerOverlay/VitalsWindow/Margin/Content/Header"] diff --git a/scripts/ui/ccs_cases_2/doctor_office.gd b/scripts/ui/ccs_cases_2/doctor_office.gd index 7e08ae2..8f47281 100644 --- a/scripts/ui/ccs_cases_2/doctor_office.gd +++ b/scripts/ui/ccs_cases_2/doctor_office.gd @@ -35,6 +35,7 @@ const DIALOGUE_BUBBLE_VERTICAL_CHROME := 95.0 @onready var vitals_launcher: Button = $ComputerOverlay/VitalsLauncher @onready var vitals_window: PanelContainer = $ComputerOverlay/VitalsWindow @onready var vitals_close_button: Button = $ComputerOverlay/VitalsWindow/Margin/Content/Header/Close +@onready var vitals_header: HBoxContainer = $ComputerOverlay/VitalsWindow/Margin/Content/Header @onready var vitals_rows: GridContainer = $ComputerOverlay/VitalsWindow/Margin/Content/Rows @onready var vitals_empty: Label = $ComputerOverlay/VitalsWindow/Margin/Content/Empty @@ -53,6 +54,8 @@ var dialogue_reveal_complete := false var dialogue_layout_generation := 0 var dialogue_input_enabled := true var computer_available := true +var vitals_dragging := false +var vitals_drag_offset := Vector2.ZERO func _ready() -> void: @@ -62,6 +65,7 @@ func _ready() -> void: computer_close_button.pressed.connect(close_computer) vitals_launcher.pressed.connect(open_vitals) vitals_close_button.pressed.connect(close_vitals) + vitals_header.gui_input.connect(_on_vitals_header_gui_input) resized.connect(_resize_vitals_window) _style_vitals_window() _resize_vitals_window() @@ -308,6 +312,7 @@ func open_vitals() -> void: func close_vitals() -> bool: if not vitals_window.visible: return false + vitals_dragging = false vitals_window.visible = false vitals_launcher.visible = true return true @@ -416,6 +421,31 @@ func _resize_vitals_window() -> void: vitals_window.offset_bottom = window_size.y * 0.5 +func _on_vitals_header_gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: + if event.pressed: + if vitals_close_button.get_global_rect().has_point(event.global_position): + return + vitals_dragging = true + vitals_drag_offset = event.global_position - vitals_window.global_position + else: + vitals_dragging = false + vitals_header.accept_event() + elif event is InputEventMouseMotion and vitals_dragging: + var target_global: Vector2 = event.global_position - vitals_drag_offset + _set_vitals_window_position(target_global - global_position) + vitals_header.accept_event() + + +func _set_vitals_window_position(target: Vector2) -> void: + var maximum := Vector2( + maxf(0.0, size.x - vitals_window.size.x), + maxf(0.0, size.y - vitals_window.size.y)) + vitals_window.position = Vector2( + clampf(target.x, 0.0, maximum.x), + clampf(target.y, 0.0, maximum.y)) + + func _event_targets_computer_launcher(event: InputEvent) -> bool: if not computer_launcher.visible: return false diff --git a/tests/ccs_cases_2_ui_test.gd b/tests/ccs_cases_2_ui_test.gd index 8177d2d..1e0512b 100644 --- a/tests/ccs_cases_2_ui_test.gd +++ b/tests/ccs_cases_2_ui_test.gd @@ -95,8 +95,14 @@ func _run() -> void: _assert(screen.active_scene.vitals_launcher.visible, "the computer desktop should expose a Vitals app") screen.active_scene.vitals_launcher.pressed.emit() _assert(screen.active_scene.is_vitals_open(), "the Vitals app should open a nurse vital-sign window") + _assert(screen.active_scene.vitals_header.get_node("Title").text == "Vitals", "the vital window should use the concise Vitals heading") _assert(_vital_values(screen.active_scene) == ["36.8 C (98.2 F)", "76/min", "14/min", "122 mm Hg", "76 mm Hg", "99% on room air"], "the Vitals app should render the active attempt data in clinical order") _assert(_vital_values_fit(screen.active_scene), "vital values should retain a readable column without character-by-character wrapping") + screen.active_scene._set_vitals_window_position(Vector2(75.0, 55.0)) + _assert(screen.active_scene.vitals_window.position.is_equal_approx(Vector2(75.0, 55.0)), "the Vitals window should support repositioning by its header") + screen.active_scene._set_vitals_window_position(Vector2(-500.0, 5000.0)) + var max_vitals_position: Vector2 = screen.active_scene.size - screen.active_scene.vitals_window.size + _assert(screen.active_scene.vitals_window.position.is_equal_approx(Vector2(0.0, max_vitals_position.y)), "dragging should keep the Vitals window inside the computer viewport") _assert(screen.close_transient_overlay(), "the first Escape should close the Vitals app") _assert(not screen.active_scene.is_vitals_open() and screen.active_scene.is_computer_open(), "closing Vitals should return to the computer desktop") _assert(screen.close_transient_overlay(), "the second Escape should close the computer")