commit 6c119e9c8338da336cfb080ab26fe35abe648dc2 Author: Dmitriy Pleshevskiy Date: Mon Feb 12 19:52:47 2024 +0300 initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4709183 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Godot 4+ specific ignores +.godot/ diff --git a/assets/back.png b/assets/back.png new file mode 100644 index 0000000..aafa5a6 Binary files /dev/null and b/assets/back.png differ diff --git a/assets/back.png.import b/assets/back.png.import new file mode 100644 index 0000000..5c52b2e --- /dev/null +++ b/assets/back.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://byohq4ewrecgx" +path="res://.godot/imported/back.png-515119acda62477b1fef7f62900af1e0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/back.png" +dest_files=["res://.godot/imported/back.png-515119acda62477b1fef7f62900af1e0.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 diff --git a/assets/edit.png b/assets/edit.png new file mode 100644 index 0000000..93bac4c Binary files /dev/null and b/assets/edit.png differ diff --git a/assets/edit.png.import b/assets/edit.png.import new file mode 100644 index 0000000..17a77d8 --- /dev/null +++ b/assets/edit.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bel5s26rbieoi" +path="res://.godot/imported/edit.png-5f81715ab2833e480257b9a0702c0ed1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/edit.png" +dest_files=["res://.godot/imported/edit.png-5f81715ab2833e480257b9a0702c0ed1.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 diff --git a/assets/next.png b/assets/next.png new file mode 100644 index 0000000..4972fc3 Binary files /dev/null and b/assets/next.png differ diff --git a/assets/next.png.import b/assets/next.png.import new file mode 100644 index 0000000..6c885f2 --- /dev/null +++ b/assets/next.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bhqx6frqdw5bc" +path="res://.godot/imported/next.png-4a14bb3d3b235bb0505e1fa2dffc1d35.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/next.png" +dest_files=["res://.godot/imported/next.png-4a14bb3d3b235bb0505e1fa2dffc1d35.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 diff --git a/components/note_item/note_item.gd b/components/note_item/note_item.gd new file mode 100644 index 0000000..ee6608a --- /dev/null +++ b/components/note_item/note_item.gd @@ -0,0 +1,21 @@ +class_name NoteItem +extends PanelContainer + +signal pressed(note: Note) + +@export var note: Note : + set(new_note): + if note != new_note: + note = new_note + if button: + button.text = note.title + +@onready var button: Button = %Button + + +func _ready(): + button.text = note.title + + +func _on_button_pressed(): + pressed.emit(note) diff --git a/components/note_item/note_item.tscn b/components/note_item/note_item.tscn new file mode 100644 index 0000000..fdfc93e --- /dev/null +++ b/components/note_item/note_item.tscn @@ -0,0 +1,36 @@ +[gd_scene load_steps=4 format=3 uid="uid://c2mebh70fr4p3"] + +[ext_resource type="Script" path="res://components/note_item/note_item.gd" id="1_hue52"] +[ext_resource type="Texture2D" uid="uid://bhqx6frqdw5bc" path="res://assets/next.png" id="1_xruk3"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q7ab1"] +bg_color = Color(1, 1, 1, 0.784314) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[node name="NoteItem" type="PanelContainer"] +theme_override_styles/panel = SubResource("StyleBoxFlat_q7ab1") +script = ExtResource("1_hue52") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 9 +theme_override_constants/margin_top = 6 +theme_override_constants/margin_right = 9 +theme_override_constants/margin_bottom = 6 + +[node name="Button" type="Button" parent="MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Hello World +" +icon = ExtResource("1_xruk3") +flat = true +alignment = 0 +clip_text = true +icon_alignment = 2 +expand_icon = true + +[connection signal="pressed" from="MarginContainer/Button" to="." method="_on_button_pressed"] diff --git a/data/note.gd b/data/note.gd new file mode 100644 index 0000000..389db2b --- /dev/null +++ b/data/note.gd @@ -0,0 +1,15 @@ +class_name Note +extends Resource + +const uuid = preload("res://utils/uuid.gd") + +@export var parent_id: String +@export var id: String +@export var title: String + + +func _init(note_title: String, note_parent_id: String = ""): + id = uuid.v4() + title = note_title + parent_id = note_parent_id + diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..b370ceb --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..0af1a6c --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfx1f18ued3w" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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/main.gd b/main.gd new file mode 100644 index 0000000..3412f36 --- /dev/null +++ b/main.gd @@ -0,0 +1,65 @@ +class_name Main +extends Control + +var notes: Array[Note] = [] +var previous_notes: Array[Note] = [] +var current_note: Note + +@onready var back_button: Button = %BackButton +@onready var page_title: Label = %PageTitle +@onready var notes_container: VBoxContainer = %NotesContainer +const NOTE_ITEM = preload("res://components/note_item/note_item.tscn") + + +func display_note() -> void: + back_button.disabled = previous_notes.size() == 0 + + page_title.text = current_note.title if current_note else "Главная" + + for child in notes_container.get_children(): + notes_container.remove_child(child) + + var display_notes = notes.filter(func (note): return note.parent_id == (current_note.id if current_note else "")) + + for note in display_notes: + var note_item = NOTE_ITEM.instantiate() + note_item.note = note + notes_container.add_child(note_item) + note_item.pressed.connect(_on_note_item_pressed) + + +func _ready(): + var note_1 = Note.new("Тест 1") + var note_2 = Note.new("Тест 2") + var note_3 = Note.new("Тест 3") + var note_1_1 = Note.new("Тест 1 1", note_1.id) + var note_1_2 = Note.new("Тест 1 2", note_1.id) + var note_1_2_1 = Note.new("Тест 1 2 1", note_1_2.id) + var note_2_1 = Note.new("Тест 2 1", note_2.id) + + notes = [ + note_1, + note_2, + note_3, + note_1_1, + note_1_2, + note_1_2_1, + note_2_1, + ] + + display_note() + + +func _on_back_button_pressed(): + current_note = previous_notes.pop_back() + display_note() + + +func _on_note_item_pressed(note: Note) -> void: + previous_notes.append(current_note) + current_note = note + display_note() + + +func _on_save_manager_load_notes(loaded_notes): + notes = loaded_notes diff --git a/main.tscn b/main.tscn new file mode 100644 index 0000000..773ccfb --- /dev/null +++ b/main.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=5 format=3 uid="uid://3snttmw814qx"] + +[ext_resource type="Script" path="res://main.gd" id="1_nrdxj"] +[ext_resource type="Texture2D" uid="uid://byohq4ewrecgx" path="res://assets/back.png" id="1_rigm4"] +[ext_resource type="Texture2D" uid="uid://bel5s26rbieoi" path="res://assets/edit.png" id="2_grsek"] +[ext_resource type="Script" path="res://save_manager/save_manager.gd" id="5_o2kvf"] + +[node name="Main" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_nrdxj") + +[node name="PanelContainer" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] +layout_mode = 2 + +[node name="PanelContainer" type="PanelContainer" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 16 +theme_override_constants/margin_bottom = 16 + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer/MarginContainer"] +layout_mode = 2 + +[node name="BackButton" type="Button" parent="PanelContainer/VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(64, 64) +layout_mode = 2 +icon = ExtResource("1_rigm4") +flat = true +expand_icon = true + +[node name="PageTitle" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Главная" + +[node name="EditButton" type="Button" parent="PanelContainer/VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"] +custom_minimum_size = Vector2(64, 64) +layout_mode = 2 +icon = ExtResource("2_grsek") +flat = true +expand_icon = true + +[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 16 +theme_override_constants/margin_bottom = 16 + +[node name="NotesContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="Utils" type="Node" parent="."] + +[node name="SaveManager" type="Node" parent="Utils"] +script = ExtResource("5_o2kvf") + +[connection signal="pressed" from="PanelContainer/VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/BackButton" to="." method="_on_back_button_pressed"] +[connection signal="load_notes" from="Utils/SaveManager" to="." method="_on_save_manager_load_notes"] diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..3abe619 --- /dev/null +++ b/project.godot @@ -0,0 +1,33 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Boronotes" +config/version="0.0.1" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.2", "Mobile") +config/icon="res://icon.svg" + +[display] + +window/size/viewport_width=900 +window/size/viewport_height=1600 +window/size/resizable=false +window/stretch/mode="canvas_items" +window/handheld/orientation=1 + +[gui] + +theme/custom="res://styles/app_theme.tres" + +[rendering] + +renderer/rendering_method="mobile" diff --git a/save_manager/save_manager.gd b/save_manager/save_manager.gd new file mode 100644 index 0000000..7154747 --- /dev/null +++ b/save_manager/save_manager.gd @@ -0,0 +1,19 @@ +class_name SaveManager +extends Node + +signal load_notes(notes: Array[Note]) + +var file_name: String = "user://save_notes.tres" + + +func save_data(notes: Array[Note]) -> void: + var saved_data = SavedData.new() + saved_data.notes = notes + ResourceSaver.save(saved_data, file_name) + + +func load_data() -> void: + if ResourceLoader.exists(file_name): + var saved_data = ResourceLoader.load(file_name) as SavedData + load_notes.emit(saved_data.notes) + diff --git a/save_manager/saved_data.gd b/save_manager/saved_data.gd new file mode 100644 index 0000000..e8d6d16 --- /dev/null +++ b/save_manager/saved_data.gd @@ -0,0 +1,4 @@ +class_name SavedData +extends Resource + +@export var notes: Array[Note] = [] diff --git a/styles/app_theme.tres b/styles/app_theme.tres new file mode 100644 index 0000000..32b83d7 --- /dev/null +++ b/styles/app_theme.tres @@ -0,0 +1,16 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://dhg5k56ynipbp"] + +[ext_resource type="StyleBox" uid="uid://bfvw2cfuiduki" path="res://styles/white_panel_flat.tres" id="1_rasl3"] + +[resource] +Button/colors/font_color = Color(0, 0, 0, 1) +Button/colors/font_focus_color = Color(0, 0, 0, 1) +Button/colors/font_hover_color = Color(0, 0, 0, 1) +Button/colors/font_hover_pressed_color = Color(0, 0, 0, 1) +Button/colors/font_outline_color = Color(0, 0, 0, 1) +Button/colors/font_pressed_color = Color(0, 0, 0, 1) +Button/font_sizes/font_size = 18 +Button/styles/normal = null +Label/colors/font_color = Color(0, 0, 0, 1) +Label/font_sizes/font_size = 30 +PanelContainer/styles/panel = ExtResource("1_rasl3") diff --git a/styles/white_panel_flat.tres b/styles/white_panel_flat.tres new file mode 100644 index 0000000..2a39ec2 --- /dev/null +++ b/styles/white_panel_flat.tres @@ -0,0 +1,4 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://bfvw2cfuiduki"] + +[resource] +bg_color = Color(1, 1, 1, 0.784314) diff --git a/utils/uuid.gd b/utils/uuid.gd new file mode 100644 index 0000000..80840b6 --- /dev/null +++ b/utils/uuid.gd @@ -0,0 +1,113 @@ +# Note: The code might not be as pretty it could be, since it's written +# in a way that maximizes performance. Methods are inlined and loops are avoided. +extends Node + +const BYTE_MASK: int = 0b11111111 + +static func uuidbin(): + # 16 random bytes with the bytes on index 6 and 8 modified + return [ + randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, + randi() & BYTE_MASK, randi() & BYTE_MASK, ((randi() & BYTE_MASK) & 0x0f) | 0x40, randi() & BYTE_MASK, + ((randi() & BYTE_MASK) & 0x3f) | 0x80, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, + randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, + ] + +static func uuidbinrng(rng: RandomNumberGenerator): + return [ + rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, + rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, ((rng.randi() & BYTE_MASK) & 0x0f) | 0x40, rng.randi() & BYTE_MASK, + ((rng.randi() & BYTE_MASK) & 0x3f) | 0x80, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, + rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, + ] + +static func v4(): + # 16 random bytes with the bytes on index 6 and 8 modified + var b = uuidbin() + + return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [ + # low + b[0], b[1], b[2], b[3], + + # mid + b[4], b[5], + + # hi + b[6], b[7], + + # clock + b[8], b[9], + + # clock + b[10], b[11], b[12], b[13], b[14], b[15] + ] + +static func v4_rng(rng: RandomNumberGenerator): + # 16 random bytes with the bytes on index 6 and 8 modified + var b = uuidbinrng(rng) + + return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [ + # low + b[0], b[1], b[2], b[3], + + # mid + b[4], b[5], + + # hi + b[6], b[7], + + # clock + b[8], b[9], + + # clock + b[10], b[11], b[12], b[13], b[14], b[15] + ] + +var _uuid: Array + +func _init(rng := RandomNumberGenerator.new()) -> void: + _uuid = uuidbinrng(rng) + +func as_array() -> Array: + return _uuid.duplicate() + +func as_dict(big_endian := true) -> Dictionary: + if big_endian: + return { + "low" : (_uuid[0] << 24) + (_uuid[1] << 16) + (_uuid[2] << 8 ) + _uuid[3], + "mid" : (_uuid[4] << 8 ) + _uuid[5], + "hi" : (_uuid[6] << 8 ) + _uuid[7], + "clock": (_uuid[8] << 8 ) + _uuid[9], + "node" : (_uuid[10] << 40) + (_uuid[11] << 32) + (_uuid[12] << 24) + (_uuid[13] << 16) + (_uuid[14] << 8 ) + _uuid[15] + } + else: + return { + "low" : _uuid[0] + (_uuid[1] << 8 ) + (_uuid[2] << 16) + (_uuid[3] << 24), + "mid" : _uuid[4] + (_uuid[5] << 8 ), + "hi" : _uuid[6] + (_uuid[7] << 8 ), + "clock": _uuid[8] + (_uuid[9] << 8 ), + "node" : _uuid[10] + (_uuid[11] << 8 ) + (_uuid[12] << 16) + (_uuid[13] << 24) + (_uuid[14] << 32) + (_uuid[15] << 40) + } + +func as_string() -> String: + return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [ + # low + _uuid[0], _uuid[1], _uuid[2], _uuid[3], + + # mid + _uuid[4], _uuid[5], + + # hi + _uuid[6], _uuid[7], + + # clock + _uuid[8], _uuid[9], + + # node + _uuid[10], _uuid[11], _uuid[12], _uuid[13], _uuid[14], _uuid[15] + ] + +func is_equal(other) -> bool: + # Godot Engine compares Array recursively + # There's no need for custom comparison here. + return _uuid == other._uuid