From 2e5c0de21b0f35531e3d42e63c27df80cf3fad02 Mon Sep 17 00:00:00 2001 From: dam Date: Wed, 1 Dec 2021 02:26:44 +0000 Subject: Convert from native to godot --- main.gd | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 main.gd (limited to 'main.gd') diff --git a/main.gd b/main.gd new file mode 100644 index 0000000..f6c1f88 --- /dev/null +++ b/main.gd @@ -0,0 +1,38 @@ +extends Control + + +# Declare member variables here. Examples: +# var a = 2 +# var b = "text" +var _timeout: float + +# Called when the node enters the scene tree for the first time. +func _ready(): + $output.text = "Hello!" + $button.connect("pressed", self, "_pressed") + + +func _process(delta: float): + $output.text = "%s" % Engine.get_frames_per_second() + $timer.text = "%s" % _timeout + if _timeout > 0.0: + _timeout -= delta + else: + Engine.target_fps = 10.0 + +func _pressed(): + $log.text += "PRESSED\n" + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass + + +func _input(event): + Engine.target_fps = 0 + _timeout = 5.0 + + +func _unhandled_input(event): + Engine.target_fps = 0 + _timeout = 5.0 -- cgit v1.2.3 From 2c1085cf56e8fa1baf3c764752828e7f04391d7e Mon Sep 17 00:00:00 2001 From: dam Date: Mon, 6 Dec 2021 18:42:53 +0000 Subject: Add file dialogs for import/export filters. Height adjusted to virtual keyboard. --- export_presets.cfg | 6 +- fonts/font_regular.tres | 1 + licenses/font_b612.txt | 93 ------------------------- licenses/godot.txt | 20 ------ licenses/licenses.gd | 23 ------- main.gd | 45 ++++++------ main.tscn | 179 +++++++++++++++++++++++++++++++++--------------- menu/font_b612.txt | 93 +++++++++++++++++++++++++ menu/godot.txt | 20 ++++++ menu/menu.gd | 112 ++++++++++++++++++++++++++++++ project.godot | 4 +- readme.md | 8 +++ 12 files changed, 383 insertions(+), 221 deletions(-) delete mode 100644 licenses/font_b612.txt delete mode 100644 licenses/godot.txt delete mode 100644 licenses/licenses.gd create mode 100644 menu/font_b612.txt create mode 100644 menu/godot.txt create mode 100644 menu/menu.gd (limited to 'main.gd') diff --git a/export_presets.cfg b/export_presets.cfg index da53431..ae6c4cf 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -27,7 +27,7 @@ keystore/debug_password="debug.password" keystore/release="" keystore/release_user="" keystore/release_password="" -one_click_deploy/clear_previous_install=true +one_click_deploy/clear_previous_install=false version/code=1 version/name="1.0" package/unique_name="com.gudinoff.$genname" @@ -137,7 +137,7 @@ permissions/process_outgoing_calls=false permissions/read_calendar=false permissions/read_call_log=false permissions/read_contacts=false -permissions/read_external_storage=false +permissions/read_external_storage=true permissions/read_frame_buffer=false permissions/read_history_bookmarks=false permissions/read_input_state=false @@ -188,7 +188,7 @@ permissions/write_apn_settings=false permissions/write_calendar=false permissions/write_call_log=false permissions/write_contacts=false -permissions/write_external_storage=false +permissions/write_external_storage=true permissions/write_gservices=false permissions/write_history_bookmarks=false permissions/write_profile=false diff --git a/fonts/font_regular.tres b/fonts/font_regular.tres index 714269f..095114d 100644 --- a/fonts/font_regular.tres +++ b/fonts/font_regular.tres @@ -3,5 +3,6 @@ [ext_resource path="res://fonts/B612Mono-Regular.ttf" type="DynamicFontData" id=1] [resource] +size = 34 use_filter = true font_data = ExtResource( 1 ) diff --git a/licenses/font_b612.txt b/licenses/font_b612.txt deleted file mode 100644 index 2826948..0000000 --- a/licenses/font_b612.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2012 The B612 Project Authors (https://github.com/polarsys/b612) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/licenses/godot.txt b/licenses/godot.txt deleted file mode 100644 index bee83fb..0000000 --- a/licenses/godot.txt +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. -Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/licenses.gd b/licenses/licenses.gd deleted file mode 100644 index 87a8d47..0000000 --- a/licenses/licenses.gd +++ /dev/null @@ -1,23 +0,0 @@ -extends Node - -const font_b612: String = "res://licenses/font_b612.txt" -const godot: String = "res://licenses/godot.txt" - -# @DAM For now, it just keeping track of licenses and storing a sample code of how to read the files. - -func _ready(): -# load_file(file) - pass - -func load_file(file: String): - var f = File.new() - f.open(file, File.READ) - var index = 1 - while not f.eof_reached(): # iterate through all lines until the end of file is reached - var line = f.get_line() - line += " " - print(line + str(index)) - - index += 1 - f.close() - return diff --git a/main.gd b/main.gd index f6c1f88..803c2d4 100644 --- a/main.gd +++ b/main.gd @@ -1,38 +1,37 @@ extends Control - -# Declare member variables here. Examples: -# var a = 2 -# var b = "text" -var _timeout: float - -# Called when the node enters the scene tree for the first time. -func _ready(): - $output.text = "Hello!" - $button.connect("pressed", self, "_pressed") +var timeout: float +onready var controls_sensible_to_keyboard: Array = [ + self, + get_node("/root/main/about"), + get_node("/root/main/delete_filters"), + get_node("/root/main/import_filters"), + get_node("/root/main/export_filters"), +] func _process(delta: float): - $output.text = "%s" % Engine.get_frames_per_second() - $timer.text = "%s" % _timeout - if _timeout > 0.0: - _timeout -= delta + + var keyboard_height: int = OS.get_virtual_keyboard_height() + for it in controls_sensible_to_keyboard: + it.margin_bottom = -keyboard_height + + # @DAM Debug information. +# $debug.text = "%s" % Engine.get_frames_per_second() +# $debug.text = "%s" % timeout + if timeout > 0.0: + timeout -= delta else: Engine.target_fps = 10.0 -func _pressed(): - $log.text += "PRESSED\n" - -# Called every frame. 'delta' is the elapsed time since the previous frame. -#func _process(delta): -# pass - func _input(event): Engine.target_fps = 0 - _timeout = 5.0 + timeout = 3.5 func _unhandled_input(event): Engine.target_fps = 0 - _timeout = 5.0 + timeout = 3.5 + + diff --git a/main.tscn b/main.tscn index adec227..83ee8d3 100644 --- a/main.tscn +++ b/main.tscn @@ -1,13 +1,15 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=5 format=2] [ext_resource path="res://main.gd" type="Script" id=1] [ext_resource path="res://date_picker/date_picker.tscn" type="PackedScene" id=2] [ext_resource path="res://theme_white.tres" type="Theme" id=3] +[ext_resource path="res://menu/menu.gd" type="Script" id=4] [node name="main" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 theme = ExtResource( 3 ) +script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false } @@ -24,7 +26,7 @@ __meta__ = { anchor_right = 1.0 anchor_bottom = 1.0 margin_left = 4.0 -margin_top = 30.0 +margin_top = 60.0 margin_right = -4.0 margin_bottom = -4.0 scroll_horizontal_enabled = false @@ -33,24 +35,23 @@ __meta__ = { } [node name="VBoxContainer" type="VBoxContainer" parent="body/record"] -margin_right = 712.0 -margin_bottom = 1246.0 +margin_right = 1072.0 +margin_bottom = 1856.0 rect_clip_content = true size_flags_horizontal = 3 size_flags_vertical = 3 -script = ExtResource( 1 ) [node name="process_number" type="LineEdit" parent="body/record/VBoxContainer"] -margin_right = 712.0 -margin_bottom = 30.0 +margin_right = 1072.0 +margin_bottom = 52.0 placeholder_text = "Nº Processo" caret_blink = true caret_blink_speed = 0.5 [node name="surgery_number" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 34.0 -margin_right = 712.0 -margin_bottom = 64.0 +margin_top = 56.0 +margin_right = 1072.0 +margin_bottom = 108.0 placeholder_text = "Nº Cirurgia" caret_blink = true caret_blink_speed = 0.5 @@ -58,111 +59,111 @@ caret_blink_speed = 0.5 [node name="date_picker" parent="body/record/VBoxContainer" instance=ExtResource( 2 )] anchor_right = 0.0 anchor_bottom = 0.0 -margin_top = 68.0 -margin_right = 712.0 -margin_bottom = 268.0 +margin_top = 112.0 +margin_right = 1072.0 +margin_bottom = 312.0 rect_min_size = Vector2( 400, 200 ) [node name="place" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 272.0 -margin_right = 712.0 -margin_bottom = 302.0 +margin_top = 316.0 +margin_right = 1072.0 +margin_bottom = 368.0 placeholder_text = "Local" caret_blink = true caret_blink_speed = 0.5 [node name="anesthesia" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 306.0 -margin_right = 712.0 -margin_bottom = 336.0 +margin_top = 372.0 +margin_right = 1072.0 +margin_bottom = 424.0 placeholder_text = "Anesthesics" caret_blink = true caret_blink_speed = 0.5 [node name="first_aider" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 340.0 -margin_right = 712.0 -margin_bottom = 370.0 +margin_top = 428.0 +margin_right = 1072.0 +margin_bottom = 480.0 placeholder_text = "1º Ajudante" caret_blink = true caret_blink_speed = 0.5 [node name="type" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 374.0 -margin_right = 712.0 -margin_bottom = 404.0 +margin_top = 484.0 +margin_right = 1072.0 +margin_bottom = 536.0 placeholder_text = "Tipo" caret_blink = true caret_blink_speed = 0.5 [node name="sub_type" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 408.0 -margin_right = 712.0 -margin_bottom = 438.0 +margin_top = 540.0 +margin_right = 1072.0 +margin_bottom = 592.0 placeholder_text = "Subtipo" caret_blink = true caret_blink_speed = 0.5 [node name="sub_sub_type" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 442.0 -margin_right = 712.0 -margin_bottom = 472.0 +margin_top = 596.0 +margin_right = 1072.0 +margin_bottom = 648.0 placeholder_text = "Sub-Subtipo" caret_blink = true caret_blink_speed = 0.5 [node name="pathology" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 476.0 -margin_right = 712.0 -margin_bottom = 506.0 +margin_top = 652.0 +margin_right = 1072.0 +margin_bottom = 704.0 placeholder_text = "Patologia" caret_blink = true caret_blink_speed = 0.5 [node name="intervention" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 510.0 -margin_right = 712.0 -margin_bottom = 540.0 +margin_top = 708.0 +margin_right = 1072.0 +margin_bottom = 760.0 placeholder_text = "Intervenção" caret_blink = true caret_blink_speed = 0.5 [node name="urgency" type="CheckBox" parent="body/record/VBoxContainer"] -margin_top = 544.0 -margin_right = 712.0 -margin_bottom = 572.0 +margin_top = 764.0 +margin_right = 1072.0 +margin_bottom = 814.0 text = "Urgência" [node name="notes" type="LineEdit" parent="body/record/VBoxContainer"] -margin_top = 576.0 -margin_right = 712.0 -margin_bottom = 606.0 +margin_top = 818.0 +margin_right = 1072.0 +margin_bottom = 870.0 placeholder_text = "Notas" caret_blink = true caret_blink_speed = 0.5 [node name="output" type="RichTextLabel" parent="body/record/VBoxContainer"] -margin_top = 610.0 -margin_right = 712.0 -margin_bottom = 660.0 +margin_top = 874.0 +margin_right = 1072.0 +margin_bottom = 924.0 rect_min_size = Vector2( 0, 50 ) __meta__ = { "_edit_use_anchors_": false } [node name="timer" type="RichTextLabel" parent="body/record/VBoxContainer"] -margin_top = 664.0 -margin_right = 712.0 -margin_bottom = 714.0 +margin_top = 928.0 +margin_right = 1072.0 +margin_bottom = 978.0 rect_min_size = Vector2( 0, 50 ) __meta__ = { "_edit_use_anchors_": false } [node name="button" type="Button" parent="body/record/VBoxContainer"] -margin_top = 718.0 -margin_right = 712.0 -margin_bottom = 768.0 +margin_top = 982.0 +margin_right = 1072.0 +margin_bottom = 1032.0 grow_horizontal = 2 grow_vertical = 2 rect_min_size = Vector2( 0, 50 ) @@ -172,9 +173,9 @@ __meta__ = { } [node name="log" type="RichTextLabel" parent="body/record/VBoxContainer"] -margin_top = 772.0 -margin_right = 712.0 -margin_bottom = 822.0 +margin_top = 1036.0 +margin_right = 1072.0 +margin_bottom = 1086.0 rect_min_size = Vector2( 0, 50 ) __meta__ = { "_edit_use_anchors_": false @@ -198,8 +199,72 @@ anchor_left = 1.0 anchor_right = 1.0 margin_left = -32.0 margin_bottom = 26.0 -text = "#" -items = [ "Importar filtros", null, 0, false, false, 0, 0, null, "", false, "Exportar filtros", null, 0, false, false, 1, 0, null, "", false, "Apagar filtros", null, 0, false, false, 2, 0, null, "", false, "Sobre", null, 0, false, false, 3, 0, null, "", false ] +text = "≡" +script = ExtResource( 4 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="about" type="AcceptDialog" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +window_title = "" +dialog_autowrap = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="delete_filters" type="ConfirmationDialog" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +window_title = "" +dialog_text = "Are you sure you want to delete all filters?" +dialog_autowrap = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="import_filters" type="FileDialog" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +window_title = "" +mode = 0 +access = 2 +filters = PoolStringArray( "*.csv" ) +show_hidden_files = true +current_dir = "" +current_path = "" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="export_filters" type="FileDialog" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +window_title = "" +access = 2 +filters = PoolStringArray( "*.csv" ) +show_hidden_files = true +current_dir = "" +current_path = "" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="debug" type="Label" parent="."] +anchor_left = 1.0 +anchor_right = 1.0 +margin_left = -571.0 +margin_top = 220.0 +margin_bottom = 829.0 __meta__ = { "_edit_use_anchors_": false } diff --git a/menu/font_b612.txt b/menu/font_b612.txt new file mode 100644 index 0000000..2826948 --- /dev/null +++ b/menu/font_b612.txt @@ -0,0 +1,93 @@ +Copyright 2012 The B612 Project Authors (https://github.com/polarsys/b612) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/menu/godot.txt b/menu/godot.txt new file mode 100644 index 0000000..bee83fb --- /dev/null +++ b/menu/godot.txt @@ -0,0 +1,20 @@ +Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. +Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/menu/menu.gd b/menu/menu.gd new file mode 100644 index 0000000..55c74fa --- /dev/null +++ b/menu/menu.gd @@ -0,0 +1,112 @@ +extends MenuButton + +const menu_items: Array = [ + { label = "IMPORT FILTERS", action = "_menu_import_filters_action" }, + { label = "EXPORT FILTERS", action = "_menu_export_filters_action" }, + { label = "CLEAR FILTERS", action = "_menu_clear_filters_action" }, + { label = "ABOUT", action = "_menu_about_action" }, +] +const license_font_b612: String = "res://licenses/font_b612.txt" +const license_godot: String = "res://licenses/godot.txt" + +onready var popup: PopupMenu = get_popup() +onready var debug: Label = get_node("/root/main/debug") as Label + +func _ready(): + + for idx in range(menu_items.size()): + popup.add_item(menu_items[idx].label, idx) + popup.connect("id_pressed", self, "id_pressed") + +# load_file(file) + + + +func id_pressed(id: int): + debug.text += "'%d':'%s'" % [id, menu_items[id].action] + self.call_deferred(menu_items[id].action) + debug.text += "!\n" + + +var csv_file: Array +var has_permissions := false +func _menu_import_filters_action(): + +# printerr("pressed: %s" % get_stack()[0]); + debug.text += "> import: " + + if OS.get_name() == "Android": + while not has_permissions: + var permissions := Array(OS.get_granted_permissions()) + if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \ + or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"): + OS.request_permissions() + # await get_tree().create_timer(1).timeout + yield(get_tree().create_timer(1), "timeout") # - for Godot 3 branch + else: + has_permissions = true + + + + var file_dialog := get_node("/root/main/import_filters") as FileDialog + file_dialog.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + file_dialog.connect("file_selected", self, "file_selected", [], CONNECT_ONESHOT) + file_dialog.show_modal(true) + file_dialog.invalidate() +# printerr("download: '%s'" % OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)) + +func file_selected(path: String): + debug.text += "'%s'\n" % path + + var file = File.new() + file.open(path, File.READ) + while file.eof_reached() == false: + var line = file.get_line() + csv_file.append(line) + printerr("%s" % line) + file.close() + +func _menu_export_filters_action(): + debug.text += "> export: \n" +# printerr("pressed: %s" % get_stack()[0]); + var file_dialog := get_node("/root/main/export_filters") as FileDialog + file_dialog.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + file_dialog.connect("file_selected", self, "file_selected_export", [], CONNECT_ONESHOT) + file_dialog.show_modal(true) + file_dialog.invalidate() + +func file_selected_export(path: String): + debug.text += "'%s'\n" % path + var file = File.new() + file.open(path, File.WRITE) + for ln in csv_file: + file.store_line(ln) + file.close() + + +func _menu_clear_filters_action(): + debug.text += "> clear\n" +# printerr("pressed: %s" % get_stack()[0]); + (get_node("/root/main/delete_filters") as ConfirmationDialog).show_modal(true) + + +func _menu_about_action(): +# printerr("pressed: %s" % get_stack()[0]); + debug.text += "> about\n" + (get_node("/root/main/about") as AcceptDialog).show_modal() + + +#func load_file(file: String): +# var f = File.new() +# f.open(file, File.READ) +# var index = 1 +# while not f.eof_reached(): # iterate through all lines until the end of file is reached +# var line = f.get_line() +# line += " " +# print(line + str(index)) +# +# index += 1 +# f.close() +# return + + diff --git a/project.godot b/project.godot index 4c68d91..6657a50 100644 --- a/project.godot +++ b/project.godot @@ -36,8 +36,8 @@ config/icon="res://icon.png" [display] -window/size/width=720 -window/size/height=1280 +window/size/width=1080 +window/size/height=1920 window/size/test_width=576 window/size/test_height=1024 window/energy_saving/keep_screen_on=false diff --git a/readme.md b/readme.md index deaf374..8dae6e0 100644 --- a/readme.md +++ b/readme.md @@ -7,5 +7,13 @@ Surgery Log - [ ] 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 -- cgit v1.2.3 From 9724fb73abf9fd3760a4f3f3ac941a119708c97b Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 16 Dec 2021 01:17:34 +0000 Subject: First prototype with working database and staging area. Fix incorrect warp in date picker. Disable physics engine. --- date_picker/date_picker.gd | 1 - date_picker/date_picker.tscn | 1 + date_picker/value_picker.gd | 2 +- icon.png | Bin 118840 -> 0 bytes icon.png.import | 35 ------- icons/add.png | Bin 0 -> 3030 bytes icons/add.png.import | 38 ++++++++ icons/delete.png | Bin 0 -> 4410 bytes icons/delete.png.import | 38 ++++++++ icons/edit.png | Bin 0 -> 5449 bytes icons/edit.png.import | 38 ++++++++ icons/icon.png | Bin 0 -> 19327 bytes icons/icon.png.import | 35 +++++++ logic/database.gd | 226 +++++++++++++++++++++++++++++++++++++++++++ logic/stage.gd | 88 +++++++++++++++++ main.gd | 8 +- main.tscn | 190 ++++++++++++++++++++---------------- project.godot | 17 +++- readme.md | 11 ++- 19 files changed, 602 insertions(+), 126 deletions(-) delete mode 100755 icon.png delete mode 100644 icon.png.import create mode 100644 icons/add.png create mode 100644 icons/add.png.import create mode 100644 icons/delete.png create mode 100644 icons/delete.png.import create mode 100755 icons/edit.png create mode 100644 icons/edit.png.import create mode 100755 icons/icon.png create mode 100644 icons/icon.png.import create mode 100644 logic/database.gd create mode 100644 logic/stage.gd (limited to 'main.gd') diff --git a/date_picker/date_picker.gd b/date_picker/date_picker.gd index 6298156..fb9cca2 100644 --- a/date_picker/date_picker.gd +++ b/date_picker/date_picker.gd @@ -1,5 +1,4 @@ extends Control - class_name DatePicker const days_per_month: Dictionary = { diff --git a/date_picker/date_picker.tscn b/date_picker/date_picker.tscn index 6506c31..21573dc 100644 --- a/date_picker/date_picker.tscn +++ b/date_picker/date_picker.tscn @@ -19,6 +19,7 @@ script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false } +min_value = 1 max_value = 9999 [node name="previous" type="Label" parent="year"] diff --git a/date_picker/value_picker.gd b/date_picker/value_picker.gd index d25a143..5e77294 100644 --- a/date_picker/value_picker.gd +++ b/date_picker/value_picker.gd @@ -120,7 +120,7 @@ func input_focus_entered(): func input_focus_exited(): if input.text.is_valid_integer(): - value = wrapi(int(input.text), min_value, max_value) + value = wrapi(int(input.text), min_value, max_value + 1) input.visible = false diff --git a/icon.png b/icon.png deleted file mode 100755 index e4d20ec..0000000 Binary files a/icon.png and /dev/null differ diff --git a/icon.png.import b/icon.png.import deleted file mode 100644 index a4c02e6..0000000 --- a/icon.png.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.png" -dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/bptc_ldr=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -process/invert_color=false -process/normal_map_invert_y=false -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/icons/add.png b/icons/add.png new file mode 100644 index 0000000..074a6c1 Binary files /dev/null and b/icons/add.png differ diff --git a/icons/add.png.import b/icons/add.png.import new file mode 100644 index 0000000..f726327 --- /dev/null +++ b/icons/add.png.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="StreamTexture" +path.s3tc="res://.import/add.png-21abb41ff0da259ea2ccbb4becce111a.s3tc.stex" +path.etc2="res://.import/add.png-21abb41ff0da259ea2ccbb4becce111a.etc2.stex" +path.etc="res://.import/add.png-21abb41ff0da259ea2ccbb4becce111a.etc.stex" +metadata={ +"imported_formats": [ "s3tc", "etc2", "etc" ], +"vram_texture": true +} + +[deps] + +source_file="res://icons/add.png" +dest_files=[ "res://.import/add.png-21abb41ff0da259ea2ccbb4becce111a.s3tc.stex", "res://.import/add.png-21abb41ff0da259ea2ccbb4becce111a.etc2.stex", "res://.import/add.png-21abb41ff0da259ea2ccbb4becce111a.etc.stex" ] + +[params] + +compress/mode=2 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=true +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=false +process/premult_alpha=true +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/icons/delete.png b/icons/delete.png new file mode 100644 index 0000000..2b1f562 Binary files /dev/null and b/icons/delete.png differ diff --git a/icons/delete.png.import b/icons/delete.png.import new file mode 100644 index 0000000..eaad4c1 --- /dev/null +++ b/icons/delete.png.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="StreamTexture" +path.s3tc="res://.import/delete.png-48500a9e66ed6e102e89aa5542feee73.s3tc.stex" +path.etc2="res://.import/delete.png-48500a9e66ed6e102e89aa5542feee73.etc2.stex" +path.etc="res://.import/delete.png-48500a9e66ed6e102e89aa5542feee73.etc.stex" +metadata={ +"imported_formats": [ "s3tc", "etc2", "etc" ], +"vram_texture": true +} + +[deps] + +source_file="res://icons/delete.png" +dest_files=[ "res://.import/delete.png-48500a9e66ed6e102e89aa5542feee73.s3tc.stex", "res://.import/delete.png-48500a9e66ed6e102e89aa5542feee73.etc2.stex", "res://.import/delete.png-48500a9e66ed6e102e89aa5542feee73.etc.stex" ] + +[params] + +compress/mode=2 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=true +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=false +process/premult_alpha=true +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/icons/edit.png b/icons/edit.png new file mode 100755 index 0000000..01fb607 Binary files /dev/null and b/icons/edit.png differ diff --git a/icons/edit.png.import b/icons/edit.png.import new file mode 100644 index 0000000..baee812 --- /dev/null +++ b/icons/edit.png.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="StreamTexture" +path.s3tc="res://.import/edit.png-d9148ff44b5b1c8f3b0a53a3c3c7c1c0.s3tc.stex" +path.etc2="res://.import/edit.png-d9148ff44b5b1c8f3b0a53a3c3c7c1c0.etc2.stex" +path.etc="res://.import/edit.png-d9148ff44b5b1c8f3b0a53a3c3c7c1c0.etc.stex" +metadata={ +"imported_formats": [ "s3tc", "etc2", "etc" ], +"vram_texture": true +} + +[deps] + +source_file="res://icons/edit.png" +dest_files=[ "res://.import/edit.png-d9148ff44b5b1c8f3b0a53a3c3c7c1c0.s3tc.stex", "res://.import/edit.png-d9148ff44b5b1c8f3b0a53a3c3c7c1c0.etc2.stex", "res://.import/edit.png-d9148ff44b5b1c8f3b0a53a3c3c7c1c0.etc.stex" ] + +[params] + +compress/mode=2 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=true +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=false +process/premult_alpha=true +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/icons/icon.png b/icons/icon.png new file mode 100755 index 0000000..4b35a32 Binary files /dev/null and b/icons/icon.png differ diff --git a/icons/icon.png.import b/icons/icon.png.import new file mode 100644 index 0000000..bc6c6d2 --- /dev/null +++ b/icons/icon.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-66e56a551f174bdc993eb27d415a6710.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/icon.png" +dest_files=[ "res://.import/icon.png-66e56a551f174bdc993eb27d415a6710.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/logic/database.gd b/logic/database.gd new file mode 100644 index 0000000..1abb984 --- /dev/null +++ b/logic/database.gd @@ -0,0 +1,226 @@ +extends ItemList +class_name Database + +const DATABASE_FILE_PATH: String = "user://database.csv" +const DATE_SEPARATOR: String = "-" +const DATE_FORMAT: String = "%04d-%02d-%02d" +const ENTRY_PROTOTYPE: Dictionary = { + "process_id": "", + "surgery_id": "", + "place": "", + "date": "", + "date_year": 0, + "date_month": 0, + "date_day": 0, + "anesthetic": "", + "first_assistant": "", + "type": "", + "sub_type": "", + "sub_sub_type": "", + "pathology": "", + "intervention": "", + "is_urgency": false, + "notes": "", +} + +var db: Array +var staged_idx: int + +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 stage: Stage = get_node("/root/main/stage") + + +func _init(): + staged_idx = -1 + load_database() + store_database() # @DAM Only for testing. + + +# @DAM WIP +#func _notification(what: int): +# if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST: +# clear_selection() + + +func _ready(): + 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.connect("discard", self, "discard") + refresh_list() + + +func get_entry_view(database_entry: Dictionary) -> String: + return "%6s | %6s | %s" % [database_entry.process_id, database_entry.surgery_id, get_entry_date(database_entry)] + + +func refresh_list(): + self.clear() + for it in db: + self.add_item(get_entry_view(it)) + + +func clear_selection(): + unselect_all() + + +func delete_action(): + # @DAM Could do some performance improvements + var selected_entries_idx := self.get_selected_items() + var sorted_idx := Array(selected_entries_idx) + sorted_idx.sort() + sorted_idx.invert() + for idx in sorted_idx: + db.remove(idx) + self.remove_item(idx) + + +func edit_action(): + var selected_entries_idx := self.get_selected_items() + if selected_entries_idx.size() != 1: + return + staged_idx = selected_entries_idx[0] + self.visible = false + stage.visible = true + stage.set_stage(db[staged_idx]) + + +func add_action(): + self.visible = false + stage.visible = true + var staged := instance_entry() + stage.set_stage(staged) + # @DAM This signal is connected but, if we select "DISCARD" it stays connectd. + # Maybe I should always keep the connection and use two different buttons on sage screen. + stage.connect("save", self, "save_new", [], CONNECT_ONESHOT) + + +func save(database_entry: Dictionary): + if staged_idx >= 0: + db[staged_idx] = database_entry + set_item_text(staged_idx, get_entry_view(database_entry)) +# items[staged_idx] = get_entry_view(database_entry) + else: + db.append(database_entry) + add_item(get_entry_view(database_entry)) + + staged_idx = -1 + self.visible = true + + +func discard(): + staged_idx = -1 + self.visible = true + + +func load_database(): + var file := File.new() + file.open(DATABASE_FILE_PATH, File.READ_WRITE) + var headers: PoolStringArray + var is_first_line := true +# while database_file.eof_reached() == false: + 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 = ENTRY_PROTOTYPE.duplicate(true) + 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) + 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(): + var file := File.new() + file.open(DATABASE_FILE_PATH, File.WRITE) + var header := PoolStringArray(ENTRY_PROTOTYPE.keys()) + file.store_csv_line(header) + var entry := PoolStringArray() + for it in db: + file.store_csv_line(it.values()) + file.close() + + +func instance_entry(params: Dictionary = {}) -> Dictionary: + var new_entry := ENTRY_PROTOTYPE.duplicate(true) + new_entry.process_id = params.get("process_id", "") + new_entry.surgery_id = params.get("surgery_id", "") + new_entry.place = params.get("place", "") + + var today = OS.get_date() + set_entry_date(new_entry, params.get("date", DATE_FORMAT % [today.year, today.month, today.day])) + + new_entry.anesthetic = params.get("anesthetic", "") + new_entry.first_assistant = params.get("first_assistant", "") + new_entry.type = params.get("type", "") + new_entry.sub_type = params.get("sub_type", "") + new_entry.sub_sub_type = params.get("sub_sub_type", "") + new_entry.pathology = params.get("pathology", "") + new_entry.intervention = params.get("intervention", "") + new_entry.is_urgency = params.get("is_urgency", false) + new_entry.notes = params.get("notes", "") + + return new_entry + + +func get_entry_date(entry: Dictionary) -> String: + return DATE_FORMAT % [entry.date_year, entry.date_month, entry.date_day] + + +func set_entry_date(entry: Dictionary, date: String): + date = date.strip_edges().replace(" ", DATE_SEPARATOR).replace("/", DATE_SEPARATOR).replace("\\", DATE_SEPARATOR) + var year_month_idx := date.find(DATE_SEPARATOR) + var month_day_idx := date.find(DATE_SEPARATOR, year_month_idx + 1) + entry.date = date + entry.date_year = int(date.substr(0, year_month_idx)) + entry.date_month = int(date.substr(year_month_idx + 1, month_day_idx - year_month_idx - 1)) + entry.date_day = int(date.substr(month_day_idx + 1)) + + diff --git a/logic/stage.gd b/logic/stage.gd new file mode 100644 index 0000000..a9f4fee --- /dev/null +++ b/logic/stage.gd @@ -0,0 +1,88 @@ +extends Control +class_name Stage + +signal save # (database_entry: Dictionary) +signal discard # () + +onready var process_id: LineEdit = get_node("controls/process_id") +onready var surgery_id: LineEdit = get_node("controls/surgery_id") +onready var date: DatePicker = get_node("controls/date_picker") +onready var place: LineEdit = get_node("controls/place") +onready var anesthetic: LineEdit = get_node("controls/anesthetic") +onready var first_assistant: LineEdit = get_node("controls/first_assistant") +onready var type: LineEdit = get_node("controls/type") +onready var sub_type: LineEdit = get_node("controls/sub_type") +onready var sub_sub_type: LineEdit = get_node("controls/sub_sub_type") +onready var pathology: LineEdit = get_node("controls/pathology") +onready var intervention: LineEdit = get_node("controls/intervention") +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") + + +func _ready(): + save_button.connect("pressed", self, "save_action") + discard_button.connect("pressed", self, "discard_action") + + +func save_action(): + self.visible = false + emit_signal("save", get_stage()) + + +func discard_action(): + self.visible = false + emit_signal("discard") + + +func set_stage(entry: Dictionary): + process_id.text = entry.process_id + surgery_id.text = entry.surgery_id + date.set_date(entry.date_year, entry.date_month, entry.date_day) + place.text = entry.place + anesthetic.text = entry.anesthetic + first_assistant.text = entry.first_assistant + type.text = entry.type + sub_type.text = entry.sub_type + sub_sub_type.text = entry.sub_sub_type + pathology.text = entry.pathology + intervention.text = entry.intervention + is_urgency.pressed = entry.is_urgency + notes.text = entry.notes + self.scroll_vertical = 0 # @DAM TODO + + +func get_stage() -> Dictionary: + var entry: Dictionary + entry.process_id = process_id.text + entry.surgery_id = surgery_id.text + entry.date_year = date.get_year() + entry.date_month = date.get_month() + entry.date_day = date.get_day() + entry.place = place.text + entry.anesthetic = anesthetic.text + entry.first_assistant = first_assistant.text + entry.type = type.text + entry.sub_type = sub_type.text + entry.sub_sub_type = sub_sub_type.text + entry.pathology = pathology.text + entry.intervention = intervention.text + entry.is_urgency = is_urgency.pressed + entry.notes = notes.text + return entry + + +func _notification(what: int): + if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST: + discard_action() + + +func _unhandled_input(event): + get_node("/root/main/debug").text += "%s\n" % event.to_string() + + +func _unhandled_key_input(event): + get_node("/root/main/debug").text += "%s\n" % event.to_string() + + diff --git a/main.gd b/main.gd index 803c2d4..d242bda 100644 --- a/main.gd +++ b/main.gd @@ -1,6 +1,7 @@ extends Control var timeout: float + onready var controls_sensible_to_keyboard: Array = [ self, get_node("/root/main/about"), @@ -10,8 +11,13 @@ onready var controls_sensible_to_keyboard: Array = [ ] +func _init(): + Physics2DServer.set_active(false) + PhysicsServer.set_active(false) + + + func _process(delta: float): - var keyboard_height: int = OS.get_virtual_keyboard_height() for it in controls_sensible_to_keyboard: it.margin_bottom = -keyboard_height diff --git a/main.tscn b/main.tscn index 83ee8d3..53f083a 100644 --- a/main.tscn +++ b/main.tscn @@ -1,9 +1,14 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=10 format=2] [ext_resource path="res://main.gd" type="Script" id=1] [ext_resource path="res://date_picker/date_picker.tscn" type="PackedScene" id=2] [ext_resource path="res://theme_white.tres" type="Theme" id=3] [ext_resource path="res://menu/menu.gd" type="Script" id=4] +[ext_resource path="res://logic/database.gd" type="Script" id=5] +[ext_resource path="res://icons/edit.png" type="Texture" id=6] +[ext_resource path="res://icons/add.png" type="Texture" id=7] +[ext_resource path="res://icons/delete.png" type="Texture" id=8] +[ext_resource path="res://logic/stage.gd" type="Script" id=9] [node name="main" type="Control"] anchor_right = 1.0 @@ -14,192 +19,211 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="body" type="TabContainer" parent="."] +[node name="database" type="ItemList" parent="."] anchor_right = 1.0 anchor_bottom = 1.0 -tab_align = 0 +margin_top = 60.0 +script = ExtResource( 5 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="actions" type="VBoxContainer" parent="database"] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -140.0 +margin_top = -410.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="delete" type="Button" parent="database/actions"] +margin_right = 140.0 +margin_bottom = 134.0 +focus_mode = 0 +enabled_focus_mode = 0 +icon = ExtResource( 8 ) +flat = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="edit" type="Button" parent="database/actions"] +margin_top = 138.0 +margin_right = 140.0 +margin_bottom = 272.0 +focus_mode = 0 +enabled_focus_mode = 0 +icon = ExtResource( 6 ) +flat = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="add" type="Button" parent="database/actions"] +margin_top = 276.0 +margin_right = 140.0 +margin_bottom = 410.0 +focus_mode = 0 +enabled_focus_mode = 0 +icon = ExtResource( 7 ) +flat = true __meta__ = { "_edit_use_anchors_": false } -[node name="record" type="ScrollContainer" parent="body"] +[node name="stage" type="ScrollContainer" parent="."] +visible = false anchor_right = 1.0 anchor_bottom = 1.0 -margin_left = 4.0 margin_top = 60.0 -margin_right = -4.0 -margin_bottom = -4.0 scroll_horizontal_enabled = false +script = ExtResource( 9 ) __meta__ = { "_edit_use_anchors_": false } -[node name="VBoxContainer" type="VBoxContainer" parent="body/record"] -margin_right = 1072.0 -margin_bottom = 1856.0 -rect_clip_content = true +[node name="controls" type="VBoxContainer" parent="stage"] +margin_right = 1080.0 +margin_bottom = 1860.0 size_flags_horizontal = 3 size_flags_vertical = 3 +__meta__ = { +"_edit_use_anchors_": false +} -[node name="process_number" type="LineEdit" parent="body/record/VBoxContainer"] -margin_right = 1072.0 +[node name="process_id" type="LineEdit" parent="stage/controls"] +margin_right = 1080.0 margin_bottom = 52.0 placeholder_text = "Nº Processo" caret_blink = true caret_blink_speed = 0.5 -[node name="surgery_number" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="surgery_id" type="LineEdit" parent="stage/controls"] margin_top = 56.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 108.0 placeholder_text = "Nº Cirurgia" caret_blink = true caret_blink_speed = 0.5 -[node name="date_picker" parent="body/record/VBoxContainer" instance=ExtResource( 2 )] +[node name="date_picker" parent="stage/controls" instance=ExtResource( 2 )] anchor_right = 0.0 anchor_bottom = 0.0 margin_top = 112.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 312.0 rect_min_size = Vector2( 400, 200 ) -[node name="place" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="place" type="LineEdit" parent="stage/controls"] margin_top = 316.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 368.0 placeholder_text = "Local" caret_blink = true caret_blink_speed = 0.5 -[node name="anesthesia" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="anesthetic" type="LineEdit" parent="stage/controls"] margin_top = 372.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 424.0 placeholder_text = "Anesthesics" caret_blink = true caret_blink_speed = 0.5 -[node name="first_aider" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="first_assistant" type="LineEdit" parent="stage/controls"] margin_top = 428.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 480.0 placeholder_text = "1º Ajudante" caret_blink = true caret_blink_speed = 0.5 -[node name="type" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="type" type="LineEdit" parent="stage/controls"] margin_top = 484.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 536.0 placeholder_text = "Tipo" caret_blink = true caret_blink_speed = 0.5 -[node name="sub_type" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="sub_type" type="LineEdit" parent="stage/controls"] margin_top = 540.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 592.0 placeholder_text = "Subtipo" caret_blink = true caret_blink_speed = 0.5 -[node name="sub_sub_type" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="sub_sub_type" type="LineEdit" parent="stage/controls"] margin_top = 596.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 648.0 placeholder_text = "Sub-Subtipo" caret_blink = true caret_blink_speed = 0.5 -[node name="pathology" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="pathology" type="LineEdit" parent="stage/controls"] margin_top = 652.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 704.0 placeholder_text = "Patologia" caret_blink = true caret_blink_speed = 0.5 -[node name="intervention" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="intervention" type="LineEdit" parent="stage/controls"] margin_top = 708.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 760.0 placeholder_text = "Intervenção" caret_blink = true caret_blink_speed = 0.5 -[node name="urgency" type="CheckBox" parent="body/record/VBoxContainer"] +[node name="is_urgency" type="CheckBox" parent="stage/controls"] margin_top = 764.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 814.0 text = "Urgência" -[node name="notes" type="LineEdit" parent="body/record/VBoxContainer"] +[node name="notes" type="LineEdit" parent="stage/controls"] margin_top = 818.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 870.0 placeholder_text = "Notas" caret_blink = true caret_blink_speed = 0.5 -[node name="output" type="RichTextLabel" parent="body/record/VBoxContainer"] +[node name="save" type="Button" parent="stage/controls"] margin_top = 874.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 924.0 +grow_horizontal = 2 +grow_vertical = 2 rect_min_size = Vector2( 0, 50 ) +text = "save" __meta__ = { "_edit_use_anchors_": false } -[node name="timer" type="RichTextLabel" parent="body/record/VBoxContainer"] +[node name="discard" type="Button" parent="stage/controls"] margin_top = 928.0 -margin_right = 1072.0 +margin_right = 1080.0 margin_bottom = 978.0 -rect_min_size = Vector2( 0, 50 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="button" type="Button" parent="body/record/VBoxContainer"] -margin_top = 982.0 -margin_right = 1072.0 -margin_bottom = 1032.0 grow_horizontal = 2 grow_vertical = 2 rect_min_size = Vector2( 0, 50 ) -text = "PRESS ME" +text = "discard" __meta__ = { "_edit_use_anchors_": false } -[node name="log" type="RichTextLabel" parent="body/record/VBoxContainer"] -margin_top = 1036.0 -margin_right = 1072.0 -margin_bottom = 1086.0 -rect_min_size = Vector2( 0, 50 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="list" type="ScrollContainer" parent="body"] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = 4.0 -margin_top = 30.0 -margin_right = -4.0 -margin_bottom = -4.0 -scroll_horizontal_enabled = false - -[node name="ItemList" type="ItemList" parent="body/list"] -visible = false - [node name="menu" type="MenuButton" parent="."] -anchor_left = 1.0 anchor_right = 1.0 -margin_left = -32.0 -margin_bottom = 26.0 +margin_bottom = 48.0 text = "≡" +align = 2 script = ExtResource( 4 ) __meta__ = { "_edit_use_anchors_": false @@ -221,7 +245,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 -window_title = "" +window_title = "delete filters" dialog_text = "Are you sure you want to delete all filters?" dialog_autowrap = true __meta__ = { @@ -233,7 +257,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 -window_title = "" +window_title = "Open a File" mode = 0 access = 2 filters = PoolStringArray( "*.csv" ) @@ -249,7 +273,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 -window_title = "" +window_title = "export filters" access = 2 filters = PoolStringArray( "*.csv" ) show_hidden_files = true @@ -260,11 +284,11 @@ __meta__ = { } [node name="debug" type="Label" parent="."] -anchor_left = 1.0 +visible = false anchor_right = 1.0 -margin_left = -571.0 -margin_top = 220.0 -margin_bottom = 829.0 +anchor_bottom = 1.0 +margin_top = 60.0 +valign = 2 __meta__ = { "_edit_use_anchors_": false } diff --git a/project.godot b/project.godot index 6657a50..41969be 100644 --- a/project.godot +++ b/project.godot @@ -9,18 +9,30 @@ config_version=4 _global_script_classes=[ { +"base": "ItemList", +"class": "Database", +"language": "GDScript", +"path": "res://logic/database.gd" +}, { "base": "Control", "class": "DatePicker", "language": "GDScript", "path": "res://date_picker/date_picker.gd" }, { "base": "Control", +"class": "Stage", +"language": "GDScript", +"path": "res://logic/stage.gd" +}, { +"base": "Control", "class": "ValuePicker", "language": "GDScript", "path": "res://date_picker/value_picker.gd" } ] _global_script_class_icons={ +"Database": "", "DatePicker": "", +"Stage": "", "ValuePicker": "" } @@ -28,11 +40,12 @@ _global_script_class_icons={ config/name="Surgery Log" run/main_scene="res://main.tscn" -boot_splash/image="res://icon.png" +boot_splash/image="res://icons/icon.png" boot_splash/fullsize=false boot_splash/use_filter=false boot_splash/bg_color=Color( 0, 0, 0, 1 ) -config/icon="res://icon.png" +config/icon="res://icons/icon.png" +config/quit_on_go_back=false [display] diff --git a/readme.md b/readme.md index 8dae6e0..c2d73b0 100644 --- a/readme.md +++ b/readme.md @@ -1,9 +1,16 @@ Surgery Log =========== +# Notes +- Location of `user://`: `~/.local/share/godot/app_userdata/`. + # ToDo - [x] Datepicker: On click (without drag) reset velocity to 0; show and focus 'input' to allow introducing value. -- [ ] 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 copy 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] Selecting last (year?)/month/day and de-selecting it, moves value to next; +- [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. - [ ] 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 @@ -15,5 +22,3 @@ Surgery Log - [ ] Create two themes: - [ ] theme_light - [ ] theme_dark - - -- cgit v1.2.3 From 06b2f34ba64726028f494060376044531d763668 Mon Sep 17 00:00:00 2001 From: dam Date: Tue, 4 Jan 2022 22:34:48 +0000 Subject: Implement clear and export data actions. Merge and reuse modal dialogs. --- logic/database.gd | 19 +++++--- logic/stage.gd | 6 +-- main.gd | 6 +-- main.tscn | 44 +++---------------- menu/menu.gd | 126 ++++++++++++++++++++++++++---------------------------- readme.md | 17 +++++++- 6 files changed, 102 insertions(+), 116 deletions(-) (limited to 'main.gd') diff --git a/logic/database.gd b/logic/database.gd index 438a2dd..2285470 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -12,7 +12,7 @@ const ENTRY_PROTOTYPE: Dictionary = { "date_year": 0, "date_month": 0, "date_day": 0, - "anesthetic": "", + "anesthesia": "", "first_assistant": "", "type": "", "sub_type": "", @@ -195,6 +195,14 @@ func store_database(file_path: String = DATABASE_FILE_PATH): file.close() +func clear_database(save_changes: bool = false): + clear_selection() + self.clear() + db.resize(0) + if save_changes: + store_database() + + static func instance_entry(params: Dictionary = {}) -> Dictionary: var new_entry := ENTRY_PROTOTYPE.duplicate(true) new_entry.process_id = params.get("process_id", "") @@ -207,7 +215,7 @@ static func instance_entry(params: Dictionary = {}) -> Dictionary: new_entry.date_day = params.get("date_day", today.day) new_entry.date = params.get("date", get_entry_date(new_entry)) # @DAM We should store only one version of the date. - new_entry.anesthetic = params.get("anesthetic", "") + new_entry.anesthesia = params.get("anesthesia", "") new_entry.first_assistant = params.get("first_assistant", "") new_entry.type = params.get("type", "") new_entry.sub_type = params.get("sub_type", "") @@ -234,9 +242,8 @@ static func set_entry_date(entry: Dictionary, date: String): entry.date_day = int(date.substr(month_day_idx + 1)) -func fake_database(): - self.clear() - db.resize(0) +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 @@ -248,6 +255,8 @@ func fake_database(): }) db.append(fake_entry) self.add_item(get_entry_view(fake_entry)) + if save_changes: + store_database() func pointer_input_handler(pointer: PointerInputSensor.PointerInputData): diff --git a/logic/stage.gd b/logic/stage.gd index 27962c6..73d905e 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -13,7 +13,7 @@ onready var process_id: LineEdit = get_node("controls/process_id") onready var surgery_id: LineEdit = get_node("controls/surgery_id") onready var date: DatePicker = get_node("controls/date_picker") onready var place: LineEdit = get_node("controls/place") -onready var anesthetic: LineEdit = get_node("controls/anesthetic") +onready var anesthesia: LineEdit = get_node("controls/anesthesia") onready var first_assistant: LineEdit = get_node("controls/first_assistant") onready var type: LineEdit = get_node("controls/type") onready var sub_type: LineEdit = get_node("controls/sub_type") @@ -81,7 +81,7 @@ func set_stage(entry: Dictionary): surgery_id.text = entry.surgery_id date.set_date(entry.date_year, entry.date_month, entry.date_day) place.text = entry.place - anesthetic.text = entry.anesthetic + anesthesia.text = entry.anesthesia first_assistant.text = entry.first_assistant type.text = entry.type sub_type.text = entry.sub_type @@ -102,7 +102,7 @@ func get_stage() -> Dictionary: "date_month": date.get_month(), "date_day": date.get_day(), "place": place.text, - "anesthetic": anesthetic.text, + "anesthesia": anesthesia.text, "first_assistant": first_assistant.text, "type": type.text, "sub_type": sub_type.text, diff --git a/main.gd b/main.gd index d242bda..c5f7d0d 100644 --- a/main.gd +++ b/main.gd @@ -4,10 +4,8 @@ var timeout: float onready var controls_sensible_to_keyboard: Array = [ self, - get_node("/root/main/about"), - get_node("/root/main/delete_filters"), - get_node("/root/main/import_filters"), - get_node("/root/main/export_filters"), + get_node("/root/main/file_picker"), + get_node("/root/main/confirm_action"), ] diff --git a/main.tscn b/main.tscn index b91cdc0..d4ff096 100644 --- a/main.tscn +++ b/main.tscn @@ -133,7 +133,7 @@ placeholder_text = "Local" caret_blink = true caret_blink_speed = 0.5 -[node name="anesthetic" type="LineEdit" parent="stage/controls"] +[node name="anesthesia" type="LineEdit" parent="stage/controls"] margin_top = 363.0 margin_right = 1080.0 margin_bottom = 412.0 @@ -237,36 +237,12 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="about" type="AcceptDialog" parent="."] +[node name="file_picker" type="FileDialog" parent="."] anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 -window_title = "" -dialog_autowrap = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="delete_filters" type="ConfirmationDialog" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -size_flags_horizontal = 3 -size_flags_vertical = 3 -window_title = "delete filters" -dialog_text = "Are you sure you want to delete all filters?" -dialog_autowrap = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="import_filters" type="FileDialog" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -size_flags_horizontal = 3 -size_flags_vertical = 3 -window_title = "Open a File" -mode = 0 +window_title = "PICK FILE" access = 2 filters = PoolStringArray( "*.csv" ) show_hidden_files = true @@ -276,20 +252,14 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="export_filters" type="FileDialog" parent="."] +[node name="confirm_action" type="ConfirmationDialog" parent="."] anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 -window_title = "export filters" -access = 2 -filters = PoolStringArray( "*.csv" ) -show_hidden_files = true -current_dir = "" -current_path = "" -__meta__ = { -"_edit_use_anchors_": false -} +window_title = "CONFIRM ACTION" +dialog_text = "Are you sure you want to delete all filters?" +dialog_autowrap = true [node name="debug" type="Label" parent="."] visible = false diff --git a/menu/menu.gd b/menu/menu.gd index 36beee8..3384436 100644 --- a/menu/menu.gd +++ b/menu/menu.gd @@ -4,100 +4,94 @@ const menu_items: Array = [ { label = "IMPORT FILTERS", action = "_menu_import_filters_action" }, { label = "EXPORT FILTERS", action = "_menu_export_filters_action" }, { label = "CLEAR FILTERS", action = "_menu_clear_filters_action" }, + { label = "EXPORT DATA", action = "_menu_export_data_action" }, + { label = "CLEAR DATA", action = "_menu_clear_data_action" }, { label = "ABOUT", action = "_menu_about_action" }, { label = "FAKE_DB", action = "_menu_fake_db_action" }, ] const license_font_b612: String = "res://licenses/font_b612.txt" const license_godot: String = "res://licenses/godot.txt" -onready var popup: PopupMenu = get_popup() -onready var debug: Label = get_node("/root/main/debug") as Label +onready var popup := get_popup() as PopupMenu +onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog +onready var file_picker := get_node("/root/main/file_picker") as FileDialog +onready var database := get_node("/root/main/database") as Database + func _ready(): - for idx in range(menu_items.size()): popup.add_item(menu_items[idx].label, idx) popup.connect("id_pressed", self, "id_pressed") - -# load_file(file) - + file_picker.get_cancel().connect("pressed", self, "dialog_cancelled", ["file_selected"]) + confirm_action.get_cancel().connect("pressed", self, "dialog_cancelled", ["confirmed"]) func id_pressed(id: int): - debug.text += "'%d':'%s'" % [id, menu_items[id].action] self.call_deferred(menu_items[id].action) - debug.text += "!\n" -var csv_file: Array -var has_permissions := false +func dialog_cancelled(confirmation_signal_name: String): + var confirmation_handlers = confirm_action.get_signal_connection_list(confirmation_signal_name) + for it in confirmation_handlers: + confirm_action.disconnect(it.signal, it.target, it.method) + + func _menu_import_filters_action(): - -# printerr("pressed: %s" % get_stack()[0]); - debug.text += "> import: " - - if OS.get_name() == "Android": - while not has_permissions: - var permissions := Array(OS.get_granted_permissions()) - if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \ - or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"): - OS.request_permissions() - # await get_tree().create_timer(1).timeout - yield(get_tree().create_timer(1), "timeout") # - for Godot 3 branch - else: - has_permissions = true - - - - var file_dialog := get_node("/root/main/import_filters") as FileDialog - file_dialog.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) - file_dialog.connect("file_selected", self, "file_selected", [], CONNECT_ONESHOT) - file_dialog.show_modal(true) - file_dialog.invalidate() -# printerr("download: '%s'" % OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)) - -func file_selected(path: String): - debug.text += "'%s'\n" % path - - var file = File.new() - file.open(path, File.READ) - while file.eof_reached() == false: - var line = file.get_line() - csv_file.append(line) - printerr("%s" % line) - file.close() + return + file_picker.window_title = "IMPORT FILTERS" + file_picker.mode = FileDialog.MODE_OPEN_FILE + file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) +# file_picker.connect("file_selected", filters, "TODO", [], CONNECT_ONESHOT) +# file_picker.show_modal(true) +# file_picker.invalidate() + func _menu_export_filters_action(): - debug.text += "> export: \n" -# printerr("pressed: %s" % get_stack()[0]); - var file_dialog := get_node("/root/main/export_filters") as FileDialog - file_dialog.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) - file_dialog.connect("file_selected", self, "file_selected_export", [], CONNECT_ONESHOT) - file_dialog.show_modal(true) - file_dialog.invalidate() - -func file_selected_export(path: String): - debug.text += "'%s'\n" % path - var file = File.new() - file.open(path, File.WRITE) - for ln in csv_file: - file.store_line(ln) - file.close() + return + file_picker.window_title = "EXPORT FILTERS" + file_picker.mode = FileDialog.MODE_SAVE_FILE + file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) +# file_picker.connect("file_selected", filters, "TODO", [], CONNECT_ONESHOT) +# file_picker.show_modal(true) +# file_picker.invalidate() func _menu_clear_filters_action(): - debug.text += "> clear\n" -# printerr("pressed: %s" % get_stack()[0]); - (get_node("/root/main/delete_filters") as ConfirmationDialog).show_modal(true) + return + confirm_action.window_title = "CLEAR FILTERS" + confirm_action.dialog_text = "Do you want to delete all filters?" +# confirm_action.connect("confirmed", filters, "TDO", [true], CONNECT_ONESHOT) +# confirm_action.show_modal(true) + + +func _menu_export_data_action(): + file_picker.window_title = "EXPORT DATA" + file_picker.mode = FileDialog.MODE_SAVE_FILE + file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + file_picker.connect("file_selected", database, "store_database", [], CONNECT_ONESHOT) + file_picker.show_modal(true) + file_picker.invalidate() + + +func _menu_clear_data_action(): + confirm_action.window_title = "CLEAR DATA" + confirm_action.dialog_text = "Do you want to delete all entries from the database?" + confirm_action.connect("confirmed", database, "clear_database", [true], CONNECT_ONESHOT) + confirm_action.show_modal(true) func _menu_about_action(): -# printerr("pressed: %s" % get_stack()[0]); - debug.text += "> about\n" - (get_node("/root/main/about") as AcceptDialog).show_modal() + confirm_action.window_title = "FAKE DB" + confirm_action.dialog_text = "About text here!" +# confirm_action.connect("confirmed", database, "fake_database", [true], CONNECT_ONESHOT) + confirm_action.show_modal(true) + func _menu_fake_db_action(): - get_node("/root/main/database").fake_database() + confirm_action.window_title = "FAKE DB" + confirm_action.dialog_text = "Do you want to delete all entries from the database and replace by fake entries?" + confirm_action.connect("confirmed", database, "fake_database", [true], CONNECT_ONESHOT) + confirm_action.show_modal(true) diff --git a/readme.md b/readme.md index 026c5eb..fc33cf1 100644 --- a/readme.md +++ b/readme.md @@ -14,7 +14,22 @@ Surgery Log - [x] edited entry does not show updated once saved; - [x] edited entry shows updated when selected then press back button; - [x] load/store database CSV file; -- [ ] export database to CSV file; +- [x] export database to CSV file; +- [x] allow to reset database; +- [ ] Implement file access permission check on Android: + ```py + if OS.get_name() == "Android": + var has_permissions := false + while not has_permissions: + var permissions := Array(OS.get_granted_permissions()) + if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \ + or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"): + OS.request_permissions() + # await get_tree().create_timer(1).timeout + yield(get_tree().create_timer(1), "timeout") # - for Godot 3 branch + else: + has_permissions = true + ``` - [ ] load/store filters CSV file; - [ ] import/export filters to CSV file; - [ ] add auto-fill buttons on stage screen: -- cgit v1.2.3 From 4d8709e47afc2eb8b4e46ced2747b662a849da29 Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 14 Jan 2022 01:11:38 +0000 Subject: Fix PointerInputSensor. Simplify TouchItemList and TouchVerticalContainer. --- logic/database.gd | 1 - logic/stage.gd | 25 +++++++++++++++++ main.gd | 6 ++--- pointer_input_sensor.gd | 2 +- touch_item_list/touch_item_list.gd | 31 ++++++---------------- .../touch_vertical_container.gd | 27 +++---------------- 6 files changed, 41 insertions(+), 51 deletions(-) (limited to 'main.gd') diff --git a/logic/database.gd b/logic/database.gd index de978b7..3d74da3 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -151,7 +151,6 @@ func store_database(file_path: String = DATABASE_FILE_PATH): 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: # @DAM This approach depends on the order the dictionary fields are created. file.store_csv_line(it.values()) diff --git a/logic/stage.gd b/logic/stage.gd index 12457c9..e4627c2 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -69,6 +69,13 @@ func _ready(): save_button.connect("pressed", self, "save_action") discard_button.connect("pressed", self, "discard_action") + for it in get_node("controls").get_children(): + it = it as Control + if it is LineEdit: + it.connect("focus_entered", it, "set_cursor_position", [99999999]) # @DAM Use MAX_INT + it.connect("focus_exited", it, "deselect") + + var auto_place := place.get_node("auto") as Button auto_place.connect("pressed", self, "auto_populate", ["place"]) @@ -168,3 +175,21 @@ func clear_filters(save_changes: bool = false): store_filters() +func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData): + .pointer_input_on_click_handler(pointer) + + var target: Control = pointer.target.get_parent() + var button: Button + if target is Button: + button = target + elif target.get_node("auto") is Button: + button = target.get_node("auto") + + if button != null && button.get_global_rect().has_point(pointer.current_position): + if button is CheckBox || button is CheckButton: + button.pressed = !button.pressed + button.emit_signal("button_down") + button.emit_signal("pressed") + button.emit_signal("button_up") + + diff --git a/main.gd b/main.gd index c5f7d0d..768366a 100644 --- a/main.gd +++ b/main.gd @@ -26,15 +26,15 @@ func _process(delta: float): if timeout > 0.0: timeout -= delta else: - Engine.target_fps = 10.0 + Engine.target_fps = 10 -func _input(event): +func _input(event: InputEvent): Engine.target_fps = 0 timeout = 3.5 -func _unhandled_input(event): +func _unhandled_input(event: InputEvent): Engine.target_fps = 0 timeout = 3.5 diff --git a/pointer_input_sensor.gd b/pointer_input_sensor.gd index ee5e390..be4d158 100644 --- a/pointer_input_sensor.gd +++ b/pointer_input_sensor.gd @@ -54,7 +54,7 @@ func _ready(): pointer = PointerInputData.new() pointer.target = self connect("mouse_entered", self, "_on_enter_exit", [true]) - connect("mouse_entered", self, "_on_enter_exit", [false]) + connect("mouse_exited", self, "_on_enter_exit", [false]) func _on_enter_exit(is_inside: bool): diff --git a/touch_item_list/touch_item_list.gd b/touch_item_list/touch_item_list.gd index 0534bb2..07d8f30 100644 --- a/touch_item_list/touch_item_list.gd +++ b/touch_item_list/touch_item_list.gd @@ -27,6 +27,7 @@ func _process(delta: float): func pointer_input_on_press_handler(pointer: PointerInputSensor.PointerInputData): is_pointer_dragging = true + grab_focus() func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData): @@ -40,29 +41,13 @@ func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputD func pointer_input_on_click_handler(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) + var selected_idx := get_item_at_position(pointer.current_position - rect_global_position, true) + if selected_idx >= 0: + select(selected_idx) + emit_signal("item_selected", selected_idx) + else: + unselect_all() + emit_signal("nothing_selected") func pointer_input_on_scroll_handler(pointer: PointerInputSensor.PointerInputData): diff --git a/touch_vertical_container/touch_vertical_container.gd b/touch_vertical_container/touch_vertical_container.gd index 10462fd..f9c43e9 100644 --- a/touch_vertical_container/touch_vertical_container.gd +++ b/touch_vertical_container/touch_vertical_container.gd @@ -27,13 +27,8 @@ func _ready(): sensor.connect("on_end_drag", self, "pointer_input_on_end_drag_handler") sensor.connect("on_click", self, "pointer_input_on_click_handler") - it.connect("focus_entered", sensor, "set_mouse_filter", [Control.MOUSE_FILTER_IGNORE]) - it.connect("focus_entered", sensor, "mouse_default_cursor_shape", [Control.CURSOR_IBEAM]) - it.connect("focus_exited", sensor, "set_mouse_filter", [Control.MOUSE_FILTER_STOP]) - it.connect("focus_exited", sensor, "mouse_default_cursor_shape", [Control.CURSOR_ARROW]) - - if it is LineEdit: - it.connect("focus_exited", it, "deselect") + it.connect("focus_entered", sensor, "set_visible", [false]) + it.connect("focus_exited", sensor, "set_visible", [true]) func _process(delta: float): @@ -45,6 +40,7 @@ func _process(delta: float): func pointer_input_on_press_handler(pointer: PointerInputSensor.PointerInputData): is_pointer_dragging = true + grab_focus() func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData): @@ -58,21 +54,6 @@ func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputD func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData): - var target: Control = pointer.target.get_parent() - var position := target.get_global_mouse_position() - target.grab_focus() - - var button: Button - if target is Button: - button = target - elif target.get_node("auto") is Button: - button = target.get_node("auto") - - if button != null && button.get_global_rect().has_point(position): - if button is CheckBox || button is CheckButton: - button.pressed = !button.pressed - button.emit_signal("button_down") - button.emit_signal("pressed") - button.emit_signal("button_up") + pointer.target.get_parent().grab_focus() -- cgit v1.2.3 From b35d5de65158ee015a8332f8fd614e2ee5cacf6e Mon Sep 17 00:00:00 2001 From: dam Date: Tue, 25 Jan 2022 23:56:04 +0000 Subject: Draft prototype of option sets structure. --- logic/stage.gd | 40 +++++++++++++++++++++++++++++++++++----- main.gd | 1 - 2 files changed, 35 insertions(+), 6 deletions(-) (limited to 'main.gd') diff --git a/logic/stage.gd b/logic/stage.gd index 1601522..3c3d59c 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -9,9 +9,9 @@ const OPTION_SETS_FILE_PATH: String = "user://option_sets.csv" var staged_entry := {} var option_sets := { "place": { - "bloco central": null, - "tondela": null, - "xpto_00": null, + "P0": null, + "P1": null, + "P2": null, "xpto_01": null, "xpto_02": null, "xpto_03": null, @@ -42,6 +42,16 @@ var option_sets := { "xpto_28": null, "xpto_29": null, }, + "first_assistant": { + "FA0": null, + "FA1": null, + "FA2": null, + }, + "anesthesia": { + "AN0": null, + "AN1": null, + "AN2": null, + }, "type": { "A": { "sub_type": { @@ -50,14 +60,34 @@ var option_sets := { "aaA": null, "aaB": null, "aaC": null, - } + }, + "pathology": { + "aaP0": null, + "aaP1": null, + "aaP2": null, + }, + "intervention": { + "aaI0": null, + "aaI1": null, + "aaI2": null, + }, }, "aB": { "sub_sub_type": { "abA": null, "abB": null, "abC": null, - } + }, + "pathology": { + "abP0": null, + "abP1": null, + "abP2": null, + }, + "intervention": { + "abI0": null, + "abI1": null, + "abI2": null, + }, }, }, }, diff --git a/main.gd b/main.gd index 768366a..a1b9aca 100644 --- a/main.gd +++ b/main.gd @@ -14,7 +14,6 @@ func _init(): PhysicsServer.set_active(false) - func _process(delta: float): var keyboard_height: int = OS.get_virtual_keyboard_height() for it in controls_sensible_to_keyboard: -- cgit v1.2.3 From cc3f6e5ea29bfe006576a35b5fa24d029a07cf7c Mon Sep 17 00:00:00 2001 From: dam Date: Wed, 9 Feb 2022 00:21:59 +0000 Subject: Add confirmation dialog to entry destructive actions. --- logic/database.gd | 8 ++++++++ logic/stage.gd | 27 ++++++++++++++++++++------- main.gd | 32 +++++++++++++++++++++----------- main.tscn | 18 +++++++++--------- menu/menu.gd | 10 +--------- readme.md | 4 ++-- 6 files changed, 61 insertions(+), 38 deletions(-) (limited to 'main.gd') diff --git a/logic/database.gd b/logic/database.gd index 3c2d6b7..1518490 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -7,6 +7,7 @@ var db: Array var selected_idx: int var staged_idx: int +onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog 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 @@ -65,6 +66,13 @@ func delete_action(): if selected_idx < 0: return + confirm_action.window_title = "DELETE ENTRY" + confirm_action.dialog_text = "Do you want to delete entry with process ID '%s' from the database?" % db[selected_idx].process_id + confirm_action.connect("confirmed", self, "delete_action_confirmed", [], CONNECT_ONESHOT) + confirm_action.show_modal(true) + + +func delete_action_confirmed(): db.remove(selected_idx) self.remove_item(selected_idx) selected_idx = -1 diff --git a/logic/stage.gd b/logic/stage.gd index 1352816..6f46489 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -19,8 +19,10 @@ const OPTION_SETS_TREE_STRUCTURE := { } } +var staged_entry_hash: int var option_sets: Dictionary +onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog onready var process_id := get_node("controls/process_id") as LineEdit onready var surgery_id := get_node("controls/surgery_id") as LineEdit onready var date := get_node("controls/date_picker") as DatePicker @@ -65,8 +67,8 @@ func _ready(): "intervention": intervention } for key in option_sets_map: - var button := option_sets_map[key].get_node("auto") as Button - button.connect("pressed", self, "auto_populate", [key]) + var button := option_sets_map[key].get_node("option_set") as Button + button.connect("pressed", self, "show_option_sets", [key]) func get_option_sets(field: String): @@ -86,13 +88,13 @@ func get_option_sets(field: String): return options -func auto_populate(field: String): +func show_option_sets(field: String): var stage_options = get_node("/root/main/popup_list") as Popup - stage_options.connect("item_selected", self, "auto_selected", [field], CONNECT_ONESHOT) + stage_options.connect("item_selected", self, "option_set_selected", [field], CONNECT_ONESHOT) stage_options.popup_options(get_option_sets(field)) -func auto_selected(index: int, field: String): +func option_set_selected(index: int, field: String): var field_input: LineEdit = self[field] field_input.text = get_option_sets(field)[index] field_input.caret_position = field_input.text.length() @@ -107,11 +109,22 @@ func save_action(): func discard_action(): + if get_stage().hash() != staged_entry_hash: + confirm_action.window_title = "DISCARD ENTRY" + confirm_action.dialog_text = "Do you want to discard the changes made?" + confirm_action.connect("confirmed", self, "discard_action_confirmed", [], CONNECT_ONESHOT) + confirm_action.show_modal(true) + else: + discard_action_confirmed() + + +func discard_action_confirmed(): self.visible = false emit_signal("discard") func set_stage(entry: Dictionary): + staged_entry_hash = entry.hash() process_id.text = entry.process_id surgery_id.text = entry.surgery_id date.set_date(entry.date_year, entry.date_month, entry.date_day) @@ -225,8 +238,8 @@ func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData var button: Button if target is Button: button = target - elif target.get_node("auto") is Button: - button = target.get_node("auto") + elif target.get_node("option_set") is Button: + button = target.get_node("option_set") if button != null && button.get_global_rect().has_point(pointer.current_position): if button is CheckBox || button is CheckButton: diff --git a/main.gd b/main.gd index a1b9aca..bf31aa0 100644 --- a/main.gd +++ b/main.gd @@ -1,11 +1,13 @@ extends Control -var timeout: float +var power_throttle_timeout: float -onready var controls_sensible_to_keyboard: Array = [ +onready var file_picker := get_node("/root/main/file_picker") as FileDialog +onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog +onready var controls_sensible_to_keyboard := [ self, - get_node("/root/main/file_picker"), - get_node("/root/main/confirm_action"), + file_picker, + confirm_action, ] @@ -14,27 +16,35 @@ func _init(): PhysicsServer.set_active(false) +func _ready(): + confirm_action.get_cancel().connect("pressed", self, "dialog_cancelled", ["confirmed"]) + file_picker.get_cancel().connect("pressed", self, "dialog_cancelled", ["file_selected"]) + + func _process(delta: float): var keyboard_height: int = OS.get_virtual_keyboard_height() for it in controls_sensible_to_keyboard: it.margin_bottom = -keyboard_height - # @DAM Debug information. -# $debug.text = "%s" % Engine.get_frames_per_second() -# $debug.text = "%s" % timeout - if timeout > 0.0: - timeout -= delta + if power_throttle_timeout > 0.0: + power_throttle_timeout -= delta else: Engine.target_fps = 10 func _input(event: InputEvent): Engine.target_fps = 0 - timeout = 3.5 + power_throttle_timeout = 3.5 func _unhandled_input(event: InputEvent): Engine.target_fps = 0 - timeout = 3.5 + power_throttle_timeout = 3.5 + + +func dialog_cancelled(confirmation_signal_name: String): + var confirmation_handlers = confirm_action.get_signal_connection_list(confirmation_signal_name) + for it in confirmation_handlers: + confirm_action.disconnect(it.signal, it.target, it.method) diff --git a/main.tscn b/main.tscn index a6abf1b..ead511e 100644 --- a/main.tscn +++ b/main.tscn @@ -110,7 +110,7 @@ placeholder_text = "Local" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/place"] +[node name="option_set" type="Button" parent="stage/controls/place"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -127,7 +127,7 @@ placeholder_text = "Anestesia" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/anesthesia"] +[node name="option_set" type="Button" parent="stage/controls/anesthesia"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -144,7 +144,7 @@ placeholder_text = "1º Ajudante" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/first_assistant"] +[node name="option_set" type="Button" parent="stage/controls/first_assistant"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -161,7 +161,7 @@ placeholder_text = "Tipo" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/type"] +[node name="option_set" type="Button" parent="stage/controls/type"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -178,7 +178,7 @@ placeholder_text = "Subtipo" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/sub_type"] +[node name="option_set" type="Button" parent="stage/controls/sub_type"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -195,7 +195,7 @@ placeholder_text = "Sub-Subtipo" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/sub_sub_type"] +[node name="option_set" type="Button" parent="stage/controls/sub_sub_type"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -212,7 +212,7 @@ placeholder_text = "Patologia" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/pathology"] +[node name="option_set" type="Button" parent="stage/controls/pathology"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -229,7 +229,7 @@ placeholder_text = "Intervenção" caret_blink = true caret_blink_speed = 0.5 -[node name="auto" type="Button" parent="stage/controls/intervention"] +[node name="option_set" type="Button" parent="stage/controls/intervention"] anchor_left = 0.85 anchor_right = 1.0 anchor_bottom = 1.0 @@ -308,7 +308,7 @@ anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 window_title = "CONFIRM ACTION" -dialog_text = "Are you sure you want to delete all filters?" +dialog_text = "Do you confirm this action?" dialog_autowrap = true [node name="popup_list" type="Popup" parent="."] diff --git a/menu/menu.gd b/menu/menu.gd index 1df6f28..5847d5b 100644 --- a/menu/menu.gd +++ b/menu/menu.gd @@ -23,20 +23,12 @@ func _ready(): for idx in range(menu_items.size()): popup.add_item(menu_items[idx].label, idx) popup.connect("id_pressed", self, "id_pressed") - file_picker.get_cancel().connect("pressed", self, "dialog_cancelled", ["file_selected"]) - confirm_action.get_cancel().connect("pressed", self, "dialog_cancelled", ["confirmed"]) func id_pressed(id: int): self.call_deferred(menu_items[id].action) -func dialog_cancelled(confirmation_signal_name: String): - var confirmation_handlers = confirm_action.get_signal_connection_list(confirmation_signal_name) - for it in confirmation_handlers: - confirm_action.disconnect(it.signal, it.target, it.method) - - func _menu_import_option_sets_action(): file_picker.window_title = "IMPORT OPTION SETS" file_picker.mode = FileDialog.MODE_OPEN_FILE @@ -85,7 +77,7 @@ func _menu_clear_data_action(): func _menu_about_action(): - confirm_action.window_title = "FAKE DB" + confirm_action.window_title = "ABOUT" confirm_action.dialog_text = "About text here!" confirm_action.show_modal(true) diff --git a/readme.md b/readme.md index ad0ec8f..7aed207 100644 --- a/readme.md +++ b/readme.md @@ -24,8 +24,8 @@ Surgery Log - 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; -- [ ] add pop-up asking if changes are to be discarded once the stage screen's discard button is pressed; -- [ ] add pop-up confirming delete-entry action; (pressing back should cancel the action); +- [x] add pop-up asking if changes are to be discarded once the stage screen's discard button is pressed; +- [x] add pop-up confirming delete-entry action; - [ ] edit and delete action buttons should be faded-out when no entry is selected; - [ ] Implement file access permission check on Android: ```py -- cgit v1.2.3 From 00fe1da84cdd89e2f168c1be12217dd7047b70ba Mon Sep 17 00:00:00 2001 From: dam Date: Sun, 13 Feb 2022 00:50:08 +0000 Subject: Check and request file access permissions on Android. --- main.gd | 7 +++++++ readme.md | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'main.gd') diff --git a/main.gd b/main.gd index bf31aa0..614c9b1 100644 --- a/main.gd +++ b/main.gd @@ -14,6 +14,13 @@ onready var controls_sensible_to_keyboard := [ func _init(): Physics2DServer.set_active(false) PhysicsServer.set_active(false) + + if OS.get_name() == "Android": + var permissions := Array(OS.get_granted_permissions()) + if permissions.has("android.permission.READ_EXTERNAL_STORAGE") == false \ + or permissions.has("android.permission.WRITE_EXTERNAL_STORAGE") == false: + OS.request_permissions() + func _ready(): diff --git a/readme.md b/readme.md index c9117e9..46a143b 100644 --- a/readme.md +++ b/readme.md @@ -28,9 +28,9 @@ Surgery Log - [x] add pop-up confirming delete-entry action; - [x] edit and delete action buttons should be faded-out when no entry is selected; - [x] Sort option sets alphabetically; -- [ ] Implement file access permission check on Android: +- [x] Check and request file access permissions on Android: ```py - if OS.get_name() == "Android": + if OS.get_name() == "Android": var has_permissions := false while not has_permissions: var permissions := Array(OS.get_granted_permissions()) -- cgit v1.2.3 From 79cac2d7115e31987ff205b7b726b353014dd62b Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 4 Mar 2022 00:07:40 +0000 Subject: Improved touch scroll. --- main.gd | 1 + pointer_input_sensor.gd | 19 +++++++------------ touch_item_list/touch_item_list.gd | 13 +++++++++---- touch_vertical_container/touch_vertical_container.gd | 19 +++++++++++++++---- 4 files changed, 32 insertions(+), 20 deletions(-) (limited to 'main.gd') diff --git a/main.gd b/main.gd index 614c9b1..5a78ede 100644 --- a/main.gd +++ b/main.gd @@ -24,6 +24,7 @@ func _init(): func _ready(): + Input.set_use_accumulated_input(false) confirm_action.get_cancel().connect("pressed", self, "dialog_cancelled", ["confirmed"]) file_picker.get_cancel().connect("pressed", self, "dialog_cancelled", ["file_selected"]) diff --git a/pointer_input_sensor.gd b/pointer_input_sensor.gd index be4d158..588bb81 100644 --- a/pointer_input_sensor.gd +++ b/pointer_input_sensor.gd @@ -70,18 +70,19 @@ func _gui_input(event: InputEvent): return pointer.event = event -# pointer.index = -1 -# pointer.scroll = 0.0 -# pointer.action = PointerInputAction.UNDEFINED -# pointer.velocity = Vector2.ZERO + pointer.action = PointerInputAction.UNDEFINED + pointer.velocity = Vector2.ZERO + pointer.relative_position = Vector2.ZERO + pointer.scroll = 0.0 + pointer.current_position = get_global_mouse_position() if event is InputEventScreenTouch && (pointer.is_pressed == false || pointer.index == event.index): var touch := event as InputEventScreenTouch pointer.is_pressed = event.pressed - pointer.current_position = get_global_mouse_position() + if pointer.is_pressed: pointer.index = touch.index - pointer.initial_position = get_global_mouse_position() + pointer.initial_position = pointer.current_position pointer.action = PointerInputAction.ON_PRESS emit_signal("on_press", pointer) else: @@ -95,14 +96,9 @@ func _gui_input(event: InputEvent): emit_signal("on_end_drag", pointer) pointer.index = -1 pointer.was_dragged = false - # @DAM Maybe add this. -# pointer.initial_position = Vector2.ZERO -# pointer.current_position = Vector2.ZERO -# pointer.relative_position = Vector2.ZERO if event is InputEventScreenDrag && event.index == pointer.index: var drag := event as InputEventScreenDrag - pointer.current_position = get_global_mouse_position() pointer.velocity = drag.speed pointer.relative_position = drag.relative if pointer.was_dragged == false && pointer.current_position.distance_to(pointer.initial_position) > drag_threshold_cm * screen_dpcm: @@ -112,7 +108,6 @@ func _gui_input(event: InputEvent): pointer.action = PointerInputAction.ON_DRAG emit_signal("on_drag", pointer) - # @DAM Clean this code please. if event is InputEventMouseButton && event.is_pressed(): if event.button_index == BUTTON_WHEEL_UP || event.button_index == BUTTON_WHEEL_DOWN: pointer.scroll = -1.0 if event.button_index == BUTTON_WHEEL_UP else 1.0 diff --git a/touch_item_list/touch_item_list.gd b/touch_item_list/touch_item_list.gd index 8b79431..67f88d7 100644 --- a/touch_item_list/touch_item_list.gd +++ b/touch_item_list/touch_item_list.gd @@ -1,12 +1,13 @@ extends ItemList class_name TouchItemList -const POINTER_VELOCITY_DECAYING_FACTOR: float = 2.5 -const POINTER_VELOCITY_BOOST_FACTOR: float = 1.5 +const POINTER_VELOCITY_DECAYING_FACTOR: float = PI +const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25 const EXACT_SELECTION: bool = false var is_pointer_dragging := false var pointer_drag_velocity := 0.0 +var when_last_dragged := 0 onready var sensor := get_node("sensor") as PointerInputSensor onready var v_scroll_bar := get_v_scroll() as ScrollBar @@ -37,13 +38,17 @@ func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData) var reported_velocity_abs := abs(pointer.velocity.y) var relative_velocity := pointer.relative_position.y * Engine.get_frames_per_second() var relative_velocity_abs := abs(relative_velocity) - var max_velocity := pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity - pointer_drag_velocity = max_velocity * POINTER_VELOCITY_BOOST_FACTOR + pointer_drag_velocity = pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity v_scroll_bar.value -= pointer.relative_position.y + when_last_dragged = OS.get_ticks_msec() func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputData): is_pointer_dragging = false + if OS.get_ticks_msec() - when_last_dragged > 20: + pointer_drag_velocity = 0.0 + else: + pointer_drag_velocity *= POINTER_VELOCITY_BOOST_FACTOR func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData): diff --git a/touch_vertical_container/touch_vertical_container.gd b/touch_vertical_container/touch_vertical_container.gd index 07b8714..4633c64 100644 --- a/touch_vertical_container/touch_vertical_container.gd +++ b/touch_vertical_container/touch_vertical_container.gd @@ -1,11 +1,12 @@ extends ScrollContainer class_name TouchVerticalContainer -const POINTER_VELOCITY_DECAYING_FACTOR: float = 2.5 -const POINTER_VELOCITY_BOOST_FACTOR: float = 1.5 +const POINTER_VELOCITY_DECAYING_FACTOR: float = PI +const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25 var is_pointer_dragging := false var pointer_drag_velocity := 0.0 +var when_last_dragged := 0 var exclude_controls := [] onready var controls = get_node("controls") @@ -34,6 +35,7 @@ func _ready(): sensor.connect("on_drag", self, "pointer_input_on_drag_handler") sensor.connect("on_end_drag", self, "pointer_input_on_end_drag_handler") sensor.connect("on_click", self, "pointer_input_on_click_handler") + sensor.connect("on_scroll", self, "pointer_input_on_scroll_handler") func _process(delta: float): @@ -53,13 +55,17 @@ func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData) var reported_velocity_abs := abs(pointer.velocity.y) var relative_velocity := pointer.relative_position.y * Engine.get_frames_per_second() var relative_velocity_abs := abs(relative_velocity) - var max_velocity := pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity - pointer_drag_velocity = max_velocity * POINTER_VELOCITY_BOOST_FACTOR + pointer_drag_velocity = pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity self.scroll_vertical -= pointer.relative_position.y + when_last_dragged = OS.get_ticks_msec() func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputData): is_pointer_dragging = false + if OS.get_ticks_msec() - when_last_dragged > 20: + pointer_drag_velocity = 0.0 + else: + pointer_drag_velocity *= POINTER_VELOCITY_BOOST_FACTOR func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData): @@ -105,3 +111,8 @@ func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData break +func pointer_input_on_scroll_handler(pointer: PointerInputSensor.PointerInputData): + var target := self + target._gui_input(pointer.event) + + -- cgit v1.2.3 From f6369eaee39abe3d360ba42278a5a2a1d166f5af Mon Sep 17 00:00:00 2001 From: dam Date: Sun, 27 Mar 2022 16:57:14 +0000 Subject: Implemented prototype of option set list component. --- date_picker/date_picker.tscn | 3 - logic/database.gd | 2 +- logic/popup.gd | 36 +++++ logic/popup_list.gd | 28 ---- main.gd | 6 + main.tscn | 28 ++-- option_set/option_set.gd | 39 +++-- option_set/option_set.tscn | 3 - option_set/option_set_list.gd | 293 ++++++++++++++++++++++------------- option_set/option_set_list.tscn | 1 + project.godot | 18 ++- touch_item_list/touch_item_list.gd | 1 + touch_item_list/touch_item_list.tscn | 3 - 13 files changed, 290 insertions(+), 171 deletions(-) create mode 100644 logic/popup.gd delete mode 100644 logic/popup_list.gd (limited to 'main.gd') diff --git a/date_picker/date_picker.tscn b/date_picker/date_picker.tscn index 54b2b82..8b082f2 100644 --- a/date_picker/date_picker.tscn +++ b/date_picker/date_picker.tscn @@ -7,9 +7,6 @@ anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource( 2 ) -__meta__ = { -"_edit_use_anchors_": false -} [node name="year" type="Control" parent="."] anchor_right = 0.333 diff --git a/logic/database.gd b/logic/database.gd index c4482f4..370a679 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -118,7 +118,7 @@ func save_stage(entry: Dictionary): next_selected_idx = staged_idx else: db.append(entry) - add_item(get_entry_view(entry)) + self.add_item(get_entry_view(entry)) next_selected_idx = db.size() - 1 select(next_selected_idx) diff --git a/logic/popup.gd b/logic/popup.gd new file mode 100644 index 0000000..1b1813a --- /dev/null +++ b/logic/popup.gd @@ -0,0 +1,36 @@ +extends Control +class_name ModalPopup + +var control : Control + + +func _init(): + anchor_right = 1.0 + anchor_bottom = 1.0 + +func popup_control(item: Control): + control = item + control.connect("visibility_changed", self, "closed") + var background = $background + control.anchor_left = background.anchor_left + control.anchor_top = background.anchor_top + control.anchor_right = background.anchor_right + control.anchor_bottom = background.anchor_bottom + control.margin_left = 20 + control.margin_top = 20 + control.margin_right = -20 + control.margin_bottom = -20 + add_child(control) + show() + control.show() +# self.visible = true + + +func closed(): + if control.visible == true: + return + control.disconnect("visibility_changed", self, "closed") + remove_child(control) + self.hide() + + diff --git a/logic/popup_list.gd b/logic/popup_list.gd deleted file mode 100644 index 7e049fb..0000000 --- a/logic/popup_list.gd +++ /dev/null @@ -1,28 +0,0 @@ -extends Popup -class_name PopupList - -signal item_selected - -onready var item_list := get_node("list") as TouchItemList -onready var blur := get_node("blur") as ColorRect - - -func _ready(): - item_list.connect("item_selected", self, "selected") - - -func popup_options(options: Array): - item_list.v_scroll_bar.value = 0.0 - item_list.clear() - for it in options: - item_list.add_item(it) - self.popup_centered_ratio(0.9) - blur.rect_global_position = Vector2.ZERO - blur.rect_size = get_viewport_rect().size - - -func selected(index: int): - self.hide() - emit_signal("item_selected", index) - - diff --git a/main.gd b/main.gd index 5a78ede..7962d52 100644 --- a/main.gd +++ b/main.gd @@ -34,6 +34,12 @@ func _process(delta: float): for it in controls_sensible_to_keyboard: it.margin_bottom = -keyboard_height + # ~1650 fps with database using ItemList + Engine.target_fps = 0 + OS.vsync_enabled = false + OS.set_window_title("%03d" % [Engine.get_frames_per_second()]) + return + if power_throttle_timeout > 0.0: power_throttle_timeout -= delta else: diff --git a/main.tscn b/main.tscn index ed59082..8e750d4 100644 --- a/main.tscn +++ b/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=12 format=2] +[gd_scene load_steps=13 format=2] [ext_resource path="res://main.gd" type="Script" id=1] [ext_resource path="res://date_picker/date_picker.tscn" type="PackedScene" id=2] @@ -7,8 +7,9 @@ [ext_resource path="res://logic/database.gd" type="Script" id=5] [ext_resource path="res://fonts/font_icons.tres" type="DynamicFont" id=6] [ext_resource path="res://option_set/option_set.tscn" type="PackedScene" id=7] +[ext_resource path="res://option_set/option_set_list.gd" type="Script" id=8] [ext_resource path="res://logic/stage.gd" type="Script" id=9] -[ext_resource path="res://logic/popup_list.gd" type="Script" id=10] +[ext_resource path="res://logic/popup.gd" type="Script" id=10] [ext_resource path="res://touch_item_list/touch_item_list.tscn" type="PackedScene" id=11] [ext_resource path="res://touch_vertical_container/touch_vertical_container.tscn" type="PackedScene" id=12] @@ -17,9 +18,6 @@ anchor_right = 1.0 anchor_bottom = 1.0 theme = ExtResource( 3 ) script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} [node name="database" parent="." instance=ExtResource( 11 )] script = ExtResource( 5 ) @@ -246,19 +244,23 @@ window_title = "CONFIRM ACTION" dialog_text = "Do you confirm this action?" dialog_autowrap = true -[node name="popup_list" type="Popup" parent="."] +[node name="popup" type="ColorRect" parent="."] +visible = false anchor_right = 1.0 anchor_bottom = 1.0 -popup_exclusive = true +color = Color( 0, 0, 0, 0.501961 ) script = ExtResource( 10 ) -[node name="blur" type="ColorRect" parent="popup_list"] -show_behind_parent = true -anchor_right = 1.0 -anchor_bottom = 1.0 -color = Color( 0, 0, 0, 0.75 ) +[node name="background" type="Panel" parent="popup"] +anchor_left = 0.05 +anchor_top = 0.025 +anchor_right = 0.95 +anchor_bottom = 0.975 -[node name="list" parent="popup_list" instance=ExtResource( 11 )] +[node name="option_set_list" type="Control" parent="."] +visible = false +rect_clip_content = true +script = ExtResource( 8 ) [node name="debug" type="Label" parent="."] visible = false diff --git a/option_set/option_set.gd b/option_set/option_set.gd index 62d10d0..040566a 100644 --- a/option_set/option_set.gd +++ b/option_set/option_set.gd @@ -1,7 +1,6 @@ extends Control class_name OptionSet -var options: Array var text: String setget set_text, get_text func set_text(var value: String): @@ -10,9 +9,12 @@ func set_text(var value: String): func get_text() -> String: return input.text +var selected_idx: int + onready var input := get_node("input") as LineEdit onready var button := get_node("options") as Button # @DAM Maybe rename this. Also requires renaming on stage. -onready var popup := get_node("/root/main/popup_list") as PopupList +onready var popup := get_node("/root/main/popup") as ModalPopup +onready var options := get_node("/root/main/option_set_list") as OptionSetList func _ready(): @@ -23,14 +25,31 @@ func _ready(): func show_options(options_array: Array): - options = options_array - popup.connect("item_selected", self, "option_selected", [], CONNECT_ONESHOT) - popup.popup_options(options) + options.clear_items() + var main = get_node("/root/main") + main.remove_child(options) + options.add_items(options_array) + if options_array[selected_idx] == input.text: + options.select(selected_idx) + else: + options.unselect() + options.connect("item_selected", self, "option_selected", [], CONNECT_ONESHOT) + popup.popup_control(options) +# popup.connect("item_selected", self, "option_selected", [], CONNECT_ONESHOT) +# popup.clear_items() +# popup_opt.visible = true +# get_node("/root/main/popup/blur") +# popup.add_items(options) + + +func option_selected(index: int, text: String): + selected_idx = index + input.text = text + input.caret_position = input.max_length + button.release_focus() + options.visible = false + var main = get_node("/root/main") + main.add_child(options) -func option_selected(index: int): - if index >= 0 && index < options.size(): - input.text = options[index] - input.caret_position = input.max_length - button.release_focus() diff --git a/option_set/option_set.tscn b/option_set/option_set.tscn index bf15806..8864e45 100644 --- a/option_set/option_set.tscn +++ b/option_set/option_set.tscn @@ -6,9 +6,6 @@ anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} [node name="input" type="LineEdit" parent="."] anchor_right = 1.0 diff --git a/option_set/option_set_list.gd b/option_set/option_set_list.gd index 3cff78d..cad9e9c 100644 --- a/option_set/option_set_list.gd +++ b/option_set/option_set_list.gd @@ -1,93 +1,115 @@ extends Control +class_name OptionSetList + +signal item_selected # (idx: int, text: String) +signal nothing_selected # () + +export var autowrap := true + +var vscrollbar : VScrollBar +var labels : Control +var labels_end_positions: Array +var labels_sizes : Array + +var items : Array +var selected_idx := -1 +var is_dirty := true + +var normal_style : StyleBoxFlat +var selected_style : StyleBoxFlat +var border_size := 5 + +onready var font : Font = get_font("font") -var vscrollbar: VScrollBar -var labels: Control -var labels_positions: Array -var labels_sizes: Array -var font: Font - -var items: Array = [ - "item 1", - "item 22", - "item 333", - "item 4444", - "item 55555", - "item 666666", - "item 7777777", - "item 88888888", - "item 999999999", - "This is the longest item of all, but eventually stops.", - "item 1", - "item 22", - "item 333", - "item 4444", - "item 55555", - "item 666666", - "item 7777777", - "item 88888888", - "item 999999999", - "This is the longest item of all, but eventually stops.", - "item 1", - "item 22", - "item 333", - "item 4444", - "item 55555", - "item 666666", - "item 7777777", - "item 88888888", - "item 999999999", - "This is the longest item of all, but eventually stops.", -] - -# @DAM List of ideas to implement on this element: -# - Allow to toggle word-wrap on or off; -# - Allow to change items; -# - Only build and process labels when needed; func _init(): + self.anchor_right = 1.0 + self.anchor_bottom = 1.0 + self.rect_clip_content = true + labels = Control.new() - labels.anchor_right = 1.0 - labels.anchor_bottom = 1.0 + labels.anchor_right = 1.0 + labels.anchor_bottom = 1.0 + labels.mouse_filter = Control.MOUSE_FILTER_IGNORE + labels.name = "labels" add_child(labels) vscrollbar = VScrollBar.new() vscrollbar.anchor_left = 1.0 vscrollbar.anchor_bottom = 1.0 - vscrollbar.grow_horizontal = Control.GROW_DIRECTION_BEGIN - vscrollbar.name = "vscrollbar" + vscrollbar.grow_horizontal = Control.GROW_DIRECTION_BEGIN + vscrollbar.name = "vscrollbar" add_child(vscrollbar) - font = get_font("font") - connect("resized", self, "build_labels") + normal_style = StyleBoxFlat.new() + normal_style.border_width_bottom = border_size + normal_style.border_color = Color.white * 0.333 + normal_style.bg_color = Color.transparent + + selected_style = StyleBoxFlat.new() + selected_style.border_width_bottom = border_size + selected_style.border_color = Color.white * 0.333 + selected_style.bg_color = Color.dimgray + + connect("resized", self, "mark_as_dirty") -# @DAM We connect build_labels to resized during init. This calls build_labels immediately and the -# labels.rect_size is still not properly set thus, making the max_required_labels to return an -# incorrect value. To patch this, we are calling build_labels again on the _ready. Maybe we can sort -# this out in a cleaner way? -func _ready(): - build_labels() +func mark_as_dirty(): + is_dirty = true +func add_item(text: String): + items.append(text) + is_dirty = true -# @DAM This is only used once so, why not inline it? -func max_required_labels() -> int: - var max_labels := ceil(labels.rect_size.y / get_line_height()) + 1 - return int(min(items.size(), max_labels)) +func add_items(texts: Array): + items.append_array(texts) + is_dirty = true -func get_line_height() -> float: - return font.get_height() + font.get_descent() +func set_item(index: int, text: String): + items[index] = text + is_dirty = true + + +func set_items(indices: Array, texts: Array): + for idx in indices.size(): + items[indices[idx]] = texts[idx] + is_dirty = true + + +func remove_item(index: int): + items.remove(index) + is_dirty = true -func build_labels(): - var new_max_required_labels := max_required_labels() - var delta := new_max_required_labels - labels.get_child_count() +func remove_items(indices: Array): + indices.sort() + var idx := indices.size()-1 + while idx >= 0: + items.remove(indices[idx]) + idx -= 1 + is_dirty = true + + +func clear_items(): + items.clear() + is_dirty = true + + +func build_labels(): + + var num_of_labels := int(min( + items.size(), + ceil(labels.rect_size.y / (font.get_height() + border_size)) + 1 + )) + + var delta := num_of_labels - labels.get_child_count() + while delta > 0: var label := Label.new() - label.autowrap = true - label.anchor_left = 0.0 + label.autowrap = autowrap label.anchor_right = 1.0 label.valign = Label.VALIGN_TOP labels.add_child(label) @@ -98,61 +120,124 @@ func build_labels(): labels.remove_child(label) label.free() delta += 1 - - -func process_labels(): - labels_positions.clear() + + labels_end_positions.clear() labels_sizes.clear() - var position := 0.0 - var limit := labels.rect_size.x + + if num_of_labels == 0: + return + + var position := 0.0 + var proto_label := labels.get_child(0) as Label + var line_spacing := proto_label.get_constant("line_spacing") + var line_height := proto_label.get_line_height() for it in items: - var size := font.get_wordwrap_string_size(it, limit).y + proto_label.text = it + var line_count := proto_label.get_line_count() + var height := line_count * line_height + (line_count - 1) * line_spacing + border_size - # The get_wordwrap_string_size does not include the vertical spacing for the last line, thus - # we need to include it manually. - # Method 1 -# var lines = size / font.get_height() -# var height = size + font.get_descent() * lines - # Method 2 - var height = get_line_height() * ceil(size / get_line_height()) - # Method 3 - not correct -# var height = size + font.get_descent() - position += height - labels_positions.append(position) + labels_end_positions.append(position) labels_sizes.append(height) func _process(delta): - # @DAM We are recalculating the labels size and position on every update. - # This only has to be done on resize or when items change. - process_labels() + if is_dirty: + build_labels() + is_dirty = false + + var ratio := 1.0 + + if items.size() > 0: + ratio = vscrollbar.max_value / float(labels_end_positions.back()) vscrollbar.min_value = 0 - vscrollbar.max_value = rect_size.y - var ratio := rect_size.y / float(labels_positions[labels_positions.size()-1]) + vscrollbar.max_value = labels.rect_size.y vscrollbar.visible = ratio < 1.0 vscrollbar.page = ratio * (vscrollbar.max_value - vscrollbar.min_value) - var offset := vscrollbar.value - var bs_value := offset / ratio - var offset_idx := labels_positions.bsearch(bs_value) - - var wasted := 0 # @DAM To be removed. - var idx := 0 + var scrollbar_offset := vscrollbar.value + var labels_offset := scrollbar_offset / ratio + var idx_offset := labels_end_positions.bsearch(labels_offset) + + var idx := idx_offset for label in labels.get_children(): - if idx + offset_idx >= items.size(): - label.text = "" - wasted += 1 + + if idx >= items.size(): + label.visible = false continue -# break # @DAM Or should we use continue? - label.text = items[idx + offset_idx] - label.rect_position.y = labels_positions[idx + offset_idx] - labels_sizes[idx + offset_idx] - bs_value + + label.visible = true + label.text = items[idx] + label.rect_size.y = labels_sizes[idx] + label.rect_position.y = labels_end_positions[idx] - labels_sizes[idx] - labels_offset + + if idx == selected_idx: + label.add_stylebox_override("normal", selected_style) + else: + label.add_stylebox_override("normal", normal_style) + idx += 1 - if Engine.get_idle_frames() % 30 == 1: - print_debug("Wasted: %s" % wasted) - labels.margin_right = -vscrollbar.rect_size.x if vscrollbar.visible else 0.0 + +func get_item_at_position(mouse_position: Vector2) -> int: + if items.size() == 0: + return -1 + + var ratio := 1.0 + + if items.size() > 0: + ratio = vscrollbar.max_value / float(labels_end_positions.back()) + + var scrollbar_offset := vscrollbar.value + var labels_offset := scrollbar_offset / ratio + var position := mouse_position.y + labels_offset + var item_idx := labels_end_positions.bsearch(position) + + return int(min(item_idx, items.size()-1)) # Return last item when position is below it. + + +func select(index: int): + selected_idx = index + emit_signal("item_selected", selected_idx, items[selected_idx]) + + +func unselect(): + selected_idx = -1 + emit_signal("nothing_selected") + + +#func _input(event: InputEvent): +## @DAM Test and debug code. Delete when ready. +# if event is InputEventKey: +# if event.pressed && event.scancode == KEY_C: +# clear_items() +# if event.pressed && event.scancode == KEY_F: +# for idx in range(1, 26): +# if idx % 10 == 0: +# add_item("-- item %06d : This is the longest item of all, but eventually stops. --" % idx) +# else: +# add_item("-- item %06d --" % idx) + + +func _gui_input(event: InputEvent): + + # @DAM A bug on GODOT-3.X makes events from non-mouse-emulated pointers (index > 0) unreliable. + if event is InputEventScreenTouch || event is InputEventScreenDrag: + if event.index != 0: + return + + if event is InputEventMouseButton == false: + return + + if event is InputEventScreenTouch && event.pressed == false: + return + + var item_idx := get_item_at_position(get_local_mouse_position()) + if item_idx >= 0 && item_idx < items.size(): + select(item_idx) + + diff --git a/option_set/option_set_list.tscn b/option_set/option_set_list.tscn index f8361fa..d336961 100644 --- a/option_set/option_set_list.tscn +++ b/option_set/option_set_list.tscn @@ -5,4 +5,5 @@ [node name="option_set_list" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 +rect_clip_content = true script = ExtResource( 1 ) diff --git a/project.godot b/project.godot index b414dff..4a59943 100644 --- a/project.godot +++ b/project.godot @@ -25,19 +25,24 @@ _global_script_classes=[ { "path": "res://date_picker/date_picker.gd" }, { "base": "Control", +"class": "ModalPopup", +"language": "GDScript", +"path": "res://logic/popup.gd" +}, { +"base": "Control", "class": "OptionSet", "language": "GDScript", "path": "res://option_set/option_set.gd" }, { "base": "Control", -"class": "PointerInputSensor", +"class": "OptionSetList", "language": "GDScript", -"path": "res://pointer_input_sensor.gd" +"path": "res://option_set/option_set_list.gd" }, { -"base": "Popup", -"class": "PopupList", +"base": "Control", +"class": "PointerInputSensor", "language": "GDScript", -"path": "res://logic/popup_list.gd" +"path": "res://pointer_input_sensor.gd" }, { "base": "TouchVerticalContainer", "class": "Stage", @@ -63,9 +68,10 @@ _global_script_class_icons={ "Database": "", "DatabaseEntry": "", "DatePicker": "", +"ModalPopup": "", "OptionSet": "", +"OptionSetList": "", "PointerInputSensor": "", -"PopupList": "", "Stage": "", "TouchItemList": "", "TouchVerticalContainer": "", diff --git a/touch_item_list/touch_item_list.gd b/touch_item_list/touch_item_list.gd index 67f88d7..82f94f2 100644 --- a/touch_item_list/touch_item_list.gd +++ b/touch_item_list/touch_item_list.gd @@ -1,4 +1,5 @@ extends ItemList +#extends OptionSetList class_name TouchItemList const POINTER_VELOCITY_DECAYING_FACTOR: float = PI diff --git a/touch_item_list/touch_item_list.tscn b/touch_item_list/touch_item_list.tscn index 072f5b8..31fed01 100644 --- a/touch_item_list/touch_item_list.tscn +++ b/touch_item_list/touch_item_list.tscn @@ -8,9 +8,6 @@ anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 script = ExtResource( 2 ) -__meta__ = { -"_edit_use_anchors_": false -} [node name="sensor" type="Control" parent="."] anchor_right = 1.0 -- cgit v1.2.3 From a3c33b7b4937f73d1b1811e9096e5fcd04b3dff8 Mon Sep 17 00:00:00 2001 From: dam Date: Sun, 27 Mar 2022 17:04:58 +0000 Subject: Restored vsync (was disabled by mistake on previous commit). --- main.gd | 6 ------ 1 file changed, 6 deletions(-) (limited to 'main.gd') diff --git a/main.gd b/main.gd index 7962d52..5a78ede 100644 --- a/main.gd +++ b/main.gd @@ -34,12 +34,6 @@ func _process(delta: float): for it in controls_sensible_to_keyboard: it.margin_bottom = -keyboard_height - # ~1650 fps with database using ItemList - Engine.target_fps = 0 - OS.vsync_enabled = false - OS.set_window_title("%03d" % [Engine.get_frames_per_second()]) - return - if power_throttle_timeout > 0.0: power_throttle_timeout -= delta else: -- cgit v1.2.3 From 177fb734c21a583b3fedf91c0cad77137cffcf4d Mon Sep 17 00:00:00 2001 From: dam Date: Mon, 28 Mar 2022 15:47:08 +0000 Subject: Moved screen dots per inch initialization to onready var. --- date_picker/value_picker.gd | 13 ++++++------- main.gd | 1 - pointer_input_sensor.gd | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'main.gd') diff --git a/date_picker/value_picker.gd b/date_picker/value_picker.gd index 7429edd..ca1bcf4 100644 --- a/date_picker/value_picker.gd +++ b/date_picker/value_picker.gd @@ -10,22 +10,22 @@ const DRAG_THRESHOLD_CM: float = 0.250 export var min_value: int export var max_value: int -onready var input := get_node("current/input") as LineEdit -onready var label_previous := get_node("previous") as Label -onready var label_current := get_node("current") as Label -onready var label_next := get_node("next") as Label - var pointer: Dictionary var anchor: float var value: int var offset: float -var screen_dpcm: float var scroll_unit_height: float var label_previous_base_position: float var label_current_base_position: float var label_next_base_position: float +onready var input := get_node("current/input") as LineEdit +onready var label_previous := get_node("previous") as Label +onready var label_current := get_node("current") as Label +onready var label_next := get_node("next") as Label +onready var screen_dpcm := float(OS.get_screen_dpi()) / 2.54 + func _ready(): pointer = { @@ -41,7 +41,6 @@ func _ready(): input.connect("focus_entered", self, "input_focus_entered") input.connect("focus_exited", self, "input_focus_exited") - screen_dpcm = float(OS.get_screen_dpi()) / 2.54 scroll_unit_height = label_current.rect_size.y label_previous_base_position = label_previous.rect_position.y label_current_base_position = label_current.rect_position.y diff --git a/main.gd b/main.gd index 5a78ede..37c5ed6 100644 --- a/main.gd +++ b/main.gd @@ -22,7 +22,6 @@ func _init(): OS.request_permissions() - func _ready(): Input.set_use_accumulated_input(false) confirm_action.get_cancel().connect("pressed", self, "dialog_cancelled", ["confirmed"]) diff --git a/pointer_input_sensor.gd b/pointer_input_sensor.gd index 588bb81..f99fc17 100644 --- a/pointer_input_sensor.gd +++ b/pointer_input_sensor.gd @@ -46,11 +46,11 @@ class PointerInputData: export var drag_threshold_cm: float = 0.250 var pointer: PointerInputData -var screen_dpcm: float + +onready var screen_dpcm := float(OS.get_screen_dpi()) / 2.54 func _ready(): - screen_dpcm = float(OS.get_screen_dpi()) / 2.54 pointer = PointerInputData.new() pointer.target = self connect("mouse_entered", self, "_on_enter_exit", [true]) -- cgit v1.2.3 From 8d4db7d2c92aa719da8ce218df9490283754d20b Mon Sep 17 00:00:00 2001 From: dam Date: Wed, 30 Mar 2022 14:52:01 +0000 Subject: Fixed database fields being parsed as floats instead of integers by JSON. --- logic/database.gd | 8 ++++++++ main.gd | 1 + readme.md | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'main.gd') diff --git a/logic/database.gd b/logic/database.gd index a6f2747..5694dcc 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -158,6 +158,14 @@ func load_database(file_path: String = DATABASE_FILE_PATH): return db = parse_result.result["database"] + + # The JSON specification does not define integer or float types, but only a number type. + # Therefore, converting a Variant to JSON text will convert all numerical values to float types. + # Thus, we cast all integer values once we load them. + for it in db: + it.date_year = int(it.date_year) + it.date_month = int(it.date_month) + it.date_day = int(it.date_day) func save_database(file_path: String = DATABASE_FILE_PATH): diff --git a/main.gd b/main.gd index 37c5ed6..6171409 100644 --- a/main.gd +++ b/main.gd @@ -12,6 +12,7 @@ onready var controls_sensible_to_keyboard := [ func _init(): + Physics2DServer.set_active(false) PhysicsServer.set_active(false) diff --git a/readme.md b/readme.md index 4119da1..4d536ab 100644 --- a/readme.md +++ b/readme.md @@ -50,10 +50,10 @@ Surgery Log - [x] Check if import_option_sets, store_option_sets, store_database require the parameter save_changes; this requires changes on databse, stage and menu scripts; - [x] Maybe replace fonts with non-mono version; - [x] Fix option sets GUI element to provide word-wrap, otherwise long texts will be hidden; +- [x] After save and load, entries opened on stage always detect changes (even when no change occurs); - [ ] Add to popup: - - [ ] title; + - [x] title; - [ ] back/close button; -- [ ] After save and load, entries opened on stage always detect changes (even when no change occurs); - [ ] Use popup to display confirmation messages; - [ ] Theming: - [ ] Make all buttons with same style; -- cgit v1.2.3 From 9b619b8c5f117e53b121c2d868b024c7c7d08f4c Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 31 Mar 2022 16:05:54 +0000 Subject: Fixed signals on popup and modal_dialog to make those usable. --- dialog/dialog.gd | 77 +++++++++++++++++++++++++++++++++++++++++++ file_picker.gd | 18 ++++++++++ logic/database.gd | 10 +++--- logic/popup.gd | 36 ++++++++++++++------ logic/stage.gd | 11 ++++--- main.gd | 10 ------ main.tscn | 25 +++++++------- menu/menu.gd | 33 ++++++++++--------- option_set/option_set.gd | 4 --- option_set/option_set_list.gd | 26 +++++++++++---- project.godot | 6 ++++ readme.md | 7 ++-- theme_dark.tres | 6 ++++ theme_white.tres | 6 ---- 14 files changed, 200 insertions(+), 75 deletions(-) create mode 100644 dialog/dialog.gd create mode 100644 file_picker.gd create mode 100644 theme_dark.tres delete mode 100644 theme_white.tres (limited to 'main.gd') diff --git a/dialog/dialog.gd b/dialog/dialog.gd new file mode 100644 index 0000000..bfdbab5 --- /dev/null +++ b/dialog/dialog.gd @@ -0,0 +1,77 @@ +extends Control +class_name Dialog + +signal answered # (accepted: bool) +signal accepted # () +signal rejected # () + +export var clear_signals_on_hide := true + +var message : Label +var accept : Button +var reject : Button + + +func _init(): + self.anchor_right = 1.0 + self.anchor_bottom = 1.0 + self.rect_clip_content = true + self.connect("hide", self, "_clear_signals") + + reject = Button.new() + reject.text = "reject" + reject.anchor_top = 1.0 + reject.anchor_left = 0.0 + reject.anchor_right = 0.5 + reject.grow_vertical = Control.GROW_DIRECTION_BEGIN + reject.name = "reject" + reject.connect("pressed", self, "_signal_rejected") + add_child(reject) + + accept = Button.new() + accept.text = "accept" + accept.anchor_top = 1.0 + accept.anchor_left = 0.5 + accept.anchor_right = 1.0 + accept.grow_vertical = Control.GROW_DIRECTION_BEGIN + accept.name = "accept" + accept.connect("pressed", self, "_signal_accepted") + add_child(accept) + + message = Label.new() + message.autowrap = true + message.align = Label.ALIGN_CENTER + message.anchor_right = 1.0 + message.anchor_bottom = 1.0 + add_child(message) + + +func _clear_signals(): + if clear_signals_on_hide == false: + return + + for signal_name in ["answered", "accepted", "rejected"]: + for it in get_signal_connection_list(signal_name): + disconnect(it.signal, it.target, it.method) + + +func _signal_rejected(): + emit_signal("rejected") + emit_signal("answered", false) + hide() + + +func _signal_accepted(): + emit_signal("accepted") + emit_signal("answered", true) + hide() + + +func setup(message: String, accept_label: String = "Accept", reject_label: String = "Reject"): + self.message.text = message + accept.visible = accept_label != "" + accept.text = accept_label + reject.visible = reject_label != "" + reject.text = reject_label + + diff --git a/file_picker.gd b/file_picker.gd new file mode 100644 index 0000000..9715d0d --- /dev/null +++ b/file_picker.gd @@ -0,0 +1,18 @@ +extends FileDialog + +export var clear_signals_on_hide := true + + +func _init(): + self.connect("hide", self, "_clear_signals") + + +func _clear_signals(): + if clear_signals_on_hide == false: + return + + for signal_name in ["file_selected"]: + for it in get_signal_connection_list(signal_name): + disconnect(it.signal, it.target, it.method) + + diff --git a/logic/database.gd b/logic/database.gd index 5694dcc..b952356 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -8,12 +8,12 @@ var db: Array var selected_idx: int var staged_idx: int -onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog 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 popup := get_node("/root/main/popup") as ModalPopup +onready var dialog := get_node("/root/main/dialog") as Dialog func _init(): selected_idx = -1 @@ -75,9 +75,9 @@ func delete_action(): if selected_idx < 0: return - confirm_action.dialog_text = "Do you want to delete entry with process ID '%s' from the database?" % db[selected_idx].process_id - confirm_action.connect("confirmed", self, "delete_action_confirmed", [], CONNECT_ONESHOT) - confirm_action.show_modal(true) + dialog.setup("Do you want to delete entry with process ID '%s' from the database?" % db[selected_idx].process_id, "Yes, delete", "No") + dialog.connect("accepted", self, "delete_action_confirmed") + popup.open_popup("Delete entry?", dialog) func delete_action_confirmed(): diff --git a/logic/popup.gd b/logic/popup.gd index 2ff91fe..31897c7 100644 --- a/logic/popup.gd +++ b/logic/popup.gd @@ -3,20 +3,39 @@ class_name ModalPopup signal dismissed # () +export var clear_signals_on_hide := true + var control : Control var control_parent : Node onready var title := get_node("title") as Label onready var background := get_node("background") as Panel +onready var dismiss := get_node("dismiss") as Button func _init(): - anchor_right = 1.0 - anchor_bottom = 1.0 + self.anchor_right = 1.0 + self.anchor_bottom = 1.0 + self.rect_clip_content = true + self.connect("hide", self, "_clear_signals") func _ready(): - get_node("dismiss").connect("pressed", self, "dismiss") + dismiss.connect("pressed", self, "_dismiss") + + +func _clear_signals(): + if clear_signals_on_hide == false: + return + + for signal_name in ["dismissed"]: + for it in get_signal_connection_list(signal_name): + disconnect(it.signal, it.target, it.method) + + +func _dismiss(): + emit_signal("dismissed") + close_popup() func open_popup(title: String, item: Control): @@ -26,6 +45,7 @@ func open_popup(title: String, item: Control): self.title.text = title control = item + control.connect("hide", self, "close_popup") control_parent = control.get_parent() control_parent.remove_child(control) self.add_child(control) @@ -38,23 +58,19 @@ func open_popup(title: String, item: Control): control.margin_top = 20 control.margin_right = -20 control.margin_bottom = -20 - add_child(control) self.show() control.show() -func dismiss(): - emit_signal("dismissed") - - func close_popup(): if visible == false: return - self.hide() + + control.disconnect("hide", self, "close_popup") control.hide() + self.hide() remove_child(control) control_parent.add_child(control) control_parent = null - diff --git a/logic/stage.gd b/logic/stage.gd index bf311b9..fde67d3 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -23,7 +23,7 @@ const OPTION_SETS_TREE_STRUCTURE := { var staged_entry_hash: int var option_sets: Dictionary -onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog + onready var process_id := get_node("controls/process_id") as LineEdit onready var surgery_id := get_node("controls/surgery_id") as LineEdit onready var date := get_node("controls/date_picker") as DatePicker @@ -40,7 +40,8 @@ onready var notes := get_node("controls/notes") as LineEdit onready var save_button := get_node("controls/save") as Button onready var discard_button := get_node("controls/discard") as Button onready var scrollbar := get_v_scrollbar() - +onready var popup := get_node("/root/main/popup") as ModalPopup +onready var dialog := get_node("/root/main/dialog") as Dialog func _init(): exclude_controls = ["date_picker", "save", "discard"] @@ -110,9 +111,9 @@ func save_action(): func discard_action(): if get_stage().hash() != staged_entry_hash: - confirm_action.dialog_text = "Do you want to discard the changes made?" - confirm_action.connect("confirmed", self, "discard_action_confirmed", [], CONNECT_ONESHOT) - confirm_action.show_modal(true) + dialog.setup("Do you want to discard the changes made?", "Yes, discard", "No") + dialog.connect("accepted", self, "discard_action_confirmed") + popup.open_popup("Discard changes?", dialog) else: discard_action_confirmed() diff --git a/main.gd b/main.gd index 6171409..5612e01 100644 --- a/main.gd +++ b/main.gd @@ -3,11 +3,9 @@ extends Control var power_throttle_timeout: float onready var file_picker := get_node("/root/main/file_picker") as FileDialog -onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog onready var controls_sensible_to_keyboard := [ self, file_picker, - confirm_action, ] @@ -25,8 +23,6 @@ func _init(): func _ready(): Input.set_use_accumulated_input(false) - confirm_action.get_cancel().connect("pressed", self, "dialog_cancelled", ["confirmed"]) - file_picker.get_cancel().connect("pressed", self, "dialog_cancelled", ["file_selected"]) func _process(delta: float): @@ -50,9 +46,3 @@ func _unhandled_input(event: InputEvent): power_throttle_timeout = 3.5 -func dialog_cancelled(confirmation_signal_name: String): - var confirmation_handlers = confirm_action.get_signal_connection_list(confirmation_signal_name) - for it in confirmation_handlers: - confirm_action.disconnect(it.signal, it.target, it.method) - - diff --git a/main.tscn b/main.tscn index 83bcaf8..d388cb5 100644 --- a/main.tscn +++ b/main.tscn @@ -1,8 +1,8 @@ -[gd_scene load_steps=14 format=2] +[gd_scene load_steps=16 format=2] [ext_resource path="res://main.gd" type="Script" id=1] [ext_resource path="res://date_picker/date_picker.tscn" type="PackedScene" id=2] -[ext_resource path="res://theme_white.tres" type="Theme" id=3] +[ext_resource path="res://theme_dark.tres" type="Theme" id=3] [ext_resource path="res://menu/menu.gd" type="Script" id=4] [ext_resource path="res://logic/database.gd" type="Script" id=5] [ext_resource path="res://fonts/font_icons.tres" type="DynamicFont" id=6] @@ -13,6 +13,8 @@ [ext_resource path="res://touch_item_list/touch_item_list.tscn" type="PackedScene" id=11] [ext_resource path="res://touch_vertical_container/touch_vertical_container.tscn" type="PackedScene" id=12] [ext_resource path="res://fonts/font_mono_regular.tres" type="DynamicFont" id=13] +[ext_resource path="res://dialog/dialog.gd" type="Script" id=14] +[ext_resource path="res://file_picker.gd" type="Script" id=15] [node name="main" type="Control"] anchor_right = 1.0 @@ -240,19 +242,11 @@ filters = PoolStringArray( "*.*" ) show_hidden_files = true current_dir = "" current_path = "" +script = ExtResource( 15 ) __meta__ = { "_edit_use_anchors_": false } -[node name="confirm_action" type="ConfirmationDialog" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -size_flags_horizontal = 3 -size_flags_vertical = 3 -window_title = "" -dialog_text = "Do you confirm this action?" -dialog_autowrap = true - [node name="popup" type="ColorRect" parent="."] visible = false anchor_right = 1.0 @@ -290,7 +284,16 @@ flat = true [node name="option_set_list" type="Control" parent="."] visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 rect_clip_content = true script = ExtResource( 8 ) +[node name="dialog" type="Control" parent="."] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_clip_content = true +script = ExtResource( 14 ) + [editable path="stage"] diff --git a/menu/menu.gd b/menu/menu.gd index c963270..021c005 100644 --- a/menu/menu.gd +++ b/menu/menu.gd @@ -12,8 +12,9 @@ const menu_items: Array = [ const license_font_b612: String = "res://licenses/font_b612.txt" const license_godot: String = "res://licenses/godot.txt" -onready var popup := get_popup() as PopupMenu -onready var confirm_action := get_node("/root/main/confirm_action") as ConfirmationDialog +onready var menu := get_popup() as PopupMenu +onready var popup := get_node("/root/main/popup") as ModalPopup +onready var dialog := get_node("/root/main/dialog") as Dialog onready var file_picker := get_node("/root/main/file_picker") as FileDialog onready var database := get_node("/root/main/database") as Database onready var stage := get_node("/root/main/stage") as Stage @@ -21,8 +22,8 @@ onready var stage := get_node("/root/main/stage") as Stage func _ready(): for idx in range(menu_items.size()): - popup.add_item(menu_items[idx].label, idx) - popup.connect("id_pressed", self, "id_pressed") + menu.add_item(menu_items[idx].label, idx) + menu.connect("id_pressed", self, "id_pressed") func id_pressed(id: int): @@ -34,7 +35,7 @@ func import_option_sets_action(): file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) file_picker.filters = ["*.json", "*.csv"] file_picker.current_file = "" - file_picker.connect("file_selected", self, "import_option_sets_action_confirmed", [], CONNECT_ONESHOT) + file_picker.connect("file_selected", self, "import_option_sets_action_confirmed") file_picker.show_modal(true) file_picker.invalidate() @@ -68,9 +69,9 @@ func export_option_sets_action(): func clear_option_sets_action(): - confirm_action.dialog_text = "Do you want to delete all option sets?" - confirm_action.connect("confirmed", stage, "clear_option_sets", [], CONNECT_ONESHOT) - confirm_action.show_modal(true) + dialog.setup("Do you want to delete all option sets?", "Yes, delete.", "No") + dialog.connect("accepted", stage, "clear_option_sets") + popup.open_popup("Clear option sets?", dialog) func export_data_action(): @@ -84,20 +85,20 @@ func export_data_action(): func clear_data_action(): - confirm_action.dialog_text = "Do you want to delete all entries from the database?" - confirm_action.connect("confirmed", database, "clear_database", [], CONNECT_ONESHOT) - confirm_action.show_modal(true) + dialog.setup("Do you want to delete all entries from the database?", "Yes, delete.", "No") + dialog.connect("accepted", database, "clear_database") + popup.open_popup("Clear database?", dialog) func about_action(): - confirm_action.dialog_text = "Surgery Log\nversion 2022-02-27" - confirm_action.show_modal(true) + dialog.setup("Surgery Log\nversion 0.1", "", "") + popup.open_popup("About", dialog) # @DAM Hide this debug method before release. func test_fake_db_action(): - confirm_action.dialog_text = "Do you want to delete all entries from the database and replace by fake entries?" - confirm_action.connect("confirmed", database, "fake_database", [], CONNECT_ONESHOT) - confirm_action.show_modal(true) + dialog.setup("Do you want to delete all entries from the database and replace by fake entries?", "Yes, replace.", "No") + dialog.connect("accepted", database, "fake_database") + popup.open_popup("Fake DB?", dialog) diff --git a/option_set/option_set.gd b/option_set/option_set.gd index 637d033..0e8f90e 100644 --- a/option_set/option_set.gd +++ b/option_set/option_set.gd @@ -35,14 +35,10 @@ func show_options(options_array: Array): options.unselect() options.connect("item_selected", self, "popup_result", []) options.connect("nothing_selected", self, "popup_result", [-1, ""]) - popup.connect("dismissed", self, "popup_result", [selected_idx, input.text]) popup.open_popup(input.placeholder_text, options) func popup_result(index: int, text: String): - options.disconnect("item_selected", self, "popup_result") - options.disconnect("nothing_selected", self, "popup_result") - popup.disconnect("dismissed", self, "popup_result") selected_idx = index input.text = text input.caret_position = input.max_length diff --git a/option_set/option_set_list.gd b/option_set/option_set_list.gd index ea6b2d8..8aad1b1 100644 --- a/option_set/option_set_list.gd +++ b/option_set/option_set_list.gd @@ -5,6 +5,8 @@ const POINTER_VELOCITY_DECAYING_FACTOR: float = PI const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25 const EXACT_SELECTION: bool = false +export var clear_signals_on_hide := true + signal item_selected # (idx: int, text: String) signal nothing_selected # () @@ -17,7 +19,7 @@ var pointer_drag_velocity := 0.0 var when_last_dragged := 0 var labels : Control -var labels_end_positions: Array +var labels_end_positions : Array var labels_sizes : Array var items : Array @@ -35,7 +37,8 @@ func _init(): self.anchor_right = 1.0 self.anchor_bottom = 1.0 self.rect_clip_content = true - + self.connect("hide", self, "_clear_signals") + labels = Control.new() labels.anchor_right = 1.0 labels.anchor_bottom = 1.0 @@ -219,6 +222,7 @@ func get_item_at_position(mouse_position: Vector2) -> int: var position := mouse_position.y + labels_offset var item_idx := labels_end_positions.bsearch(position) + # @DAM Fix this to return -1 if no item was selected. return int(min(item_idx, items.size()-1)) # Return last item when position is below it. @@ -227,6 +231,7 @@ func select(index: int): emit_signal("item_selected", selected_idx, items[selected_idx]) +# @DAM Do we really need this? We should have just one signal to the selection. func unselect(): selected_idx = -1 emit_signal("nothing_selected") @@ -256,10 +261,10 @@ func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputD func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData): - var selected_idx := get_item_at_position(pointer.current_position - rect_global_position) - if selected_idx >= 0: - select(selected_idx) - emit_signal("item_selected", selected_idx) + var item_idx := get_item_at_position(pointer.current_position - rect_global_position) + if item_idx >= 0: + select(item_idx) + emit_signal("item_selected", item_idx) else: unselect() emit_signal("nothing_selected") @@ -269,3 +274,12 @@ func pointer_input_on_scroll_handler(pointer: PointerInputSensor.PointerInputDat pointer_drag_velocity -= pointer.scroll * POINTER_VELOCITY_BOOST_FACTOR * screen_dpcm +func _clear_signals(): + if clear_signals_on_hide == false: + return + + for signal_name in ["item_selected", "nothing_selected"]: + for it in get_signal_connection_list(signal_name): + disconnect(it.signal, it.target, it.method) + + diff --git a/project.godot b/project.godot index 4a59943..4a3e1ed 100644 --- a/project.godot +++ b/project.godot @@ -25,6 +25,11 @@ _global_script_classes=[ { "path": "res://date_picker/date_picker.gd" }, { "base": "Control", +"class": "Dialog", +"language": "GDScript", +"path": "res://dialog/dialog.gd" +}, { +"base": "Control", "class": "ModalPopup", "language": "GDScript", "path": "res://logic/popup.gd" @@ -68,6 +73,7 @@ _global_script_class_icons={ "Database": "", "DatabaseEntry": "", "DatePicker": "", +"Dialog": "", "ModalPopup": "", "OptionSet": "", "OptionSetList": "", diff --git a/readme.md b/readme.md index c32faf9..5d48eff 100644 --- a/readme.md +++ b/readme.md @@ -55,8 +55,11 @@ Surgery Log - [x] title; - [x] dismiss button; - [x] Hide dialogs title bar (appear in the top with 1 or 2 pixels height); -- [ ] Use popup to display confirmation messages; -- [ ] What to do when the optionset has no options available? +- [x] Fix automation to automatically disconnect acceptance signal handlers when reject is chosen on file_picker/confirm_action; +- [x] Use popup to display confirmation messages; +- [ ] Improve option sets: + - [ ] selecting outside optionset entry should select none + - [ ] if no options are available, show nothing instead of "--" - [ ] Fix back button: - on stage screen should show pop-up asking it changes are to be discarded; - on file-pickers screen should close them; diff --git a/theme_dark.tres b/theme_dark.tres new file mode 100644 index 0000000..832b226 --- /dev/null +++ b/theme_dark.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=2] + +[ext_resource path="res://fonts/font_regular.tres" type="DynamicFont" id=1] + +[resource] +default_font = ExtResource( 1 ) diff --git a/theme_white.tres b/theme_white.tres deleted file mode 100644 index 832b226..0000000 --- a/theme_white.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Theme" load_steps=2 format=2] - -[ext_resource path="res://fonts/font_regular.tres" type="DynamicFont" id=1] - -[resource] -default_font = ExtResource( 1 ) -- cgit v1.2.3 From 0093ad0682e493e5f8115566f71c8cb8a2d7c0d9 Mon Sep 17 00:00:00 2001 From: dam Date: Mon, 4 Apr 2022 10:36:26 +0000 Subject: Implement back button logic. --- logic/database.gd | 13 ------------- logic/popup.gd | 4 ++-- main.gd | 17 +++++++++++++++++ readme.md | 3 +-- 4 files changed, 20 insertions(+), 17 deletions(-) (limited to 'main.gd') diff --git a/logic/database.gd b/logic/database.gd index ed39d43..ea27ef2 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -38,19 +38,6 @@ func _ready(): clear_selection() -func _notification(what: int): - # @DAM This code should be moved into the main.gd which should check which node was currently - # active and above, and send the signal there. - 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)] diff --git a/logic/popup.gd b/logic/popup.gd index 31897c7..d7c98f9 100644 --- a/logic/popup.gd +++ b/logic/popup.gd @@ -21,7 +21,7 @@ func _init(): func _ready(): - dismiss.connect("pressed", self, "_dismiss") + dismiss.connect("pressed", self, "dismiss") func _clear_signals(): @@ -33,7 +33,7 @@ func _clear_signals(): disconnect(it.signal, it.target, it.method) -func _dismiss(): +func dismiss(): emit_signal("dismissed") close_popup() diff --git a/main.gd b/main.gd index 5612e01..ec449df 100644 --- a/main.gd +++ b/main.gd @@ -2,7 +2,10 @@ extends Control var power_throttle_timeout: float +onready var popup := get_node("/root/main/popup") as ModalPopup onready var file_picker := get_node("/root/main/file_picker") as FileDialog +onready var stage := get_node("/root/main/stage") as Stage +onready var database := get_node("/root/main/database") as Database onready var controls_sensible_to_keyboard := [ self, file_picker, @@ -46,3 +49,17 @@ func _unhandled_input(event: InputEvent): power_throttle_timeout = 3.5 +func _notification(what: int): + if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST: + if popup.visible: + popup.call_deferred("dismiss") + elif file_picker.visible: + file_picker.hide() + elif stage.visible: + stage.discard_action() + elif database.selected_idx != -1: + database.clear_selection() + else: + get_tree().quit() + + diff --git a/readme.md b/readme.md index 56f2021..646e94d 100644 --- a/readme.md +++ b/readme.md @@ -59,13 +59,12 @@ Surgery Log - [x] Use popup to display confirmation messages; - [x] Improve option sets: selecting outside optionset entry should select none; if no options are available, show nothing instead of "--"; - [x] Set stage discard/save buttons side by side; -- [ ] Fix back button: +- [x] Implement back button logic; - 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; - - on popup - [ ] Improve appearance: - [ ] database menu; - [ ] database action buttons; -- cgit v1.2.3