aboutsummaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-06-30 21:38:53 -0700
committerrtk0c <[email protected]>2022-06-30 21:38:53 -0700
commit7fe47a9d5b1727a61dc724523b530762f6d6ba19 (patch)
treee95be6e66db504ed06d00b72c579565bab873277 /core/src
parent2cf952088d375ac8b2f45b144462af0953436cff (diff)
Restructure project
Diffstat (limited to 'core/src')
-rw-r--r--core/src/Entrypoint/Backend.hpp23
-rw-r--r--core/src/Entrypoint/Backend_DirectX11.cpp250
-rw-r--r--core/src/Entrypoint/Backend_DirectX12.cpp470
-rw-r--r--core/src/Entrypoint/Backend_Metal.mm40
-rw-r--r--core/src/Entrypoint/Backend_OpenGL2.cpp106
-rw-r--r--core/src/Entrypoint/Backend_OpenGL3.cpp121
-rw-r--r--core/src/Entrypoint/Backend_Vulkan.cpp438
-rw-r--r--core/src/Entrypoint/main.cpp163
-rw-r--r--core/src/Locale/zh_CN.h159
-rw-r--r--core/src/Model/Assets.cpp306
-rw-r--r--core/src/Model/Assets.hpp130
-rw-r--r--core/src/Model/Database.cpp163
-rw-r--r--core/src/Model/Database.hpp79
-rw-r--r--core/src/Model/Filter.cpp1
-rw-r--r--core/src/Model/Filter.hpp6
-rw-r--r--core/src/Model/GlobalStates.cpp163
-rw-r--r--core/src/Model/GlobalStates.hpp55
-rw-r--r--core/src/Model/Items.cpp114
-rw-r--r--core/src/Model/Items.hpp253
-rw-r--r--core/src/Model/Project.cpp168
-rw-r--r--core/src/Model/Project.hpp57
-rw-r--r--core/src/Model/Template/TableTemplate.cpp591
-rw-r--r--core/src/Model/Template/TableTemplate.hpp223
-rw-r--r--core/src/Model/Template/TableTemplateIterator.cpp52
-rw-r--r--core/src/Model/Template/TableTemplateIterator.hpp35
-rw-r--r--core/src/Model/Template/Template.hpp68
-rw-r--r--core/src/Model/Template/Template_Main.cpp214
-rw-r--r--core/src/Model/Template/Template_RTTI.cpp29
-rw-r--r--core/src/Model/Template/fwd.hpp11
-rw-r--r--core/src/Model/Workflow/Evaluation.cpp174
-rw-r--r--core/src/Model/Workflow/Evaluation.hpp67
-rw-r--r--core/src/Model/Workflow/Nodes/DocumentNodes.cpp18
-rw-r--r--core/src/Model/Workflow/Nodes/DocumentNodes.hpp13
-rw-r--r--core/src/Model/Workflow/Nodes/NumericNodes.cpp94
-rw-r--r--core/src/Model/Workflow/Nodes/NumericNodes.hpp44
-rw-r--r--core/src/Model/Workflow/Nodes/TextNodes.cpp231
-rw-r--r--core/src/Model/Workflow/Nodes/TextNodes.hpp53
-rw-r--r--core/src/Model/Workflow/Nodes/UserInputNodes.cpp32
-rw-r--r--core/src/Model/Workflow/Nodes/UserInputNodes.hpp23
-rw-r--r--core/src/Model/Workflow/Nodes/fwd.hpp15
-rw-r--r--core/src/Model/Workflow/Value.hpp94
-rw-r--r--core/src/Model/Workflow/ValueInternals.hpp21
-rw-r--r--core/src/Model/Workflow/Value_Main.cpp35
-rw-r--r--core/src/Model/Workflow/Value_RTTI.cpp174
-rw-r--r--core/src/Model/Workflow/Values/Basic.cpp111
-rw-r--r--core/src/Model/Workflow/Values/Basic.hpp67
-rw-r--r--core/src/Model/Workflow/Values/Database.cpp88
-rw-r--r--core/src/Model/Workflow/Values/Database.hpp51
-rw-r--r--core/src/Model/Workflow/Values/Dictionary.cpp49
-rw-r--r--core/src/Model/Workflow/Values/Dictionary.hpp25
-rw-r--r--core/src/Model/Workflow/Values/List.cpp100
-rw-r--r--core/src/Model/Workflow/Values/List.hpp50
-rw-r--r--core/src/Model/Workflow/Values/fwd.hpp17
-rw-r--r--core/src/Model/Workflow/Workflow.hpp316
-rw-r--r--core/src/Model/Workflow/Workflow_Main.cpp846
-rw-r--r--core/src/Model/Workflow/Workflow_RTTI.cpp143
-rw-r--r--core/src/Model/Workflow/fwd.hpp22
-rw-r--r--core/src/Model/fwd.hpp35
-rw-r--r--core/src/UI/UI.hpp48
-rw-r--r--core/src/UI/UI_DatabaseView.cpp668
-rw-r--r--core/src/UI/UI_Items.cpp252
-rw-r--r--core/src/UI/UI_MainWindow.cpp237
-rw-r--r--core/src/UI/UI_Settings.cpp8
-rw-r--r--core/src/UI/UI_Templates.cpp977
-rw-r--r--core/src/UI/UI_Utils.cpp315
-rw-r--r--core/src/UI/UI_Workflows.cpp293
-rw-r--r--core/src/UI/fwd.hpp6
-rw-r--r--core/src/Utils/Color.hpp202
-rw-r--r--core/src/Utils/Hash.hpp15
-rw-r--r--core/src/Utils/I18n.hpp10
-rw-r--r--core/src/Utils/IO/Archive.cpp57
-rw-r--r--core/src/Utils/IO/Archive.hpp18
-rw-r--r--core/src/Utils/IO/CstdioFile.cpp36
-rw-r--r--core/src/Utils/IO/CstdioFile.hpp17
-rw-r--r--core/src/Utils/IO/DataStream.cpp283
-rw-r--r--core/src/Utils/IO/DataStream.hpp210
-rw-r--r--core/src/Utils/IO/FileStream.cpp7
-rw-r--r--core/src/Utils/IO/FileStream.hpp97
-rw-r--r--core/src/Utils/IO/FileStream_Cstdio.inl126
-rw-r--r--core/src/Utils/IO/FileStream_Custom.inl358
-rw-r--r--core/src/Utils/IO/Helper.hpp43
-rw-r--r--core/src/Utils/IO/StringIntegration.hpp37
-rw-r--r--core/src/Utils/IO/TslArrayIntegration.hpp50
-rw-r--r--core/src/Utils/IO/TslRobinIntegration.hpp78
-rw-r--r--core/src/Utils/IO/UuidIntegration.hpp27
-rw-r--r--core/src/Utils/IO/VectorIntegration.hpp42
-rw-r--r--core/src/Utils/IO/fwd.hpp13
-rw-r--r--core/src/Utils/Macros.hpp13
-rw-r--r--core/src/Utils/Math.hpp11
-rw-r--r--core/src/Utils/RTTI.hpp49
-rw-r--r--core/src/Utils/ScopeGuard.hpp39
-rw-r--r--core/src/Utils/Sigslot.cpp233
-rw-r--r--core/src/Utils/Sigslot.hpp165
-rw-r--r--core/src/Utils/Size.hpp65
-rw-r--r--core/src/Utils/StandardDirectories.cpp78
-rw-r--r--core/src/Utils/StandardDirectories.hpp10
-rw-r--r--core/src/Utils/Time.cpp29
-rw-r--r--core/src/Utils/Time.hpp11
-rw-r--r--core/src/Utils/UUID.hpp5
-rw-r--r--core/src/Utils/Variant.hpp33
-rw-r--r--core/src/Utils/Vector.hpp144
-rw-r--r--core/src/Utils/VectorHash.hpp46
-rw-r--r--core/src/Utils/fwd.hpp17
-rw-r--r--core/src/cplt_fwd.hpp5
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 = &paramPalette;
- 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"