diff options
Diffstat (limited to 'core/src')
104 files changed, 0 insertions, 12979 deletions
diff --git a/core/src/Entrypoint/Backend.hpp b/core/src/Entrypoint/Backend.hpp deleted file mode 100644 index ca391e6..0000000 --- a/core/src/Entrypoint/Backend.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include <memory> - -class RenderingBackend -{ -public: - // Implemented in Backend_OpenGL2.cpp - static std::unique_ptr<RenderingBackend> CreateOpenGL2Backend(); - // Implemented in Backend_OpenGL3.cpp - static std::unique_ptr<RenderingBackend> CreateOpenGL3Backend(); - // Implemented in Backend_Vulkan.cpp - static std::unique_ptr<RenderingBackend> CreateVulkanBackend(); - // Implemented in Backend_DirectX11.cpp - static std::unique_ptr<RenderingBackend> CreateDx11Backend(); - // Implemented in Backend_DirectX12.cpp - static std::unique_ptr<RenderingBackend> CreateDx12Backend(); - // Implemented in Backend_Metal.cpp - static std::unique_ptr<RenderingBackend> CreateMetalBackend(); - - virtual ~RenderingBackend() = default; - virtual void RunUntilWindowClose(void (*windowContent)()) = 0; -}; diff --git a/core/src/Entrypoint/Backend_DirectX11.cpp b/core/src/Entrypoint/Backend_DirectX11.cpp deleted file mode 100644 index 4dc33f7..0000000 --- a/core/src/Entrypoint/Backend_DirectX11.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include "Backend.hpp" - -#if BUILD_CORE_WITH_DX11_BACKEND -# include <backend/imgui_impl_dx11.h> -# include <backend/imgui_impl_dx11.cpp> -# include <stdexcept> -# include <d3d11.h> -# include <tchar.h> -# include <backend/imgui_impl_win32.h> - -// Forward declare message handler from imgui_impl_win32.cpp -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - -class DirectX11Backend : public RenderingBackend -{ -private: - HWND hWnd; - WNDCLASSEX wc; - - ID3D11Device* mD3dDevice = nullptr; - ID3D11DeviceContext* mD3dDeviceContext = nullptr; - IDXGISwapChain* mSwapChain = nullptr; - ID3D11RenderTargetView* mMainRenderTargetView = nullptr; - -public: - DirectX11Backend() - { - ImGui_ImplWin32_EnableDpiAwareness(); - - wc.cbSize = sizeof(WNDCLASSEX); - wc.style = CS_CLASSDC; - wc.lpfnWndProc = &StaticWndProc; - wc.cbClsExtra = 0L; - wc.cbWndExtra = 0L; - wc.hInstance = GetModuleHandle(nullptr); - wc.hIcon = nullptr; - wc.hCursor = nullptr; - wc.hbrBackground = nullptr; - wc.lpszMenuName = nullptr; - wc.lpszClassName = _T("Cplt"); - wc.hIconSm = nullptr; - ::RegisterClassEx(&wc); - - hWnd = ::CreateWindow( - wc.lpszClassName, - _T("Cplt main window"), - WS_OVERLAPPEDWINDOW, - /* x */ 100, - /* y */ 100, - /* window width */ 1280, - /* window height */ 800, - nullptr, - nullptr, - wc.hInstance, - this); - - if (!CreateDeviceD3D()) { - CleanupDeviceD3D(); - ::UnregisterClass(wc.lpszClassName, wc.hInstance); - throw std::runtime_error("Failed to create d3d device."); - } - - ::ShowWindow(hWnd, SW_SHOWDEFAULT); - ::UpdateWindow(hWnd); - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - - ImGui_ImplWin32_Init(hWnd); - ImGui_ImplDX11_Init(mD3dDevice, mD3dDeviceContext); - } - - virtual ~DirectX11Backend() - { - ImGui_ImplDX11_Shutdown(); - ImGui_ImplWin32_Shutdown(); - ImGui::DestroyContext(); - - CleanupDeviceD3D(); - ::DestroyWindow(hWnd); - ::UnregisterClass(wc.lpszClassName, wc.hInstance); - } - - virtual void RunUntilWindowClose(void (*windowContent)()) - { - while (true) { - MSG msg; - bool done = false; - while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - if (msg.message == WM_QUIT) { - done = true; - } - } - if (done) break; - - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - windowContent(); - - ImGui::Render(); - const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - const float kClearColorWithAlpha[4] = { kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w }; - mD3dDeviceContext->OMSetRenderTargets(1, &mMainRenderTargetView, nullptr); - mD3dDeviceContext->ClearRenderTargetView(mMainRenderTargetView, kClearColorWithAlpha); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - - mSwapChain->Present(1, 0); // Present with vsync - } - } - -private: - bool CreateDeviceD3D() - { - // Setup swap chain - DXGI_SWAP_CHAIN_DESC sd; - ZeroMemory(&sd, sizeof(sd)); - sd.BufferCount = 2; - sd.BufferDesc.Width = 0; - sd.BufferDesc.Height = 0; - sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - sd.BufferDesc.RefreshRate.Numerator = 60; - sd.BufferDesc.RefreshRate.Denominator = 1; - sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - sd.OutputWindow = hWnd; - sd.SampleDesc.Count = 1; - sd.SampleDesc.Quality = 0; - sd.Windowed = TRUE; - sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - - UINT createDeviceFlags = 0; - //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; - D3D_FEATURE_LEVEL featureLevel; - const D3D_FEATURE_LEVEL featureLevelArray[2] = { - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_0, - }; - if (D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &mSwapChain, &mD3dDevice, &featureLevel, &mD3dDeviceContext) != S_OK) { - return false; - } - - CreateRenderTarget(); - return true; - } - - void CleanupDeviceD3D() - { - CleanupRenderTarget(); - if (mSwapChain) { - mSwapChain->Release(); - mSwapChain = nullptr; - } - if (mD3dDeviceContext) { - mD3dDeviceContext->Release(); - mD3dDeviceContext = nullptr; - } - if (mD3dDevice) { - mD3dDevice->Release(); - mD3dDevice = nullptr; - } - } - - void CreateRenderTarget() - { - ID3D11Texture2D* pBackBuffer; - mSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - mD3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &mMainRenderTargetView); - pBackBuffer->Release(); - } - - void CleanupRenderTarget() - { - if (mMainRenderTargetView) { - mMainRenderTargetView->Release(); - mMainRenderTargetView = nullptr; - } - } - - static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - DirectX11Backend* self; - if (uMsg == WM_NCCREATE) { - auto lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); - self = static_cast<DirectX11Backend*>(lpcs->lpCreateParams); - self->hWnd = hWnd; - SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self)); - } else { - self = reinterpret_cast<DirectX11Backend*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)); - } - - if (self) { - return self->WndProc(uMsg, wParam, lParam); - } else { - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - } - - LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam) - { - if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) { - return true; - } - - switch (msg) { - case WM_SIZE: { - if (mD3dDevice != nullptr && wParam != SIZE_MINIMIZED) { - CleanupRenderTarget(); - mSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0); - CreateRenderTarget(); - } - return 0; - } - - case WM_SYSCOMMAND: { - // Disable ALT application menu - if ((wParam & 0xfff0) == SC_KEYMENU) { - return 0; - } - } break; - - case WM_DESTROY: { - ::PostQuitMessage(0); - return 0; - } - } - return ::DefWindowProc(hWnd, msg, wParam, lParam); - } -}; - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx11Backend() -{ - try { - return std::make_unique<DirectX11Backend>(); - } catch (std::exception& e) { - return nullptr; - } -} - -#else // ^^ BUILD_CORE_WITH_DX11_BACKEND | BUILD_CORE_WITH_DX11_BACKEND vv - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx11Backend() -{ - return nullptr; -} - -#endif diff --git a/core/src/Entrypoint/Backend_DirectX12.cpp b/core/src/Entrypoint/Backend_DirectX12.cpp deleted file mode 100644 index fd4a531..0000000 --- a/core/src/Entrypoint/Backend_DirectX12.cpp +++ /dev/null @@ -1,470 +0,0 @@ -#include "Backend.hpp" - -#if BUILD_CORE_WITH_DX12_BACKEND -# include <backend/imgui_impl_dx12.h> -# include <backend/imgui_impl_win32.h> -# include <d3d12.h> -# include <dxgi1_4.h> -# include <tchar.h> -# include <backend/imgui_impl_dx12.cpp> -# include <stdexcept> - -constexpr int kNumFramesInFlight = 3; -constexpr int kNumBackBuffers = 3; - -// Forward declare message handler from imgui_impl_win32.cpp -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - -class DirectX12Backend : public RenderingBackend -{ -private: - struct FrameContext - { - ID3D12CommandAllocator* CommandAllocator; - UINT64 FenceValue; - }; - - HWND hWnd; - WNDCLASSEX wc; - - FrameContext mFrameContext[kNumFramesInFlight] = {}; - UINT mFrameIndex = 0; - - ID3D12Device* mD3dDevice = nullptr; - ID3D12DescriptorHeap* mD3dRtvDescHeap = nullptr; - ID3D12DescriptorHeap* mD3dSrvDescHeap = nullptr; - ID3D12CommandQueue* mD3dCommandQueue = nullptr; - ID3D12GraphicsCommandList* mD3dCommandList = nullptr; - ID3D12Fence* mFence = nullptr; - HANDLE mFenceEvent = nullptr; - UINT64 mFenceLastSignaledValue = 0; - IDXGISwapChain3* mSwapChain = nullptr; - HANDLE mSwapChainWaitableObject = nullptr; - ID3D12Resource* mMainRenderTargetResource[kNumBackBuffers] = {}; - D3D12_CPU_DESCRIPTOR_HANDLE mMainRenderTargetDescriptor[kNumBackBuffers] = {}; - -public: - DirectX12Backend() - { - ImGui_ImplWin32_EnableDpiAwareness(); - - wc.cbSize = sizeof(WNDCLASSEX); - wc.style = CS_CLASSDC; - wc.lpfnWndProc = &StaticWndProc; - wc.cbClsExtra = 0L; - wc.cbWndExtra = 0L; - wc.hInstance = GetModuleHandle(nullptr); - wc.hIcon = nullptr; - wc.hCursor = nullptr; - wc.hbrBackground = nullptr; - wc.lpszMenuName = nullptr; - wc.lpszClassName = _T("Cplt"); - wc.hIconSm = nullptr; - ::RegisterClassEx(&wc); - - hWnd = ::CreateWindow( - wc.lpszClassName, - _T("Cplt main window"), - WS_OVERLAPPEDWINDOW, - /* x */ 100, - /* y */ 100, - /* window width */ 1280, - /* window height */ 800, - nullptr, - nullptr, - wc.hInstance, - this); - - if (!CreateDeviceD3D()) { - CleanupDeviceD3D(); - ::UnregisterClass(wc.lpszClassName, wc.hInstance); - throw std::runtime_error("Failed to create d3d device."); - } - - ::ShowWindow(hWnd, SW_SHOWDEFAULT); - ::UpdateWindow(hWnd); - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - - ImGui_ImplWin32_Init(hWnd); - ImGui_ImplDX12_Init(mD3dDevice, kNumFramesInFlight, DXGI_FORMAT_R8G8B8A8_UNORM, mD3dSrvDescHeap, mD3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), mD3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); - } - - virtual ~DirectX12Backend() - { - WaitForLastSubmittedFrame(); - - // Cleanup - ImGui_ImplDX12_Shutdown(); - ImGui_ImplWin32_Shutdown(); - ImGui::DestroyContext(); - - CleanupDeviceD3D(); - ::DestroyWindow(hWnd); - ::UnregisterClass(wc.lpszClassName, wc.hInstance); - } - - virtual void RunUntilWindowClose(void (*windowContent)()) - { - while (true) { - MSG msg; - bool done = false; - while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) - { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - if (msg.message == WM_QUIT) { - done = true; - } - } - if (done) break; - - // Start the Dear ImGui frame - ImGui_ImplDX12_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - windowContent(); - - ImGui::Render(); - - FrameContext* frameCtx = WaitForNextFrameResources(); - UINT backBufferIdx = mSwapChain->GetCurrentBackBufferIndex(); - frameCtx->CommandAllocator->Reset(); - - D3D12_RESOURCE_BARRIER barrier = {}; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = mMainRenderTargetResource[backBufferIdx]; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - mD3dCommandList->Reset(frameCtx->CommandAllocator, nullptr); - mD3dCommandList->ResourceBarrier(1, &barrier); - - const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - const float kClearColorWithAlpha[4] = { kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w }; - mD3dCommandList->ClearRenderTargetView(mMainRenderTargetDescriptor[backBufferIdx], kClearColorWithAlpha, 0, nullptr); - mD3dCommandList->OMSetRenderTargets(1, &mMainRenderTargetDescriptor[backBufferIdx], FALSE, nullptr); - mD3dCommandList->SetDescriptorHeaps(1, &mD3dSrvDescHeap); - ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), mD3dCommandList); - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - mD3dCommandList->ResourceBarrier(1, &barrier); - mD3dCommandList->Close(); - - mD3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&mD3dCommandList); - - mSwapChain->Present(1, 0); // Present with vsync - - UINT64 fenceValue = mFenceLastSignaledValue + 1; - mD3dCommandQueue->Signal(mFence, fenceValue); - mFenceLastSignaledValue = fenceValue; - frameCtx->FenceValue = fenceValue; - } - } - -private: - bool CreateDeviceD3D() - { - // Setup swap chain - DXGI_SWAP_CHAIN_DESC1 sd; - { - ZeroMemory(&sd, sizeof(sd)); - sd.BufferCount = kNumBackBuffers; - sd.Width = 0; - sd.Height = 0; - sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; - sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - sd.SampleDesc.Count = 1; - sd.SampleDesc.Quality = 0; - sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; - sd.Scaling = DXGI_SCALING_STRETCH; - sd.Stereo = FALSE; - } - - // Create device - D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; - if (D3D12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&mD3dDevice)) != S_OK) { - return false; - } - - { - D3D12_DESCRIPTOR_HEAP_DESC desc = {}; - desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - desc.NumDescriptors = kNumBackBuffers; - desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - desc.NodeMask = 1; - if (mD3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&mD3dRtvDescHeap)) != S_OK) { - return false; - } - - SIZE_T rtvDescriptorSize = mD3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = mD3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart(); - for (UINT i = 0; i < kNumBackBuffers; i++) { - mMainRenderTargetDescriptor[i] = rtvHandle; - rtvHandle.ptr += rtvDescriptorSize; - } - } - - { - D3D12_DESCRIPTOR_HEAP_DESC desc = {}; - desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - desc.NumDescriptors = 1; - desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (mD3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&mD3dSrvDescHeap)) != S_OK) { - return false; - } - } - - { - D3D12_COMMAND_QUEUE_DESC desc = {}; - desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - desc.NodeMask = 1; - if (mD3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&mD3dCommandQueue)) != S_OK) { - return false; - } - } - - for (UINT i = 0; i < kNumFramesInFlight; i++) { - if (mD3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mFrameContext[i].CommandAllocator)) != S_OK) { - return false; - } - } - - if (mD3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mFrameContext[0].CommandAllocator, nullptr, IID_PPV_ARGS(&mD3dCommandList)) != S_OK || - mD3dCommandList->Close() != S_OK) - { - return false; - } - - if (mD3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)) != S_OK) return false; - - mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (mFenceEvent == nullptr) return false; - - { - IDXGIFactory4* dxgiFactory = nullptr; - IDXGISwapChain1* swapChain1 = nullptr; - if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK) - return false; - if (dxgiFactory->CreateSwapChainForHwnd(mD3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK) - return false; - if (swapChain1->QueryInterface(IID_PPV_ARGS(&mSwapChain)) != S_OK) - return false; - swapChain1->Release(); - dxgiFactory->Release(); - mSwapChain->SetMaximumFrameLatency(kNumBackBuffers); - mSwapChainWaitableObject = mSwapChain->GetFrameLatencyWaitableObject(); - } - - CreateRenderTarget(); - return true; - } - - void CleanupDeviceD3D() - { - CleanupRenderTarget(); - if (mSwapChain) { - mSwapChain->Release(); - mSwapChain = nullptr; - } - if (mSwapChainWaitableObject != nullptr) { - CloseHandle(mSwapChainWaitableObject); - } - for (UINT i = 0; i < kNumFramesInFlight; i++) - if (mFrameContext[i].CommandAllocator) { - mFrameContext[i].CommandAllocator->Release(); - mFrameContext[i].CommandAllocator = nullptr; - } - if (mD3dCommandQueue) { - mD3dCommandQueue->Release(); - mD3dCommandQueue = nullptr; - } - if (mD3dCommandList) { - mD3dCommandList->Release(); - mD3dCommandList = nullptr; - } - if (mD3dRtvDescHeap) { - mD3dRtvDescHeap->Release(); - mD3dRtvDescHeap = nullptr; - } - if (mD3dSrvDescHeap) { - mD3dSrvDescHeap->Release(); - mD3dSrvDescHeap = nullptr; - } - if (mFence) { - mFence->Release(); - mFence = nullptr; - } - if (mFenceEvent) { - CloseHandle(mFenceEvent); - mFenceEvent = nullptr; - } - if (mD3dDevice) { - mD3dDevice->Release(); - mD3dDevice = nullptr; - } - } - - void CreateRenderTarget() - { - for (UINT i = 0; i < kNumBackBuffers; i++) - { - ID3D12Resource* pBackBuffer = nullptr; - mSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer)); - mD3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, mMainRenderTargetDescriptor[i]); - mMainRenderTargetResource[i] = pBackBuffer; - } - } - - void CleanupRenderTarget() - { - WaitForLastSubmittedFrame(); - - for (UINT i = 0; i < kNumBackBuffers; i++) - if (mMainRenderTargetResource[i]) { - mMainRenderTargetResource[i]->Release(); - mMainRenderTargetResource[i] = nullptr; - } - } - - void WaitForLastSubmittedFrame() - { - FrameContext* frameCtx = &mFrameContext[mFrameIndex % kNumFramesInFlight]; - - UINT64 fenceValue = frameCtx->FenceValue; - if (fenceValue == 0) - return; // No fence was signaled - - frameCtx->FenceValue = 0; - if (mFence->GetCompletedValue() >= fenceValue) - return; - - mFence->SetEventOnCompletion(fenceValue, mFenceEvent); - WaitForSingleObject(mFenceEvent, INFINITE); - } - - FrameContext* WaitForNextFrameResources() - { - UINT nextFrameIndex = mFrameIndex + 1; - mFrameIndex = nextFrameIndex; - - HANDLE waitableObjects[] = { mSwapChainWaitableObject, nullptr }; - DWORD numWaitableObjects = 1; - - FrameContext* frameCtx = &mFrameContext[nextFrameIndex % kNumFramesInFlight]; - UINT64 fenceValue = frameCtx->FenceValue; - if (fenceValue != 0) // means no fence was signaled - { - frameCtx->FenceValue = 0; - mFence->SetEventOnCompletion(fenceValue, mFenceEvent); - waitableObjects[1] = mFenceEvent; - numWaitableObjects = 2; - } - - WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE); - - return frameCtx; - } - - void ResizeSwapChain(int width, int height) - { - DXGI_SWAP_CHAIN_DESC1 sd; - mSwapChain->GetDesc1(&sd); - sd.Width = width; - sd.Height = height; - - IDXGIFactory4* dxgiFactory = nullptr; - mSwapChain->GetParent(IID_PPV_ARGS(&dxgiFactory)); - - mSwapChain->Release(); - CloseHandle(mSwapChainWaitableObject); - - IDXGISwapChain1* swapChain1 = nullptr; - dxgiFactory->CreateSwapChainForHwnd(mD3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1); - swapChain1->QueryInterface(IID_PPV_ARGS(&mSwapChain)); - swapChain1->Release(); - dxgiFactory->Release(); - - mSwapChain->SetMaximumFrameLatency(kNumBackBuffers); - - mSwapChainWaitableObject = mSwapChain->GetFrameLatencyWaitableObject(); - assert(mSwapChainWaitableObject != nullptr); - } - - static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - DirectX12Backend* self; - if (uMsg == WM_NCCREATE) { - auto lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); - self = static_cast<DirectX12Backend*>(lpcs->lpCreateParams); - self->hWnd = hWnd; - SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self)); - } else { - self = reinterpret_cast<DirectX12Backend*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)); - } - - if (self) { - return self->WndProc(uMsg, wParam, lParam); - } else { - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - } - - LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam) - { - if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) { - return true; - } - - switch (msg) { - case WM_SIZE: { - if (mD3dDevice != nullptr && wParam != SIZE_MINIMIZED) { - WaitForLastSubmittedFrame(); - ImGui_ImplDX12_InvalidateDeviceObjects(); - CleanupRenderTarget(); - ResizeSwapChain((UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); - CreateRenderTarget(); - ImGui_ImplDX12_CreateDeviceObjects(); - } - return 0; - } - - case WM_SYSCOMMAND: { - // Disable ALT application menu - if ((wParam & 0xfff0) == SC_KEYMENU) { - return 0; - } - } break; - - case WM_DESTROY: { - ::PostQuitMessage(0); - return 0; - } - } - return ::DefWindowProc(hWnd, msg, wParam, lParam); - } -}; - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx12Backend() -{ - try { - return std::make_unique<DirectX12Backend>(); - } catch (std::exception& e) { - return nullptr; - } -} - -#else // ^^ BUILD_CORE_WITH_DX12_BACKEND | BUILD_CORE_WITH_DX12_BACKEND vv - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateDx12Backend() -{ - return nullptr; -} - -#endif diff --git a/core/src/Entrypoint/Backend_Metal.mm b/core/src/Entrypoint/Backend_Metal.mm deleted file mode 100644 index 276bef2..0000000 --- a/core/src/Entrypoint/Backend_Metal.mm +++ /dev/null @@ -1,40 +0,0 @@ -#include "Backend.hpp" - -#if BUILD_CORE_WITH_METAL_BACKEND - -class MetalBackend : public RenderingBackend -{ -public: - MetalBackend() - { - // TODO - } - - virtual ~MetalBackend() - { - // TODO - } - - virtual void RunUntilWindowClose(void (*windowContent)()) - { - // TODO - } -}; - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateMetalBackend() -{ - try { - return std::make_unique<MetalBackend>(); - } catch (std::exception& e) { - return nullptr; - } -} - -#else // ^^ BUILD_CORE_WITH_METAL_BACKEND | BUILD_CORE_WITH_METAL_BACKEND vv - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateMetalBackend() -{ - return nullptr; -} - -#endif diff --git a/core/src/Entrypoint/Backend_OpenGL2.cpp b/core/src/Entrypoint/Backend_OpenGL2.cpp deleted file mode 100644 index 8f80094..0000000 --- a/core/src/Entrypoint/Backend_OpenGL2.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "Entrypoint/Backend.hpp" - -#if BUILD_CORE_WITH_OPENGL2_BACKEND -# include <glad/glad.h> - -# include <GLFW/glfw3.h> -# include <backend/imgui_impl_glfw.h> -# include <backend/imgui_impl_opengl2.h> -# include <imgui.h> -# include <stdexcept> -# include <iostream> - -# define IMGUI_IMPL_OPENGL_LOADER_CUSTOM -# include <backend/imgui_impl_opengl2.cpp> - -class OpenGL2Backend : public RenderingBackend -{ -private: - GLFWwindow* mWindow; - -public: - OpenGL2Backend() - { - glfwSetErrorCallback(&GlfwErrorCallback); - if (!glfwInit()) { - throw std::runtime_error("Failed to initialize GLFW."); - } - - mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr); - if (mWindow == nullptr) { - throw std::runtime_error("Failed to create GLFW window."); - } - glfwMakeContextCurrent(mWindow); - glfwSwapInterval(1); // Enable vsync - - if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0) { - throw std::runtime_error("Failed to initialize OpenGL."); - } - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - - ImGui_ImplGlfw_InitForOpenGL(mWindow, true); - ImGui_ImplOpenGL2_Init(); - } - - virtual ~OpenGL2Backend() - { - ImGui_ImplOpenGL2_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - - glfwDestroyWindow(mWindow); - glfwTerminate(); - } - - virtual void RunUntilWindowClose(void (*windowContent)()) - { - while (!glfwWindowShouldClose(mWindow)) { - glfwPollEvents(); - - ImGui_ImplOpenGL2_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - windowContent(); - - int displayWidth, displayHeight; - glfwGetFramebufferSize(mWindow, &displayWidth, &displayHeight); - glViewport(0, 0, displayWidth, displayHeight); - - const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - glClearColor(kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w); - glClear(GL_COLOR_BUFFER_BIT); - - ImGui::Render(); - ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); - - glfwMakeContextCurrent(mWindow); - glfwSwapBuffers(mWindow); - } - } - - static void GlfwErrorCallback(int errorCode, const char* message) - { - std::cerr << "GLFW Error " << errorCode << ": " << message << "\n"; - } -}; - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL2Backend() -{ - try { - return std::make_unique<OpenGL2Backend>(); - } catch (std::exception& e) { - return nullptr; - } -} - -#else // ^^ BUILD_CORE_WITH_OPENGL2_BACKEND | !BUILD_CORE_WITH_OPENGL2_BACKEND vv - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL2Backend() -{ - return nullptr; -} - -#endif diff --git a/core/src/Entrypoint/Backend_OpenGL3.cpp b/core/src/Entrypoint/Backend_OpenGL3.cpp deleted file mode 100644 index 96a260a..0000000 --- a/core/src/Entrypoint/Backend_OpenGL3.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "Entrypoint/Backend.hpp" - -#if BUILD_CORE_WITH_OPENGL3_BACKEND -# include <glad/glad.h> - -# include <GLFW/glfw3.h> -# include <iostream> -# include <backend/imgui_impl_glfw.h> -# include <backend/imgui_impl_opengl3.h> -# include <imgui.h> -# include <stdexcept> - -# define IMGUI_IMPL_OPENGL_LOADER_CUSTOM -# include <backend/imgui_impl_opengl3.cpp> - -class OpenGL3Backend : public RenderingBackend -{ -private: - GLFWwindow* mWindow; - -public: - OpenGL3Backend() - { - glfwSetErrorCallback(&GlfwErrorCallback); - if (!glfwInit()) { - throw std::runtime_error("Failed to initialize GLFW."); - } - -# if defined(__APPLE__) - // GL 3.2 + GLSL 150 - const char* glslVersion = "#version 150"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac -# else - // GL 3.0 + GLSL 130 - const char* glslVersion = "#version 130"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only - // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only -# endif - - mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr); - if (mWindow == nullptr) { - throw std::runtime_error("Failed to create GLFW window."); - } - glfwMakeContextCurrent(mWindow); - glfwSwapInterval(1); // Enable vsync - - if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0) { - throw std::runtime_error("Failed to initialize OpenGL."); - } - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - - ImGui_ImplGlfw_InitForOpenGL(mWindow, true); - ImGui_ImplOpenGL3_Init(glslVersion); - } - - virtual ~OpenGL3Backend() - { - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - - glfwDestroyWindow(mWindow); - glfwTerminate(); - } - - virtual void RunUntilWindowClose(void (*windowContent)()) - { - while (!glfwWindowShouldClose(mWindow)) { - glfwPollEvents(); - - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - windowContent(); - - int displayWidth, displayHeight; - glfwGetFramebufferSize(mWindow, &displayWidth, &displayHeight); - glViewport(0, 0, displayWidth, displayHeight); - - const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - glClearColor(kClearColor.x * kClearColor.w, kClearColor.y * kClearColor.w, kClearColor.z * kClearColor.w, kClearColor.w); - glClear(GL_COLOR_BUFFER_BIT); - - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - glfwSwapBuffers(mWindow); - } - } - - static void GlfwErrorCallback(int errorCode, const char* message) - { - std::cerr << "GLFW Error " << errorCode << ": " << message << "\n"; - } -}; - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL3Backend() -{ - try { - return std::make_unique<OpenGL3Backend>(); - } catch (std::exception& e) { - return nullptr; - } -} - -#else // ^^ BUILD_CORE_WITH_OPENGL3_BACKEND | !BUILD_CORE_WITH_OPENGL3_BACKEND vv - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateOpenGL3Backend() -{ - return nullptr; -} - -#endif diff --git a/core/src/Entrypoint/Backend_Vulkan.cpp b/core/src/Entrypoint/Backend_Vulkan.cpp deleted file mode 100644 index de3b5ca..0000000 --- a/core/src/Entrypoint/Backend_Vulkan.cpp +++ /dev/null @@ -1,438 +0,0 @@ -#include "Entrypoint/Backend.hpp" - -#if BUILD_CORE_WITH_VULKAN_BACKEND -# include <iostream> -# include <stdexcept> - -# define GLFW_INCLUDE_NONE -# define GLFW_INCLUDE_VULKAN -# include <GLFW/glfw3.h> - -# include <backend/imgui_impl_glfw.h> -# include <backend/imgui_impl_vulkan.h> -# include <backend/imgui_impl_vulkan.cpp> - -class VulkanBackend : public RenderingBackend -{ -private: - GLFWwindow* mWindow; - - VkAllocationCallbacks* mAllocator = NULL; - VkInstance mInstance = VK_NULL_HANDLE; - VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; - VkDevice mDevice = VK_NULL_HANDLE; - uint32_t mQueueFamily = (uint32_t)-1; - VkQueue mQueue = VK_NULL_HANDLE; - VkDebugReportCallbackEXT mDebugReport = VK_NULL_HANDLE; - VkPipelineCache mPipelineCache = VK_NULL_HANDLE; - VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE; - - ImGui_ImplVulkanH_Window mMainWindowData; - int mMinImageCount = 2; - bool mSwapChainRebuild = false; - -public: - VulkanBackend() - { - glfwSetErrorCallback(&GlfwErrorCallback); - if (!glfwInit()) { - throw std::runtime_error("Failed to initialize GLFW."); - } - - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - mWindow = glfwCreateWindow(1280, 720, "Cplt", nullptr, nullptr); - if (mWindow == nullptr) { - throw std::runtime_error("Failed to create GLFW window."); - } - - if (!glfwVulkanSupported()) { - throw std::runtime_error("GLFW reports vulkan not supported."); - } - - uint32_t extensionsCount = 0; - const char** extensions = glfwGetRequiredInstanceExtensions(&extensionsCount); - SetupVulkan(extensions, extensionsCount); - - // Create window surface - VkSurfaceKHR surface; - VkResult err = glfwCreateWindowSurface(mInstance, mWindow, mAllocator, &surface); - CheckVkResults(err); - - // Create framebuffers - int w, h; - glfwGetFramebufferSize(mWindow, &w, &h); - SetupVulkanWindow(&mMainWindowData, surface, w, h); - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - - ImGui_ImplGlfw_InitForVulkan(mWindow, true); - ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Instance = mInstance; - init_info.PhysicalDevice = mPhysicalDevice; - init_info.Device = mDevice; - init_info.QueueFamily = mQueueFamily; - init_info.Queue = mQueue; - init_info.PipelineCache = mPipelineCache; - init_info.DescriptorPool = mDescriptorPool; - init_info.Allocator = mAllocator; - init_info.MinImageCount = mMinImageCount; - init_info.ImageCount = mMainWindowData.ImageCount; - init_info.CheckVkResultFn = CheckVkResults; - ImGui_ImplVulkan_Init(&init_info, mMainWindowData.RenderPass); - } - - virtual ~VulkanBackend() - { - auto err = vkDeviceWaitIdle(mDevice); - CheckVkResults(err); - ImGui_ImplVulkan_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - - CleanupVulkanWindow(); - CleanupVulkan(); - - glfwDestroyWindow(mWindow); - glfwTerminate(); - } - - virtual void RunUntilWindowClose(void (*windowContent)()) override - { - // Upload Fonts - { - // Use any command queue - VkCommandPool commandPool = mMainWindowData.Frames[mMainWindowData.FrameIndex].CommandPool; - VkCommandBuffer commandBuffer = mMainWindowData.Frames[mMainWindowData.FrameIndex].CommandBuffer; - - CheckVkResults(vkResetCommandPool(mDevice, commandPool, 0)); - VkCommandBufferBeginInfo beginInfo = {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - CheckVkResults(vkBeginCommandBuffer(commandBuffer, &beginInfo)); - - ImGui_ImplVulkan_CreateFontsTexture(commandBuffer); - - VkSubmitInfo endInfo = {}; - endInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - endInfo.commandBufferCount = 1; - endInfo.pCommandBuffers = &commandBuffer; - CheckVkResults(vkEndCommandBuffer(commandBuffer)); - CheckVkResults(vkQueueSubmit(mQueue, 1, &endInfo, VK_NULL_HANDLE)); - - CheckVkResults(vkDeviceWaitIdle(mDevice)); - ImGui_ImplVulkan_DestroyFontUploadObjects(); - } - - while (!glfwWindowShouldClose(mWindow)) { - glfwPollEvents(); - - // Resize swap chain? - if (mSwapChainRebuild) { - int width, height; - glfwGetFramebufferSize(mWindow, &width, &height); - if (width > 0 && height > 0) { - ImGui_ImplVulkan_SetMinImageCount(mMinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(mInstance, mPhysicalDevice, mDevice, &mMainWindowData, mQueueFamily, mAllocator, width, height, mMinImageCount); - mMainWindowData.FrameIndex = 0; - mSwapChainRebuild = false; - } - } - - // Start the Dear ImGui frame - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - windowContent(); - - ImGui::Render(); - ImDrawData* drawData = ImGui::GetDrawData(); - const bool isMinimized = (drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f); - if (!isMinimized) { - const ImVec4 kClearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - mMainWindowData.ClearValue.color.float32[0] = kClearColor.x * kClearColor.w; - mMainWindowData.ClearValue.color.float32[1] = kClearColor.y * kClearColor.w; - mMainWindowData.ClearValue.color.float32[2] = kClearColor.z * kClearColor.w; - mMainWindowData.ClearValue.color.float32[3] = kClearColor.w; - FrameRender(&mMainWindowData, drawData); - FramePresent(&mMainWindowData); - } - } - } - -private: - void SetupVulkan(const char** extensions, uint32_t extensions_count) - { - VkResult err; - - // Create Vulkan Instance - { - VkInstanceCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.enabledExtensionCount = extensions_count; - createInfo.ppEnabledExtensionNames = extensions; - // Create Vulkan Instance without any debug feature - err = vkCreateInstance(&createInfo, mAllocator, &mInstance); - CheckVkResults(err); - } - - // Select GPU - { - uint32_t gpuCount; - err = vkEnumeratePhysicalDevices(mInstance, &gpuCount, NULL); - CheckVkResults(err); - IM_ASSERT(gpuCount > 0); - - VkPhysicalDevice* gpus = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpuCount); - err = vkEnumeratePhysicalDevices(mInstance, &gpuCount, gpus); - CheckVkResults(err); - - // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers - // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple - // dedicated GPUs) is out of scope of this sample. - int useGpu = 0; - for (int i = 0; i < (int)gpuCount; i++) - { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(gpus[i], &properties); - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) - { - useGpu = i; - break; - } - } - - mPhysicalDevice = gpus[useGpu]; - free(gpus); - } - - // Select graphics queue family - { - uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &count, NULL); - - auto queues = std::make_unique<VkQueueFamilyProperties[]>(count); - vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &count, queues.get()); - for (uint32_t i = 0; i < count; i++) { - if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - mQueueFamily = i; - break; - } - } - - IM_ASSERT(mQueueFamily != (uint32_t)-1); - } - - // Create Logical Device (with 1 queue) - { - int deviceExtensionCount = 1; - const char* deviceExtensions[] = { "VK_KHR_swapchain" }; - const float queuePriority[] = { 1.0f }; - VkDeviceQueueCreateInfo queue_info[1] = {}; - queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_info[0].queueFamilyIndex = mQueueFamily; - queue_info[0].queueCount = 1; - queue_info[0].pQueuePriorities = queuePriority; - VkDeviceCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); - createInfo.pQueueCreateInfos = queue_info; - createInfo.enabledExtensionCount = deviceExtensionCount; - createInfo.ppEnabledExtensionNames = deviceExtensions; - err = vkCreateDevice(mPhysicalDevice, &createInfo, mAllocator, &mDevice); - CheckVkResults(err); - vkGetDeviceQueue(mDevice, mQueueFamily, 0, &mQueue); - } - - // Create Descriptor Pool - { - VkDescriptorPoolSize poolSizes[] = { - { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } - }; - VkDescriptorPoolCreateInfo poolInfo = {}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - poolInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes); - poolInfo.poolSizeCount = (uint32_t)IM_ARRAYSIZE(poolSizes); - poolInfo.pPoolSizes = poolSizes; - err = vkCreateDescriptorPool(mDevice, &poolInfo, mAllocator, &mDescriptorPool); - CheckVkResults(err); - } - } - - void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) - { - wd->Surface = surface; - - // Check for WSI support - VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mQueueFamily, wd->Surface, &res); - if (res != VK_TRUE) { - throw "Error no WSI support on physical device 0."; - } - - // Select Surface Format - const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(mPhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); - - // Select Present Mode - VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(mPhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); - - // Create SwapChain, RenderPass, Framebuffer, etc. - IM_ASSERT(mMinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(mInstance, mPhysicalDevice, mDevice, wd, mQueueFamily, mAllocator, width, height, mMinImageCount); - } - - void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* drawData) - { - VkResult err; - - VkSemaphore imageAcquiredSemaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; - VkSemaphore renderCompleteSemaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(mDevice, wd->Swapchain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { - mSwapChainRebuild = true; - return; - } - CheckVkResults(err); - - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; - { - err = vkWaitForFences(mDevice, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking - CheckVkResults(err); - - err = vkResetFences(mDevice, 1, &fd->Fence); - CheckVkResults(err); - } - { - err = vkResetCommandPool(mDevice, fd->CommandPool, 0); - CheckVkResults(err); - VkCommandBufferBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(fd->CommandBuffer, &info); - CheckVkResults(err); - } - { - VkRenderPassBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - info.renderPass = wd->RenderPass; - info.framebuffer = fd->Framebuffer; - info.renderArea.extent.width = wd->Width; - info.renderArea.extent.height = wd->Height; - info.clearValueCount = 1; - info.pClearValues = &wd->ClearValue; - vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); - } - - // Record dear imgui primitives into command buffer - ImGui_ImplVulkan_RenderDrawData(drawData, fd->CommandBuffer); - - // Submit command buffer - vkCmdEndRenderPass(fd->CommandBuffer); - { - VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &imageAcquiredSemaphore; - info.pWaitDstStageMask = &wait_stage; - info.commandBufferCount = 1; - info.pCommandBuffers = &fd->CommandBuffer; - info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &renderCompleteSemaphore; - - err = vkEndCommandBuffer(fd->CommandBuffer); - CheckVkResults(err); - err = vkQueueSubmit(mQueue, 1, &info, fd->Fence); - CheckVkResults(err); - } - } - - void FramePresent(ImGui_ImplVulkanH_Window* wd) - { - if (mSwapChainRebuild) { - return; - } - - VkSemaphore renderCompleteSemaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - VkPresentInfoKHR info = {}; - info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &renderCompleteSemaphore; - info.swapchainCount = 1; - info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &wd->FrameIndex; - VkResult err = vkQueuePresentKHR(mQueue, &info); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { - mSwapChainRebuild = true; - return; - } - CheckVkResults(err); - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores - } - - void CleanupVulkan() - { - vkDestroyDescriptorPool(mDevice, mDescriptorPool, mAllocator); - - vkDestroyDevice(mDevice, mAllocator); - vkDestroyInstance(mInstance, mAllocator); - } - - void CleanupVulkanWindow() - { - ImGui_ImplVulkanH_DestroyWindow(mInstance, mDevice, &mMainWindowData, mAllocator); - } - - static void CheckVkResults(VkResult err) - { - if (err == 0) return; - - std::string message; - message += "Vulkan error: VkResult = "; - message += err; - - if (err < 0) { - throw std::runtime_error(message); - } else { - std::cerr << message << '\n'; - } - } - static void GlfwErrorCallback(int errorCode, const char* message) - { - std::cerr << "GLFW Error " << errorCode << ": " << message << "\n"; - } -}; - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateVulkanBackend() -{ - try { - return std::make_unique<VulkanBackend>(); - } catch (std::exception& e) { - return nullptr; - } -} - -#else // ^^ BUILD_CORE_WITH_VULKAN_BACKEND | ~BUILD_CORE_WITH_VULKAN_BACKEND vv - -std::unique_ptr<RenderingBackend> RenderingBackend::CreateVulkanBackend() -{ - return nullptr; -} - -#endif diff --git a/core/src/Entrypoint/main.cpp b/core/src/Entrypoint/main.cpp deleted file mode 100644 index abfb26c..0000000 --- a/core/src/Entrypoint/main.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "Entrypoint/Backend.hpp" -#include "Model/GlobalStates.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/ScopeGuard.hpp" -#include "Utils/Sigslot.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <argparse/argparse.hpp> -#include <filesystem> -#include <iostream> -#include <memory> -#include <stdexcept> -#include <string> -#include <string_view> - -namespace fs = std::filesystem; -using namespace std::literals::string_literals; -using namespace std::literals::string_view_literals; - -static std::unique_ptr<RenderingBackend> CreateDefaultBackend() -{ -#if defined(_WIN32) -# if BUILD_CORE_WITH_DX12_BACKEND - if (auto backend = RenderingBackend::CreateDx12Backend()) { - return backend; - } -# endif -# if BUILD_CORE_WITH_DX11_BACKEND - if (auto backend = RenderingBackend::CreateDx11Backend()) { - return backend; - } -# endif -# if BUILD_CORE_WITH_VULKAN_BACKEND - if (auto backend = RenderingBackend::CreateVulkanBackend()) { - return backend; - } -# endif -# if BUILD_CORE_WITH_OPENGL3_BACKEND - if (auto backend = RenderingBackend::CreateOpenGL3Backend()) { - return backend; - } -# endif -# if BUILD_CORE_WITH_OPENGL2_BACKEND - if (auto backend = RenderingBackend::CreateOpenGL2Backend()) { - return backend; - } -# endif -#elif defined(__APPLE__) - // We currently only support using metal on macos - return RenderingBackend::CreateMetalBackend(); -#elif defined(__linux__) -# if BUILD_CORE_WITH_VULKAN_BACKEND - if (auto backend = RenderingBackend::CreateVulkanBackend()) { - return backend; - } -# endif -# if BUILD_CORE_WITH_OPENGL3_BACKEND - if (auto backend = RenderingBackend::CreateOpenGL3Backend()) { - return backend; - } -# endif -# if BUILD_CORE_WITH_OPENGL2_BACKEND - if (auto backend = RenderingBackend::CreateOpenGL2Backend()) { - return backend; - } -# endif -#endif - - return nullptr; -} - -static std::unique_ptr<RenderingBackend> CreateBackend(std::string_view option) -{ - if (option == "default") { - return CreateDefaultBackend(); - } else if (option == "opengl2") { - return RenderingBackend::CreateOpenGL2Backend(); - } else if (option == "opengl3") { - return RenderingBackend::CreateOpenGL3Backend(); - } else if (option == "vulkan") { - return RenderingBackend::CreateVulkanBackend(); - } else if (option == "dx11") { - return RenderingBackend::CreateDx11Backend(); - } else if (option == "dx12") { - return RenderingBackend::CreateDx12Backend(); - } else if (option == "metal") { - return RenderingBackend::CreateMetalBackend(); - } else { - std::string message; - message += "Unknown backend '"; - message += option; - message += "'.\n"; - throw std::runtime_error(message); - } -} - -#ifdef DOCTEST_CONFIG_DISABLE -int main(int argc, char* argv[]) -{ - argparse::ArgumentParser parser; - parser.add_argument("--global-data-directory") - .help("Directory in which global data (such as recently used projects) are saved to. Use 'default' to use the default directory on each platform.") - .default_value("default"s); - parser.add_argument("--rendering-backend") - .help("Which rendering backend to use. If equals 'default', the preferred API for each platform will be used") - .default_value("default"s); - - try { - parser.parse_args(argc, argv); - } catch (const std::runtime_error& error) { - std::cout << error.what() << '\n'; - std::cout << parser; - return -1; - } - - auto backendOption = parser.get<std::string>("--rendering-backend"); - auto backend = CreateBackend(backendOption); - - auto& io = ImGui::GetIO(); - - // Disable saving window positions - io.IniFilename = nullptr; - // Disable log (dump widget tree) file, we don't trigger it but just to be safe - io.LogFilename = nullptr; - - // Light mode because all major OS's default theme is white - // TODO follow system theme - ImGui::StyleColorsLight(); - - // Configure default fonts - { - // Includes latin alphabet, although for some reason smaller than if rendered using 18 point NotoSans regular - io.Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.otf", 18, nullptr, io.Fonts->GetGlyphRangesChineseFull()); - - ImWchar iconRanges[] = { ICON_MIN_FA, ICON_MAX_FA }; - ImFontConfig config; - config.MergeMode = true; - io.Fonts->AddFontFromFileTTF("fonts/FontAwesome5-Solid.otf", 14, &config, iconRanges); - } - - auto dataDirOption = parser.get<std::string>("--global-data-directory"); - if (dataDirOption == "default") { - GlobalStates::Init(); - } else { - fs::path path(dataDirOption); - GlobalStates::Init(std::move(path)); - } - DEFER - { - GlobalStates::Shutdown(); - }; - - // Main loop - backend->RunUntilWindowClose(&UI::MainWindow); - - return 0; -} -#else -# define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -# include <doctest/doctest.h> -#endif diff --git a/core/src/Locale/zh_CN.h b/core/src/Locale/zh_CN.h deleted file mode 100644 index f654464..0000000 --- a/core/src/Locale/zh_CN.h +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once - -#define L10N_ERROR "错误" -#define L10N_ADD "新建" -#define L10N_EDIT "编辑" -#define L10N_DELETE "删除" -#define L10N_RENAME "重命名" -#define L10N_DISCONNECT "断开连接" -#define L10N_OPEN "打开" -#define L10N_CLOSE "关闭" -#define L10N_CONFIRM "确定" -#define L10N_CANCEL "取消" -#define L10N_NAME "名称" -#define L10N_TYPE "类型" - -#define L10N_INVALID_PATH_ERROR "无效路径" -#define L10N_EMPTY_NAME_ERROR "名称不能为空" -#define L10N_DUPLICATE_NAME_ERROR "名称已被占用" - -#define L10N_MAIN_TAB_SETTINGS "设置" -#define L10N_MAIN_WINDOW_TAB_PROJECT "项目" -#define L10N_MAIN_WINDOW_TAB_DATABASE_VIEW "数据" -#define L10N_MAIN_WINDOW_TAB_ITEMS "物品" -#define L10N_MAIN_WINDOW_TAB_WORKFLOWS "工作流" -#define L10N_MAIN_WINDOW_TAB_TEMPLATES "模板" - -#define L10N_PROJECT_NEW "新建项目..." -#define L10N_PROJECT_NEW_DIALOG_TITLE "新建项目向导" -#define L10N_PROJECT_NAME "项目名称" -#define L10N_PROJECT_PATH "项目路径" -#define L10N_PROJECT_NEW_PATH_DIALOG_TITLE "项目路径" -#define L10N_PROJECT_OPEN "打开项目..." -#define L10N_PROJECT_OPEN_DIALOG_TITLE "打开项目" -#define L10N_PROJECT_RECENTS "最近使用" -#define L10N_PROJECT_RECENTS_CLEAR "清空" -#define L10N_PROJECT_RECENTS_NONE_PRESENT "(暂无最近使用的项目)" -#define L10N_PROJECT_RECENTS_OPEN_TOOLTIP "打开该项目" -#define L10N_PROJECT_RECENTS_DELETE_TOOLTIP "将该项目从最近使用列表中删除,项目本身将不受影响。" -#define L10N_PROJECT_INVALID_PROJECT_FORMAT "无效的项目文件" - -#define L10N_PROJECT_OPEN_IN_FILESYSTEM "在文件系统中打开" - -#define L10N_DATABASE_SALES_VIEW_TAB_NAME "销售" -#define L10N_DATABASE_SALES_VIEW_EDIT_DIALOG_TITLE "编辑销售记录" -#define L10N_DATABASE_PURCHASES_VIEW_TAB_NAME "采购" -#define L10N_DATABASE_PURCHASES_VIEW_EDIT_DIALOG_TITLE "编辑采购记录" -#define L10N_DATABASE_COLUMN_ITEMS "项目" -#define L10N_DATABASE_COLUMN_CUSTOMER "客户" -#define L10N_DATABASE_COLUMN_FACTORY "工厂" -/// 销售订单的交货期限 -#define L10N_DATABASE_COLUMN_DEADLINE "交货期限" -/// 采购订单的下单时间 -#define L10N_DATABASE_COLUMN_ORDER_TIME "下单时间" -/// 所有订单的“完成”时间。对于销售来说是实际交货时间,对于采购来说是收货时间。 -#define L10N_DATABASE_COLUMN_DELIVERY_TIME "交货时间" -/// 运输批次的发货时间,适用于采购和销售批次。 -#define L10N_DATABASE_COLUMN_SHIPMENT_TIME "发货时间" -/// 运输批次的收获时间,适用于采购和销售批次。 -#define L10N_DATABASE_COLUMN_ARRIVAL_TIME "实际到达时间" -#define L10N_DATABASE_MESSAGE_NO_ORDER_SELECTED "选择任意一个订单以查看与其相关的批次" -#define L10N_DATABASE_MESSAGE_NOT_DELIVERED "N/A" - -#define L10N_ITEM_ADD_DIALOG_TITLE "新建物品项" -#define L10N_ITEM_EDIT_DIALOG_TITLE "编辑物品项" -#define L10N_ITEM_DELETE_DIALOG_TITLE "删除物品项" -#define L10N_ITEM_DELETE_DIALOG_MESSAGE "确定删除该物品项吗?" -#define L10N_ITEM_CATEGORY_PRODUCT "产品" -#define L10N_ITEM_CATEGORY_FACTORY "工厂" -#define L10N_ITEM_CATEGORY_CUSTOMER "客户" -#define L10N_ITEM_COLUMN_NAME "名称" -#define L10N_ITEM_COLUMN_DESCRIPTION "描述" -#define L10N_ITEM_COLUMN_EMAIL "邮箱" -#define L10N_ITEM_COLUMN_STOCK "库存" -#define L10N_ITEM_COLUMN_PRICE "价格" - -#define L10N_ASSET_OPEN "打开资源..." -#define L10N_ASSET_OPEN_DIALOG_TITLE "打开资源" -#define L10N_ASSET_MANAGE "管理资源..." -#define L10N_ASSET_MANAGE_DIALOG_TITLE "管理资源" -#define L10N_ADD_ASSET_DIALOG_TITLE "新建资源向导" -#define L10N_DELETE_ASSET_DIALOG_TITLE "确认删除资源" -#define L10N_RENAME_ASSET_DIALOG_TITLE "重命名资源" -#define L10N_TEMPLATE_INVALID_TYPE_ERROR "无效的模板类型" - -#define L10N_VALUE_NUMERIC "数值" -#define L10N_VALUE_TEXT "文本" -#define L10N_VALUE_DATE_TIME "时间戳" -#define L10N_VALUE_ROW_ID "数据库表格行" -#define L10N_VALUE_LIST "列表" -#define L10N_VALUE_DICT "字典" -#define L10N_VALUE_OBJECT "对象" -#define L10N_VALUE_SALE_RECORD "销售记录" -#define L10N_VALUE_PURCHASE_RECORD "采购记录" - -#define L10N_VALUE_PROPERTY_CUSTOMER "客户" -#define L10N_VALUE_PROPERTY_DEADLINE "交货期限" -#define L10N_VALUE_PROPERTY_FACTORY "工厂" -#define L10N_VALUE_PROPERTY_ORDER_TIME "下单时间" -#define L10N_VALUE_PROPERTY_DELIVERY_TIME "交货时间" - -#define L10N_WORKFLOW_KIND_INPUT "输入节点" -#define L10N_WORKFLOW_KIND_TRANSFORM "计算节点" -#define L10N_WORKFLOW_KIND_OUTPUT "输出节点" - -#define L10N_WORKFLOW_ADD "加法" -#define L10N_WORKFLOW_SUB "减法" -#define L10N_WORKFLOW_MUL "乘法" -#define L10N_WORKFLOW_DIV "除法" -#define L10N_WORKFLOW_EVAL "对表达式求值" -#define L10N_WORKFLOW_FMT "格式化文本" -#define L10N_WORKFLOW_INSTANTIATE_TEMPLATE "实例化文档" -#define L10N_WORKFLOW_FORM_INPUT "表单输入" -#define L10N_WORKFLOW_DB_INPUT "数据库输入" - -#define L10N_WORKFLOW_CATEGORY_NUMERIC "数字" -#define L10N_WORKFLOW_CATEGORY_TEXT "文本" -#define L10N_WORKFLOW_CATEGORY_DOCUMENT "文档" -#define L10N_WORKFLOW_CATEGORY_USER_INPUT "用户输入" -#define L10N_WORKFLOW_CATEGORY_SYS_INPUT "环境输入" -#define L10N_WORKFLOW_CATEGORY_OUTPUT "输出" - -#define L10N_WORKFLOW_RTERROR_DIV_BY_0 "错误:除数为0" - -#define L10N_TEMPLATE_TABLE "表格模板" - -#define L10N_TABLE "表格" -#define L10N_TABLE_CONFIGURE_PROPERTIES "编辑表格属性..." -#define L10N_TABLE_PROPERTIES "表格属性" -#define L10N_TABLE_EDIT_TABLE "编辑表格" -#define L10N_TABLE_EDIT_RESIZE_COLS "调整列宽度" -#define L10N_TABLE_EDIT_RESIZE_ROWS "调整行高度" -#define L10N_TABLE_WIDTH "宽度" -#define L10N_TABLE_HEIGHT "长度" -#define L10N_TABLE_SINGLE_PARAMS "参数" -#define L10N_TABLE_ARRAY_GROUPS "列表参数组" -#define L10N_TABLE_CELL "单元格" -#define L10N_TABLE_CELL_POS "位置:%s%s" -#define L10N_TABLE_CELL_TYPE_CONST "类型:普通单元格" -#define L10N_TABLE_CELL_TYPE_PARAM "类型:参数单元格" -#define L10N_TABLE_CELL_TYPE_CREATE_AG "类型:列表参数组" -#define L10N_TABLE_CELL_CONV_CONST "转换为普通单元格" -#define L10N_TABLE_CELL_CONV_PARAM "转换为参数单元格" -#define L10N_TABLE_CELL_CONV_CREATE_AG "创建列表参数组" -#define L10N_TABLE_CELL_CONV_ADD_AG_LEFT "添加至左侧的列表参数组" -#define L10N_TABLE_CELL_CONV_ADD_AG_RIGHT "添加至右侧的列表参数组" -#define L10N_TABLE_CELL_SELECT_MSG "请单击选择一个单元格以编辑其属性" -#define L10N_TABLE_CELL_HORIZONTAL_ALIGNMENT "水平对齐" -#define L10N_TABLE_CELL_CONTENT "内容" -#define L10N_TABLE_CELL_VAR_NAME "变量名" -#define L10N_TABLE_CELL_VAR_TOOLTIP "参数单元格的唯一名称(表格内不得重复)。" -#define L10N_TABLE_CELL_ARRAY_VAR_TOOLTIP "列表参数组内单个参数单元格的名称,在组内唯一(不同的参数组可以包含相同的参数名)。" -#define L10N_TABLE_CELL_VAR_NAME_DUP "参数名已重复" -#define L10N_TABLE_CELL_VERTICAL_ALIGNMENT "垂直对齐" -#define L10N_TABLE_CELL_ALIGN_LEFT "左对齐" -#define L10N_TABLE_CELL_ALIGN_CENTER "居中" -#define L10N_TABLE_CELL_ALIGN_RIGHT "右对齐" -#define L10N_TABLE_CELL_ALIGN_TOP "顶部对齐" -#define L10N_TABLE_CELL_ALIGN_MIDDLE "居中" -#define L10N_TABLE_CELL_ALIGN_BOTTOM "底部对齐" diff --git a/core/src/Model/Assets.cpp b/core/src/Model/Assets.cpp deleted file mode 100644 index fa29523..0000000 --- a/core/src/Model/Assets.cpp +++ /dev/null @@ -1,306 +0,0 @@ -#include "Assets.hpp" - -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/IO/DataStream.hpp" -#include "Utils/IO/StringIntegration.hpp" -#include "Utils/IO/UuidIntegration.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <imgui_stdlib.h> -#include <tsl/array_map.h> -#include <string> -#include <utility> - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; - -template <class TSavedAsset, class TStream> -void OperateStreamForSavedAsset(TSavedAsset& cell, TStream& proxy) -{ - proxy.template ObjectAdapted<DataStreamAdapters::String>(cell.Name); - proxy.template ObjectAdapted<DataStreamAdapters::Uuid>(cell.Uuid); - proxy.Value(cell.Payload); -} - -void SavedAsset::ReadFromDataStream(InputDataStream& stream) -{ - ::OperateStreamForSavedAsset(*this, stream); -} - -void SavedAsset::WriteToDataStream(OutputDataStream& stream) const -{ - ::OperateStreamForSavedAsset(*this, stream); -} - -Asset::Asset() = default; - -class AssetList::Private -{ -public: - Project* ConnectedProject; - tsl::array_map<char, SavedAsset> Assets; - tsl::array_map<char, std::unique_ptr<Asset>> Cache; - int CacheSizeLimit = 0; - - struct - { - std::string NewName; - NameSelectionError NewNameError = NameSelectionError::Empty; - - void ShowErrors() const - { - switch (NewNameError) { - case NameSelectionError::None: break; - case NameSelectionError::Duplicated: - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - break; - case NameSelectionError::Empty: - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - break; - } - } - - bool HasErrors() const - { - return NewNameError != NameSelectionError::None; - } - - void Validate(const AssetList& self) - { - if (NewName.empty()) { - NewNameError = NameSelectionError::Empty; - return; - } - - if (self.FindByName(NewName)) { - NewNameError = NameSelectionError::Duplicated; - return; - } - - NewNameError = NameSelectionError::None; - } - } PopupPrivateState; -}; - -AssetList::AssetList(Project& project) - : mPrivate{ std::make_unique<Private>() } -{ - mPrivate->ConnectedProject = &project; -} - -// Write an empty destructor here so std::unique_ptr's destructor can see AssetList::Private's implementation -AssetList::~AssetList() -{ -} - -Project& AssetList::GetConnectedProject() const -{ - return *mPrivate->ConnectedProject; -} - -void AssetList::Reload() -{ - // TODO fix asset dicovery loading - mPrivate->Assets.clear(); - mPrivate->Cache.clear(); - DiscoverFiles([this](SavedAsset asset) -> void { - mPrivate->Assets.insert(asset.Name, std::move(asset)); - }); -} - -int AssetList::GetCount() const -{ - return mPrivate->Assets.size(); -} - -const tsl::array_map<char, SavedAsset>& AssetList::GetAssets() const -{ - return mPrivate->Assets; -} - -const SavedAsset* AssetList::FindByName(std::string_view name) const -{ - auto iter = mPrivate->Assets.find(name); - if (iter != mPrivate->Assets.end()) { - return &iter.value(); - } else { - return nullptr; - } -} - -const SavedAsset& AssetList::Create(SavedAsset asset) -{ - auto [iter, DISCARD] = mPrivate->Assets.insert(asset.Name, SavedAsset{}); - auto& savedAsset = iter.value(); - - savedAsset = std::move(asset); - if (savedAsset.Uuid.is_nil()) { - savedAsset.Uuid = uuids::uuid_random_generator{}(); - } - - SaveInstance(savedAsset, nullptr); - - return savedAsset; -} - -std::unique_ptr<Asset> AssetList::CreateAndLoad(SavedAsset assetIn) -{ - auto& savedAsset = Create(std::move(assetIn)); - auto asset = std::unique_ptr<Asset>(CreateInstance(savedAsset)); - return asset; -} - -std::unique_ptr<Asset> AssetList::Load(std::string_view name) const -{ - if (auto savedAsset = FindByName(name)) { - auto asset = Load(*savedAsset); - return asset; - } else { - return nullptr; - } -} - -std::unique_ptr<Asset> AssetList::Load(const SavedAsset& asset) const -{ - return std::unique_ptr<Asset>(LoadInstance(asset)); -} - -const SavedAsset* AssetList::Rename(std::string_view oldName, std::string_view newName) -{ - auto iter = mPrivate->Assets.find(oldName); - if (iter == mPrivate->Assets.end()) return nullptr; - - auto info = std::move(iter.value()); - info.Name = newName; - - RenameInstanceOnDisk(info, oldName); - - mPrivate->Assets.erase(iter); - auto [newIter, DISCARD] = mPrivate->Assets.insert(newName, std::move(info)); - - return &newIter.value(); -} - -bool AssetList::Remove(std::string_view name) -{ - auto iter = mPrivate->Assets.find(name); - if (iter == mPrivate->Assets.end()) { - return false; - } - auto& asset = iter.value(); - - fs::remove(RetrievePathFromAsset(asset)); - mPrivate->Assets.erase(iter); - - return true; -} - -int AssetList::GetCacheSizeLimit() const -{ - return mPrivate->CacheSizeLimit; -} - -void AssetList::SetCacheSizeLimit(int limit) -{ - mPrivate->CacheSizeLimit = limit; -} - -void AssetList::DisplayIconsList(ListState& state) -{ - // TODO -} - -void AssetList::DisplayDetailsList(ListState& state) -{ - // Note: stub function remained here in case any state processing needs to be done before issuing to implementers - DisplayDetailsTable(state); -} - -void AssetList::DisplayControls(ListState& state) -{ - auto& ps = mPrivate->PopupPrivateState; - bool openedDummy = true; - - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - ImGui::OpenPopup(I18N_TEXT("Add asset wizard", L10N_ADD_ASSET_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Add asset wizard", L10N_ADD_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - DisplayAssetCreator(state); - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_I_CURSOR " " I18N_TEXT("Rename", L10N_RENAME), state.SelectedAsset == nullptr)) { - ImGui::OpenPopup(I18N_TEXT("Rename asset wizard", L10N_RENAME_ASSET_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Rename asset wizard", L10N_RENAME_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &ps.NewName)) { - ps.Validate(*this); - } - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), ps.HasErrors())) { - ImGui::CloseCurrentPopup(); - - auto movedAsset = Rename(state.SelectedAsset->Name, ps.NewName); - // Update the selected pointer to the new location (we mutated the map, the pointer may be invalid now) - state.SelectedAsset = movedAsset; - - ps = {}; - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - ps.ShowErrors(); - - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), state.SelectedAsset == nullptr)) { - ImGui::OpenPopup(I18N_TEXT("Delete asset", L10N_DELETE_ASSET_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Delete asset", L10N_DELETE_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM))) { - ImGui::CloseCurrentPopup(); - - auto& assetName = state.SelectedAsset->Name; - Remove(assetName); - - state.SelectedAsset = nullptr; - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } -} - -void AssetList::DiscoverFilesByExtension(const std::function<void(SavedAsset)>& callback, const fs::path& containerDir, std::string_view extension) const -{ - for (auto entry : fs::directory_iterator(containerDir)) { - if (!entry.is_regular_file()) continue; - - // If the caller provided an extension to match against, and it doesn't equal to current file extension, skip - if (!extension.empty() && - entry.path().extension() != extension) - { - continue; - } - - callback(SavedAsset{ - .Name = RetrieveNameFromFile(entry.path()), - .Uuid = RetrieveUuidFromFile(entry.path()), - // TODO load payload - }); - } -} - -void AssetList::DiscoverFilesByHeader(const std::function<void(SavedAsset)>& callback, const fs::path& containerDir, const std::function<bool(std::istream&)>& validater) const -{ - // TODO -} diff --git a/core/src/Model/Assets.hpp b/core/src/Model/Assets.hpp deleted file mode 100644 index 3d90d3f..0000000 --- a/core/src/Model/Assets.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "Utils/UUID.hpp" -#include "cplt_fwd.hpp" - -#include <tsl/array_map.h> -#include <filesystem> -#include <iosfwd> -#include <memory> -#include <string_view> - -/// A structure representing a ready-to-be-loaded asset, locating on the disk. -/// Each asset should be identified by a unique uuid within the asset category (i.e. a workflow and a template can share the same uuid), -/// generated on insertion to an asset list if not given by the caller. -struct SavedAsset -{ - std::string Name; - /// UUID of this asset. This field is generated as a random UUID v4 upon insertion into an AssetList, if not already provided by the caller (indicated by !is_nil()). - uuids::uuid Uuid; - /// Extra data to be used by the AssetList/Asset implementation. - uint64_t Payload; - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; -}; - -class Asset -{ -public: - Asset(); - virtual ~Asset() = default; -}; - -enum class NameSelectionError -{ - None, - Duplicated, - Empty, -}; - -class AssetList -{ -private: - class Private; - std::unique_ptr<Private> mPrivate; - -public: - AssetList(Project& project); - virtual ~AssetList(); - - Project& GetConnectedProject() const; - - // TODO support file watches - void Reload(); - - int GetCount() const; - // TODO convert to custom iterable - const tsl::array_map<char, SavedAsset>& GetAssets() const; - - const SavedAsset* FindByName(std::string_view name) const; - const SavedAsset& Create(SavedAsset asset); - std::unique_ptr<Asset> CreateAndLoad(SavedAsset asset); - /// Load the asset on disk by its name. - std::unique_ptr<Asset> Load(std::string_view name) const; - /// Load the asset on disk by a reference to its SavedAsset instance. This function assumes that the given SavedAsset - /// is stored in AssetList, otherwise the behavior is undefined. - std::unique_ptr<Asset> Load(const SavedAsset& asset) const; - const SavedAsset* Rename(std::string_view oldName, std::string_view newName); - bool Remove(std::string_view name); - - int GetCacheSizeLimit() const; - void SetCacheSizeLimit(int limit); - - struct ListState - { - const SavedAsset* SelectedAsset = nullptr; - }; - void DisplayIconsList(ListState& state); - void DisplayDetailsList(ListState& state); - void DisplayControls(ListState& state); - -protected: - virtual void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const = 0; - - // Helper - void DiscoverFilesByExtension(const std::function<void(SavedAsset)>& callback, const std::filesystem::path& containerDir, std::string_view extension) const; - void DiscoverFilesByHeader(const std::function<void(SavedAsset)>& callback, const std::filesystem::path& containerDir, const std::function<bool(std::istream&)>& validater) const; - - /// Create an empty/default instance of this asset type on disk, potentially qualified by SavedAsset::Payload. - /// Return `true` on success and `false` on failure. - virtual bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const = 0; - /// The returned pointer indicate ownership to the object. - virtual Asset* LoadInstance(const SavedAsset& assetInfo) const = 0; - /// Create an empty/default instance of this asset type, potentially qualified by SavedAsset::Payload. - /// The returned pointer indicate ownership to the object. - virtual Asset* CreateInstance(const SavedAsset& assetInfo) const = 0; - virtual bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const = 0; - - virtual std::string RetrieveNameFromFile(const std::filesystem::path& file) const = 0; - virtual uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const = 0; - virtual std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const = 0; - - virtual void DisplayAssetCreator(ListState& state) = 0; - virtual void DisplayDetailsTable(ListState& state) const = 0; -}; - -template <class T> -class AssetListTyped : public AssetList -{ -public: - using AssetList::AssetList; - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - std::unique_ptr<T> CreateAndLoad(SavedAsset asset) - { - return std::unique_ptr<T>(static_cast<T*>(AssetList::CreateAndLoad(asset).release())); - } - - std::unique_ptr<T> Load(std::string_view name) const - { - return std::unique_ptr<T>(static_cast<T*>(AssetList::Load(name).release())); - } - - std::unique_ptr<T> Load(const SavedAsset& asset) const - { - return std::unique_ptr<T>(static_cast<T*>(AssetList::Load(asset).release())); - } -#pragma clang diagnostic pop -}; diff --git a/core/src/Model/Database.cpp b/core/src/Model/Database.cpp deleted file mode 100644 index 569b949..0000000 --- a/core/src/Model/Database.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "Database.hpp" - -#include "Model/Project.hpp" - -#include <filesystem> -#include <stdexcept> - -namespace fs = std::filesystem; - -SalesTable::SalesTable(MainDatabase& db) - // language=SQLite - : GetRowCount(db.GetSQLite(), "SELECT Count(*) FROM Sales") - // language=SQLite - , GetRows(db.GetSQLite(), "SELECT * FROM Sales LIMIT ? OFFSET ?") - // language=SQLite - , GetItems(db.GetSQLite(), "SELECT * FROM SalesItems WHERE SaleId == ?") -{ -} - -PurchasesTable::PurchasesTable(MainDatabase& db) - // language=SQLite - : GetRowCount(db.GetSQLite(), "SELECT Count(*) FROM Purchases") - // language=SQLite - , GetRows(db.GetSQLite(), "SELECT * FROM Purchases LIMIT ? OFFSET ?") - // language=SQLite - , GetItems(db.GetSQLite(), "SELECT * FROM PurchasesItems WHERE PurchaseId == ?") -{ -} - -DeliveryTable::DeliveryTable(MainDatabase& db) - // language=SQLite - : FilterByTypeAndId(db.GetSQLite(), "SELECT * FROM Deliveries WHERE AssociatedOrder == ? AND Outgoing = ?") - // language=SQLite - , GetItems(db.GetSQLite(), "SELECT * FROM DeliveriesItems WHERE DeliveryId == ?") -{ -} - -static std::string GetDatabaseFilePath(const Project& project) -{ - auto dbsDir = project.GetPath() / "databases"; - fs::create_directories(dbsDir); - - auto dbFile = dbsDir / "transactions.sqlite3"; - return dbFile.string(); -} - -/// Wrapper for SQLite::Database that creates the default tables -MainDatabase::DatabaseWrapper::DatabaseWrapper(MainDatabase& self) - : mSqlite(GetDatabaseFilePath(*self.mProject), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE) -{ - // If this table doesn't exist, the database probably just got initialized - if (mSqlite.tableExists("Sales")) { - return; - } - - // 'Sales' schema - // - Customer: the customer item ID - // - Deadline: unix epoch time of order deadline - // - DeliveryTime: the time this order was completed (through a set of deliveries) - - // 'Purchases' schema - // - Factory: the factory id, - // - OrderTime: the time this order was made - // - DeliveryTime: the time this order was completed (through a set of deliveries) - - // 'Deliveries' schema - // - ShipmentTime: unix epoch time stamp of sending to delivery - // - ArrivalTime: unix epoch time stamp of delivery arrived at warehouse; 0 if not arrived yet - // - AssociatedOrder: Id of the order that this delivery is completing (which table: Outgoing=true -> Sales, Outgoing=false -> Purchases) - // - Outgoing: true if the delivery is from warehouse to customer; false if the delivery is from factory to warehouse - - // Note: the 'Id' key would be unique (not recycled after row deletion) because it's explicit - // https://www.sqlite.org/rowidtable.html - - // language=SQLite - mSqlite.exec(R"""( -CREATE TABLE IF NOT EXISTS Sales( - Id INT PRIMARY KEY, - Customer INT, - Deadline DATETIME, - DeliveryTime DATETIME -); -CREATE TABLE IF NOT EXISTS SalesItems( - SaleId INT, - ItemId INT, - Count INT -); - -CREATE TABLE IF NOT EXISTS Purchases( - Id INT PRIMARY KEY, - Factory INT, - OrderTime DATETIME, - DeliveryTime DATETIME -); -CREATE TABLE IF NOT EXISTS PurchasesItems( - PurchaseId INT, - ItemId INT, - Count INT -); - -CREATE TABLE IF NOT EXISTS Deliveries( - Id INT PRIMARY KEY, - ShipmentTime DATETIME, - ArrivalTime DATETIME, - AssociatedOrder INT, - Outgoing BOOLEAN -); -CREATE TABLE IF NOT EXISTS DeliveriesItems( - DeliveryId INT, - ItemId INT, - Count INT -); -)"""); -} - -MainDatabase::MainDatabase(Project& project) - : mProject{ &project } - , mDbWrapper(*this) - , mSales(*this) - , mPurchases(*this) - , mDeliveries(*this) -{ -} - -const SQLite::Database& MainDatabase::GetSQLite() const -{ - return mDbWrapper.mSqlite; -} - -SQLite::Database& MainDatabase::GetSQLite() -{ - return mDbWrapper.mSqlite; -} - -const SalesTable& MainDatabase::GetSales() const -{ - return mSales; -} - -SalesTable& MainDatabase::GetSales() -{ - return mSales; -} - -const PurchasesTable& MainDatabase::GetPurchases() const -{ - return mPurchases; -} - -PurchasesTable& MainDatabase::GetPurchases() -{ - return mPurchases; -} - -const DeliveryTable& MainDatabase::GetDeliveries() const -{ - return mDeliveries; -} - -DeliveryTable& MainDatabase::GetDeliveries() -{ - return mDeliveries; -} diff --git a/core/src/Model/Database.hpp b/core/src/Model/Database.hpp deleted file mode 100644 index 107baf6..0000000 --- a/core/src/Model/Database.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "cplt_fwd.hpp" - -#include <SQLiteCpp/Database.h> -#include <SQLiteCpp/Statement.h> -#include <cstdint> - -enum class TableKind -{ - Sales, - SalesItems, - Purchases, - PurchasesItems, - Deliveries, - DeliveriesItems, -}; - -class SalesTable -{ -public: - SQLite::Statement GetRowCount; - SQLite::Statement GetRows; - SQLite::Statement GetItems; - -public: - SalesTable(MainDatabase& db); -}; - -class PurchasesTable -{ -public: - SQLite::Statement GetRowCount; - SQLite::Statement GetRows; - SQLite::Statement GetItems; - -public: - PurchasesTable(MainDatabase& db); -}; - -class DeliveryTable -{ -public: - SQLite::Statement FilterByTypeAndId; - SQLite::Statement GetItems; - -public: - DeliveryTable(MainDatabase& db); -}; - -class MainDatabase -{ -private: - class DatabaseWrapper - { - public: - SQLite::Database mSqlite; - DatabaseWrapper(MainDatabase& self); - }; - - Project* mProject; - DatabaseWrapper mDbWrapper; - SalesTable mSales; - PurchasesTable mPurchases; - DeliveryTable mDeliveries; - -public: - MainDatabase(Project& project); - - const SQLite::Database& GetSQLite() const; - SQLite::Database& GetSQLite(); - - const SalesTable& GetSales() const; - SalesTable& GetSales(); - const PurchasesTable& GetPurchases() const; - PurchasesTable& GetPurchases(); - const DeliveryTable& GetDeliveries() const; - DeliveryTable& GetDeliveries(); -}; diff --git a/core/src/Model/Filter.cpp b/core/src/Model/Filter.cpp deleted file mode 100644 index 1e4b31b..0000000 --- a/core/src/Model/Filter.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Filter.hpp" diff --git a/core/src/Model/Filter.hpp b/core/src/Model/Filter.hpp deleted file mode 100644 index 1b923e1..0000000 --- a/core/src/Model/Filter.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -class TableRowsFilter -{ - // TODO -}; diff --git a/core/src/Model/GlobalStates.cpp b/core/src/Model/GlobalStates.cpp deleted file mode 100644 index a449afb..0000000 --- a/core/src/Model/GlobalStates.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "GlobalStates.hpp" - -#include "Model/Project.hpp" -#include "Utils/StandardDirectories.hpp" - -#include <json/reader.h> -#include <json/value.h> -#include <json/writer.h> -#include <cassert> -#include <filesystem> -#include <fstream> -#include <memory> - -namespace fs = std::filesystem; - -static std::unique_ptr<GlobalStates> globalStateInstance; -static fs::path globalDataPath; - -void GlobalStates::Init() -{ - Init(StandardDirectories::UserData() / "cplt"); -} - -void GlobalStates::Init(std::filesystem::path userDataDir) -{ - globalStateInstance = std::make_unique<GlobalStates>(); - globalDataPath = userDataDir; - fs::create_directories(globalDataPath); - - // Reading recent projects - { - std::ifstream ifs(globalDataPath / "recents.json"); - if (!ifs) return; - - Json::Value root; - ifs >> root; - - if (!root.isObject()) return; - if (auto& recents = root["RecentProjects"]; recents.isArray()) { - for (auto& elm : recents) { - if (!elm.isString()) continue; - - fs::path path(elm.asCString()); - if (!fs::exists(path)) continue; - - auto utf8String = path.string(); - globalStateInstance->mRecentProjects.push_back(RecentProject{ - .Path = std::move(path), - .CachedUtf8String = std::move(utf8String), - }); - } - } - } -} - -void GlobalStates::Shutdown() -{ - if (!globalStateInstance) return; - - globalStateInstance->SetCurrentProject(nullptr); - - if (globalStateInstance->mDirty) { - globalStateInstance->WriteToDisk(); - } -} - -GlobalStates& GlobalStates::GetInstance() -{ - return *globalStateInstance; -} - -const std::filesystem::path& GlobalStates::GetUserDataPath() -{ - return globalDataPath; -} - -const std::vector<GlobalStates::RecentProject>& GlobalStates::GetRecentProjects() const -{ - return mRecentProjects; -} - -void GlobalStates::ClearRecentProjects() -{ - mRecentProjects.clear(); - MarkDirty(); -} - -void GlobalStates::AddRecentProject(const Project& project) -{ - mRecentProjects.push_back(RecentProject{ - .Path = project.GetPath(), - .CachedUtf8String = project.GetPath().string(), - }); - MarkDirty(); -} - -void GlobalStates::MoveProjectToTop(const Project& project) -{ - for (auto it = mRecentProjects.begin(); it != mRecentProjects.end(); ++it) { - if (it->Path == project.GetPath()) { - std::rotate(it, it + 1, mRecentProjects.end()); - MarkDirty(); - return; - } - } - AddRecentProject(project); -} - -void GlobalStates::RemoveRecentProject(int idx) -{ - assert(idx >= 0 && idx < mRecentProjects.size()); - - mRecentProjects.erase(mRecentProjects.begin() + idx); - MarkDirty(); -} - -bool GlobalStates::HasCurrentProject() const -{ - return mCurrentProject != nullptr; -} - -Project* GlobalStates::GetCurrentProject() const -{ - return mCurrentProject.get(); -} - -void GlobalStates::SetCurrentProject(std::unique_ptr<Project> project) -{ - if (mCurrentProject) { - mCurrentProject->WriteToDisk(); - mCurrentProject = nullptr; - } - if (project) { - MoveProjectToTop(*project); - } - mCurrentProject = std::move(project); -} - -void GlobalStates::WriteToDisk() const -{ - Json::Value root; - - auto& recentProjects = root["RecentProjects"] = Json::Value(Json::arrayValue); - for (auto& [path, _] : mRecentProjects) { - recentProjects.append(Json::Value(path.string())); - } - - std::ofstream ofs(globalDataPath / "recents.json"); - ofs << root; - - mDirty = false; -} - -bool GlobalStates::IsDirty() const -{ - return mDirty; -} - -void GlobalStates::MarkDirty() -{ - mDirty = true; - OnModified(); -} diff --git a/core/src/Model/GlobalStates.hpp b/core/src/Model/GlobalStates.hpp deleted file mode 100644 index cc41bd5..0000000 --- a/core/src/Model/GlobalStates.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "Utils/Sigslot.hpp" -#include "cplt_fwd.hpp" - -#include <filesystem> -#include <string> -#include <vector> - -class GlobalStates -{ -public: - static void Init(); - static void Init(std::filesystem::path userDataDir); - static void Shutdown(); - - static GlobalStates& GetInstance(); - static const std::filesystem::path& GetUserDataPath(); - - struct RecentProject - { - std::filesystem::path Path; - std::string CachedUtf8String; - }; - -public: - Signal<> OnModified; - -private: - std::vector<RecentProject> mRecentProjects; - std::unique_ptr<Project> mCurrentProject; - mutable bool mDirty = false; - -public: - const std::vector<RecentProject>& GetRecentProjects() const; - void ClearRecentProjects(); - void AddRecentProject(const Project& project); - /// Move or add the project to end of the recent projects list. - /// If the project is not in the list of recently used projects, it will be appended, otherwise - /// it will be moved to the end. - void MoveProjectToTop(const Project& project); - void RemoveRecentProject(int idx); - - bool HasCurrentProject() const; - Project* GetCurrentProject() const; - void SetCurrentProject(std::unique_ptr<Project> project); - - // TODO async autosaving to prevent data loss on crash - void WriteToDisk() const; - - bool IsDirty() const; - -private: - void MarkDirty(); -}; diff --git a/core/src/Model/Items.cpp b/core/src/Model/Items.cpp deleted file mode 100644 index 9d2abc6..0000000 --- a/core/src/Model/Items.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "Items.hpp" - -const std::string& ProductItem::GetDescription() const -{ - return mDescription; -} - -void ProductItem::SetDescription(std::string description) -{ - mDescription = std::move(description); -} - -int ProductItem::GetPrice() const -{ - return mPrice; -} -void ProductItem::SetPrice(int price) -{ - mPrice = price; -} - -int ProductItem::GetStock() const -{ - return mStock; -} - -void ProductItem::SetStock(int stock) -{ - mStock = stock; -} - -Json::Value ProductItem::Serialize() const -{ - Json::Value elm; - elm["Description"] = mDescription; - elm["Price"] = mPrice; - elm["Stock"] = mStock; - return elm; -} - -void ProductItem::Deserialize(const Json::Value& elm) -{ - mDescription = elm["Description"].asString(); - mPrice = elm["Price"].asInt(); - mStock = elm["Stock"].asInt(); -} - -const std::string& FactoryItem::GetDescription() const -{ - return mDescription; -} - -void FactoryItem::SetDescription(std::string description) -{ - mDescription = std::move(description); -} - -const std::string& FactoryItem::GetEmail() const -{ - return mEmail; -} - -void FactoryItem::SetEmail(std::string email) -{ - mEmail = std::move(email); -} - -Json::Value FactoryItem::Serialize() const -{ - Json::Value elm; - elm["Description"] = mDescription; - elm["Email"] = mEmail; - return elm; -} - -void FactoryItem::Deserialize(const Json::Value& elm) -{ - mDescription = elm["Description"].asString(); - mEmail = elm["Email"].asString(); -} - -const std::string& CustomerItem::GetDescription() const -{ - return mDescription; -} - -void CustomerItem::SetDescription(std::string description) -{ - mDescription = std::move(description); -} - -const std::string& CustomerItem::GetEmail() const -{ - return mEmail; -} - -void CustomerItem::SetEmail(std::string email) -{ - mEmail = std::move(email); -} - -Json::Value CustomerItem::Serialize() const -{ - Json::Value elm; - elm["Description"] = mDescription; - elm["Email"] = mEmail; - return elm; -} - -void CustomerItem::Deserialize(const Json::Value& elm) -{ - mDescription = elm["Description"].asString(); - mEmail = elm["Email"].asString(); -} diff --git a/core/src/Model/Items.hpp b/core/src/Model/Items.hpp deleted file mode 100644 index 859aedf..0000000 --- a/core/src/Model/Items.hpp +++ /dev/null @@ -1,253 +0,0 @@ -#pragma once - -#include "cplt_fwd.hpp" - -#include <json/reader.h> -#include <json/value.h> -#include <json/writer.h> -#include <tsl/array_map.h> -#include <cstddef> -#include <limits> -#include <stdexcept> -#include <string> -#include <string_view> -#include <utility> -#include <vector> - -template <class T> -class ItemList -{ -private: - std::vector<T> mStorage; - tsl::array_map<char, size_t> mNameLookup; - -public: - template <class... Args> - T& Insert(std::string name, Args... args) - { - auto iter = mNameLookup.find(name); - if (iter != mNameLookup.end()) { - throw std::runtime_error("Duplicate key."); - } - - for (size_t i = 0; i < mStorage.size(); ++i) { - if (mStorage[i].IsInvalid()) { - mStorage[i] = T(*this, i, std::move(name), std::forward<Args>(args)...); - mNameLookup.insert(name, i); - return mStorage[i]; - } - } - - size_t id = mStorage.size(); - mNameLookup.insert(name, id); - mStorage.emplace_back(*this, id, std::move(name), std::forward<Args>(args)...); - return mStorage[id]; - } - - void Remove(size_t index) - { - auto& item = mStorage[index]; - mNameLookup.erase(item.GetName()); - mStorage[index] = T(*this); - } - - T* Find(size_t id) - { - return &mStorage[id]; - } - - const T* Find(size_t id) const - { - return &mStorage[id]; - } - - const T* Find(std::string_view name) const - { - auto iter = mNameLookup.find(name); - if (iter != mNameLookup.end()) { - return &mStorage[iter.value()]; - } else { - return nullptr; - } - } - - Json::Value Serialize() const - { - Json::Value items(Json::arrayValue); - for (auto& item : mStorage) { - if (!item.IsInvalid()) { - auto elm = item.Serialize(); - elm["Id"] = item.GetId(); - elm["Name"] = item.GetName(); - items.append(elm); - } - } - - Json::Value root; - root["MaxItemId"] = mStorage.size(); - root["Items"] = std::move(items); - - return root; - } - - ItemList() = default; - - ItemList(const Json::Value& root) - { - constexpr const char* kMessage = "Failed to load item list from JSON."; - - auto& itemCount = root["MaxItemId"]; - if (!itemCount.isIntegral()) throw std::runtime_error(kMessage); - - mStorage.resize(itemCount.asInt64(), T(*this)); - - auto& items = root["Items"]; - if (!items.isArray()) throw std::runtime_error(kMessage); - - for (auto& elm : items) { - if (!elm.isObject()) throw std::runtime_error(kMessage); - - auto& id = elm["Id"]; - if (!id.isIntegral()) throw std::runtime_error(kMessage); - auto& name = elm["Name"]; - if (!name.isString()) throw std::runtime_error(kMessage); - - size_t iid = id.asInt64(); - mStorage[iid] = T(*this, iid, name.asString()); - mStorage[iid].Deserialize(elm); - } - } - - typename decltype(mStorage)::iterator begin() - { - return mStorage.begin(); - } - - typename decltype(mStorage)::iterator end() - { - return mStorage.end(); - } - - typename decltype(mStorage)::const_iterator begin() const - { - return mStorage.begin(); - } - - typename decltype(mStorage)::const_iterator end() const - { - return mStorage.end(); - } - -private: - template <class TSelf> - friend class ItemBase; - - void UpdateItemName(const T& item, const std::string& newName) - { - mNameLookup.erase(item.GetName()); - mNameLookup.insert(newName, item.GetId()); - } -}; - -template <class TSelf> -class ItemBase -{ -private: - ItemList<TSelf>* mList; - size_t mId; - std::string mName; - -public: - ItemBase(ItemList<TSelf>& list, size_t id = std::numeric_limits<size_t>::max(), std::string name = "") - : mList{ &list } - , mId{ id } - , mName{ std::move(name) } - { - } - - bool IsInvalid() const - { - return mId == std::numeric_limits<size_t>::max(); - } - - ItemList<TSelf>& GetList() const - { - return *mList; - } - - size_t GetId() const - { - return mId; - } - - const std::string& GetName() const - { - return mName; - } - - void SetName(std::string name) - { - mList->UpdateItemName(static_cast<TSelf&>(*this), name); - mName = std::move(name); - } -}; - -class ProductItem : public ItemBase<ProductItem> -{ -private: - std::string mDescription; - int mPrice = 0; - int mStock = 0; - -public: - using ItemBase::ItemBase; - - const std::string& GetDescription() const; - void SetDescription(std::string description); - /// Get the price of this item in US cents. - int GetPrice() const; - void SetPrice(int price); - /// Get the current number of this product in warehouse. - /// This is a housekeeping field and shouldn't be editable by the user from the UI. - int GetStock() const; - void SetStock(int stock); - - Json::Value Serialize() const; - void Deserialize(const Json::Value& elm); -}; - -class FactoryItem : public ItemBase<FactoryItem> -{ -private: - std::string mDescription; - std::string mEmail; - -public: - using ItemBase::ItemBase; - - const std::string& GetDescription() const; - void SetDescription(std::string description); - const std::string& GetEmail() const; - void SetEmail(std::string email); - - Json::Value Serialize() const; - void Deserialize(const Json::Value& elm); -}; - -class CustomerItem : public ItemBase<CustomerItem> -{ -private: - std::string mDescription; - std::string mEmail; - -public: - using ItemBase::ItemBase; - - const std::string& GetDescription() const; - void SetDescription(std::string description); - const std::string& GetEmail() const; - void SetEmail(std::string email); - - Json::Value Serialize() const; - void Deserialize(const Json::Value& elm); -}; diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp deleted file mode 100644 index 523ee9b..0000000 --- a/core/src/Model/Project.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "Project.hpp" - -#include "Model/Workflow/Workflow.hpp" -#include "Utils/Macros.hpp" - -#include <json/reader.h> -#include <json/value.h> -#include <json/writer.h> -#include <filesystem> -#include <fstream> -#include <stdexcept> -#include <utility> - -namespace fs = std::filesystem; - -template <class T> -static void ReadItemList(ItemList<T>& list, const fs::path& filePath) -{ - std::ifstream ifs(filePath); - if (ifs) { - Json::Value root; - ifs >> root; - - list = ItemList<T>(root); - } -} - -static void CreateProjectSubfolders(const Project& project) -{ - fs::create_directory(project.GetDatabasesDirectory()); - fs::create_directory(project.GetItemsDirectory()); - fs::create_directory(project.GetWorkflowsDirectory()); - fs::create_directory(project.GetTemplatesDirectory()); -} - -Project::Project(fs::path rootPath) - : mRootPath{ std::move(rootPath) } - , mRootPathString{ mRootPath.string() } - , Workflows(*this) - , Templates(*this) - , Database(*this) -{ - // TODO better diagnostic - const char* kInvalidFormatErr = "Failed to load project: invalid format."; - - std::ifstream ifs(mRootPath / "cplt_project.json"); - if (!ifs) { - std::string message; - message += "Failed to load project file at '"; - message += mRootPath.string(); - message += "'."; - throw std::runtime_error(message); - } - - { - Json::Value root; - ifs >> root; - - const auto& croot = root; // Use const reference so that accessors default to returning a null if not found, instead of silently creating new elements - if (!croot.isObject()) { - throw std::runtime_error(kInvalidFormatErr); - } - - if (auto& name = croot["Name"]; name.isString()) { - mName = name.asString(); - } else { - throw std::runtime_error(kInvalidFormatErr); - } - } - - CreateProjectSubfolders(*this); - - auto itemsDir = mRootPath / "items"; - ReadItemList(Products, itemsDir / "products.json"); - ReadItemList(Factories, itemsDir / "factories.json"); - ReadItemList(Customers, itemsDir / "customers.json"); - - Workflows.Reload(); - Templates.Reload(); -} - -Project::Project(fs::path rootPath, std::string name) - : mRootPath{ std::move(rootPath) } - , mRootPathString{ mRootPath.string() } - , mName{ std::move(name) } - , Workflows(*this) - , Templates(*this) - , Database(*this) -{ - CreateProjectSubfolders(*this); -} - -const fs::path& Project::GetPath() const -{ - return mRootPath; -} - -const std::string& Project::GetPathString() const -{ - return mRootPathString; -} - -fs::path Project::GetDatabasesDirectory() const -{ - return mRootPath / "databases"; -} - -fs::path Project::GetItemsDirectory() const -{ - return mRootPath / "items"; -} - -fs::path Project::GetWorkflowsDirectory() const -{ - return mRootPath / "workflows"; -} - -fs::path Project::GetWorkflowPath(std::string_view name) const -{ - return (mRootPath / "workflows" / name).concat(".cplt-workflow"); -} - -fs::path Project::GetTemplatesDirectory() const -{ - return mRootPath / "templates"; -} - -fs::path Project::GetTemplatePath(std::string_view name) const -{ - return (mRootPath / "templates" / name).concat(".cplt-template"); -} - -const std::string& Project::GetName() const -{ - return mName; -} - -void Project::SetName(std::string name) -{ - mName = std::move(name); -} - -Json::Value Project::Serialize() -{ - Json::Value root(Json::objectValue); - - root["Name"] = mName; - - return root; -} - -template <class T> -static void WriteItemList(ItemList<T>& list, const fs::path& filePath) -{ - std::ofstream ofs(filePath); - ofs << list.Serialize(); -} - -void Project::WriteToDisk() -{ - std::ofstream ofs(mRootPath / "cplt_project.json"); - ofs << this->Serialize(); - - auto itemsDir = GetItemsDirectory(); - WriteItemList(Products, itemsDir / "products.json"); - WriteItemList(Factories, itemsDir / "factories.json"); - WriteItemList(Customers, itemsDir / "customers.json"); -} diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp deleted file mode 100644 index 17d9acb..0000000 --- a/core/src/Model/Project.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "Model/Assets.hpp" -#include "Model/Database.hpp" -#include "Model/Items.hpp" -#include "Model/Template/Template.hpp" -#include "Model/Workflow/Workflow.hpp" - -#include <json/forwards.h> -#include <tsl/array_map.h> -#include <filesystem> -#include <string> -#include <string_view> - -class Project -{ -private: - std::filesystem::path mRootPath; - std::string mRootPathString; - std::string mName; - - // (Exception to style guidelines) - // This is put after the private fields, so that when XxxDatabase's constructor runs, all of them will be initialized -public: - WorkflowAssetList Workflows; - TemplateAssetList Templates; - ItemList<ProductItem> Products; - ItemList<FactoryItem> Factories; - ItemList<CustomerItem> Customers; - MainDatabase Database; - -public: - /// Load the project from a directory containing the cplt_project.json file. - /// This only loads the main project file, the caller needs to - Project(std::filesystem::path rootPath); - - /// Create a project with the given name in the given path. Note that the path should be a directory that will contain the project files once created. - /// This function assumes the given directory will exist and is empty. - Project(std::filesystem::path rootPath, std::string name); - - /// Path to a *directory* that contains the project file. - const std::filesystem::path& GetPath() const; - const std::string& GetPathString() const; - - std::filesystem::path GetDatabasesDirectory() const; - std::filesystem::path GetItemsDirectory() const; - std::filesystem::path GetWorkflowsDirectory() const; - std::filesystem::path GetWorkflowPath(std::string_view name) const; - std::filesystem::path GetTemplatesDirectory() const; - std::filesystem::path GetTemplatePath(std::string_view name) const; - - const std::string& GetName() const; - void SetName(std::string name); - - Json::Value Serialize(); - void WriteToDisk(); -}; diff --git a/core/src/Model/Template/TableTemplate.cpp b/core/src/Model/Template/TableTemplate.cpp deleted file mode 100644 index 57caac4..0000000 --- a/core/src/Model/Template/TableTemplate.cpp +++ /dev/null @@ -1,591 +0,0 @@ -#include "TableTemplate.hpp" - -#include "Utils/IO/StringIntegration.hpp" -#include "Utils/IO/TslArrayIntegration.hpp" -#include "Utils/IO/VectorIntegration.hpp" - -#include <xlsxwriter.h> -#include <algorithm> -#include <charconv> -#include <cstddef> -#include <cstdint> -#include <iostream> -#include <map> - -bool TableCell::IsDataHoldingCell() const -{ - return IsPrimaryCell() || !IsMergedCell(); -} - -bool TableCell::IsPrimaryCell() const -{ - return PrimaryCellLocation == Location; -} - -bool TableCell::IsMergedCell() const -{ - return PrimaryCellLocation.x == -1 || PrimaryCellLocation.y == -1; -} - -template <class TTableCell, class TStream> -void OperateStreamForTableCell(TTableCell& cell, TStream& proxy) -{ - proxy.template ObjectAdapted<DataStreamAdapters::String>(cell.Content); - proxy.Object(cell.Location); - proxy.Object(cell.PrimaryCellLocation); - proxy.Value(cell.SpanX); - proxy.Value(cell.SpanY); - proxy.Enum(cell.HorizontalAlignment); - proxy.Enum(cell.VerticalAlignment); - proxy.Enum(cell.Type); - proxy.Value(cell.DataId); -} - -void TableCell::ReadFromDataStream(InputDataStream& stream) -{ - ::OperateStreamForTableCell(*this, stream); -} - -void TableCell::WriteToDataStream(OutputDataStream& stream) const -{ - ::OperateStreamForTableCell(*this, stream); -} - -Vec2i TableArrayGroup::GetLeftCell() const -{ - return { Row, LeftCell }; -} - -Vec2i TableArrayGroup::GetRightCell() const -{ - return { Row, RightCell }; -} - -int TableArrayGroup::GetCount() const -{ - return RightCell - LeftCell + 1; -} - -Vec2i TableArrayGroup::FindCell(std::string_view name) -{ - // TODO - return Vec2i{}; -} - -template <class TMap> -static bool UpdateElementName(TMap& map, std::string_view oldName, std::string_view newName) -{ - auto iter = map.find(oldName); - if (iter == map.end()) { - return false; - } - - auto elm = iter.value(); - auto [DISCARD, inserted] = map.insert(newName, elm); - if (!inserted) { - return false; - } - - map.erase(iter); - return true; -} - -bool TableArrayGroup::UpdateCellName(std::string_view oldName, std::string_view newName) -{ - return ::UpdateElementName(mName2Cell, oldName, newName); -} - -template <class TTableArrayGroup, class TStream> -void OperateStreamForTableArrayGroup(TTableArrayGroup& group, TStream& stream) -{ - stream.Value(group.Row); - stream.Value(group.LeftCell); - stream.Value(group.RightCell); -} - -void TableArrayGroup::ReadFromDataStream(InputDataStream& stream) -{ - ::OperateStreamForTableArrayGroup(*this, stream); -} - -void TableArrayGroup::WriteToDataStream(OutputDataStream& stream) const -{ - ::OperateStreamForTableArrayGroup(*this, stream); -} - -TableInstantiationParameters::TableInstantiationParameters(const TableTemplate& table) - : mTable{ &table } -{ -} - -TableInstantiationParameters& TableInstantiationParameters::ResetTable(const TableTemplate& newTable) -{ - mTable = &newTable; - return *this; -} - -TableInstantiationParameters TableInstantiationParameters::RebindTable(const TableTemplate& newTable) const -{ - TableInstantiationParameters result(newTable); - result.SingularCells = this->SingularCells; - result.ArrayGroups = this->ArrayGroups; - return result; -} - -const TableTemplate& TableInstantiationParameters::GetTable() const -{ - return *mTable; -} - -bool TableTemplate::IsInstance(const Template* tmpl) -{ - return tmpl->GetKind() == KD_Table; -} - -TableTemplate::TableTemplate() - : Template(KD_Table) -{ -} - -int TableTemplate::GetTableWidth() const -{ - return mColumnWidths.size(); -} - -int TableTemplate::GetTableHeight() const -{ - return mRowHeights.size(); -} - -void TableTemplate::Resize(int newWidth, int newHeight) -{ - // TODO this doesn't gracefully handle resizing to a smaller size which trims some merged cells - - std::vector<TableCell> cells; - cells.reserve(newWidth * newHeight); - - int tableWidth = GetTableWidth(); - int tableHeight = GetTableHeight(); - - for (int y = 0; y < newHeight; ++y) { - if (y >= tableHeight) { - for (int x = 0; x < newWidth; ++x) { - cells.push_back(TableCell{}); - } - continue; - } - - for (int x = 0; x < newWidth; ++x) { - if (x >= tableWidth) { - cells.push_back(TableCell{}); - } else { - auto& cell = GetCell({ x, y }); - cells.push_back(std::move(cell)); - } - } - } - - mCells = std::move(cells); - mColumnWidths.resize(newWidth, 80); - mRowHeights.resize(newHeight, 20); -} - -int TableTemplate::GetRowHeight(int row) const -{ - return mRowHeights[row]; -} - -void TableTemplate::SetRowHeight(int row, int height) -{ - mRowHeights[row] = height; -} - -int TableTemplate::GetColumnWidth(int column) const -{ - return mColumnWidths[column]; -} - -void TableTemplate::SetColumnWidth(int column, int width) -{ - mColumnWidths[column] = width; -} - -const TableCell& TableTemplate::GetCell(Vec2i pos) const -{ - int tableWidth = GetTableWidth(); - return mCells[pos.y * tableWidth + pos.x]; -} - -TableCell& TableTemplate::GetCell(Vec2i pos) -{ - return const_cast<TableCell&>(const_cast<const TableTemplate*>(this)->GetCell(pos)); -} - -void TableTemplate::SetCellType(Vec2i pos, TableCell::CellType type) -{ - auto& cell = GetCell(pos); - if (cell.Type == type) { - return; - } - - switch (cell.Type) { - // Nothing to change - case TableCell::ConstantCell: break; - - case TableCell::SingularParametricCell: - mName2Parameters.erase(cell.Content); - break; - - case TableCell::ArrayParametricCell: { - auto& ag = mArrayGroups[cell.DataId]; - if (pos.x == ag.LeftCell) { - ag.LeftCell++; - } else if (pos.x == ag.RightCell) { - ag.RightCell--; - } else { - } - } break; - } - - switch (type) { - // Nothing to do - case TableCell::ConstantCell: break; - - case TableCell::SingularParametricCell: { - int idx = pos.y * GetTableWidth() + pos.x; - auto [DISCARD, inserted] = mName2Parameters.insert(cell.Content, idx); - - // Duplicate name - if (!inserted) { - return; - } - } break; - - case TableCell::ArrayParametricCell: { - auto ptr = AddArrayGroup(pos.y, pos.x, pos.x); - - // Duplicate name - if (ptr == nullptr) { - return; - } - } break; - } - - cell.Type = type; -} - -bool TableTemplate::UpdateParameterName(std::string_view oldName, std::string_view newName) -{ - return ::UpdateElementName(mName2Parameters, oldName, newName); -} - -int TableTemplate::GetArrayGroupCount() const -{ - return mArrayGroups.size(); -} - -const TableArrayGroup& TableTemplate::GetArrayGroup(int id) const -{ - return mArrayGroups[id]; -} - -TableArrayGroup& TableTemplate::GetArrayGroup(int id) -{ - return mArrayGroups[id]; -} - -TableArrayGroup* TableTemplate::AddArrayGroup(int row, int left, int right) -{ - // size_t max value: 18446744073709551615 - // ^~~~~~~~~~~~~~~~~~~~ 20 chars - char name[20]; - auto res = std::to_chars(std::begin(name), std::end(name), mArrayGroups.size()); - std::string_view nameStr(name, res.ptr - name); - - return AddArrayGroup(nameStr, row, left, right); -} - -TableArrayGroup* TableTemplate::AddArrayGroup(std::string_view name, int row, int left, int right) -{ - assert(row >= 0 && row < GetTableHeight()); - assert(left >= 0 && left < GetTableWidth()); - assert(right >= 0 && right < GetTableWidth()); - - // TODO check for overlap - - if (left > right) { - std::swap(left, right); - } - - auto [DISCARD, inserted] = mName2ArrayGroups.insert(name, (int)mArrayGroups.size()); - if (!inserted) { - return nullptr; - } - - mArrayGroups.push_back(TableArrayGroup{ - .Row = row, - .LeftCell = left, - .RightCell = right, - }); - auto& ag = mArrayGroups.back(); - - for (int x = left; x <= right; x++) { - auto& cell = GetCell({ x, row }); - - // Update type - cell.Type = TableCell::ArrayParametricCell; - - // Insert parameter name lookup - while (true) { - auto [DISCARD, inserted] = ag.mName2Cell.insert(cell.Content, x); - if (inserted) { - break; - } - - cell.Content += "-"; - } - } - - return &ag; -} - -bool TableTemplate::UpdateArrayGroupName(std::string_view oldName, std::string_view newName) -{ - return ::UpdateElementName(mName2ArrayGroups, oldName, newName); -} - -bool TableTemplate::ExtendArrayGroupLeft(int id, int n) -{ - assert(n > 0); - - auto& ag = mArrayGroups[id]; - ag.LeftCell -= n; - - return false; -} - -bool TableTemplate::ExtendArrayGroupRight(int id, int n) -{ - assert(n > 0); - - auto& ag = mArrayGroups[id]; - ag.RightCell += n; - - return false; -} - -TableCell* TableTemplate::FindCell(std::string_view name) -{ - auto iter = mName2Parameters.find(name); - if (iter != mName2Parameters.end()) { - return &mCells[iter.value()]; - } else { - return nullptr; - } -} - -TableArrayGroup* TableTemplate::FindArrayGroup(std::string_view name) -{ - auto iter = mName2ArrayGroups.find(name); - if (iter != mName2ArrayGroups.end()) { - return &mArrayGroups[iter.value()]; - } else { - return nullptr; - } -} - -TableTemplate::MergeCellsResult TableTemplate::MergeCells(Vec2i topLeft, Vec2i bottomRight) -{ - auto SortTwo = [](int& a, int& b) { - if (a > b) { - std::swap(a, b); - } - }; - SortTwo(topLeft.x, bottomRight.x); - SortTwo(topLeft.y, bottomRight.y); - - auto ResetProgress = [&]() { - for (int y = topLeft.y; y < bottomRight.y; ++y) { - for (int x = topLeft.x; x < bottomRight.x; ++x) { - auto& cell = GetCell({ x, y }); - cell.PrimaryCellLocation = { -1, -1 }; - } - } - }; - - for (int y = topLeft.y; y < bottomRight.y; ++y) { - for (int x = topLeft.x; x < bottomRight.x; ++x) { - auto& cell = GetCell({ x, y }); - if (cell.IsMergedCell()) { - ResetProgress(); - return MCR_CellAlreadyMerged; - } - - cell.PrimaryCellLocation = topLeft; - } - } - - auto& primaryCell = GetCell(topLeft); - primaryCell.SpanX = bottomRight.x - topLeft.x; - primaryCell.SpanY = bottomRight.y - topLeft.y; - - return MCR_Success; -} - -TableTemplate::BreakCellsResult TableTemplate::BreakCells(Vec2i topLeft) -{ - auto& primaryCell = GetCell(topLeft); - if (!primaryCell.IsMergedCell()) { - return BCR_CellNotMerged; - } - - for (int dy = 0; dy < primaryCell.SpanY; ++dy) { - for (int dx = 0; dx < primaryCell.SpanX; ++dx) { - auto& cell = GetCell({ topLeft.x + dx, topLeft.y + dy }); - cell.PrimaryCellLocation = { -1, -1 }; - } - } - - primaryCell.SpanX = 1; - primaryCell.SpanY = 1; - - return BCR_Success; -} - -lxw_workbook* TableTemplate::InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const -{ - auto workbook = workbook_new("Table.xlsx"); - InstantiateToExcelWorksheet(workbook, params); - return workbook; -} - -lxw_worksheet* TableTemplate::InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const -{ - auto worksheet = workbook_add_worksheet(workbook, "CpltExport.xlsx"); - - // Map: row number -> length of generated ranges - std::map<int, int> generatedRanges; - - for (size_t i = 0; i < mArrayGroups.size(); ++i) { - auto& info = mArrayGroups[i]; - auto& param = params.ArrayGroups[i]; - - auto iter = generatedRanges.find(i); - if (iter != generatedRanges.end()) { - int available = iter->second; - if (available >= param.size()) { - // Current space is enough to fit in this array group, skip - continue; - } - } - - // Not enough space to fit in this array group, update (or insert) the appropriate amount of generated rows - int row = i; - int count = param.size(); - generatedRanges.try_emplace(row, count); - } - - auto GetOffset = [&](int y) -> int { - // std::find_if <values less than y> - int verticalOffset = 0; - for (auto it = generatedRanges.begin(); it != generatedRanges.end() && it->first < y; ++it) { - verticalOffset += it->second; - } - return verticalOffset; - }; - - auto WriteCell = [&](int row, int col, const TableCell& cell, const char* text) -> void { - if (cell.IsPrimaryCell()) { - int lastRow = row + cell.SpanY - 1; - int lastCol = col + cell.SpanX - 1; - // When both `string` and `format` are null, the top-left cell contents are untouched (what we just wrote in the above switch) - worksheet_merge_range(worksheet, row, col, lastRow, lastCol, text, nullptr); - } else { - worksheet_write_string(worksheet, row, col, text, nullptr); - } - }; - - // Write/instantiate all array groups - for (size_t i = 0; i < mArrayGroups.size(); ++i) { - auto& groupInfo = mArrayGroups[i]; - auto& groupParams = params.ArrayGroups[i]; - - int rowCellCount = groupInfo.GetCount(); - int rowCount = groupParams.size(); - int baseRowIdx = groupInfo.Row + GetOffset(groupInfo.Row); - - // For each row that would be generated - for (int rowIdx = 0; rowIdx < rowCount; ++rowIdx) { - auto& row = groupParams[rowIdx]; - - // For each cell in the row - for (int rowCellIdx = 0; rowCellIdx < rowCellCount; ++rowCellIdx) { - // TODO support merged cells in array groups - worksheet_write_string(worksheet, baseRowIdx + rowIdx, rowCellIdx, row[rowCellIdx].c_str(), nullptr); - } - } - } - - int tableWidth = GetTableWidth(); - int tableHeight = GetTableHeight(); - - // Write all regular and singular parameter cells - for (int y = 0; y < tableHeight; ++y) { - for (int x = 0; x < tableWidth; ++x) { - auto& cell = GetCell({ x, y }); - - if (!cell.IsDataHoldingCell()) { - continue; - } - - switch (cell.Type) { - case TableCell::ConstantCell: { - int row = y + GetOffset(y); - int col = x; - - WriteCell(row, col, cell, cell.Content.c_str()); - } break; - - case TableCell::SingularParametricCell: { - int row = y + GetOffset(y); - int col = x; - - auto iter = params.SingularCells.find({ x, y }); - if (iter != params.SingularCells.end()) { - WriteCell(row, col, cell, iter.value().c_str()); - } - } break; - - // See loop above that processes whole array groups at the same time - case TableCell::ArrayParametricCell: break; - } - } - } - - return worksheet; -} - -class TableTemplate::Private -{ -public: - template <class TTableTemplate, class TProxy> - static void OperateStream(TTableTemplate& table, TProxy& proxy) - { - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mColumnWidths); - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mRowHeights); - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mCells); - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mArrayGroups); - proxy.template ObjectAdapted<DataStreamAdapters::TslArrayMap<>>(table.mName2Parameters); - proxy.template ObjectAdapted<DataStreamAdapters::TslArrayMap<>>(table.mName2ArrayGroups); - } -}; - -void TableTemplate::ReadFromDataStream(InputDataStream& stream) -{ - Private::OperateStream(*this, stream); -} - -void TableTemplate::WriteToDataStream(OutputDataStream& stream) const -{ - Private::OperateStream(*this, stream); -} diff --git a/core/src/Model/Template/TableTemplate.hpp b/core/src/Model/Template/TableTemplate.hpp deleted file mode 100644 index 8771867..0000000 --- a/core/src/Model/Template/TableTemplate.hpp +++ /dev/null @@ -1,223 +0,0 @@ -#pragma once - -#include "Model/Template/Template.hpp" -#include "Utils/Vector.hpp" -#include "Utils/VectorHash.hpp" -#include "cplt_fwd.hpp" - -#include <tsl/array_map.h> -#include <tsl/robin_map.h> -#include <string> -#include <string_view> -#include <vector> - -class TableCell -{ -public: - enum TextAlignment - { - /// For horizontal alignment, this means align left. For vertical alignment, this means align top. - AlignAxisMin, - /// Align middle of the text to the middle of the axis. - AlignCenter, - /// For horizontal alignment, this means align right. For vertical alignment, this means align bottom. - AlignAxisMax, - }; - - enum CellType - { - ConstantCell, - SingularParametricCell, - ArrayParametricCell, - }; - -public: - /// Display content of this cell. This doesn't necessarily have to line up with the parameter name (if this cell is one). - std::string Content; - Vec2i Location; - /// Location of the primary (top left) cell, if this cell is a part of a merged group. - /// Otherwise, either component of this field shall be -1. - Vec2i PrimaryCellLocation{ -1, -1 }; - int SpanX = 0; - int SpanY = 0; - TextAlignment HorizontalAlignment = AlignCenter; - TextAlignment VerticalAlignment = AlignCenter; - CellType Type = ConstantCell; - /// The id of the group description object, if this cell isn't a constant or singular parameter cell. Otherwise, this value is -1. - int DataId = -1; - -public: - /// Return whether this cell holds meaningful data, i.e. true when this cell is either unmerged or the primary cell of a merged range. - bool IsDataHoldingCell() const; - /// Return whether this cell is the primary (i.e. top left) cell of a merged range or not. - bool IsPrimaryCell() const; - /// Return whether this cell is a part of a merged range or not. Includes the primary cell. - bool IsMergedCell() const; - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; -}; - -// TODO support reverse (bottom to top) filling order -// TODO support horizontal filling order - -/// Parameter group information for a grouped array of cells. When instantiated, an array of 0 or more -/// elements shall be provided by the user, which will replace the group of templated cells with a list -/// of rows, each instantiated with the n-th element in the provided array. -/// \code -/// [["foo", "bar", "foobar"], -/// ["a", "b", c"], -/// ["1", "2", "3"], -/// ["x", "y", "z"]] -/// // ... may be more -/// \endcode -/// This would create 4 rows of data in the place of the original parameter group. -/// -/// If more than one array parameter groups are on the same row, they would share space between each other: -/// \code -/// | 2 elements was fed to it -/// | | 1 element was fed to it -/// V V -/// {~~~~~~~~~~~~~~~~}{~~~~~~~~~~~~~~} -/// +------+---------+---------------+ -/// | Foo | Example | Another group | -/// +------+---------+---------------+ -/// | Cool | Example | | -/// +------+---------+---------------+ -/// \endcode -/// -/// \see TableCell -/// \see TableInstantiationParameters -/// \see TableTemplate -class TableArrayGroup -{ -public: - /// Parameter name mapped to cell location (index from LeftCell). - tsl::array_map<char, int> mName2Cell; - int Row; - /// Leftmost cell in this group - int LeftCell; - /// Rightmost cell in this group - int RightCell; - -public: - Vec2i GetLeftCell() const; - Vec2i GetRightCell() const; - int GetCount() const; - - /// Find the location of the cell within this array group that has the given name. - Vec2i FindCell(std::string_view name); - bool UpdateCellName(std::string_view oldName, std::string_view newName); - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; -}; - -// Forward declaration of libxlsxwriter structs -struct lxw_workbook; -struct lxw_worksheet; - -/// An object containing the necessary information to instantiate a table template. -/// \see TableTemplate -class TableInstantiationParameters -{ -private: - const TableTemplate* mTable; - -public: - tsl::robin_map<Vec2i, std::string> SingularCells; - - using ArrayGroupRow = std::vector<std::string>; - using ArrayGroupData = std::vector<ArrayGroupRow>; - std::vector<ArrayGroupData> ArrayGroups; - -public: - TableInstantiationParameters(const TableTemplate& table); - - TableInstantiationParameters& ResetTable(const TableTemplate& newTable); - TableInstantiationParameters RebindTable(const TableTemplate& newTable) const; - - const TableTemplate& GetTable() const; -}; - -/// A table template, where individual cells can be filled by workflows instantiating this template. Merged cells, -/// parametric rows/columns, and grids are also supported. -/// -/// This current supports exporting to xlsx files. -class TableTemplate : public Template -{ - friend class TableSingleParamsIter; - friend class TableArrayGroupsIter; - class Private; - -private: - /// Map from parameter name to index of the parameter cell (stored in mCells). - tsl::array_map<char, int> mName2Parameters; - /// Map from array group name to the index of the array group (stored in mArrayGroups). - tsl::array_map<char, int> mName2ArrayGroups; - std::vector<TableCell> mCells; - std::vector<TableArrayGroup> mArrayGroups; - std::vector<int> mRowHeights; - std::vector<int> mColumnWidths; - -public: - static bool IsInstance(const Template* tmpl); - TableTemplate(); - - int GetTableWidth() const; - int GetTableHeight() const; - void Resize(int newWidth, int newHeight); - - int GetRowHeight(int row) const; - void SetRowHeight(int row, int height); - int GetColumnWidth(int column) const; - void SetColumnWidth(int column, int width); - - const TableCell& GetCell(Vec2i pos) const; - TableCell& GetCell(Vec2i pos); - /// <ul> - /// <li> In case of becoming a SingularParametricCell: the parameter name is filled with TableCell::Content. - /// <li> In case of becoming a ArrayGroupParametricCell: the array group name is automatically generated as the nth group it would be come. - /// i.e., if there aRe currently 3 groups, the newly created group would be named "4". - /// If this name collides with an existing group, hyphens \c - will be append to the name until no collision happens. - /// </ul> - void SetCellType(Vec2i pos, TableCell::CellType type); - - /// Updates the parameter cell to a new name. Returns true on success and false on failure (param not found or name duplicates). - bool UpdateParameterName(std::string_view oldName, std::string_view newName); - - int GetArrayGroupCount() const; - const TableArrayGroup& GetArrayGroup(int id) const; - TableArrayGroup& GetArrayGroup(int id); - TableArrayGroup* AddArrayGroup(int row, int left, int right); - TableArrayGroup* AddArrayGroup(std::string_view name, int row, int left, int right); - bool UpdateArrayGroupName(std::string_view oldName, std::string_view newName); - bool ExtendArrayGroupLeft(int id, int n); - bool ExtendArrayGroupRight(int id, int n); - - /// Find a singular parameter cell by its name. This does not include cells within an array group. - TableCell* FindCell(std::string_view name); - - /// Find an array group by its name. - TableArrayGroup* FindArrayGroup(std::string_view name); - - enum MergeCellsResult - { - MCR_CellAlreadyMerged, - MCR_Success, - }; - MergeCellsResult MergeCells(Vec2i topLeft, Vec2i bottomRight); - - enum BreakCellsResult - { - BCR_CellNotMerged, - BCR_Success, - }; - BreakCellsResult BreakCells(Vec2i topLeft); - - lxw_workbook* InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const; - lxw_worksheet* InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const; - - void ReadFromDataStream(InputDataStream& stream) override; - void WriteToDataStream(OutputDataStream& stream) const override; -}; diff --git a/core/src/Model/Template/TableTemplateIterator.cpp b/core/src/Model/Template/TableTemplateIterator.cpp deleted file mode 100644 index 19e30b9..0000000 --- a/core/src/Model/Template/TableTemplateIterator.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "TableTemplateIterator.hpp" - -TableSingleParamsIter::TableSingleParamsIter(TableTemplate& tmpl) - : mTemplate{ &tmpl } - , mIter{ tmpl.mName2Parameters.begin() } -{ -} - -bool TableSingleParamsIter::HasNext() const -{ - return mIter != mTemplate->mName2Parameters.end(); -} - -TableCell& TableSingleParamsIter::Next() -{ - int id = mIter.value(); - ++mIter; - - return mTemplate->mCells[id]; -} - -TableArrayGroupsIter::TableArrayGroupsIter(TableTemplate& tmpl) - : mTemplate{ &tmpl } - , mIter{ tmpl.mName2ArrayGroups.begin() } -{ -} - -bool TableArrayGroupsIter::HasNext() const -{ - return mIter != mTemplate->mName2ArrayGroups.end(); -} - -TableArrayGroup& TableArrayGroupsIter::Peek() const -{ - int id = mIter.value(); - return mTemplate->mArrayGroups[id]; -} - -std::string_view TableArrayGroupsIter::PeekName() const -{ - return mIter.key_sv(); -} - -const char* TableArrayGroupsIter::PeekNameCStr() const -{ - return mIter.key(); -} - -void TableArrayGroupsIter::Next() -{ - ++mIter; -} diff --git a/core/src/Model/Template/TableTemplateIterator.hpp b/core/src/Model/Template/TableTemplateIterator.hpp deleted file mode 100644 index bf7f517..0000000 --- a/core/src/Model/Template/TableTemplateIterator.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "Model/Template/TableTemplate.hpp" -#include "Model/Template/Template.hpp" - -#include <string_view> - -class TableSingleParamsIter -{ -private: - TableTemplate* mTemplate; - tsl::array_map<char, int>::iterator mIter; - -public: - TableSingleParamsIter(TableTemplate& tmpl); - - bool HasNext() const; - TableCell& Next(); -}; - -class TableArrayGroupsIter -{ -private: - TableTemplate* mTemplate; - tsl::array_map<char, int>::iterator mIter; - -public: - TableArrayGroupsIter(TableTemplate& tmpl); - - bool HasNext() const; - TableArrayGroup& Peek() const; - std::string_view PeekName() const; - const char* PeekNameCStr() const; - void Next(); -}; diff --git a/core/src/Model/Template/Template.hpp b/core/src/Model/Template/Template.hpp deleted file mode 100644 index 061cc07..0000000 --- a/core/src/Model/Template/Template.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "Model/Assets.hpp" -#include "cplt_fwd.hpp" - -#include <filesystem> -#include <iosfwd> -#include <memory> -#include <string> - -class Template : public Asset -{ -public: - enum Kind - { - KD_Table, - - InvalidKind, - KindCount = InvalidKind, - }; - - using CategoryType = TemplateAssetList; - -private: - Kind mKind; - -public: - static const char* FormatKind(Kind kind); - static std::unique_ptr<Template> CreateByKind(Kind kind); - - static bool IsInstance(const Template* tmpl); - - Template(Kind kind); - ~Template() override = default; - - Kind GetKind() const; - - virtual void ReadFromDataStream(InputDataStream& stream) = 0; - virtual void WriteToDataStream(OutputDataStream& stream) const = 0; -}; - -class TemplateAssetList final : public AssetListTyped<Template> -{ -private: - // AC = Asset Creator - std::string mACNewName; - NameSelectionError mACNewNameError = NameSelectionError::Empty; - Template::Kind mACNewKind = Template::InvalidKind; - -public: - // Inherit constructors - using AssetListTyped::AssetListTyped; - -protected: - void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const override; - - std::string RetrieveNameFromFile(const std::filesystem::path& file) const override; - uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const override; - std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const override; - - bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const override; - Template* LoadInstance(const SavedAsset& assetInfo) const override; - Template* CreateInstance(const SavedAsset& assetInfo) const override; - bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const override; - - void DisplayAssetCreator(ListState& state) override; - void DisplayDetailsTable(ListState& state) const override; -}; diff --git a/core/src/Model/Template/Template_Main.cpp b/core/src/Model/Template/Template_Main.cpp deleted file mode 100644 index 4d6b67c..0000000 --- a/core/src/Model/Template/Template_Main.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "Template.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/IO/Archive.hpp" -#include "Utils/UUID.hpp" - -#include <imgui.h> -#include <imgui_stdlib.h> -#include <algorithm> -#include <cstdint> -#include <fstream> - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; - -Template::Template(Kind kind) - : mKind{ kind } -{ -} - -Template::Kind Template::GetKind() const -{ - return mKind; -} - -void TemplateAssetList::DiscoverFiles(const std::function<void(SavedAsset)>& callback) const -{ - auto dir = GetConnectedProject().GetTemplatesDirectory(); - DiscoverFilesByExtension(callback, dir, ".cplt-template"sv); -} - -std::string TemplateAssetList::RetrieveNameFromFile(const fs::path& file) const -{ - auto res = DataArchive::LoadFile(file); - if (!res) return ""; - auto& stream = res.value(); - - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - return assetInfo.Name; -} - -uuids::uuid TemplateAssetList::RetrieveUuidFromFile(const fs::path& file) const -{ - return uuids::uuid::from_string(file.stem().string()); -} - -fs::path TemplateAssetList::RetrievePathFromAsset(const SavedAsset& asset) const -{ - auto fileName = uuids::to_string(asset.Uuid); - return GetConnectedProject().GetTemplatePath(fileName); -} - -bool TemplateAssetList::SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const -{ - auto path = RetrievePathFromAsset(assetInfo); - auto res = DataArchive::SaveFile(path); - if (!res) return false; - auto& stream = res.value(); - - stream.WriteObject(assetInfo); - // This cast is fine: calls to this class will always be wrapped in TypedAssetList<T>, which will ensure `asset` points to some Template - if (auto tmpl = static_cast<const Template*>(asset)) { // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) - stream.WriteObject(*tmpl); - } - - return true; -} - -static std::unique_ptr<Template> LoadTemplateFromFile(const fs::path& path) -{ - auto res = DataArchive::LoadFile(path); - if (!res) return nullptr; - auto& stream = res.value(); - - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - auto kind = static_cast<Template::Kind>(assetInfo.Payload); - auto tmpl = Template::CreateByKind(kind); - stream.ReadObject(*tmpl); - - return tmpl; -} - -Template* TemplateAssetList::LoadInstance(const SavedAsset& assetInfo) const -{ - return ::LoadTemplateFromFile(RetrievePathFromAsset(assetInfo)).release(); -} - -Template* TemplateAssetList::CreateInstance(const SavedAsset& assetInfo) const -{ - auto kind = static_cast<Template::Kind>(assetInfo.Payload); - return Template::CreateByKind(kind).release(); -} - -bool TemplateAssetList::RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const -{ - // Get asset path, which is only dependent on UUID - auto path = RetrievePathFromAsset(assetInfo); - - auto tmpl = ::LoadTemplateFromFile(path); - if (!tmpl) return false; - - // Rewrite the asset with the updated name (note the given assetInfo already has the update name) - SaveInstance(assetInfo, tmpl.get()); - - return true; -} - -void TemplateAssetList::DisplayAssetCreator(ListState& state) -{ - auto ValidateNewName = [&]() -> void { - if (mACNewName.empty()) { - mACNewNameError = NameSelectionError::Empty; - return; - } - - if (FindByName(mACNewName)) { - mACNewNameError = NameSelectionError::Duplicated; - return; - } - - mACNewNameError = NameSelectionError::None; - }; - - auto ShowNewNameErrors = [&]() -> void { - switch (mACNewNameError) { - case NameSelectionError::None: break; - case NameSelectionError::Duplicated: - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - break; - case NameSelectionError::Empty: - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - break; - } - }; - - auto ShowNewKindErrors = [&]() -> void { - if (mACNewKind == Template::InvalidKind) { - ImGui::ErrorMessage(I18N_TEXT("Invalid template type", L10N_TEMPLATE_INVALID_TYPE_ERROR)); - } - }; - - auto IsInputValid = [&]() -> bool { - return mACNewNameError == NameSelectionError::None && - mACNewKind != Template::InvalidKind; - }; - - auto ResetState = [&]() -> void { - mACNewName.clear(); - mACNewKind = Template::InvalidKind; - ValidateNewName(); - }; - - if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &mACNewName)) { - ValidateNewName(); - } - - if (ImGui::BeginCombo(I18N_TEXT("Type", L10N_TYPE), Template::FormatKind(mACNewKind))) { - for (int i = 0; i < Template::KindCount; ++i) { - auto kind = static_cast<Template::Kind>(i); - if (ImGui::Selectable(Template::FormatKind(kind), mACNewKind == kind)) { - mACNewKind = kind; - } - } - ImGui::EndCombo(); - } - - ShowNewNameErrors(); - ShowNewKindErrors(); - - if (ImGui::Button(I18N_TEXT("OK", L10N_CONFIRM), !IsInputValid())) { - ImGui::CloseCurrentPopup(); - - Create(SavedAsset{ - .Name = mACNewName, - .Payload = static_cast<uint64_t>(mACNewKind), - }); - ResetState(); - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } -} - -void TemplateAssetList::DisplayDetailsTable(ListState& state) const -{ - ImGui::BeginTable("AssetDetailsTable", 2, ImGuiTableFlags_Borders); - - ImGui::TableSetupColumn(I18N_TEXT("Name", L10N_NAME)); - ImGui::TableSetupColumn(I18N_TEXT("Type", L10N_TYPE)); - ImGui::TableHeadersRow(); - - for (auto& asset : this->GetAssets()) { - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups)) { - state.SelectedAsset = &asset; - } - - ImGui::TableNextColumn(); - auto kind = static_cast<Template::Kind>(asset.Payload); - ImGui::TextUnformatted(Template::FormatKind(kind)); - } - - ImGui::EndTable(); -} diff --git a/core/src/Model/Template/Template_RTTI.cpp b/core/src/Model/Template/Template_RTTI.cpp deleted file mode 100644 index d1affe7..0000000 --- a/core/src/Model/Template/Template_RTTI.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "Template.hpp" - -#include "Model/Template/TableTemplate.hpp" -#include "Utils/I18n.hpp" - -const char* Template::FormatKind(Kind kind) -{ - switch (kind) { - case KD_Table: return I18N_TEXT("Table template", L10N_TEMPLATE_TABLE); - - case InvalidKind: break; - } - return ""; -} - -std::unique_ptr<Template> Template::CreateByKind(Kind kind) -{ - switch (kind) { - case KD_Table: return std::make_unique<TableTemplate>(); - - case InvalidKind: break; - } - return nullptr; -} - -bool Template::IsInstance(const Template* tmpl) -{ - return true; -} diff --git a/core/src/Model/Template/fwd.hpp b/core/src/Model/Template/fwd.hpp deleted file mode 100644 index 8378871..0000000 --- a/core/src/Model/Template/fwd.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// TableTemplate.hpp -class TableCell; -class TableArrayGroup; -class TableInstantiationParameters; -class TableTemplate; - -// Template.hpp -class Template; -class TemplateAssetList; diff --git a/core/src/Model/Workflow/Evaluation.cpp b/core/src/Model/Workflow/Evaluation.cpp deleted file mode 100644 index 7035bf9..0000000 --- a/core/src/Model/Workflow/Evaluation.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "Evaluation.hpp" - -#include <queue> - -const char* WorkflowEvaluationError::FormatMessageType(enum MessageType messageType) -{ - switch (messageType) { - case Error: return "Error"; - case Warning: return "Warning"; - } -} - -const char* WorkflowEvaluationError::FormatPinType(enum PinType pinType) -{ - switch (pinType) { - case NoPin: return nullptr; - case InputPin: return "Input pin"; - case OutputPin: return "Output pin"; - } -} - -std::string WorkflowEvaluationError::Format() const -{ - // TODO convert to std::format - - std::string result; - result += FormatMessageType(this->Type); - result += " at "; - result += NodeId; - if (auto pinText = FormatPinType(this->PinType)) { - result += "/"; - result += pinText; - result += " "; - result += PinId; - } - result += ": "; - result += this->Message; - - return result; -} - -struct WorkflowEvaluationContext::RuntimeNode -{ - enum EvaluationStatus - { - ST_Unevaluated, - ST_Success, - ST_Failed, - }; - - EvaluationStatus Status = ST_Unevaluated; -}; - -struct WorkflowEvaluationContext::RuntimeConnection -{ - std::unique_ptr<BaseValue> Value; - - bool IsAvailableValue() const - { - return Value != nullptr; - } -}; - -WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow) - : mWorkflow{ &workflow } -{ - mRuntimeNodes.resize(workflow.mNodes.size()); - mRuntimeConnections.resize(workflow.mConnections.size()); -} - -BaseValue* WorkflowEvaluationContext::GetConnectionValue(size_t id, bool constant) -{ - if (constant) { - return mWorkflow->GetConstantById(id); - } else { - return mRuntimeConnections[id].Value.get(); - } -} - -BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::InputPin& inputPin) -{ - if (inputPin.IsConnected()) { - return GetConnectionValue(inputPin.Connection, inputPin.IsConstantConnection()); - } else { - return nullptr; - } -} - -void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) -{ - mRuntimeConnections[id].Value = std::move(value); -} - -void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) -{ - if (outputPin.IsConnected()) { - SetConnectionValue(outputPin.Connection, std::move(value)); - } -} - -void WorkflowEvaluationContext::Run() -{ - int evaluatedCount = 0; - int erroredCount = 0; - - for (auto& depthGroup : mWorkflow->GetDepthGroups()) { - for (size_t idx : depthGroup) { - auto& rn = mRuntimeNodes[idx]; - auto& n = *mWorkflow->mNodes[idx]; - - // TODO - - int preEvalErrors = mErrors.size(); - n.Evaluate(*this); - if (preEvalErrors != mErrors.size()) { - erroredCount++; - } else { - evaluatedCount++; - } - } - } - - for (size_t i = 0; i < mRuntimeNodes.size(); ++i) { - auto& rn = mRuntimeNodes[i]; - auto& n = *mWorkflow->mNodes[i]; - if (n.GetType() == WorkflowNode::OutputType) { - // TODO record outputs - } - } -} - -void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = pinId, - .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin, - .Type = WorkflowEvaluationError::Error, - }); -} - -void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = -1, - .PinType = WorkflowEvaluationError::NoPin, - .Type = WorkflowEvaluationError::Error, - }); -} - -void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = pinId, - .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin, - .Type = WorkflowEvaluationError::Warning, - }); -} - -void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = -1, - .PinType = WorkflowEvaluationError::NoPin, - .Type = WorkflowEvaluationError::Warning, - }); -} diff --git a/core/src/Model/Workflow/Evaluation.hpp b/core/src/Model/Workflow/Evaluation.hpp deleted file mode 100644 index 4d78872..0000000 --- a/core/src/Model/Workflow/Evaluation.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -#include <cstddef> -#include <cstdint> -#include <string> -#include <vector> - -class WorkflowEvaluationError -{ -public: - enum MessageType : int16_t - { - Error, - Warning, - }; - - enum PinType : int16_t - { - NoPin, - InputPin, - OutputPin, - }; - -public: - std::string Message; - size_t NodeId; - int PinId; - PinType PinType; - MessageType Type; - -public: - static const char* FormatMessageType(enum MessageType messageType); - static const char* FormatPinType(enum PinType pinType); - - std::string Format() const; -}; - -class WorkflowEvaluationContext -{ -private: - struct RuntimeNode; - struct RuntimeConnection; - - Workflow* mWorkflow; - std::vector<RuntimeNode> mRuntimeNodes; - std::vector<RuntimeConnection> mRuntimeConnections; - std::vector<WorkflowEvaluationError> mErrors; - std::vector<WorkflowEvaluationError> mWarnings; - -public: - WorkflowEvaluationContext(Workflow& workflow); - - BaseValue* GetConnectionValue(size_t id, bool constant); - BaseValue* GetConnectionValue(const WorkflowNode::InputPin& inputPin); - void SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value); - void SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value); - - void ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin); - void ReportError(std::string message, const WorkflowNode& node); - void ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin); - void ReportWarning(std::string message, const WorkflowNode& node); - - /// Run until all possible paths have been evaluated. - void Run(); -}; diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp b/core/src/Model/Workflow/Nodes/DocumentNodes.cpp deleted file mode 100644 index 6729c63..0000000 --- a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "DocumentNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" - -bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_DocumentTemplateExpansion; -} - -DocumentTemplateExpansionNode::DocumentTemplateExpansionNode() - : WorkflowNode(KD_DocumentTemplateExpansion, false) -{ -} - -void DocumentTemplateExpansionNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.hpp b/core/src/Model/Workflow/Nodes/DocumentNodes.hpp deleted file mode 100644 index 85bba9e..0000000 --- a/core/src/Model/Workflow/Nodes/DocumentNodes.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -class DocumentTemplateExpansionNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - DocumentTemplateExpansionNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.cpp b/core/src/Model/Workflow/Nodes/NumericNodes.cpp deleted file mode 100644 index 3a81979..0000000 --- a/core/src/Model/Workflow/Nodes/NumericNodes.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "NumericNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" -#include "Utils/I18n.hpp" -#include "Utils/Macros.hpp" -#include "Utils/RTTI.hpp" - -#include <cassert> -#include <utility> - -WorkflowNode::Kind NumericOperationNode::OperationTypeToNodeKind(OperationType type) -{ - switch (type) { - case Addition: return KD_NumericAddition; - case Subtraction: return KD_NumericSubtraction; - case Multiplication: return KD_NumericMultiplication; - case Division: return KD_NumericDivision; - default: return InvalidKind; - } -} - -NumericOperationNode::OperationType NumericOperationNode::NodeKindToOperationType(Kind kind) -{ - switch (kind) { - case KD_NumericAddition: return Addition; - case KD_NumericSubtraction: return Subtraction; - case KD_NumericMultiplication: return Multiplication; - case KD_NumericDivision: return Division; - default: return InvalidType; - } -} - -bool NumericOperationNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() >= KD_NumericAddition && node->GetKind() <= KD_NumericDivision; -} - -NumericOperationNode::NumericOperationNode(OperationType type) - : WorkflowNode(OperationTypeToNodeKind(type), false) - , mType{ type } -{ - mInputs.resize(2); - mInputs[0].MatchingType = BaseValue::KD_Numeric; - mInputs[1].MatchingType = BaseValue::KD_Numeric; - - mOutputs.resize(1); - mOutputs[0].MatchingType = BaseValue::KD_Numeric; -} - -void NumericOperationNode::Evaluate(WorkflowEvaluationContext& ctx) -{ - auto lhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[0])); - if (!lhsVal) return; - double lhs = lhsVal->GetValue(); - - auto rhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[1])); - if (!rhsVal) return; - double rhs = rhsVal->GetValue(); - - double res; - switch (mType) { - case Addition: res = lhs + rhs; break; - case Subtraction: res = lhs - rhs; break; - case Multiplication: res = lhs * rhs; break; - case Division: { - if (rhs == 0.0) { - ctx.ReportError(I18N_TEXT("Error: division by 0", L10N_WORKFLOW_RTERROR_DIV_BY_0), *this); - return; - } - res = lhs / rhs; - } break; - - default: return; - } - - auto value = std::make_unique<NumericValue>(); - value->SetValue(res); - ctx.SetConnectionValue(mOutputs[0], std::move(value)); -} - -bool NumericExpressionNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_NumericExpression; -} - -NumericExpressionNode::NumericExpressionNode() - : WorkflowNode(KD_NumericExpression, false) -{ -} - -void NumericExpressionNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.hpp b/core/src/Model/Workflow/Nodes/NumericNodes.hpp deleted file mode 100644 index 56c0313..0000000 --- a/core/src/Model/Workflow/Nodes/NumericNodes.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -#include <cstddef> -#include <memory> -#include <variant> -#include <vector> - -class NumericOperationNode : public WorkflowNode -{ -public: - enum OperationType - { - Addition, - Subtraction, - Multiplication, - Division, - - InvalidType, - TypeCount = InvalidType, - }; - -private: - OperationType mType; - -public: - static Kind OperationTypeToNodeKind(OperationType type); - static OperationType NodeKindToOperationType(Kind kind); - static bool IsInstance(const WorkflowNode* node); - NumericOperationNode(OperationType type); - - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; - -class NumericExpressionNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - NumericExpressionNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -};
\ No newline at end of file diff --git a/core/src/Model/Workflow/Nodes/TextNodes.cpp b/core/src/Model/Workflow/Nodes/TextNodes.cpp deleted file mode 100644 index 4628dd3..0000000 --- a/core/src/Model/Workflow/Nodes/TextNodes.cpp +++ /dev/null @@ -1,231 +0,0 @@ -#include "TextNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" -#include "Utils/Macros.hpp" -#include "Utils/RTTI.hpp" -#include "Utils/Variant.hpp" - -#include <cassert> -#include <utility> -#include <variant> -#include <vector> - -class TextFormatterNode::Impl -{ -public: - template <class TFunction> - static void ForArguments(std::vector<Element>::iterator begin, std::vector<Element>::iterator end, const TFunction& func) - { - for (auto it = begin; it != end; ++it) { - auto& elm = *it; - if (auto arg = std::get_if<Argument>(&elm)) { - func(*arg); - } - } - } - - /// Find the pin index that the \c elmIdx -th element should have, based on the elements coming before it. - static int FindPinForElement(const std::vector<Element>& vec, int elmIdx) - { - for (int i = elmIdx; i >= 0; --i) { - auto& elm = vec[i]; - if (auto arg = std::get_if<Argument>(&elm)) { - return arg->PinIdx + 1; - } - } - return 0; - } -}; - -BaseValue::Kind TextFormatterNode::ArgumentTypeToValueKind(TextFormatterNode::ArgumentType arg) -{ - switch (arg) { - case NumericArgument: return BaseValue::KD_Numeric; - case TextArgument: return BaseValue::KD_Text; - case DateTimeArgument: return BaseValue::KD_DateTime; - } -} - -bool TextFormatterNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_TextFormatting; -} - -TextFormatterNode::TextFormatterNode() - : WorkflowNode(KD_TextFormatting, false) -{ -} - -int TextFormatterNode::GetElementCount() const -{ - return mElements.size(); -} - -const TextFormatterNode::Element& TextFormatterNode::GetElement(int idx) const -{ - return mElements[idx]; -} - -void TextFormatterNode::SetElement(int idx, std::string text) -{ - assert(idx >= 0 && idx < mElements.size()); - - std::visit( - Overloaded{ - [&](const std::string& original) { mMinOutputChars -= original.size(); }, - [&](const Argument& original) { PreRemoveElement(idx); }, - }, - mElements[idx]); - - mMinOutputChars += text.size(); - mElements[idx] = std::move(text); -} - -void TextFormatterNode::SetElement(int idx, ArgumentType argument) -{ - assert(idx >= 0 && idx < mElements.size()); - - std::visit( - Overloaded{ - [&](const std::string& original) { - mMinOutputChars -= original.size(); - - mElements[idx] = Argument{ - .Type = argument, - .PinIdx = Impl::FindPinForElement(mElements, idx), - }; - /* `original` is invalid from this point */ - }, - [&](const Argument& original) { - int pinIdx = original.PinIdx; - - // Create pin - auto& pin = mInputs[pinIdx]; - pin.MatchingType = ArgumentTypeToValueKind(argument); - - // Create element - mElements[idx] = Argument{ - .Type = argument, - .PinIdx = pinIdx, - }; - /* `original` is invalid from this point */ - }, - }, - mElements[idx]); -} - -void TextFormatterNode::InsertElement(int idx, std::string text) -{ - assert(idx >= 0); - if (idx >= mElements.size()) AppendElement(std::move(text)); - - mMinOutputChars += text.size(); - mElements.insert(mElements.begin() + idx, std::move(text)); -} - -void TextFormatterNode::InsertElement(int idx, ArgumentType argument) -{ - assert(idx >= 0); - if (idx >= mElements.size()) AppendElement(argument); - - int pinIdx = Impl::FindPinForElement(mElements, idx); - - // Create pin - auto& pin = InsertInputPin(pinIdx); - pin.MatchingType = ArgumentTypeToValueKind(argument); - - // Create element - mElements.insert( - mElements.begin() + idx, - Argument{ - .Type = argument, - .PinIdx = pinIdx, - }); -} - -void TextFormatterNode::AppendElement(std::string text) -{ - mMinOutputChars += text.size(); - mElements.push_back(std::move(text)); -} - -void TextFormatterNode::AppendElement(ArgumentType argument) -{ - int pinIdx = mInputs.size(); - // Create pin - mInputs.push_back(InputPin{}); - mInputs.back().MatchingType = ArgumentTypeToValueKind(argument); - // Creat eelement - mElements.push_back(Argument{ - .Type = argument, - .PinIdx = pinIdx, - }); -} - -void TextFormatterNode::RemoveElement(int idx) -{ - assert(idx >= 0 && idx < mElements.size()); - - PreRemoveElement(idx); - if (auto arg = std::get_if<Argument>(&mElements[idx])) { - RemoveInputPin(arg->PinIdx); - } - mElements.erase(mElements.begin() + idx); -} - -void TextFormatterNode::Evaluate(WorkflowEvaluationContext& ctx) -{ - std::string result; - result.reserve((size_t)(mMinOutputChars * 1.5f)); - - auto HandleText = [&](const std::string& str) { - result += str; - }; - auto HandleArgument = [&](const Argument& arg) { - switch (arg.Type) { - case NumericArgument: { - if (auto val = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { - result += val->GetString(); - } else { - // TODO localize - ctx.ReportError("Non-numeric value connected to a numeric text format parameter.", *this); - } - } break; - case TextArgument: { - if (auto val = dyn_cast<TextValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { - result += val->GetValue(); - } else { - // TODO localize - ctx.ReportError("Non-text value connected to a textual text format parameter.", *this); - } - } break; - case DateTimeArgument: { - if (auto val = dyn_cast<DateTimeValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { - result += val->GetString(); - } else { - // TODO localize - ctx.ReportError("Non-date/time value connected to a date/time text format parameter.", *this); - } - } break; - } - }; - - for (auto& elm : mElements) { - std::visit(Overloaded{ HandleText, HandleArgument }, elm); - } -} - -void TextFormatterNode::PreRemoveElement(int idx) -{ - auto& elm = mElements[idx]; - if (auto arg = std::get_if<Argument>(&elm)) { - RemoveInputPin(arg->PinIdx); - Impl::ForArguments( - mElements.begin() + idx + 1, - mElements.end(), - [&](Argument& arg) { - arg.PinIdx--; - }); - } -} diff --git a/core/src/Model/Workflow/Nodes/TextNodes.hpp b/core/src/Model/Workflow/Nodes/TextNodes.hpp deleted file mode 100644 index c33854c..0000000 --- a/core/src/Model/Workflow/Nodes/TextNodes.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -#include <cstddef> -#include <memory> -#include <variant> -#include <vector> - -class TextFormatterNode : public WorkflowNode -{ -public: - enum ArgumentType - { - NumericArgument, - TextArgument, - DateTimeArgument, - }; - -private: - class Impl; - - struct Argument - { - ArgumentType Type; - int PinIdx; - }; - using Element = std::variant<std::string, Argument>; - - std::vector<Element> mElements; - int mMinOutputChars; - -public: - static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg); - static bool IsInstance(const WorkflowNode* node); - TextFormatterNode(); - - int GetElementCount() const; - const Element& GetElement(int idx) const; - - void SetElement(int idx, std::string text); - void SetElement(int idx, ArgumentType argument); - void InsertElement(int idx, std::string text); - void InsertElement(int idx, ArgumentType argument); - void AppendElement(std::string text); - void AppendElement(ArgumentType argument); - void RemoveElement(int idx); - - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; - -private: - void PreRemoveElement(int idx); -}; diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp b/core/src/Model/Workflow/Nodes/UserInputNodes.cpp deleted file mode 100644 index 0b6d471..0000000 --- a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "UserInputNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" - -bool FormInputNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_FormInput; -} - -FormInputNode::FormInputNode() - : WorkflowNode(KD_FormInput, false) -{ -} - -void FormInputNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} - -bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_DatabaseRowsInput; -} - -DatabaseRowsInputNode::DatabaseRowsInputNode() - : WorkflowNode(KD_DatabaseRowsInput, false) -{ -} - -void DatabaseRowsInputNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.hpp b/core/src/Model/Workflow/Nodes/UserInputNodes.hpp deleted file mode 100644 index 10ea95d..0000000 --- a/core/src/Model/Workflow/Nodes/UserInputNodes.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -class FormInputNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - FormInputNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; - -class DatabaseRowsInputNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - DatabaseRowsInputNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; diff --git a/core/src/Model/Workflow/Nodes/fwd.hpp b/core/src/Model/Workflow/Nodes/fwd.hpp deleted file mode 100644 index 4153825..0000000 --- a/core/src/Model/Workflow/Nodes/fwd.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -// DocumentNodes.hpp -class DocumentTemplateExpansionNode; - -// InputNodes.hpp -class FormInputNode; -class DatabaseRowsInputNode; - -// NumericNodes.hpp -class NumericOperationNode; -class NumericExpressionNode; - -// TextNodes.hpp -class TextFormatterNode; diff --git a/core/src/Model/Workflow/Value.hpp b/core/src/Model/Workflow/Value.hpp deleted file mode 100644 index 2198674..0000000 --- a/core/src/Model/Workflow/Value.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "Utils/Color.hpp" -#include "cplt_fwd.hpp" - -#include <iosfwd> -#include <memory> -#include <string> -#include <vector> - -class BaseValue -{ -public: - enum Kind - { - KD_Numeric, - KD_Text, - KD_DateTime, - KD_DatabaseRowId, - KD_List, - KD_Dictionary, - - KD_BaseObject, - KD_SaleDatabaseRow, - KD_PurchaseDatabaseRow, - KD_BaseObjectLast = KD_PurchaseDatabaseRow, - - /// An unspecified type, otherwise known as "any" in some contexts. - InvalidKind, - KindCount = InvalidKind, - }; - - struct KindInfo - { - ImGui::IconType PinIcon; - RgbaColor PinColor; - }; - -private: - Kind mKind; - -public: - static const KindInfo& QueryInfo(Kind kind); - static const char* Format(Kind kind); - static std::unique_ptr<BaseValue> CreateByKind(Kind kind); - - static bool IsInstance(const BaseValue* value); - - BaseValue(Kind kind); - virtual ~BaseValue() = default; - - BaseValue(const BaseValue&) = delete; - BaseValue& operator=(const BaseValue&) = delete; - BaseValue(BaseValue&&) = default; - BaseValue& operator=(BaseValue&&) = default; - - Kind GetKind() const; - - // TODO get constant editor - - /// The functions \c ReadFrom, \c WriteTo will only be valid to call if this function returns true. - virtual bool SupportsConstant() const; - virtual void ReadFrom(std::istream& stream); - virtual void WriteTo(std::ostream& stream); -}; - -class BaseObjectDescription -{ -public: - struct Property - { - std::string Name; - BaseValue::Kind Kind; - bool Mutatable = true; - }; - -public: - std::vector<Property> Properties; -}; - -class BaseObjectValue : public BaseValue -{ -public: - /// \param kind A value kind enum, within the range of KD_BaseObject and KD_BaseObjectLast (both inclusive). - static const BaseObjectDescription& QueryObjectInfo(Kind kind); - - static bool IsInstance(const BaseValue* value); - BaseObjectValue(Kind kind); - - const BaseObjectDescription& GetObjectDescription() const; - - virtual const BaseValue* GetProperty(int idx) const = 0; - virtual bool SetProperty(int idx, std::unique_ptr<BaseValue> value) = 0; -}; diff --git a/core/src/Model/Workflow/ValueInternals.hpp b/core/src/Model/Workflow/ValueInternals.hpp deleted file mode 100644 index 49981f0..0000000 --- a/core/src/Model/Workflow/ValueInternals.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// This file contains utility classes and macros for implementing values -// As consumers, you should not include this header as it contains unnecessary symbols and can pollute your files -// for this reason, classes here aren't forward-declared in fwd.hpp either. - -#pragma once - -#include "Utils/RTTI.hpp" - -#include <utility> - -#define CHECK_VALUE_TYPE(Type, value) \ - if (!is_a<Type>(value)) { \ - return false; \ - } - -#define CHECK_VALUE_TYPE_AND_MOVE(Type, dest, value) \ - if (auto ptr = dyn_cast<Type>(value)) { \ - dest = std::move(*ptr); \ - } else { \ - return false; \ - } diff --git a/core/src/Model/Workflow/Value_Main.cpp b/core/src/Model/Workflow/Value_Main.cpp deleted file mode 100644 index ca972c4..0000000 --- a/core/src/Model/Workflow/Value_Main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "Value.hpp" - -BaseValue::BaseValue(Kind kind) - : mKind{ kind } -{ -} - -BaseValue::Kind BaseValue::GetKind() const -{ - return mKind; -} - -bool BaseValue::SupportsConstant() const -{ - return false; -} - -void BaseValue::ReadFrom(std::istream& stream) -{ -} - -void BaseValue::WriteTo(std::ostream& stream) -{ -} - -BaseObjectValue::BaseObjectValue(Kind kind) - : BaseValue(kind) -{ - assert(kind >= KD_BaseObject && kind <= KD_BaseObjectLast); -} - -const BaseObjectDescription& BaseObjectValue::GetObjectDescription() const -{ - return QueryObjectInfo(this->GetKind()); -} diff --git a/core/src/Model/Workflow/Value_RTTI.cpp b/core/src/Model/Workflow/Value_RTTI.cpp deleted file mode 100644 index 0561239..0000000 --- a/core/src/Model/Workflow/Value_RTTI.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "Value.hpp" - -#include "Model/Workflow/Values/Basic.hpp" -#include "Model/Workflow/Values/Database.hpp" -#include "Model/Workflow/Values/Dictionary.hpp" -#include "Model/Workflow/Values/List.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" - -constexpr BaseValue::KindInfo kEmptyInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(), -}; - -constexpr BaseValue::KindInfo kNumericInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(147, 226, 74), -}; - -constexpr BaseValue::KindInfo kTextInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(124, 21, 153), -}; - -constexpr BaseValue::KindInfo kDateTimeInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(147, 226, 74), -}; - -constexpr BaseValue::KindInfo kDatabaseRowIdInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(216, 42, 221), -}; - -constexpr BaseValue::KindInfo kListInfo{ - .PinIcon = ImGui::IconType::Diamond, - .PinColor = RgbaColor(58, 154, 214), -}; - -constexpr BaseValue::KindInfo kDictionaryInfo{ - .PinIcon = ImGui::IconType::Diamond, - .PinColor = RgbaColor(240, 240, 34), -}; - -constexpr BaseValue::KindInfo kDatabaseRowInfo{ - .PinIcon = ImGui::IconType::Square, - .PinColor = RgbaColor(15, 124, 196), -}; - -constexpr BaseValue::KindInfo kObjectInfo{ - .PinIcon = ImGui::IconType::Square, - .PinColor = RgbaColor(161, 161, 161), -}; - -const BaseValue::KindInfo& BaseValue::QueryInfo(BaseValue::Kind kind) -{ - switch (kind) { - case KD_Numeric: return kNumericInfo; - case KD_Text: return kTextInfo; - case KD_DateTime: return kDateTimeInfo; - case KD_DatabaseRowId: return kDatabaseRowIdInfo; - case KD_List: return kListInfo; - case KD_Dictionary: return kDictionaryInfo; - - case KD_BaseObject: return kObjectInfo; - case KD_SaleDatabaseRow: - case KD_PurchaseDatabaseRow: - return kDatabaseRowInfo; - - case InvalidKind: break; - } - return kEmptyInfo; -} - -const char* BaseValue::Format(Kind kind) -{ - switch (kind) { - case KD_Numeric: return I18N_TEXT("Numeric", L10N_VALUE_NUMERIC); - case KD_Text: return I18N_TEXT("Text", L10N_VALUE_TEXT); - case KD_DateTime: return I18N_TEXT("Date/time", L10N_VALUE_DATE_TIME); - case KD_DatabaseRowId: return I18N_TEXT("Row id", L10N_VALUE_ROW_ID); - case KD_List: return I18N_TEXT("List", L10N_VALUE_LIST); - case KD_Dictionary: return I18N_TEXT("Dictionary", L10N_VALUE_DICT); - - case KD_BaseObject: return I18N_TEXT("Object", L10N_VALUE_OBJECT); - case KD_SaleDatabaseRow: return I18N_TEXT("Sale record", L10N_VALUE_SALE_RECORD); - case KD_PurchaseDatabaseRow: return I18N_TEXT("Purchase record", L10N_VALUE_PURCHASE_RECORD); - - case InvalidKind: break; - } - return ""; -} - -std::unique_ptr<BaseValue> BaseValue::CreateByKind(BaseValue::Kind kind) -{ - switch (kind) { - case KD_Numeric: return std::make_unique<NumericValue>(); - case KD_Text: return std::make_unique<TextValue>(); - case KD_DateTime: return std::make_unique<DateTimeValue>(); - case KD_DatabaseRowId: return std::make_unique<DatabaseRowIdValue>(); - case KD_List: return std::make_unique<ListValue>(); - case KD_Dictionary: return std::make_unique<DictionaryValue>(); - - case KD_BaseObject: return nullptr; - case KD_SaleDatabaseRow: return std::make_unique<SaleDatabaseRowValue>(); - case KD_PurchaseDatabaseRow: return std::make_unique<PurchaseDatabaseRowValue>(); - - case InvalidKind: break; - } - return nullptr; -} - -bool BaseValue::IsInstance(const BaseValue* value) -{ - return true; -} - -const BaseObjectDescription kEmptyObjectInfo{ - .Properties = {}, -}; - -const BaseObjectDescription kSaleDbRowObject{ - .Properties = { - { - .Name = I18N_TEXT("Customer", L10N_VALUE_PROPERTY_CUSTOMER), - .Kind = BaseValue::KD_Text, - .Mutatable = false, - }, - { - .Name = I18N_TEXT("Deadline", L10N_VALUE_PROPERTY_DEADLINE), - .Kind = BaseValue::KD_DateTime, - }, - { - .Name = I18N_TEXT("Delivery time", L10N_VALUE_PROPERTY_DELIVERY_TIME), - .Kind = BaseValue::KD_DateTime, - }, - }, -}; - -const BaseObjectDescription kPurchaseDbRowObject{ - .Properties = { - { - .Name = I18N_TEXT("Factory", L10N_VALUE_PROPERTY_FACTORY), - .Kind = BaseValue::KD_Text, - .Mutatable = false, - }, - { - .Name = I18N_TEXT("Order time", L10N_VALUE_PROPERTY_ORDER_TIME), - .Kind = BaseValue::KD_DateTime, - }, - { - .Name = I18N_TEXT("Delivery time", L10N_VALUE_PROPERTY_DELIVERY_TIME), - .Kind = BaseValue::KD_DateTime, - }, - }, -}; - -const BaseObjectDescription& BaseObjectValue::QueryObjectInfo(Kind kind) -{ - switch (kind) { - case KD_BaseObject: return kEmptyObjectInfo; - case KD_SaleDatabaseRow: return kSaleDbRowObject; - case KD_PurchaseDatabaseRow: return kPurchaseDbRowObject; - - default: break; - } - return kEmptyObjectInfo; -} - -bool BaseObjectValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() >= KD_BaseObject && - value->GetKind() <= KD_BaseObjectLast; -} diff --git a/core/src/Model/Workflow/Values/Basic.cpp b/core/src/Model/Workflow/Values/Basic.cpp deleted file mode 100644 index 198387c..0000000 --- a/core/src/Model/Workflow/Values/Basic.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "Basic.hpp" - -#include <charconv> -#include <cmath> -#include <limits> - -bool NumericValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_Numeric; -} - -NumericValue::NumericValue() - : BaseValue(BaseValue::KD_Numeric) -{ -} - -template <class T, int kMaxSize> -static std::string NumberToString(T value) -{ - char buf[kMaxSize]; - auto res = std::to_chars(buf, buf + kMaxSize, value); - if (res.ec == std::errc()) { - return std::string(buf, res.ptr); - } else { - return "<err>"; - } -} - -std::string NumericValue::GetTruncatedString() const -{ - constexpr auto kMaxSize = std::numeric_limits<int64_t>::digits10; - return ::NumberToString<int64_t, kMaxSize>((int64_t)mValue); -} - -std::string NumericValue::GetRoundedString() const -{ - constexpr auto kMaxSize = std::numeric_limits<int64_t>::digits10; - return ::NumberToString<int64_t, kMaxSize>((int64_t)std::round(mValue)); -} - -std::string NumericValue::GetString() const -{ - constexpr auto kMaxSize = std::numeric_limits<double>::max_digits10; - return ::NumberToString<double, kMaxSize>(mValue); -} - -int64_t NumericValue::GetInt() const -{ - return static_cast<int64_t>(mValue); -} - -double NumericValue::GetValue() const -{ - return mValue; -} - -void NumericValue::SetValue(double value) -{ - mValue = value; -} - -bool TextValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_Text; -} - -TextValue::TextValue() - : BaseValue(BaseValue::KD_Text) -{ -} - -const std::string& TextValue::GetValue() const -{ - return mValue; -} - -void TextValue::SetValue(const std::string& value) -{ - mValue = value; -} - -bool DateTimeValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_DateTime; -} - -DateTimeValue::DateTimeValue() - : BaseValue(BaseValue::KD_DateTime) -{ -} - -std::string DateTimeValue::GetString() const -{ - namespace chrono = std::chrono; - auto t = chrono::system_clock::to_time_t(mValue); - - char data[32]; - std::strftime(data, sizeof(data), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); - - return std::string(data); -} - -const std::chrono::time_point<std::chrono::system_clock>& DateTimeValue::GetValue() const -{ - return mValue; -} - -void DateTimeValue::SetValue(const std::chrono::time_point<std::chrono::system_clock>& value) -{ - mValue = value; -} diff --git a/core/src/Model/Workflow/Values/Basic.hpp b/core/src/Model/Workflow/Values/Basic.hpp deleted file mode 100644 index 38e0531..0000000 --- a/core/src/Model/Workflow/Values/Basic.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" - -#include <chrono> -#include <cstdint> -#include <string> - -class NumericValue : public BaseValue -{ -private: - double mValue; - -public: - static bool IsInstance(const BaseValue* value); - NumericValue(); - - NumericValue(const NumericValue&) = delete; - NumericValue& operator=(const NumericValue&) = delete; - NumericValue(NumericValue&&) = default; - NumericValue& operator=(NumericValue&&) = default; - - std::string GetTruncatedString() const; - std::string GetRoundedString() const; - std::string GetString() const; - - int64_t GetInt() const; - double GetValue() const; - void SetValue(double value); -}; - -class TextValue : public BaseValue -{ -private: - std::string mValue; - -public: - static bool IsInstance(const BaseValue* value); - TextValue(); - - TextValue(const TextValue&) = delete; - TextValue& operator=(const TextValue&) = delete; - TextValue(TextValue&&) = default; - TextValue& operator=(TextValue&&) = default; - - const std::string& GetValue() const; - void SetValue(const std::string& value); -}; - -class DateTimeValue : public BaseValue -{ -private: - std::chrono::time_point<std::chrono::system_clock> mValue; - -public: - static bool IsInstance(const BaseValue* value); - DateTimeValue(); - - DateTimeValue(const DateTimeValue&) = delete; - DateTimeValue& operator=(const DateTimeValue&) = delete; - DateTimeValue(DateTimeValue&&) = default; - DateTimeValue& operator=(DateTimeValue&&) = default; - - std::string GetString() const; - const std::chrono::time_point<std::chrono::system_clock>& GetValue() const; - void SetValue(const std::chrono::time_point<std::chrono::system_clock>& value); -}; diff --git a/core/src/Model/Workflow/Values/Database.cpp b/core/src/Model/Workflow/Values/Database.cpp deleted file mode 100644 index cdc2b4f..0000000 --- a/core/src/Model/Workflow/Values/Database.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Database.hpp" - -#include "Model/Database.hpp" -#include "Model/Workflow/ValueInternals.hpp" - -#include <limits> - -TableKind DatabaseRowIdValue::GetTable() const -{ - return mTable; -} - -int64_t DatabaseRowIdValue::GetRowId() const -{ - return mRowId; -} - -bool DatabaseRowIdValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_DatabaseRowId; -} - -DatabaseRowIdValue::DatabaseRowIdValue() - : BaseValue(KD_DatabaseRowId) - , mTable{ TableKind::Sales } - , mRowId{ std::numeric_limits<int64_t>::max() } -{ -} - -bool SaleDatabaseRowValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_SaleDatabaseRow; -} - -SaleDatabaseRowValue::SaleDatabaseRowValue() - : BaseObjectValue(KD_SaleDatabaseRow) -{ -} - -const BaseValue* SaleDatabaseRowValue::GetProperty(int idx) const -{ - switch (idx) { - case 0: return &mCustomerName; - case 1: return &mDeadline; - case 2: return &mDeliveryTime; - default: return nullptr; - } -} - -bool SaleDatabaseRowValue::SetProperty(int idx, std::unique_ptr<BaseValue> value) -{ - switch (idx) { - case 0: return false; - case 1: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mDeadline, value.get()); break; - case 2: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mDeliveryTime, value.get()); break; - } - return true; -} - -bool PurchaseDatabaseRowValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_PurchaseDatabaseRow; -} - -PurchaseDatabaseRowValue::PurchaseDatabaseRowValue() - : BaseObjectValue(KD_PurchaseDatabaseRow) -{ -} - -const BaseValue* PurchaseDatabaseRowValue::GetProperty(int idx) const -{ - switch (idx) { - case 0: return &mFactoryName; - case 1: return &mOrderTime; - case 2: return &mDeliveryTime; - default: return nullptr; - } -} - -bool PurchaseDatabaseRowValue::SetProperty(int idx, std::unique_ptr<BaseValue> value) -{ - switch (idx) { - case 0: return false; - case 1: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mOrderTime, value.get()); break; - case 2: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mDeliveryTime, value.get()); break; - } - return true; -} diff --git a/core/src/Model/Workflow/Values/Database.hpp b/core/src/Model/Workflow/Values/Database.hpp deleted file mode 100644 index e8a4f83..0000000 --- a/core/src/Model/Workflow/Values/Database.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" -#include "Model/Workflow/Values/Basic.hpp" -#include "cplt_fwd.hpp" - -class DatabaseRowIdValue : public BaseValue -{ -private: - TableKind mTable; - int64_t mRowId; - -public: - static bool IsInstance(const BaseValue* value); - DatabaseRowIdValue(); - - TableKind GetTable() const; - int64_t GetRowId() const; -}; - -class SaleDatabaseRowValue : public BaseObjectValue -{ -private: - int mCustomerId; - TextValue mCustomerName; - DateTimeValue mDeadline; - DateTimeValue mDeliveryTime; - -public: - static bool IsInstance(const BaseValue* value); - SaleDatabaseRowValue(); - - virtual const BaseValue* GetProperty(int idx) const; - virtual bool SetProperty(int idx, std::unique_ptr<BaseValue> value); -}; - -class PurchaseDatabaseRowValue : public BaseObjectValue -{ -private: - int mFactoryId; - TextValue mFactoryName; - DateTimeValue mOrderTime; - DateTimeValue mDeliveryTime; - -public: - static bool IsInstance(const BaseValue* value); - PurchaseDatabaseRowValue(); - - virtual const BaseValue* GetProperty(int idx) const; - virtual bool SetProperty(int idx, std::unique_ptr<BaseValue> value); -}; diff --git a/core/src/Model/Workflow/Values/Dictionary.cpp b/core/src/Model/Workflow/Values/Dictionary.cpp deleted file mode 100644 index 106e48d..0000000 --- a/core/src/Model/Workflow/Values/Dictionary.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "Dictionary.hpp" - -#include "Utils/Macros.hpp" - -bool DictionaryValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_Dictionary; -} - -DictionaryValue::DictionaryValue() - : BaseValue(KD_Dictionary) -{ -} - -int DictionaryValue::GetCount() const -{ - return mElements.size(); -} - -BaseValue* DictionaryValue::Find(std::string_view key) -{ - auto iter = mElements.find(key); - if (iter != mElements.end()) { - return iter.value().get(); - } else { - return nullptr; - } -} - -BaseValue* DictionaryValue::Insert(std::string_view key, std::unique_ptr<BaseValue>& value) -{ - auto [iter, success] = mElements.insert(key, std::move(value)); - if (success) { - return iter.value().get(); - } else { - return nullptr; - } -} - -BaseValue& DictionaryValue::InsertOrReplace(std::string_view key, std::unique_ptr<BaseValue> value) -{ - auto [iter, DISCARD] = mElements.emplace(key, std::move(value)); - return *iter.value(); -} - -void DictionaryValue::Remove(std::string_view key) -{ - mElements.erase(mElements.find(key)); -} diff --git a/core/src/Model/Workflow/Values/Dictionary.hpp b/core/src/Model/Workflow/Values/Dictionary.hpp deleted file mode 100644 index 65ea82f..0000000 --- a/core/src/Model/Workflow/Values/Dictionary.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" - -#include <tsl/array_map.h> -#include <memory> -#include <string> -#include <string_view> - -class DictionaryValue : public BaseValue -{ -private: - tsl::array_map<char, std::unique_ptr<BaseValue>> mElements; - -public: - static bool IsInstance(const BaseValue* value); - DictionaryValue(); - - int GetCount() const; - BaseValue* Find(std::string_view key); - - BaseValue* Insert(std::string_view key, std::unique_ptr<BaseValue>& value); - BaseValue& InsertOrReplace(std::string_view key, std::unique_ptr<BaseValue> value); - void Remove(std::string_view key); -}; diff --git a/core/src/Model/Workflow/Values/List.cpp b/core/src/Model/Workflow/Values/List.cpp deleted file mode 100644 index 9fd6bfd..0000000 --- a/core/src/Model/Workflow/Values/List.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "List.hpp" - -#include <utility> - -BaseValue* ListValue::Iterator::operator*() const -{ - return mIter->get(); -} - -BaseValue* ListValue::Iterator::operator->() const -{ - return mIter->get(); -} - -ListValue::Iterator& ListValue::Iterator::operator++() -{ - ++mIter; - return *this; -} - -ListValue::Iterator ListValue::Iterator::operator++(int) const -{ - return Iterator(mIter + 1); -} - -ListValue::Iterator& ListValue::Iterator::operator--() -{ - --mIter; - return *this; -} - -ListValue::Iterator ListValue::Iterator::operator--(int) const -{ - return Iterator(mIter - 1); -} - -bool operator==(const ListValue::Iterator& a, const ListValue::Iterator& b) -{ - return a.mIter == b.mIter; -} - -ListValue::Iterator::Iterator(decltype(mIter) iter) - : mIter{ iter } -{ -} - -bool ListValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_List; -} - -ListValue::ListValue() - : BaseValue(KD_List) -{ -} - -int ListValue::GetCount() const -{ - return mElements.size(); -} - -BaseValue* ListValue::GetElement(int i) const -{ - return mElements[i].get(); -} - -void ListValue::Append(std::unique_ptr<BaseValue> element) -{ - mElements.push_back(std::move(element)); -} - -void ListValue::Insert(int i, std::unique_ptr<BaseValue> element) -{ - mElements.insert(mElements.begin() + i, std::move(element)); -} - -void ListValue::Insert(Iterator iter, std::unique_ptr<BaseValue> element) -{ - mElements.insert(iter.mIter, std::move(element)); -} - -void ListValue::Remove(int i) -{ - mElements.erase(mElements.begin() + i); -} - -void ListValue::Remove(Iterator iter) -{ - mElements.erase(iter.mIter); -} - -ListValue::Iterator ListValue::begin() -{ - return Iterator(mElements.begin()); -} - -ListValue::Iterator ListValue::end() -{ - return Iterator(mElements.end()); -} diff --git a/core/src/Model/Workflow/Values/List.hpp b/core/src/Model/Workflow/Values/List.hpp deleted file mode 100644 index 706a95c..0000000 --- a/core/src/Model/Workflow/Values/List.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" - -#include <memory> -#include <vector> - -class ListValue : public BaseValue -{ -public: - class Iterator - { - private: - std::vector<std::unique_ptr<BaseValue>>::iterator mIter; - - public: - BaseValue* operator*() const; - BaseValue* operator->() const; - - Iterator& operator++(); - Iterator operator++(int) const; - Iterator& operator--(); - Iterator operator--(int) const; - - friend bool operator==(const Iterator& a, const Iterator& b); - - private: - friend class ListValue; - Iterator(decltype(mIter) iter); - }; - -private: - std::vector<std::unique_ptr<BaseValue>> mElements; - -public: - static bool IsInstance(const BaseValue* value); - ListValue(); - - int GetCount() const; - BaseValue* GetElement(int i) const; - - void Append(std::unique_ptr<BaseValue> element); - void Insert(int i, std::unique_ptr<BaseValue> element); - void Insert(Iterator iter, std::unique_ptr<BaseValue> element); - void Remove(int i); - void Remove(Iterator iter); - - Iterator begin(); - Iterator end(); -}; diff --git a/core/src/Model/Workflow/Values/fwd.hpp b/core/src/Model/Workflow/Values/fwd.hpp deleted file mode 100644 index 51a04e9..0000000 --- a/core/src/Model/Workflow/Values/fwd.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -// Basic.hpp -class NumericValue; -class TextValue; -class DateTimeValue; - -// Database.hpp -class DatabaseRowIdValue; -class SaleDatabaseRowValue; -class PurchaseDatabaseRowValue; - -// Dictionary.hpp -class DictionaryValue; - -// List.hpp -class ListValue; diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp deleted file mode 100644 index 3c4d320..0000000 --- a/core/src/Model/Workflow/Workflow.hpp +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once - -#include "Model/Assets.hpp" -#include "Model/Workflow/Value.hpp" -#include "Utils/Vector.hpp" -#include "cplt_fwd.hpp" - -#include <imgui_node_editor.h> -#include <cstddef> -#include <cstdint> -#include <filesystem> -#include <functional> -#include <iosfwd> -#include <limits> -#include <memory> -#include <span> -#include <string> -#include <variant> -#include <vector> - -namespace ImNodes = ax::NodeEditor; - -class WorkflowConnection -{ -public: - static constexpr auto kInvalidId = std::numeric_limits<uint32_t>::max(); - - uint32_t Id; - uint32_t SourceNode; - uint32_t SourcePin; - uint32_t DestinationNode; - uint32_t DestinationPin; - -public: - WorkflowConnection(); - - bool IsValid() const; - - /// Used for `LinkId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading). - ImNodes::LinkId GetLinkId() const; - - void DrawDebugInfo() const; - void ReadFrom(std::istream& stream); - void WriteTo(std::ostream& stream) const; -}; - -class WorkflowNode -{ -public: - static constexpr auto kInvalidId = std::numeric_limits<uint32_t>::max(); - static constexpr auto kInvalidPinId = std::numeric_limits<uint32_t>::max(); - - enum Type - { - InputType, - TransformType, - OutputType, - }; - - enum Kind - { - KD_NumericAddition, - KD_NumericSubtraction, - KD_NumericMultiplication, - KD_NumericDivision, - KD_NumericExpression, - KD_TextFormatting, - KD_DocumentTemplateExpansion, - KD_FormInput, - KD_DatabaseRowsInput, - - InvalidKind, - KindCount = InvalidKind, - }; - - enum Category - { - CG_Numeric, - CG_Text, - CG_Document, - CG_UserInput, - CG_SystemInput, - CG_Output, - - InvalidCategory, - CategoryCount = InvalidCategory, - }; - - struct InputPin - { - uint32_t Connection = WorkflowConnection::kInvalidId; - BaseValue::Kind MatchingType = BaseValue::InvalidKind; - bool ConnectionToConst = false; - - /// A constant connection connects from a user-specified constant value, feeding to a valid \c DestinationNode and \c DestinationPin (i.e. input pins). - bool IsConstantConnection() const; - bool IsConnected() const; - BaseValue::Kind GetMatchingType() const; - }; - - struct OutputPin - { - uint32_t Connection = WorkflowConnection::kInvalidId; - BaseValue::Kind MatchingType = BaseValue::InvalidKind; - - bool IsConnected() const; - BaseValue::Kind GetMatchingType() const; - }; - -protected: - friend class Workflow; - friend class WorkflowEvaluationContext; - - Workflow* mWorkflow; - std::vector<InputPin> mInputs; - std::vector<OutputPin> mOutputs; - Vec2i mPosition; - uint32_t mId; - Kind mKind; - int mDepth; - bool mLocked; - -public: - static const char* FormatKind(Kind kind); - static const char* FormatCategory(Category category); - static const char* FormatType(Type type); - static Category QueryCategory(Kind kind); - static std::span<const Kind> QueryCategoryMembers(Category category); - static std::unique_ptr<WorkflowNode> CreateByKind(Kind kind); - - static bool IsInstance(const WorkflowNode* node); - - WorkflowNode(Kind kind, bool locked); - virtual ~WorkflowNode() = default; - - WorkflowNode(const WorkflowNode&) = delete; - WorkflowNode& operator=(const WorkflowNode&) = delete; - WorkflowNode(WorkflowNode&&) = default; - WorkflowNode& operator=(WorkflowNode&&) = default; - - void SetPosition(const Vec2i& position); - Vec2i GetPosition() const; - - uint32_t GetId() const; - /// Used for `NodeId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading). - ImNodes::NodeId GetNodeId() const; - Kind GetKind() const; - int GetDepth() const; - bool IsLocked() const; - - Type GetType() const; - bool IsInputNode() const; - bool IsOutputNode() const; - - void ConnectInput(uint32_t pinId, WorkflowNode& srcNode, uint32_t srcPinId); - void DisconnectInput(uint32_t pinId); - - void DrawInputPinDebugInfo(uint32_t pinId) const; - const InputPin& GetInputPin(uint32_t pinId) const; - ImNodes::PinId GetInputPinUniqueId(uint32_t pinId) const; - - void ConnectOutput(uint32_t pinId, WorkflowNode& dstNode, uint32_t dstPinId); - void DisconnectOutput(uint32_t pinId); - - void DrawOutputPinDebugInfo(uint32_t pinId) const; - const OutputPin& GetOutputPin(uint32_t pinId) const; - ImNodes::PinId GetOutputPinUniqueId(uint32_t pinId) const; - - virtual void Evaluate(WorkflowEvaluationContext& ctx) = 0; - - void Draw(); - virtual void DrawExtra() {} - - void DrawDebugInfo() const; - virtual void DrawExtraDebugInfo() const {} - - virtual void ReadFrom(std::istream& istream); - virtual void WriteTo(std::ostream& ostream); - -protected: - InputPin& InsertInputPin(int atIdx); - void RemoveInputPin(int pin); - void SwapInputPin(int a, int b); - OutputPin& InsertOutputPin(int atIdx); - void RemoveOutputPin(int pin); - void SwapOutputPin(int a, int b); - - /* For \c Workflow to invoke, override by implementations */ - - void OnAttach(Workflow& workflow, uint32_t newId); - void OnDetach(); -}; - -class Workflow : public Asset -{ - friend class WorkflowNode; - friend class WorkflowEvaluationContext; - class Private; - -public: - using CategoryType = WorkflowAssetList; - static constinit const WorkflowAssetList Category; - -private: - std::vector<WorkflowConnection> mConnections; - std::vector<std::unique_ptr<WorkflowNode>> mNodes; - std::vector<std::unique_ptr<BaseValue>> mConstants; - std::vector<std::vector<uint32_t>> mDepthGroups; - int mConnectionCount; - int mNodeCount; - int mConstantCount; - bool mDepthsDirty = true; - -public: - /* Graph access */ - - const std::vector<WorkflowConnection>& GetConnections() const; - std::vector<WorkflowConnection>& GetConnections(); - const std::vector<std::unique_ptr<WorkflowNode>>& GetNodes() const; - std::vector<std::unique_ptr<WorkflowNode>>& GetNodes(); - const std::vector<std::unique_ptr<BaseValue>>& GetConstants() const; - std::vector<std::unique_ptr<BaseValue>>& GetConstants(); - - WorkflowConnection* GetConnectionById(uint32_t id); - WorkflowConnection* GetConnectionByLinkId(ImNodes::LinkId linkId); - WorkflowNode* GetNodeById(uint32_t id); - WorkflowNode* GetNodeByNodeId(ImNodes::NodeId nodeId); - BaseValue* GetConstantById(uint32_t id); - - struct GlobalPinId - { - WorkflowNode* Node; - uint32_t PinId; - /// true => input pin - /// false => output pin - bool IsOutput; - }; - - /// `pinId` should be the `UniqueId` of a pin from a node that's within this workflow. - GlobalPinId DisassembleGlobalPinId(ImNodes::PinId id); - ImNodes::PinId FabricateGlobalPinId(const WorkflowNode& node, uint32_t pinId, bool isOutput) const; - - const std::vector<std::vector<uint32_t>>& GetDepthGroups() const; - bool DoesDepthNeedsUpdate() const; - - /* Graph mutation */ - - void AddNode(std::unique_ptr<WorkflowNode> step); - void RemoveNode(uint32_t id); - - void RemoveConnection(uint32_t id); - - bool Connect(WorkflowNode& sourceNode, uint32_t sourcePin, WorkflowNode& destinationNode, uint32_t destinationPin); - bool DisconnectBySource(WorkflowNode& sourceNode, uint32_t sourcePin); - bool DisconnectByDestination(WorkflowNode& destinationNode, uint32_t destinationPin); - - /* Graph rebuild */ - - enum GraphUpdateResult - { - /// Successfully rebuilt graph dependent data. - /// Details: nothing is written. - GUR_Success, - /// Nothing has changed since last time UpdateGraph() was called. - /// Details: nothing is written. - GUR_NoWorkToDo, - /// Details: list of nodes is written. - GUR_UnsatisfiedDependencies, - /// Details: list of nodes is written. - GUR_UnreachableNodes, - }; - - using GraphUpdateDetails = std::variant< - // Case: nothing - std::monostate, - // Case: list of nodes (ids) - std::vector<uint32_t>>; - - GraphUpdateResult UpdateGraph(GraphUpdateDetails* details = nullptr); - - /* Serialization */ - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; - -private: - std::pair<WorkflowConnection&, uint32_t> AllocWorkflowConnection(); - std::pair<std::unique_ptr<WorkflowNode>&, uint32_t> AllocWorkflowStep(); -}; - -class WorkflowAssetList final : public AssetListTyped<Workflow> -{ -private: - // AC = Asset Creator - std::string mACNewName; - NameSelectionError mACNewNameError = NameSelectionError::Empty; - -public: - // Inherit constructors - using AssetListTyped::AssetListTyped; - -protected: - void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const override; - - std::string RetrieveNameFromFile(const std::filesystem::path& file) const override; - uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const override; - std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const override; - - bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const override; - Workflow* LoadInstance(const SavedAsset& assetInfo) const override; - Workflow* CreateInstance(const SavedAsset& assetInfo) const override; - bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const override; - - void DisplayAssetCreator(ListState& state) override; - void DisplayDetailsTable(ListState& state) const override; -}; diff --git a/core/src/Model/Workflow/Workflow_Main.cpp b/core/src/Model/Workflow/Workflow_Main.cpp deleted file mode 100644 index 3be2d4d..0000000 --- a/core/src/Model/Workflow/Workflow_Main.cpp +++ /dev/null @@ -1,846 +0,0 @@ -#include "Workflow.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/IO/Archive.hpp" -#include "Utils/UUID.hpp" - -#include <imgui.h> -#include <imgui_node_editor.h> -#include <imgui_stdlib.h> -#include <tsl/robin_set.h> -#include <algorithm> -#include <cassert> -#include <cstdint> -#include <fstream> -#include <iostream> -#include <queue> -#include <utility> - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; -namespace ImNodes = ax::NodeEditor; - -WorkflowConnection::WorkflowConnection() - : Id{ 0 } - , SourceNode{ WorkflowNode::kInvalidId } - , SourcePin{ WorkflowNode::kInvalidPinId } - , DestinationNode{ WorkflowNode::kInvalidId } - , DestinationPin{ WorkflowNode::kInvalidPinId } -{ -} - -bool WorkflowConnection::IsValid() const -{ - return Id != 0; -} - -ImNodes::LinkId WorkflowConnection::GetLinkId() const -{ - // Our id is 0-based (represents an index directly) - // but imgui-node-editor uses the value 0 to represent a null id, so we need to offset by 1 - return Id + 1; -} - -void WorkflowConnection::DrawDebugInfo() const -{ - ImGui::Text("Source (node with output pin):"); - ImGui::Text("{ Node = %u, Pin = %u }", SourceNode, SourcePin); - ImGui::Text("Destination (node with input pin):"); - ImGui::Text("{ Node = %u, Pin = %u }", DestinationNode, DestinationPin); -} - -void WorkflowConnection::ReadFrom(std::istream& stream) -{ - stream >> SourceNode >> SourcePin; - stream >> DestinationNode >> DestinationPin; -} - -void WorkflowConnection::WriteTo(std::ostream& stream) const -{ - stream << SourceNode << SourcePin; - stream << DestinationNode << DestinationPin; -} - -bool WorkflowNode::InputPin::IsConstantConnection() const -{ - return ConnectionToConst && IsConnected(); -} - -bool WorkflowNode::InputPin::IsConnected() const -{ - return Connection != WorkflowConnection::kInvalidId; -} - -BaseValue::Kind WorkflowNode::InputPin::GetMatchingType() const -{ - return MatchingType; -} - -bool WorkflowNode::OutputPin::IsConnected() const -{ - return Connection != WorkflowConnection::kInvalidId; -} - -BaseValue::Kind WorkflowNode::OutputPin::GetMatchingType() const -{ - return MatchingType; -} - -WorkflowNode::WorkflowNode(Kind kind, bool locked) - : mKind{ kind } - , mDepth{ -1 } - , mLocked(locked) -{ -} - -Vec2i WorkflowNode::GetPosition() const -{ - return mPosition; -} - -void WorkflowNode::SetPosition(const Vec2i& position) -{ - mPosition = position; -} - -uint32_t WorkflowNode::GetId() const -{ - return mId; -} - -ImNodes::NodeId WorkflowNode::GetNodeId() const -{ - // See WorkflowConnection::GetLinkId for the rationale - return mId + 1; -} - -WorkflowNode::Kind WorkflowNode::GetKind() const -{ - return mKind; -} - -int WorkflowNode::GetDepth() const -{ - return mDepth; -} - -bool WorkflowNode::IsLocked() const -{ - return mLocked; -} - -WorkflowNode::Type WorkflowNode::GetType() const -{ - if (IsInputNode()) { - return InputType; - } else if (IsOutputNode()) { - return OutputType; - } else { - return TransformType; - } -} - -bool WorkflowNode::IsInputNode() const -{ - return mInputs.size() == 0; -} - -bool WorkflowNode::IsOutputNode() const -{ - return mOutputs.size() == 0; -} - -void WorkflowNode::ConnectInput(uint32_t pinId, WorkflowNode& srcNode, uint32_t srcPinId) -{ - mWorkflow->Connect(*this, pinId, srcNode, srcPinId); -} - -void WorkflowNode::DisconnectInput(uint32_t pinId) -{ - mWorkflow->DisconnectByDestination(*this, pinId); -} - -void WorkflowNode::DrawInputPinDebugInfo(uint32_t pinId) const -{ - ImGui::Text("Node ID: %d", mId); - ImGui::Text("Pin ID: (input) %d", pinId); -} - -const WorkflowNode::InputPin& WorkflowNode::GetInputPin(uint32_t pinId) const -{ - return mInputs[pinId]; -} - -ImNodes::PinId WorkflowNode::GetInputPinUniqueId(uint32_t pinId) const -{ - return mWorkflow->FabricateGlobalPinId(*this, pinId, false); -} - -void WorkflowNode::ConnectOutput(uint32_t pinId, WorkflowNode& dstNode, uint32_t dstPinId) -{ - mWorkflow->Connect(dstNode, dstPinId, *this, pinId); -} - -void WorkflowNode::DisconnectOutput(uint32_t pinId) -{ - mWorkflow->DisconnectBySource(*this, pinId); -} - -void WorkflowNode::DrawOutputPinDebugInfo(uint32_t pinId) const -{ - ImGui::Text("Node ID: %d", mId); - ImGui::Text("Pin ID: (output) %d", pinId); -} - -const WorkflowNode::OutputPin& WorkflowNode::GetOutputPin(uint32_t pinId) const -{ - return mOutputs[pinId]; -} - -ImNodes::PinId WorkflowNode::GetOutputPinUniqueId(uint32_t pinId) const -{ - return mWorkflow->FabricateGlobalPinId(*this, pinId, true); -} - -void WorkflowNode::Draw() -{ - for (uint32_t i = 0; i < mInputs.size(); ++i) { - auto& pin = mInputs[i]; - auto& typeInfo = BaseValue::QueryInfo(pin.MatchingType); - ImNodes::BeginPin(GetInputPinUniqueId(i), ImNodes::PinKind::Input); - // TODO - ImNodes::EndPin(); - } - for (uint32_t i = 0; i < mOutputs.size(); ++i) { - auto& pin = mOutputs[i]; - auto& typeInfo = BaseValue::QueryInfo(pin.MatchingType); - ImNodes::BeginPin(GetOutputPinUniqueId(i), ImNodes::PinKind::Output); - // TODO - ImNodes::EndPin(); - } -} - -void WorkflowNode::DrawDebugInfo() const -{ - ImGui::Text("Node kind: %s", FormatKind(mKind)); - ImGui::Text("Node type: %s", FormatType(GetType())); - ImGui::Text("Node ID: %u", mId); - ImGui::Text("Depth: %d", mDepth); - DrawExtraDebugInfo(); -} - -void WorkflowNode::ReadFrom(std::istream& stream) -{ - stream >> mId; - stream >> mPosition.x >> mPosition.y; -} - -void WorkflowNode::WriteTo(std::ostream& stream) -{ - stream << mId; - stream << mPosition.x << mPosition.y; -} - -WorkflowNode::InputPin& WorkflowNode::InsertInputPin(int atIdx) -{ - assert(atIdx >= 0 && atIdx < mInputs.size()); - - mInputs.push_back(InputPin{}); - for (int i = (int)mInputs.size() - 1, end = atIdx + 1; i >= end; --i) { - SwapInputPin(i, i + 1); - } - - return mInputs[atIdx]; -} - -void WorkflowNode::RemoveInputPin(int pin) -{ - DisconnectInput(pin); - for (int i = 0, end = (int)mInputs.size() - 1; i < end; ++i) { - SwapInputPin(i, i + 1); - } - mInputs.resize(mInputs.size() - 1); -} - -void WorkflowNode::SwapInputPin(int a, int b) -{ - auto& pinA = mInputs[a]; - auto& pinB = mInputs[b]; - - if (mWorkflow) { - if (pinA.IsConnected() && !pinA.IsConstantConnection()) { - auto& conn = *mWorkflow->GetConnectionById(pinA.Connection); - conn.DestinationPin = b; - } - if (pinB.IsConnected() && !pinB.IsConstantConnection()) { - auto& conn = *mWorkflow->GetConnectionById(pinB.Connection); - conn.DestinationPin = a; - } - } - - std::swap(pinA, pinB); -} - -WorkflowNode::OutputPin& WorkflowNode::InsertOutputPin(int atIdx) -{ - assert(atIdx >= 0 && atIdx < mOutputs.size()); - - mOutputs.push_back(OutputPin{}); - for (int i = (int)mOutputs.size() - 1, end = atIdx + 1; i >= end; --i) { - SwapOutputPin(i, i + 1); - } - - return mOutputs[atIdx]; -} - -void WorkflowNode::RemoveOutputPin(int pin) -{ - DisconnectOutput(pin); - for (int i = 0, end = (int)mOutputs.size() - 1; i < end; ++i) { - SwapInputPin(i, i + 1); - } - mOutputs.resize(mOutputs.size() - 1); -} - -void WorkflowNode::SwapOutputPin(int a, int b) -{ - auto& pinA = mOutputs[a]; - auto& pinB = mOutputs[b]; - - if (mWorkflow) { - if (pinA.IsConnected()) { - auto& conn = *mWorkflow->GetConnectionById(pinA.Connection); - conn.SourcePin = b; - } - if (pinB.IsConnected()) { - auto& conn = *mWorkflow->GetConnectionById(pinB.Connection); - conn.SourcePin = a; - } - } - - std::swap(pinA, pinB); -} - -void WorkflowNode::OnAttach(Workflow& workflow, uint32_t newId) -{ -} - -void WorkflowNode::OnDetach() -{ -} - -const std::vector<WorkflowConnection>& Workflow::GetConnections() const -{ - return mConnections; -} - -std::vector<WorkflowConnection>& Workflow::GetConnections() -{ - return mConnections; -} - -const std::vector<std::unique_ptr<WorkflowNode>>& Workflow::GetNodes() const -{ - return mNodes; -} - -std::vector<std::unique_ptr<WorkflowNode>>& Workflow::GetNodes() -{ - return mNodes; -} - -const std::vector<std::unique_ptr<BaseValue>>& Workflow::GetConstants() const -{ - return mConstants; -} - -std::vector<std::unique_ptr<BaseValue>>& Workflow::GetConstants() -{ - return mConstants; -} - -WorkflowConnection* Workflow::GetConnectionById(uint32_t id) -{ - return &mConnections[id]; -} - -WorkflowConnection* Workflow::GetConnectionByLinkId(ImNodes::LinkId id) -{ - return &mConnections[(uint32_t)(size_t)id - 1]; -} - -WorkflowNode* Workflow::GetNodeById(uint32_t id) -{ - return mNodes[id].get(); -} - -WorkflowNode* Workflow::GetNodeByNodeId(ImNodes::NodeId id) -{ - return mNodes[(uint32_t)(size_t)id - 1].get(); -} - -BaseValue* Workflow::GetConstantById(uint32_t id) -{ - return mConstants[id].get(); -} - -Workflow::GlobalPinId Workflow::DisassembleGlobalPinId(ImNodes::PinId pinId) -{ - // imgui-node-editor requires all pins to have a global, unique id - // but in our model the pin are typed (input vs output) and associated with a node: there is no built-in global id - // Therefore we encode one ourselves - - // Global pin id format - // nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn Tppppppp ppppppppp pppppppp pppppppp - // <------- (32 bits) node id -------> ^<------ (31 bits) pin id --------> - // | (1 bit) input (false) vs output (true) - - // 1 is added to pin id to prevent the 0th node's 0th input pin resulting in a 0 global pin id - // (this is problematic because imgui-node-editor use 0 to represent null) - - auto id = static_cast<uint64_t>(pinId); - GlobalPinId result; - - result.Node = mNodes[id >> 32].get(); - result.PinId = (uint32_t)(id & 0x000000001FFFFFFF) - 1; - result.IsOutput = id >> 31; - - return result; -} - -ImNodes::PinId Workflow::FabricateGlobalPinId(const WorkflowNode& node, uint32_t pinId, bool isOutput) const -{ - // See this->DisassembleGlobalPinId for format details and rationale - - uint64_t id = 0; - id |= ((uint64_t)node.GetId() << 32); - id |= (isOutput << 31); - id |= ((pinId + 1) & 0x1FFFFFFF); - - return id; -} - -const std::vector<std::vector<uint32_t>>& Workflow::GetDepthGroups() const -{ - return mDepthGroups; -} - -bool Workflow::DoesDepthNeedsUpdate() const -{ - return mDepthsDirty; -} - -void Workflow::AddNode(std::unique_ptr<WorkflowNode> step) -{ - auto [storage, id] = AllocWorkflowStep(); - storage = std::move(step); - storage->OnAttach(*this, id); - storage->mWorkflow = this; - storage->mId = id; -} - -void Workflow::RemoveNode(uint32_t id) -{ - auto& step = mNodes[id]; - if (step == nullptr) return; - - step->OnDetach(); - step->mWorkflow = nullptr; - step->mId = WorkflowNode::kInvalidId; -} - -void Workflow::RemoveConnection(uint32_t id) -{ - auto& conn = mConnections[id]; - if (!conn.IsValid()) return; - - mNodes[conn.SourceNode]->mInputs[conn.SourcePin].Connection = WorkflowNode::kInvalidId; - mNodes[conn.DestinationNode]->mInputs[conn.DestinationPin].Connection = WorkflowNode::kInvalidId; - - conn = {}; - mDepthsDirty = true; -} - -bool Workflow::Connect(WorkflowNode& sourceNode, uint32_t sourcePin, WorkflowNode& destinationNode, uint32_t destinationPin) -{ - auto& src = sourceNode.mOutputs[sourcePin]; - auto& dst = destinationNode.mInputs[destinationPin]; - - // TODO report error to user? - if (src.GetMatchingType() != dst.GetMatchingType()) { - return false; - } - - if (src.IsConnected()) { - DisconnectBySource(sourceNode, sourcePin); - } - - auto [conn, id] = AllocWorkflowConnection(); - conn.SourceNode = sourceNode.GetId(); - conn.SourcePin = sourcePin; - conn.DestinationNode = destinationNode.GetId(); - conn.DestinationPin = destinationPin; - - src.Connection = id; - dst.Connection = id; - - mDepthsDirty = true; - return true; -} - -bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, uint32_t sourcePin) -{ - auto& sn = sourceNode.mOutputs[sourcePin]; - if (!sn.IsConnected()) return false; - - auto& conn = mConnections[sn.Connection]; - auto& dn = mNodes[conn.DestinationNode]->mInputs[conn.DestinationPin]; - - sn.Connection = WorkflowConnection::kInvalidId; - dn.Connection = WorkflowConnection::kInvalidId; - conn = {}; - - mDepthsDirty = true; - return true; -} - -bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, uint32_t destinationPin) -{ - auto& dn = destinationNode.mOutputs[destinationPin]; - if (!dn.IsConnected()) return false; - - auto& conn = mConnections[dn.Connection]; - auto& sn = mNodes[conn.SourceNode]->mInputs[conn.SourcePin]; - - sn.Connection = WorkflowConnection::kInvalidId; - dn.Connection = WorkflowConnection::kInvalidId; - conn = {}; - - mDepthsDirty = true; - return true; -} - -Workflow::GraphUpdateResult Workflow::UpdateGraph(GraphUpdateDetails* details) -{ - if (!mDepthsDirty) { - return GUR_NoWorkToDo; - } - - // Terminology: - // - Dependency = nodes its input pins are connected to - // - Dependents = nodes its output pins are connected to - - struct WorkingNode - { - // The max depth out of all dependency nodes, maintained during the traversal and committed as the actual depth - // when all dependencies of this node has been resolved. Add 1 to get the depth that will be assigned to the node. - int MaximumDepth = 0; - int FulfilledInputCount = 0; - }; - - std::vector<WorkingNode> workingNodes; - std::queue<uint32_t> q; - - // Check if all dependencies of this node is satisfied - auto CheckNodeDependencies = [&](WorkflowNode& node) -> bool { - for (auto& pin : node.mInputs) { - if (!pin.IsConnected()) { - return false; - } - } - return true; - }; - - workingNodes.reserve(mNodes.size()); - { - std::vector<uint32_t> unsatisfiedNodes; - for (uint32_t i = 0; i < mNodes.size(); ++i) { - auto& node = mNodes[i]; - workingNodes.push_back(WorkingNode{}); - - if (!node) continue; - - if (!CheckNodeDependencies(*node)) { - unsatisfiedNodes.push_back(i); - } - - node->mDepth = -1; - - // Start traversing with the input nodes - if (node->GetType() == WorkflowNode::InputType) { - q.push(i); - } - } - - if (!unsatisfiedNodes.empty()) { - if (details) { - details->emplace<decltype(unsatisfiedNodes)>(std::move(unsatisfiedNodes)); - } - return GUR_UnsatisfiedDependencies; - } - } - - auto ProcessNode = [&](WorkflowNode& node) -> void { - for (auto& pin : node.mOutputs) { - if (!pin.IsConnected()) continue; - auto& conn = mConnections[pin.Connection]; - - auto& wn = workingNodes[conn.DestinationNode]; - auto& n = *mNodes[conn.DestinationPin].get(); - - wn.FulfilledInputCount++; - wn.MaximumDepth = std::max(node.mDepth, wn.MaximumDepth); - - // Node's dependency is fulfilled, we can process its dependents next - // We use >= here because for a many-to-one pin, the dependency is an "or" relation ship, i.e. any of the nodes firing before this will fulfill the requirement - if (n.mInputs.size() >= wn.FulfilledInputCount) { - n.mDepth = wn.MaximumDepth + 1; - } - } - }; - - int processedNodes = 0; - while (!q.empty()) { - auto& wn = workingNodes[q.front()]; - auto& n = *mNodes[q.front()]; - q.pop(); - processedNodes++; - - ProcessNode(n); - } - - if (processedNodes < mNodes.size()) { - // There is unreachable nodes, collect them and report to the caller - - std::vector<uint32_t> unreachableNodes; - for (uint32_t i = 0; i < mNodes.size(); ++i) { - auto& wn = workingNodes[i]; - auto& n = *mNodes[i]; - - // This is a reachable node - if (n.mDepth != -1) continue; - - unreachableNodes.push_back(i); - } - - if (details) { - details->emplace<decltype(unreachableNodes)>(std::move(unreachableNodes)); - } - return GUR_UnreachableNodes; - } - - return GUR_Success; -} - -class Workflow::Private -{ -public: - template <class TSelf, class TProxy> - static void OperateStream(TSelf& self, TProxy& proxy) - { - // TODO - } -}; - -void Workflow::ReadFromDataStream(InputDataStream& stream) -{ - Private::OperateStream(*this, stream); -} - -void Workflow::WriteToDataStream(OutputDataStream& stream) const -{ - Private::OperateStream(*this, stream); -} - -std::pair<WorkflowConnection&, uint32_t> Workflow::AllocWorkflowConnection() -{ - for (size_t idx = 0; idx < mConnections.size(); ++idx) { - auto& elm = mConnections[idx]; - if (!elm.IsValid()) { - return { elm, (uint32_t)idx }; - } - } - - auto id = (uint32_t)mConnections.size(); - auto& conn = mConnections.emplace_back(WorkflowConnection{}); - conn.Id = id; - - return { conn, id }; -} - -std::pair<std::unique_ptr<WorkflowNode>&, uint32_t> Workflow::AllocWorkflowStep() -{ - for (size_t idx = 0; idx < mNodes.size(); ++idx) { - auto& elm = mNodes[idx]; - if (elm == nullptr) { - return { elm, (uint32_t)idx }; - } - } - - auto id = (uint32_t)mNodes.size(); - auto& node = mNodes.emplace_back(std::unique_ptr<WorkflowNode>()); - - return { node, id }; -} - -void WorkflowAssetList::DiscoverFiles(const std::function<void(SavedAsset)>& callback) const -{ - auto dir = GetConnectedProject().GetWorkflowsDirectory(); - DiscoverFilesByExtension(callback, dir, ".cplt-workflow"sv); -} - -std::string WorkflowAssetList::RetrieveNameFromFile(const fs::path& file) const -{ - auto res = DataArchive::LoadFile(file); - if (!res) return ""; - auto& stream = res.value(); - - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - return assetInfo.Name; -} - -uuids::uuid WorkflowAssetList::RetrieveUuidFromFile(const fs::path& file) const -{ - return uuids::uuid::from_string(file.stem().string()); -} - -fs::path WorkflowAssetList::RetrievePathFromAsset(const SavedAsset& asset) const -{ - auto fileName = uuids::to_string(asset.Uuid); - return GetConnectedProject().GetWorkflowPath(fileName); -} - -bool WorkflowAssetList::SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const -{ - auto path = RetrievePathFromAsset(assetInfo); - auto res = DataArchive::SaveFile(path); - if (!res) return false; - auto& stream = res.value(); - - stream.WriteObject(assetInfo); - // This cast is fine: calls to this class will always be wrapped in TypedAssetList<T>, which will ensure `asset` points to some Workflow - if (auto workflow = static_cast<const Workflow*>(asset)) { // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) - stream.WriteObject(*workflow); - } - - return true; -} - -static std::unique_ptr<Workflow> LoadWorkflowFromFile(const fs::path& path) -{ - auto res = DataArchive::LoadFile(path); - if (!res) return nullptr; - auto& stream = res.value(); - - // TODO this is currently unused - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - auto workflow = std::make_unique<Workflow>(); - stream.ReadObject(*workflow); - - return workflow; -} - -Workflow* WorkflowAssetList::LoadInstance(const SavedAsset& assetInfo) const -{ - return ::LoadWorkflowFromFile(RetrievePathFromAsset(assetInfo)).release(); -} - -Workflow* WorkflowAssetList::CreateInstance(const SavedAsset& assetInfo) const -{ - return new Workflow(); -} - -bool WorkflowAssetList::RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const -{ - auto path = RetrievePathFromAsset(assetInfo); - - auto workflow = ::LoadWorkflowFromFile(path); - if (!workflow) return false; - - SaveInstance(assetInfo, workflow.get()); - - return true; -} - -void WorkflowAssetList::DisplayAssetCreator(ListState& state) -{ - auto ValidateNewName = [&]() -> void { - if (mACNewName.empty()) { - mACNewNameError = NameSelectionError::Empty; - return; - } - - if (FindByName(mACNewName)) { - mACNewNameError = NameSelectionError::Duplicated; - return; - } - - mACNewNameError = NameSelectionError::None; - }; - - auto ShowNewNameErrors = [&]() -> void { - switch (mACNewNameError) { - case NameSelectionError::None: break; - case NameSelectionError::Duplicated: - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - break; - case NameSelectionError::Empty: - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - break; - } - }; - - auto IsInputValid = [&]() -> bool { - return mACNewNameError == NameSelectionError::None; - }; - - auto ResetState = [&]() -> void { - mACNewName.clear(); - ValidateNewName(); - }; - - if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &mACNewName)) { - ValidateNewName(); - } - - ShowNewNameErrors(); - - if (ImGui::Button(I18N_TEXT("OK", L10N_CONFIRM), !IsInputValid())) { - ImGui::CloseCurrentPopup(); - - Create(SavedAsset{ - .Name = mACNewName, - }); - ResetState(); - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } -} - -void WorkflowAssetList::DisplayDetailsTable(ListState& state) const -{ - ImGui::BeginTable("AssetDetailsTable", 1, ImGuiTableFlags_Borders); - - ImGui::TableSetupColumn(I18N_TEXT("Name", L10N_NAME)); - ImGui::TableHeadersRow(); - - for (auto& asset : this->GetAssets()) { - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups)) { - state.SelectedAsset = &asset; - } - } - - ImGui::EndTable(); -} diff --git a/core/src/Model/Workflow/Workflow_RTTI.cpp b/core/src/Model/Workflow/Workflow_RTTI.cpp deleted file mode 100644 index cb66c69..0000000 --- a/core/src/Model/Workflow/Workflow_RTTI.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "Workflow.hpp" - -#include "Model/Workflow/Nodes/DocumentNodes.hpp" -#include "Model/Workflow/Nodes/NumericNodes.hpp" -#include "Model/Workflow/Nodes/TextNodes.hpp" -#include "Model/Workflow/Nodes/UserInputNodes.hpp" -#include "Utils/I18n.hpp" -#include "Utils/Macros.hpp" - -#include <memory> - -const char* WorkflowNode::FormatKind(Kind kind) -{ - switch (kind) { - case KD_NumericAddition: return I18N_TEXT("Add", L10N_WORKFLOW_ADD); - case KD_NumericSubtraction: return I18N_TEXT("Subtract", L10N_WORKFLOW_SUB); - case KD_NumericMultiplication: return I18N_TEXT("Multiply", L10N_WORKFLOW_MUL); - case KD_NumericDivision: return I18N_TEXT("Divide", L10N_WORKFLOW_DIV); - case KD_NumericExpression: return I18N_TEXT("Evaluate expression", L10N_WORKFLOW_EVAL); - case KD_TextFormatting: return I18N_TEXT("Format text", L10N_WORKFLOW_FMT); - case KD_DocumentTemplateExpansion: return I18N_TEXT("Expand template", L10N_WORKFLOW_INSTANTIATE_TEMPLATE); - case KD_FormInput: return I18N_TEXT("Form input", L10N_WORKFLOW_FORM_INPUT); - case KD_DatabaseRowsInput: return I18N_TEXT("Database input", L10N_WORKFLOW_DB_INPUT); - - case InvalidKind: break; - } - return ""; -} - -const char* WorkflowNode::FormatCategory(WorkflowNode::Category category) -{ - switch (category) { - case CG_Numeric: return I18N_TEXT("Numeric", L10N_WORKFLOW_CATEGORY_NUMERIC); - case CG_Text: return I18N_TEXT("Text", L10N_WORKFLOW_CATEGORY_TEXT); - case CG_Document: return I18N_TEXT("Document", L10N_WORKFLOW_CATEGORY_DOCUMENT); - case CG_UserInput: return I18N_TEXT("User input", L10N_WORKFLOW_CATEGORY_USER_INPUT); - case CG_SystemInput: return I18N_TEXT("System input", L10N_WORKFLOW_CATEGORY_SYS_INPUT); - case CG_Output: return I18N_TEXT("Output", L10N_WORKFLOW_CATEGORY_OUTPUT); - - case InvalidCategory: break; - } - return ""; -} - -const char* WorkflowNode::FormatType(Type type) -{ - switch (type) { - case InputType: return I18N_TEXT("Input", L10N_WORKFLOW_KIND_INPUT); - case TransformType: return I18N_TEXT("Transform", L10N_WORKFLOW_KIND_TRANSFORM); - case OutputType: return I18N_TEXT("Output", L10N_WORKFLOW_KIND_OUTPUT); - } - return ""; -} - -WorkflowNode::Category WorkflowNode::QueryCategory(Kind kind) -{ - switch (kind) { - case KD_NumericAddition: - case KD_NumericSubtraction: - case KD_NumericMultiplication: - case KD_NumericDivision: - case KD_NumericExpression: - return CG_Numeric; - case KD_TextFormatting: - return CG_Text; - case KD_DocumentTemplateExpansion: - return CG_Document; - case KD_FormInput: - case KD_DatabaseRowsInput: - return CG_UserInput; - - case InvalidKind: break; - } - return InvalidCategory; -} - -std::span<const WorkflowNode::Kind> WorkflowNode::QueryCategoryMembers(Category category) -{ - constexpr WorkflowNode::Kind kNumeric[] = { - KD_NumericAddition, - KD_NumericSubtraction, - KD_NumericMultiplication, - KD_NumericDivision, - KD_NumericExpression, - }; - - constexpr WorkflowNode::Kind kText[] = { - KD_TextFormatting, - }; - - constexpr WorkflowNode::Kind kDocument[] = { - KD_DocumentTemplateExpansion, - }; - - constexpr WorkflowNode::Kind kUserInput[] = { - KD_FormInput, - KD_DatabaseRowsInput, - }; - - // TODO remove invalid kinds after we have nodes of these categories - constexpr WorkflowNode::Kind kSystemInput[] = { - InvalidKind, - }; - - constexpr WorkflowNode::Kind kOutput[] = { - InvalidKind, - }; - - switch (category) { - case CG_Numeric: return kNumeric; - case CG_Text: return kText; - case CG_Document: return kDocument; - case CG_UserInput: return kUserInput; - case CG_SystemInput: return kSystemInput; - case CG_Output: return kOutput; - - case InvalidCategory: break; - } - return {}; -} - -std::unique_ptr<WorkflowNode> WorkflowNode::CreateByKind(WorkflowNode::Kind kind) -{ - switch (kind) { - case KD_NumericAddition: return std::make_unique<NumericOperationNode>(NumericOperationNode::Addition); - case KD_NumericSubtraction: return std::make_unique<NumericOperationNode>(NumericOperationNode::Subtraction); - case KD_NumericMultiplication: return std::make_unique<NumericOperationNode>(NumericOperationNode::Multiplication); - case KD_NumericDivision: return std::make_unique<NumericOperationNode>(NumericOperationNode::Division); - case KD_NumericExpression: return std::make_unique<NumericExpressionNode>(); - case KD_TextFormatting: return std::make_unique<TextFormatterNode>(); - case KD_DocumentTemplateExpansion: return std::make_unique<DocumentTemplateExpansionNode>(); - case KD_FormInput: return std::make_unique<FormInputNode>(); - case KD_DatabaseRowsInput: return std::make_unique<DatabaseRowsInputNode>(); - - case InvalidKind: break; - } - return nullptr; -} - -bool WorkflowNode::IsInstance(const WorkflowNode* node) -{ - return true; -} diff --git a/core/src/Model/Workflow/fwd.hpp b/core/src/Model/Workflow/fwd.hpp deleted file mode 100644 index ed39bdb..0000000 --- a/core/src/Model/Workflow/fwd.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "Model/Workflow/Nodes/fwd.hpp" -#include "Model/Workflow/Values/fwd.hpp" - -// Evaluation.hpp -class WorkflowEvaluationError; -class WorkflowEvaluationContext; - -// SavedWorkflow.hpp -class SavedWorkflowCache; -class SavedWorkflow; - -// Value.hpp -class BaseValue; -class BaseObjectValue; - -// Workflow.hpp -class WorkflowConnection; -class WorkflowNode; -class Workflow; -class WorkflowAssetList; diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp deleted file mode 100644 index 358fc49..0000000 --- a/core/src/Model/fwd.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "Model/Template/fwd.hpp" -#include "Model/Workflow/fwd.hpp" - -// Database.hpp -enum class TableKind; -class SalesTable; -class PurchasesTable; -class DeliveryTable; -class MainDatabase; - -// Assets.hpp -struct SavedAsset; -class Asset; -enum class NameSelectionError; -class AssetList; - -// Filter.hpp -class TableRowsFilter; - -// GlobalStates.hpp -class GlobalStates; - -// Items.hpp -template <class T> -class ItemList; -template <class TSelf> -class ItemBase; -class ProductItem; -class FactoryItem; -class CustomerItem; - -// Project.hpp -class Project; diff --git a/core/src/UI/UI.hpp b/core/src/UI/UI.hpp deleted file mode 100644 index 0a80b4c..0000000 --- a/core/src/UI/UI.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include <imgui.h> - -namespace ImGui { - -void SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond cond = ImGuiCond_None); -void SetNextWindowCentered(ImGuiCond cond = ImGuiCond_None); - -void PushDisabled(); -void PopDisabled(); - -bool Button(const char* label, bool disabled); -bool Button(const char* label, const ImVec2& sizeArg, bool disabled); - -void ErrorIcon(); -void ErrorMessage(const char* fmt, ...); -void WarningIcon(); -void WarningMessage(const char* fmt, ...); - -enum class IconType -{ - Flow, - Circle, - Square, - Grid, - RoundSquare, - Diamond, -}; - -void DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor); -void Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color = ImVec4(1, 1, 1, 1), const ImVec4& innerColor = ImVec4(0, 0, 0, 0)); - -bool Splitter(bool splitVertically, float thickness, float* size1, float* size2, float minSize1, float minSize2, float splitterLongAxisSize = -1.0f); - -} // namespace ImGui - -namespace UI { - -void MainWindow(); - -void SettingsTab(); -void DatabaseViewTab(); -void ItemsTab(); -void WorkflowsTab(); -void TemplatesTab(); - -} // namespace UI diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp deleted file mode 100644 index e128a59..0000000 --- a/core/src/UI/UI_DatabaseView.cpp +++ /dev/null @@ -1,668 +0,0 @@ -#include "UI.hpp" - -#include "Model/Filter.hpp" -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Utils/I18n.hpp" -#include "Utils/ScopeGuard.hpp" -#include "Utils/Time.hpp" - -#include <IconsFontAwesome.h> -#include <SQLiteCpp/Statement.h> -#include <imgui.h> -#include <tsl/robin_map.h> -#include <cstdint> -#include <iostream> -#include <memory> -#include <vector> - -namespace CPLT_UNITY_ID { - -// TODO move to Settings -constexpr int kMaxEntriesPerPage = 32; -constexpr int kSummaryItemCount = 3; -constexpr int kSummaryMaxLength = 25; - -std::pair<int, int> SplitEntryIndex(int entryIdx) -{ - int page = entryIdx / kMaxEntriesPerPage; - int row = entryIdx % kMaxEntriesPerPage; - return { page, row }; -} - -enum class DeliveryDirection -{ - FactoryToWarehouse, - WarehouseToCustomer, -}; - -struct Item -{ - int ItemId; - int Count; -}; - -struct DeliveryEntry -{ - std::vector<Item> Items; - std::string ItemsSummary; - std::string ShipmentTime; - std::string ArriveTime; - DeliveryDirection Direction; - - const char* StringifyDirection() const - { - switch (Direction) { - case DeliveryDirection::FactoryToWarehouse: return "Factory to warehouse"; - case DeliveryDirection::WarehouseToCustomer: return "Warehouse to customer"; - } - } -}; - -struct SaleEntry -{ - static constexpr auto kType = DeliveryDirection::WarehouseToCustomer; - - std::vector<DeliveryEntry> AssociatedDeliveries; - std::vector<Item> Items; - std::string ItemsSummary; - std::string Customer; - std::string Deadline; - std::string DeliveryTime; - int Id; - bool DeliveriesCached = false; -}; - -struct PurchaseEntry -{ - static constexpr auto kType = DeliveryDirection::FactoryToWarehouse; - - std::vector<DeliveryEntry> AssociatedDeliveries; - std::vector<Item> Items; - std::string ItemsSummary; - std::string Factory; - std::string OrderTime; - std::string DeliveryTime; - int Id; - bool DeliveriesCached; -}; - -template <class T> -class GenericTableView -{ -public: - // clang-format off - static constexpr bool kHasItems = requires(T t) - { - t.Items; - t.ItemsSummary; - }; - static constexpr bool kHasCustomer = requires(T t) { t.Customer; }; - static constexpr bool kHasDeadline = requires(T t) { t.Deadline; }; - static constexpr bool kHasFactory = requires(T t) { t.Factory; }; - static constexpr bool kHasOrderTime = requires(T t) { t.OrderTime; }; - static constexpr bool kHasCompletionTime = requires(T t) { t.DeliveryTime; }; - static constexpr int kColumnCount = kHasItems + kHasCustomer + kHasDeadline + kHasFactory + kHasOrderTime + kHasCompletionTime; - // clang-format on - - using Page = std::vector<T>; - - struct QueryStatements - { - SQLite::Statement* GetRowCount; - SQLite::Statement* GetRows; - SQLite::Statement* GetItems; - SQLite::Statement* FilterRows; - } Statements; - -protected: - // Translation entries for implementer to fill out - const char* mEditDialogTitle; - - Project* mProject; - Page* mCurrentPage = nullptr; - - /// Current active filter object, or \c nullptr. - std::unique_ptr<TableRowsFilter> mActiveFilter; - - tsl::robin_map<int, Page> mPages; - - /// A vector of entry indices (in \c mEntries) that are visible under the current filter. - std::vector<int> mActiveEntries; - - /// Number of rows in the table. - int mRowCount; - /// Last possible page for the current set table and filter (inclusive). - int mLastPage; - - /// The current page the user is on. - int mCurrentPageNumber; - - /// Row index of the select entry - int mSelectRow; - -public: - /// Calculate the first visible row's entry index. - int GetFirstVisibleRowIdx() const - { - return mCurrentPageNumber * kMaxEntriesPerPage; - } - - Project* GetProject() const - { - return mProject; - } - - void OnProjectChanged(Project* newProject) - { - mProject = newProject; - - auto& stmt = *Statements.GetRowCount; - if (stmt.executeStep()) { - mRowCount = stmt.getColumn(0).getInt(); - } else { - std::cerr << "Failed to fetch row count from SQLite.\n"; - mRowCount = 0; - } - - mActiveFilter = nullptr; - mActiveEntries.clear(); - - mPages.clear(); - mCurrentPage = nullptr; - UpdateLastPage(); - SetPage(0); - - mSelectRow = -1; - } - - TableRowsFilter* GetFilter() const - { - return mActiveFilter.get(); - } - - void OnFilterChanged() - { - auto& stmt = *Statements.FilterRows; - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - // TODO lazy loading when too many results - mActiveEntries.clear(); - int columnIdx = stmt.getColumnIndex("Id"); - while (stmt.executeStep()) { - mActiveEntries.push_back(stmt.getColumn(columnIdx).getInt()); - } - - UpdateLastPage(); - SetPage(0); - - mSelectRow = -1; - } - - void OnFilterChanged(std::unique_ptr<TableRowsFilter> filter) - { - mActiveFilter = std::move(filter); - OnFilterChanged(); - } - - void Display() - { - bool dummy = true; - - if (ImGui::Button(ICON_FA_ARROW_LEFT, mCurrentPageNumber == 0)) { - SetPage(mCurrentPageNumber - 1); - } - - ImGui::SameLine(); - // +1 to convert from 0-based indices to 1-based, for human legibility - ImGui::Text("%d/%d", mCurrentPageNumber + 1, mLastPage + 1); - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_RIGHT, mCurrentPageNumber == mLastPage)) { - SetPage(mCurrentPageNumber + 1); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - // TODO - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EDIT " " I18N_TEXT("Edit", L10N_EDIT), mSelectRow == -1)) { - ImGui::OpenPopup(mEditDialogTitle); - } - if (ImGui::BeginPopupModal(mEditDialogTitle, &dummy, ImGuiWindowFlags_AlwaysAutoResize)) { - auto& entry = (*mCurrentPage)[mSelectRow]; - int entryIdx = GetFirstVisibleRowIdx() + mSelectRow; - EditEntry(entry, entryIdx, mSelectRow); - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), mSelectRow == -1)) { - // TODO - } - - ImGui::Columns(2); - { - DisplayMainTable(); - ImGui::NextColumn(); - - if (mSelectRow == -1) { - ImGui::TextWrapped("%s", I18N_TEXT("Select an entry to show associated deliveries", L10N_DATABASE_MESSAGE_NO_ORDER_SELECTED)); - } else { - DisplayDeliveriesTable(); - } - ImGui::NextColumn(); - } - ImGui::Columns(1); - } - - void SetPage(int page) - { - mCurrentPageNumber = page; - mCurrentPage = &LoadAndGetPage(page); - mSelectRow = -1; - } - -private: - static int CalcPageForRowId(int64_t entryIdx) - { - return entryIdx / kMaxEntriesPerPage; - } - - /// Calculate range [begin, end) of index for the list of entries that are currently visible that the path-th page would show. - /// i.e. when there is a filter, look into \c mActiveEntryIndices; when there is no filter, use directly. - static std::pair<int64_t, int64_t> CalcRangeForPage(int page) - { - int begin = page * kMaxEntriesPerPage; - return { begin, begin + kMaxEntriesPerPage }; - } - - void DisplayMainTable() - { - if (ImGui::BeginTable("DataTable", kColumnCount, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { - - if constexpr (kHasCustomer) ImGui::TableSetupColumn(I18N_TEXT("Customer", L10N_DATABASE_COLUMN_CUSTOMER)); - if constexpr (kHasDeadline) ImGui::TableSetupColumn(I18N_TEXT("Deadline", L10N_DATABASE_COLUMN_DEADLINE)); - if constexpr (kHasFactory) ImGui::TableSetupColumn(I18N_TEXT("Factory", L10N_DATABASE_COLUMN_FACTORY)); - if constexpr (kHasOrderTime) ImGui::TableSetupColumn(I18N_TEXT("Order time", L10N_DATABASE_COLUMN_ORDER_TIME)); - if constexpr (kHasCompletionTime) ImGui::TableSetupColumn(I18N_TEXT("Completion time", L10N_DATABASE_COLUMN_DELIVERY_TIME)); - if constexpr (kHasItems) ImGui::TableSetupColumn(I18N_TEXT("Items", L10N_DATABASE_COLUMN_ITEMS)); - ImGui::TableHeadersRow(); - - if (mActiveFilter) { - // TODO - auto [begin, end] = CalcRangeForPage(mCurrentPageNumber); - end = std::min(end, (int64_t)mActiveEntries.size() - 1); - for (int i = begin; i < end; ++i) { - int rowIdx = mActiveEntries[i]; - DisplayEntry(rowIdx); - } - } else { - int firstRowIdx = mCurrentPageNumber * kMaxEntriesPerPage; - auto& page = *mCurrentPage; - - int end = std::min((int)page.size(), kMaxEntriesPerPage); - for (int i = 0; i < end; ++i) { - DisplayEntry(page[i], i, firstRowIdx + i); - } - } - - ImGui::EndTable(); - } - } - - void DisplayEntry(int rowIdx) - { - // TODO - // auto [pageNumber, pageEntry] = SplitRowIndex(rowIdx); - // auto& entry = LoadAndGetPage(pageNumber)[pageEntry]; - // DisplayEntry(entry, rowIdx); - } - - void DisplayEntry(T& entry, int rowIdx, int entryIdx) - { - ImGui::PushID(rowIdx); - ImGui::TableNextRow(); - - if constexpr (kHasCustomer) { - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Customer.c_str(), mSelectRow == rowIdx, ImGuiSelectableFlags_SpanAllColumns)) { - mSelectRow = rowIdx; - } - } - - if constexpr (kHasDeadline) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.Deadline.c_str()); - } - - if constexpr (kHasFactory) { - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Factory.c_str(), mSelectRow == rowIdx, ImGuiSelectableFlags_SpanAllColumns)) { - mSelectRow = rowIdx; - } - } - - if constexpr (kHasOrderTime) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.OrderTime.c_str()); - } - - if constexpr (kHasCompletionTime) { - ImGui::TableNextColumn(); - if (entry.DeliveryTime.empty()) { - ImGui::TextUnformatted(I18N_TEXT("Not delivered", L10N_DATABASE_MESSAGE_NOT_DELIVERED)); - } else { - ImGui::TextUnformatted(entry.DeliveryTime.c_str()); - } - } - - if constexpr (kHasItems) { - ImGui::TableNextColumn(); - if (ImGui::TreeNode(entry.ItemsSummary.c_str())) { - DrawItems(entry.Items); - ImGui::TreePop(); - } - } - - ImGui::PopID(); - } - - void EditEntry(T& entry, int rowIdx, int entryIdx) - { - // TODO - } - - void DisplayDeliveriesTable() - { - if (ImGui::BeginTable("DeliveriesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { - - ImGui::TableSetupColumn(I18N_TEXT("Shipment time", L10N_DATABASE_COLUMN_SHIPMENT_TIME)); - ImGui::TableSetupColumn(I18N_TEXT("Arrival time", L10N_DATABASE_COLUMN_ARRIVAL_TIME)); - ImGui::TableSetupColumn(I18N_TEXT("Items", L10N_DATABASE_COLUMN_ITEMS)); - ImGui::TableHeadersRow(); - - auto& entry = (*mCurrentPage)[mSelectRow]; - auto& deliveries = entry.AssociatedDeliveries; - for (auto& delivery : deliveries) { - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - ImGui::TextUnformatted(delivery.ShipmentTime.c_str()); - - ImGui::TableNextColumn(); - ImGui::TextUnformatted(delivery.ArriveTime.c_str()); - - ImGui::TableNextColumn(); - if (ImGui::TreeNode(delivery.ItemsSummary.c_str())) { - DrawItems(delivery.Items); - ImGui::TreePop(); - } - } - - ImGui::EndTable(); - } - } - - std::vector<Item> LoadItems(SQLite::Statement& stmt, int64_t id) - { - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - stmt.bind(1, id); - - std::vector<Item> entries; - int itemIdCol = stmt.getColumnIndex("ItemId"); - int countCol = stmt.getColumnIndex("Count"); - while (stmt.executeStep()) { - entries.push_back(Item{ - .ItemId = stmt.getColumn(itemIdCol).getInt(), - .Count = stmt.getColumn(countCol).getInt(), - }); - } - - return entries; - } - - std::string CreateItemsSummary(const std::vector<Item>& items) - { - if (items.empty()) { - return "<empty>"; - } - - std::string result; - for (int i = 0, max = std::min((int)items.size(), kSummaryItemCount); i < max; ++i) { - auto& name = mProject->Products.Find(items[i].ItemId)->GetName(); - if (result.length() + name.length() > kSummaryMaxLength) { - break; - } - - result += name; - result += ", "; - } - - // Remove ", " - result.pop_back(); - result.pop_back(); - - result += "..."; - - return result; - } - - std::vector<DeliveryEntry> LoadDeliveriesEntries(int64_t orderId, DeliveryDirection type) - { - bool outgoingFlag; - switch (type) { - case DeliveryDirection::FactoryToWarehouse: outgoingFlag = false; break; - case DeliveryDirection::WarehouseToCustomer: outgoingFlag = true; break; - } - - auto& stmt = mProject->Database.GetDeliveries().FilterByTypeAndId; - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - stmt.bind(1, orderId); - stmt.bind(2, outgoingFlag); - - std::vector<DeliveryEntry> entries; - int rowIdCol = stmt.getColumnIndex("Id"); - int sendTimeCol = stmt.getColumnIndex("ShipmentTime"); - int arrivalTimeCol = stmt.getColumnIndex("ArrivalTime"); - while (stmt.executeStep()) { - auto items = LoadItems( - mProject->Database.GetDeliveries().GetItems, - stmt.getColumn(rowIdCol).getInt64()); - auto summary = CreateItemsSummary(items); - - entries.push_back(DeliveryEntry{ - .Items = std::move(items), - .ItemsSummary = std::move(summary), - .ShipmentTime = TimeUtils::StringifyTimeStamp(stmt.getColumn(arrivalTimeCol).getInt64()), - .ArriveTime = TimeUtils::StringifyTimeStamp(stmt.getColumn(sendTimeCol).getInt64()), - .Direction = type, - }); - } - - return entries; - } - - Page& LoadAndGetPage(int page) - { - auto iter = mPages.find(page); - if (iter != mPages.end()) { - return iter.value(); - } - - auto& stmt = *Statements.GetRows; - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - stmt.bind(1, kMaxEntriesPerPage); - stmt.bind(2, page * kMaxEntriesPerPage); - - // If a field flag is false, the column index won't be used (controlled by other if constexpr's downstream) - // so there is no UB here - - // This column is always necessary (and present) because the deliveries table require it - int idCol = stmt.getColumnIndex("Id"); - - int customerCol; - if constexpr (kHasCustomer) customerCol = stmt.getColumnIndex("Customer"); - - int deadlineCol; - if constexpr (kHasDeadline) deadlineCol = stmt.getColumnIndex("Deadline"); - - int factoryCol; - if constexpr (kHasFactory) factoryCol = stmt.getColumnIndex("Factory"); - - int orderTimeCol; - if constexpr (kHasOrderTime) orderTimeCol = stmt.getColumnIndex("OrderTime"); - - int deliveryTimeCol; - if constexpr (kHasCompletionTime) deliveryTimeCol = stmt.getColumnIndex("DeliveryTime"); - - Page entries; - while (stmt.executeStep()) { - auto& entry = entries.emplace_back(); - - auto id = stmt.getColumn(idCol).getInt64(); - entry.AssociatedDeliveries = LoadDeliveriesEntries(id, T::kType); - - if constexpr (kHasItems) { - auto items = LoadItems( - *Statements.GetItems, - id); - auto itemsSummary = CreateItemsSummary(items); - entry.Items = std::move(items); - entry.ItemsSummary = std::move(itemsSummary); - } - - if constexpr (kHasCustomer) { - auto customerId = stmt.getColumn(customerCol).getInt(); - entry.Customer = mProject->Customers.Find(customerId)->GetName(); - } - - if constexpr (kHasDeadline) { - auto timeStamp = stmt.getColumn(deadlineCol).getInt64(); - entry.Deadline = TimeUtils::StringifyTimeStamp(timeStamp); - } - - if constexpr (kHasFactory) { - auto factoryId = stmt.getColumn(factoryCol).getInt(); - entry.Factory = mProject->Factories.Find(factoryId)->GetName(); - } - - if constexpr (kHasOrderTime) { - auto timeStamp = stmt.getColumn(orderTimeCol).getInt64(); - entry.OrderTime = TimeUtils::StringifyTimeStamp(timeStamp); - } - - if constexpr (kHasCompletionTime) { - auto timeStamp = stmt.getColumn(deliveryTimeCol).getInt64(); - entry.DeliveryTime = TimeUtils::StringifyTimeStamp(timeStamp); - } - } - - auto [res, _] = mPages.try_emplace(page, std::move(entries)); - return res.value(); - } - - void DrawItems(const std::vector<Item>& items) - { - for (auto& item : items) { - auto& name = mProject->Products.Find(item.ItemId)->GetName(); - ImGui::Text("%s × %d", name.c_str(), item.Count); - } - } - - void UpdateLastPage() - { - mLastPage = mActiveEntries.empty() - ? CalcPageForRowId(mRowCount) - : CalcPageForRowId(mActiveEntries.back()); - } -}; - -class SalesTableView : public GenericTableView<SaleEntry> -{ -public: - SalesTableView() - { - mEditDialogTitle = I18N_TEXT("Edit sales entry", L10N_DATABASE_SALES_VIEW_EDIT_DIALOG_TITLE); - } - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - void OnProjectChanged(Project* newProject) - { - auto& table = newProject->Database.GetSales(); - Statements.GetRowCount = &table.GetRowCount; - Statements.GetRows = &table.GetRows; - Statements.GetItems = &table.GetItems; - // TODO - // stmts.FilterRowsStatement = ; - - GenericTableView<SaleEntry>::OnProjectChanged(newProject); - } -#pragma clang diagnostic pop -}; - -class PurchasesTableView : public GenericTableView<PurchaseEntry> -{ -public: - PurchasesTableView() - { - mEditDialogTitle = I18N_TEXT("Edit purchase entry", L10N_DATABASE_PURCHASES_VIEW_EDIT_DIALOG_TITLE); - } - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - void OnProjectChanged(Project* newProject) - { - auto& table = newProject->Database.GetPurchases(); - Statements.GetRowCount = &table.GetRowCount; - Statements.GetRows = &table.GetRows; - Statements.GetItems = &table.GetItems; - // TODO - // stmts.FilterRowsStatement = ; - - GenericTableView<PurchaseEntry>::OnProjectChanged(newProject); - } -#pragma clang diagnostic pop -}; -} // namespace CPLT_UNITY_ID - -void UI::DatabaseViewTab() -{ - auto& gs = GlobalStates::GetInstance(); - - static Project* currentProject = nullptr; - static CPLT_UNITY_ID::SalesTableView sales; - static CPLT_UNITY_ID::PurchasesTableView purchases; - - if (currentProject != gs.GetCurrentProject()) { - currentProject = gs.GetCurrentProject(); - sales.OnProjectChanged(currentProject); - purchases.OnProjectChanged(currentProject); - } - - if (ImGui::BeginTabBar("DatabaseViewTabs")) { - if (ImGui::BeginTabItem(I18N_TEXT("Sales", L10N_DATABASE_SALES_VIEW_TAB_NAME))) { - sales.Display(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(I18N_TEXT("Purchases", L10N_DATABASE_PURCHASES_VIEW_TAB_NAME))) { - purchases.Display(); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } -} diff --git a/core/src/UI/UI_Items.cpp b/core/src/UI/UI_Items.cpp deleted file mode 100644 index a33c61b..0000000 --- a/core/src/UI/UI_Items.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Utils/I18n.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <imgui_stdlib.h> - -namespace CPLT_UNITY_ID { - -enum class ActionResult -{ - Confirmed, - Canceled, - Pending, -}; - -/// \param list Item list that the item is in. -/// \param item A non-null pointer to the currently being edited item. It should not change until this function returns a non-\c ActionResult::Pending value. -template <class T> -ActionResult ItemEditor(ItemList<T>& list, T* item) -{ - constexpr bool kHasDescription = requires(T t) - { - t.GetDescription(); - }; - constexpr bool kHasEmail = requires(T t) - { - t.GetEmail(); - }; - - static bool duplicateName = false; - - static std::string name; - static std::string description; - static std::string email; - if (name.empty()) { - name = item->GetName(); - if constexpr (kHasDescription) description = item->GetDescription(); - if constexpr (kHasEmail) email = item->GetEmail(); - } - - auto ClearStates = [&]() { - duplicateName = false; - name = {}; - description = {}; - }; - - if (ImGui::InputText(I18N_TEXT("Name", L10N_ITEM_COLUMN_NAME), &name)) { - duplicateName = name != item->GetName() && list.Find(name) != nullptr; - } - if constexpr (kHasDescription) ImGui::InputText(I18N_TEXT("Description", L10N_ITEM_COLUMN_DESCRIPTION), &description); - if constexpr (kHasEmail) ImGui::InputText(I18N_TEXT("Email", L10N_ITEM_COLUMN_EMAIL), &email); - - if (name.empty()) { - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - } - if (duplicateName) { - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - } - - // Return Value - auto rv = ActionResult::Pending; - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), name.empty() || duplicateName)) { - if (item->GetName() != name) { - item->SetName(std::move(name)); - } - if constexpr (kHasDescription) - if (item->GetDescription() != description) { - item->SetDescription(std::move(description)); - } - if constexpr (kHasEmail) - if (item->GetEmail() != email) { - item->SetEmail(std::move(email)); - } - - ImGui::CloseCurrentPopup(); - ClearStates(); - rv = ActionResult::Confirmed; - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - ClearStates(); - rv = ActionResult::Canceled; - } - - return rv; -} - -template <class T> -void ItemListEntries(ItemList<T>& list, int& selectedIdx) -{ - constexpr bool kHasDescription = requires(T t) - { - t.GetDescription(); - }; - constexpr bool kHasEmail = requires(T t) - { - t.GetEmail(); - }; - constexpr bool kHasStock = requires(T t) - { - t.GetPrice(); - }; - constexpr bool kHasPrice = requires(T t) - { - t.GetPrice(); - }; - constexpr int kColumns = 1 /* Name column */ + kHasDescription + kHasEmail + kHasStock + kHasPrice; - - if (ImGui::BeginTable("", kColumns, ImGuiTableFlags_Borders)) { - - ImGui::TableSetupColumn(I18N_TEXT("Name", L10N_ITEM_COLUMN_NAME)); - if constexpr (kHasDescription) ImGui::TableSetupColumn(I18N_TEXT("Description", L10N_ITEM_COLUMN_DESCRIPTION)); - if constexpr (kHasEmail) ImGui::TableSetupColumn(I18N_TEXT("Email", L10N_ITEM_COLUMN_EMAIL)); - if constexpr (kHasStock) ImGui::TableSetupColumn(I18N_TEXT("Stock", L10N_ITEM_COLUMN_STOCK)); - if constexpr (kHasPrice) ImGui::TableSetupColumn(I18N_TEXT("Price", L10N_ITEM_COLUMN_PRICE)); - ImGui::TableHeadersRow(); - - size_t idx = 0; - for (auto& entry : list) { - if (entry.IsInvalid()) { - continue; - } - - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.GetName().c_str(), selectedIdx == idx, ImGuiSelectableFlags_SpanAllColumns)) { - selectedIdx = idx; - } - - if constexpr (kHasDescription) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.GetDescription().c_str()); - } - - if constexpr (kHasEmail) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.GetEmail().c_str()); - } - - if constexpr (kHasStock) { - ImGui::TableNextColumn(); - ImGui::Text("%d", entry.GetStock()); - } - - if constexpr (kHasPrice) { - ImGui::TableNextColumn(); - // TODO format in dollars - ImGui::Text("%d", entry.GetPrice()); - } - - idx++; - } - ImGui::EndTable(); - } -} - -template <class T> -void ItemListEditor(ItemList<T>& list) -{ - bool opened = true; - static int selectedIdx = -1; - static T* editingItem = nullptr; - - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Add item", L10N_ITEM_ADD_DIALOG_TITLE)); - - editingItem = &list.Insert(""); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Add item", L10N_ITEM_ADD_DIALOG_TITLE), &opened, ImGuiWindowFlags_AlwaysAutoResize)) { - switch (ItemEditor(list, editingItem)) { - case ActionResult::Confirmed: - editingItem = nullptr; - break; - case ActionResult::Canceled: - list.Remove(editingItem->GetId()); - editingItem = nullptr; - break; - default: - break; - } - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EDIT " " I18N_TEXT("Edit", L10N_EDIT), selectedIdx == -1)) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Edit item", L10N_ITEM_EDIT_DIALOG_TITLE)); - - editingItem = list.Find(selectedIdx); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Edit item", L10N_ITEM_EDIT_DIALOG_TITLE), &opened, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ItemEditor(list, editingItem) != ActionResult::Pending) { - editingItem = nullptr; - } - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), selectedIdx == -1)) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Delete item", L10N_ITEM_DELETE_DIALOG_TITLE)); - - list.Remove(selectedIdx); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Delete item", L10N_ITEM_DELETE_DIALOG_TITLE), &opened, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::TextUnformatted(I18N_TEXT("Are you sure you want to delete this item?", L10N_ITEM_DELETE_DIALOG_MESSAGE)); - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM))) { - ImGui::CloseCurrentPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - - ItemListEntries<T>(list, selectedIdx); -} -} // namespace CPLT_UNITY_ID - -void UI::ItemsTab() -{ - auto& gs = GlobalStates::GetInstance(); - - if (ImGui::BeginTabBar("ItemViewTabs")) { - if (ImGui::BeginTabItem(I18N_TEXT("Products", L10N_ITEM_CATEGORY_PRODUCT))) { - CPLT_UNITY_ID::ItemListEditor(gs.GetCurrentProject()->Products); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(I18N_TEXT("Factories", L10N_ITEM_CATEGORY_FACTORY))) { - CPLT_UNITY_ID::ItemListEditor(gs.GetCurrentProject()->Factories); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(I18N_TEXT("Customers", L10N_ITEM_CATEGORY_CUSTOMER))) { - CPLT_UNITY_ID::ItemListEditor(gs.GetCurrentProject()->Customers); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } -} diff --git a/core/src/UI/UI_MainWindow.cpp b/core/src/UI/UI_MainWindow.cpp deleted file mode 100644 index d059359..0000000 --- a/core/src/UI/UI_MainWindow.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Utils/I18n.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <imgui_stdlib.h> -#include <portable-file-dialogs.h> -#include <filesystem> -#include <memory> - -namespace fs = std::filesystem; - -namespace CPLT_UNITY_ID { -void ProjectTab_Normal() -{ - auto& gs = GlobalStates::GetInstance(); - - if (ImGui::Button(ICON_FA_TIMES " " I18N_TEXT("Close", L10N_CLOSE))) { - gs.SetCurrentProject(nullptr); - return; - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER " " I18N_TEXT("Open in filesystem", L10N_PROJECT_OPEN_IN_FILESYSTEM))) { - // TODO - } - - ImGui::Text("%s %s", I18N_TEXT("Project name", L10N_PROJECT_NAME), gs.GetCurrentProject()->GetName().c_str()); - ImGui::Text("%s %s", I18N_TEXT("Project path", L10N_PROJECT_PATH), gs.GetCurrentProject()->GetPathString().c_str()); -} - -void ProjectTab_NoProject() -{ - auto& gs = GlobalStates::GetInstance(); - - bool openedDummy = true; - bool openErrorDialog = false; - static std::string projectName; - static std::string dirName; - static fs::path dirPath; - static bool dirNameIsValid = false; - - auto TrySelectPath = [&](fs::path newPath) { - if (fs::exists(newPath)) { - dirNameIsValid = true; - dirName = newPath.string(); - dirPath = std::move(newPath); - } else { - dirNameIsValid = false; - } - }; - - if (ImGui::Button(I18N_TEXT("Create project....", L10N_PROJECT_NEW))) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Create project wizard", L10N_PROJECT_NEW_DIALOG_TITLE)); - } - - // Make it so that the modal dialog has a close button - if (ImGui::BeginPopupModal(I18N_TEXT("Create project wizard", L10N_PROJECT_NEW_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::InputTextWithHint("##ProjectName", I18N_TEXT("Project name", L10N_PROJECT_NAME), &projectName); - - if (ImGui::InputTextWithHint("##ProjectPath", I18N_TEXT("Project path", L10N_PROJECT_PATH), &dirName)) { - // Changed, validate value - TrySelectPath(fs::path(dirName)); - } - ImGui::SameLine(); - if (ImGui::Button("...")) { - auto selection = pfd::select_folder(I18N_TEXT("Project path", L10N_PROJECT_NEW_PATH_DIALOG_TITLE)).result(); - if (!selection.empty()) { - TrySelectPath(fs::path(selection)); - } - } - - if (projectName.empty()) { - ImGui::ErrorMessage("%s", I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - } - if (!dirNameIsValid) { - ImGui::ErrorMessage("%s", I18N_TEXT("Invalid path", L10N_INVALID_PATH_ERROR)); - } - - ImGui::Spacing(); - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), !dirNameIsValid || projectName.empty())) { - ImGui::CloseCurrentPopup(); - - gs.SetCurrentProject(std::make_unique<Project>(std::move(dirPath), std::move(projectName))); - - // Dialog just got closed, reset states - projectName.clear(); - dirName.clear(); - dirPath.clear(); - dirNameIsValid = false; - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Open project...", L10N_PROJECT_OPEN))) { - auto selection = pfd::open_file(I18N_TEXT("Open project", L10N_PROJECT_OPEN_DIALOG_TITLE)).result(); - if (!selection.empty()) { - fs::path path(selection[0]); - - try { - // Project's constructor wants a path to directory containing cplt_project.json - gs.SetCurrentProject(std::make_unique<Project>(path.parent_path())); - openErrorDialog = false; - } catch (const std::exception& e) { - openErrorDialog = true; - } - } - } - - // TODO cleanup UI - // Recent projects - - ImGui::Separator(); - ImGui::TextUnformatted(I18N_TEXT("Recent projects", L10N_PROJECT_RECENTS)); - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Clear", L10N_PROJECT_RECENTS_CLEAR))) { - gs.ClearRecentProjects(); - } - - auto& rp = gs.GetRecentProjects(); - // End of vector is the most recently used, so that appending has less overhead - size_t toRemoveIdx = rp.size(); - - if (rp.empty()) { - ImGui::TextUnformatted(I18N_TEXT("No recent projects", L10N_PROJECT_RECENTS_NONE_PRESENT)); - } else { - for (auto it = rp.rbegin(); it != rp.rend(); ++it) { - auto& [path, recent] = *it; - ImGui::TextUnformatted(recent.c_str()); - - size_t idx = std::distance(it, rp.rend()) - 1; - ImGui::PushID(idx); - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER_OPEN)) { - try { - gs.SetCurrentProject(std::make_unique<Project>(path)); - openErrorDialog = false; - } catch (const std::exception& e) { - openErrorDialog = true; - } - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(I18N_TEXT("Open this project", L10N_PROJECT_RECENTS_OPEN_TOOLTIP)); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH)) { - toRemoveIdx = idx; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(I18N_TEXT("Delete this project from the Recent Projects list, the project itself will not be affected", L10N_PROJECT_RECENTS_DELETE_TOOLTIP)); - } - - ImGui::PopID(); - } - } - - if (toRemoveIdx != rp.size()) { - gs.RemoveRecentProject(toRemoveIdx); - } - - if (openErrorDialog) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Error", L10N_ERROR)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Error", L10N_ERROR), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::ErrorMessage("%s", I18N_TEXT("Invalid project file", L10N_PROJECT_INVALID_PROJECT_FORMAT)); - ImGui::EndPopup(); - } -} -} // namespace CPLT_UNITY_ID - -void UI::MainWindow() -{ - auto& gs = GlobalStates::GetInstance(); - - auto windowSize = ImGui::GetMainViewport()->Size; - ImGui::SetNextWindowSize({ windowSize.x, windowSize.y }); - ImGui::SetNextWindowPos({ 0, 0 }); - ImGui::Begin("##MainWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); - if (ImGui::BeginTabBar("MainWindowTabs")) { - if (ImGui::BeginTabItem(ICON_FA_COGS " " I18N_TEXT("Settings", L10N_MAIN_TAB_SETTINGS))) { - UI::SettingsTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_FILE " " I18N_TEXT("Project", L10N_MAIN_WINDOW_TAB_PROJECT), nullptr)) { - if (gs.HasCurrentProject()) { - CPLT_UNITY_ID::ProjectTab_Normal(); - } else { - CPLT_UNITY_ID::ProjectTab_NoProject(); - } - ImGui::EndTabItem(); - } - if (!gs.HasCurrentProject()) { - // No project open, simply skip all project specific tabs - goto endTab; - } - - if (ImGui::BeginTabItem(ICON_FA_DATABASE " " I18N_TEXT("Data", L10N_MAIN_WINDOW_TAB_DATABASE_VIEW))) { - UI::DatabaseViewTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_BOX " " I18N_TEXT("Items", L10N_MAIN_WINDOW_TAB_ITEMS))) { - UI::ItemsTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_SCROLL " " I18N_TEXT("Workflows", L10N_MAIN_WINDOW_TAB_WORKFLOWS))) { - UI::WorkflowsTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_TABLE " " I18N_TEXT("Templates", L10N_MAIN_WINDOW_TAB_TEMPLATES))) { - UI::TemplatesTab(); - ImGui::EndTabItem(); - } - - endTab: - ImGui::EndTabBar(); - } - ImGui::End(); -} diff --git a/core/src/UI/UI_Settings.cpp b/core/src/UI/UI_Settings.cpp deleted file mode 100644 index 107b94c..0000000 --- a/core/src/UI/UI_Settings.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "UI/UI.hpp" - -#include <imgui.h> - -void UI::SettingsTab() -{ - // TODO -} diff --git a/core/src/UI/UI_Templates.cpp b/core/src/UI/UI_Templates.cpp deleted file mode 100644 index cd15cb7..0000000 --- a/core/src/UI/UI_Templates.cpp +++ /dev/null @@ -1,977 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Model/Template/TableTemplate.hpp" -#include "Model/Template/TableTemplateIterator.hpp" -#include "Model/Template/Template.hpp" -#include "Utils/I18n.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <imgui_extra_math.h> -#include <imgui_internal.h> -#include <imgui_stdlib.h> -#include <charconv> -#include <fstream> -#include <iostream> -#include <utility> -#include <variant> - -namespace CPLT_UNITY_ID { -class TemplateUI -{ -public: - static std::unique_ptr<TemplateUI> CreateByKind(std::unique_ptr<Template> tmpl); - static std::unique_ptr<TemplateUI> CreateByKind(Template::Kind kind); - - virtual ~TemplateUI() = default; - virtual void Display() = 0; - virtual void Close() = 0; -}; - -// Table template styles -constexpr ImU32 kSingleParamOutline = IM_COL32(255, 255, 0, 255); -constexpr ImU32 kArrayGroupOutline = IM_COL32(255, 0, 0, 255); - -class TableTemplateUI : public TemplateUI -{ -private: - std::unique_ptr<TableTemplate> mTable; - - struct UICell - { - bool Hovered = false; - bool Held = false; - bool Selected = false; - }; - std::vector<UICell> mUICells; - - struct UIArrayGroup - { - ImVec2 Pos; - ImVec2 Size; - }; - std::vector<UIArrayGroup> mUIArrayGroups; - - struct Sizer - { - bool Hovered = false; - bool Held = false; - }; - std::vector<Sizer> mRowSizers; - std::vector<Sizer> mColSizers; - - /* Selection range */ - Vec2i mSelectionTL; - Vec2i mSelectionBR; - - /* Selection states */ - - /// "CStates" stands for "Constant cell selection States" - struct CStates - { - }; - - /// "SStates" stands for "Singular parameter selection States". - struct SStates - { - std::string EditBuffer; - bool ErrorDuplicateVarName; - bool HasLeftAG; - bool HasRightAG; - }; - - /// "AStates" stands for "Array group parameter selection States". - struct AStates - { - std::string EditBuffer; - bool ErrorDuplicateVarName; - }; - - // "RStates" stands for "Range selection States". - struct RStates - { - }; - - union - { - // Initialize to this element - std::monostate mIdleState{}; - CStates mCS; - SStates mSS; - AStates mAS; - RStates mRS; - }; - - /* Table resizer dialog states */ - int mNewTableWidth; - int mNewTableHeight; - - /* Table states */ - enum EditMode - { - ModeEditing, - ModeColumnResizing, - ModeRowResizing, - }; - EditMode mMode = ModeEditing; - - float mStartDragDim; - /// Depending on row/column sizer being dragged, this will be the y/x coordinate - float mStartDragMouseCoordinate; - - bool mDirty = false; - bool mFirstDraw = true; - -public: - TableTemplateUI(std::unique_ptr<TableTemplate> table) - : mTable{ std::move(table) } - , mSelectionTL{ -1, -1 } - , mSelectionBR{ -1, -1 } - { - // TODO debug code - Resize(6, 5); - } - - ~TableTemplateUI() override - { - // We can't move this to be a destructor of the union - // because that way it would run after the destruction of mTable - if (!IsSelected()) { - // Case: mIdleState - // Noop - } else if (mSelectionTL == mSelectionBR) { - switch (mTable->GetCell(mSelectionTL).Type) { - case TableCell::ConstantCell: - // Case: mCS - // Noop - break; - - case TableCell::SingularParametricCell: - // Case: mSS - mSS.EditBuffer.std::string::~string(); - break; - - case TableCell::ArrayParametricCell: - // Case: mAS - mAS.EditBuffer.std::string::~string(); - break; - } - } else { - // Case: mRS - // Noop - } - } - - void Display() override - { - ImGui::Columns(2); - if (mFirstDraw) { - mFirstDraw = false; - ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.15f); - } - - DisplayInspector(); - ImGui::NextColumn(); - - auto initialPos = ImGui::GetCursorPos(); - DisplayTable(); - DisplayTableResizers(initialPos); - ImGui::NextColumn(); - - ImGui::Columns(1); - } - - void Close() override - { - // TODO - } - - void Resize(int width, int height) - { - mTable->Resize(width, height); - mUICells.resize(width * height); - mUIArrayGroups.resize(mTable->GetArrayGroupCount()); - mRowSizers.resize(width); - mColSizers.resize(height); - - for (size_t i = 0; i < mUIArrayGroups.size(); ++i) { - auto& ag = mTable->GetArrayGroup(i); - auto& uag = mUIArrayGroups[i]; - - auto itemSpacing = ImGui::GetStyle().ItemSpacing; - uag.Pos.x = CalcTablePixelWidth() + itemSpacing.x; - uag.Pos.y = CalcTablePixelHeight() + itemSpacing.y; - - uag.Size.x = mTable->GetRowHeight(ag.Row); - uag.Size.y = 0; - for (int x = ag.LeftCell; x <= ag.RightCell; ++x) { - uag.Size.y += mTable->GetColumnWidth(x); - } - } - - mSelectionTL = { 0, 0 }; - mSelectionBR = { 0, 0 }; - } - -private: - void DisplayInspector() - { - bool openedDummy = true; - - // This is an id, no need to localize - if (ImGui::BeginTabBar("Inspector")) { - if (ImGui::BeginTabItem(I18N_TEXT("Cell", L10N_TABLE_CELL))) { - if (!IsSelected()) { - ImGui::Text(I18N_TEXT("Select a cell to edit", L10N_TABLE_CELL_SELECT_MSG)); - } else if (mSelectionTL == mSelectionBR) { - DisplayCellProperties(mSelectionTL); - } else { - DisplayRangeProperties(mSelectionTL, mSelectionBR); - } - ImGui::EndTabItem(); - } - - auto OpenPopup = [](const char* name) { - // Act as if ImGui::OpenPopup is executed in the previous id stack frame (tab bar level) - // Note: we can't simply use ImGui::GetItemID() here, because that would return the id of the ImGui::Button - auto tabBar = ImGui::GetCurrentContext()->CurrentTabBar; - auto id = tabBar->Tabs[tabBar->LastTabItemIdx].ID; - ImGui::PopID(); - ImGui::OpenPopup(name); - ImGui::PushOverrideID(id); - }; - if (ImGui::BeginTabItem(I18N_TEXT("Table", L10N_TABLE))) { - if (ImGui::Button(I18N_TEXT("Configure table properties...", L10N_TABLE_CONFIGURE_PROPERTIES))) { - mNewTableWidth = mTable->GetTableWidth(); - mNewTableHeight = mTable->GetTableHeight(); - OpenPopup(I18N_TEXT("Table properties", L10N_TABLE_PROPERTIES)); - } - - int mode = mMode; - ImGui::RadioButton(I18N_TEXT("Edit table", L10N_TABLE_EDIT_TABLE), &mode, ModeEditing); - ImGui::RadioButton(I18N_TEXT("Resize column widths", L10N_TABLE_EDIT_RESIZE_COLS), &mode, ModeColumnResizing); - ImGui::RadioButton(I18N_TEXT("Resize rows heights", L10N_TABLE_EDIT_RESIZE_ROWS), &mode, ModeRowResizing); - mMode = static_cast<EditMode>(mode); - - // Table contents - DisplayTableContents(); - - ImGui::EndTabItem(); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Table properties", L10N_TABLE_PROPERTIES), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - DisplayTableProperties(); - ImGui::EndPopup(); - } - - ImGui::EndTabBar(); - } - } - - static char NthUppercaseLetter(int n) - { - return (char)((int)'A' + n); - } - - static void ExcelRow(int row, char* bufferBegin, char* bufferEnd) - { - auto res = std::to_chars(bufferBegin, bufferEnd, row); - if (res.ec != std::errc()) { - return; - } - } - - static char* ExcelColumn(int column, char* bufferBegin, char* bufferEnd) - { - // https://stackoverflow.com/a/182924/11323702 - - int dividend = column; - int modulo; - - char* writeHead = bufferEnd - 1; - *writeHead = '\0'; - --writeHead; - - while (dividend > 0) { - if (writeHead < bufferBegin) { - return nullptr; - } - - modulo = (dividend - 1) % 26; - - *writeHead = NthUppercaseLetter(modulo); - --writeHead; - - dividend = (dividend - modulo) / 26; - } - - // `writeHead` at this point would be a one-past-the-bufferEnd reverse iterator (i.e. one-past-the-(text)beginning in the bufferBegin) - // add 1 to get to the actual beginning of the text - return writeHead + 1; - } - - void DisplayCellProperties(Vec2i pos) - { - auto& cell = mTable->GetCell(pos); - auto& uiCell = mUICells[pos.y * mTable->GetTableWidth() + pos.x]; - - char colStr[8]; // 2147483647 -> FXSHRXW, len == 7, along with \0 - char* colBegin = ExcelColumn(pos.x + 1, std::begin(colStr), std::end(colStr)); - char rowStr[11]; // len(2147483647) == 10, along with \0 - ExcelRow(pos.y + 1, std::begin(rowStr), std::end(rowStr)); - ImGui::Text(I18N_TEXT("Location: %s%s", L10N_TABLE_CELL_POS), colBegin, rowStr); - - switch (cell.Type) { - case TableCell::ConstantCell: - ImGui::Text(I18N_TEXT("Type: Constant", L10N_TABLE_CELL_TYPE_CONST)); - break; - case TableCell::SingularParametricCell: - ImGui::Text(I18N_TEXT("Type: Single parameter", L10N_TABLE_CELL_TYPE_PARAM)); - break; - case TableCell::ArrayParametricCell: - ImGui::Text(I18N_TEXT("Type: Array group", L10N_TABLE_CELL_TYPE_CREATE_AG)); - break; - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EDIT)) { - ImGui::OpenPopup("ConvertCtxMenu"); - } - if (ImGui::BeginPopup("ConvertCtxMenu")) { - bool constantEnabled = cell.Type != TableCell::ConstantCell; - if (ImGui::MenuItem(I18N_TEXT("Convert to regular cell", L10N_TABLE_CELL_CONV_CONST), nullptr, false, constantEnabled)) { - mTable->SetCellType(pos, TableCell::ConstantCell); - ResetCS(); - } - - bool singleEnabled = cell.Type != TableCell::SingularParametricCell; - if (ImGui::MenuItem(I18N_TEXT("Convert to parameter cell", L10N_TABLE_CELL_CONV_PARAM), nullptr, false, singleEnabled)) { - mTable->SetCellType(pos, TableCell::SingularParametricCell); - ResetSS(pos); - } - - bool arrayEnabled = cell.Type != TableCell::ArrayParametricCell; - if (ImGui::MenuItem(I18N_TEXT("Add to a new array group", L10N_TABLE_CELL_CONV_CREATE_AG), nullptr, false, arrayEnabled)) { - mTable->AddArrayGroup(pos.y, pos.x, pos.x); // Use automatically generated name - ResetAS(pos); - } - - bool leftEnabled = mSS.HasLeftAG && arrayEnabled; - if (ImGui::MenuItem(I18N_TEXT("Add to the array group to the left", L10N_TABLE_CELL_CONV_ADD_AG_LEFT), nullptr, false, leftEnabled)) { - auto& leftCell = mTable->GetCell({ pos.x - 1, pos.y }); - mTable->ExtendArrayGroupRight(leftCell.DataId, 1); - ResetAS(pos); - } - - bool rightEnabled = mSS.HasRightAG && arrayEnabled; - if (ImGui::MenuItem(I18N_TEXT("Add to the array group to the right", L10N_TABLE_CELL_CONV_ADD_AG_RIGHT), nullptr, false, rightEnabled)) { - auto& rightCell = mTable->GetCell({ pos.x + 1, pos.y }); - mTable->ExtendArrayGroupLeft(rightCell.DataId, 1); - ResetAS(pos); - } - - ImGui::EndPopup(); - } - - ImGui::Spacing(); - - constexpr auto kLeft = I18N_TEXT("Left", L10N_TABLE_CELL_ALIGN_LEFT); - constexpr auto kCenter = I18N_TEXT("Center", L10N_TABLE_CELL_ALIGN_CENTER); - constexpr auto kRight = I18N_TEXT("Right", L10N_TABLE_CELL_ALIGN_RIGHT); - - const char* horizontalText; - switch (cell.HorizontalAlignment) { - case TableCell::AlignAxisMin: horizontalText = kLeft; break; - case TableCell::AlignCenter: horizontalText = kCenter; break; - case TableCell::AlignAxisMax: horizontalText = kRight; break; - } - - if (ImGui::BeginCombo(I18N_TEXT("Horizontal alignment", L10N_TABLE_CELL_HORIZONTAL_ALIGNMENT), horizontalText)) { - if (ImGui::Selectable(kLeft, cell.HorizontalAlignment == TableCell::AlignAxisMin)) { - cell.HorizontalAlignment = TableCell::AlignAxisMin; - } - if (ImGui::Selectable(kCenter, cell.HorizontalAlignment == TableCell::AlignCenter)) { - cell.HorizontalAlignment = TableCell::AlignCenter; - } - if (ImGui::Selectable(kRight, cell.HorizontalAlignment == TableCell::AlignAxisMax)) { - cell.HorizontalAlignment = TableCell::AlignAxisMax; - } - ImGui::EndCombo(); - } - - constexpr auto kTop = I18N_TEXT("Left", L10N_TABLE_CELL_ALIGN_TOP); - constexpr auto kMiddle = I18N_TEXT("Middle", L10N_TABLE_CELL_ALIGN_MIDDLE); - constexpr auto kBottom = I18N_TEXT("Right", L10N_TABLE_CELL_ALIGN_BOTTOM); - - const char* verticalText; - switch (cell.VerticalAlignment) { - case TableCell::AlignAxisMin: verticalText = kTop; break; - case TableCell::AlignCenter: verticalText = kMiddle; break; - case TableCell::AlignAxisMax: verticalText = kBottom; break; - } - - if (ImGui::BeginCombo(I18N_TEXT("Vertical alignment", L10N_TABLE_CELL_VERTICAL_ALIGNMENT), verticalText)) { - if (ImGui::Selectable(kTop, cell.VerticalAlignment == TableCell::AlignAxisMin)) { - cell.VerticalAlignment = TableCell::AlignAxisMin; - } - if (ImGui::Selectable(kMiddle, cell.VerticalAlignment == TableCell::AlignCenter)) { - cell.VerticalAlignment = TableCell::AlignCenter; - } - if (ImGui::Selectable(kBottom, cell.VerticalAlignment == TableCell::AlignAxisMax)) { - cell.VerticalAlignment = TableCell::AlignAxisMax; - } - ImGui::EndCombo(); - } - - switch (cell.Type) { - case TableCell::ConstantCell: - ImGui::InputText(I18N_TEXT("Content", L10N_TABLE_CELL_CONTENT), &cell.Content); - break; - - case TableCell::SingularParametricCell: - if (ImGui::InputText(I18N_TEXT("Variable name", L10N_TABLE_CELL_VAR_NAME), &mSS.EditBuffer)) { - // Sync name change to table - bool success = mTable->UpdateParameterName(cell.Content, mSS.EditBuffer); - if (success) { - // Flush name to display content - cell.Content = mSS.EditBuffer; - mSS.ErrorDuplicateVarName = false; - } else { - mSS.ErrorDuplicateVarName = true; - } - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(I18N_TEXT("Name of the parameter link to this cell.", L10N_TABLE_CELL_VAR_TOOLTIP)); - } - - if (mSS.ErrorDuplicateVarName) { - ImGui::ErrorMessage(I18N_TEXT("Variable name duplicated.", L10N_TABLE_CELL_VAR_NAME_DUP)); - } - break; - - case TableCell::ArrayParametricCell: - if (ImGui::InputText(I18N_TEXT("Variable name", L10N_TABLE_CELL_VAR_NAME), &mAS.EditBuffer)) { - auto ag = mTable->GetArrayGroup(cell.DataId); - bool success = ag.UpdateCellName(cell.Content, mAS.EditBuffer); - if (success) { - cell.Content = mAS.EditBuffer; - mAS.ErrorDuplicateVarName = false; - } else { - mAS.ErrorDuplicateVarName = true; - } - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(I18N_TEXT("Name of the parameter link to this cell; local within the array group.", L10N_TABLE_CELL_ARRAY_VAR_TOOLTIP)); - } - - if (mAS.ErrorDuplicateVarName) { - ImGui::ErrorMessage(I18N_TEXT("Variable name duplicated.", L10N_TABLE_CELL_VAR_NAME_DUP)); - } - break; - } - } - - void DisplayRangeProperties(Vec2i tl, Vec2i br) - { - // TODO - } - - void DisplayTableContents() - { - if (ImGui::TreeNode(ICON_FA_BONG " " I18N_TEXT("Parameters", L10N_TABLE_SINGLE_PARAMS))) { - TableSingleParamsIter iter(*mTable); - while (iter.HasNext()) { - auto& cell = iter.Next(); - if (ImGui::Selectable(cell.Content.c_str())) { - SelectCell(cell.Location); - } - } - ImGui::TreePop(); - } - if (ImGui::TreeNode(ICON_FA_LIST " " I18N_TEXT("Array groups", L10N_TABLE_ARRAY_GROUPS))) { - TableArrayGroupsIter iter(*mTable); - // For each array group - while (iter.HasNext()) { - if (ImGui::TreeNode(iter.PeekNameCStr())) { - auto& ag = iter.Peek(); - // For each cell in the array group - for (int x = ag.LeftCell; x <= ag.RightCell; ++x) { - Vec2i pos{ x, ag.Row }; - auto& cell = mTable->GetCell(pos); - if (ImGui::Selectable(cell.Content.c_str())) { - SelectCell(pos); - } - } - ImGui::TreePop(); - } - iter.Next(); - } - ImGui::TreePop(); - } - } - - void DisplayTableProperties() - { - ImGui::InputInt(I18N_TEXT("Width", L10N_TABLE_WIDTH), &mNewTableWidth); - ImGui::InputInt(I18N_TEXT("Height", L10N_TABLE_HEIGHT), &mNewTableHeight); - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM))) { - ImGui::CloseCurrentPopup(); - Resize(mNewTableWidth, mNewTableHeight); - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - // TODO - } - - void DisplayTable() - { - struct CellPalette - { - ImU32 Regular; - ImU32 Hovered; - ImU32 Active; - - ImU32 GetColorFor(const UICell& cell) const - { - if (cell.Held) { - return Active; - } else if (cell.Hovered) { - return Hovered; - } else { - return Regular; - } - } - }; - - CellPalette constantPalette{ - .Regular = ImGui::GetColorU32(ImGuiCol_Button), - .Hovered = ImGui::GetColorU32(ImGuiCol_ButtonHovered), - .Active = ImGui::GetColorU32(ImGuiCol_ButtonActive), - }; - CellPalette paramPalette{ - .Regular = IM_COL32(0, 214, 4, 102), - .Hovered = IM_COL32(0, 214, 4, 255), - .Active = IM_COL32(0, 191, 2, 255), - }; - - // TODO array group color - - auto navHighlight = ImGui::GetColorU32(ImGuiCol_NavHighlight); - - int colCount = mTable->GetTableWidth(); - int rowCount = mTable->GetTableHeight(); - for (int rowIdx = 0; rowIdx < rowCount; ++rowIdx) { - int rowHeight = mTable->GetRowHeight(rowIdx); - - for (int colIdx = 0; colIdx < colCount; ++colIdx) { - int colWidth = mTable->GetColumnWidth(colIdx); - - int i = rowIdx * colCount + colIdx; - auto window = ImGui::GetCurrentWindow(); - auto id = window->GetID(i); - - Vec2i cellLoc{ colIdx, rowIdx }; - auto& cell = mTable->GetCell(cellLoc); - auto& uiCell = mUICells[i]; - - ImVec2 size(colWidth, rowHeight); - ImRect rect{ - window->DC.CursorPos, - window->DC.CursorPos + ImGui::CalcItemSize(size, 0.0f, 0.0f), - }; - - /* Draw cell selection */ - - if (uiCell.Selected) { - constexpr int mt = 2; // Marker Thickness - constexpr int ms = 8; // Marker Size - - ImVec2 outerTL(rect.Min - ImVec2(mt, mt)); - ImVec2 outerBR(rect.Max + ImVec2(mt, mt)); - - // Top left - window->DrawList->AddRectFilled(outerTL + ImVec2(0, 0), outerTL + ImVec2(ms, mt), navHighlight); - window->DrawList->AddRectFilled(outerTL + ImVec2(0, mt), outerTL + ImVec2(mt, ms), navHighlight); - - // Top right - ImVec2 outerTR(outerBR.x, outerTL.y); - window->DrawList->AddRectFilled(outerTR + ImVec2(-ms, 0), outerTR + ImVec2(0, mt), navHighlight); - window->DrawList->AddRectFilled(outerTR + ImVec2(-mt, mt), outerTR + ImVec2(0, ms), navHighlight); - - // Bottom right - window->DrawList->AddRectFilled(outerBR + ImVec2(-ms, -mt), outerBR + ImVec2(0, 0), navHighlight); - window->DrawList->AddRectFilled(outerBR + ImVec2(-mt, -ms), outerBR + ImVec2(0, -mt), navHighlight); - - // Bottom left - ImVec2 outerBL(outerTL.x, outerBR.y); - window->DrawList->AddRectFilled(outerBL + ImVec2(0, -mt), outerBL + ImVec2(ms, 0), navHighlight); - window->DrawList->AddRectFilled(outerBL + ImVec2(0, -ms), outerBL + ImVec2(mt, -mt), navHighlight); - } - - /* Draw cell body */ - - CellPalette* palette; - switch (cell.Type) { - case TableCell::ConstantCell: - palette = &constantPalette; - break; - - case TableCell::SingularParametricCell: - case TableCell::ArrayParametricCell: - palette = ¶mPalette; - break; - } - - window->DrawList->AddRectFilled(rect.Min, rect.Max, palette->GetColorFor(uiCell)); - - /* Draw cell content */ - - auto content = cell.Content.c_str(); - auto contentEnd = content + cell.Content.size(); - auto textSize = ImGui::CalcTextSize(content, contentEnd); - - ImVec2 textRenderPos; - switch (cell.HorizontalAlignment) { - case TableCell::AlignAxisMin: textRenderPos.x = rect.Min.x; break; - case TableCell::AlignCenter: textRenderPos.x = rect.Min.x + colWidth / 2 - textSize.x / 2; break; - case TableCell::AlignAxisMax: textRenderPos.x = rect.Max.x - textSize.x; break; - } - switch (cell.VerticalAlignment) { - case TableCell::AlignAxisMin: textRenderPos.y = rect.Min.y; break; - case TableCell::AlignCenter: textRenderPos.y = rect.Min.y + rowHeight / 2 - textSize.y / 2; break; - case TableCell::AlignAxisMax: textRenderPos.y = rect.Max.y - textSize.y; break; - } - window->DrawList->AddText(textRenderPos, IM_COL32(0, 0, 0, 255), content, contentEnd); - - /* Update ImGui cursor */ - - ImGui::ItemSize(size); - if (!ImGui::ItemAdd(rect, id)) { - goto logicEnd; - } - - if (mMode != ModeEditing) { - goto logicEnd; - } - if (ImGui::ButtonBehavior(rect, id, &uiCell.Hovered, &uiCell.Held)) { - if (ImGui::GetIO().KeyShift && IsSelected()) { - SelectRange(mSelectionTL, { colIdx, rowIdx }); - } else { - SelectCell({ colIdx, rowIdx }); - } - } - - logicEnd: - // Don't SameLine() if we are on the last cell in the row - if (colIdx != colCount - 1) { - ImGui::SameLine(); - } - } - } - - for (auto& uag : mUIArrayGroups) { - ImGui::GetCurrentWindow()->DrawList->AddRect( - uag.Pos - ImVec2(1, 1), - uag.Pos + uag.Size + ImVec2(1, 1), - kArrayGroupOutline); - } - } - - void DisplayResizers( - ImVec2 pos, - ImVec2 sizerDim, - ImVec2 sizerOffset, - std::type_identity_t<float ImVec2::*> vecCompGetter, - std::type_identity_t<int (TableTemplate::*)() const> lenGetter, - std::type_identity_t<int (TableTemplate::*)(int) const> dimGetter, - std::type_identity_t<void (TableTemplate::*)(int, int)> dimSetter) - { - auto window = ImGui::GetCurrentWindow(); - auto spacing = ImGui::GetStyle().ItemSpacing.*vecCompGetter; - - auto regularColor = ImGui::GetColorU32(ImGuiCol_Button); - auto hoveredColor = ImGui::GetColorU32(ImGuiCol_ButtonHovered); - auto activeColor = ImGui::GetColorU32(ImGuiCol_ButtonActive); - - auto GetColor = [&](const Sizer& sizer) -> ImU32 { - if (sizer.Held) { - return activeColor; - } else if (sizer.Hovered) { - return hoveredColor; - } else { - return regularColor; - } - }; - - for (int ix = 0; ix < (mTable.get()->*lenGetter)(); ++ix) { - // ImGui uses float for sizes, our table uses int (because excel uses int) - // Convert here to avoid mountains of narrowing warnings below - auto dim = (float)(mTable.get()->*dimGetter)(ix); - - pos.*vecCompGetter += dim; - ImRect rect{ - pos - sizerOffset, - pos - sizerOffset + ImGui::CalcItemSize(sizerDim, 0.0f, 0.0f), - }; - pos.*vecCompGetter += spacing; - - auto& sizer = mColSizers[ix]; - auto id = window->GetID(ix); - window->DrawList->AddRectFilled(rect.Min, rect.Max, GetColor(sizer)); - - if (ImGui::ButtonBehavior(rect, id, &sizer.Hovered, &sizer.Held, ImGuiButtonFlags_PressedOnClick)) { - mStartDragDim = dim; - mStartDragMouseCoordinate = ImGui::GetMousePos().*vecCompGetter; - } - if (sizer.Held) { - float change = ImGui::GetMousePos().*vecCompGetter - mStartDragMouseCoordinate; - float colWidth = std::max(mStartDragDim + change, 1.0f); - (mTable.get()->*dimSetter)(ix, (int)colWidth); - } - } - } - - void DisplayTableResizers(ImVec2 topLeftPixelPos) - { - constexpr float kExtraSideLength = 5.0f; - constexpr float kExtraAxialLength = 2.0f; - - switch (mMode) { - case ModeEditing: break; - - case ModeColumnResizing: - ImGui::PushID("Cols"); - DisplayResizers( - topLeftPixelPos, - ImVec2( - ImGui::GetStyle().ItemSpacing.x + kExtraSideLength * 2, - CalcTablePixelHeight() + kExtraAxialLength * 2), - ImVec2(kExtraSideLength, kExtraAxialLength), - &ImVec2::x, - &TableTemplate::GetTableWidth, - &TableTemplate::GetColumnWidth, - &TableTemplate::SetColumnWidth); - ImGui::PopID(); - break; - - case ModeRowResizing: - ImGui::PushID("Rows"); - DisplayResizers( - topLeftPixelPos, - ImVec2( - CalcTablePixelWidth() + kExtraAxialLength * 2, - ImGui::GetStyle().ItemSpacing.y + kExtraSideLength * 2), - ImVec2(kExtraAxialLength, kExtraSideLength), - &ImVec2::y, - &TableTemplate::GetTableHeight, - &TableTemplate::GetRowHeight, - &TableTemplate::SetRowHeight); - ImGui::PopID(); - break; - } - } - - float CalcTablePixelWidth() const - { - float horizontalSpacing = ImGui::GetStyle().ItemSpacing.x; - float width = 0; - for (int x = 0; x < mTable->GetTableWidth(); ++x) { - width += mTable->GetColumnWidth(x); - width += horizontalSpacing; - } - return width - horizontalSpacing; - } - - float CalcTablePixelHeight() const - { - float verticalSpacing = ImGui::GetStyle().ItemSpacing.y; - float height = 0; - for (int y = 0; y < mTable->GetTableHeight(); ++y) { - height += mTable->GetRowHeight(y); - height += verticalSpacing; - } - return height - verticalSpacing; - } - - template <class TFunction> - void ForeachSelectedCell(const TFunction& func) - { - for (int y = mSelectionTL.y; y <= mSelectionBR.y; ++y) { - for (int x = mSelectionTL.x; x <= mSelectionBR.x; ++x) { - int i = y * mTable->GetTableWidth() + x; - func(i, x, y); - } - } - } - - bool IsSelected() const - { - return mSelectionTL.x != -1; - } - - void ClearSelection() - { - if (IsSelected()) { - ForeachSelectedCell([this](int i, int, int) { - auto& uiCell = mUICells[i]; - uiCell.Selected = false; - }); - } - - mSelectionTL = { -1, -1 }; - mSelectionBR = { -1, -1 }; - - ResetIdleState(); - } - - void ResetIdleState() - { - mIdleState = {}; - } - - void SelectRange(Vec2i p1, Vec2i p2) - { - ClearSelection(); - - if (p2.x < p1.x) { - std::swap(p2.x, p1.x); - } - if (p2.y < p1.y) { - std::swap(p2.y, p1.y); - } - - mSelectionTL = p1; - mSelectionBR = p2; - - ForeachSelectedCell([this](int i, int, int) { - auto& uiCell = mUICells[i]; - uiCell.Selected = true; - }); - - ResetRS(); - } - - void ResetRS() - { - mRS = {}; - } - - void SelectCell(Vec2i pos) - { - ClearSelection(); - - mSelectionTL = pos; - mSelectionBR = pos; - - int i = pos.y * mTable->GetTableWidth() + pos.x; - mUICells[i].Selected = true; - - switch (mTable->GetCell(pos).Type) { - case TableCell::ConstantCell: ResetCS(); break; - case TableCell::SingularParametricCell: ResetSS(pos); break; - case TableCell::ArrayParametricCell: ResetAS(pos); break; - } - } - - void ResetCS() - { - mCS = {}; - } - - void ResetSS(Vec2i pos) - { - new (&mSS) SStates{ - .EditBuffer = mTable->GetCell(pos).Content, - .ErrorDuplicateVarName = false, - .HasLeftAG = pos.x > 1 && mTable->GetCell({ pos.x - 1, pos.y }).Type == TableCell::ArrayParametricCell, - .HasRightAG = pos.x < mTable->GetTableWidth() - 1 && mTable->GetCell({ pos.x + 1, pos.y }).Type == TableCell::ArrayParametricCell, - }; - } - - void ResetAS(Vec2i pos) - { - new (&mAS) AStates{ - .EditBuffer = mTable->GetCell(pos).Content, - .ErrorDuplicateVarName = false, - }; - } -}; - -template <class TTarget> -static auto CastTemplateAs(std::unique_ptr<Template>& input) requires std::is_base_of_v<Template, TTarget> -{ - return std::unique_ptr<TTarget>(static_cast<TTarget*>(input.release())); -} - -std::unique_ptr<TemplateUI> TemplateUI::CreateByKind(std::unique_ptr<Template> tmpl) -{ - switch (tmpl->GetKind()) { - case Template::KD_Table: return std::make_unique<TableTemplateUI>(CastTemplateAs<TableTemplate>(tmpl)); - case Template::InvalidKind: break; - } - return nullptr; -} - -std::unique_ptr<TemplateUI> TemplateUI::CreateByKind(Template::Kind kind) -{ - switch (kind) { - case Template::KD_Table: return std::make_unique<TableTemplateUI>(std::make_unique<TableTemplate>()); - case Template::InvalidKind: break; - } - return nullptr; -} -} // namespace CPLT_UNITY_ID - -void UI::TemplatesTab() -{ - auto& project = *GlobalStates::GetInstance().GetCurrentProject(); - - static std::unique_ptr<CPLT_UNITY_ID::TemplateUI> openTemplate; - static AssetList::ListState state; - bool openedDummy = true; - - // Toolbar item: close - if (ImGui::Button(ICON_FA_TIMES " " I18N_TEXT("Close", L10N_CLOSE), openTemplate == nullptr)) { - openTemplate->Close(); - openTemplate = nullptr; - } - - // Toolbar item: open... - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Open asset...", L10N_ASSET_OPEN))) { - ImGui::OpenPopup(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::Button(ICON_FA_FOLDER_OPEN " " I18N_TEXT("Open", L10N_OPEN), state.SelectedAsset == nullptr)) { - ImGui::CloseCurrentPopup(); - - auto tmpl = project.Templates.Load(*state.SelectedAsset); - openTemplate = CPLT_UNITY_ID::TemplateUI::CreateByKind(std::move(tmpl)); - } - ImGui::SameLine(); - project.Templates.DisplayControls(state); - project.Templates.DisplayDetailsList(state); - - ImGui::EndPopup(); - } - - // Toolbar item: manage... - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Manage assets...", L10N_ASSET_MANAGE))) { - ImGui::OpenPopup(I18N_TEXT("Manage assets", L10N_ASSET_MANAGE_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Manage assets", L10N_ASSET_MANAGE_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - project.Templates.DisplayControls(state); - project.Templates.DisplayDetailsList(state); - ImGui::EndPopup(); - } - - if (openTemplate) { - openTemplate->Display(); - } -} diff --git a/core/src/UI/UI_Utils.cpp b/core/src/UI/UI_Utils.cpp deleted file mode 100644 index a2bf692..0000000 --- a/core/src/UI/UI_Utils.cpp +++ /dev/null @@ -1,315 +0,0 @@ -#include "UI.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> - -#define IMGUI_DEFINE_MATH_OPERATORS -#include <imgui_internal.h> - -void ImGui::SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond cond) -{ - auto vs = ImGui::GetMainViewport()->Size; - ImGui::SetNextWindowSize({ vs.x * xPercent, vs.y * yPercent }, cond); -} - -void ImGui::SetNextWindowCentered(ImGuiCond cond) -{ - auto vs = ImGui::GetMainViewport()->Size; - ImGui::SetNextWindowPos({ vs.x / 2, vs.y / 2 }, cond, { 0.5f, 0.5f }); -} - -void ImGui::PushDisabled() -{ - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f * ImGui::GetStyle().Alpha); -} - -void ImGui::PopDisabled() -{ - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); -} - -bool ImGui::Button(const char* label, bool disabled) -{ - return Button(label, ImVec2{}, disabled); -} - -bool ImGui::Button(const char* label, const ImVec2& sizeArg, bool disabled) -{ - if (disabled) PushDisabled(); - bool res = ImGui::Button(label, sizeArg); - if (disabled) PopDisabled(); - - // Help clang-tidy's static analyzer: if the button is disabled, res should always be false - assert(!disabled || (disabled && !res)); - - return res; -} - -void ImGui::ErrorIcon() -{ - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 237 / 255.0f, 67 / 255.0f, 55 / 255.0f, 1.0f }); // #ED4337 - ImGui::Text(ICON_FA_EXCLAMATION_CIRCLE); - ImGui::PopStyleColor(); -} - -void ImGui::ErrorMessage(const char* fmt, ...) -{ - ErrorIcon(); - SameLine(); - - va_list args; - va_start(args, fmt); - TextV(fmt, args); - va_end(args); -} - -void ImGui::WarningIcon() -{ - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 255 / 255.0f, 184 / 255.0f, 24 / 255.0f, 1.0f }); // #FFB818 - ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE); - ImGui::PopStyleColor(); -} - -void ImGui::WarningMessage(const char* fmt, ...) -{ - WarningIcon(); - SameLine(); - - va_list args; - va_start(args, fmt); - TextV(fmt, args); - va_end(args); -} - -void ImGui::DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor) -{ - // Taken from https://github.com/thedmd/imgui-node-editor/blob/master/examples/blueprints-example/utilities/drawing.cpp - // ax::NodeEditor::DrawIcon - - // Brace style was adapted but no names are changed - - auto rect = ImRect(a, b); - auto rect_x = rect.Min.x; - auto rect_y = rect.Min.y; - auto rect_w = rect.Max.x - rect.Min.x; - auto rect_h = rect.Max.y - rect.Min.y; - auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f; - auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f; - auto rect_center = ImVec2(rect_center_x, rect_center_y); - const auto outline_scale = rect_w / 24.0f; - const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle - - if (type == IconType::Flow) { - const auto origin_scale = rect_w / 24.0f; - - const auto offset_x = 1.0f * origin_scale; - const auto offset_y = 0.0f * origin_scale; - const auto margin = 2.0f * origin_scale; - const auto rounding = 0.1f * origin_scale; - const auto tip_round = 0.7f; // percentage of triangle edge (for tip) - //const auto edge_round = 0.7f; // percentage of triangle edge (for corner) - const auto canvas = ImRect( - rect.Min.x + margin + offset_x, - rect.Min.y + margin + offset_y, - rect.Max.x - margin + offset_x, - rect.Max.y - margin + offset_y); - const auto canvas_x = canvas.Min.x; - const auto canvas_y = canvas.Min.y; - const auto canvas_w = canvas.Max.x - canvas.Min.x; - const auto canvas_h = canvas.Max.y - canvas.Min.y; - - const auto left = canvas_x + canvas_w * 0.5f * 0.3f; - const auto right = canvas_x + canvas_w - canvas_w * 0.5f * 0.3f; - const auto top = canvas_y + canvas_h * 0.5f * 0.2f; - const auto bottom = canvas_y + canvas_h - canvas_h * 0.5f * 0.2f; - const auto center_y = (top + bottom) * 0.5f; - //const auto angle = AX_PI * 0.5f * 0.5f * 0.5f; - - const auto tip_top = ImVec2(canvas_x + canvas_w * 0.5f, top); - const auto tip_right = ImVec2(right, center_y); - const auto tip_bottom = ImVec2(canvas_x + canvas_w * 0.5f, bottom); - - drawList->PathLineTo(ImVec2(left, top) + ImVec2(0, rounding)); - drawList->PathBezierCurveTo( - ImVec2(left, top), - ImVec2(left, top), - ImVec2(left, top) + ImVec2(rounding, 0)); - drawList->PathLineTo(tip_top); - drawList->PathLineTo(tip_top + (tip_right - tip_top) * tip_round); - drawList->PathBezierCurveTo( - tip_right, - tip_right, - tip_bottom + (tip_right - tip_bottom) * tip_round); - drawList->PathLineTo(tip_bottom); - drawList->PathLineTo(ImVec2(left, bottom) + ImVec2(rounding, 0)); - drawList->PathBezierCurveTo( - ImVec2(left, bottom), - ImVec2(left, bottom), - ImVec2(left, bottom) - ImVec2(0, rounding)); - - if (!filled) { - if (innerColor & 0xFF000000) { - drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor); - } - - drawList->PathStroke(color, true, 2.0f * outline_scale); - } else { - drawList->PathFillConvex(color); - } - } else { - auto triangleStart = rect_center_x + 0.32f * rect_w; - - auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f); - - rect.Min.x += rect_offset; - rect.Max.x += rect_offset; - rect_x += rect_offset; - rect_center_x += rect_offset * 0.5f; - rect_center.x += rect_offset * 0.5f; - - if (type == IconType::Circle) { - const auto c = rect_center; - - if (!filled) { - const auto r = 0.5f * rect_w / 2.0f - 0.5f; - - if (innerColor & 0xFF000000) - drawList->AddCircleFilled(c, r, innerColor, 12 + extra_segments); - drawList->AddCircle(c, r, color, 12 + extra_segments, 2.0f * outline_scale); - } else { - drawList->AddCircleFilled(c, 0.5f * rect_w / 2.0f, color, 12 + extra_segments); - } - } - - if (type == IconType::Square) { - if (filled) { - const auto r = 0.5f * rect_w / 2.0f; - const auto p0 = rect_center - ImVec2(r, r); - const auto p1 = rect_center + ImVec2(r, r); - - drawList->AddRectFilled(p0, p1, color, 0, 15 + extra_segments); - } else { - const auto r = 0.5f * rect_w / 2.0f - 0.5f; - const auto p0 = rect_center - ImVec2(r, r); - const auto p1 = rect_center + ImVec2(r, r); - - if (innerColor & 0xFF000000) - drawList->AddRectFilled(p0, p1, innerColor, 0, 15 + extra_segments); - - drawList->AddRect(p0, p1, color, 0, 15 + extra_segments, 2.0f * outline_scale); - } - } - - if (type == IconType::Grid) { - const auto r = 0.5f * rect_w / 2.0f; - const auto w = ceilf(r / 3.0f); - - const auto baseTl = ImVec2(floorf(rect_center_x - w * 2.5f), floorf(rect_center_y - w * 2.5f)); - const auto baseBr = ImVec2(floorf(baseTl.x + w), floorf(baseTl.y + w)); - - auto tl = baseTl; - auto br = baseBr; - for (int i = 0; i < 3; ++i) { - tl.x = baseTl.x; - br.x = baseBr.x; - drawList->AddRectFilled(tl, br, color); - tl.x += w * 2; - br.x += w * 2; - if (i != 1 || filled) - drawList->AddRectFilled(tl, br, color); - tl.x += w * 2; - br.x += w * 2; - drawList->AddRectFilled(tl, br, color); - - tl.y += w * 2; - br.y += w * 2; - } - - triangleStart = br.x + w + 1.0f / 24.0f * rect_w; - } - - if (type == IconType::RoundSquare) { - if (filled) { - const auto r = 0.5f * rect_w / 2.0f; - const auto cr = r * 0.5f; - const auto p0 = rect_center - ImVec2(r, r); - const auto p1 = rect_center + ImVec2(r, r); - - drawList->AddRectFilled(p0, p1, color, cr, 15); - } else { - const auto r = 0.5f * rect_w / 2.0f - 0.5f; - const auto cr = r * 0.5f; - const auto p0 = rect_center - ImVec2(r, r); - const auto p1 = rect_center + ImVec2(r, r); - - if (innerColor & 0xFF000000) - drawList->AddRectFilled(p0, p1, innerColor, cr, 15); - - drawList->AddRect(p0, p1, color, cr, 15, 2.0f * outline_scale); - } - } else if (type == IconType::Diamond) { - if (filled) { - const auto r = 0.607f * rect_w / 2.0f; - const auto c = rect_center; - - drawList->PathLineTo(c + ImVec2(0, -r)); - drawList->PathLineTo(c + ImVec2(r, 0)); - drawList->PathLineTo(c + ImVec2(0, r)); - drawList->PathLineTo(c + ImVec2(-r, 0)); - drawList->PathFillConvex(color); - } else { - const auto r = 0.607f * rect_w / 2.0f - 0.5f; - const auto c = rect_center; - - drawList->PathLineTo(c + ImVec2(0, -r)); - drawList->PathLineTo(c + ImVec2(r, 0)); - drawList->PathLineTo(c + ImVec2(0, r)); - drawList->PathLineTo(c + ImVec2(-r, 0)); - - if (innerColor & 0xFF000000) - drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor); - - drawList->PathStroke(color, true, 2.0f * outline_scale); - } - } else { - const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f); - - drawList->AddTriangleFilled( - ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f), - ImVec2(triangleStart, rect_center_y + 0.15f * rect_h), - ImVec2(triangleStart, rect_center_y - 0.15f * rect_h), - color); - } - } -} - -void ImGui::Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color, const ImVec4& innerColor) -{ - // Taken from https://github.com/thedmd/imgui-node-editor/blob/master/examples/blueprints-example/utilities/widgets.cpp - // ax::NodeEditor::Icon - - if (ImGui::IsRectVisible(size)) - { - auto cursorPos = ImGui::GetCursorScreenPos(); - auto drawList = ImGui::GetWindowDrawList(); - DrawIcon(drawList, cursorPos, cursorPos + size, type, filled, ImColor(color), ImColor(innerColor)); - } - - ImGui::Dummy(size); -} - -bool ImGui::Splitter(bool splitVertically, float thickness, float* size1, float* size2, float minSize1, float minSize2, float splitterLongAxisSize) -{ - // Taken from https://github.com/thedmd/imgui-node-editor/blob/master/examples/blueprints-example/blueprints-example.cpp - // ::Splitter - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID("##Splitter"); - ImRect bb; - bb.Min = window->DC.CursorPos + (splitVertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1)); - bb.Max = bb.Min + CalcItemSize(splitVertically ? ImVec2(thickness, splitterLongAxisSize) : ImVec2(splitterLongAxisSize, thickness), 0.0f, 0.0f); - return SplitterBehavior(bb, id, splitVertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, minSize1, minSize2, 0.0f); -} diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp deleted file mode 100644 index c85850a..0000000 --- a/core/src/UI/UI_Workflows.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Model/Workflow/Nodes/DocumentNodes.hpp" -#include "Model/Workflow/Nodes/NumericNodes.hpp" -#include "Model/Workflow/Nodes/TextNodes.hpp" -#include "Model/Workflow/Nodes/UserInputNodes.hpp" -#include "Model/Workflow/Workflow.hpp" -#include "Utils/I18n.hpp" -#include "Utils/Macros.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <imgui_node_editor.h> -#include <imgui_stdlib.h> -#include <memory> -#include <span> -#include <vector> - -namespace ImNodes = ax::NodeEditor; - -namespace CPLT_UNITY_ID { -class WorkflowUI -{ -private: - std::unique_ptr<Workflow> mWorkflow; - - ImNodes::EditorContext* mContext; - - ImNodes::NodeId mContextMenuNodeId = 0; - ImNodes::PinId mContextMenuPinId = 0; - ImNodes::LinkId mContextMenuLinkId = 0; - -public: - WorkflowUI(std::unique_ptr<Workflow> workflow) - : mWorkflow{ std::move(workflow) } - { - mContext = ImNodes::CreateEditor(); - } - - ~WorkflowUI() - { - ImNodes::DestroyEditor(mContext); - } - - void Display() - { - ImNodes::SetCurrentEditor(mContext); - ImNodes::Begin(""); - - // Defer creation of tooltip because within the node editor, cursor positioning is going to be off - const char* tooltipMessage = nullptr; - - for (auto& node : mWorkflow->GetNodes()) { - if (!node) continue; - - ImNodes::BeginNode(node->GetId()); - node->Draw(); - ImNodes::EndNode(); - } - - for (auto& conn : mWorkflow->GetConnections()) { - if (!conn.IsValid()) continue; - - auto srcId = mWorkflow->GetNodes()[conn.SourceNode]->GetOutputPinUniqueId(conn.SourcePin); - auto dstId = mWorkflow->GetNodes()[conn.DestinationNode]->GetInputPinUniqueId(conn.DestinationPin); - ImNodes::Link(conn.GetLinkId(), srcId, dstId); - } - - if (ImNodes::BeginCreate()) { - ImNodes::PinId src = 0, dst = 0; - if (ImNodes::QueryNewLink(&src, &dst)) { - if (!src || !dst) { - goto createError; - } - - auto [srcNode, srcPinId, srcIsOutput] = mWorkflow->DisassembleGlobalPinId(src); - auto [dstNode, dstPinId, dstIsOutput] = mWorkflow->DisassembleGlobalPinId(dst); - - if (srcNode == dstNode) { - ImNodes::RejectNewItem(); - goto createError; - } - - if (srcIsOutput == dstIsOutput) { - ImNodes::RejectNewItem(); - goto createError; - } - - auto srcPin = srcNode->GetOutputPin(srcPinId); - auto dstPin = dstNode->GetOutputPin(dstPinId); - - if (srcPin.MatchingType != dstPin.MatchingType) { - ImNodes::RejectNewItem(); - goto createError; - } - - if (ImNodes::AcceptNewItem()) { - mWorkflow->Connect(*srcNode, srcPinId, *dstNode, dstPinId); - } - } - - ImNodes::PinId newNodePin = 0; - if (ImNodes::QueryNewNode(&newNodePin)) { - auto [node, pinId, isOutput] = mWorkflow->DisassembleGlobalPinId(newNodePin); - - if ((isOutput && node->GetOutputPin(pinId).IsConnected()) || - (!isOutput && node->GetInputPin(pinId).IsConnected())) - { - ImNodes::RejectNewItem(); - goto createError; - } - - if (ImNodes::AcceptNewItem()) { - ImNodes::Suspend(); - ImGui::BeginPopup("CreateNodeCtxMenu"); - ImNodes::Resume(); - } - } - } - createError: - ImNodes::EndCreate(); - - if (ImNodes::BeginDelete()) { - ImNodes::LinkId deletedLinkId; - if (ImNodes::QueryDeletedLink(&deletedLinkId)) { - auto& conn = *mWorkflow->GetConnectionByLinkId(deletedLinkId); - mWorkflow->RemoveConnection(conn.Id); - } - - ImNodes::NodeId deletedNodeId; - if (ImNodes::QueryDeletedNode(&deletedNodeId)) { - auto node = mWorkflow->GetNodeByNodeId(deletedNodeId); - if (!node) { - ImNodes::RejectDeletedItem(); - goto deleteError; - } - - if (node->IsLocked()) { - ImNodes::RejectDeletedItem(); - goto deleteError; - } - } - } - deleteError: - ImNodes::EndDelete(); - - // Popups - ImNodes::Suspend(); - if (ImNodes::ShowNodeContextMenu(&mContextMenuNodeId)) { - ImGui::OpenPopup("NodeCtxMenu"); - } else if (ImNodes::ShowPinContextMenu(&mContextMenuPinId)) { - ImGui::OpenPopup("PinCtxMenu"); - } else if (ImNodes::ShowLinkContextMenu(&mContextMenuLinkId)) { - ImGui::OpenPopup("LinkCtxMenu"); - } - - if (ImGui::BeginPopup("NodeCtxMenu")) { - auto& node = *mWorkflow->GetNodeByNodeId(mContextMenuNodeId); - node.DrawDebugInfo(); - - if (ImGui::MenuItem(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE))) { - ImNodes::DeleteNode(mContextMenuNodeId); - } - - ImGui::EndPopup(); - } - - if (ImGui::BeginPopup("PinCtxMenu")) { - auto [node, pinId, isOutput] = mWorkflow->DisassembleGlobalPinId(mContextMenuPinId); - if (isOutput) { - node->DrawOutputPinDebugInfo(pinId); - } else { - node->DrawInputPinDebugInfo(pinId); - } - - if (ImGui::MenuItem(ICON_FA_UNLINK " " I18N_TEXT("Disconnect", L10N_DISCONNECT))) { - if (isOutput) { - auto& pin = node->GetOutputPin(pinId); - if (pin.IsConnected()) { - auto linkId = mWorkflow->GetConnectionById(pin.Connection)->GetLinkId(); - ImNodes::DeleteLink(linkId); - } - } else { - auto& pin = node->GetInputPin(pinId); - if (pin.IsConstantConnection()) { - // TODO - } else if (pin.IsConnected()) { - auto linkId = mWorkflow->GetConnectionById(pin.Connection)->GetLinkId(); - ImNodes::DeleteLink(linkId); - } - } - } - - ImGui::EndPopup(); - } - - if (ImGui::BeginPopup("LinkCtxMenu")) { - auto& conn = *mWorkflow->GetConnectionByLinkId(mContextMenuLinkId); - conn.DrawDebugInfo(); - - if (ImGui::MenuItem(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE))) { - ImNodes::DeleteLink(mContextMenuLinkId); - } - - ImGui::EndPopup(); - } - - if (ImGui::BeginPopup("CreateNodeCtxMenu")) { - for (int i = WorkflowNode::CG_Numeric; i < WorkflowNode::InvalidCategory; ++i) { - auto category = (WorkflowNode::Category)i; - auto members = WorkflowNode::QueryCategoryMembers(category); - - if (ImGui::BeginMenu(WorkflowNode::FormatCategory(category))) { - for (auto member : members) { - if (ImGui::MenuItem(WorkflowNode::FormatKind(member))) { - // Create node - auto uptr = WorkflowNode::CreateByKind(member); - mWorkflow->AddNode(std::move(uptr)); - } - } - ImGui::EndMenu(); - } - } - ImGui::EndPopup(); - } - - if (tooltipMessage) { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(tooltipMessage); - ImGui::EndTooltip(); - } - ImNodes::Resume(); - - ImNodes::End(); - } - - void Close() - { - // TODO - } -}; -} // namespace CPLT_UNITY_ID - -void UI::WorkflowsTab() -{ - auto& project = *GlobalStates::GetInstance().GetCurrentProject(); - - static std::unique_ptr<CPLT_UNITY_ID::WorkflowUI> openWorkflow; - static AssetList::ListState state; - bool openedDummy = true; - - // Toolbar item: close - if (ImGui::Button(ICON_FA_TIMES " " I18N_TEXT("Close", L10N_CLOSE), openWorkflow == nullptr)) { - openWorkflow->Close(); - openWorkflow = nullptr; - } - - // Toolbar item: open... - ImGui::SameLine(); - if (ImGui::Button((I18N_TEXT("Open asset...", L10N_ASSET_OPEN)))) { - ImGui::OpenPopup(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::Button(ICON_FA_FOLDER_OPEN " " I18N_TEXT("Open", L10N_OPEN), state.SelectedAsset == nullptr)) { - ImGui::CloseCurrentPopup(); - - auto workflow = project.Workflows.Load(*state.SelectedAsset); - openWorkflow = std::make_unique<CPLT_UNITY_ID::WorkflowUI>(std::move(workflow)); - } - ImGui::SameLine(); - project.Workflows.DisplayControls(state); - project.Workflows.DisplayDetailsList(state); - - ImGui::EndPopup(); - } - - // Toolbar item: manage... - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Manage assets...", L10N_ASSET_MANAGE))) { - ImGui::OpenPopup(I18N_TEXT("Manage assets", L10N_ASSET_MANAGE_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Manage assets", L10N_ASSET_MANAGE_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - project.Workflows.DisplayControls(state); - project.Workflows.DisplayDetailsList(state); - ImGui::EndPopup(); - } - - if (openWorkflow) { - openWorkflow->Display(); - } -} diff --git a/core/src/UI/fwd.hpp b/core/src/UI/fwd.hpp deleted file mode 100644 index 756e567..0000000 --- a/core/src/UI/fwd.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -// UI.hpp -namespace ImGui { -enum class IconType; -} diff --git a/core/src/Utils/Color.hpp b/core/src/Utils/Color.hpp deleted file mode 100644 index 19227e0..0000000 --- a/core/src/Utils/Color.hpp +++ /dev/null @@ -1,202 +0,0 @@ -#pragma once - -#include "Utils/Math.hpp" -#include "Utils/Vector.hpp" -#include "Utils/fwd.hpp" - -#include <imgui.h> -#include <algorithm> -#include <cstdint> -#include <limits> - -class RgbaColor -{ -public: - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - -public: - constexpr RgbaColor() noexcept - : r{ 255 } - , g{ 255 } - , b{ 255 } - , a{ 255 } - { - } - - constexpr RgbaColor(float r, float g, float b, float a = 1.0f) noexcept - : r{ static_cast<uint8_t>(r * 255.0f) } - , g{ static_cast<uint8_t>(g * 255.0f) } - , b{ static_cast<uint8_t>(b * 255.0f) } - , a{ static_cast<uint8_t>(a * 255.0f) } - { - } - - constexpr RgbaColor(int r, int g, int b, int a = 255) noexcept - : r{ static_cast<uint8_t>(r & 0xFF) } - , g{ static_cast<uint8_t>(g & 0xFF) } - , b{ static_cast<uint8_t>(b & 0xFF) } - , a{ static_cast<uint8_t>(a & 0xFF) } - { - } - - constexpr RgbaColor(uint32_t rgba) noexcept - : r{ static_cast<uint8_t>((rgba >> 0) & 0xFF) } - , g{ static_cast<uint8_t>((rgba >> 8) & 0xFF) } - , b{ static_cast<uint8_t>((rgba >> 16) & 0xFF) } - , a{ static_cast<uint8_t>((rgba >> 24) & 0xFF) } - { - } - - constexpr uint32_t GetScalar() const noexcept - { - uint32_t res = 0; - res |= r << 24; - res |= g << 16; - res |= b << 8; - res |= a; - return res; - } - - constexpr void SetScalar(uint32_t scalar) noexcept - { - r = (scalar >> 0) & 0xFF; - g = (scalar >> 8) & 0xFF; - b = (scalar >> 16) & 0xFF; - a = (scalar >> 24) & 0xFF; - } - - constexpr float GetNormalizedRed() const noexcept - { - return r / 255.0f; - } - - constexpr float GetNormalizedGreen() const noexcept - { - return g / 255.0f; - } - - constexpr float GetNormalizedBlue() const noexcept - { - return b / 255.0f; - } - - constexpr float GetNormalizedAlpha() const noexcept - { - return a / 255.0f; - } - - constexpr Vec4i AsVec4i() const noexcept - { - return Vec4i{ r, g, b, a }; - } - - constexpr Vec4f AsVec4f() const noexcept - { - return Vec4f{ - GetNormalizedRed(), - GetNormalizedGreen(), - GetNormalizedBlue(), - GetNormalizedAlpha(), - }; - } - - ImVec4 AsImVec() const - { - auto v = AsVec4f(); - return ImVec4{ v.x, v.y, v.z, v.w }; - } - - ImColor AsImColor() const - { - auto v = AsVec4f(); - return ImColor{ v.x, v.y, v.z, v.w }; - } - - ImU32 AsImU32() const - { - ImU32 res; - res |= r << IM_COL32_R_SHIFT; - res |= g << IM_COL32_G_SHIFT; - res |= b << IM_COL32_B_SHIFT; - res |= a << IM_COL32_A_SHIFT; - return res; - } - - constexpr void SetVec(const Vec4f& vec) noexcept - { - r = (uint8_t)(vec.x * 255.0f); - g = (uint8_t)(vec.y * 255.0f); - b = (uint8_t)(vec.z * 255.0f); - a = (uint8_t)(vec.w * 255.0f); - } - - // Forward declaring because cyclic reference between RgbaColor and HsvColor - constexpr HsvColor ToHsv() const noexcept; - - friend constexpr bool operator==(const RgbaColor&, const RgbaColor&) noexcept = default; -}; - -class HsvColor -{ -public: - float h; - float s; - float v; - float a; - -public: - constexpr HsvColor() noexcept - : h{ 0.0f } - , s{ 0.0f } - , v{ 1.0f } - , a{ 1.0f } - { - } - - constexpr HsvColor(float h, float s, float v, float a) noexcept - : h{ h } - , s{ s } - , v{ v } - , a{ a } - { - } - - // Forward declaring because cyclic reference between RgbaColor and HsvColor - constexpr RgbaColor ToRgba() const noexcept; -}; - -constexpr HsvColor RgbaColor::ToHsv() const noexcept -{ - float fr = GetNormalizedRed(); - float fg = GetNormalizedBlue(); - float fb = GetNormalizedGreen(); - float fa = GetNormalizedAlpha(); - - auto p = fg < fb ? Vec4f{ fb, fg, -1, 2.0f / 3.0f } : Vec4f{ fg, fb, 0, -1.0f / 3.0f }; - auto q = fr < p.x ? Vec4f{ p.x, p.y, p.w, fr } : Vec4f{ fr, p.y, p.z, p.x }; - float c = q.x - std::min(q.w, q.y); - float h = MathUtils::Abs((q.w - q.y) / (6 * c + std::numeric_limits<float>::epsilon()) + q.z); - - Vec3f hcv{ h, c, q.x }; - float s = hcv.y / (hcv.z + std::numeric_limits<float>::epsilon()); - return HsvColor(hcv.x, s, hcv.z, fa); -} - -constexpr RgbaColor HsvColor::ToRgba() const noexcept -{ - float r = MathUtils::Abs(h * 6 - 3) - 1; - float g = 2 - MathUtils::Abs(h * 6 - 2); - float b = 2 - MathUtils::Abs(h * 6 - 4); - - auto rgb = Vec3f{ - std::clamp(r, 0.0f, 1.0f), - std::clamp(g, 0.0f, 1.0f), - std::clamp(b, 0.0f, 1.0f), - }; - auto vc = (rgb - Vec3f{ 0, 0, 0 }) * s + Vec3f{ 1, 1, 1 } * v; - - return RgbaColor(vc.x, vc.y, vc.z, a); -} diff --git a/core/src/Utils/Hash.hpp b/core/src/Utils/Hash.hpp deleted file mode 100644 index cf7713a..0000000 --- a/core/src/Utils/Hash.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include <cstddef> -#include <functional> - -namespace HashUtils { - -template <class T> -void Combine(size_t& seed, const T& v) -{ - std::hash<T> hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); -} - -} // namespace HashUtils diff --git a/core/src/Utils/I18n.hpp b/core/src/Utils/I18n.hpp deleted file mode 100644 index e9eaac9..0000000 --- a/core/src/Utils/I18n.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "Utils/Macros.hpp" - -#if !defined(TARGET_LOCALE) -# define I18N_TEXT(defaultText, name) defaultText -#else -# include TARGET_LOCALE_FILE -# define I18N_TEXT(defaultText, name) name -#endif diff --git a/core/src/Utils/IO/Archive.cpp b/core/src/Utils/IO/Archive.cpp deleted file mode 100644 index f6e7b27..0000000 --- a/core/src/Utils/IO/Archive.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "Archive.hpp" - -constexpr uint8_t kMagicNumbers[] = { 0x98, 0xd8, 0xa4, 0x65, 0x18, 0xa2, 0xd6, 0xa0 }; -constexpr size_t kMagicNumberCount = std::size(kMagicNumbers); - -constexpr uint8_t kByteOrderMark = []() { - switch (std::endian::native) { - case std::endian::little: return 0; - case std::endian::big: return 1; - } -}(); - -std::span<const uint8_t, 8> DataArchive::GetMagicNumbers() -{ - return std::span<const uint8_t, 8>{ kMagicNumbers }; -} - -std::optional<InputDataStream> DataArchive::LoadFile(const std::filesystem::path& path) -{ - InputFileStream fileStream(path); - fileStream.SetReadInSize(1024); - InputDataStream stream(std::move(fileStream)); - - uint8_t magicNumbers[kMagicNumberCount]; - stream.ReadBytes(kMagicNumberCount, magicNumbers); - - for (size_t i = 0; i < kMagicNumberCount; ++i) { - if (magicNumbers[i] != kMagicNumbers[i]) { - return {}; - } - } - - uint8_t byteOrderMark; - stream.Read(byteOrderMark); - - switch (byteOrderMark) { - case 0: stream.SetEndianness(std::endian::little); break; - case 1: stream.SetEndianness(std::endian::big); break; - default: std::abort(); - } - - return stream; -} - -std::optional<OutputDataStream> DataArchive::SaveFile(const std::filesystem::path& path) -{ - OutputFileStream fileStream(path, OutputFileStream::TruncateFile); - fileStream.SetMaxBufferSize(1024); - OutputDataStream stream(std::move(fileStream)); - - stream.WriteBytes(kMagicNumberCount, kMagicNumbers); - stream.Write(kByteOrderMark); - - stream.SetEndianness(std::endian::native); - - return stream; -} diff --git a/core/src/Utils/IO/Archive.hpp b/core/src/Utils/IO/Archive.hpp deleted file mode 100644 index 1bb8b59..0000000 --- a/core/src/Utils/IO/Archive.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" - -#include <cstdint> -#include <filesystem> -#include <optional> -#include <span> - -class DataArchive -{ -public: - static std::span<const uint8_t, 8> GetMagicNumbers(); - - // TODO more complete impl - static std::optional<InputDataStream> LoadFile(const std::filesystem::path& path); - static std::optional<OutputDataStream> SaveFile(const std::filesystem::path& path); -}; diff --git a/core/src/Utils/IO/CstdioFile.cpp b/core/src/Utils/IO/CstdioFile.cpp deleted file mode 100644 index 0f6378a..0000000 --- a/core/src/Utils/IO/CstdioFile.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "CstdioFile.hpp" - -#include "Utils/Macros.hpp" - -#pragma push_macro("MODE_STRING") -#undef MODE_STRING - -#if defined(_WIN32) -# define MODE_STRING(x) L##x -#else -# define MODE_STRING(x) x -#endif - -namespace CPLT_UNITY_ID { -auto GetModeString(FileUtils::IoMode mode) -{ - switch (mode) { - case FileUtils::IM_Read: return MODE_STRING("rb"); - case FileUtils::IM_WriteAppend: return MODE_STRING("ab"); - case FileUtils::IM_WriteTruncate: return MODE_STRING("wb"); - } - return MODE_STRING(""); -} -} // namespace CPLT_UNITY_ID - -#pragma pop_macro("MODE_STRING") - -FILE* FileUtils::OpenCstdioFile(const std::filesystem::path& path, IoMode mode) -{ -#ifdef _WIN32 - // std::filesystem::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively - return _wfopen(path.c_str(), ::GetModeString(mode)); -#else - return fopen(path.c_str(), CPLT_UNITY_ID::GetModeString(mode)); -#endif -} diff --git a/core/src/Utils/IO/CstdioFile.hpp b/core/src/Utils/IO/CstdioFile.hpp deleted file mode 100644 index e863dd5..0000000 --- a/core/src/Utils/IO/CstdioFile.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include <cstdio> -#include <filesystem> - -namespace FileUtils { - -enum IoMode -{ - IM_Read, - IM_WriteAppend, - IM_WriteTruncate, -}; - -FILE* OpenCstdioFile(const std::filesystem::path& path, IoMode mode); - -} // namespace FileUtils diff --git a/core/src/Utils/IO/DataStream.cpp b/core/src/Utils/IO/DataStream.cpp deleted file mode 100644 index c0797e3..0000000 --- a/core/src/Utils/IO/DataStream.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "DataStream.hpp" - -#include <bit> -#include <limits> -#include <utility> - -static_assert(std::numeric_limits<float>::is_iec559, "Non IEE754/IEC559 'float' is not supported."); -static_assert(std::numeric_limits<double>::is_iec559, "Non IEE754/IEC559 'double' is not supported."); - -static_assert(std::endian::native == std::endian::little || std::endian::native == std::endian::big, "Mixed endian is not supported."); - -// Reading/writing signed integer byte-by-byte is fine, since the representation got fixed to 2's complements in C++20 - -static uint16_t ByteSwap(uint16_t n) -{ - auto bytes = reinterpret_cast<std::byte*>(n); - std::swap(bytes[0], bytes[1]); - return n; -} - -static uint32_t ByteSwap(uint32_t n) -{ -#ifdef _MSC_VER - return _byteswap_ulong(n); -#else - return __builtin_bswap32(n); -#endif -} - -static uint64_t ByteSwap(uint64_t n) -{ -#ifdef _MSC_VER - return _byteswap_uint64(n); -#else - return __builtin_bswap64(n); -#endif -} - -template <class TSigned> -static TSigned ByteSwap(TSigned n) -{ - using Unsigned = std::make_unsigned_t<TSigned>; - - auto swapped = ::ByteSwap(std::bit_cast<Unsigned>(n)); - return std::bit_cast<TSigned>(swapped); -} - -std::endian BaseDataStream::GetEndianness() const -{ - return mEndian; -} - -void BaseDataStream::SetEndianness(std::endian endianness) -{ - mEndian = endianness; -} - -InputDataStream::InputDataStream(InputFileStream stream) - : mBackend{ std::move(stream) } -{ -} - -void InputDataStream::ReadBytes(size_t byteCount, std::byte* buffer) -{ - mBackend.ReadBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<std::byte*>(buffer)); -} - -void InputDataStream::ReadBytes(size_t byteCount, char* buffer) -{ - mBackend.ReadBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<std::byte*>(buffer)); -} - -void InputDataStream::ReadBytes(size_t byteCount, signed char* buffer) -{ - mBackend.ReadBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<std::byte*>(buffer)); -} - -void InputDataStream::ReadBytes(size_t byteCount, unsigned char* buffer) -{ - mBackend.ReadBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<std::byte*>(buffer)); -} - -void InputDataStream::Read(int8_t& n) -{ - // sizeof() of a reference type yields the size of the reference - mBackend.ReadBytes(sizeof(n), reinterpret_cast<std::byte*>(&n)); -} - -void InputDataStream::Read(int16_t& n) -{ - int16_t tmp; - mBackend.ReadBytes(sizeof(tmp), reinterpret_cast<std::byte*>(&tmp)); - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(tmp); - } else { - n = tmp; - } -} - -void InputDataStream::Read(int32_t& n) -{ - int32_t tmp; - mBackend.ReadBytes(sizeof(tmp), reinterpret_cast<std::byte*>(&tmp)); - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(tmp); - } else { - n = tmp; - } -} - -void InputDataStream::Read(int64_t& n) -{ - int64_t tmp; - mBackend.ReadBytes(sizeof(tmp), reinterpret_cast<std::byte*>(&tmp)); - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(tmp); - } else { - n = tmp; - } -} - -void InputDataStream::Read(uint8_t& n) -{ - mBackend.ReadBytes(sizeof(n), reinterpret_cast<std::byte*>(&n)); -} - -void InputDataStream::Read(uint16_t& n) -{ - uint16_t tmp; - mBackend.ReadBytes(sizeof(tmp), reinterpret_cast<std::byte*>(&tmp)); - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(tmp); - } else { - n = tmp; - } -} - -void InputDataStream::Read(uint32_t& n) -{ - uint32_t tmp; - mBackend.ReadBytes(sizeof(tmp), reinterpret_cast<std::byte*>(&tmp)); - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(tmp); - } else { - n = tmp; - } -} - -void InputDataStream::Read(uint64_t& n) -{ - uint64_t tmp; - mBackend.ReadBytes(sizeof(tmp), reinterpret_cast<std::byte*>(&tmp)); - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(tmp); - } else { - n = tmp; - } -} - -void InputDataStream::Read(float& n) -{ - uint32_t buffer; - Read(buffer); - - if (GetEndianness() != std::endian::native) { - buffer = ::ByteSwap(buffer); - } - - n = std::bit_cast<float>(buffer); -} - -void InputDataStream::Read(double& n) -{ - uint64_t buffer; - Read(buffer); - - if (GetEndianness() != std::endian::native) { - buffer = ::ByteSwap(buffer); - } - - n = std::bit_cast<double>(buffer); -} - -OutputDataStream::OutputDataStream(OutputFileStream stream) - : mBackend{ std::move(stream) } -{ -} - -void OutputDataStream::WriteBytes(size_t byteCount, const std::byte* buffer) -{ - mBackend.WriteBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<const std::byte*>(buffer)); -} - -void OutputDataStream::WriteBytes(size_t byteCount, const char* buffer) -{ - mBackend.WriteBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<const std::byte*>(buffer)); -} - -void OutputDataStream::WriteBytes(size_t byteCount, const signed char* buffer) -{ - mBackend.WriteBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<const std::byte*>(buffer)); -} - -void OutputDataStream::WriteBytes(size_t byteCount, const unsigned char* buffer) -{ - mBackend.WriteBytes(static_cast<std::streamsize>(byteCount), reinterpret_cast<const std::byte*>(buffer)); -} - -void OutputDataStream::Write(int8_t n) -{ - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(int16_t n) -{ - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(n); - } - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(int32_t n) -{ - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(n); - } - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(int64_t n) -{ - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(n); - } - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(uint8_t n) -{ - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(uint16_t n) -{ - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(n); - } - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(uint32_t n) -{ - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(n); - } - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(uint64_t n) -{ - if (GetEndianness() != std::endian::native) { - n = ::ByteSwap(n); - } - mBackend.WriteBytes(sizeof(n), reinterpret_cast<const std::byte*>(&n)); -} - -void OutputDataStream::Write(float n) -{ - auto buffer = std::bit_cast<uint32_t>(n); - if (GetEndianness() != std::endian::native) { - buffer = ::ByteSwap(buffer); - } - mBackend.WriteBytes(sizeof(buffer), reinterpret_cast<const std::byte*>(&buffer)); -} - -void OutputDataStream::Write(double n) -{ - auto buffer = std::bit_cast<uint64_t>(n); - if (GetEndianness() != std::endian::native) { - buffer = ::ByteSwap(buffer); - } - mBackend.WriteBytes(sizeof(buffer), reinterpret_cast<const std::byte*>(&buffer)); -} diff --git a/core/src/Utils/IO/DataStream.hpp b/core/src/Utils/IO/DataStream.hpp deleted file mode 100644 index c0891ac..0000000 --- a/core/src/Utils/IO/DataStream.hpp +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -#include "FileStream.hpp" -#include "cplt_fwd.hpp" - -#include <bit> -#include <cstddef> -#include <cstdint> -#include <span> - -class BaseDataStream -{ -private: - std::endian mEndian = std::endian::big; - -public: - std::endian GetEndianness() const; - void SetEndianness(std::endian endianness); -}; - -class InputDataStream : public BaseDataStream -{ -private: - InputFileStream mBackend; - -public: - static constexpr bool IsSerializer() - { - return false; - } - - InputDataStream(InputFileStream stream); - - void ReadBytes(size_t byteCount, std::byte* buffer); - void ReadBytes(size_t byteCount, char* buffer); - void ReadBytes(size_t byteCount, signed char* buffer); - void ReadBytes(size_t byteCount, unsigned char* buffer); - - template <class TInserter> - void ReadBytes(size_t byteCount, TInserter&& inserter) - { - for (size_t i = 0; i < byteCount; ++i) { - uint8_t byte; - Read(byte); - - inserter = byte; - } - } - - void Read(int8_t& n); - void Read(int16_t& n); - void Read(int32_t& n); - void Read(int64_t& n); - - void Read(uint8_t& n); - void Read(uint16_t& n); - void Read(uint32_t& n); - void Read(uint64_t& n); - - void Read(float& n); - void Read(double& n); - - template <class TEnum> - requires std::is_enum_v<TEnum> - void ReadEnum(TEnum& e) - { - std::underlying_type_t<TEnum> n; - Read(n); - e = static_cast<TEnum>(e); - } - - template <class TObject> - void ReadObject(TObject& obj) - { - obj.ReadFromDataStream(*this); - } - - template <class TAdapter, class TObject> - void ReadObjectAdapted(TObject& obj) - { - TAdapter::ReadFromDataStream(*this, obj); - } - -public: - // Proxy functions for writing templated IO functions - - template <class T> - void Bytes(size_t byteCount, T* buffer) - { - ReadBytes(byteCount, buffer); - } - - template <class T> - void Value(T& t) - { - Read(t); - } - - template <class T> - void Enum(T& t) - { - ReadEnum(t); - } - - template <class T> - void Object(T& obj) - { - ReadObject(obj); - } - - template <class TAdapter, class TObject> - void ObjectAdapted(TObject& obj) - { - ReadObjectAdapted<TAdapter>(obj); - } -}; - -class OutputDataStream : public BaseDataStream -{ -private: - OutputFileStream mBackend; - -public: - static constexpr bool IsSerializer() - { - return true; - } - - OutputDataStream(OutputFileStream stream); - - void WriteBytes(size_t byteCount, const std::byte* buffer); - void WriteBytes(size_t byteCount, const char* buffer); - void WriteBytes(size_t byteCount, const signed char* buffer); - void WriteBytes(size_t byteCount, const unsigned char* buffer); - - template <class TIterator> - void WriteBytes(TIterator&& begin, TIterator&& end) - { - for (; begin != end; ++begin) { - uint8_t byte = *begin; - Write(byte); - } - } - - void Write(int8_t n); - void Write(int16_t n); - void Write(int32_t n); - void Write(int64_t n); - - void Write(uint8_t n); - void Write(uint16_t n); - void Write(uint32_t n); - void Write(uint64_t n); - - void Write(float n); - void Write(double n); - - template <class TEnum> - requires std::is_enum_v<TEnum> - void WriteEnum(TEnum e) - { - auto n = static_cast<std::underlying_type_t<TEnum>>(e); - Write(n); - } - - template <class TObject> - void WriteObject(const TObject& obj) - { - obj.WriteToDataStream(*this); - } - - template <class TAdapter, class TObject> - void WriteObjectAdapted(const TObject& obj) - { - TAdapter::WriteToDataStream(*this, obj); - } - -public: - // Proxy functions for writing templated IO functions - - template <class T> - void Bytes(size_t byteCount, T* buffer) - { - WriteBytes(byteCount, buffer); - } - - template <class T> - void Value(T t) - { - Write(t); - } - - template <class T> - void Enum(T t) - { - WriteEnum(t); - } - - template <class T> - void Object(T& obj) - { - WriteObject(obj); - } - - template <class TAdapter, class TObject> - void ObjectAdapted(TObject& obj) - { - WriteObjectAdapted<TAdapter>(obj); - } -}; diff --git a/core/src/Utils/IO/FileStream.cpp b/core/src/Utils/IO/FileStream.cpp deleted file mode 100644 index 8b83712..0000000 --- a/core/src/Utils/IO/FileStream.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "FileStream.hpp" - -#if defined(CPLT_FILESTREAM_USE_CSTDIO) -# include "FileStream_Cstdio.inl" -#else -# include "FileStream_Custom.inl" -#endif diff --git a/core/src/Utils/IO/FileStream.hpp b/core/src/Utils/IO/FileStream.hpp deleted file mode 100644 index 453ddbe..0000000 --- a/core/src/Utils/IO/FileStream.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -#include <cstddef> -#include <cstdint> -#include <filesystem> -#include <memory> - -// TODO switch to custom when unit tests are ready and bugs are fixed -#define CPLT_FILESTREAM_USE_CSTDIO - -struct IoResult -{ - enum ErrorKind - { - ERR_None, - ERR_PermissionDenied, - ERR_UnexpectedEof, - ERR_Unsupported, - ERR_OutOfSpace, - ERR_Other, - }; - - ErrorKind Error; - uint32_t SystemError; - size_t BytesMoved; -}; - -class InputFileStream -{ -private: -#if defined(CPLT_FILESTREAM_USE_CSTDIO) - FILE* mFile; -#else - alignas(void*) char mOsFileHandle[sizeof(void*)]; - - // mBuffer is always mReadInSize size - std::unique_ptr<std::byte[]> mBuffer; - int mReadInSize = 1024; - - int mFirstByteIdx = 0; - int mAvailableBytes = 0; - - bool mEof = false; -#endif - -public: - InputFileStream(const std::filesystem::path& path); - ~InputFileStream(); - - InputFileStream(const InputFileStream&) = delete; - InputFileStream& operator=(const InputFileStream&) = delete; - InputFileStream(InputFileStream&&); - InputFileStream& operator=(InputFileStream&&); - - int GetReadInSize() const; - void SetReadInSize(int size); - - bool IsEof() const; - - IoResult ReadBytes(size_t bufferLength, std::byte* buffer); -}; - -class OutputFileStream -{ -public: - enum WriteMode - { - AppendFile, - TruncateFile, - }; - -private: -#if defined(CPLT_FILESTREAM_USE_CSTDIO) - FILE* mFile; -#else - alignas(void*) char mOsFileHandle[sizeof(void*)]; - std::unique_ptr<std::byte[]> mBuffer; - int mMaxBufferSize = 1024; - int mCurrentBufferSize = 0; -#endif - -public: - OutputFileStream(const std::filesystem::path& path, WriteMode mode); - ~OutputFileStream(); - - OutputFileStream(const OutputFileStream&) = delete; - OutputFileStream& operator=(const OutputFileStream&) = delete; - OutputFileStream(OutputFileStream&&); - OutputFileStream& operator=(OutputFileStream&&); - - int GetMaxBufferSize() const; - void SetMaxBufferSize(int maxSize); - - IoResult WriteBytes(size_t bufferLength, const std::byte* buffer); - - void FlushBuffer(); -}; diff --git a/core/src/Utils/IO/FileStream_Cstdio.inl b/core/src/Utils/IO/FileStream_Cstdio.inl deleted file mode 100644 index ee55681..0000000 --- a/core/src/Utils/IO/FileStream_Cstdio.inl +++ /dev/null @@ -1,126 +0,0 @@ -// Note: included by FileStream.cpp conditionally, not compiled separately -#include "FileStream.hpp" - -#include "Utils/IO/CstdioFile.hpp" - -#include <cstdio> -#include <filesystem> - -namespace fs = std::filesystem; - -InputFileStream::InputFileStream(const fs::path& path) - : mFile{ FileUtils::OpenCstdioFile(path, FileUtils::IM_Read) } -{ -} - -InputFileStream::~InputFileStream() -{ - if (mFile) { - std::fclose(mFile); - } -} - -InputFileStream::InputFileStream(InputFileStream&& that) - : mFile{ that.mFile } -{ - that.mFile = nullptr; -} - -InputFileStream& InputFileStream::operator=(InputFileStream&& that) -{ - if (this == &that) return *this; - - if (mFile) { - std::fclose(mFile); - } - mFile = that.mFile; - that.mFile = nullptr; - - return *this; -} - -OutputFileStream::OutputFileStream(const fs::path& path, WriteMode mode) -{ - switch (mode) { - case AppendFile: mFile = FileUtils::OpenCstdioFile(path, FileUtils::IM_WriteAppend); break; - case TruncateFile: mFile = FileUtils::OpenCstdioFile(path, FileUtils::IM_WriteTruncate); break; - } -} - -OutputFileStream::~OutputFileStream() -{ - if (mFile) { - std::fclose(mFile); - } -} - -OutputFileStream::OutputFileStream(OutputFileStream&& that) - : mFile{ that.mFile } -{ - that.mFile = nullptr; -} - -OutputFileStream& OutputFileStream::operator=(OutputFileStream&& that) -{ - if (this == &that) return *this; - - if (mFile) { - std::fclose(mFile); - } - mFile = that.mFile; - that.mFile = nullptr; - - return *this; -} - -int InputFileStream::GetReadInSize() const -{ - return 0; -} - -void InputFileStream::SetReadInSize(int size) -{ - // No-op -} - -bool InputFileStream::IsEof() const -{ - return std::feof(mFile); -} - -IoResult InputFileStream::ReadBytes(size_t bufferLength, std::byte* buffer) -{ - auto bytesRead = std::fread(buffer, 1, bufferLength, mFile); - - return { - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = bytesRead, - }; -} - -int OutputFileStream::GetMaxBufferSize() const -{ - return 0; -} - -void OutputFileStream::SetMaxBufferSize(int maxSize) -{ - // No-op -} - -IoResult OutputFileStream::WriteBytes(size_t bufferLength, const std::byte* buffer) -{ - auto bytesWritten = std::fwrite(buffer, 1, bufferLength, mFile); - - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = bytesWritten, - }; -} - -void OutputFileStream::FlushBuffer() -{ - // No-op -} diff --git a/core/src/Utils/IO/FileStream_Custom.inl b/core/src/Utils/IO/FileStream_Custom.inl deleted file mode 100644 index 004dd01..0000000 --- a/core/src/Utils/IO/FileStream_Custom.inl +++ /dev/null @@ -1,358 +0,0 @@ -// Note: included by FileStream.cpp conditionally, not compiled separately -#include "FileStream.hpp" - -#include <cstring> -#include <filesystem> -#include <iostream> - -namespace fs = std::filesystem; - -#if defined(_WIN32) -# define WIN32_LEAN_AND_MEAN -# define NOMINMAX -# include <Windows.h> - -InputFileStream::InputFileStream(const fs::path& path) - : mOsFileHandle{ 0 } -{ - auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle); - - *handle = CreateFileW( - path.c_str(), // fs::path::c_str() returns a wide string on Windows - GENERIC_READ, - /* No sharing */ 0, - /* Use default security*/ nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - /* No attribute template */ nullptr); - - // TODO handle error -} - -InputFileStream::~InputFileStream() -{ - auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle); - CloseHandle(*handle); -} - -OutputFileStream::OutputFileStream(const fs::path& path, WriteMode mode) - : mOsFileHandle{ 0 } -{ - auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle); - - DWORD creationDisposition; - switch (mode) { - case AppendFile: creationDisposition = OPEN_ALWAYS; break; - case TruncateFile: creationDisposition = CREATE_ALWAYS; break; - } - - *handle = CreateFileW( - path.c_str(), - GENERIC_WRITE, - /* No sharing */ 0, - /* Use default security*/ nullptr, - creationDisposition, - FILE_ATTRIBUTE_NORMAL, - /* No attribute template */ nullptr); - - // TODO handle error -} - -OutputFileStream::~OutputFileStream() -{ - auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle); - CloseHandle(*handle); -} - -static IoResult::ErrorKind MapErrorCodeToIoResult(DWORD error) -{ - switch (error) { - // TODO - - default: - std::cerr << "Unimplemented win32 error code " << error << ", report bug immediately.\n"; - std::abort(); - } -} - -static IoResult ReadBytesDirect(HANDLE hFile, size_t byteCount, std::byte* bytes) -{ - DWORD bytesRead; - BOOL result = ReadFile(hFile, bytes, byteCount, &bytesRead, nullptr); - - if (result) { - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = bytesRead, - }; - } else { - DWORD errorCode = GetLastError(); - return IoResult{ - .Error = ::MapErrorCodeToIoResult(errorCode), - .SystemError = errorCode, - .BytesMoved = bytesRead, - }; - } -} - -static IoResult WriteBytesDirect(HANDLE hFile, size_t byteCount, const std::byte* bytes) -{ - DWORD bytesWritten; - BOOL result = WriteFile(hFile, bytes, byteCount, &bytesWritten, nullptr); - - if (result) { - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = bytesWritten, - }; - } else { - DWORD errorCode = GetLastError(); - return IoResult{ - .Error = ::MapErrorCodeToIoResult(errorCode), - .SystemError = errorCode, - .BytesMoved = bytesWritten, - }; - } -} - -#elif defined(__APPLE__) || defined(__linux__) -# include <fcntl.h> -# include <sys/stat.h> -# include <sys/types.h> -# include <unistd.h> - -InputFileStream::InputFileStream(const fs::path& path) - : mOsFileHandle{ 0 } -{ - auto fd = reinterpret_cast<int*>(mOsFileHandle); - *fd = open(path.c_str(), O_RDONLY); -} - -InputFileStream::~InputFileStream() -{ - auto fd = reinterpret_cast<int*>(mOsFileHandle); - close(*fd); -} - -OutputFileStream::OutputFileStream(const fs::path& path, WriteMode mode) - : mOsFileHandle{ 0 } -{ - auto fd = reinterpret_cast<int*>(mOsFileHandle); - - int flags = O_WRONLY | O_CREAT; - switch (mode) { - case AppendFile: flags |= O_APPEND; break; - case TruncateFile: flags |= O_TRUNC; break; - } - - *fd = open(path.c_str(), flags, 0644); -} - -OutputFileStream::~OutputFileStream() -{ - auto fd = reinterpret_cast<int*>(mOsFileHandle); - close(*fd); -} - -static IoResult::ErrorKind MapErrnoToIoResult(int err) -{ - switch (err) { - // TODO - case EFAULT: return IoResult::ERR_UnexpectedEof; - case EPERM: return IoResult::ERR_PermissionDenied; - case ENOSPC: return IoResult::ERR_OutOfSpace; - case EIO: return IoResult::ERR_Other; - - default: - std::cerr << "Unimplemented POSIX errno " << err << ", report bug immediately.\n"; - std::abort(); - } -} - -static IoResult ReadBytesDirect(const char* osFileHandle, size_t byteCount, std::byte* bytes) -{ - int fd = *reinterpret_cast<const int*>(osFileHandle); - int status = read(fd, bytes, byteCount); - - if (status == -1) { - int err = errno; - return IoResult{ - .Error = ::MapErrnoToIoResult(err), - .SystemError = (uint32_t)err, - .BytesMoved = 0, - }; - } else { - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = (size_t)status, // Equal to number of bytes read - }; - } -} - -static IoResult WriteBytesDirect(const char* osFileHandle, size_t byteCount, const std::byte* bytes) -{ - int fd = *reinterpret_cast<const int*>(osFileHandle); - int status = write(fd, bytes, byteCount); - - if (status == -1) { - int err = errno; - return IoResult{ - .Error = ::MapErrnoToIoResult(err), - .SystemError = (uint32_t)err, - .BytesMoved = 0, - }; - } else { - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = (size_t)status, // Equal to number of bytes read - }; - } -} - -#else -# error "Unsupported target platform." -#endif - -int InputFileStream::GetReadInSize() const -{ - return mReadInSize; -} - -void InputFileStream::SetReadInSize(int size) -{ - if (size > mReadInSize) { - mReadInSize = size; - mBuffer = std::make_unique<std::byte[]>(size); - } -} - -bool InputFileStream::IsEof() const -{ - return mEof; -} - -IoResult InputFileStream::ReadBytes(size_t bufferLength, std::byte* buffer) -{ - // TODO reduce duplicated code - - auto bytesMoved = std::min<size_t>(mAvailableBytes, bufferLength); - - // On first call after construction, mFirstByteIdx will equal to mReadInSize, i.e. bytesAvailable == 0 - // and this call to std::memcpy will be no-op - std::memcpy(buffer, &mBuffer[mFirstByteIdx], bytesMoved); - mFirstByteIdx += (int)bytesMoved; - mAvailableBytes -= (int)bytesMoved; - buffer += bytesMoved; - - size_t bytesLeft = bufferLength - bytesMoved; - if (bytesLeft > mReadInSize) { - // Our buffer can't handle rest of the request, just skip the buffering step - - // Read rest of the data into buffer - { - auto result = ::ReadBytesDirect(mOsFileHandle, bytesLeft, buffer); - bytesMoved += result.BytesMoved; - - if (result.Error == IoResult::ERR_None) { - if (result.BytesMoved < mReadInSize) { - mEof = true; - } - } else { - goto end; - } - } - - // Refill our buffer - { - auto result = ::ReadBytesDirect(mOsFileHandle, mReadInSize, mBuffer.get()); - mFirstByteIdx = 0; - mAvailableBytes = (int)result.BytesMoved; - - if (result.Error == IoResult::ERR_None) { - if (result.BytesMoved < mReadInSize) { - mEof = true; - } - } else { - goto end; - } - } - } else if (bytesLeft > 0) { - // Our buffer can handle rest of the request, first buffer than supply the requested data - - // Refill our buffer - { - auto result = ::ReadBytesDirect(mOsFileHandle, mReadInSize, mBuffer.get()); - mFirstByteIdx = 0; - mAvailableBytes = (int)result.BytesMoved; - - if (result.Error == IoResult::ERR_None) { - if (result.BytesMoved < mReadInSize) { - mEof = true; - } - } else { - goto end; - } - } - - // Copy data into buffer - { - std::memcpy(buffer, &mBuffer[mFirstByteIdx], bytesLeft); - mFirstByteIdx += (int)bytesLeft; - bytesMoved += bytesLeft; - buffer += bytesLeft; - } - } else { - // Request completed already - } - -end: - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = bytesMoved, - }; -} - -int OutputFileStream::GetMaxBufferSize() const -{ - return mMaxBufferSize; -} - -void OutputFileStream::SetMaxBufferSize(int maxSize) -{ - FlushBuffer(); - if (maxSize > mMaxBufferSize) { - mMaxBufferSize = maxSize; - mBuffer = std::make_unique<std::byte[]>(maxSize); - } -} - -IoResult OutputFileStream::WriteBytes(size_t bufferLength, const std::byte* buffer) -{ - if (bufferLength + mCurrentBufferSize > mMaxBufferSize) { - FlushBuffer(); - - if (bufferLength > mMaxBufferSize) { - return ::WriteBytesDirect(mOsFileHandle, bufferLength, buffer); - } - } - - std::memcpy(mBuffer.get() + mCurrentBufferSize, buffer, bufferLength); - mCurrentBufferSize += (int)bufferLength; - - return IoResult{ - .Error = IoResult::ERR_None, - .SystemError = 0, - .BytesMoved = bufferLength, - }; -} - -void OutputFileStream::FlushBuffer() -{ - ::WriteBytesDirect(mOsFileHandle, mCurrentBufferSize, mBuffer.get()); - mCurrentBufferSize = 0; -} diff --git a/core/src/Utils/IO/Helper.hpp b/core/src/Utils/IO/Helper.hpp deleted file mode 100644 index ebd47a7..0000000 --- a/core/src/Utils/IO/Helper.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" - -namespace DataStreamAdapters { - -/// Helper to invoke either Read() or ReadObject(). -/// This is intended for writing IO adapters, users that's writing IO logic shouldn't using this - it increases compile time while reducing readability. -template <class TAdapter, class T> -void ReadHelper(InputDataStream& stream, T& t) -{ - if constexpr (!std::is_same_v<TAdapter, void>) { - stream.ReadObjectAdapted<TAdapter>(t); - } else if constexpr (requires(T tt, InputDataStream ss) { ss.Read(tt); }) { - stream.Read(t); - } else if constexpr (requires(T tt, InputDataStream ss) { ss.ReadEnum(tt); }) { - stream.ReadEnum(t); - } else if constexpr (requires(T tt, InputDataStream ss) { ss.ReadObject(tt); }) { - stream.ReadObject(t); - } else { - static_assert(false && sizeof(T), "This type is neither a 'value' nor an 'object'."); - } -} - -/// Helper to invoke either Write() or WriteObject(). -/// This is intended for writing IO adapters, users that's writing IO logic shouldn't using this - it increases compile time while reducing readability. -template <class TAdapter, class T> -void WriteHelper(OutputDataStream& stream, T& t) -{ - if constexpr (!std::is_same_v<TAdapter, void>) { - stream.WriteObjectAdapted<TAdapter>(t); - } else if constexpr (requires(T tt, OutputDataStream ss) { ss.Write(tt); }) { - stream.Write(t); - } else if constexpr (requires(T tt, OutputDataStream ss) { ss.WriteEnum(tt); }) { - stream.WriteEnum(t); - } else if constexpr (requires(T tt, OutputDataStream ss) { ss.WriteObject(tt); }) { - stream.WriteObject(t); - } else { - static_assert(false && sizeof(T), "This type is neither a 'value' nor an 'object'."); - } -} - -} // namespace DataStreamAdapters diff --git a/core/src/Utils/IO/StringIntegration.hpp b/core/src/Utils/IO/StringIntegration.hpp deleted file mode 100644 index 536cb83..0000000 --- a/core/src/Utils/IO/StringIntegration.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" - -#include <iterator> -#include <string> -#include <string_view> - -namespace DataStreamAdapters { -struct String -{ - static void ReadFromDataStream(InputDataStream& stream, std::string& str) - { - uint64_t size; - stream.Read(size); - - str = {}; - str.reserve(size); - stream.ReadBytes(size, std::back_inserter(str)); - } - - static void WriteToDataStream(OutputDataStream& stream, const std::string& str) - { - stream.Write((uint64_t)str.size()); - stream.WriteBytes(str.size(), str.data()); - } -}; - -struct StringView -{ - static void WriteToDataStream(OutputDataStream& stream, const std::string_view& str) - { - stream.Write((uint64_t)str.size()); - stream.WriteBytes(str.size(), str.data()); - } -}; -} // namespace DataStreamAdapters diff --git a/core/src/Utils/IO/TslArrayIntegration.hpp b/core/src/Utils/IO/TslArrayIntegration.hpp deleted file mode 100644 index af1197c..0000000 --- a/core/src/Utils/IO/TslArrayIntegration.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" -#include "Utils/IO/Helper.hpp" -#include "Utils/IO/StringIntegration.hpp" - -#include <tsl/array_map.h> -#include <tsl/array_set.h> -#include <string> -#include <type_traits> - -// TODO support custom key types - -namespace DataStreamAdapters { -template <class TAdapter = void> -struct TslArrayMap -{ - template <class TValue> - static void ReadFromDataStream(InputDataStream& stream, tsl::array_map<char, TValue>& map) - { - static_assert(std::is_default_constructible_v<TValue>); - static_assert(std::is_move_constructible_v<TValue>); - - uint64_t size; - stream.Read(size); - map.reserve(size); - - for (uint64_t i = 0; i < size; ++i) { - std::string key; - stream.ReadObjectAdapted<DataStreamAdapters::String>(key); - - TValue value; - ReadHelper<TAdapter>(stream, value); - - map.insert(key, std::move(value)); - } - } - - template <class TValue> - static void WriteToDataStream(OutputDataStream& stream, const tsl::array_map<char, TValue>& map) - { - stream.Write((uint64_t)map.size()); - - for (auto it = map.begin(); it != map.end(); ++it) { - stream.WriteObjectAdapted<DataStreamAdapters::StringView>(it.key_sv()); - WriteHelper<TAdapter>(stream, it.value()); - } - } -}; -} // namespace DataStreamAdapters diff --git a/core/src/Utils/IO/TslRobinIntegration.hpp b/core/src/Utils/IO/TslRobinIntegration.hpp deleted file mode 100644 index 50775fe..0000000 --- a/core/src/Utils/IO/TslRobinIntegration.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" -#include "Utils/IO/Helper.hpp" - -#include <tsl/robin_map.h> -#include <tsl/robin_set.h> -#include <type_traits> - -namespace DataStreamAdapters { -template <class TKeyAdapter = void, class TValueAdapter = void> -struct TslRobinMap -{ - template <class TKey, class TValue> - static void ReadFromDataStream(InputDataStream& stream, tsl::robin_map<TKey, TValue>& map) - { - static_assert(std::is_default_constructible_v<TValue>); - static_assert(std::is_move_constructible_v<TValue>); - - uint64_t size; - stream.Read(size); - map.reserve(size); - - for (uint64_t i = 0; i < size; ++i) { - TKey key; - ReadHelper<TKeyAdapter>(stream, key); - - TValue value; - ReadHelper<TValueAdapter>(stream, value); - - map.insert(std::move(key), std::move(value)); - } - } - - template <class TKey, class TValue> - static void WriteToDataStream(OutputDataStream& stream, const tsl::robin_map<TKey, TValue>& map) - { - stream.Write((uint64_t)map.size()); - - for (auto it = map.begin(); it != map.end(); ++it) { - WriteHelper<TKeyAdapter>(stream, it.key()); - WriteHelper<TValueAdapter>(stream, it.value()); - } - } -}; - -template <class TAdapter = void> -struct TslRobinSet -{ - template <class TElement> - static void ReadFromDataStream(InputDataStream& stream, tsl::robin_set<TElement>& set) - { - static_assert(std::is_default_constructible_v<TElement>); - static_assert(std::is_move_constructible_v<TElement>); - - uint64_t size; - stream.Read(size); - set.reserve(size); - - for (uint64_t i = 0; i < size; ++i) { - TElement element; - ReadHelper<TAdapter>(stream, element); - - set.insert(std::move(element)); - } - } - - template <class TElement> - static void WriteToDataStream(OutputDataStream& stream, const tsl::robin_set<TElement>& set) - { - stream.Write((uint64_t)set.size()); - - for (auto& element : set) { - WriteHelper<TAdapter>(stream, element); - } - } -}; -} // namespace DataStreamAdapters diff --git a/core/src/Utils/IO/UuidIntegration.hpp b/core/src/Utils/IO/UuidIntegration.hpp deleted file mode 100644 index d028c50..0000000 --- a/core/src/Utils/IO/UuidIntegration.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" -#include "Utils/UUID.hpp" - -#include <cstddef> -#include <cstdint> -#include <iterator> - -namespace DataStreamAdapters { -struct Uuid -{ - static void ReadFromDataStream(InputDataStream& stream, uuids::uuid& uuid) - { - uint8_t buffer[16]; - stream.ReadBytes(16, buffer); - - uuid = uuids::uuid(gsl::span<uint8_t, 16>{ buffer }); - } - - static void WriteToDataStream(OutputDataStream& stream, const uuids::uuid& uuid) - { - auto gslSpan = uuid.as_bytes(); - stream.WriteBytes(gslSpan.size(), gslSpan.data()); - } -}; -} // namespace DataStreamAdapters diff --git a/core/src/Utils/IO/VectorIntegration.hpp b/core/src/Utils/IO/VectorIntegration.hpp deleted file mode 100644 index 3689505..0000000 --- a/core/src/Utils/IO/VectorIntegration.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" -#include "Utils/IO/Helper.hpp" - -#include <type_traits> -#include <vector> - -namespace DataStreamAdapters { -template <class TAdapter = void> -struct Vector -{ - template <class TElement> - static void ReadFromDataStream(InputDataStream& stream, std::vector<TElement>& vec) - { - static_assert(std::is_default_constructible_v<TElement>); - static_assert(std::is_move_constructible_v<TElement>); - - uint64_t size; - stream.Read(size); - - vec.clear(); - vec.reserve(size); - - for (uint64_t i = 0; i < size; ++i) { - TElement element; - ReadHelper<TAdapter>(stream, element); - - vec.push_back(std::move(element)); - } - } - - template <class TElement> - static void WriteToDataStream(OutputDataStream& stream, const std::vector<TElement>& vec) - { - stream.Write((uint64_t)vec.size()); - for (auto& element : vec) { - WriteHelper<TAdapter>(stream, element); - } - } -}; -} // namespace DataStreamAdapters diff --git a/core/src/Utils/IO/fwd.hpp b/core/src/Utils/IO/fwd.hpp deleted file mode 100644 index 9f1492b..0000000 --- a/core/src/Utils/IO/fwd.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -// Archive.hpp -class DataArchive; - -// BaseDataStream.hpp -class BaseDataStream; -class InputDataStream; -class OutputDataStream; - -// FileStream.hpp -class InputFileStream; -class OutputFileStream; diff --git a/core/src/Utils/Macros.hpp b/core/src/Utils/Macros.hpp deleted file mode 100644 index 6958ed1..0000000 --- a/core/src/Utils/Macros.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#define STRINGIFY_IMPL(text) #text -#define STRINGIFY(text) STRINGIFY_IMPL(text) - -#define CONCAT_IMPL(a, b) a##b -#define CONCAT(a, b) CONCAT_IMPL(a, b) -#define CONCAT_3(a, b, c) CONCAT(a, CONCAT(b, c)) -#define CONCAT_4(a, b, c, d) CONCAT(CONCAT(a, b), CONCAT(c, d)) - -#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__) -#define UNIQUE_NAME_LINE(prefix) CONCAT(prefix, __LINE__) -#define DISCARD UNIQUE_NAME(_discard) diff --git a/core/src/Utils/Math.hpp b/core/src/Utils/Math.hpp deleted file mode 100644 index da53da2..0000000 --- a/core/src/Utils/Math.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace MathUtils { - -template <class T> -constexpr T Abs(T t) -{ - return t < 0 ? -t : t; -} - -} // namespace MathUtils diff --git a/core/src/Utils/RTTI.hpp b/core/src/Utils/RTTI.hpp deleted file mode 100644 index 86b1e2c..0000000 --- a/core/src/Utils/RTTI.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include <cassert> - -template <class T, class TBase> -bool is_a(TBase* t) -{ - assert(t != nullptr); - return T::IsInstance(t); -} - -template <class T, class TBase> -bool is_a_nullable(TBase* t) -{ - if (t) { - return is_a<T, TBase>(t); - } else { - return false; - } -} - -template <class T, class TBase> -T* dyn_cast(TBase* t) -{ - assert(t != nullptr); - if (T::IsInstance(t)) { - return static_cast<T*>(t); - } else { - return nullptr; - } -} - -template <class T, class TBase> -const T* dyn_cast(const TBase* t) -{ - assert(t != nullptr); - if (T::IsInstance(t)) { - return static_cast<const T*>(t); - } else { - return nullptr; - } -} - -template <class T, class TBase> -T* dyn_cast_nullable(TBase* t) -{ - if (!t) return nullptr; - return dyn_cast<T, TBase>(t); -} diff --git a/core/src/Utils/ScopeGuard.hpp b/core/src/Utils/ScopeGuard.hpp deleted file mode 100644 index 28ffd0b..0000000 --- a/core/src/Utils/ScopeGuard.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "Utils/Macros.hpp" - -#include <utility> - -template <class TCleanupFunc> -class ScopeGuard -{ -private: - TCleanupFunc mFunc; - bool mDismissed = false; - -public: - /// Specifically left this implicit so that constructs like - /// \code - /// ScopeGuard sg = [&]() { res.Cleanup(); }; - /// \endcode - /// would work. It is highly discourage and unlikely that one would want to use ScopeGuard as a function - /// parameter, so the normal argument that implicit conversion are harmful doesn't really apply here. - ScopeGuard(TCleanupFunc func) - : mFunc{ std::move(func) } - { - } - - ~ScopeGuard() - { - if (!mDismissed) { - mFunc(); - } - } - - void Dismiss() noexcept - { - mDismissed = true; - } -}; - -#define DEFER ScopeGuard UNIQUE_NAME(scopeGuard) = [&]() diff --git a/core/src/Utils/Sigslot.cpp b/core/src/Utils/Sigslot.cpp deleted file mode 100644 index 1132dfb..0000000 --- a/core/src/Utils/Sigslot.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include "Sigslot.hpp" - -#include <doctest/doctest.h> - -bool SignalStub::Connection::IsOccupied() const -{ - return id != InvalidId; -} - -SignalStub::SignalStub(IWrapper& wrapper) - : mWrapper{ &wrapper } -{ -} - -SignalStub::~SignalStub() -{ - RemoveAllConnections(); -} - -std::span<const SignalStub::Connection> SignalStub::GetConnections() const -{ - return mConnections; -} - -SignalStub::Connection& SignalStub::InsertConnection(SlotGuard* guard) -{ - Connection* result; - int size = static_cast<int>(mConnections.size()); - for (int i = 0; i < size; ++i) { - auto& conn = mConnections[i]; - if (!conn.IsOccupied()) { - result = &conn; - result->id = i; - goto setup; - } - } - - mConnections.push_back(Connection{}); - result = &mConnections.back(); - result->id = size; - -setup: - if (guard) { - result->guard = guard; - result->slotId = guard->InsertConnection(*this, result->id); - } - return *result; -} - -void SignalStub::RemoveConnection(int id) -{ - if (id >= 0 && id < mConnections.size()) { - auto& conn = mConnections[id]; - if (conn.IsOccupied()) { - mWrapper->RemoveFunction(conn.id); - if (conn.guard) { - conn.guard->RemoveConnection(conn.slotId); - } - - conn.guard = nullptr; - conn.slotId = SignalStub::InvalidId; - conn.id = SignalStub::InvalidId; - } - } -} - -void SignalStub::RemoveConnectionFor(SlotGuard& guard) -{ - guard.RemoveConnectionFor(*this); -} - -void SignalStub::RemoveAllConnections() -{ - for (size_t i = 0; i < mConnections.size(); ++i) { - RemoveConnection(i); - } -} - -SlotGuard::SlotGuard() -{ -} - -SlotGuard::~SlotGuard() -{ - DisconnectAll(); -} - -void SlotGuard::DisconnectAll() -{ - for (auto& conn : mConnections) { - if (conn.stub) { - // Also calls SlotGuard::removeConnection, our copy of the data will be cleared in it - conn.stub->RemoveConnection(conn.stubId); - } - } -} - -int SlotGuard::InsertConnection(SignalStub& stub, int stubId) -{ - int size = static_cast<int>(mConnections.size()); - for (int i = 0; i < size; ++i) { - auto& conn = mConnections[i]; - if (!conn.stub) { - conn.stub = &stub; - conn.stubId = stubId; - return i; - } - } - - mConnections.push_back(Connection{}); - auto& conn = mConnections.back(); - conn.stub = &stub; - conn.stubId = stubId; - return size; -} - -void SlotGuard::RemoveConnectionFor(SignalStub& stub) -{ - for (auto& conn : mConnections) { - if (conn.stub == &stub) { - conn.stub->RemoveConnection(conn.stubId); - } - } -} - -void SlotGuard::RemoveConnection(int slotId) -{ - mConnections[slotId] = {}; -} - -TEST_CASE("Signal connect and disconnect") -{ - Signal<> sig; - - int counter = 0; - int id = sig.Connect([&]() { counter++; }); - - sig(); - CHECK(counter == 1); - - sig(); - CHECK(counter == 2); - - sig.Disconnect(id); - sig(); - CHECK(counter == 2); -} - -TEST_CASE("Signal with parameters") -{ - Signal<int> sig; - - int counter = 0; - int id = sig.Connect([&](int i) { counter += i; }); - - sig(1); - CHECK(counter == 1); - - sig(0); - CHECK(counter == 1); - - sig(4); - CHECK(counter == 5); - - sig.Disconnect(id); - sig(1); - CHECK(counter == 5); -} - -TEST_CASE("Signal disconnectAll()") -{ - Signal<> sig; - - int counter1 = 0; - int counter2 = 0; - sig.Connect([&]() { counter1++; }); - sig.Connect([&]() { counter2++; }); - - sig(); - CHECK(counter1 == 1); - CHECK(counter2 == 1); - - sig(); - CHECK(counter1 == 2); - CHECK(counter2 == 2); - - sig.DisconnectAll(); - sig(); - CHECK(counter1 == 2); - CHECK(counter2 == 2); -} - -TEST_CASE("SlotGuard auto-disconnection") -{ - int counter1 = 0; - int counter2 = 0; - Signal<> sig; - - { - SlotGuard guard; - sig.Connect(guard, [&]() { counter1 += 1; }); - sig.Connect(guard, [&]() { counter2 += 1; }); - - sig(); - CHECK(counter1 == 1); - CHECK(counter2 == 1); - - sig(); - CHECK(counter1 == 2); - CHECK(counter2 == 2); - } - - sig(); - CHECK(counter1 == 2); - CHECK(counter2 == 2); -} - -TEST_CASE("Signal destruct before SlotGuard") -{ - int counter = 0; - SlotGuard guard; - - { - Signal<> sig2; - sig2.Connect(guard, [&]() { counter++; }); - - sig2(); - CHECK(counter == 1); - } - - // Shouldn't error - guard.DisconnectAll(); -} diff --git a/core/src/Utils/Sigslot.hpp b/core/src/Utils/Sigslot.hpp deleted file mode 100644 index 5638f12..0000000 --- a/core/src/Utils/Sigslot.hpp +++ /dev/null @@ -1,165 +0,0 @@ -#pragma once - -#include "Utils/fwd.hpp" - -#include <cstddef> -#include <functional> -#include <span> -#include <utility> -#include <vector> - -class SignalStub -{ -public: - /// Non-template interface for Signal<T...> to implement (a barrier to stop template - /// arguments propagation). - class IWrapper - { - public: - virtual ~IWrapper() = default; - virtual void RemoveFunction(int id) = 0; - }; - - enum - { - InvalidId = -1, - }; - - struct Connection - { - SlotGuard* guard; - int slotId; - int id = InvalidId; // If `InvalidId`, then this "spot" is unused - - bool IsOccupied() const; - }; - -private: - std::vector<Connection> mConnections; - IWrapper* mWrapper; - -private: - template <class...> - friend class Signal; - friend class SlotGuard; - - SignalStub(IWrapper& wrapper); - ~SignalStub(); - - SignalStub(const SignalStub&) = delete; - SignalStub& operator=(const SignalStub&) = delete; - SignalStub(SignalStub&&) = default; - SignalStub& operator=(SignalStub&&) = default; - - std::span<const Connection> GetConnections() const; - Connection& InsertConnection(SlotGuard* guard = nullptr); - void RemoveConnection(int id); - void RemoveConnectionFor(SlotGuard& guard); - void RemoveAllConnections(); -}; - -template <class... TArgs> -class Signal : public SignalStub::IWrapper -{ -private: - // Must be in this order so that mFunctions is still intact when mStub's destructor runs - std::vector<std::function<void(TArgs...)>> mFunctions; - SignalStub mStub; - -public: - Signal() - : mStub(*this) - { - } - - virtual ~Signal() = default; - - Signal(const Signal&) = delete; - Signal& operator=(const Signal&) = delete; - Signal(Signal&&) = default; - Signal& operator=(Signal&&) = default; - - void operator()(TArgs... args) - { - for (auto& conn : mStub.GetConnections()) { - if (conn.IsOccupied()) { - mFunctions[conn.id](std::forward<TArgs>(args)...); - } - } - } - - template <class TFunction> - int Connect(TFunction slot) - { - auto& conn = mStub.InsertConnection(); - mFunctions.resize(std::max(mFunctions.size(), (size_t)conn.id + 1)); - mFunctions[conn.id] = std::move(slot); - return conn.id; - } - - template <class TFunction> - int Connect(SlotGuard& guard, TFunction slot) - { - auto& conn = mStub.InsertConnection(&guard); - mFunctions.resize(std::max(mFunctions.size(), (size_t)conn.id + 1)); - mFunctions[conn.id] = std::move(slot); - return conn.id; - } - - void Disconnect(int id) - { - mStub.RemoveConnection(id); - } - - void DisconnectFor(SlotGuard& guard) - { - mStub.RemoveConnectionFor(guard); - } - - void DisconnectAll() - { - mStub.RemoveAllConnections(); - } - - virtual void RemoveFunction(int id) - { - mFunctions[id] = {}; - } -}; - -/// Automatic disconnection mechanism for Signal<>. -/// Bind connection to this guard by using the Connect(SlotGuard&, TFunction) overload. -/// Either DisconnectAll() or the destructor disconnects all connections bound to this guard. -class SlotGuard -{ -private: - struct Connection - { - SignalStub* stub = nullptr; - int stubId = SignalStub::InvalidId; - }; - std::vector<Connection> mConnections; - -public: - friend class SignalStub; - SlotGuard(); - ~SlotGuard(); - - SlotGuard(const SlotGuard&) = delete; - SlotGuard& operator=(const SlotGuard&) = delete; - SlotGuard(SlotGuard&&) = default; - SlotGuard& operator=(SlotGuard&&) = default; - - /// DisconnectBySource all connection associated with this SlotGuard. - void DisconnectAll(); - -private: - /// \return Slot id. - int InsertConnection(SignalStub& stub, int stubId); - /// Remove the connection data in this associated with slotId. This does not invoke - /// the connections' stub's RemoveConnection function. - void RemoveConnection(int slotId); - /// DisconnectBySource all connections from the given stub associated with this SlotGuard. - /// Implementation for SignalStub::RemoveConnectionsFor(SlotGuard&) - void RemoveConnectionFor(SignalStub& stub); -}; diff --git a/core/src/Utils/Size.hpp b/core/src/Utils/Size.hpp deleted file mode 100644 index 98be41a..0000000 --- a/core/src/Utils/Size.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "Utils/Vector.hpp" - -template <class T> -class Size2 { -public: - T width; - T height; - -public: - Size2() - : width{ 0 }, height{ 0 } { - } - - Size2(T width, T height) - : width{ width }, height{ height } { - } - - Size2(Vec2<T> vec) - : width{ vec.x }, height{ vec.y } - { - } - - operator Vec2<T>() const - { - return { width, height }; - } - - Vec2<T> AsVec() const - { - return { width, height }; - } - - friend bool operator==(const Size2<T>&, const Size2<T>&) = default; - - template <class TTarget> - Size2<TTarget> Cast() const - { - return { - static_cast<TTarget>(width), - static_cast<TTarget>(height), - }; - } -}; - -template <class T> -Size2<T> operator+(Size2<T> a, Size2<T> b) { - return { a.width + b.width, a.height + b.height }; -} - -template <class T> -Size2<T> operator-(Size2<T> a, Size2<T> b) { - return { a.width - b.width, a.height - b.height }; -} - -template <class T, class N> -auto operator*(Size2<T> a, N mult) -> Size2<decltype(a.width * mult)> { - return { a.width * mult, a.height * mult }; -} - -template <class T, class N> -auto operator/(Size2<T> a, N mult) -> Size2<decltype(a.width / mult)> { - return { a.width / mult, a.height / mult }; -} diff --git a/core/src/Utils/StandardDirectories.cpp b/core/src/Utils/StandardDirectories.cpp deleted file mode 100644 index 2202f51..0000000 --- a/core/src/Utils/StandardDirectories.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "StandardDirectories.hpp" - -#include <filesystem> -#include <stdexcept> - -namespace fs = std::filesystem; - -#if defined(_WIN32) -// https://stackoverflow.com/questions/54499256/how-to-find-the-saved-games-folder-programmatically-in-c-c -# include <ShlObj_core.h> -# include <objbase.h> -# pragma comment(lib, "shell32.lib") -# pragma comment(lib, "ole32.lib") - -static fs::path GetAppDataRoaming() -{ - PWSTR path = nullptr; - HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &path); - if (SUCCEEDED(hr)) { - auto dataDir = fs::path(path); - CoTaskMemFree(path); - - fs::create_directories(dataDir); - return dataDir; - } else { - fs::path dataDir("~/AppData/Roaming"); - fs::create_directories(dataDir); - return dataDir; - } -} - -#elif defined(__APPLE__) -// TODO -#elif defined(__linux__) -# include <cstdlib> - -static fs::path GetEnvVar(const char* name, const char* backup) -{ - if (const char* path = std::getenv(name)) { - fs::path dataDir(path); - fs::create_directories(dataDir); - return dataDir; - } else { - fs::path dataDir(backup); - fs::create_directories(dataDir); - return dataDir; - } -} - -#endif - -const std::filesystem::path& StandardDirectories::UserData() -{ - static auto userDataDir = []() -> fs::path { -#if defined(_WIN32) - return GetAppDataRoaming(); -#elif defined(__APPLE__) - // TODO where? -#elif defined(__linux__) - return GetEnvVar("XDG_DATA_HOME", "~/.local/share"); -#endif - }(); - return userDataDir; -} - -const std::filesystem::path& StandardDirectories::UserConfig() -{ - static auto userConfigDir = []() -> fs::path { -#if defined(_WIN32) - return GetAppDataRoaming(); -#elif defined(__APPLE__) - // TODO where? -#elif defined(__linux__) - return GetEnvVar("XDG_CONFIG_HOME", "~/.config"); -#endif - }(); - return userConfigDir; -} diff --git a/core/src/Utils/StandardDirectories.hpp b/core/src/Utils/StandardDirectories.hpp deleted file mode 100644 index 4f7e5e2..0000000 --- a/core/src/Utils/StandardDirectories.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include <filesystem> - -namespace StandardDirectories { - -const std::filesystem::path& UserData(); -const std::filesystem::path& UserConfig(); - -} // namespace StandardDirectories diff --git a/core/src/Utils/Time.cpp b/core/src/Utils/Time.cpp deleted file mode 100644 index 4e79ffa..0000000 --- a/core/src/Utils/Time.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "Time.hpp" - -#include <ctime> - -std::string TimeUtils::StringifyTimePoint(std::chrono::time_point<std::chrono::system_clock> tp) -{ - auto t = std::chrono::system_clock::to_time_t(tp); - - char data[32]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // C++ doesn't have std::localtime_s - std::strftime(data, sizeof(data), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); -#pragma clang diagnostic pop - - return std::string(data); -} - -std::string TimeUtils::StringifyTimeStamp(int64_t timeStamp) -{ - if (timeStamp == 0) { - return ""; - } - - namespace chrono = std::chrono; - chrono::milliseconds d{ timeStamp }; - chrono::time_point<chrono::system_clock> tp{ d }; - - return StringifyTimePoint(tp); -} diff --git a/core/src/Utils/Time.hpp b/core/src/Utils/Time.hpp deleted file mode 100644 index fbbd3b2..0000000 --- a/core/src/Utils/Time.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include <chrono> -#include <string> - -namespace TimeUtils { - -std::string StringifyTimePoint(std::chrono::time_point<std::chrono::system_clock> tp); -std::string StringifyTimeStamp(int64_t timeStamp); - -} // namespace TimeUtils diff --git a/core/src/Utils/UUID.hpp b/core/src/Utils/UUID.hpp deleted file mode 100644 index 9044aa6..0000000 --- a/core/src/Utils/UUID.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include <uuid.h> diff --git a/core/src/Utils/Variant.hpp b/core/src/Utils/Variant.hpp deleted file mode 100644 index df2f882..0000000 --- a/core/src/Utils/Variant.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include <utility> -#include <variant> - -template <class... Ts> -struct Overloaded : Ts... -{ - using Ts::operator()...; -}; -template <class... Ts> -Overloaded(Ts...) -> Overloaded<Ts...>; - -template <class... Args> -struct VariantCastProxy -{ - std::variant<Args...> v; - - template <class... ToArgs> - operator std::variant<ToArgs...>() const - { - return std::visit( - [](auto&& arg) -> std::variant<ToArgs...> { return arg; }, - v); - } -}; - -/// Use snake_case naming to align with `static_cast`, `dynamic_cast`, etc.. -template <class... Args> -auto variant_cast(std::variant<Args...> v) -> VariantCastProxy<Args...> -{ - return { std::move(v) }; -} diff --git a/core/src/Utils/Vector.hpp b/core/src/Utils/Vector.hpp deleted file mode 100644 index 4d3f3b3..0000000 --- a/core/src/Utils/Vector.hpp +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include "Utils/IO/DataStream.hpp" - -template <class T> -struct Vec2 -{ - T x = 0; - T y = 0; - - template <class TTarget> - Vec2<TTarget> Cast() const - { - return { - static_cast<TTarget>(x), - static_cast<TTarget>(y), - }; - } - - void ReadFromDataStream(InputDataStream& stream) - { - stream.Value(x); - stream.Value(y); - } - - void WriteToDataStream(OutputDataStream& stream) const - { - stream.Value(x); - stream.Value(y); - } - - friend constexpr bool operator==(const Vec2& a, const Vec2& b) = default; - - friend constexpr Vec2 operator+(const Vec2& a, const Vec2& b) { return { a.x + b.x, a.y + b.y }; } - friend constexpr Vec2 operator-(const Vec2& a, const Vec2& b) { return { a.x - b.x, a.y - b.y }; } - friend constexpr Vec2 operator*(const Vec2& a, const Vec2& b) { return { a.x * b.x, a.y * b.y }; } - friend constexpr Vec2 operator/(const Vec2& a, const Vec2& b) { return { a.x / b.x, a.y / b.y }; } - - friend constexpr Vec2 operator+(const Vec2& a, T n) { return { a.x + n, a.y + n }; } - friend constexpr Vec2 operator-(const Vec2& a, T n) { return { a.x - n, a.y - n }; } - friend constexpr Vec2 operator*(const Vec2& a, T n) { return { a.x * n, a.y * n }; } - friend constexpr Vec2 operator/(const Vec2& a, T n) { return { a.x / n, a.y / n }; } -}; - -using Vec2i = Vec2<int>; -using Vec2f = Vec2<float>; - -template <class T> -struct Vec3 -{ - T x = 0; - T y = 0; - T z = 0; - - template <class TTarget> - Vec3<TTarget> Cast() const - { - return { - static_cast<TTarget>(x), - static_cast<TTarget>(y), - static_cast<TTarget>(z), - }; - } - - void ReadFromDataStream(InputDataStream& stream) - { - stream.Value(x); - stream.Value(y); - stream.Value(z); - } - - void WriteToDataStream(OutputDataStream& stream) const - { - stream.Value(x); - stream.Value(y); - stream.Value(z); - } - - friend constexpr bool operator==(const Vec3& a, const Vec3& b) = default; - - friend constexpr Vec3 operator+(const Vec3& a, const Vec3& b) { return { a.x + b.x, a.y + b.y, a.z + b.z }; } - friend constexpr Vec3 operator-(const Vec3& a, const Vec3& b) { return { a.x - b.x, a.y - b.y, a.z - b.z }; } - friend constexpr Vec3 operator*(const Vec3& a, const Vec3& b) { return { a.x * b.x, a.y * b.y, a.z * b.z }; } - friend constexpr Vec3 operator/(const Vec3& a, const Vec3& b) { return { a.x / b.x, a.y / b.y, a.z / b.z }; } - - friend constexpr Vec3 operator+(const Vec3& a, T n) { return { a.x + n, a.y + n, a.z + n }; } - friend constexpr Vec3 operator-(const Vec3& a, T n) { return { a.x - n, a.y - n, a.z - n }; } - friend constexpr Vec3 operator*(const Vec3& a, T n) { return { a.x * n, a.y * n, a.z * n }; } - friend constexpr Vec3 operator/(const Vec3& a, T n) { return { a.x / n, a.y / n, a.z / n }; } -}; - -using Vec3i = Vec3<int>; -using Vec3f = Vec3<float>; - -template <class T> -struct Vec4 -{ - T x = 0; - T y = 0; - T z = 0; - T w = 0; - - template <class TTarget> - Vec4<TTarget> Cast() const - { - return { - static_cast<TTarget>(x), - static_cast<TTarget>(y), - static_cast<TTarget>(z), - static_cast<TTarget>(w), - }; - } - - void ReadFromDataStream(InputDataStream& stream) - { - stream.Value(x); - stream.Value(y); - stream.Value(z); - stream.Value(w); - } - - void WriteToDataStream(OutputDataStream& stream) const - { - stream.Value(x); - stream.Value(y); - stream.Value(z); - stream.Value(w); - } - - friend constexpr bool operator==(const Vec4& a, const Vec4& b) = default; - - friend constexpr Vec4 operator+(const Vec4& a, const Vec4& b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } - friend constexpr Vec4 operator-(const Vec4& a, const Vec4& b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } - friend constexpr Vec4 operator*(const Vec4& a, const Vec4& b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } - friend constexpr Vec4 operator/(const Vec4& a, const Vec4& b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } - - friend constexpr Vec4 operator+(const Vec4& a, T n) { return { a.x + n, a.y + n, a.z + n, a.w + n }; } - friend constexpr Vec4 operator-(const Vec4& a, T n) { return { a.x - n, a.y - n, a.z - n, a.w - n }; } - friend constexpr Vec4 operator*(const Vec4& a, T n) { return { a.x * n, a.y * n, a.z * n, a.w * n }; } - friend constexpr Vec4 operator/(const Vec4& a, T n) { return { a.x / n, a.y / n, a.z / n, a.w / n }; } -}; - -using Vec4i = Vec4<int>; -using Vec4f = Vec4<float>; diff --git a/core/src/Utils/VectorHash.hpp b/core/src/Utils/VectorHash.hpp deleted file mode 100644 index 7df9c35..0000000 --- a/core/src/Utils/VectorHash.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "Utils/Hash.hpp" -#include "Utils/Vector.hpp" - -#include <cstddef> -#include <functional> - -template <class T> -struct std::hash<Vec2<T>> -{ - size_t operator()(const Vec2<T>& vec) const - { - size_t result; - HashUtils::Combine(result, vec.x); - HashUtils::Combine(result, vec.y); - return result; - } -}; - -template <class T> -struct std::hash<Vec3<T>> -{ - size_t operator()(const Vec3<T>& vec) const - { - size_t result; - HashUtils::Combine(result, vec.x); - HashUtils::Combine(result, vec.y); - HashUtils::Combine(result, vec.z); - return result; - } -}; - -template <class T> -struct std::hash<Vec4<T>> -{ - size_t operator()(const Vec4<T>& vec) const - { - size_t result; - HashUtils::Combine(result, vec.x); - HashUtils::Combine(result, vec.y); - HashUtils::Combine(result, vec.z); - HashUtils::Combine(result, vec.w); - return result; - } -}; diff --git a/core/src/Utils/fwd.hpp b/core/src/Utils/fwd.hpp deleted file mode 100644 index c740bd1..0000000 --- a/core/src/Utils/fwd.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Utils/IO/fwd.hpp" - -// Color.hpp -class RgbaColor; -class HsvColor; - -// Sigslot.hpp -class SignalStub; -template <class... TArgs> -class Signal; -class SlotGuard; - -// String.hpp -class Utf8Iterator; -class Utf8IterableString; diff --git a/core/src/cplt_fwd.hpp b/core/src/cplt_fwd.hpp deleted file mode 100644 index e05eb4f..0000000 --- a/core/src/cplt_fwd.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "Model/fwd.hpp" -#include "UI/fwd.hpp" -#include "Utils/fwd.hpp" |