This commit implements Phase 7 of the UIModule, adding advanced features that make the UI system production-ready. ## Phase 7.1 - UIScrollPanel New scrollable container widget with: - Vertical and horizontal scrolling (configurable) - Mouse wheel support with smooth scrolling - Drag-to-scroll functionality (drag content or scrollbar) - Interactive scrollbar with proportional thumb - Automatic content size calculation - Visibility culling for performance - Full styling support (colors, borders, scrollbar) Files added: - modules/UIModule/Widgets/UIScrollPanel.h - modules/UIModule/Widgets/UIScrollPanel.cpp - modules/UIModule/Core/UIContext.h (added mouseWheelDelta) - modules/UIModule/UIModule.cpp (mouse wheel event routing) ## Phase 7.2 - Tooltips Smart tooltip system with: - Hover delay (500ms default) - Automatic positioning with edge avoidance - Semi-transparent background with border - Per-widget tooltip text via JSON - Tooltip property on all UIWidget types - Renders on top of all UI elements Files added: - modules/UIModule/Core/UITooltip.h - modules/UIModule/Core/UITooltip.cpp - modules/UIModule/Core/UIWidget.h (added tooltip property) - modules/UIModule/Core/UITree.cpp (tooltip parsing) ## Tests Added comprehensive visual tests: - test_28_ui_scroll.cpp - ScrollPanel with 35+ items - test_29_ui_advanced.cpp - Tooltips on various widgets - assets/ui/test_scroll.json - ScrollPanel layout - assets/ui/test_tooltips.json - Tooltips layout ## Documentation - docs/UI_MODULE_PHASE7_COMPLETE.md - Complete Phase 7 docs - docs/PROMPT_UI_MODULE_PHASE6.md - Phase 6 & 7 prompt - Updated CMakeLists.txt for new files and tests ## UIModule Status UIModule is now feature-complete with: ✅ 9 widget types (Panel, Label, Button, Image, Slider, Checkbox, ProgressBar, TextInput, ScrollPanel) ✅ Flexible layout system (vertical, horizontal, stack, absolute) ✅ Theme and style system ✅ Complete event system ✅ Tooltips with smart positioning ✅ Hot-reload support ✅ Comprehensive tests (Phases 1-7) 🚀 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
317 lines
10 KiB
C++
317 lines
10 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <functional>
|
|
#include "IDataValue.h"
|
|
|
|
namespace grove {
|
|
|
|
/**
|
|
* @brief Interface for a single node in the data tree
|
|
*
|
|
* Each node can have:
|
|
* - Children nodes (tree navigation)
|
|
* - Its own data blob (IDataValue)
|
|
* - Properties accessible by name with type safety
|
|
*/
|
|
class IDataNode {
|
|
public:
|
|
virtual ~IDataNode() = default;
|
|
|
|
// ========================================
|
|
// TREE NAVIGATION
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Get direct child by name (transfers ownership)
|
|
* @param name Exact name of the child
|
|
* @return Child node or nullptr if not found
|
|
* @warning This removes the child from the tree. Use getChildReadOnly() for non-destructive reads.
|
|
*/
|
|
virtual std::unique_ptr<IDataNode> getChild(const std::string& name) = 0;
|
|
|
|
/**
|
|
* @brief Get direct child by name (read-only, no ownership transfer)
|
|
* @param name Exact name of the child
|
|
* @return Raw pointer to child node or nullptr if not found
|
|
* @note The returned pointer is valid as long as the parent node exists and the child isn't removed.
|
|
*/
|
|
virtual IDataNode* getChildReadOnly(const std::string& name) = 0;
|
|
|
|
/**
|
|
* @brief Get names of all direct children
|
|
* @return Vector of child names
|
|
*/
|
|
virtual std::vector<std::string> getChildNames() = 0;
|
|
|
|
/**
|
|
* @brief Check if this node has any children
|
|
* @return true if children exist
|
|
*/
|
|
virtual bool hasChildren() = 0;
|
|
|
|
/**
|
|
* @brief Check if this node has a direct child with the given name
|
|
* @param name Exact name of the child to check
|
|
* @return true if child exists
|
|
*/
|
|
virtual bool hasChild(const std::string& name) const = 0;
|
|
|
|
// ========================================
|
|
// EXACT SEARCH IN CHILDREN
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Find all children with exact name (direct children only)
|
|
* @param name Exact name to search for
|
|
* @return Vector of matching child nodes
|
|
*/
|
|
virtual std::vector<IDataNode*> getChildrenByName(const std::string& name) = 0;
|
|
|
|
/**
|
|
* @brief Check if any children have the exact name
|
|
* @param name Exact name to search for
|
|
* @return true if found
|
|
*/
|
|
virtual bool hasChildrenByName(const std::string& name) const = 0;
|
|
|
|
/**
|
|
* @brief Get first child with exact name
|
|
* @param name Exact name to search for
|
|
* @return First matching child or nullptr
|
|
*/
|
|
virtual IDataNode* getFirstChildByName(const std::string& name) = 0;
|
|
|
|
// ========================================
|
|
// PATTERN MATCHING SEARCH (DEEP SEARCH IN WHOLE SUBTREE)
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Find all nodes in subtree matching pattern
|
|
* @param pattern Pattern with wildcards (* supported)
|
|
* @return Vector of matching nodes in entire subtree
|
|
*
|
|
* Examples:
|
|
* - "component*" matches "component_armor", "component_engine"
|
|
* - "*heavy*" matches "tank_heavy_mk1", "artillery_heavy"
|
|
* - "model_*" matches "model_01", "model_02"
|
|
*/
|
|
virtual std::vector<IDataNode*> getChildrenByNameMatch(const std::string& pattern) = 0;
|
|
|
|
/**
|
|
* @brief Check if any nodes in subtree match pattern
|
|
* @param pattern Pattern with wildcards
|
|
* @return true if any matches found
|
|
*/
|
|
virtual bool hasChildrenByNameMatch(const std::string& pattern) const = 0;
|
|
|
|
/**
|
|
* @brief Get first node in subtree matching pattern
|
|
* @param pattern Pattern with wildcards
|
|
* @return First matching node or nullptr
|
|
*/
|
|
virtual IDataNode* getFirstChildByNameMatch(const std::string& pattern) = 0;
|
|
|
|
// ========================================
|
|
// QUERY BY PROPERTIES
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Query nodes in subtree by property value
|
|
* @param propName Property name to check
|
|
* @param predicate Function to test property value
|
|
* @return Vector of nodes where predicate returns true
|
|
*
|
|
* Example:
|
|
* // Find all tanks with armor > 150
|
|
* queryByProperty("armor", [](const IDataValue& val) {
|
|
* return val.isNumber() && val.asInt() > 150;
|
|
* });
|
|
*/
|
|
virtual std::vector<IDataNode*> queryByProperty(const std::string& propName,
|
|
const std::function<bool(const IDataValue&)>& predicate) = 0;
|
|
|
|
// ========================================
|
|
// NODE'S OWN DATA
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Get this node's data blob
|
|
* @return Data value or null if no data
|
|
*/
|
|
virtual std::unique_ptr<IDataValue> getData() const = 0;
|
|
|
|
/**
|
|
* @brief Check if this node has data
|
|
* @return true if data exists
|
|
*/
|
|
virtual bool hasData() const = 0;
|
|
|
|
/**
|
|
* @brief Set this node's data
|
|
* @param data Data to set
|
|
*/
|
|
virtual void setData(std::unique_ptr<IDataValue> data) = 0;
|
|
|
|
// ========================================
|
|
// TYPED DATA ACCESS BY PROPERTY NAME
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Get string property from this node's data
|
|
* @param name Property name
|
|
* @param defaultValue Default if property not found or wrong type
|
|
* @return Property value or default
|
|
*/
|
|
virtual std::string getString(const std::string& name, const std::string& defaultValue = "") const = 0;
|
|
|
|
/**
|
|
* @brief Get integer property from this node's data
|
|
* @param name Property name
|
|
* @param defaultValue Default if property not found or wrong type
|
|
* @return Property value or default
|
|
*/
|
|
virtual int getInt(const std::string& name, int defaultValue = 0) const = 0;
|
|
|
|
/**
|
|
* @brief Get double property from this node's data
|
|
* @param name Property name
|
|
* @param defaultValue Default if property not found or wrong type
|
|
* @return Property value or default
|
|
*/
|
|
virtual double getDouble(const std::string& name, double defaultValue = 0.0) const = 0;
|
|
|
|
/**
|
|
* @brief Get boolean property from this node's data
|
|
* @param name Property name
|
|
* @param defaultValue Default if property not found or wrong type
|
|
* @return Property value or default
|
|
*/
|
|
virtual bool getBool(const std::string& name, bool defaultValue = false) const = 0;
|
|
|
|
/**
|
|
* @brief Check if property exists in this node's data
|
|
* @param name Property name
|
|
* @return true if property exists
|
|
*/
|
|
virtual bool hasProperty(const std::string& name) const = 0;
|
|
|
|
// ========================================
|
|
// TYPED DATA MODIFICATION BY PROPERTY NAME
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Set string property in this node's data
|
|
* @param name Property name
|
|
* @param value Value to set
|
|
*
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual void setString(const std::string& name, const std::string& value) = 0;
|
|
|
|
/**
|
|
* @brief Set integer property in this node's data
|
|
* @param name Property name
|
|
* @param value Value to set
|
|
*
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual void setInt(const std::string& name, int value) = 0;
|
|
|
|
/**
|
|
* @brief Set double property in this node's data
|
|
* @param name Property name
|
|
* @param value Value to set
|
|
*
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual void setDouble(const std::string& name, double value) = 0;
|
|
|
|
/**
|
|
* @brief Set boolean property in this node's data
|
|
* @param name Property name
|
|
* @param value Value to set
|
|
*
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual void setBool(const std::string& name, bool value) = 0;
|
|
|
|
// ========================================
|
|
// HASH SYSTEM FOR VALIDATION & SYNCHRO
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Get hash of this node's data only
|
|
* @return SHA256 hash of data blob
|
|
*/
|
|
virtual std::string getDataHash() = 0;
|
|
|
|
/**
|
|
* @brief Get recursive hash of this node and all children
|
|
* @return SHA256 hash of entire subtree
|
|
*/
|
|
virtual std::string getTreeHash() = 0;
|
|
|
|
/**
|
|
* @brief Get hash of specific child subtree
|
|
* @param childPath Path to child from this node
|
|
* @return SHA256 hash of child subtree
|
|
*/
|
|
virtual std::string getSubtreeHash(const std::string& childPath) = 0;
|
|
|
|
// ========================================
|
|
// METADATA
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Get full path from root to this node
|
|
* @return Path string (e.g., "vehicles/tanks/heavy/model5")
|
|
*/
|
|
virtual std::string getPath() const = 0;
|
|
|
|
/**
|
|
* @brief Get this node's name
|
|
* @return Node name
|
|
*/
|
|
virtual std::string getName() const = 0;
|
|
|
|
/**
|
|
* @brief Get node type (extensible for templates/inheritance later)
|
|
* @return Node type identifier
|
|
*/
|
|
virtual std::string getNodeType() const = 0;
|
|
|
|
// ========================================
|
|
// TREE MODIFICATION (For data/ and runtime/ nodes)
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Add or update a child node
|
|
* @param name Child name
|
|
* @param node Child node to add/replace
|
|
*
|
|
* If a child with this name already exists, it will be replaced.
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual void setChild(const std::string& name, std::unique_ptr<IDataNode> node) = 0;
|
|
|
|
/**
|
|
* @brief Remove a child node
|
|
* @param name Child name to remove
|
|
* @return true if child was found and removed
|
|
*
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual bool removeChild(const std::string& name) = 0;
|
|
|
|
/**
|
|
* @brief Clear all children from this node
|
|
*
|
|
* Only works for data/ and runtime/ nodes. Config nodes are read-only.
|
|
*/
|
|
virtual void clearChildren() = 0;
|
|
};
|
|
|
|
} // namespace grove
|