feat: Add texture support to UI widgets and update gitignore
UI Widget Enhancements: - Add texture support to UICheckbox (box and checkmark textures) - Add texture support to UISlider (track and handle textures) - Add texture support to UIPanel (background texture) - Add texture support to UIProgressBar (background and fill textures) - Add texture support to UIScrollPanel (background and scrollbar textures) - All widgets now support textureId with tint color for flexible styling BgfxRenderer: - Add texture loading helpers for widget texturing - Update RHI device for texture management - Add ResourceCache texture ID support Maintenance: - Add tmpclaude-* to .gitignore (temporary Claude Code directories) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0441a9d648
commit
b39854cf2c
1
.gitignore
vendored
1
.gitignore
vendored
@ -44,3 +44,4 @@ desktop.ini
|
||||
*.swp
|
||||
*~
|
||||
nul
|
||||
tmpclaude-*
|
||||
|
||||
@ -23,6 +23,14 @@ namespace grove {
|
||||
BgfxRendererModule::BgfxRendererModule() = default;
|
||||
BgfxRendererModule::~BgfxRendererModule() = default;
|
||||
|
||||
ResourceCache* BgfxRendererModule::getResourceCache() const {
|
||||
return m_resourceCache.get();
|
||||
}
|
||||
|
||||
rhi::IRHIDevice* BgfxRendererModule::getDevice() const {
|
||||
return m_device.get();
|
||||
}
|
||||
|
||||
void BgfxRendererModule::setConfiguration(const IDataNode& config, IIO* io, ITaskScheduler* scheduler) {
|
||||
m_io = io;
|
||||
|
||||
|
||||
@ -45,6 +45,13 @@ public:
|
||||
std::string getType() const override { return "bgfx_renderer"; }
|
||||
bool isIdle() const override { return true; }
|
||||
|
||||
// ========================================
|
||||
// Public API for external access
|
||||
// ========================================
|
||||
|
||||
ResourceCache* getResourceCache() const;
|
||||
rhi::IRHIDevice* getDevice() const;
|
||||
|
||||
private:
|
||||
// Logger
|
||||
std::shared_ptr<spdlog::logger> m_logger;
|
||||
|
||||
@ -205,9 +205,13 @@ public:
|
||||
}
|
||||
|
||||
UniformHandle createUniform(const char* name, uint8_t numVec4s) override {
|
||||
// Detect sampler uniforms by name prefix (bgfx convention: s_*)
|
||||
bool isSampler = (name[0] == 's' && name[1] == '_');
|
||||
|
||||
bgfx::UniformHandle uniform = bgfx::createUniform(
|
||||
name,
|
||||
numVec4s == 1 ? bgfx::UniformType::Vec4 : bgfx::UniformType::Mat4
|
||||
isSampler ? bgfx::UniformType::Sampler :
|
||||
(numVec4s == 1 ? bgfx::UniformType::Vec4 : bgfx::UniformType::Mat4)
|
||||
);
|
||||
|
||||
UniformHandle result;
|
||||
@ -557,6 +561,15 @@ public:
|
||||
if (hasTexture) {
|
||||
bgfx::TextureHandle tex = { pendingTexture.id };
|
||||
bgfx::UniformHandle sampler = { pendingSampler.id };
|
||||
|
||||
static int submitCount = 0;
|
||||
if (submitCount < 10) {
|
||||
spdlog::info("[Submit #{}] BgfxDevice::submit() - pendingTexture.id={}, tex.idx={}, sampler.idx={}, slot={}",
|
||||
submitCount, pendingTexture.id, tex.idx, sampler.idx, pendingTextureSlot);
|
||||
spdlog::info(" bgfx::isValid(tex): {}", bgfx::isValid(tex));
|
||||
submitCount++;
|
||||
}
|
||||
|
||||
bgfx::setTexture(pendingTextureSlot, sampler, tex);
|
||||
}
|
||||
bgfx::ProgramHandle program = { cmd.submit.shader.id };
|
||||
|
||||
@ -31,6 +31,10 @@ public:
|
||||
// Loading (called from main thread) - returns texture ID
|
||||
uint16_t loadTextureWithId(rhi::IRHIDevice& device, const std::string& path);
|
||||
|
||||
// Register an already-created texture (for runtime/procedural textures)
|
||||
// Returns the assigned texture ID
|
||||
uint16_t registerTexture(rhi::TextureHandle handle, const std::string& name = "");
|
||||
|
||||
// Legacy loading (returns handle directly)
|
||||
rhi::TextureHandle loadTexture(rhi::IRHIDevice& device, const std::string& path);
|
||||
rhi::ShaderHandle loadShader(rhi::IRHIDevice& device, const std::string& name,
|
||||
|
||||
@ -86,6 +86,9 @@ void UITree::registerDefaultWidgets() {
|
||||
style.borderRadius = static_cast<float>(styleNode->getDouble("borderRadius", style.borderRadius));
|
||||
style.textureId = styleNode->getInt("textureId", 0);
|
||||
style.useTexture = style.textureId > 0;
|
||||
if (style.textureId > 0) {
|
||||
spdlog::info("UIButton style parsed: textureId={}, useTexture={}", style.textureId, style.useTexture);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse style (const_cast safe for read-only operations)
|
||||
|
||||
@ -34,22 +34,42 @@ void UICheckbox::render(UIRenderer& renderer) {
|
||||
// Box background (retained mode)
|
||||
int boxLayer = renderer.nextLayer();
|
||||
uint32_t currentBoxColor = isHovered ? 0x475569FF : boxColor;
|
||||
renderer.updateRect(m_renderId, boxX, boxY, boxSize, boxSize, currentBoxColor, boxLayer);
|
||||
|
||||
if (useBoxTexture && boxTextureId > 0) {
|
||||
renderer.updateSprite(m_renderId, boxX, boxY, boxSize, boxSize, boxTextureId, boxTintColor, boxLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_renderId, boxX, boxY, boxSize, boxSize, currentBoxColor, boxLayer);
|
||||
}
|
||||
|
||||
// Check mark if checked (retained mode)
|
||||
int checkLayer = renderer.nextLayer();
|
||||
if (checked) {
|
||||
// Draw a smaller filled rect as checkmark
|
||||
float checkPadding = boxSize * 0.25f;
|
||||
renderer.updateRect(
|
||||
m_checkRenderId,
|
||||
boxX + checkPadding,
|
||||
boxY + checkPadding,
|
||||
boxSize - checkPadding * 2,
|
||||
boxSize - checkPadding * 2,
|
||||
checkColor,
|
||||
checkLayer
|
||||
);
|
||||
|
||||
if (useCheckTexture && checkTextureId > 0) {
|
||||
// Draw checkmark texture
|
||||
renderer.updateSprite(
|
||||
m_checkRenderId,
|
||||
boxX + checkPadding,
|
||||
boxY + checkPadding,
|
||||
boxSize - checkPadding * 2,
|
||||
boxSize - checkPadding * 2,
|
||||
checkTextureId,
|
||||
checkTintColor,
|
||||
checkLayer
|
||||
);
|
||||
} else {
|
||||
// Draw a smaller filled rect as checkmark
|
||||
renderer.updateRect(
|
||||
m_checkRenderId,
|
||||
boxX + checkPadding,
|
||||
boxY + checkPadding,
|
||||
boxSize - checkPadding * 2,
|
||||
boxSize - checkPadding * 2,
|
||||
checkColor,
|
||||
checkLayer
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Hide checkmark when unchecked (zero-size rect)
|
||||
renderer.updateRect(m_checkRenderId, 0, 0, 0, 0, 0x00000000, checkLayer);
|
||||
|
||||
@ -49,6 +49,15 @@ public:
|
||||
float fontSize = 16.0f;
|
||||
float spacing = 8.0f; // Space between box and text
|
||||
|
||||
// Texture support
|
||||
int boxTextureId = 0; // Box texture ID (0 = solid color)
|
||||
bool useBoxTexture = false; // Use texture for box
|
||||
uint32_t boxTintColor = 0xFFFFFFFF; // Tint for box texture
|
||||
|
||||
int checkTextureId = 0; // Checkmark texture ID (0 = solid rect)
|
||||
bool useCheckTexture = false; // Use texture for checkmark
|
||||
uint32_t checkTintColor = 0xFFFFFFFF; // Tint for checkmark texture
|
||||
|
||||
// State
|
||||
bool isHovered = false;
|
||||
bool isPressed = false;
|
||||
|
||||
@ -31,7 +31,16 @@ void UIPanel::render(UIRenderer& renderer) {
|
||||
|
||||
// Retained mode: only publish if changed
|
||||
int layer = renderer.nextLayer();
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, bgColor, layer);
|
||||
|
||||
// Check if fully transparent (alpha channel = 0)
|
||||
bool isFullyTransparent = (bgColor & 0xFF) == 0;
|
||||
|
||||
// Render background (texture or solid color) - skip if fully transparent
|
||||
if (useTexture && textureId > 0) {
|
||||
renderer.updateSprite(m_renderId, absX, absY, width, height, textureId, tintColor, layer);
|
||||
} else if (!isFullyTransparent) {
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, bgColor, layer);
|
||||
}
|
||||
|
||||
// Render children on top
|
||||
renderChildren(renderer);
|
||||
|
||||
@ -25,6 +25,11 @@ public:
|
||||
float borderRadius = 0.0f; // For future use
|
||||
float borderWidth = 0.0f;
|
||||
uint32_t borderColor = 0x000000FF;
|
||||
|
||||
// Texture support
|
||||
int textureId = 0; // Texture ID (0 = solid color)
|
||||
bool useTexture = false; // Use texture instead of solid color
|
||||
uint32_t tintColor = 0xFFFFFFFF; // RGBA tint for texture (white = no tint)
|
||||
};
|
||||
|
||||
} // namespace grove
|
||||
|
||||
@ -30,16 +30,28 @@ void UIProgressBar::render(UIRenderer& renderer) {
|
||||
|
||||
// Retained mode: only publish if changed
|
||||
int bgLayer = renderer.nextLayer();
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, bgColor, bgLayer);
|
||||
if (useBgTexture && bgTextureId > 0) {
|
||||
renderer.updateSprite(m_renderId, absX, absY, width, height, bgTextureId, bgTintColor, bgLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, bgColor, bgLayer);
|
||||
}
|
||||
|
||||
// Render fill based on progress
|
||||
int fillLayer = renderer.nextLayer();
|
||||
if (horizontal) {
|
||||
float fillWidth = progress * width;
|
||||
renderer.updateRect(m_fillRenderId, absX, absY, fillWidth, height, fillColor, fillLayer);
|
||||
if (useFillTexture && fillTextureId > 0) {
|
||||
renderer.updateSprite(m_fillRenderId, absX, absY, fillWidth, height, fillTextureId, fillTintColor, fillLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_fillRenderId, absX, absY, fillWidth, height, fillColor, fillLayer);
|
||||
}
|
||||
} else {
|
||||
float fillHeight = progress * height;
|
||||
renderer.updateRect(m_fillRenderId, absX, absY + height - fillHeight, width, fillHeight, fillColor, fillLayer);
|
||||
if (useFillTexture && fillTextureId > 0) {
|
||||
renderer.updateSprite(m_fillRenderId, absX, absY + height - fillHeight, width, fillHeight, fillTextureId, fillTintColor, fillLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_fillRenderId, absX, absY + height - fillHeight, width, fillHeight, fillColor, fillLayer);
|
||||
}
|
||||
}
|
||||
|
||||
// Render percentage text if enabled
|
||||
|
||||
@ -42,6 +42,15 @@ public:
|
||||
uint32_t textColor = 0xFFFFFFFF;
|
||||
float fontSize = 14.0f;
|
||||
|
||||
// Texture support
|
||||
int bgTextureId = 0; // Background texture ID (0 = solid color)
|
||||
bool useBgTexture = false; // Use texture for background
|
||||
uint32_t bgTintColor = 0xFFFFFFFF; // Tint for background texture
|
||||
|
||||
int fillTextureId = 0; // Fill texture ID (0 = solid color)
|
||||
bool useFillTexture = false; // Use texture for fill
|
||||
uint32_t fillTintColor = 0xFFFFFFFF; // Tint for fill texture
|
||||
|
||||
private:
|
||||
// Retained mode render IDs
|
||||
uint32_t m_fillRenderId = 0; // Separate ID for fill bar element
|
||||
|
||||
@ -72,7 +72,11 @@ void UIScrollPanel::render(UIRenderer& renderer) {
|
||||
|
||||
// Render background
|
||||
int bgLayer = renderer.nextLayer();
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, bgColor, bgLayer);
|
||||
if (useBgTexture && bgTextureId > 0) {
|
||||
renderer.updateSprite(m_renderId, absX, absY, width, height, bgTextureId, bgTintColor, bgLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, bgColor, bgLayer);
|
||||
}
|
||||
|
||||
// Render border if needed
|
||||
if (borderWidth > 0.0f) {
|
||||
@ -225,7 +229,12 @@ void UIScrollPanel::renderScrollbar(UIRenderer& renderer) {
|
||||
// Render scrollbar background track
|
||||
float trackX = absX + width - scrollbarWidth;
|
||||
int trackLayer = renderer.nextLayer();
|
||||
renderer.updateRect(m_scrollTrackId, trackX, absY, scrollbarWidth, height, scrollbarBgColor, trackLayer);
|
||||
|
||||
if (useScrollbarTrackTexture && scrollbarTrackTextureId > 0) {
|
||||
renderer.updateSprite(m_scrollTrackId, trackX, absY, scrollbarWidth, height, scrollbarTrackTextureId, scrollbarTrackTintColor, trackLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_scrollTrackId, trackX, absY, scrollbarWidth, height, scrollbarBgColor, trackLayer);
|
||||
}
|
||||
|
||||
// Render scrollbar thumb
|
||||
float sbX, sbY, sbW, sbH;
|
||||
@ -233,7 +242,12 @@ void UIScrollPanel::renderScrollbar(UIRenderer& renderer) {
|
||||
|
||||
// Use hover color if hovered (would need ctx passed to render, simplified for now)
|
||||
int thumbLayer = renderer.nextLayer();
|
||||
renderer.updateRect(m_scrollThumbId, sbX, sbY, sbW, sbH, scrollbarColor, thumbLayer);
|
||||
|
||||
if (useScrollbarThumbTexture && scrollbarThumbTextureId > 0) {
|
||||
renderer.updateSprite(m_scrollThumbId, sbX, sbY, sbW, sbH, scrollbarThumbTextureId, scrollbarThumbTintColor, thumbLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_scrollThumbId, sbX, sbY, sbW, sbH, scrollbarColor, thumbLayer);
|
||||
}
|
||||
}
|
||||
|
||||
void UIScrollPanel::updateScrollInteraction(UIContext& ctx) {
|
||||
|
||||
@ -48,6 +48,19 @@ public:
|
||||
float borderWidth = 1.0f;
|
||||
uint32_t borderColor = 0x444444FF;
|
||||
|
||||
// Texture support
|
||||
int bgTextureId = 0; // Background texture ID (0 = solid color)
|
||||
bool useBgTexture = false; // Use texture for background
|
||||
uint32_t bgTintColor = 0xFFFFFFFF; // Tint for background texture
|
||||
|
||||
int scrollbarTrackTextureId = 0; // Scrollbar track texture ID (0 = solid color)
|
||||
bool useScrollbarTrackTexture = false; // Use texture for scrollbar track
|
||||
uint32_t scrollbarTrackTintColor = 0xFFFFFFFF; // Tint for scrollbar track texture
|
||||
|
||||
int scrollbarThumbTextureId = 0; // Scrollbar thumb texture ID (0 = solid color)
|
||||
bool useScrollbarThumbTexture = false; // Use texture for scrollbar thumb
|
||||
uint32_t scrollbarThumbTintColor = 0xFFFFFFFF; // Tint for scrollbar thumb texture
|
||||
|
||||
// Interaction state
|
||||
bool isDraggingContent = false;
|
||||
bool isDraggingScrollbar = false;
|
||||
|
||||
@ -38,16 +38,28 @@ void UISlider::render(UIRenderer& renderer) {
|
||||
|
||||
// Render track (background)
|
||||
int trackLayer = renderer.nextLayer();
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, trackColor, trackLayer);
|
||||
if (useTrackTexture && trackTextureId > 0) {
|
||||
renderer.updateSprite(m_renderId, absX, absY, width, height, trackTextureId, trackTintColor, trackLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_renderId, absX, absY, width, height, trackColor, trackLayer);
|
||||
}
|
||||
|
||||
// Render fill (progress)
|
||||
int fillLayer = renderer.nextLayer();
|
||||
if (horizontal) {
|
||||
float fillWidth = (value - minValue) / (maxValue - minValue) * width;
|
||||
renderer.updateRect(m_fillRenderId, absX, absY, fillWidth, height, fillColor, fillLayer);
|
||||
if (useFillTexture && fillTextureId > 0) {
|
||||
renderer.updateSprite(m_fillRenderId, absX, absY, fillWidth, height, fillTextureId, fillTintColor, fillLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_fillRenderId, absX, absY, fillWidth, height, fillColor, fillLayer);
|
||||
}
|
||||
} else {
|
||||
float fillHeight = (value - minValue) / (maxValue - minValue) * height;
|
||||
renderer.updateRect(m_fillRenderId, absX, absY + height - fillHeight, width, fillHeight, fillColor, fillLayer);
|
||||
if (useFillTexture && fillTextureId > 0) {
|
||||
renderer.updateSprite(m_fillRenderId, absX, absY + height - fillHeight, width, fillHeight, fillTextureId, fillTintColor, fillLayer);
|
||||
} else {
|
||||
renderer.updateRect(m_fillRenderId, absX, absY + height - fillHeight, width, fillHeight, fillColor, fillLayer);
|
||||
}
|
||||
}
|
||||
|
||||
// Render handle
|
||||
@ -57,15 +69,29 @@ void UISlider::render(UIRenderer& renderer) {
|
||||
// Handle is a small square
|
||||
int handleLayer = renderer.nextLayer();
|
||||
float halfHandle = handleSize * 0.5f;
|
||||
renderer.updateRect(
|
||||
m_handleRenderId,
|
||||
handleX - halfHandle,
|
||||
handleY - halfHandle,
|
||||
handleSize,
|
||||
handleSize,
|
||||
handleColor,
|
||||
handleLayer
|
||||
);
|
||||
|
||||
if (useHandleTexture && handleTextureId > 0) {
|
||||
renderer.updateSprite(
|
||||
m_handleRenderId,
|
||||
handleX - halfHandle,
|
||||
handleY - halfHandle,
|
||||
handleSize,
|
||||
handleSize,
|
||||
handleTextureId,
|
||||
handleTintColor,
|
||||
handleLayer
|
||||
);
|
||||
} else {
|
||||
renderer.updateRect(
|
||||
m_handleRenderId,
|
||||
handleX - halfHandle,
|
||||
handleY - halfHandle,
|
||||
handleSize,
|
||||
handleSize,
|
||||
handleColor,
|
||||
handleLayer
|
||||
);
|
||||
}
|
||||
|
||||
// Render children on top
|
||||
renderChildren(renderer);
|
||||
|
||||
@ -61,6 +61,19 @@ public:
|
||||
uint32_t handleColor = 0xecf0f1FF;
|
||||
float handleSize = 16.0f;
|
||||
|
||||
// Texture support
|
||||
int trackTextureId = 0; // Track texture ID (0 = solid color)
|
||||
bool useTrackTexture = false; // Use texture for track
|
||||
uint32_t trackTintColor = 0xFFFFFFFF; // Tint for track texture
|
||||
|
||||
int fillTextureId = 0; // Fill texture ID (0 = solid color)
|
||||
bool useFillTexture = false; // Use texture for fill
|
||||
uint32_t fillTintColor = 0xFFFFFFFF; // Tint for fill texture
|
||||
|
||||
int handleTextureId = 0; // Handle texture ID (0 = solid color)
|
||||
bool useHandleTexture = false; // Use texture for handle
|
||||
uint32_t handleTintColor = 0xFFFFFFFF; // Tint for handle texture
|
||||
|
||||
// State
|
||||
bool isDragging = false;
|
||||
bool isHovered = false;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user