diff --git a/src/editor/JulGameEditor/Components/ComponentInputs.jl b/src/editor/JulGameEditor/Components/ComponentInputs.jl index b6dea27..9f7fa68 100644 --- a/src/editor/JulGameEditor/Components/ComponentInputs.jl +++ b/src/editor/JulGameEditor/Components/ComponentInputs.jl @@ -13,14 +13,14 @@ using JulGame.UI show_field_editor(entity, field) Creates inputs based on the component type and populates them. """ -function show_field_editor(entity, fieldName, animation_window_dict, animator_preview_dict) +function show_field_editor(entity, fieldName, animation_window_dict, animator_preview_dict, newScriptText) field = getfield(entity, fieldName) if field == C_NULL || field === nothing return end if !is_a_julgame_component(field) - show_component_field_input(entity, fieldName) + show_component_field_input(entity, fieldName, newScriptText) return end @@ -39,7 +39,7 @@ function show_field_editor(entity, fieldName, animation_window_dict, animator_pr show_animator_properties(entity.animator, animation_window_dict, animator_preview_dict) else for field in fieldnames(typeof(field)) - show_component_field_input(getfield(entity, Symbol(lowercase(fieldName))), field) + show_component_field_input(getfield(entity, Symbol(lowercase(fieldName))), field, "") end end @@ -84,7 +84,7 @@ This function displays the input fields for a given component field. It takes tw The function checks the type of the field value and displays the corresponding input fields using CImGui library. It updates the field value based on the user input. """ -function show_component_field_input(component, componentField) +function show_component_field_input(component, componentField, newScriptText) fieldValue = getfield(component, componentField) if isa(fieldValue, Math._Vector2{Float64}) || isa(fieldValue, Math._Vector2{Int32}) isFloat::Bool = isa(fieldValue, Math._Vector2{Float64}) ? true : false @@ -164,7 +164,7 @@ function show_component_field_input(component, componentField) end setfield!(component, componentField, x) elseif String(componentField) == "scripts" - show_script_editor(component) + show_script_editor(component, newScriptText) elseif isa(fieldValue, Vector) # Then we need to unpack the nested items for i = eachindex(fieldValue) continue # TODO: Implement this @@ -274,7 +274,7 @@ function show_animator_properties(animator, animation_window_dict, animator_prev end end else - show_component_field_input(animator, field) + show_component_field_input(animator, field, "") end end catch e @@ -356,7 +356,7 @@ function show_sprite_fields(sprite, animation_window_dict) window_info[]["points"][][2] = ImVec2(round(vec4i[1] + vec4i[3]), round(vec4i[2] + vec4i[4])) sprite.crop = JulGame.Math.Vector4(Int32(vec4i[1]), Int32(vec4i[2]), Int32(vec4i[3]), Int32(vec4i[4])) else - show_component_field_input(sprite, field) + show_component_field_input(sprite, field, "") end end end @@ -487,7 +487,7 @@ function show_sound_source_fields(soundSource) CImGui.Button("Load Sound") && (Component.load_sound(soundSource, currentTextInTextBox, false)) CImGui.Button("Load Music") && (Component.load_sound(soundSource, currentTextInTextBox, true)) else - show_component_field_input(soundSource, field) + show_component_field_input(soundSource, field, "") end end end @@ -507,29 +507,37 @@ function is_a_julgame_component(field) return isa(field, JulGame.TransformModule.Transform) || isa(field, JulGame.SpriteModule.InternalSprite) || isa(field, JulGame.ColliderModule.InternalCollider) || isa(field, JulGame.RigidbodyModule.InternalRigidbody) || isa(field, JulGame.SoundSourceModule.InternalSoundSource) || isa(field, JulGame.AnimatorModule.InternalAnimator) || isa(field, JulGame.ShapeModule.InternalShape) || isa(field, JulGame.CircleColliderModule.InternalCircleCollider) || isa(field, JulGame.AnimationModule.Animation) end -function show_script_editor(entity) +function create_new_script(name) + path = joinpath(JulGame.BasePath, "scripts", "$(name).jl") + touch(joinpath(path)) + file = open(path, "w") + println(file, newScriptContent(name)) + close(file) + + SDL2.SDL_OpenURL("vscode://file/$(path)") +end + +function show_script_editor(entity, newScriptText) if CImGui.TreeNode("Scripts") show_help_marker("Add a script here to run it on the entity.") - CImGui.Button("Add Script") && (push!(entity.scripts, scriptObj("",[])); return;) + text = text_input_single_line("Name", newScriptText) + CImGui.SameLine() + CImGui.Button("Create New Script") && (push!(entity.scripts, scriptObj(String(text), [])); create_new_script(text);) + + script = display_files(joinpath(JulGame.BasePath, "scripts"), "scripts", "Add Script") + if script != "" + push!(entity.scripts, scriptObj(script, [])) + end + for i = eachindex(entity.scripts) if CImGui.TreeNode("Script $(i)") - buf = "$(entity.scripts[i].name)"*"\0"^(64) CImGui.Button("Delete $(i)") && (deleteat!(entity.scripts, i); return;) - CImGui.InputText("Script $(i)", buf, length(buf)) - currentTextInTextBox = "" - for characterIndex = eachindex(buf) - if Int32(buf[characterIndex]) == 0 - if characterIndex != 1 - currentTextInTextBox = String(SubString(buf, 1, characterIndex-1)) - end - break - end - end + CImGui.Text(entity.scripts[i].name) - entity.scripts[i] = scriptObj(currentTextInTextBox, entity.scripts[i].parameters) + entity.scripts[i] = scriptObj(entity.scripts[i].name, entity.scripts[i].parameters) if CImGui.TreeNode("Script $(i) parameters") params = entity.scripts[i].parameters - CImGui.Button("Add New Script Parameter") && (push!(params, ""); entity.scripts[i] = scriptObj(currentTextInTextBox, params); break;) + CImGui.Button("Add New Script Parameter") && (push!(params, ""); entity.scripts[i] = scriptObj(entity.scripts[i].name, params); break;) for j = eachindex(entity.scripts[i].parameters) buf = "$(entity.scripts[i].parameters[j])"*"\0"^(64) diff --git a/src/editor/JulGameEditor/Components/FileFinderMenu.jl b/src/editor/JulGameEditor/Components/FileFinderMenu.jl index aff6cf4..d084582 100644 --- a/src/editor/JulGameEditor/Components/FileFinderMenu.jl +++ b/src/editor/JulGameEditor/Components/FileFinderMenu.jl @@ -1,15 +1,21 @@ imageExtensions = [".png", ".jpg", ".jpeg", ".bmp", ".gif"] soundExtensions = [".wav", ".ogg", ".flac", ".mp3", ".aac", ".m4a", ".wma", ".aiff", ".aif", ".aifc", ".amr", ".au", ".snd", ".ra", ".rm", ".rmvb", ".mka", ".opus", ".sln", ".voc", ".vox", ".raw", ".wv", ".webm", ".dts", ".ac3", ".ec3", ".mlp", ".tta", ".mka", ".mks", ".m3u", ".m3u8", ".pls", ".asx", ".xspf", ".m4b", ".m4p", ".m4r", ".m4v", ".3gp", ".3g2", ".mp4", ".m4v", ".mkv", ".webm", ".flv", ".vob", ".ogv", ".avi", ".wmv", ".mov", ".qt", ".mpg", ".mpeg", ".m2v", ".m4v", ".svi", ".3gp", ".3g2", ".mxf", ".roq", ".nsv", ".f4v", ".f4p", ".f4a", ".f4b", ".f4m"] fontExtensions = [".ttf", ".otf", ".ttc", ".woff", ".woff2", ".eot", ".sfnt", ".pfa", ".pfb", ".pfr", ".gsf", ".cid", ".cff", ".bdf", ".pcf", ".snf", ".mm", ".otb", ".dfont", ".bin", ".sfd", ".t42", ".t1", ".fon", ".fnt"] +scriptExtensions = [".jl"] +extensionsDict = Dict("images" => imageExtensions, "sounds" => soundExtensions, "fonts" => fontExtensions, "scripts" => scriptExtensions) -extensionsDict = Dict("images" => imageExtensions, "sounds" => soundExtensions, "fonts" => fontExtensions) -function display_files(base_path::String, file_type::String, depth::Int = 1)::String +function display_files(base_path::String, file_type::String, title::String = "", depth::Int = 1)::String extensions = extensionsDict[file_type] value = "" pathName = split(base_path, "/")[end] pathName = split(pathName, "\\")[end] - if CImGui.BeginMenu("$(pathName)") + + if title == "" + title = pathName + end + + if CImGui.BeginMenu("$(title)") for file::String in readdir(joinpath(base_path)) if isdir(joinpath(base_path, file)) value = display_files(joinpath(base_path, file), file_type, depth+1) @@ -21,6 +27,9 @@ function display_files(base_path::String, file_type::String, depth::Int = 1)::St if extension in extensions if CImGui.MenuItem(file) value = "$(joinpath(base_path, file))" + if file_type == "scripts" + value = split(file, ".")[1] + end end end end diff --git a/src/editor/JulGameEditor/Components/TextInputs.jl b/src/editor/JulGameEditor/Components/TextInputs.jl index 014cf57..9187d0d 100644 --- a/src/editor/JulGameEditor/Components/TextInputs.jl +++ b/src/editor/JulGameEditor/Components/TextInputs.jl @@ -24,4 +24,50 @@ function text_input_single_line(name::String, currentText; maxBuf=128, filters = end CImGui.PopID() return currentText[] +end + +mutable struct TextInputSingleLine + name::String + currentText::String + maxBuf::Int + filters::CImGui.ImGuiInputTextFlags + + function TextInputSingleLine(name::String, currentText::String; maxBuf=128, filters = CImGui.ImGuiInputTextFlags_None) + this = new() + + this.name = name + this.currentText = currentText + this.maxBuf = maxBuf + this.filters = filters + + return this + end +end + + """ + text_input_single_line(this::TextInputSingleLine) + + Create a single-line text input field. + + # Arguments + - `this::TextInputSingleLine`: The text input field. + - `filters`: Optional. The ImGui filters to apply to the text input field. + + # Returns + - `currentText`: The current text entered in the text input field. + + """ + +function text_input_single_line(this::TextInputSingleLine) + buf = "$(this.currentText)"*"\0"^this.maxBuf + CImGui.PushID(this.name) + CImGui.InputText(this.name, buf, length(buf), this.filters) + for characterIndex = eachindex(buf) + if Int32(buf[characterIndex]) == 0 # The end of the buffer will be recognized as a 0 + this.currentText = characterIndex == 1 ? "" : String(SubString(buf, 1, characterIndex - 1)) + break + end + end + CImGui.PopID() + return this.currentText end \ No newline at end of file diff --git a/src/editor/JulGameEditor/Editor.jl b/src/editor/JulGameEditor/Editor.jl index 2031898..630bbd9 100644 --- a/src/editor/JulGameEditor/Editor.jl +++ b/src/editor/JulGameEditor/Editor.jl @@ -77,6 +77,7 @@ module Editor currentDialog::Base.RefValue{String} = Ref("") newSceneText = Ref("") newProjectText = Ref("") + newScriptText = Ref("") panOffset = Math.Vector2(0, 0) camera = JulGame.CameraModule.Camera(Vector2(500,500), Vector2f(),Vector2f(), C_NULL) @@ -334,7 +335,20 @@ module Editor end CImGui.End() catch e - log_exceptions("Hierarchy Window Error:", latest_exceptions, e, is_test_mode) + # Get the stack trace + bt = stacktrace(catch_backtrace()) + + file = "" + line = "" + if !isempty(bt) + top_frame = bt[1] + file = top_frame.file + line = top_frame.line + else + @info("Stack trace is empty.") + end + + log_exceptions("Hierarchy Window Error:", latest_exceptions, e, "$(file):$(line)", is_test_mode) end show_debug_window(latest_exceptions) @@ -351,7 +365,7 @@ module Editor CImGui.PopID() CImGui.Separator() for entityField in fieldnames(Entity) - show_field_editor(currentSceneMain.selectedEntity, entityField, animation_window_dict, animator_preview_dict) + show_field_editor(currentSceneMain.selectedEntity, entityField, animation_window_dict, animator_preview_dict, newScriptText) end CImGui.Separator() @@ -364,7 +378,20 @@ module Editor end CImGui.End() catch e - log_exceptions("Entity Inspector Window Error:", latest_exceptions, e, is_test_mode) + # Get the stack trace + bt = stacktrace(catch_backtrace()) + + file = "" + line = "" + if !isempty(bt) + top_frame = bt[1] + file = top_frame.file + line = top_frame.line + else + @info("Stack trace is empty.") + end + + log_exceptions("Entity Inspector Window Error:", latest_exceptions, e, "$(file):$(line)", is_test_mode) end try @@ -402,7 +429,20 @@ module Editor end CImGui.End() catch e - log_exceptions("UI Inspector Window Error:", latest_exceptions, e, is_test_mode) + # Get the stack trace + bt = stacktrace(catch_backtrace()) + + file = "" + line = "" + if !isempty(bt) + top_frame = bt[1] + file = top_frame.file + line = top_frame.line + else + @info("Stack trace is empty.") + end + + log_exceptions("UI Inspector Window Error:", latest_exceptions, e, "$(file):$(line)", is_test_mode) end SDL2.SDL_SetRenderTarget(renderer, sceneTexture) diff --git a/src/editor/JulGameEditor/Utils/EditorUtils.jl b/src/editor/JulGameEditor/Utils/EditorUtils.jl index e33482b..1bc40ec 100644 --- a/src/editor/JulGameEditor/Utils/EditorUtils.jl +++ b/src/editor/JulGameEditor/Utils/EditorUtils.jl @@ -344,10 +344,9 @@ function move_entities(entities, origin, destination) splice!(entities, destinationIndex : destinationIndex, updatedEntities) end -function log_exceptions(error_type, latest_exceptions, e, is_test_mode) - println("Error: ", e) +function log_exceptions(error_type, latest_exceptions, e, top_backtrace, is_test_mode) Base.show_backtrace(stderr, catch_backtrace()) - push!(latest_exceptions, [e, String("$(Dates.now())")]) + push!(latest_exceptions, [e, String("$(Dates.now())"), top_backtrace]) if length(latest_exceptions) > 10 deleteat!(latest_exceptions, 1) end diff --git a/src/editor/JulGameEditor/Windows/DebugWindow.jl b/src/editor/JulGameEditor/Windows/DebugWindow.jl index 04ad0f0..be03417 100644 --- a/src/editor/JulGameEditor/Windows/DebugWindow.jl +++ b/src/editor/JulGameEditor/Windows/DebugWindow.jl @@ -7,7 +7,7 @@ function show_debug_window(latestExceptions) for exception in latestExceptions CImGui.Text("[$(counter)] $(exception[2]): $(exception[1])") CImGui.Button("Copy to clipboard") && (CImGui.SetClipboardText("[$(counter)] $(exception[2]): $(exception[1])");) - CImGui.Button("Open link: ") && (SDL2.SDL_OpenURL("vscode://file/F:/Projects/Julia/julGame/src/editor/JulGameEditor/Windows/DebugWindow.jl").SetClipboardText("[$(counter)] $(exception[2]): $(exception[1])");) + CImGui.Button("Open in vscode") && (SDL2.SDL_OpenURL("vscode://file/$(exception[3])");) counter += 1 end CImGui.End()