From 465200cd283af77a82b7146705cba2aefc9568b3 Mon Sep 17 00:00:00 2001 From: Face <69168154+face-hh@users.noreply.github.com> Date: Fri, 25 Jul 2025 13:43:11 +0300 Subject: [PATCH] input (date) --- Assets/Icons/arrow-down.svg | 1 + Assets/Icons/arrow-down.svg.import | 37 ++ Assets/Icons/arrow-up.svg | 1 + Assets/Icons/arrow-up.svg.import | 37 ++ Assets/Icons/calendar.svg | 1 + Assets/Icons/calendar.svg.import | 37 ++ Assets/Icons/calendar_16x16.svg | 2 + Assets/Icons/calendar_16x16.svg.import | 37 ++ README.md | 3 +- Scenes/Styles/BrowserText.tres | 6 +- Scenes/Tab.tscn | 13 +- Scenes/Tags/input.tscn | 8 +- Scripts/Tab.gd | 3 + Scripts/TabContainer.gd | 1 - Scripts/Tags/input.gd | 108 +++-- Scripts/main.gd | 4 +- addons/DatePicker/Calendar.cs.uid | 1 + addons/DatePicker/Calendar.gd | 95 ++++ addons/DatePicker/Calendar.gd.uid | 1 + addons/DatePicker/Calendar.tscn | 88 ++++ addons/DatePicker/CalendarGroup.tres | 4 + addons/DatePicker/DateButton.cs.uid | 1 + addons/DatePicker/DateButton.gd | 77 +++ addons/DatePicker/DateButton.gd.uid | 1 + addons/DatePicker/DateButton.tscn | 33 ++ addons/DatePicker/ICalendarView.cs.uid | 1 + addons/DatePicker/ICalendarView.gd | 19 + addons/DatePicker/ICalendarView.gd.uid | 1 + addons/DatePicker/LICENSE.md | 21 + addons/DatePicker/MonthView.cs.uid | 1 + addons/DatePicker/MonthView.gd | 146 ++++++ addons/DatePicker/MonthView.gd.uid | 1 + addons/DatePicker/MonthView.tscn | 642 +++++++++++++++++++++++++ addons/DatePicker/README.md | 4 + addons/DatePicker/YearView.cs.uid | 1 + addons/DatePicker/YearView.gd | 92 ++++ addons/DatePicker/YearView.gd.uid | 1 + addons/DatePicker/YearView.tscn | 82 ++++ addons/DatePicker/plugin.cfg | 7 + project.godot | 2 +- 40 files changed, 1554 insertions(+), 67 deletions(-) create mode 100644 Assets/Icons/arrow-down.svg create mode 100644 Assets/Icons/arrow-down.svg.import create mode 100644 Assets/Icons/arrow-up.svg create mode 100644 Assets/Icons/arrow-up.svg.import create mode 100644 Assets/Icons/calendar.svg create mode 100644 Assets/Icons/calendar.svg.import create mode 100644 Assets/Icons/calendar_16x16.svg create mode 100644 Assets/Icons/calendar_16x16.svg.import create mode 100644 addons/DatePicker/Calendar.cs.uid create mode 100644 addons/DatePicker/Calendar.gd create mode 100644 addons/DatePicker/Calendar.gd.uid create mode 100644 addons/DatePicker/Calendar.tscn create mode 100644 addons/DatePicker/CalendarGroup.tres create mode 100644 addons/DatePicker/DateButton.cs.uid create mode 100644 addons/DatePicker/DateButton.gd create mode 100644 addons/DatePicker/DateButton.gd.uid create mode 100644 addons/DatePicker/DateButton.tscn create mode 100644 addons/DatePicker/ICalendarView.cs.uid create mode 100644 addons/DatePicker/ICalendarView.gd create mode 100644 addons/DatePicker/ICalendarView.gd.uid create mode 100644 addons/DatePicker/LICENSE.md create mode 100644 addons/DatePicker/MonthView.cs.uid create mode 100644 addons/DatePicker/MonthView.gd create mode 100644 addons/DatePicker/MonthView.gd.uid create mode 100644 addons/DatePicker/MonthView.tscn create mode 100644 addons/DatePicker/README.md create mode 100644 addons/DatePicker/YearView.cs.uid create mode 100644 addons/DatePicker/YearView.gd create mode 100644 addons/DatePicker/YearView.gd.uid create mode 100644 addons/DatePicker/YearView.tscn create mode 100644 addons/DatePicker/plugin.cfg diff --git a/Assets/Icons/arrow-down.svg b/Assets/Icons/arrow-down.svg new file mode 100644 index 0000000..f5ed454 --- /dev/null +++ b/Assets/Icons/arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Assets/Icons/arrow-down.svg.import b/Assets/Icons/arrow-down.svg.import new file mode 100644 index 0000000..cd8f62f --- /dev/null +++ b/Assets/Icons/arrow-down.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1bqnfwurnnrc" +path="res://.godot/imported/arrow-down.svg-1e5275b4ffe06ccf19d892794fc84c5b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Icons/arrow-down.svg" +dest_files=["res://.godot/imported/arrow-down.svg-1e5275b4ffe06ccf19d892794fc84c5b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Assets/Icons/arrow-up.svg b/Assets/Icons/arrow-up.svg new file mode 100644 index 0000000..1b72e56 --- /dev/null +++ b/Assets/Icons/arrow-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Assets/Icons/arrow-up.svg.import b/Assets/Icons/arrow-up.svg.import new file mode 100644 index 0000000..f631ef5 --- /dev/null +++ b/Assets/Icons/arrow-up.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d1jslyy8aajcn" +path="res://.godot/imported/arrow-up.svg-e673f59c1623de68e856c93a57be6cb0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Icons/arrow-up.svg" +dest_files=["res://.godot/imported/arrow-up.svg-e673f59c1623de68e856c93a57be6cb0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Assets/Icons/calendar.svg b/Assets/Icons/calendar.svg new file mode 100644 index 0000000..861324c --- /dev/null +++ b/Assets/Icons/calendar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Assets/Icons/calendar.svg.import b/Assets/Icons/calendar.svg.import new file mode 100644 index 0000000..a47d734 --- /dev/null +++ b/Assets/Icons/calendar.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://da721lhb4r0so" +path="res://.godot/imported/calendar.svg-4a7aa93bfd00cdce85eae886ac3d4d51.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Icons/calendar.svg" +dest_files=["res://.godot/imported/calendar.svg-4a7aa93bfd00cdce85eae886ac3d4d51.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Assets/Icons/calendar_16x16.svg b/Assets/Icons/calendar_16x16.svg new file mode 100644 index 0000000..fe00460 --- /dev/null +++ b/Assets/Icons/calendar_16x16.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Assets/Icons/calendar_16x16.svg.import b/Assets/Icons/calendar_16x16.svg.import new file mode 100644 index 0000000..964f37a --- /dev/null +++ b/Assets/Icons/calendar_16x16.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mol3s1ujp0k2" +path="res://.godot/imported/calendar_16x16.svg-38c497b40cde0596f1f838e0a52171d2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Icons/calendar_16x16.svg" +dest_files=["res://.godot/imported/calendar_16x16.svg-38c497b40cde0596f1f838e0a52171d2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/README.md b/README.md index f45ef62..36f16b6 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ TODO: 8. **More input types** (url, tel, date, time, etc.) 9. **Required** attribute for inputs 10. Installer should register **gurt://** as a valid protocol thru the registry. +11. < input type=**datetime** />, essentially a type "date" but with a vertical separator, then `mm | ss | FORMAT` layout for time. Issues: 1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing Notes: -- **** is sort-of inline in normal web. We render it as a block element (new-line). +- **< input />** is sort-of inline in normal web. We render it as a block element (new-line). - A single `RichTextLabel` for inline text tags should stop, we should use invididual ones so it's easier to style and achieve separation through a `vboxcontainer`. diff --git a/Scenes/Styles/BrowserText.tres b/Scenes/Styles/BrowserText.tres index 30f6d3d..61f5f0c 100644 --- a/Scenes/Styles/BrowserText.tres +++ b/Scenes/Styles/BrowserText.tres @@ -20,8 +20,8 @@ [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_c32on"] -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7g0pl"] -bg_color = Color(0.168627, 0.168627, 0.168627, 1) +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0v877"] +bg_color = Color(0.105882, 0.105882, 0.105882, 1) corner_radius_top_left = 4 corner_radius_top_right = 4 corner_radius_bottom_right = 4 @@ -142,7 +142,7 @@ corner_radius_bottom_left = 4 [resource] Button/styles/focus = SubResource("StyleBoxEmpty_c32on") -Button/styles/hover = SubResource("StyleBoxFlat_7g0pl") +Button/styles/hover = SubResource("StyleBoxFlat_0v877") Button/styles/normal = SubResource("StyleBoxFlat_c32on") CheckBox/constants/icon_max_width = 24 CheckBox/icons/checked = ExtResource("2_3k2jb") diff --git a/Scenes/Tab.tscn b/Scenes/Tab.tscn index 20e6c0c..5a06534 100644 --- a/Scenes/Tab.tscn +++ b/Scenes/Tab.tscn @@ -91,7 +91,7 @@ _data = { } [node name="Tab1" type="Control"] -custom_minimum_size = Vector2(350, 50) +custom_minimum_size = Vector2(0, 50) layout_mode = 3 anchors_preset = 0 offset_right = 350.0 @@ -103,7 +103,7 @@ unique_name_in_owner = true z_index = -2 clip_contents = true layout_mode = 1 -offset_right = 350.0 +offset_right = 47.0 offset_bottom = 50.0 focus_mode = 0 mouse_default_cursor_shape = 2 @@ -124,9 +124,8 @@ text_overrun_behavior = 3 unique_name_in_owner = true z_index = 1 layout_mode = 0 -offset_left = 278.0 +offset_left = -64.0 offset_top = 2.0 -offset_right = 342.0 offset_bottom = 50.0 texture = ExtResource("3_q3baj") @@ -134,9 +133,8 @@ texture = ExtResource("3_q3baj") unique_name_in_owner = true custom_minimum_size = Vector2(23, 23) layout_mode = 0 -offset_left = 8.0 +offset_left = -23.0 offset_top = 13.0 -offset_right = 31.0 offset_bottom = 36.0 texture = ExtResource("6_ib6pj") expand_mode = 1 @@ -146,9 +144,8 @@ unique_name_in_owner = true z_index = 2 custom_minimum_size = Vector2(34, 34) layout_mode = 0 -offset_left = 319.0 +offset_left = -34.0 offset_top = 12.0 -offset_right = 353.0 offset_bottom = 46.0 scale = Vector2(0.75, 0.75) focus_mode = 0 diff --git a/Scenes/Tags/input.tscn b/Scenes/Tags/input.tscn index 5fe6a7a..33aace4 100644 --- a/Scenes/Tags/input.tscn +++ b/Scenes/Tags/input.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=4 format=3 uid="uid://c7yay102a3b4c"] +[gd_scene load_steps=5 format=3 uid="uid://c7yay102a3b4c"] [ext_resource type="Script" uid="uid://kv6ebscarj2e" path="res://Scripts/Tags/input.gd" id="1_input"] [ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_theme"] +[ext_resource type="PackedScene" uid="uid://b6c1twaog7hui" path="res://addons/DatePicker/DateButton.tscn" id="3_a88g6"] [sub_resource type="ButtonGroup" id="ButtonGroup_06us3"] @@ -21,6 +22,7 @@ size_flags_vertical = 0 script = ExtResource("1_input") [node name="LineEdit" type="LineEdit" parent="."] +visible = false layout_mode = 1 offset_right = 400.0 offset_bottom = 35.0 @@ -48,9 +50,13 @@ button_group = SubResource("ButtonGroup_06us3") flat = true [node name="ColorPickerButton" type="ColorPickerButton" parent="."] +visible = false layout_mode = 0 offset_right = 83.0 offset_bottom = 35.0 toggle_mode = false +[node name="DateButton" parent="." instance=ExtResource("3_a88g6")] +layout_mode = 0 + [connection signal="popup_closed" from="ColorPickerButton" to="." method="_on_color_picker_popup_closed"] diff --git a/Scripts/Tab.gd b/Scripts/Tab.gd index d346ffa..8b2040b 100644 --- a/Scripts/Tab.gd +++ b/Scripts/Tab.gd @@ -88,6 +88,9 @@ func _exit_tree(): loading_tween = null remove_from_group("tabs") +func _enter_tree() -> void: + $AnimationPlayer.play("appear") + func _on_button_pressed() -> void: # Check if click was on close button area var mouse_pos = get_global_mouse_position() diff --git a/Scripts/TabContainer.gd b/Scripts/TabContainer.gd index 68b1b9c..3cf8012 100644 --- a/Scripts/TabContainer.gd +++ b/Scripts/TabContainer.gd @@ -81,7 +81,6 @@ func create_tab() -> void: tab.tab_closed.connect(_tab_closed.bind(index)) h_box_container.add_child(tab) - tab.animation.play("appear") set_active_tab(index) diff --git a/Scripts/Tags/input.gd b/Scripts/Tags/input.gd index c8ab390..d168862 100644 --- a/Scripts/Tags/input.gd +++ b/Scripts/Tags/input.gd @@ -6,9 +6,6 @@ const BROWSER_TEXT: Theme = preload("res://Scenes/Styles/BrowserText.tres") var custom_hex_input: LineEdit func init(element: HTMLParser.HTMLElement) -> void: - var line_edit: LineEdit = $LineEdit - var check_box: CheckBox = $CheckBox - var radio_button: CheckBox = $RadioButton var color_picker_button: ColorPickerButton = $ColorPickerButton var picker: ColorPicker = color_picker_button.get_picker() @@ -54,55 +51,66 @@ func init(element: HTMLParser.HTMLElement) -> void: var maxlength = element.get_attribute("maxlength") var pattern = element.get_attribute("pattern") - if input_type == "checkbox": - if is_instance_valid(line_edit): line_edit.queue_free() - if is_instance_valid(radio_button): radio_button.queue_free() - if is_instance_valid(color_picker_button): color_picker_button.queue_free() - check_box.visible = true - if value and value == "true": check_box.button_pressed = true - custom_minimum_size = check_box.size + # Define which child should be active for each input type + var active_child_map = { + "checkbox": "CheckBox", + "radio": "RadioButton", + "color": "ColorPickerButton", + "password": "LineEdit", + "date": "DateButton" + } + + var active_child_name = active_child_map.get(input_type, "LineEdit") + remove_unused_children(active_child_name) + + var active_child = get_node(active_child_name) + active_child.visible = true + custom_minimum_size = active_child.size - elif input_type == "radio": - if is_instance_valid(line_edit): line_edit.queue_free() - if is_instance_valid(check_box): check_box.queue_free() - if is_instance_valid(color_picker_button): color_picker_button.queue_free() - radio_button.visible = true - radio_button.toggle_mode = true - if value and value == "true": radio_button.button_pressed = true - custom_minimum_size = radio_button.size + match input_type: + "checkbox": + var checkbox = active_child as CheckBox + if value and value == "true": + checkbox.button_pressed = true + + "radio": + var radio = active_child as CheckBox + radio.toggle_mode = true + if value and value == "true": + radio.button_pressed = true + + if group.length() > 0: + if not button_groups.has(group): + button_groups[group] = ButtonGroup.new() + radio.button_group = button_groups[group] + + "color": + var color_button = active_child as ColorPickerButton + if value and value.length() > 0: + var color = Color.from_string(value, Color.WHITE) + color_button.color = color + + "password": + var line_edit = active_child as LineEdit + line_edit.secret = true + setup_text_input(line_edit, placeholder, value, minlength, maxlength, pattern) + + "date": + var date_button = active_child as DateButton + if value and value.length() > 0: + date_button.init_with_date(value) + else: + date_button.init() + + _: # Default case (text input) + var line_edit = active_child as LineEdit + line_edit.secret = false + setup_text_input(line_edit, placeholder, value, minlength, maxlength, pattern) - if group.length() > 0: - if not button_groups.has(group): - button_groups[group] = ButtonGroup.new() - radio_button.button_group = button_groups[group] - - elif input_type == "color": - if is_instance_valid(line_edit): line_edit.queue_free() - if is_instance_valid(check_box): check_box.queue_free() - if is_instance_valid(radio_button): radio_button.queue_free() - color_picker_button.visible = true - if value and value.length() > 0: - var color = Color.from_string(value, Color.WHITE) - color_picker_button.color = color - custom_minimum_size = color_picker_button.size - - elif input_type == "password": - if is_instance_valid(check_box): check_box.queue_free() - if is_instance_valid(radio_button): radio_button.queue_free() - if is_instance_valid(color_picker_button): color_picker_button.queue_free() - line_edit.visible = true - line_edit.secret = true - custom_minimum_size = line_edit.size - setup_text_input(line_edit, placeholder, value, minlength, maxlength, pattern) - - else: - if is_instance_valid(check_box): check_box.queue_free() - if is_instance_valid(radio_button): radio_button.queue_free() - if is_instance_valid(color_picker_button): color_picker_button.queue_free() - line_edit.visible = true - line_edit.secret = false - custom_minimum_size = line_edit.size - setup_text_input(line_edit, placeholder, value, minlength, maxlength, pattern) +func remove_unused_children(keep_child_name: String) -> void: + for child in get_children(): + if child.name != keep_child_name: + child.queue_free() func setup_text_input(line_edit: LineEdit, placeholder: String, value: String, minlength: String, maxlength: String, pattern: String) -> void: if placeholder: line_edit.placeholder_text = placeholder diff --git a/Scripts/main.gd b/Scripts/main.gd index 126ff01..2fbcfa6 100644 --- a/Scripts/main.gd +++ b/Scripts/main.gd @@ -92,6 +92,8 @@ line breaks