diff options
| author | dam <dam@gudinoff> | 2022-04-18 09:06:19 +0000 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2022-04-18 09:06:19 +0000 |
| commit | 79691f93bab7aa093bb606bfb80d2b4b236ee091 (patch) | |
| tree | 6e90c6601893895d7640f478f0cad402cc7590b4 /ui/date_picker | |
| parent | 697e1ba3c4cb0a96c4584f1553de368d46287ab7 (diff) | |
| parent | ee31a9a3d387121030a5f4503adeac5816d7726f (diff) | |
| download | surgery-log-1.0.tar.zst surgery-log-1.0.zip | |
Merge godot branch.v1.0
Diffstat (limited to 'ui/date_picker')
| -rw-r--r-- | ui/date_picker/date_picker.gd | 64 | ||||
| -rw-r--r-- | ui/date_picker/date_picker.tscn | 189 | ||||
| -rw-r--r-- | ui/date_picker/value_picker.gd | 132 |
3 files changed, 385 insertions, 0 deletions
diff --git a/ui/date_picker/date_picker.gd b/ui/date_picker/date_picker.gd new file mode 100644 index 0000000..e2a793f --- /dev/null +++ b/ui/date_picker/date_picker.gd @@ -0,0 +1,64 @@ +extends Control +class_name DatePicker + +const days_per_month: Dictionary = { + 1: 31, + 2: 28, + 3: 31, + 4: 30, + 5: 31, + 6: 30, + 7: 31, + 8: 31, + 9: 30, + 10: 31, + 11: 30, + 12: 31, +} + +onready var year_picker := get_node("year") as ValuePicker +onready var month_picker := get_node("month") as ValuePicker +onready var day_picker := get_node("day") as ValuePicker + + +func _process(delta: float): + var year := year_picker.value + var month := month_picker.value + var day := day_picker.value + var days_on_month: int = days_per_month[month] + + var is_leap_year := (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 + if is_leap_year && month == 2: + days_on_month = 29 + + if day > days_on_month: + day_picker.value = days_on_month + day_picker.max_value = days_on_month + + +func get_day() -> int: + return day_picker.value + + +func get_month() -> int: + return month_picker.value + + +func get_year() -> int: + return year_picker.value + + +func get_date() -> Dictionary: + return { + year = year_picker.value, + month = month_picker.value, + day = day_picker.value, + } + + +func set_date(new_year: int, new_month: int, new_day: int): + year_picker.value = new_year + month_picker.value = new_month + day_picker.value = new_day + + diff --git a/ui/date_picker/date_picker.tscn b/ui/date_picker/date_picker.tscn new file mode 100644 index 0000000..e3d88c4 --- /dev/null +++ b/ui/date_picker/date_picker.tscn @@ -0,0 +1,189 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://ui/date_picker/value_picker.gd" type="Script" id=1] +[ext_resource path="res://ui/date_picker/date_picker.gd" type="Script" id=2] + +[node name="date_picker" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 2 ) + +[node name="year" type="Control" parent="."] +anchor_right = 0.333 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +min_value = 1 +max_value = 9999 + +[node name="previous" type="Label" parent="year"] +anchor_right = 1.0 +anchor_bottom = 0.333 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="current" type="Label" parent="year"] +anchor_top = 0.333 +anchor_right = 1.0 +anchor_bottom = 0.666 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="input" type="LineEdit" parent="year/current"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_next = NodePath("../../../month/current/input") +custom_constants/minimum_spaces = 0 +align = 1 +max_length = 4 +caret_blink = true + +[node name="next" type="Label" parent="year"] +anchor_top = 0.666 +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="month" type="Control" parent="."] +anchor_left = 0.333 +anchor_right = 0.666 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +min_value = 1 +max_value = 12 + +[node name="previous" type="Label" parent="month"] +anchor_right = 1.0 +anchor_bottom = 0.333 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="current" type="Label" parent="month"] +anchor_top = 0.333 +anchor_right = 1.0 +anchor_bottom = 0.666 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="input" type="LineEdit" parent="month/current"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_next = NodePath("../../../day/current/input") +focus_previous = NodePath("../../../year/current/input") +custom_constants/minimum_spaces = 0 +align = 1 +max_length = 2 +caret_blink = true + +[node name="next" type="Label" parent="month"] +anchor_top = 0.666 +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="day" type="Control" parent="."] +anchor_left = 0.666 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +min_value = 1 +max_value = 31 + +[node name="previous" type="Label" parent="day"] +anchor_right = 1.0 +anchor_bottom = 0.333 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="current" type="Label" parent="day"] +anchor_top = 0.333 +anchor_right = 1.0 +anchor_bottom = 0.666 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="input" type="LineEdit" parent="day/current"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_previous = NodePath("../../../month/current/input") +custom_constants/minimum_spaces = 0 +align = 1 +max_length = 2 +caret_blink = true + +[node name="next" type="Label" parent="day"] +anchor_top = 0.666 +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 1 +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="split_upper" type="ColorRect" parent="."] +anchor_top = 0.333 +anchor_right = 1.0 +anchor_bottom = 0.343 +color = Color( 1, 1, 1, 0.25 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="split_lower" type="ColorRect" parent="."] +anchor_top = 0.666 +anchor_right = 1.0 +anchor_bottom = 0.676 +color = Color( 1, 1, 1, 0.25 ) +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/ui/date_picker/value_picker.gd b/ui/date_picker/value_picker.gd new file mode 100644 index 0000000..b70f1aa --- /dev/null +++ b/ui/date_picker/value_picker.gd @@ -0,0 +1,132 @@ +extends Control + +class_name ValuePicker + +const VELOCITY_DECAYING_FACTOR: float = 5.5 +const BINNING_THRESHOLD: float = 0.75 +const BINNING_ADJUST_P: float = 5.0 +const DRAG_THRESHOLD_CM: float = 0.250 + +export var min_value: int +export var max_value: int + +var pointer: Dictionary +var anchor: float +var value: int +var offset: 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 = { + index = -1, + initial_position = Vector2.ZERO, + current_position = Vector2.ZERO, + velocity = Vector2.ZERO, + was_dragged = false, + is_active = false, + } + + input.connect("text_entered", self, "input_text_entered") + input.connect("focus_entered", self, "input_focus_entered") + input.connect("focus_exited", self, "input_focus_exited") + + 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 + label_next_base_position = label_next.rect_position.y + + value = min_value + offset = 0.0 + + +func _process(delta: float): + + if pointer.is_active: + var dragged_distance: float = - (pointer.current_position.y - pointer.initial_position.y) + offset = anchor + (dragged_distance / scroll_unit_height) + value = int(offset) + offset -= value + else: + pointer.velocity *= clamp((1.0 - VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0) + offset -= pointer.velocity.y * delta / scroll_unit_height + if abs(pointer.velocity.y) < BINNING_THRESHOLD * scroll_unit_height: + offset -= offset * BINNING_ADJUST_P * delta + + # Using 'offset * 2.0' (equivalent to 'offset / 0.5') rounds the value based on 0.5 units. + var cummulative_displacement := int(offset * 2.0) + value = wrapi(value + cummulative_displacement, min_value, max_value + 1) + offset -= cummulative_displacement + + label_current.text = "%d" % value + label_next.text = "%d" % wrapi(value + 1, min_value, max_value + 1) + label_previous.text = "%d" % wrapi(value - 1, min_value, max_value + 1) + + var offset_position := offset * scroll_unit_height + label_current.rect_position.y = label_current_base_position - offset_position + label_previous.rect_position.y = label_previous_base_position - offset_position + label_next.rect_position.y = label_next_base_position - offset_position + + label_previous.modulate.a = 0.5 - offset + label_next.modulate.a = offset + 0.5 + + +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 InputEventScreenTouch && (pointer.is_active == false || pointer.index == event.index): + var touch := event as InputEventScreenTouch + pointer.is_active = event.pressed + pointer.current_position = touch.position + if pointer.is_active: + input.release_focus() + pointer.index = touch.index + pointer.initial_position = touch.position + anchor = value + offset + else: + if pointer.was_dragged == false: # Click detected. + input.grab_focus() + pointer.index = -1 + pointer.was_dragged = false + + if event is InputEventScreenDrag && event.index == pointer.index: + var drag := event as InputEventScreenDrag + pointer.current_position = drag.position + pointer.velocity = drag.speed + if pointer.current_position.distance_to(pointer.initial_position) / screen_dpcm > DRAG_THRESHOLD_CM: + pointer.was_dragged = true + + +func input_text_entered(new_text: String): + input.release_focus() + + +func input_focus_entered(): + pointer.velocity = Vector2.ZERO # Avoid changing to other value once entering input. + input.text = "%d" % value + input.visible = true + input.select_all() + label_current.self_modulate.a = 0.0 + + +func input_focus_exited(): + if input.text.is_valid_integer(): + value = wrapi(int(input.text), min_value, max_value + 1) + input.visible = false + label_current.self_modulate.a = 1.0 + + |
