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

Color

+

Date

+ @@ -104,7 +106,6 @@ line breaks ".to_utf8_buffer() - # Create parser and parse var parser: HTMLParser = HTMLParser.new(html_bytes) var parse_result = parser.parse() @@ -113,7 +114,6 @@ line breaks if parse_result.errors.size() > 0: print("Parse errors: " + str(parse_result.errors)) - # TODO: render the shit on the screen var tab = tab_container.tabs[tab_container.active_tab] var title = parser.get_title() diff --git a/addons/DatePicker/Calendar.cs.uid b/addons/DatePicker/Calendar.cs.uid new file mode 100644 index 0000000..25e3578 --- /dev/null +++ b/addons/DatePicker/Calendar.cs.uid @@ -0,0 +1 @@ +uid://c134lf0pxabrf diff --git a/addons/DatePicker/Calendar.gd b/addons/DatePicker/Calendar.gd new file mode 100644 index 0000000..ec8fab6 --- /dev/null +++ b/addons/DatePicker/Calendar.gd @@ -0,0 +1,95 @@ +class_name Calendar +extends PopupPanel + +@export var header: Button +@export var next: BaseButton +@export var previous: BaseButton +@export var view_parent: Control + +@export var month_view: MonthView +@export var year_view: YearView + +var margin: float = 16.0 + +var view: Node: + get: + return view_parent.get_child(0) if view_parent.get_child_count() > 0 else null + +var _date_time: Dictionary = Time.get_datetime_dict_from_system() +## The date that the calendar is currently displaying. +var date_time: Dictionary: + get: + return _date_time + set(value): + _date_time = value + date_changed.emit() + +var _selected: Dictionary = Time.get_datetime_dict_from_system() +## The date that the user has selected. +var selected: Dictionary: + get: + return _selected + set(value): + _selected = value + date_selected.emit() + +## The earliest date that can be selected. +var lower_limit: Dictionary +## The latest date that can be selected. +var upper_limit: Dictionary +## The button that displays this calendar - can be null. +var calendar_button: Button + +## The signal emitted when the view changes. +signal view_changed(view: Control) +## The signal emitted when the date changes. +signal date_changed() +## The signal emitted when the user selects a date in ANY view. +signal date_selected() +## The signal emitted when the user is finished selecting a date. +signal finished() + +func init(): + header.text = "%s %s" % [MonthView.num_to_month(date_time.month), date_time.year] + + header.pressed.connect(_on_header_pressed) + previous.pressed.connect(func(): if view: view.previous()) + next.pressed.connect(func(): if view: view.next()) + + year_view.init() + +func _enter_tree(): + get_tree().root.gui_focus_changed.connect(_on_gui_focus_changed) + +func _exit_tree(): + if get_tree() and get_tree().root: + get_tree().root.gui_focus_changed.disconnect(_on_gui_focus_changed) + +func _on_header_pressed(): + var is_default = month_view.visible + if is_default: + month_view.hide() + year_view.show() + else: + month_view.show() + year_view.hide() + +func _on_gui_focus_changed(focus: Control): + if focus and not is_ancestor_of(focus) and not focus is DateButton: + visible = false + +func set_view(new_view: Node): + if view: + view.queue_free() + new_view.calendar = self + view_parent.add_child(new_view) + view_changed.emit(new_view) + +func _gui_input(event: InputEvent): + if event is InputEventMouseButton: + var mb = event as InputEventMouseButton + if mb.pressed: + if mb.button_index == MOUSE_BUTTON_WHEEL_DOWN and view: + view.next() + elif mb.button_index == MOUSE_BUTTON_WHEEL_UP and view: + view.previous() diff --git a/addons/DatePicker/Calendar.gd.uid b/addons/DatePicker/Calendar.gd.uid new file mode 100644 index 0000000..356ae5b --- /dev/null +++ b/addons/DatePicker/Calendar.gd.uid @@ -0,0 +1 @@ +uid://bh27iqijrabku diff --git a/addons/DatePicker/Calendar.tscn b/addons/DatePicker/Calendar.tscn new file mode 100644 index 0000000..4c10253 --- /dev/null +++ b/addons/DatePicker/Calendar.tscn @@ -0,0 +1,88 @@ +[gd_scene load_steps=14 format=3 uid="uid://dgn7djiy1xhlu"] + +[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="1_1ddb1"] +[ext_resource type="Script" uid="uid://bh27iqijrabku" path="res://addons/DatePicker/Calendar.gd" id="1_heut6"] +[ext_resource type="Texture2D" uid="uid://d1jslyy8aajcn" path="res://Assets/Icons/arrow-up.svg" id="3_1ddb1"] +[ext_resource type="Texture2D" uid="uid://df1m4j7uxi63v" path="res://Assets/Icons/chevron-down.svg" id="3_rjgii"] +[ext_resource type="Texture2D" uid="uid://c1bqnfwurnnrc" path="res://Assets/Icons/arrow-down.svg" id="4_rjgii"] +[ext_resource type="PackedScene" uid="uid://n5ra5w5aupx7" path="res://addons/DatePicker/YearView.tscn" id="5_qmkbm"] +[ext_resource type="PackedScene" uid="uid://hiv2ckjbs43p" path="res://addons/DatePicker/MonthView.tscn" id="6_rjgii"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_w2pln"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_s0l8g"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mavvk"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_j0d5i"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1e668"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5m4sc"] + +[node name="Calendar" type="PopupPanel" node_paths=PackedStringArray("header", "next", "previous", "view_parent", "month_view", "year_view")] +size = Vector2i(263, 308) +visible = true +theme = ExtResource("1_1ddb1") +script = ExtResource("1_heut6") +header = NodePath("VboxContainer/HeaderContainer/HeaderButton") +next = NodePath("VboxContainer/HeaderContainer/Next") +previous = NodePath("VboxContainer/HeaderContainer/Previous") +view_parent = NodePath("VboxContainer/View") +month_view = NodePath("VboxContainer/View/MonthView") +year_view = NodePath("VboxContainer/View/YearView") + +[node name="VboxContainer" type="VBoxContainer" parent="."] +offset_left = 15.0 +offset_top = 15.0 +offset_right = 248.0 +offset_bottom = 293.0 +theme_override_constants/separation = 12 + +[node name="HeaderContainer" type="HBoxContainer" parent="VboxContainer"] +layout_mode = 2 + +[node name="HeaderButton" type="Button" parent="VboxContainer/HeaderContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = SubResource("StyleBoxEmpty_w2pln") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_s0l8g") +text = "July 1980" +icon = ExtResource("3_rjgii") +flat = true +alignment = 0 +icon_alignment = 2 + +[node name="Spacer" type="Control" parent="VboxContainer/HeaderContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Previous" type="Button" parent="VboxContainer/HeaderContainer"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = SubResource("StyleBoxEmpty_mavvk") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_j0d5i") +icon = ExtResource("3_1ddb1") +flat = true + +[node name="Next" type="Button" parent="VboxContainer/HeaderContainer"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = SubResource("StyleBoxEmpty_1e668") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_5m4sc") +icon = ExtResource("4_rjgii") +flat = true + +[node name="View" type="Control" parent="VboxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="MonthView" parent="VboxContainer/View" node_paths=PackedStringArray("calendar") instance=ExtResource("6_rjgii")] +layout_mode = 0 +calendar = NodePath("../../..") + +[node name="YearView" parent="VboxContainer/View" node_paths=PackedStringArray("calendar") instance=ExtResource("5_qmkbm")] +visible = false +layout_mode = 0 +calendar = NodePath("../../..") diff --git a/addons/DatePicker/CalendarGroup.tres b/addons/DatePicker/CalendarGroup.tres new file mode 100644 index 0000000..5bf5c20 --- /dev/null +++ b/addons/DatePicker/CalendarGroup.tres @@ -0,0 +1,4 @@ +[gd_resource type="ButtonGroup" format=3 uid="uid://bqxxq56yxl44h"] + +[resource] +allow_unpress = true diff --git a/addons/DatePicker/DateButton.cs.uid b/addons/DatePicker/DateButton.cs.uid new file mode 100644 index 0000000..a232e9c --- /dev/null +++ b/addons/DatePicker/DateButton.cs.uid @@ -0,0 +1 @@ +uid://ds2esqjkjc4h0 diff --git a/addons/DatePicker/DateButton.gd b/addons/DatePicker/DateButton.gd new file mode 100644 index 0000000..18e3ab6 --- /dev/null +++ b/addons/DatePicker/DateButton.gd @@ -0,0 +1,77 @@ +class_name DateButton +extends Button + +var calendar: Calendar +var calendar_control: Control + +const CALENDAR = preload("res://addons/DatePicker/Calendar.tscn") + +func init(): + text = "mm/dd/yyyy" + _init_calendar() + +func _init_calendar(): + calendar = CALENDAR.instantiate() + add_child(calendar) + calendar.hide() + calendar.init() + + pressed.connect(_on_button_pressed) + calendar.popup_hide.connect(func(): button_pressed = false) + calendar.finished.connect(_on_date_selected) + tree_exited.connect(func(): if is_instance_valid(calendar): calendar.queue_free()) + +func init_with_date(date_string: String) -> void: + _init_calendar() + + var parsed_date = parse_date_string(date_string) + if parsed_date: + calendar.date_time = parsed_date + calendar.selected = parsed_date.duplicate() + update_button_text() + else: + text = "mm/dd/yyyy" # Set default text if parsing fails + +func parse_date_string(date_string: String) -> Dictionary: + # Parse ISO date format (YYYY-MM-DD) + var parts = date_string.split("-") + if parts.size() != 3: + return {} + + var year = parts[0].to_int() + var month = parts[1].to_int() + var day = parts[2].to_int() + + # Validate date components + if year < 1 or month < 1 or month > 12 or day < 1 or day > 31: + return {} + + return { + "year": year, + "month": month, + "day": day + } + +func update_button_text() -> void: + var date = calendar.selected + text = "%d/%d/%d" % [date.month, date.day, date.year] + +func _on_date_selected(): + var date = calendar.selected + text = "%02d/%02d/%04d" % [date.month, date.day, date.year] + +func _on_button_pressed(): + if calendar.is_visible(): + calendar.hide() + return + + var viewport_height = get_viewport_rect().size.y + var show_above = false + if get_global_position().y + size.y + calendar.size.y > viewport_height and get_global_position().y * 2 + size.y > viewport_height: + show_above = true + + var h_offset = (size.x - calendar.size.x) / 2 + var v_offset = -calendar.size.y if show_above else size.y + + calendar.position = get_screen_position() + Vector2(h_offset, v_offset) + calendar.popup() diff --git a/addons/DatePicker/DateButton.gd.uid b/addons/DatePicker/DateButton.gd.uid new file mode 100644 index 0000000..fa84441 --- /dev/null +++ b/addons/DatePicker/DateButton.gd.uid @@ -0,0 +1 @@ +uid://do548vwv7m0rq diff --git a/addons/DatePicker/DateButton.tscn b/addons/DatePicker/DateButton.tscn new file mode 100644 index 0000000..c955e0f --- /dev/null +++ b/addons/DatePicker/DateButton.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=6 format=3 uid="uid://b6c1twaog7hui"] + +[ext_resource type="Texture2D" uid="uid://mol3s1ujp0k2" path="res://Assets/Icons/calendar_16x16.svg" id="1_ef33s"] +[ext_resource type="Script" uid="uid://do548vwv7m0rq" path="res://addons/DatePicker/DateButton.gd" id="1_sskq5"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ef33s"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ufs0o"] +content_margin_left = 4.0 +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 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pnupr"] +content_margin_left = 4.0 +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 +corner_radius_bottom_left = 4 + +[node name="DateButton" type="Button"] +offset_right = 115.0 +offset_bottom = 34.0 +theme_override_styles/focus = SubResource("StyleBoxEmpty_ef33s") +theme_override_styles/hover = SubResource("StyleBoxFlat_ufs0o") +theme_override_styles/normal = SubResource("StyleBoxFlat_pnupr") +action_mode = 0 +text = "3/13/2025" +icon = ExtResource("1_ef33s") +script = ExtResource("1_sskq5") diff --git a/addons/DatePicker/ICalendarView.cs.uid b/addons/DatePicker/ICalendarView.cs.uid new file mode 100644 index 0000000..9f168df --- /dev/null +++ b/addons/DatePicker/ICalendarView.cs.uid @@ -0,0 +1 @@ +uid://crwtb840n4orl diff --git a/addons/DatePicker/ICalendarView.gd b/addons/DatePicker/ICalendarView.gd new file mode 100644 index 0000000..eff273b --- /dev/null +++ b/addons/DatePicker/ICalendarView.gd @@ -0,0 +1,19 @@ +class_name ICalendarView +extends RefCounted + +## A view that dictates how a calendar should be displayed (i.e. MonthView or YearView). + +## The calendar that this view is a part of. +var calendar: Calendar + +## When the user clicks the previous button (i.e. go to the previous year). +func previous(): + pass + +## When the user clicks the next button (i.e. go to the next year). +func next(): + pass + +## Update the view to reflect the selected date. +func refresh(): + pass diff --git a/addons/DatePicker/ICalendarView.gd.uid b/addons/DatePicker/ICalendarView.gd.uid new file mode 100644 index 0000000..df20de9 --- /dev/null +++ b/addons/DatePicker/ICalendarView.gd.uid @@ -0,0 +1 @@ +uid://lxw1ab33fva2 diff --git a/addons/DatePicker/LICENSE.md b/addons/DatePicker/LICENSE.md new file mode 100644 index 0000000..1d5983a --- /dev/null +++ b/addons/DatePicker/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Christopher Charbonneau + +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/addons/DatePicker/MonthView.cs.uid b/addons/DatePicker/MonthView.cs.uid new file mode 100644 index 0000000..6fdcad2 --- /dev/null +++ b/addons/DatePicker/MonthView.cs.uid @@ -0,0 +1 @@ +uid://bc88158gaxg4f diff --git a/addons/DatePicker/MonthView.gd b/addons/DatePicker/MonthView.gd new file mode 100644 index 0000000..ba99a10 --- /dev/null +++ b/addons/DatePicker/MonthView.gd @@ -0,0 +1,146 @@ +class_name MonthView +extends VBoxContainer + +@export var calendar: Calendar + +var buttons: GridContainer +var dates: Array = [] +var _updating: bool = false + +func _ready(): + buttons = $Days + for i in range(buttons.get_child_count()): + buttons.get_child(i).toggled.connect(_on_day_toggled.bind(i)) + refresh() + +func _on_day_toggled(toggled: bool, button_index: int): + if _updating or button_index >= dates.size() or not dates[button_index]: + return + + var button = buttons.get_child(button_index) + if not toggled: + button.set_pressed_no_signal(true) + return + + for i in range(buttons.get_child_count()): + if i != button_index: + buttons.get_child(i).set_pressed_no_signal(false) + + calendar.selected = dates[button_index] + calendar.date_time = dates[button_index].duplicate() + calendar.hide() + calendar.finished.emit() + +func previous(): + var new_date = calendar.date_time.duplicate() + new_date.month -= 1 + if new_date.month < 1: + new_date.month = 12 + new_date.year -= 1 + calendar.date_time = new_date + refresh() + +func next(): + var new_date = calendar.date_time.duplicate() + new_date.month += 1 + if new_date.month > 12: + new_date.month = 1 + new_date.year += 1 + calendar.date_time = new_date + refresh() + +func refresh(): + _updating = true + calendar.header.text = "%s %s" % [num_to_month(calendar.date_time.month), calendar.date_time.year] + dates.clear() + dates.resize(buttons.get_child_count()) + + for i in range(buttons.get_child_count()): + var button = buttons.get_child(i) + button.text = "" + button.set_pressed_no_signal(false) + dates[i] = null + + var first_day = calendar.date_time.duplicate() + first_day.day = 1 + var start_day = Time.get_datetime_dict_from_unix_time( + Time.get_unix_time_from_datetime_dict(first_day) + ).weekday % 7 + + var days_in_month = get_days_in_month(calendar.date_time.year, calendar.date_time.month) + + var prev_month_num = calendar.date_time.month - 1 + var prev_year = calendar.date_time.year + if prev_month_num < 1: + prev_month_num = 12 + prev_year -= 1 + var prev_days = get_days_in_month(prev_year, prev_month_num) + + var gray_color = Color(0.51, 0.54, 0.596) + var dark_color = Color(0.133, 0.137, 0.137) + + # Fill previous month days + for i in range(start_day): + var day = prev_days - start_day + i + 1 + var button = buttons.get_child(i) + dates[i] = {"year": prev_year, "month": prev_month_num, "day": day} + button.text = str(day) + button.add_theme_color_override("font_color", gray_color) + button.add_theme_color_override("font_hover_color", gray_color) + button.set_pressed_no_signal( + day == calendar.selected.day and + calendar.selected.year == prev_year and + calendar.selected.month == prev_month_num + ) + + # Fill current month days + for i in range(days_in_month): + var button = buttons.get_child(i + start_day) + dates[i + start_day] = {"year": calendar.date_time.year, "month": calendar.date_time.month, "day": i + 1} + button.text = str(i + 1) + button.add_theme_color_override("font_color", dark_color) + button.add_theme_color_override("font_hover_color", dark_color) + button.set_pressed_no_signal( + i + 1 == calendar.selected.day and + calendar.selected.year == calendar.date_time.year and + calendar.selected.month == calendar.date_time.month + ) + + var next_month_num = calendar.date_time.month + 1 + var next_year = calendar.date_time.year + if next_month_num > 12: + next_month_num = 1 + next_year += 1 + + # Fill next month days + for i in range(days_in_month + start_day, buttons.get_child_count()): + var day = i + 1 - days_in_month - start_day + var button = buttons.get_child(i) + dates[i] = {"year": next_year, "month": next_month_num, "day": day} + button.text = str(day) + button.add_theme_color_override("font_color", gray_color) + button.add_theme_color_override("font_hover_color", gray_color) + button.set_pressed_no_signal( + day == calendar.selected.day and + calendar.selected.year == next_year and + calendar.selected.month == next_month_num + ) + + _updating = false + +func get_days_in_month(year: int, month: int) -> int: + var thirty_one = [1, 3, 5, 7, 8, 10, 12] + var thirty = [4, 6, 9, 11] + + if month in thirty_one: + return 31 + elif month in thirty: + return 30 + elif month == 2: + return 29 if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) else 28 + else: + return 0 + +static func num_to_month(month: int) -> String: + var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + return months[month - 1] diff --git a/addons/DatePicker/MonthView.gd.uid b/addons/DatePicker/MonthView.gd.uid new file mode 100644 index 0000000..f19e6f5 --- /dev/null +++ b/addons/DatePicker/MonthView.gd.uid @@ -0,0 +1 @@ +uid://drxqmka0n1ntw diff --git a/addons/DatePicker/MonthView.tscn b/addons/DatePicker/MonthView.tscn new file mode 100644 index 0000000..bcd3375 --- /dev/null +++ b/addons/DatePicker/MonthView.tscn @@ -0,0 +1,642 @@ +[gd_scene load_steps=7 format=3 uid="uid://hiv2ckjbs43p"] + +[ext_resource type="Script" uid="uid://drxqmka0n1ntw" path="res://addons/DatePicker/MonthView.gd" id="1_iouni"] +[ext_resource type="ButtonGroup" uid="uid://bqxxq56yxl44h" path="res://addons/DatePicker/CalendarGroup.tres" id="2_nfkp2"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5l1mp"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5l1mp"] +bg_color = Color(0.613675, 0.748845, 0.957838, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pressed"] +bg_color = Color(0.247059, 0.466667, 0.807843, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ochoi"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[node name="MonthView" type="VBoxContainer"] +offset_right = 234.0 +offset_bottom = 240.0 +script = ExtResource("1_iouni") + +[node name="DayOfWeek" type="GridContainer" parent="."] +layout_mode = 2 +columns = 7 + +[node name="Sunday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "Su" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Monday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "Mo" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Tuesday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "Tu" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Wednesday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "We" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Thursday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "Th" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Friday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "Fr" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Saturday" type="Label" parent="DayOfWeek"] +custom_minimum_size = Vector2(0, 36) +layout_mode = 2 +size_flags_horizontal = 3 +text = "Sa" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Days" type="GridContainer" parent="."] +layout_mode = 2 +columns = 7 + +[node name="Button0" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button1" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button2" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button3" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button4" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button5" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button6" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button7" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button8" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button9" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button10" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button11" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button12" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button13" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button14" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button15" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button16" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button17" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button18" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button19" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button20" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button21" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button22" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button23" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button24" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button25" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button26" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button27" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button28" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button29" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button30" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button31" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button32" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button33" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button34" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button35" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button36" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button37" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button38" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button39" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button40" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") + +[node name="Button41" type="Button" parent="Days"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_5l1mp") +theme_override_styles/hover = SubResource("StyleBoxFlat_5l1mp") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_ochoi") +toggle_mode = true +button_group = ExtResource("2_nfkp2") diff --git a/addons/DatePicker/README.md b/addons/DatePicker/README.md new file mode 100644 index 0000000..de43fb0 --- /dev/null +++ b/addons/DatePicker/README.md @@ -0,0 +1,4 @@ +This is a heavily modified version of **DatePicker** by [chrisizeful](https://github.com/chrisizeful/) + +- GitHub: https://github.com/chrisizeful/DatePicker +- Godot Asset: https://godotengine.org/asset-library/asset/3835 diff --git a/addons/DatePicker/YearView.cs.uid b/addons/DatePicker/YearView.cs.uid new file mode 100644 index 0000000..b5057b9 --- /dev/null +++ b/addons/DatePicker/YearView.cs.uid @@ -0,0 +1 @@ +uid://b5qslpdcfyikx diff --git a/addons/DatePicker/YearView.gd b/addons/DatePicker/YearView.gd new file mode 100644 index 0000000..647aa59 --- /dev/null +++ b/addons/DatePicker/YearView.gd @@ -0,0 +1,92 @@ +class_name YearView +extends HBoxContainer + +@export var calendar: Calendar + +func init(): + var button_example: Button = %Button + var year_container: VBoxContainer = %YearContainer + + var buttons_data = [] + for i in range(200): + var year = i + 1900 + buttons_data.append(year) + + for year in buttons_data: + var new_btn: Button = button_example.duplicate() + new_btn.name = "Year" + str(year) + new_btn.pressed.connect(_on_year_selected.bind(year)) + new_btn.text = str(year) + year_container.add_child(new_btn) + + button_example.queue_free() + + year_container.resized.connect(func(): + scroll_to_current_year() + refresh() + ) + +func scroll_to_current_year(): + var year_container: VBoxContainer = %YearContainer + var scroll_container: ScrollContainer = $SmoothScrollContainer + + var current_year = calendar.date_time.year + var target_button: Button = year_container.get_node_or_null("Year" + str(current_year)) + + if not target_button: + return + + while target_button.position.y <= 0: + await get_tree().process_frame + + for child in year_container.get_children(): + if child is Button: + child.set_pressed_no_signal(child == target_button) + + var button_position = target_button.position.y + var container_height = scroll_container.size.y + var button_height = target_button.size.y + var scroll_position = button_position - (container_height / 2) + (button_height / 2) + + scroll_container.scroll_vertical = max(0, scroll_position) + +func _on_year_selected(year: int): + var new_date = calendar.date_time.duplicate() + new_date.year = year + calendar.selected = new_date + calendar.date_time = new_date + + if calendar.has_signal("date_changed"): + calendar.date_changed.emit(new_date) + + calendar.year_view.hide() + calendar.month_view.show() + calendar.month_view.refresh() + + calendar.finished.emit() + +func previous(): + var new_date = calendar.date_time.duplicate() + new_date.year -= 1 + calendar.date_time = new_date + refresh() + +func next(): + var new_date = calendar.date_time.duplicate() + new_date.year += 1 + calendar.date_time = new_date + refresh() + +func refresh(): + var current_time = Time.get_unix_time_from_system() + + calendar.header.text = "%s %s" % [MonthView.num_to_month(calendar.date_time.month), calendar.date_time.year] + + for child in %YearContainer.get_children(): + if child is Button: + child.set_pressed_no_signal(false) + + var target_button_name = "Year" + str(calendar.date_time.year) + var target_button = %YearContainer.get_node_or_null(target_button_name) + if target_button: + target_button.set_pressed_no_signal(true) diff --git a/addons/DatePicker/YearView.gd.uid b/addons/DatePicker/YearView.gd.uid new file mode 100644 index 0000000..2dd9a36 --- /dev/null +++ b/addons/DatePicker/YearView.gd.uid @@ -0,0 +1 @@ +uid://c8nfr825g08sy diff --git a/addons/DatePicker/YearView.tscn b/addons/DatePicker/YearView.tscn new file mode 100644 index 0000000..35810a1 --- /dev/null +++ b/addons/DatePicker/YearView.tscn @@ -0,0 +1,82 @@ +[gd_scene load_steps=11 format=3 uid="uid://n5ra5w5aupx7"] + +[ext_resource type="Script" uid="uid://c8nfr825g08sy" path="res://addons/DatePicker/YearView.gd" id="1_hiiks"] +[ext_resource type="Script" uid="uid://bgqglerkcylxx" path="res://addons/SmoothScroll/SmoothScrollContainer.gd" id="1_sw7yn"] +[ext_resource type="ButtonGroup" uid="uid://bqxxq56yxl44h" path="res://addons/DatePicker/CalendarGroup.tres" id="2_2qddy"] +[ext_resource type="Script" uid="uid://b7h0k2h2qwlqv" path="res://addons/SmoothScroll/scroll_damper/expo_scroll_damper.gd" id="2_iofl3"] + +[sub_resource type="Resource" id="Resource_wpifq"] +script = ExtResource("2_iofl3") +friction = 4.0 +minimum_velocity = 0.4 +rebound_strength = 7.0 + +[sub_resource type="Resource" id="Resource_kdl71"] +script = ExtResource("2_iofl3") +friction = 4.0 +minimum_velocity = 0.4 +rebound_strength = 7.0 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_iofl3"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kdl71"] +content_margin_left = 8.0 +bg_color = Color(0.168627, 0.168627, 0.168627, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pressed"] +content_margin_left = 8.0 +bg_color = Color(0.247059, 0.466667, 0.807843, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wpifq"] +content_margin_left = 8.0 +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 +corner_radius_bottom_left = 4 + +[node name="YearView" type="HBoxContainer"] +script = ExtResource("1_hiiks") + +[node name="SmoothScrollContainer" type="ScrollContainer" parent="."] +custom_minimum_size = Vector2(234, 240) +layout_mode = 2 +script = ExtResource("1_sw7yn") +wheel_scroll_damper = SubResource("Resource_wpifq") +dragging_scroll_damper = SubResource("Resource_kdl71") +drag_with_mouse = false +allow_overdragging = false +metadata/_custom_type_script = "uid://bgqglerkcylxx" + +[node name="YearContainer" type="VBoxContainer" parent="SmoothScrollContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(234, 240) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 + +[node name="Button" type="Button" parent="SmoothScrollContainer/YearContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(34, 5) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 1 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_colors/font_pressed_color = Color(1, 1, 1, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_iofl3") +theme_override_styles/hover = SubResource("StyleBoxFlat_kdl71") +theme_override_styles/pressed = SubResource("StyleBoxFlat_pressed") +theme_override_styles/normal = SubResource("StyleBoxFlat_wpifq") +toggle_mode = true +button_group = ExtResource("2_2qddy") +text = "2025" +alignment = 0 diff --git a/addons/DatePicker/plugin.cfg b/addons/DatePicker/plugin.cfg new file mode 100644 index 0000000..a6f3e00 --- /dev/null +++ b/addons/DatePicker/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="DatePicker" +description="A themed Godot calendar addon inspiried by the Windows 11 calendar." +author="Chris Charbonneau" +version="1.0.0" +script="" diff --git a/project.godot b/project.godot index 9315199..c145239 100644 --- a/project.godot +++ b/project.godot @@ -28,7 +28,7 @@ window/stretch/aspect="ignore" [editor_plugins] -enabled=PackedStringArray("res://addons/SmoothScroll/plugin.cfg") +enabled=PackedStringArray("res://addons/DatePicker/plugin.cfg", "res://addons/SmoothScroll/plugin.cfg") [file_customization]