diff --git a/Editor/src/ScriptingContext/ScriptingContext.cpp b/Editor/src/ScriptingContext/ScriptingContext.cpp index 6736bb15..78725f37 100644 --- a/Editor/src/ScriptingContext/ScriptingContext.cpp +++ b/Editor/src/ScriptingContext/ScriptingContext.cpp @@ -5,22 +5,22 @@ void ScriptingContext::Initialize() { - m_Modules = + modules = { CreateRef() }; - for (auto& m : m_Modules) + for (auto& m : modules) { m->RegisterMethods(); } // Load Nuake assembly DLL - /*auto m_LoadContext2 = Nuake::ScriptingEngineNet::Get().GetHostInstance()->CreateAssemblyLoadContext("NuakeEditorContext"); - m_NuakeAssembly = Nuake::ScriptingEngineNet::Get().ReloadEngineAPI(m_LoadContext2); - m_EditorAssembly = m_LoadContext2.LoadAssembly("EditorNet.dll"); + /*auto loadContext2 = Nuake::ScriptingEngineNet::Get().GetHostInstance()->CreateAssemblyLoadContext("NuakeEditorContext"); + nuakeAssembly = Nuake::ScriptingEngineNet::Get().ReloadEngineAPI(loadContext2); + m_EditorAssembly = loadContext2.LoadAssembly("EditorNet.dll"); - for (const auto& netModule : m_Modules) + for (const auto& netModule : modules) { for (const auto& [methodName, methodPtr] : netModule->GetMethods()) { diff --git a/Editor/src/ScriptingContext/ScriptingContext.h b/Editor/src/ScriptingContext/ScriptingContext.h index 2382432b..cc4338c9 100644 --- a/Editor/src/ScriptingContext/ScriptingContext.h +++ b/Editor/src/ScriptingContext/ScriptingContext.h @@ -13,13 +13,13 @@ namespace Coral class ScriptingContext { private: - Coral::HostInstance* m_HostInstance; - Coral::AssemblyLoadContext m_LoadContext; + Coral::HostInstance* hostInstance; + Coral::AssemblyLoadContext loadContext; - std::unordered_map m_LoadedAssemblies; - std::vector> m_Modules; + std::unordered_map loadedAssemblies; + std::vector> modules; - Coral::ManagedAssembly m_NuakeAssembly; // Nuake DLL + Coral::ManagedAssembly nuakeAssembly; // Nuake DLL Coral::ManagedAssembly m_EditorAssembly; // Editor DLL public: static ScriptingContext& Get() diff --git a/Nuake/src/Rendering/SceneRenderer.cpp b/Nuake/src/Rendering/SceneRenderer.cpp index 4b9f5ba0..66aabc24 100644 --- a/Nuake/src/Rendering/SceneRenderer.cpp +++ b/Nuake/src/Rendering/SceneRenderer.cpp @@ -177,6 +177,8 @@ namespace Nuake GL_NEAREST // Filtering mode (NEAREST or LINEAR) ); + // World Space UI + DebugRendererPass(scene); Ref finalOutput = mShadingBuffer->GetTexture(); @@ -428,6 +430,15 @@ namespace Nuake } mTempFrameBuffer->Unbind(); + //ImGui::Begin("SDF Params"); + //{ + // static auto renderer = NuakeUI::Renderer::Get(); + // ImGui::DragFloat("Subpixel threshold", &renderer.subpixelThreshold, 0.01f, 0.0f); + // ImGui::DragFloat("Subpixel curve tolerance", &renderer.curveTolerance, 0.01f, 0.0f); + // ImGui::DragFloat("Subpixel amount", &renderer.subpixelAmount, 0.01f, 0.0f); + //} + //ImGui::End(); + const auto uiView = scene.m_Registry.view(); for (auto ui : uiView) { diff --git a/Nuake/src/Resource/UI.cpp b/Nuake/src/Resource/UI.cpp index 18e47a26..5ec6647d 100644 --- a/Nuake/src/Resource/UI.cpp +++ b/Nuake/src/Resource/UI.cpp @@ -24,12 +24,19 @@ UIResource::UIResource(const std::string& path) : inputManager = new MyInputManager(*Engine::GetCurrentWindow()); } - CanvasParser parser; - canvas = parser.Parse(FileSystem::RelativeToAbsolute(path)); + canvas = CanvasParser::Get().Parse(FileSystem::RelativeToAbsolute(path)); canvas->SetInputManager(inputManager); canvas->ComputeLayout(defaultSize); } +void UIResource::Tick() +{ + if (canvas) + { + canvas->Tick(); + } +} + void UIResource::Draw() { framebuffer->Bind(); @@ -58,8 +65,7 @@ void UIResource::Reload() return; } - CanvasParser parser; - canvas = parser.Parse(FileSystem::RelativeToAbsolute(filePath)); + canvas = CanvasParser::Get().Parse(FileSystem::RelativeToAbsolute(filePath)); if (canvas) { canvas->SetInputManager(inputManager); diff --git a/Nuake/src/Resource/UI.h b/Nuake/src/Resource/UI.h index 1b5400a8..be387241 100644 --- a/Nuake/src/Resource/UI.h +++ b/Nuake/src/Resource/UI.h @@ -20,6 +20,7 @@ namespace Nuake UIResource(const std::string& path); ~UIResource() = default; + void Tick(); void Draw(); void Resize(const Vector2& size); void Reload(); diff --git a/Nuake/src/Scene/Systems/ScriptingSystem.cpp b/Nuake/src/Scene/Systems/ScriptingSystem.cpp index f1bb8138..841cd4c4 100644 --- a/Nuake/src/Scene/Systems/ScriptingSystem.cpp +++ b/Nuake/src/Scene/Systems/ScriptingSystem.cpp @@ -6,6 +6,7 @@ #include "src/Scripting/ScriptingEngineNet.h" #include "src/Physics/PhysicsManager.h" +#include namespace Nuake { @@ -61,6 +62,13 @@ namespace Nuake netScriptComponent.Initialized = true; } + // Instantiate UI widgets + for (auto& uiWidget : CanvasParser::Get().GetAllCustomWidgetInstance()) + { + scriptingEngineNet.RegisterCustomWidgetInstance(uiWidget.first, uiWidget.second); + } + + // Call OnInit on entity script instances for (auto& e : netEntities) { auto entity = Entity{ e, m_Scene }; @@ -72,6 +80,17 @@ namespace Nuake scriptInstance.InvokeMethod("OnInit"); } } + + // Call OnInit on UI widgets + for (auto& widget : CanvasParser::Get().GetAllCustomWidgetInstance()) + { + const UUID& widgetInstanceUUID = widget.first; + if (scriptingEngineNet.HasCustomWidgetInstance(widgetInstanceUUID)) + { + auto widgetInstance = scriptingEngineNet.GetCustomWidgetInstance(widgetInstanceUUID); + widgetInstance.InvokeMethod("OnInit"); + } + } return true; } diff --git a/Nuake/src/Scripting/ScriptingEngineNet.cpp b/Nuake/src/Scripting/ScriptingEngineNet.cpp index d7586e9c..1103a46f 100644 --- a/Nuake/src/Scripting/ScriptingEngineNet.cpp +++ b/Nuake/src/Scripting/ScriptingEngineNet.cpp @@ -44,19 +44,19 @@ namespace Nuake .ExceptionCallback = ExceptionCallback, }; - m_HostInstance = new Coral::HostInstance(); - m_HostInstance->Initialize(settings); + hostInstance = new Coral::HostInstance(); + hostInstance->Initialize(settings); // Initialize API modules // ---------------------------------- - m_Modules = + modules = { CreateRef(), CreateRef(), CreateRef() }; - for (auto& m : m_Modules) + for (auto& m : modules) { m->RegisterMethods(); } @@ -66,14 +66,14 @@ namespace Nuake if (!FileSystem::FileExists(m_EngineAssemblyName, true)) { - m_IsInitialized = false; + isInitialized = false; return; } } ScriptingEngineNet::~ScriptingEngineNet() { - m_HostInstance->Shutdown(); + hostInstance->Shutdown(); } std::vector ScriptingEngineNet::ExtractErrors(const std::string& output) @@ -149,7 +149,7 @@ namespace Nuake // Upload internal calls for each module // -------------------------------------------------- - for (const auto& netModule : m_Modules) + for (const auto& netModule : modules) { for (const auto& [methodName, methodPtr] : netModule->GetMethods()) { @@ -165,23 +165,31 @@ namespace Nuake void ScriptingEngineNet::Initialize() { - m_LoadContext = m_HostInstance->CreateAssemblyLoadContext(m_ContextName); + loadContext = hostInstance->CreateAssemblyLoadContext(m_ContextName); - m_NuakeAssembly = ReloadEngineAPI(m_LoadContext); + nuakeAssembly = ReloadEngineAPI(loadContext); - m_IsInitialized = true; + isInitialized = true; - m_GameEntityTypes.clear(); + gameEntityTypes.clear(); + brushEntityTypes.clear(); + pointEntityTypes.clear(); + uiWidgets.clear(); } void ScriptingEngineNet::Uninitialize() { - if (!m_IsInitialized) + if (!isInitialized) { return; } - for (auto& [entity, managedObject] : m_EntityToManagedObjects) + for (auto& [entity, managedObject] : entityToManagedObjects) + { + managedObject.Destroy(); + } + + for (auto& [widgetUUID, managedObject] : widgetUUIDToManagedObjects) { managedObject.Destroy(); } @@ -189,9 +197,10 @@ namespace Nuake Coral::GC::Collect(); Coral::GC::WaitForPendingFinalizers(); - GetHostInstance()->UnloadAssemblyLoadContext(m_LoadContext); + GetHostInstance()->UnloadAssemblyLoadContext(loadContext); - m_EntityToManagedObjects.clear(); + entityToManagedObjects.clear(); + widgetUUIDToManagedObjects.clear(); } void ScriptingEngineNet::UpdateEntityWithExposedVar(Entity entity) @@ -257,16 +266,59 @@ namespace Nuake className = filePath; } - if (m_GameEntityTypes.find(className) == m_GameEntityTypes.end()) + if (gameEntityTypes.find(className) == gameEntityTypes.end()) { // The class name parsed in the file was not found in the game's DLL. const std::string& msg = "Skipped .net entity script: \n Class: " + - className + " not found in " + std::string(m_GameAssembly.GetName()); + className + " not found in " + std::string(gameAssembly.GetName()); Logger::Log(msg, ".net", CRITICAL); return std::vector(); } - return m_GameEntityTypes[className].exposedVars; + return gameEntityTypes[className].exposedVars; + } + + bool ScriptingEngineNet::HasUIWidget(const std::string& widgetName) + { + return uiWidgets.contains(widgetName); + } + + UIWidgetObject& ScriptingEngineNet::GetUIWidget(const std::string& widgetName) + { + ASSERT(HasUIWidget(widgetName)); + return uiWidgets[widgetName]; + } + + void ScriptingEngineNet::RegisterCustomWidgetInstance(const UUID& uuid, const std::string& widgetTypeName) + { + if (uiWidgets.find(widgetTypeName) == uiWidgets.end()) + { + // The class name parsed in the file was not found in the game's DLL. + const std::string& msg = "Skipped .net widget script: \n Class: " + + widgetTypeName + " not found in " + std::string(gameAssembly.GetName()); + Logger::Log(msg, ".net", CRITICAL); + return; + } + + auto classInstance = uiWidgets[widgetTypeName].coralType->CreateInstance(); + classInstance.SetPropertyValue("ID", uuid); + widgetUUIDToManagedObjects[uuid] = classInstance; + } + + bool ScriptingEngineNet::HasCustomWidgetInstance(const UUID& uuid) + { + return widgetUUIDToManagedObjects.contains(uuid); + } + + Coral::ManagedObject ScriptingEngineNet::GetCustomWidgetInstance(const UUID& uuid) + { + if (!HasCustomWidgetInstance(uuid)) + { + Logger::Log("Failed to get custom widget .Net script instance, doesn't exist", ".net", CRITICAL); + return Coral::ManagedObject(); + } + + return widgetUUIDToManagedObjects[uuid]; } std::vector ScriptingEngineNet::BuildProjectAssembly(Ref project) @@ -293,7 +345,7 @@ namespace Nuake void ScriptingEngineNet::LoadProjectAssembly(Ref project) { - if (!m_IsInitialized) + if (!isInitialized) { return; } @@ -308,15 +360,17 @@ namespace Nuake } const std::string absoluteAssemblyPath = FileSystem::Root + assemblyPath; - m_GameAssembly = m_LoadContext.LoadAssembly(absoluteAssemblyPath); + gameAssembly = loadContext.LoadAssembly(absoluteAssemblyPath); - m_PrefabType = m_GameAssembly.GetType("Nuake.Net.Prefab"); - auto entityScriptType = m_GameAssembly.GetType("Nuake.Net.Entity"); - auto& exposedFieldAttributeType = m_GameAssembly.GetType("Nuake.Net.ExposedAttribute"); - auto& brushScriptAttributeType = m_GameAssembly.GetType("Nuake.Net.BrushScript"); - auto& pointScriptAttributeType = m_GameAssembly.GetType("Nuake.Net.PointScript"); + prefabType = gameAssembly.GetType("Nuake.Net.Prefab"); + auto entityScriptType = gameAssembly.GetType("Nuake.Net.Entity"); + auto& exposedFieldAttributeType = gameAssembly.GetType("Nuake.Net.ExposedAttribute"); + auto& brushScriptAttributeType = gameAssembly.GetType("Nuake.Net.BrushScript"); + auto& pointScriptAttributeType = gameAssembly.GetType("Nuake.Net.PointScript"); + auto& uiWidgetType = gameAssembly.GetType("Nuake.Net.UIWidget"); + auto& uiWidgetExternalLayoutType = gameAssembly.GetType("Nuake.Net.ExternalHTML"); - for (auto& type : m_GameAssembly.GetTypes()) + for (auto& type : gameAssembly.GetTypes()) { // Brush bool isBrushScript = false; @@ -352,7 +406,7 @@ namespace Nuake continue; } - m_BaseEntityType = type->GetBaseType(); + baseEntityType = type->GetBaseType(); auto typeSplits = String::Split(type->GetFullName(), '.'); std::string shortenedTypeName = typeSplits[typeSplits.size() - 1]; @@ -437,22 +491,43 @@ namespace Nuake { gameScriptObject.Description = brushDescription; gameScriptObject.isTrigger = isTrigger; - m_BrushEntityTypes[shortenedTypeName] = gameScriptObject; + brushEntityTypes[shortenedTypeName] = gameScriptObject; } if (isPointScript) { gameScriptObject.Description = pointDescription; - m_PointEntityTypes[shortenedTypeName] = gameScriptObject; + pointEntityTypes[shortenedTypeName] = gameScriptObject; + } + gameEntityTypes[shortenedTypeName] = gameScriptObject; + } + + if (type->IsSubclassOf(uiWidgetType)) + { + auto typeSplits = String::Split(type->GetFullName(), '.'); + std::string shortenedTypeName = typeSplits[typeSplits.size() - 1]; + for (auto& attribute : type->GetAttributes()) + { + if (attribute.GetType() == uiWidgetExternalLayoutType) + { + Coral::String htmlPath = attribute.GetFieldValue("HTMLPath"); + if (FileSystem::FileExists(htmlPath)) + { + uiWidgets[shortenedTypeName] = { type, htmlPath }; + } + else + { + Logger::Log("Couldn't register UI Widget: " + shortenedTypeName + " \nExternal HTML file doesn't exist with path: " + std::string(htmlPath), "UI", WARNING); + } + } } - m_GameEntityTypes[shortenedTypeName] = gameScriptObject; } } } void ScriptingEngineNet::RegisterEntityScript(Entity& entity) { - if (!m_IsInitialized) + if (!isInitialized) { return; } @@ -483,16 +558,16 @@ namespace Nuake className = filePath; } - if(m_GameEntityTypes.find(className) == m_GameEntityTypes.end()) + if(gameEntityTypes.find(className) == gameEntityTypes.end()) { // The class name parsed in the file was not found in the game's DLL. const std::string& msg = "Skipped .net entity script: \n Class: " + - className + " not found in " + std::string(m_GameAssembly.GetName()); + className + " not found in " + std::string(gameAssembly.GetName()); Logger::Log(msg, ".net", CRITICAL); return; } - auto classInstance = m_GameEntityTypes[className].coralType->CreateInstance(); + auto classInstance = gameEntityTypes[className].coralType->CreateInstance(); int handle = entity.GetHandle(); int id = entity.GetID(); @@ -505,9 +580,9 @@ namespace Nuake } std::vector detectedExposedVar; - detectedExposedVar.reserve(std::size(m_GameEntityTypes[className].exposedVars)); + detectedExposedVar.reserve(std::size(gameEntityTypes[className].exposedVars)); // Update new default values if they have changed in the code. - for (auto& exposedVar : m_GameEntityTypes[className].exposedVars) + for (auto& exposedVar : gameEntityTypes[className].exposedVars) { const std::string varName = exposedVar.Name; detectedExposedVar.push_back(varName); @@ -563,7 +638,7 @@ namespace Nuake } } - m_EntityToManagedObjects.emplace(entity.GetID(), classInstance); + entityToManagedObjects.emplace(entity.GetID(), classInstance); // Override with user values set in the editor. for (auto& exposedVarUserValue : component.ExposedVar) @@ -597,7 +672,7 @@ namespace Nuake else { // In the case where the entity doesnt have an instance, we create one - auto newEntity = m_BaseEntityType.CreateInstance(scriptEntity.GetHandle()); + auto newEntity = baseEntityType.CreateInstance(scriptEntity.GetHandle()); classInstance.SetFieldValue(exposedVarUserValue.Name, newEntity); } } @@ -615,7 +690,7 @@ namespace Nuake if (FileSystem::FileExists(path)) { // In the case where the entity doesnt have an instance, we create one - auto newPrefab = m_PrefabType.CreateInstance(Coral::String::New(path)); + auto newPrefab = prefabType.CreateInstance(Coral::String::New(path)); classInstance.SetFieldValue(exposedVarUserValue.Name, newPrefab); } else @@ -641,17 +716,17 @@ namespace Nuake return Coral::ManagedObject(); } - return m_EntityToManagedObjects[entity.GetID()]; + return entityToManagedObjects[entity.GetID()]; } bool ScriptingEngineNet::HasEntityScriptInstance(const Entity& entity) { - if (!m_IsInitialized) + if (!isInitialized) { return false; } - return m_EntityToManagedObjects.find(entity.GetID()) != m_EntityToManagedObjects.end(); + return entityToManagedObjects.find(entity.GetID()) != entityToManagedObjects.end(); } void ScriptingEngineNet::CopyNuakeNETAssemblies(const std::string& path) diff --git a/Nuake/src/Scripting/ScriptingEngineNet.h b/Nuake/src/Scripting/ScriptingEngineNet.h index 4df07f19..28316704 100644 --- a/Nuake/src/Scripting/ScriptingEngineNet.h +++ b/Nuake/src/Scripting/ScriptingEngineNet.h @@ -1,25 +1,28 @@ #pragma once #include "src/Core/Core.h" #include "src/Scene/Entities/Entity.h" - #include "src/Scripting/NetModules/NetAPIModule.h" + +// For some reason HostInstance.hpp doesn't include filesystem but it needs it. +#include + +#include +#include +#include +#include +#include + #include + namespace Coral { class HostInstance; class Type; } -#include -#include -#include -#include -#include -#include - -namespace Nuake { - +namespace Nuake +{ class Project; enum class ExposedVarTypes @@ -44,6 +47,12 @@ namespace Nuake { std::string Name; }; + struct UIWidgetObject + { + Coral::Type* coralType; + std::string htmlPath; + }; + struct NetGameScriptObject { Coral::Type* coralType; @@ -64,74 +73,78 @@ namespace Nuake { class ScriptingEngineNet { + public: + static ScriptingEngineNet& Get(); + + void Initialize(); + void Uninitialize(); + bool IsInitialized() const { return isInitialized; } + + Coral::HostInstance* GetHostInstance() { return hostInstance; } + Coral::AssemblyLoadContext& GetLoadContext() { return loadContext; } + Coral::ManagedAssembly GetNuakeAssembly() const { return nuakeAssembly; } + + Coral::ManagedAssembly ReloadEngineAPI(Coral::AssemblyLoadContext & context); + + void GenerateSolution(const std::string& path, const std::string& projectName); + void CopyNuakeNETAssemblies(const std::string& path); + std::vector BuildProjectAssembly(Ref project); + void LoadProjectAssembly(Ref project); + + void CreateEntityScript(const std::string& path, const std::string& entityName); + void RegisterEntityScript(Entity& entity); + Coral::ManagedObject GetEntityScript(const Entity& entity); + bool HasEntityScriptInstance(const Entity& entity); + std::string FindClassNameInScript(const std::string& filePath); + + void UpdateEntityWithExposedVar(Entity entity); + std::vector GetExposedVarForTypes(Entity entity); + + bool HasUIWidget(const std::string& widgetName); + UIWidgetObject& GetUIWidget(const std::string& widgetName); + void RegisterCustomWidgetInstance(const UUID& uuid, const std::string& widgetTypeName); + bool HasCustomWidgetInstance(const UUID& uuid); + Coral::ManagedObject GetCustomWidgetInstance(const UUID& uuid); + + std::unordered_map GetBrushEntities() const { return brushEntityTypes; } + std::unordered_map GetPointEntities() const { return pointEntityTypes; } + std::unordered_map GetUIWidgets() const { return uiWidgets; } + private: const std::string m_Scope = "Nuake.Net"; const std::string m_EngineAssemblyName = "NuakeNet.dll"; const std::string m_NetDirectory = ".net"; const std::string m_ContextName = "NuakeEngineContext"; - Coral::Type m_BaseEntityType; - Coral::Type m_PrefabType; - bool m_IsInitialized = false; + Coral::Type baseEntityType; + Coral::Type prefabType; + bool isInitialized = false; - Coral::HostInstance* m_HostInstance; - Coral::AssemblyLoadContext m_LoadContext; + Coral::HostInstance* hostInstance; + Coral::AssemblyLoadContext loadContext; - std::unordered_map m_LoadedAssemblies; - std::vector> m_Modules; + std::unordered_map loadedAssemblies; + std::vector> modules; - Coral::ManagedAssembly m_NuakeAssembly; // Nuake DLL - Coral::ManagedAssembly m_GameAssembly; // Game DLL + Coral::ManagedAssembly nuakeAssembly; // Nuake DLL + Coral::ManagedAssembly gameAssembly; // Game DLL // This is a map of all the entity scripts detected in the game's assembly. // This is filled when loading the game's assembly. // This is a map that contains all the instances of entity scripts. - std::unordered_map m_GameEntityTypes; - std::unordered_map m_BrushEntityTypes; - std::unordered_map m_PointEntityTypes; - std::unordered_map m_EntityToManagedObjects; + std::unordered_map gameEntityTypes; + std::unordered_map brushEntityTypes; + std::unordered_map pointEntityTypes; + std::unordered_map uiWidgets; + std::unordered_map entityToManagedObjects; + std::unordered_map widgetUUIDToManagedObjects; ScriptingEngineNet(); ~ScriptingEngineNet(); - std::vector ExtractErrors(const std::string& input); - - public: - static ScriptingEngineNet& Get(); - - Coral::HostInstance* GetHostInstance() { return m_HostInstance; } - Coral::AssemblyLoadContext& GetLoadContext() { return m_LoadContext; } - - Coral::ManagedAssembly ReloadEngineAPI(Coral::AssemblyLoadContext & context); - - void Initialize(); - void Uninitialize(); - - - bool IsInitialized() const { return m_IsInitialized; } - - void UpdateEntityWithExposedVar(Entity entity); - std::vector GetExposedVarForTypes(Entity entity); - - std::vector BuildProjectAssembly(Ref project); - void LoadProjectAssembly(Ref project); - - void RegisterEntityScript(Entity& entity); - Coral::ManagedObject GetEntityScript(const Entity& entity); - bool HasEntityScriptInstance(const Entity& entity); - - void CopyNuakeNETAssemblies(const std::string& path); - void GenerateSolution(const std::string& path, const std::string& projectName); - void CreateEntityScript(const std::string& path, const std::string& entityName); - - std::string FindClassNameInScript(const std::string& filePath); - - Coral::ManagedAssembly GetNuakeAssembly() const { return m_NuakeAssembly; } - - std::unordered_map GetBrushEntities() const { return m_BrushEntityTypes; } - std::unordered_map GetPointEntities() const { return m_PointEntityTypes; } private: std::string GenerateGUID(); + std::vector ExtractErrors(const std::string& input); }; } \ No newline at end of file diff --git a/Nuake/src/Scripting/WrenScript.cpp b/Nuake/src/Scripting/WrenScript.cpp index 24ebf5a3..6515339e 100644 --- a/Nuake/src/Scripting/WrenScript.cpp +++ b/Nuake/src/Scripting/WrenScript.cpp @@ -35,7 +35,7 @@ namespace Nuake if (s == "class" && i + 1 < splits.size() && !hasFoundModule) { std::string moduleFound = splits[i + 1]; - m_Modules.push_back(moduleFound); + modules.push_back(moduleFound); hasFoundModule = true; } } @@ -46,7 +46,7 @@ namespace Nuake std::vector WrenScript::GetModules() const { - return m_Modules; + return modules; } void WrenScript::Build(unsigned int moduleId, bool isEntity) diff --git a/Nuake/src/Scripting/WrenScript.h b/Nuake/src/Scripting/WrenScript.h index f7518706..d439b878 100644 --- a/Nuake/src/Scripting/WrenScript.h +++ b/Nuake/src/Scripting/WrenScript.h @@ -15,7 +15,7 @@ namespace Nuake private: bool m_HasCompiledSuccesfully; bool m_IsEntityScript = false; - std::vector m_Modules; + std::vector modules; Ref mFile; diff --git a/Nuake/src/UI/Nodes/Node.h b/Nuake/src/UI/Nodes/Node.h index d2cbafdb..f1d296f3 100644 --- a/Nuake/src/UI/Nodes/Node.h +++ b/Nuake/src/UI/Nodes/Node.h @@ -1,15 +1,16 @@ #pragma once #include "NodeState.h" -#include "../DataBinding/DataBindObject.h" -#include "../DataBinding/DataModelOperations.h" +#include "src/UI/DataBinding/DataBindObject.h" +#include "src/UI/DataBinding/DataModelOperations.h" +#include "src/UI/Styles/StyleSheet.h" +#include "src/UI/Nodes/NodeStyle.h" +#include "src/UI/InputManager.h" -#include "../Styles/StyleSheet.h" -#include "../Nodes/NodeStyle.h" +#include "src/Core/Maths.h" +#include "src/Resource/UUID.h" +#include "src/Scripting/ScriptingEngineNet.h" -#include "../InputManager.h" - -#include #include #include @@ -17,6 +18,7 @@ #include #include + #define SetLength(name) \ if (ComputedStyle.##name.type == LengthType::Auto) \ YGNodeStyleSet##name##Auto(mNode); \ @@ -75,18 +77,24 @@ break;\ break; \ +using namespace NuakeUI; + namespace NuakeUI { class Node; - typedef std::shared_ptr NodePtr; - class Renderer; class CanvasParser; + class Renderer; + + typedef std::shared_ptr NodePtr; + class Node { - friend CanvasParser; - friend Renderer; + friend NuakeUI::CanvasParser; + friend NuakeUI::Renderer; + private: static Node* mFocused; + UUID scriptingId; protected: float ScrollDelta = 0.0f; @@ -102,7 +110,7 @@ namespace NuakeUI bool mHasBeenInitialized = false; void InitializeNode(); - + public: bool CanGrabFocus = false; std::any UserData; @@ -126,21 +134,32 @@ namespace NuakeUI virtual void Tick(InputManager* manager); virtual void Calculate(); - virtual void OnMouseHover(InputManager* inputManager) {}; - virtual void OnMouseExit(InputManager* inputManager) {}; - virtual void OnClick(InputManager* inputManager) {}; - virtual void OnTick(InputManager* manager) {}; - virtual void OnClickReleased(InputManager* inputManager) {}; - virtual void OnScroll(InputManager* inputManager) {}; + void OnMouseHover(InputManager* inputManager) {}; + void OnMouseExit(InputManager* inputManager) {}; + void OnClick(InputManager* inputManager) + { + if (ScriptingEngineNet::Get().HasCustomWidgetInstance(scriptingId)) + { + ScriptingEngineNet::Get().GetCustomWidgetInstance(scriptingId).InvokeMethod("OnClick"); + } + }; + void OnTick(InputManager* manager) {}; + void OnClickReleased(InputManager* inputManager) {}; + void OnScroll(InputManager* inputManager) {}; + + void SetScriptingID(const UUID& uuid) + { + scriptingId = uuid; + } bool HasFocus() const; void GrabFocus(); void ReleaseFocus(); void ApplyStyleProperties(std::map properties); - - void AddClass(const std::string& c) - { + + void AddClass(const std::string& c) + { bool containClass = false; for (auto& classe : Classes) { @@ -169,7 +188,7 @@ namespace NuakeUI i++; } - if(found) + if (found) Classes.erase(Classes.begin() + i); } @@ -237,4 +256,4 @@ namespace NuakeUI return false; } }; -} \ No newline at end of file +} diff --git a/Nuake/src/UI/Parsers/CanvasParser.cpp b/Nuake/src/UI/Parsers/CanvasParser.cpp index 24f3d992..8e120e97 100644 --- a/Nuake/src/UI/Parsers/CanvasParser.cpp +++ b/Nuake/src/UI/Parsers/CanvasParser.cpp @@ -1,303 +1,353 @@ #include "CanvasParser.h" - -#include "../Nodes/Canvas.h" - -#include "../Nodes/Text.h" -#include "../Nodes/Button.h" - -#include "../FileSystem.h" #include "StyleSheetParser.h" -#include "../StringHelper.h" + +#include "src/FileSystem/FileSystem.h" +#include "src/UI/StringHelper.h" + +// Built-in widgets +#include "src/UI/Nodes/Canvas.h" +#include "src/UI/Nodes/Text.h" +#include "src/UI/Nodes/Button.h" + +#include "src/Scripting/ScriptingEngineNet.h" #include #include #include -namespace NuakeUI + +using namespace NuakeUI; + +CanvasParser::CanvasParser() { - CanvasParser::CanvasParser() + // Built-in node types + RegisterNodeType("div", Node::New); + RegisterNodeType("text", Text::New); + RegisterNodeType("button", Button::New); +} + +void CanvasParser::RegisterNodeType(const std::string& name, refNew refConstructor) +{ + NodeTypes[name] = refConstructor; +} + +bool CanvasParser::HasNodeType(const std::string& name) const +{ + return NodeTypes.find(name) != NodeTypes.end(); +} + +refNew CanvasParser::GetNodeType(const std::string& name) const +{ + if (HasNodeType(name)) { - RegisterNodeType("div", Node::New); - RegisterNodeType("text", Text::New); - RegisterNodeType("button", Button::New); + return NodeTypes.at(name); } - void CanvasParser::RegisterNodeType(const std::string& name, refNew refConstructor) - { - NodeTypes[name] = refConstructor; - } + return nullptr; +} - bool CanvasParser::HasNodeType(const std::string& name) const - { - return NodeTypes.find(name) != NodeTypes.end(); - } +NodePtr CanvasParser::CreateNodeFromXML(tinyxml2::XMLElement* xml, const std::string& id) +{ + NodePtr newNode; + std::string nodeId = id; + std::string type = xml->Value(); + std::string text = xml->GetText() ? xml->GetText() : ""; - refNew CanvasParser::GetNodeType(const std::string& name) const + // Check if built-in uiWidget or custom one + if (HasNodeType(type)) { - if (HasNodeType(name)) + newNode = GetNodeType(type)(nodeId, text); + newNode->Type = type; + + if (!newNode->HasBeenInitialized()) { - return NodeTypes.at(name); + newNode->InitializeNode(); } - - return nullptr; } - - NodePtr CanvasParser::CreateNodeFromXML(tinyxml2::XMLElement* xml, const std::string& id) + else if(ScriptingEngineNet::Get().HasUIWidget(type)) { - NodePtr newNode; - std::string nodeId = id; - std::string type = xml->Value(); - std::string text = xml->GetText() ? xml->GetText() : ""; + // Look in C# if the user has defined custom widgets + auto widget = ScriptingEngineNet::Get().GetUIWidget(type); + } + + return newNode; +} - if (HasNodeType(type)) +void CanvasParser::AddClassesToNode(tinyxml2::XMLElement* e, NodePtr node) +{ + auto classAttribute = e->FindAttribute("class"); + if (!classAttribute) + return; + + std::string strClasses = classAttribute->Value(); + node->Classes = StringHelper::Split(strClasses, ' '); +} + +void CanvasParser::WriteValueFromString(std::variant& var, const std::string& str) +{ + // Determine type of value + if (str.find("'") != std::string::npos) + { + // Removing second bracket + const auto& stringSplit = StringHelper::Split(str, "'"); + var = stringSplit[1]; + } + else if (str.find(".") != std::string::npos) + { + const auto& begin = str.data(); + const auto& end = begin + std::size(str); + float rightFloat; + std::from_chars(begin, end, rightFloat); + var = rightFloat; + } + else if (str.find("true") != std::string::npos) + { + var = true; + } + else if (str.find("false") != std::string::npos) + { + var = false; + } + else + { + const auto& begin = str.data(); + const auto& end = begin + std::size(str); + int rightInt; + std::from_chars(begin, end, rightInt); + var = rightInt; + } +} + +void CanvasParser::AddModelIfToNode(tinyxml2::XMLElement* e, NodePtr node) +{ + if (auto modelIf = e->FindAttribute("if"); modelIf) + { + std::string ifCondition = modelIf->Value(); + ifCondition = StringHelper::RemoveChar(ifCondition, ' '); + + ComparaisonType compType = ComparaisonType::None; + std::vector splits; + + const std::vector operators { "==", "!=", ">=", "<=", ">", "<" }; + for (auto i = 0; i < std::size(operators); i++) { - newNode = GetNodeType(type)(nodeId, text); - newNode->Type = type; - - if (!newNode->HasBeenInitialized()) + std::string operatorString = operators[i]; + if (ifCondition.find(operatorString) != std::string::npos) { - newNode->InitializeNode(); + compType = (ComparaisonType)i; + splits = StringHelper::Split(ifCondition, operatorString); } } - - return newNode; - } - void CanvasParser::AddClassesToNode(tinyxml2::XMLElement* e, NodePtr node) - { - auto classAttribute = e->FindAttribute("class"); - if (!classAttribute) + if (std::size(splits) < 2 || compType == ComparaisonType::None) return; - std::string strClasses = classAttribute->Value(); - node->Classes = StringHelper::Split(strClasses, ' '); + auto operation = DataModelOperation::New(splits[0], OperationType::If, compType); + WriteValueFromString(operation->Right, splits[1]); + + node->AddDataModelOperation(operation); } +} - void CanvasParser::WriteValueFromString(std::variant& var, const std::string& str) - { - // Determine type of value - if (str.find("'") != std::string::npos) - { - // Removing second bracket - const auto& stringSplit = StringHelper::Split(str, "'"); - var = stringSplit[1]; - } - else if (str.find(".") != std::string::npos) - { - const auto& begin = str.data(); - const auto& end = begin + std::size(str); - float rightFloat; - std::from_chars(begin, end, rightFloat); - var = rightFloat; - } - else if (str.find("true") != std::string::npos) - { - var = true; - } - else if (str.find("false") != std::string::npos) - { - var = false; - } - else - { - const auto& begin = str.data(); - const auto& end = begin + std::size(str); - int rightInt; - std::from_chars(begin, end, rightInt); - var = rightInt; - } - } +void CanvasParser::AddModelClasses(tinyxml2::XMLElement* e, NodePtr node) +{ + auto currentAttribute = e->FirstAttribute(); - void CanvasParser::AddModelIfToNode(tinyxml2::XMLElement* e, NodePtr node) + while (currentAttribute) { - if (auto modelIf = e->FindAttribute("if"); modelIf) + std::string attributeName = currentAttribute->Name(); + if (attributeName == "modelClass") { - std::string ifCondition = modelIf->Value(); - ifCondition = StringHelper::RemoveChar(ifCondition, ' '); + std::string attributeValue = currentAttribute->Value(); + auto attributeValueSplits = StringHelper::Split(attributeValue, ':'); + + if (std::size(attributeValueSplits) < 2) + continue; + + std::string className = StringHelper::RemoveChar(attributeValueSplits[0], '['); + className = StringHelper::RemoveChar(className, ']'); ComparaisonType compType = ComparaisonType::None; std::vector splits; - const std::vector operators { "==", "!=", ">=", "<=", ">", "<" }; + // TODO: Move to another reusable method. + const std::vector operators{ "==", "!=", ">=", "<=", ">", "<" }; for (auto i = 0; i < std::size(operators); i++) { std::string operatorString = operators[i]; - if (ifCondition.find(operatorString) != std::string::npos) + std::string logicalExpression = attributeValueSplits[1]; + if (logicalExpression.find(operatorString) != std::string::npos) { compType = (ComparaisonType)i; - splits = StringHelper::Split(ifCondition, operatorString); + splits = StringHelper::Split(logicalExpression, operatorString); } } if (std::size(splits) < 2 || compType == ComparaisonType::None) return; - auto operation = DataModelOperation::New(splits[0], OperationType::If, compType); - WriteValueFromString(operation->Right, splits[1]); - + std::string dataProp = StringHelper::RemoveChar(splits[0], ' '); + auto operation = DataModelOperation::New(dataProp, OperationType::IfClass, compType); + WriteValueFromString(operation->Right, StringHelper::RemoveChar(splits[1], ' ')); + operation->ClassName = className; node->AddDataModelOperation(operation); } + currentAttribute = currentAttribute->Next(); } +} - void CanvasParser::AddModelClasses(tinyxml2::XMLElement* e, NodePtr node) +void CanvasParser::ScanFragment(tinyxml2::XMLElement* e, NodePtr node) +{ + // We have a with a src path. + const std::string nodeType = e->Value(); + if (nodeType == "fragment") { - auto currentAttribute = e->FirstAttribute(); - - while (currentAttribute) + if (auto srcAttr = e->FindAttribute("src"); srcAttr) { - std::string attributeName = currentAttribute->Name(); - if (attributeName == "modelClass") + std::string fragmentPath = _parsingPath + "/../" + srcAttr->Value(); + if (FileSystem::FileExists(fragmentPath)) { - std::string attributeValue = currentAttribute->Value(); - auto attributeValueSplits = StringHelper::Split(attributeValue, ':'); - - if (std::size(attributeValueSplits) < 2) - continue; - - std::string className = StringHelper::RemoveChar(attributeValueSplits[0], '['); - className = StringHelper::RemoveChar(className, ']'); - - ComparaisonType compType = ComparaisonType::None; - std::vector splits; - - // TODO: Move to another reusable method. - const std::vector operators{ "==", "!=", ">=", "<=", ">", "<" }; - for (auto i = 0; i < std::size(operators); i++) + tinyxml2::XMLDocument doc; + if (tinyxml2::XMLError error = doc.LoadFile(fragmentPath.c_str())) { - std::string operatorString = operators[i]; - std::string logicalExpression = attributeValueSplits[1]; - if (logicalExpression.find(operatorString) != std::string::npos) - { - compType = (ComparaisonType)i; - splits = StringHelper::Split(logicalExpression, operatorString); - } + doc.PrintError(); } - if (std::size(splits) < 2 || compType == ComparaisonType::None) - return; - - std::string dataProp = StringHelper::RemoveChar(splits[0], ' '); - auto operation = DataModelOperation::New(dataProp, OperationType::IfClass, compType); - WriteValueFromString(operation->Right, StringHelper::RemoveChar(splits[1], ' ')); - operation->ClassName = className; - node->AddDataModelOperation(operation); + auto firstNode = doc.FirstChildElement(); + IterateOverElement(firstNode, node); } - currentAttribute = currentAttribute->Next(); - } - } - - void CanvasParser::ScanFragment(tinyxml2::XMLElement* e, NodePtr node) - { - // We have a with a src path. - const std::string nodeType = e->Value(); - if (nodeType == "fragment") - { - if (auto srcAttr = e->FindAttribute("src"); srcAttr) + else { - std::string fragmentPath = _parsingPath + "/../" + srcAttr->Value(); - if (FileSystem::FileExists(fragmentPath)) - { - tinyxml2::XMLDocument doc; - if (tinyxml2::XMLError error = doc.LoadFile(fragmentPath.c_str())) - { - doc.PrintError(); - } - - auto firstNode = doc.FirstChildElement(); - IterateOverElement(firstNode, node); - } - else - { - std::cout << "Fragment src attributes error. Cant find file at: " << fragmentPath << std::endl; - } + std::cout << "Fragment src attributes error. Cant find file at: " << fragmentPath << std::endl; } } } +} - void CanvasParser::IterateOverElement(tinyxml2::XMLElement* e, NodePtr node) +void CanvasParser::ScanCustomWidgets(tinyxml2::XMLElement* e, NodePtr node) +{ + const std::string nodeTypeName = e->Value(); + if (!ScriptingEngineNet::Get().HasUIWidget(nodeTypeName)) { - tinyxml2::XMLElement* current = e; - while (current) + return; + } + + // Is template file valid? + UIWidgetObject& customWidget = ScriptingEngineNet::Get().GetUIWidget(nodeTypeName); + if (!FileSystem::FileExists(customWidget.htmlPath)) + { + Logger::Log("Custom widget html file doesnt exist: " + nodeTypeName + " with HTML path: " + customWidget.htmlPath, "ui", CRITICAL); + return; + } + + // Allow to link between C# script and node using a UUID + UUID scriptingId = UUID(); + node->SetScriptingID(scriptingId); + customWidgetIDs.push_back(std::make_pair(scriptingId, nodeTypeName)); + + // Parse load HTML file now + const std::string& absoluteFilePath = FileSystem::RelativeToAbsolute(customWidget.htmlPath); + tinyxml2::XMLDocument doc; + if (tinyxml2::XMLError error = doc.LoadFile(absoluteFilePath.c_str())) + { + doc.PrintError(); + } + + // Let's parse the file + auto firstNode = doc.FirstChildElement(); + IterateOverElement(firstNode, node); +} + +void CanvasParser::IterateOverElement(tinyxml2::XMLElement* e, NodePtr node) +{ + tinyxml2::XMLElement* current = e; + while (current) + { + std::string id = "Node"; + + // Look if the node has an id. + auto idAttribute = current->FindAttribute("id"); + if (idAttribute) { - std::string id = "Node"; + id = idAttribute->Value(); + } - // Look if the node has an id. - auto idAttribute = current->FindAttribute("id"); - if (idAttribute) - { - id = idAttribute->Value(); - } + // Let's keep fragments for now as they remove + // the need to create a C# class for simple templating. + ScanFragment(current, node); + + // Let's add custom widgets to the DOM. + ScanCustomWidgets(current, node); - ScanFragment(current, node); - + NodePtr newNode = CreateNodeFromXML(current, id); + if (newNode) + { + AddClassesToNode(current, newNode); + AddModelIfToNode(current, newNode); + AddModelClasses(current, newNode); - NodePtr newNode = CreateNodeFromXML(current, id); - if (newNode) - { - AddClassesToNode(current, newNode); - AddModelIfToNode(current, newNode); - AddModelClasses(current, newNode); + // Insert in the tree + node->InsertChild(newNode); - // Insert in the tree - node->InsertChild(newNode); + // Recursivity on the childs of the current node. + IterateOverElement(current->FirstChildElement(), newNode); + } - // Recursivity on the childs of the current node. - IterateOverElement(current->FirstChildElement(), newNode); - } + // Continue to the sibbling after going Depth first. + current = current->NextSiblingElement(); + } +} - // Continue to the sibbling after going Depth first. - current = current->NextSiblingElement(); +Ref CanvasParser::Parse(const std::string& path) +{ + customWidgetIDs.clear(); + + _parsingPath = path; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError error; + bool fileLoaded = false; + for (int i = 0; i < 5; ++i) { // Try 5 times + error = doc.LoadFile(path.c_str()); + if (error == tinyxml2::XML_SUCCESS) { + fileLoaded = true; + break; + } + else if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait before retrying } } - Ref CanvasParser::Parse(const std::string& path) + if (error) { - _parsingPath = path; + doc.PrintError(); + return nullptr; + } - tinyxml2::XMLDocument doc; - tinyxml2::XMLError error; - bool fileLoaded = false; - for (int i = 0; i < 5; ++i) { // Try 5 times - error = doc.LoadFile(path.c_str()); - if (error == tinyxml2::XML_SUCCESS) { - fileLoaded = true; - break; - } - else if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait before retrying - } - } + CanvasPtr canvas = Canvas::New(); + NodePtr root = Node::New("root"); - if (error) - { - doc.PrintError(); - return nullptr; - } - - CanvasPtr canvas = Canvas::New(); - NodePtr root = Node::New("root"); - - auto firstNode = doc.FirstChildElement(); - if (!firstNode) - { - return canvas; - } - - // Look for stylesheet attribute in root. - auto styleSheet = firstNode->FindAttribute("stylesheet"); - if (styleSheet) - { - std::string relativePath = path + "/../" + styleSheet->Value(); - if (FileSystem::FileExists(relativePath)) - { - auto styleSheet = StyleSheetParser::Get().Parse(relativePath); - canvas->SetStyleSheet(styleSheet); - } - } - - IterateOverElement(firstNode, root); - - canvas->SetRoot(root); + auto firstNode = doc.FirstChildElement(); + if (!firstNode) + { return canvas; } -} \ No newline at end of file + + // Look for stylesheet attribute in root. + auto styleSheet = firstNode->FindAttribute("stylesheet"); + if (styleSheet) + { + std::string relativePath = path + "/../" + styleSheet->Value(); + if (FileSystem::FileExists(relativePath)) + { + auto styleSheet = StyleSheetParser::Get().Parse(relativePath); + canvas->SetStyleSheet(styleSheet); + } + } + + IterateOverElement(firstNode, root); + + canvas->SetRoot(root); + + return canvas; +} diff --git a/Nuake/src/UI/Parsers/CanvasParser.h b/Nuake/src/UI/Parsers/CanvasParser.h index 7feb6c32..af1e9a42 100644 --- a/Nuake/src/UI/Parsers/CanvasParser.h +++ b/Nuake/src/UI/Parsers/CanvasParser.h @@ -1,5 +1,6 @@ #pragma once #include "src/Core/Core.h" +#include "src/Resource/UUID.h" #include "../Nodes/Canvas.h" #include @@ -18,11 +19,20 @@ namespace NuakeUI private: std::map NodeTypes; std::string _parsingPath; + std::vector> customWidgetIDs; public: + static CanvasParser& Get() + { + static CanvasParser instance; + return instance; + } + + private: CanvasParser(); ~CanvasParser() = default; + public: /// /// Register a custom node type. /// @@ -33,8 +43,13 @@ namespace NuakeUI refNew GetNodeType(const std::string& name) const; Ref Parse(const std::string& file); + + std::vector> GetAllCustomWidgetInstance() { return customWidgetIDs; } + private: void ScanFragment(tinyxml2::XMLElement* e, NodePtr node); + void ScanCustomWidgets(tinyxml2::XMLElement* e, NodePtr node); + void WriteValueFromString(std::variant& var, const std::string& str); void IterateOverElement(tinyxml2::XMLElement* e, NodePtr node); NodePtr CreateNodeFromXML(tinyxml2::XMLElement* xml, const std::string& id = "Node"); diff --git a/Nuake/src/UI/Parsers/StyleSheetParser.cpp b/Nuake/src/UI/Parsers/StyleSheetParser.cpp index 0c84240c..17c09fc2 100644 --- a/Nuake/src/UI/Parsers/StyleSheetParser.cpp +++ b/Nuake/src/UI/Parsers/StyleSheetParser.cpp @@ -1,374 +1,374 @@ #include "StyleSheetParser.h" +#include "src/FileSystem/FileSystem.h" + #include +#include #include -#include "../FileSystem.h" -#include -namespace NuakeUI +using namespace NuakeUI; + +std::shared_ptr StyleSheetParser::Parse(const std::string& path) { - std::shared_ptr StyleSheetParser::Parse(const std::string& path) + assert(FileSystem::FileExists(path)); + + _parsingPath = path; + + std::string fileContent = FileSystem::ReadFile(path); + auto data = katana_parse(fileContent.c_str(), fileContent.length(), KatanaParserModeStylesheet); + + auto styleSheet = StyleSheet::New(); + // Print out errors. + if (data->errors.length > 0) { - assert(FileSystem::FileExists(path)); - - _parsingPath = path; - - std::string fileContent = FileSystem::ReadFile(path); - auto data = katana_parse(fileContent.c_str(), fileContent.length(), KatanaParserModeStylesheet); - - auto styleSheet = StyleSheet::New(); - // Print out errors. - if (data->errors.length > 0) + KatanaArray errors = data->errors; + for (uint32_t i = 0; i < errors.length; i++) { - KatanaArray errors = data->errors; - for (uint32_t i = 0; i < errors.length; i++) - { - KatanaError* error = (KatanaError*)errors.data[i]; - std::cout << "Failed to parse css file \"" + path + "\"." << std::endl; - std::cout << "Error is " << error->message << std::endl; - std::cout << "ERROR at line " + std::to_string(error->first_line) + - " : " + std::to_string(error->first_column) << std::endl; - } - return styleSheet; + KatanaError* error = (KatanaError*)errors.data[i]; + std::cout << "Failed to parse css file \"" + path + "\"." << std::endl; + std::cout << "Error is " << error->message << std::endl; + std::cout << "ERROR at line " + std::to_string(error->first_line) + + " : " + std::to_string(error->first_column) << std::endl; } - else - { - ParseRules(data->stylesheet, styleSheet); - } - - _visitedFiles.clear(); - return styleSheet; } - - bool StyleSheetParser::FileAlreadyVisited(const std::string& path) + else { - return std::find(_visitedFiles.begin(), _visitedFiles.end(), path) != _visitedFiles.end(); - } - - void StyleSheetParser::ParseRules(KatanaStylesheet* katanaStylesheet, StyleSheetPtr stylesheet) - { - // Import files first - auto imports = katanaStylesheet->imports; - for (uint32_t i = 0; i < imports.length; i++) - { - KatanaImportRule* importRule = static_cast(imports.data[i]); - ParseImportRule(importRule, stylesheet); - } - - // Parse generic rules - auto rules = katanaStylesheet->rules; - for (uint32_t i = 0; i < rules.length; i++) - { - KatanaRule* rule = (KatanaRule*)rules.data[i]; - - auto ruleType = rule->type; - switch (ruleType) - { - case KatanaRuleStyle: // Not sure if needed. - ParseStyleRule(rule, stylesheet); - break; - } - } - } - - void StyleSheetParser::ParseImportRule(KatanaImportRule* rule, StyleSheetPtr styleSheet) - { - std::string path = rule->href; - - if (FileAlreadyVisited(path)) - { - std::cout << "Cyclic file import detected! " << "File is: " << path << std::endl; - return; - } - - _visitedFiles.push_back(path); - - if (!FileSystem::FileExists(path)) - { - std::cout << "CSS Import rule error: Cannot find file: " << path << std::endl; - return; - } - - std::string fileContent = FileSystem::ReadFile(path); - auto data = katana_parse(fileContent.c_str(), fileContent.length(), KatanaParserModeStylesheet); - - if (data->errors.length > 0) - { - KatanaArray errors = data->errors; - for (uint32_t i = 0; i < errors.length; i++) - { - KatanaError* error = (KatanaError*)errors.data[i]; - std::cout << "Failed to parse css file \"" + path + "\"." << std::endl; - std::cout << "Error is " << error->message << std::endl; - std::cout << "ERROR at line " + std::to_string(error->first_line) + - " : " + std::to_string(error->first_column) << std::endl; - } - - return; - } - ParseRules(data->stylesheet, styleSheet); } + + _visitedFiles.clear(); - StyleProperties GetPropFromString(const std::string& prop) + return styleSheet; +} + +bool StyleSheetParser::FileAlreadyVisited(const std::string& path) +{ + return std::find(_visitedFiles.begin(), _visitedFiles.end(), path) != _visitedFiles.end(); +} + +void StyleSheetParser::ParseRules(KatanaStylesheet* katanaStylesheet, StyleSheetPtr stylesheet) +{ + // Import files first + auto imports = katanaStylesheet->imports; + for (uint32_t i = 0; i < imports.length; i++) { - if (prop == "height") return StyleProperties::Height; - else if (prop == "max-height") return StyleProperties::MaxHeight; - else if (prop == "min-height") return StyleProperties::MinHeight; - else if (prop == "width") return StyleProperties::Width; - else if (prop == "max-width") return StyleProperties::MaxWidth; - else if (prop == "min-width") return StyleProperties::MinWidth; - else if (prop == "padding-left") return StyleProperties::PaddingLeft; - else if (prop == "padding-right") return StyleProperties::PaddingRight; - else if (prop == "padding-top") return StyleProperties::PaddingTop; - else if (prop == "padding-bottom") return StyleProperties::PaddingBottom; - else if (prop == "margin-left") return StyleProperties::MarginLeft; - else if (prop == "margin-right") return StyleProperties::MarginRight; - else if (prop == "margin-top") return StyleProperties::MarginTop; - else if (prop == "margin-bottom") return StyleProperties::MarginBottom; - else if (prop == "position") return StyleProperties::Position; - else if (prop == "align-items") return StyleProperties::AlignItems; - else if (prop == "self-align") return StyleProperties::SelfAlign; - else if (prop == "aspect-ratio") return StyleProperties::AspectRatio; - else if (prop == "flex-direction") return StyleProperties::FlexDirection; - else if (prop == "flex-wrap") return StyleProperties::FlexWrap; - else if (prop == "flex-basis") return StyleProperties::FlexBasis; - else if (prop == "flex-grow") return StyleProperties::FlexGrow; - else if (prop == "flex-shrink") return StyleProperties::FlexShrink; - else if (prop == "justify-content") return StyleProperties::JustifyContent; - else if (prop == "align-content") return StyleProperties::AlignContent; - else if (prop == "layout-direction") return StyleProperties::LayoutDirection; - else if (prop == "border-size") return StyleProperties::BorderSize; - else if (prop == "border-radius") return StyleProperties::BorderRadius; - else if (prop == "border-color") return StyleProperties::BorderColor; - else if (prop == "background-color") return StyleProperties::BackgroundColor; - else if (prop == "text-align") return StyleProperties::TextAlign; - else if (prop == "color") return StyleProperties::Color; - else if (prop == "overflow") return StyleProperties::Overflow; - else if (prop == "font-size") return StyleProperties::FontSize; - else if (prop == "visibility") return StyleProperties::Visibility; - else if (prop == "z-index") return StyleProperties::ZIndex; - else if (prop == "top") return StyleProperties::Top; - else if (prop == "bottom") return StyleProperties::Bottom; - else if (prop == "left") return StyleProperties::Left; - else if (prop == "right") return StyleProperties::Right; - else if (prop == "background-image") return StyleProperties::BackgroundImage; - return StyleProperties::None; + KatanaImportRule* importRule = static_cast(imports.data[i]); + ParseImportRule(importRule, stylesheet); } - void StyleSheetParser::ParseStyleRule(KatanaRule* rule, StyleSheetPtr styleSheet) + // Parse generic rules + auto rules = katanaStylesheet->rules; + for (uint32_t i = 0; i < rules.length; i++) { - auto styleRule = reinterpret_cast(rule); - std::string styleName = rule->name; - - for (uint32_t s = 0; s < styleRule->selectors->length; s++) + KatanaRule* rule = (KatanaRule*)rules.data[i]; + + auto ruleType = rule->type; + switch (ruleType) { - auto styleSelector = std::vector(); - - // unsafe c-style void* in the array. - void* selectorData = styleRule->selectors->data[s]; - auto selector = reinterpret_cast(selectorData); - while (selector) - { - auto match = selector->match; // tag, id or class - switch (match) - { - case KatanaSelectorMatchPseudoClass: - { - std::string matchPseudo = selector->data->value; - styleSelector.push_back({ StyleSelectorType::Pseudo, matchPseudo }); - } - break; - case KatanaSelectorMatchTag: - { - std::string matchTag = selector->tag->local; - styleSelector.push_back({ StyleSelectorType::Tag, matchTag }); - } - break; - case KatanaSelectorMatchId: - { - std::string matchId = selector->data->value; - styleSelector.push_back({ StyleSelectorType::Id, matchId }); - } - break; - case KatanaSelectorMatchClass: - { - std::string matchClass = selector->data->value; - styleSelector.push_back({ StyleSelectorType::Class, matchClass }); - } - break; - } - - selector = selector->tagHistory; - } - - // Added the new rule with selectors. - auto newRule = StyleRule(styleSelector); - - // Now add the properties to the new rule. - for (uint32_t d = 0; d < styleRule->declarations->length; d++) - { - // unsafe c-style void* in the array. - void* declarationData = styleRule->declarations->data[d]; - auto declaration = reinterpret_cast(declarationData); - - // convert from string to property enum. - StyleProperties propType = GetPropFromString(declaration->property); - - PropValue propValue{}; - for (uint32_t v = 0; v < declaration->values->length; v++) - { - // unsafe c-style voir* in the array. - void* valueData = declaration->values->data[v]; - KatanaValue* value = reinterpret_cast(valueData); - - switch (value->unit) - { - case KatanaValueUnit::KATANA_VALUE_STRING: - { - std::string stringValue = value->string; - if (propType == StyleProperties::BackgroundImage) - { - stringValue = _parsingPath + "/../" + stringValue; - } - propValue.string = stringValue; - propValue.type = PropValueType::String; - } - break; - case KatanaValueUnit::KATANA_VALUE_PERCENTAGE: - case KatanaValueUnit::KATANA_VALUE_PX: - { - propValue.value.Number = (float)value->fValue; - propValue.type = value->unit == KatanaValueUnit::KATANA_VALUE_PX ? PropValueType::Pixel : PropValueType::Percent; - } - break; - case KatanaValueUnit::KATANA_VALUE_PARSER_HEXCOLOR: - { - int r, g, b, a = 255; - int result = sscanf_s(value->string, "%02x%02x%02x%02x", &r, &g, &b, &a); - propValue.value.Color = Color(r, g, b, a); - propValue.type = PropValueType::Color; - } - break; - case KatanaValueUnit::KATANA_VALUE_UNKNOWN: - { - std::string valueStr = value->string; - } - break; - case KatanaValueUnit::KATANA_VALUE_NUMBER: - propValue.value.Number = (int)value->fValue; - break; - case KatanaValueUnit::KATANA_VALUE_IDENT: - { - std::string valueStr = value->string; - if (propType == StyleProperties::Position) - { - PositionType positionType = PositionType::Relative; - if (valueStr == "absolute") - propValue.value.Enum = (int)PositionType::Absolute; - } - if (propType == StyleProperties::AlignContent) - { - AlignContentType align; - if (valueStr == "flex-start") align = AlignContentType::FlexStart; - else if (valueStr == "center") align = AlignContentType::Center; - else if (valueStr == "flex-end") align = AlignContentType::FlexEnd; - else if (valueStr == "stretch") align = AlignContentType::Stretch; - else if (valueStr == "space-between") align = AlignContentType::SpaceBetween; - else if (valueStr == "space-around") align = AlignContentType::SpaceAround; - else align = AlignContentType::FlexStart; - - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)align; - } - else if (propType == StyleProperties::AlignItems || propType == StyleProperties::SelfAlign) - { - AlignItemsType align; - if (valueStr == "flex-start") align = AlignItemsType::FlexStart; - else if (valueStr == "center") align = AlignItemsType::Center; - else if (valueStr == "flex-end") align = AlignItemsType::FlexEnd; - else if (valueStr == "stretch") align = AlignItemsType::Stretch; - else if (valueStr == "space-between") align = AlignItemsType::SpaceBetween; - else if (valueStr == "space-around") align = AlignItemsType::SpaceAround; - else align = AlignItemsType::FlexStart; - - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)align; - } - else if (propType == StyleProperties::FlexDirection) - { - FlexDirectionType direction = FlexDirectionType::Row; - if (valueStr == "column") direction = FlexDirectionType::Column; - else if (valueStr == "row-reversed") direction = FlexDirectionType::RowReversed; - else if (valueStr == "column-reversed") direction = FlexDirectionType::ColumnReversed; - - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)direction; - } - else if (propType == StyleProperties::FlexWrap) - { - FlexWrapType type = FlexWrapType::Wrap; - if (valueStr == "no-wrap") type = FlexWrapType::NoWrap; - else if (valueStr == "wrap-reversed") type = FlexWrapType::WrapReversed; - - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)type; - } - else if (propType == StyleProperties::JustifyContent) - { - auto justify = JustifyContentType::FlexStart; - if (valueStr == "center") justify = JustifyContentType::Center; - else if (valueStr == "flex-end") justify = JustifyContentType::FlexEnd; - else if (valueStr == "space-around") justify = JustifyContentType::SpaceAround; - else if (valueStr == "space-between") justify = JustifyContentType::SpaceBetween; - else if (valueStr == "space-evenly") justify = JustifyContentType::SpaceEvenly; - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)justify; - } - else if (propType == StyleProperties::LayoutDirection) - { - auto direction = LayoutDirectionType::LTR; - if (valueStr == "RTL") direction = LayoutDirectionType::RTL; - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)direction; - } - else if (propType == StyleProperties::TextAlign) - { - auto align = TextAlignType::Left; - if (valueStr == "center") align = TextAlignType::Center; - if (valueStr == "right") align = TextAlignType::Right; - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)align; - } - else if (propType == StyleProperties::Overflow) - { - OverflowType overflow = OverflowType::Show; - if (valueStr == "hidden") overflow = OverflowType::Hidden; - else if (valueStr == "show") overflow = OverflowType::Show; - else if (valueStr == "scroll") overflow = OverflowType::Scroll; - - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)overflow; - } - else if (propType == StyleProperties::Visibility) - { - VisibilityType visibility = VisibilityType::Show; - if (valueStr == "hidden") visibility = VisibilityType::Hidden; - - propValue.type = PropValueType::Enum; - propValue.value.Enum = (int)visibility; - } - } - break; - } - } - - newRule.SetProp(propType, propValue); - } - - styleSheet->Rules.push_back(newRule); + case KatanaRuleStyle: // Not sure if needed. + ParseStyleRule(rule, stylesheet); + break; } } -} \ No newline at end of file +} + +void StyleSheetParser::ParseImportRule(KatanaImportRule* rule, StyleSheetPtr styleSheet) +{ + std::string path = rule->href; + + if (FileAlreadyVisited(path)) + { + std::cout << "Cyclic file import detected! " << "File is: " << path << std::endl; + return; + } + + _visitedFiles.push_back(path); + + if (!FileSystem::FileExists(path)) + { + std::cout << "CSS Import rule error: Cannot find file: " << path << std::endl; + return; + } + + std::string fileContent = FileSystem::ReadFile(path); + auto data = katana_parse(fileContent.c_str(), fileContent.length(), KatanaParserModeStylesheet); + + if (data->errors.length > 0) + { + KatanaArray errors = data->errors; + for (uint32_t i = 0; i < errors.length; i++) + { + KatanaError* error = (KatanaError*)errors.data[i]; + std::cout << "Failed to parse css file \"" + path + "\"." << std::endl; + std::cout << "Error is " << error->message << std::endl; + std::cout << "ERROR at line " + std::to_string(error->first_line) + + " : " + std::to_string(error->first_column) << std::endl; + } + + return; + } + + ParseRules(data->stylesheet, styleSheet); +} + +StyleProperties GetPropFromString(const std::string& prop) +{ + if (prop == "height") return StyleProperties::Height; + else if (prop == "max-height") return StyleProperties::MaxHeight; + else if (prop == "min-height") return StyleProperties::MinHeight; + else if (prop == "width") return StyleProperties::Width; + else if (prop == "max-width") return StyleProperties::MaxWidth; + else if (prop == "min-width") return StyleProperties::MinWidth; + else if (prop == "padding-left") return StyleProperties::PaddingLeft; + else if (prop == "padding-right") return StyleProperties::PaddingRight; + else if (prop == "padding-top") return StyleProperties::PaddingTop; + else if (prop == "padding-bottom") return StyleProperties::PaddingBottom; + else if (prop == "margin-left") return StyleProperties::MarginLeft; + else if (prop == "margin-right") return StyleProperties::MarginRight; + else if (prop == "margin-top") return StyleProperties::MarginTop; + else if (prop == "margin-bottom") return StyleProperties::MarginBottom; + else if (prop == "position") return StyleProperties::Position; + else if (prop == "align-items") return StyleProperties::AlignItems; + else if (prop == "self-align") return StyleProperties::SelfAlign; + else if (prop == "aspect-ratio") return StyleProperties::AspectRatio; + else if (prop == "flex-direction") return StyleProperties::FlexDirection; + else if (prop == "flex-wrap") return StyleProperties::FlexWrap; + else if (prop == "flex-basis") return StyleProperties::FlexBasis; + else if (prop == "flex-grow") return StyleProperties::FlexGrow; + else if (prop == "flex-shrink") return StyleProperties::FlexShrink; + else if (prop == "justify-content") return StyleProperties::JustifyContent; + else if (prop == "align-content") return StyleProperties::AlignContent; + else if (prop == "layout-direction") return StyleProperties::LayoutDirection; + else if (prop == "border-size") return StyleProperties::BorderSize; + else if (prop == "border-radius") return StyleProperties::BorderRadius; + else if (prop == "border-color") return StyleProperties::BorderColor; + else if (prop == "background-color") return StyleProperties::BackgroundColor; + else if (prop == "text-align") return StyleProperties::TextAlign; + else if (prop == "color") return StyleProperties::Color; + else if (prop == "overflow") return StyleProperties::Overflow; + else if (prop == "font-size") return StyleProperties::FontSize; + else if (prop == "visibility") return StyleProperties::Visibility; + else if (prop == "z-index") return StyleProperties::ZIndex; + else if (prop == "top") return StyleProperties::Top; + else if (prop == "bottom") return StyleProperties::Bottom; + else if (prop == "left") return StyleProperties::Left; + else if (prop == "right") return StyleProperties::Right; + else if (prop == "background-image") return StyleProperties::BackgroundImage; + return StyleProperties::None; +} + +void StyleSheetParser::ParseStyleRule(KatanaRule* rule, StyleSheetPtr styleSheet) +{ + auto styleRule = reinterpret_cast(rule); + std::string styleName = rule->name; + + for (uint32_t s = 0; s < styleRule->selectors->length; s++) + { + auto styleSelector = std::vector(); + + // unsafe c-style void* in the array. + void* selectorData = styleRule->selectors->data[s]; + auto selector = reinterpret_cast(selectorData); + while (selector) + { + auto match = selector->match; // tag, id or class + switch (match) + { + case KatanaSelectorMatchPseudoClass: + { + std::string matchPseudo = selector->data->value; + styleSelector.push_back({ StyleSelectorType::Pseudo, matchPseudo }); + } + break; + case KatanaSelectorMatchTag: + { + std::string matchTag = selector->tag->local; + styleSelector.push_back({ StyleSelectorType::Tag, matchTag }); + } + break; + case KatanaSelectorMatchId: + { + std::string matchId = selector->data->value; + styleSelector.push_back({ StyleSelectorType::Id, matchId }); + } + break; + case KatanaSelectorMatchClass: + { + std::string matchClass = selector->data->value; + styleSelector.push_back({ StyleSelectorType::Class, matchClass }); + } + break; + } + + selector = selector->tagHistory; + } + + // Added the new rule with selectors. + auto newRule = StyleRule(styleSelector); + + // Now add the properties to the new rule. + for (uint32_t d = 0; d < styleRule->declarations->length; d++) + { + // unsafe c-style void* in the array. + void* declarationData = styleRule->declarations->data[d]; + auto declaration = reinterpret_cast(declarationData); + + // convert from string to property enum. + StyleProperties propType = GetPropFromString(declaration->property); + + PropValue propValue{}; + for (uint32_t v = 0; v < declaration->values->length; v++) + { + // unsafe c-style voir* in the array. + void* valueData = declaration->values->data[v]; + KatanaValue* value = reinterpret_cast(valueData); + + switch (value->unit) + { + case KatanaValueUnit::KATANA_VALUE_STRING: + { + std::string stringValue = value->string; + if (propType == StyleProperties::BackgroundImage) + { + stringValue = _parsingPath + "/../" + stringValue; + } + propValue.string = stringValue; + propValue.type = PropValueType::String; + } + break; + case KatanaValueUnit::KATANA_VALUE_PERCENTAGE: + case KatanaValueUnit::KATANA_VALUE_PX: + { + propValue.value.Number = (float)value->fValue; + propValue.type = value->unit == KatanaValueUnit::KATANA_VALUE_PX ? PropValueType::Pixel : PropValueType::Percent; + } + break; + case KatanaValueUnit::KATANA_VALUE_PARSER_HEXCOLOR: + { + int r, g, b, a = 255; + int result = sscanf_s(value->string, "%02x%02x%02x%02x", &r, &g, &b, &a); + propValue.value.Color = Color(r, g, b, a); + propValue.type = PropValueType::Color; + } + break; + case KatanaValueUnit::KATANA_VALUE_UNKNOWN: + { + std::string valueStr = value->string; + } + break; + case KatanaValueUnit::KATANA_VALUE_NUMBER: + propValue.value.Number = (int)value->fValue; + break; + case KatanaValueUnit::KATANA_VALUE_IDENT: + { + std::string valueStr = value->string; + if (propType == StyleProperties::Position) + { + PositionType positionType = PositionType::Relative; + if (valueStr == "absolute") + propValue.value.Enum = (int)PositionType::Absolute; + } + if (propType == StyleProperties::AlignContent) + { + AlignContentType align; + if (valueStr == "flex-start") align = AlignContentType::FlexStart; + else if (valueStr == "center") align = AlignContentType::Center; + else if (valueStr == "flex-end") align = AlignContentType::FlexEnd; + else if (valueStr == "stretch") align = AlignContentType::Stretch; + else if (valueStr == "space-between") align = AlignContentType::SpaceBetween; + else if (valueStr == "space-around") align = AlignContentType::SpaceAround; + else align = AlignContentType::FlexStart; + + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)align; + } + else if (propType == StyleProperties::AlignItems || propType == StyleProperties::SelfAlign) + { + AlignItemsType align; + if (valueStr == "flex-start") align = AlignItemsType::FlexStart; + else if (valueStr == "center") align = AlignItemsType::Center; + else if (valueStr == "flex-end") align = AlignItemsType::FlexEnd; + else if (valueStr == "stretch") align = AlignItemsType::Stretch; + else if (valueStr == "space-between") align = AlignItemsType::SpaceBetween; + else if (valueStr == "space-around") align = AlignItemsType::SpaceAround; + else align = AlignItemsType::FlexStart; + + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)align; + } + else if (propType == StyleProperties::FlexDirection) + { + FlexDirectionType direction = FlexDirectionType::Row; + if (valueStr == "column") direction = FlexDirectionType::Column; + else if (valueStr == "row-reversed") direction = FlexDirectionType::RowReversed; + else if (valueStr == "column-reversed") direction = FlexDirectionType::ColumnReversed; + + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)direction; + } + else if (propType == StyleProperties::FlexWrap) + { + FlexWrapType type = FlexWrapType::Wrap; + if (valueStr == "no-wrap") type = FlexWrapType::NoWrap; + else if (valueStr == "wrap-reversed") type = FlexWrapType::WrapReversed; + + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)type; + } + else if (propType == StyleProperties::JustifyContent) + { + auto justify = JustifyContentType::FlexStart; + if (valueStr == "center") justify = JustifyContentType::Center; + else if (valueStr == "flex-end") justify = JustifyContentType::FlexEnd; + else if (valueStr == "space-around") justify = JustifyContentType::SpaceAround; + else if (valueStr == "space-between") justify = JustifyContentType::SpaceBetween; + else if (valueStr == "space-evenly") justify = JustifyContentType::SpaceEvenly; + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)justify; + } + else if (propType == StyleProperties::LayoutDirection) + { + auto direction = LayoutDirectionType::LTR; + if (valueStr == "RTL") direction = LayoutDirectionType::RTL; + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)direction; + } + else if (propType == StyleProperties::TextAlign) + { + auto align = TextAlignType::Left; + if (valueStr == "center") align = TextAlignType::Center; + if (valueStr == "right") align = TextAlignType::Right; + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)align; + } + else if (propType == StyleProperties::Overflow) + { + OverflowType overflow = OverflowType::Show; + if (valueStr == "hidden") overflow = OverflowType::Hidden; + else if (valueStr == "show") overflow = OverflowType::Show; + else if (valueStr == "scroll") overflow = OverflowType::Scroll; + + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)overflow; + } + else if (propType == StyleProperties::Visibility) + { + VisibilityType visibility = VisibilityType::Show; + if (valueStr == "hidden") visibility = VisibilityType::Hidden; + + propValue.type = PropValueType::Enum; + propValue.value.Enum = (int)visibility; + } + } + break; + } + } + + newRule.SetProp(propType, propValue); + } + + styleSheet->Rules.push_back(newRule); + } +} diff --git a/Nuake/src/UI/UIInputManager.h b/Nuake/src/UI/UIInputManager.h index cddea33b..1e8e520e 100644 --- a/Nuake/src/UI/UIInputManager.h +++ b/Nuake/src/UI/UIInputManager.h @@ -24,7 +24,7 @@ namespace Nuake bool IsMouseInputDown() override { - return Input::IsMouseButtonDown(1); + return Input::IsMouseButtonDown(0); } bool IsKeyPressed(uint32_t key) override