aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--logic/database.gd144
-rw-r--r--logic/stage.gd150
-rw-r--r--main.tscn61
-rw-r--r--pointer_input_sensor.gd38
-rw-r--r--readme.md18
5 files changed, 192 insertions, 219 deletions
diff --git a/logic/database.gd b/logic/database.gd
index 41ba679..c492321 100644
--- a/logic/database.gd
+++ b/logic/database.gd
@@ -22,16 +22,19 @@ const ENTRY_PROTOTYPE: Dictionary = {
"is_urgency": false,
"notes": "",
}
+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: Stage = get_node("/root/main/stage")
onready var delete_button: Button = get_node("actions/delete")
onready var edit_button: Button = get_node("actions/edit")
onready var add_button: Button = get_node("actions/add")
-onready var v_scroll_bar: ScrollBar = get_v_scroll() # @DAM Stop scroll inertia when buttons are pressed.
+onready var v_scroll_bar: ScrollBar = get_v_scroll()
onready var drag_sensor: PointerInputSensor = get_node("drag_sensor")
@@ -53,66 +56,25 @@ func _ready():
stage.connect("save", self, "save")
stage.connect("discard", self, "discard")
- drag_sensor.connect("on_press", self, "on_press")
- drag_sensor.connect("on_drag", self, "on_drag")
- drag_sensor.connect("on_end_drag", self, "on_end_drag")
- drag_sensor.connect("on_click", self, "on_click")
+ 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))
-# @DAM Cleanup this code.
-export var scroll_velocity_decaying_factor: float = 2.5
-var drag_velocity := 0.0
-var is_dragging := false
-
func _process(delta: float):
- if is_dragging == false && abs(drag_velocity) > 0.5:
- drag_velocity *= clamp((1.0 - scroll_velocity_decaying_factor * delta), 0.0, 1.0)
- v_scroll_bar.value -= drag_velocity * delta
-
-func on_click(pointer: PointerInputSensor.PointerInputData):
- 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)
-
-func on_press(pointer: PointerInputSensor.PointerInputData):
- is_dragging = true
-
-func on_end_drag(pointer: PointerInputSensor.PointerInputData):
- is_dragging = false
-
-func on_drag(pointer: PointerInputSensor.PointerInputData):
- is_dragging = true
- drag_velocity = pointer.velocity.y
- v_scroll_bar.value -= pointer.relative_position.y
-
-
+ # 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):
- if visible == false:
+ # @DAM Yet to be polished.
+ if visible == false || has_focus() == false:
return
if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST:
@@ -127,11 +89,7 @@ func get_entry_view(database_entry: Dictionary) -> String:
func item_selected(index: int):
- if selected_idx >= 0:
- set_item_text(selected_idx, get_entry_view(db[selected_idx]))
selected_idx = index
- set_item_text(selected_idx, "> " + get_entry_view(db[selected_idx]) + " <")
-
func clear_selection():
@@ -223,40 +181,6 @@ func load_database(file_path: String = DATABASE_FILE_PATH):
db.append(entry)
fake_database()
- return
-
- db = [
- instance_entry({
- "process_id": "000001",
- "surgery_id": "100000",
- "place": "central",
- "date": "2020-01-31",
- }),
- instance_entry({
- "process_id": "000002",
- "surgery_id": "200000",
- "place": "central",
- "date": "2020-02-31",
- }),
- instance_entry({
- "process_id": "000003",
- "surgery_id": "300000",
- "place": "central",
- "date": "2020-03-31",
- }),
- instance_entry({
- "process_id": "000004",
- "surgery_id": "400000",
- "place": "central",
- "date": "2020-04-31",
- }),
- instance_entry({
- "process_id": "000012",
- "surgery_id": "210000",
- "place": "central",
- "date": "2020-12-31",
- }),
- ]
func store_database(file_path: String = DATABASE_FILE_PATH):
@@ -321,3 +245,43 @@ func fake_database():
"date": "%04d-%02d-%02d" % [date_year, date_month, date_day]
}))
+
+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)
+
+
diff --git a/logic/stage.gd b/logic/stage.gd
index 62e0a58..27962c6 100644
--- a/logic/stage.gd
+++ b/logic/stage.gd
@@ -4,21 +4,10 @@ class_name Stage
signal save # (database_entry: Dictionary)
signal discard # ()
-#onready var process_id: LineEdit = get_node("touch_scroll/controls/process_id")
-#onready var surgery_id: LineEdit = get_node("touch_scroll/controls/surgery_id")
-#onready var date: DatePicker = get_node("touch_scroll/controls/date_picker")
-#onready var place: LineEdit = get_node("touch_scroll/controls/place")
-#onready var anesthetic: LineEdit = get_node("touch_scroll/controls/anesthetic")
-#onready var first_assistant: LineEdit = get_node("touch_scroll/controls/first_assistant")
-#onready var type: LineEdit = get_node("touch_scroll/controls/type")
-#onready var sub_type: LineEdit = get_node("touch_scroll/controls/sub_type")
-#onready var sub_sub_type: LineEdit = get_node("touch_scroll/controls/sub_sub_type")
-#onready var pathology: LineEdit = get_node("touch_scroll/controls/pathology")
-#onready var intervention: LineEdit = get_node("touch_scroll/controls/intervention")
-#onready var is_urgency: Button = get_node("touch_scroll/controls/is_urgency")
-#onready var notes: LineEdit = get_node("touch_scroll/controls/notes")
-#onready var save_button: Button = get_node("touch_scroll/controls/save")
-#onready var discard_button: Button = get_node("touch_scroll/controls/discard")
+const POINTER_VELOCITY_DECAYING_FACTOR: float = 2.5
+
+var is_pointer_dragging := false
+var pointer_drag_velocity := 0.0
onready var process_id: LineEdit = get_node("controls/process_id")
onready var surgery_id: LineEdit = get_node("controls/surgery_id")
@@ -35,94 +24,46 @@ onready var is_urgency: Button = get_node("controls/is_urgency")
onready var notes: LineEdit = get_node("controls/notes")
onready var save_button: Button = get_node("controls/save")
onready var discard_button: Button = get_node("controls/discard")
+onready var v_scroll_bar: VScrollBar = get_v_scrollbar()
func _ready():
save_button.connect("pressed", self, "save_action")
discard_button.connect("pressed", self, "discard_action")
-
for it in get_node("controls").get_children():
-# print("%s" % it.name)
+ it = it as Control
match it.name:
"date_picker", "save", "discard":
- print("- %s" % it.name)
-
+ pass
+
_:
-# "first_assistant":
- print("+ %s" % it.name)
var drag_sensor = PointerInputSensor.new()
+ it.add_child(drag_sensor)
+
drag_sensor.name = "drag_sensor"
- drag_sensor.mouse_default_cursor_shape = Control.CURSOR_IBEAM
- (it as Control).add_child(drag_sensor)
drag_sensor.anchor_right = 1.0
drag_sensor.anchor_bottom = 1.0
- drag_sensor.connect("on_press", self, "on_press")
- drag_sensor.connect("on_drag", self, "on_drag")
- drag_sensor.connect("on_end_drag", self, "on_end_drag")
- drag_sensor.connect("on_click", self, "on_click")
- drag_sensor.on_process = funcref(self, "bazinga")
-
-
-# @DAM Cleanup this code.
-onready var v_scroll_bar: VScrollBar = get_v_scrollbar()
-export var scroll_velocity_decaying_factor: float = 2.5
-var drag_velocity := 0.0
-var is_dragging := false
+ 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")
+
+ it.connect("focus_entered", drag_sensor, "set_mouse_filter", [Control.MOUSE_FILTER_IGNORE])
+ it.connect("focus_entered", drag_sensor, "mouse_default_cursor_shape", [Control.CURSOR_IBEAM])
+ it.connect("focus_exited", drag_sensor, "set_mouse_filter", [Control.MOUSE_FILTER_STOP])
+ it.connect("focus_exited", drag_sensor, "mouse_default_cursor_shape", [Control.CURSOR_ARROW])
+
+ if it is LineEdit:
+ it.connect("focus_exited", it, "deselect")
-func bazinga(delta: float, pointer: PointerInputSensor):
- if (pointer.get_parent() as Control).has_focus():
- pointer.mouse_filter = Control.MOUSE_FILTER_IGNORE
- else:
- pointer.mouse_filter = Control.MOUSE_FILTER_STOP
func _process(delta: float):
- if is_dragging == false && abs(drag_velocity) > 0.5:
- drag_velocity *= clamp((1.0 - scroll_velocity_decaying_factor * delta), 0.0, 1.0)
- v_scroll_bar.value -= drag_velocity * delta
-
-func on_click(pointer: PointerInputSensor.PointerInputData):
- var target: Control = pointer.target.get_parent()
- 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)
-
-func on_press(pointer: PointerInputSensor.PointerInputData):
- is_dragging = true
-
-func on_end_drag(pointer: PointerInputSensor.PointerInputData):
- is_dragging = false
-
-func on_drag(pointer: PointerInputSensor.PointerInputData):
- is_dragging = true
- drag_velocity = pointer.velocity.y
- v_scroll_bar.value -= pointer.relative_position.y
-
-
-
-
-
-
+ # 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 save_action():
@@ -179,3 +120,42 @@ func _notification(what: int):
discard_action()
+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: Control = pointer.target.get_parent()
+ 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)
+
+
diff --git a/main.tscn b/main.tscn
index 331d94d..b91cdc0 100644
--- a/main.tscn
+++ b/main.tscn
@@ -24,6 +24,7 @@ __meta__ = {
anchor_right = 1.0
anchor_bottom = 1.0
margin_top = 60.0
+mouse_filter = 2
script = ExtResource( 5 )
__meta__ = {
"_edit_use_anchors_": false
@@ -103,15 +104,15 @@ size_flags_vertical = 3
[node name="process_id" type="LineEdit" parent="stage/controls"]
margin_right = 1080.0
-margin_bottom = 52.0
+margin_bottom = 49.0
placeholder_text = "Nº Processo"
caret_blink = true
caret_blink_speed = 0.5
[node name="surgery_id" type="LineEdit" parent="stage/controls"]
-margin_top = 56.0
+margin_top = 53.0
margin_right = 1080.0
-margin_bottom = 108.0
+margin_bottom = 102.0
placeholder_text = "Nº Cirurgia"
caret_blink = true
caret_blink_speed = 0.5
@@ -119,93 +120,93 @@ caret_blink_speed = 0.5
[node name="date_picker" parent="stage/controls" instance=ExtResource( 2 )]
anchor_right = 0.0
anchor_bottom = 0.0
-margin_top = 112.0
+margin_top = 106.0
margin_right = 1080.0
-margin_bottom = 312.0
+margin_bottom = 306.0
rect_min_size = Vector2( 400, 200 )
[node name="place" type="LineEdit" parent="stage/controls"]
-margin_top = 316.0
+margin_top = 310.0
margin_right = 1080.0
-margin_bottom = 368.0
+margin_bottom = 359.0
placeholder_text = "Local"
caret_blink = true
caret_blink_speed = 0.5
[node name="anesthetic" type="LineEdit" parent="stage/controls"]
-margin_top = 372.0
+margin_top = 363.0
margin_right = 1080.0
-margin_bottom = 424.0
-placeholder_text = "Anesthesics"
+margin_bottom = 412.0
+placeholder_text = "Anestesia"
caret_blink = true
caret_blink_speed = 0.5
[node name="first_assistant" type="LineEdit" parent="stage/controls"]
-margin_top = 428.0
+margin_top = 416.0
margin_right = 1080.0
-margin_bottom = 480.0
+margin_bottom = 465.0
placeholder_text = "1º Ajudante"
caret_blink = true
caret_blink_speed = 0.5
[node name="type" type="LineEdit" parent="stage/controls"]
-margin_top = 484.0
+margin_top = 469.0
margin_right = 1080.0
-margin_bottom = 536.0
+margin_bottom = 518.0
placeholder_text = "Tipo"
caret_blink = true
caret_blink_speed = 0.5
[node name="sub_type" type="LineEdit" parent="stage/controls"]
-margin_top = 540.0
+margin_top = 522.0
margin_right = 1080.0
-margin_bottom = 592.0
+margin_bottom = 571.0
placeholder_text = "Subtipo"
caret_blink = true
caret_blink_speed = 0.5
[node name="sub_sub_type" type="LineEdit" parent="stage/controls"]
-margin_top = 596.0
+margin_top = 575.0
margin_right = 1080.0
-margin_bottom = 648.0
+margin_bottom = 624.0
placeholder_text = "Sub-Subtipo"
caret_blink = true
caret_blink_speed = 0.5
[node name="pathology" type="LineEdit" parent="stage/controls"]
-margin_top = 652.0
+margin_top = 628.0
margin_right = 1080.0
-margin_bottom = 704.0
+margin_bottom = 677.0
placeholder_text = "Patologia"
caret_blink = true
caret_blink_speed = 0.5
[node name="intervention" type="LineEdit" parent="stage/controls"]
-margin_top = 708.0
+margin_top = 681.0
margin_right = 1080.0
-margin_bottom = 760.0
+margin_bottom = 730.0
placeholder_text = "Intervenção"
caret_blink = true
caret_blink_speed = 0.5
[node name="is_urgency" type="CheckBox" parent="stage/controls"]
-margin_top = 764.0
+margin_top = 734.0
margin_right = 1080.0
-margin_bottom = 814.0
+margin_bottom = 781.0
text = "Urgência"
[node name="notes" type="LineEdit" parent="stage/controls"]
-margin_top = 818.0
+margin_top = 785.0
margin_right = 1080.0
-margin_bottom = 870.0
+margin_bottom = 834.0
placeholder_text = "Notas"
caret_blink = true
caret_blink_speed = 0.5
[node name="discard" type="Button" parent="stage/controls"]
-margin_top = 928.0
+margin_top = 838.0
margin_right = 1080.0
-margin_bottom = 978.0
+margin_bottom = 888.0
grow_horizontal = 2
grow_vertical = 2
rect_min_size = Vector2( 0, 50 )
@@ -215,9 +216,9 @@ __meta__ = {
}
[node name="save" type="Button" parent="stage/controls"]
-margin_top = 874.0
+margin_top = 892.0
margin_right = 1080.0
-margin_bottom = 924.0
+margin_bottom = 942.0
grow_horizontal = 2
grow_vertical = 2
rect_min_size = Vector2( 0, 50 )
diff --git a/pointer_input_sensor.gd b/pointer_input_sensor.gd
index 95e1b4b..27f257c 100644
--- a/pointer_input_sensor.gd
+++ b/pointer_input_sensor.gd
@@ -1,8 +1,7 @@
extends Control
class_name PointerInputSensor
-export var drag_threshold_cm: float = 0.250
-
+# All on_ACTION signals have a single argument of type PointerInputData.
signal on_press
signal on_release
#signal on_release_outside
@@ -14,19 +13,35 @@ signal on_begin_drag
signal on_drag
signal on_end_drag
+enum PointerInputAction {
+ UNDEFINED,
+ ON_PRESS,
+ ON_RELEASE,
+# ON_RELEASE_OUTSIDE,
+ ON_CLICK,
+ ON_ENTER,
+ ON_EXIT,
+# ON_EXIT_APP_WINDOW,
+ ON_BEGIN_DRAG,
+ ON_DRAG,
+ ON_END_DRAG,
+}
+
class PointerInputData:
var target: PointerInputSensor
var index := -1
var initial_position := Vector2.ZERO
var current_position := Vector2.ZERO
- var relative_position = Vector2.ZERO
+ var relative_position := Vector2.ZERO
var velocity := Vector2.ZERO
var was_dragged := false
var is_pressed := false
+ var action: int = PointerInputAction.UNDEFINED
+
+export var drag_threshold_cm: float = 0.250
var pointer: PointerInputData
var screen_dpcm: float
-var on_process: FuncRef
func _ready():
@@ -37,12 +52,8 @@ func _ready():
connect("mouse_entered", self, "_on_enter_exit", [false])
-func _process(delta: float):
- if on_process != null && on_process.is_valid():
- on_process.call_func(delta, self)
-
-
func _on_enter_exit(is_inside: bool):
+ pointer.action = PointerInputAction.ON_ENTER if is_inside else PointerInputAction.ON_EXIT
emit_signal("on_enter" if is_inside else "on_exit", pointer)
@@ -60,12 +71,16 @@ func _gui_input(event: InputEvent):
if pointer.is_pressed:
pointer.index = touch.index
pointer.initial_position = get_global_mouse_position()
+ pointer.action = PointerInputAction.ON_PRESS
emit_signal("on_press", pointer)
else:
+ pointer.action = PointerInputAction.ON_RELEASE
emit_signal("on_release", pointer)
if pointer.was_dragged == false:
+ pointer.action = PointerInputAction.ON_CLICK
emit_signal("on_click", pointer)
else:
+ pointer.action = PointerInputAction.ON_END_DRAG
emit_signal("on_end_drag", pointer)
pointer.index = -1
pointer.was_dragged = false
@@ -75,10 +90,11 @@ func _gui_input(event: InputEvent):
pointer.current_position = get_global_mouse_position()
pointer.velocity = drag.speed
pointer.relative_position = drag.relative
- # @DAM Instead of constantly converting the pointer distance to cm... just conver the drag_threshold_cm to pixels
- if pointer.was_dragged == false && pointer.current_position.distance_to(pointer.initial_position) / screen_dpcm > drag_threshold_cm:
+ if pointer.was_dragged == false && pointer.current_position.distance_to(pointer.initial_position) > drag_threshold_cm * screen_dpcm:
pointer.was_dragged = true
+ pointer.action = PointerInputAction.ON_BEGIN_DRAG
emit_signal("on_begin_drag", pointer)
+ pointer.action = PointerInputAction.ON_DRAG
emit_signal("on_drag", pointer)
diff --git a/readme.md b/readme.md
index c2d73b0..4a28a70 100644
--- a/readme.md
+++ b/readme.md
@@ -10,15 +10,27 @@ Surgery Log
- [x] Solve how entries are shared across db_screen and stage_screen;
- [x] Remove db_entry;
- [x] Rename db script/node to database;
-- [ ] Input form fields is a column of input items; each input item has (from left to right) a 'LineEdit' and a 'Button'; pressing the button shows a list with possible entries for that field, and selecting an entry will coyp that entry text to the 'LineEdit'; if the 'LineEdit' has text, it's 'Clear Button Enabled' is set and the 'Button' should become smaller (to allow more text to be visible.
+- [x] scrolling down on the database screen jumps to the end of the list immediatelly;
+- [ ] load/store database CSV file;
+- [ ] load/store filters CSV file;
+- [ ] add auto-fill button buttons on stage screen:
+ - should show a pop-up with multiple options filtered according to current filters;
+ - allow options to be scrolled by dragging;
+ - selecting option puts that text on the associated LineEdit;
+- [ ] on stage screen: if a LineEdit is has text, it's "Clear Button Enabled" is set and the auto-entry-button should become smaller to allow more text to be visible;
+- [ ] fix back button:
+ - on stage screen should show pop-up asking it changes are to be discarded;
+ - on file-pickers screen should close them;
+ - on about screen should close it;
+ - on auto-fill pop-up, should close it;
+ - on database screen, should deselect selected item, otherwise should quit the app;
+- [ ] add pop-up asking if changes are to be discarded once the stage screen's discard button is pressed;
- [ ] Share DB:
- share db via html email with db inserted in encoded downliadable field;
- Use this to send email: https://docs.godotengine.org/en/stable/classes/class_os.html#class-os-method-shell-open
-- [ ] File dialogs should be dismissed when back button is pressed (instead of closing the app);
- [ ] Translations:
- https://docs.godotengine.org/en/stable/getting_started/workflow/assets/importing_translations.html
- https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html#introduction
-- [ ] Set tabs titles dynamically: https://docs.godotengine.org/en/stable/classes/class_tabcontainer.html?highlight=tabcontainer#class-tabcontainer-method-set-tab-title
- [ ] Create two themes:
- [ ] theme_light
- [ ] theme_dark