new UI system
This commit is contained in:
parent
d840a81448
commit
216972e73a
82 changed files with 4837 additions and 3243 deletions
166
implementations/Crafter.Graphics-UIScene.cpp
Normal file
166
implementations/Crafter.Graphics-UIScene.cpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
Crafter®.Graphics
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
catcrafts.net
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License version 3.0 as published by the Free Software Foundation;
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
module;
|
||||
#include "vulkan/vulkan.h"
|
||||
module Crafter.Graphics:UIScene_impl;
|
||||
import :UIScene;
|
||||
import :Window;
|
||||
import :Types;
|
||||
import :DescriptorHeapVulkan;
|
||||
import :UIRenderer;
|
||||
import :UIHit;
|
||||
import :UILayout;
|
||||
import :UIDrawList;
|
||||
import :UIWidget;
|
||||
import Crafter.Event;
|
||||
import std;
|
||||
|
||||
using namespace Crafter;
|
||||
using namespace Crafter::UI;
|
||||
|
||||
UIScene::~UIScene() {
|
||||
// Release listeners before the rest of the scene tears down.
|
||||
mouseListener_.reset();
|
||||
updateListener_.reset();
|
||||
textListener_.reset();
|
||||
keyListener_.reset();
|
||||
focused_ = nullptr;
|
||||
|
||||
if (window_) {
|
||||
// De-register the renderer pass.
|
||||
auto& v = window_->passes;
|
||||
v.erase(std::remove(v.begin(), v.end(), static_cast<RenderPass*>(&renderer)), v.end());
|
||||
|
||||
// Clear the descriptor-heap pointer if we owned it; the heap's
|
||||
// destructor releases its Vulkan buffers on its own.
|
||||
if (ownsHeap_ && window_->descriptorHeap == &ownedHeap_) {
|
||||
window_->descriptorHeap = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float UIScene::WindowScale() const {
|
||||
if (!window_) return 1.0f;
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
return window_->scale;
|
||||
#else
|
||||
return 1.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UIScene::Initialize(Window& window, const std::filesystem::path& spvPath) {
|
||||
window_ = &window;
|
||||
|
||||
// Auto-create a heap for UI-only apps. Generous defaults so most
|
||||
// user-augmented heaps will fit too — if the user wants to share with
|
||||
// 3D content, they should pre-create their own heap and attach it
|
||||
// before calling Initialize.
|
||||
if (!window.descriptorHeap) {
|
||||
ownedHeap_.Initialize(/*images*/ 388, /*buffers*/ 35, /*samplers*/ 17);
|
||||
window.descriptorHeap = &ownedHeap_;
|
||||
ownsHeap_ = true;
|
||||
}
|
||||
|
||||
// One-shot init — needed by the atlas image transition. Each
|
||||
// StartInit/FinishInit pair reuses the per-frame command buffer.
|
||||
VkCommandBuffer cmd = window.StartInit();
|
||||
renderer.Initialize(window, cmd, spvPath);
|
||||
window.FinishInit();
|
||||
|
||||
// Register as a RenderPass (after any other pass already in
|
||||
// window.passes — typically RTPass for mixed scenes).
|
||||
window.passes.push_back(&renderer);
|
||||
|
||||
// Mouse: update focus to the topmost focusable under the cursor (or
|
||||
// null if none), then dispatch the click via the bubble chain.
|
||||
mouseListener_ = std::make_unique<EventListener<void>>(
|
||||
&window.onMouseLeftClick,
|
||||
[this]() {
|
||||
if (!root_) return;
|
||||
float x = window_->currentMousePos.x;
|
||||
float y = window_->currentMousePos.y;
|
||||
|
||||
Widget* hit = UI::HitTest(*root_, x, y);
|
||||
Widget* focusTarget = nullptr;
|
||||
for (Widget* w = hit; w != nullptr; w = w->parent) {
|
||||
if (w->IsFocusable()) { focusTarget = w; break; }
|
||||
}
|
||||
SetFocus(focusTarget);
|
||||
|
||||
UI::DispatchClick(*root_, x, y);
|
||||
}
|
||||
);
|
||||
|
||||
// Text input: only the currently-focused widget receives it.
|
||||
textListener_ = std::make_unique<EventListener<const std::string_view>>(
|
||||
&window.onTextInput,
|
||||
[this](std::string_view t) {
|
||||
if (focused_) focused_->OnTextInput(t);
|
||||
}
|
||||
);
|
||||
|
||||
// Non-character keys (Backspace, arrows, Enter, …).
|
||||
keyListener_ = std::make_unique<EventListener<CrafterKeys>>(
|
||||
&window.onAnyKeyDown,
|
||||
[this](CrafterKeys key) {
|
||||
if (focused_) focused_->OnKeyDown(key);
|
||||
}
|
||||
);
|
||||
|
||||
// Per-frame: re-layout, emit, push items.
|
||||
updateListener_ = std::make_unique<EventListener<FrameTime>>(
|
||||
&window.onUpdate,
|
||||
[this](FrameTime) { RebuildFrame(); }
|
||||
);
|
||||
}
|
||||
|
||||
void UIScene::SetFocus(Widget* w) {
|
||||
if (w == focused_) return;
|
||||
if (focused_) focused_->OnBlur();
|
||||
focused_ = w;
|
||||
if (focused_) focused_->OnFocus();
|
||||
}
|
||||
|
||||
void UIScene::RebuildFrame() {
|
||||
if (!root_ || !window_) return;
|
||||
float sc = WindowScale();
|
||||
|
||||
// Layout the tree against the current surface size.
|
||||
UI::RunLayout(
|
||||
*root_,
|
||||
{ static_cast<float>(window_->width), static_cast<float>(window_->height) },
|
||||
sc
|
||||
);
|
||||
|
||||
// Emit draw items.
|
||||
drawList.Reset();
|
||||
drawList.atlas = &renderer.atlas;
|
||||
drawList.bindlessBaseHeapIdx = renderer.BindlessBaseHeapIdx();
|
||||
drawList.scale = sc;
|
||||
if (background_) {
|
||||
drawList.AddRect(
|
||||
{ 0, 0, static_cast<float>(window_->width), static_cast<float>(window_->height) },
|
||||
*background_
|
||||
);
|
||||
}
|
||||
UI::EmitTree(*root_, drawList);
|
||||
|
||||
// Stage to GPU.
|
||||
renderer.SetItems(drawList.items);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue