extends ItemList class_name Database const DATABASE_FILE_PATH: String = "user://database.csv" const POINTER_VELOCITY_DECAYING_FACTOR: float = 2.5 var db: Array var selected_idx: int var staged_idx: int var is_pointer_dragging := false var pointer_drag_velocity := 0.0 onready var stage := get_node("/root/main/stage") as Stage onready var delete_button := get_node("actions/delete") as Button onready var edit_button := get_node("actions/edit") as Button onready var add_button := get_node("actions/add") as Button onready var v_scroll_bar := get_v_scroll() as ScrollBar onready var drag_sensor := get_node("drag_sensor") as PointerInputSensor func _init(): selected_idx = -1 staged_idx = -1 load_database() store_database() # @DAM Only for testing. func _ready(): self.connect("item_selected", self, "item_selected") self.connect("nothing_selected", self, "clear_selection") delete_button.connect("pressed", self, "delete_action") edit_button.connect("pressed", self, "edit_action") add_button.connect("pressed", self, "add_action") stage.connect("save", self, "save_stage") stage.connect("discard", self, "discard_stage") drag_sensor.connect("on_press", self, "pointer_input_handler") drag_sensor.connect("on_drag", self, "pointer_input_handler") drag_sensor.connect("on_end_drag", self, "pointer_input_handler") drag_sensor.connect("on_click", self, "pointer_input_handler") for it in db: self.add_item(get_entry_view(it)) func _process(delta: float): # Apply drag movement inertia. if is_pointer_dragging == false && abs(pointer_drag_velocity) > 0.5: pointer_drag_velocity *= clamp((1.0 - POINTER_VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0) v_scroll_bar.value -= pointer_drag_velocity * delta func _notification(what: int): # @DAM Yet to be polished. if visible == false || has_focus() == false: return if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST: if selected_idx >= 0: clear_selection() else: get_tree().quit() func get_entry_view(database_entry: Dictionary) -> String: return "%6s | %6s | %s" % [database_entry.process_id, database_entry.surgery_id, DatabaseEntry.get_entry_date(database_entry)] func item_selected(index: int): selected_idx = index func clear_selection(): selected_idx = -1 unselect_all() func delete_action(): if selected_idx < 0: return db.remove(selected_idx) self.remove_item(selected_idx) selected_idx = -1 store_database() func edit_action(): if selected_idx < 0: return staged_idx = selected_idx self.visible = false stage.visible = true stage.set_stage(db[staged_idx]) func add_action(): self.visible = false stage.visible = true var staged := DatabaseEntry.instance_entry() stage.set_stage(staged) func scroll_down(): v_scroll_bar.value = v_scroll_bar.max_value func save_stage(entry: Dictionary): entry = DatabaseEntry.instance_entry(entry) # @DAM Maybe we could not be creating endless dictionaries? var next_selected_idx: int if staged_idx >= 0: db[staged_idx] = entry next_selected_idx = staged_idx else: db.append(entry) add_item(get_entry_view(entry)) call_deferred("scroll_down") next_selected_idx = db.size() - 1 select(next_selected_idx) emit_signal("item_selected", next_selected_idx) # Calling "select" does not trigger the "item_selected" signal. set_item_text(selected_idx, get_entry_view(db[selected_idx])) v_scroll_bar.value = float(selected_idx)/float(db.size()) * v_scroll_bar.max_value - (v_scroll_bar.page * 0.5) store_database() staged_idx = -1 self.visible = true grab_focus() func discard_stage(): staged_idx = -1 self.visible = true grab_focus() func load_database(file_path: String = DATABASE_FILE_PATH): var file := File.new() file.open(file_path, File.READ_WRITE) var headers: PoolStringArray var is_first_line := true # while file.eof_reached() == false: # @DAM Why this? while file.get_position() < file.get_len(): var csv_entry := file.get_csv_line() if is_first_line: is_first_line = false headers = csv_entry continue var entry = DatabaseEntry.instance_entry() for idx in headers.size(): var field_name := headers[idx] var field_value := csv_entry[idx] match field_name: "date_year", "date_month", "date_day": entry[field_name] = int(field_value) "is_urgency": entry[field_name] = true if field_value.strip_edges().to_lower() == "true" else false _: entry[field_name] = field_value db.append(entry) func store_database(file_path: String = DATABASE_FILE_PATH): var file := File.new() file.open(file_path, File.WRITE) var header := PoolStringArray(DatabaseEntry.ENTRY_PROTOTYPE.keys()) file.store_csv_line(header) var entry := PoolStringArray() for it in db: file.store_csv_line(it.values()) file.close() func clear_database(save_changes: bool = false): clear_selection() self.clear() db.resize(0) if save_changes: store_database() func fake_database(save_changes: bool = false): clear_database() for idx in range(500): var date_year = 1 + int(float(idx) / 365.0) var date_month = idx % 12 var date_day = idx % 365 var fake_entry = DatabaseEntry.instance_entry({ "process_id": "%06d" % idx, "surgery_id": "s%05d" % idx, "date": "%04d-%02d-%02d" % [date_year, date_month, date_day] }) db.append(fake_entry) self.add_item(get_entry_view(fake_entry)) if save_changes: store_database() func pointer_input_handler(pointer: PointerInputSensor.PointerInputData): match pointer.action: PointerInputSensor.PointerInputAction.ON_PRESS: is_pointer_dragging = true PointerInputSensor.PointerInputAction.ON_DRAG: is_pointer_dragging = true pointer_drag_velocity = pointer.velocity.y v_scroll_bar.value -= pointer.relative_position.y PointerInputSensor.PointerInputAction.ON_END_DRAG: is_pointer_dragging = false PointerInputSensor.PointerInputAction.ON_CLICK: var target := self var position := target.get_global_mouse_position() - target.rect_global_position var event_touch := InputEventScreenTouch.new() event_touch.index = 0 event_touch.position = position var event_mouse := InputEventMouseButton.new() event_mouse.button_index = BUTTON_LEFT event_mouse.button_mask = BUTTON_MASK_LEFT event_mouse.position = position event_mouse.pressed = true event_touch.pressed = true target._gui_input(event_mouse) target._gui_input(event_touch) target.grab_focus() event_mouse.pressed = false event_touch.pressed = false target._gui_input(event_mouse) target._gui_input(event_touch)