aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordam <dam@gudinoff>2022-03-27 16:57:14 +0000
committerdam <dam@gudinoff>2022-03-27 16:57:14 +0000
commitf6369eaee39abe3d360ba42278a5a2a1d166f5af (patch)
tree1fec29cbf9de9537efec5a4b7ddd870e4fc2f512
parenta6d836d53a09c5b2abedccba51ac428fcfc74379 (diff)
downloadsurgery-log-f6369eaee39abe3d360ba42278a5a2a1d166f5af.tar.zst
surgery-log-f6369eaee39abe3d360ba42278a5a2a1d166f5af.zip
Implemented prototype of option set list component.
-rw-r--r--date_picker/date_picker.tscn3
-rw-r--r--logic/database.gd2
-rw-r--r--logic/popup.gd36
-rw-r--r--logic/popup_list.gd28
-rw-r--r--main.gd6
-rw-r--r--main.tscn28
-rw-r--r--option_set/option_set.gd39
-rw-r--r--option_set/option_set.tscn3
-rw-r--r--option_set/option_set_list.gd293
-rw-r--r--option_set/option_set_list.tscn1
-rw-r--r--project.godot18
-rw-r--r--touch_item_list/touch_item_list.gd1
-rw-r--r--touch_item_list/touch_item_list.tscn3
13 files changed, 290 insertions, 171 deletions
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