refactor: Update Widget Measurement System and Toolbar Layout
- Refactored the toolbar layout in Toolset to enhance compactness and usability, adjusting padding and button sizes for better alignment. - Introduced a new WidgetMeasurement class to track widget dimensions, improving debugging and test automation capabilities. - Updated CMake configuration to reflect the new file structure for widget measurement components, ensuring proper integration.
This commit is contained in:
140
src/app/gui/widgets/widget_measurement.cc
Normal file
140
src/app/gui/widgets/widget_measurement.cc
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "app/gui/widgets/widget_measurement.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
WidgetMetrics WidgetMeasurement::MeasureLastItem(const std::string& widget_id,
|
||||
const std::string& type) {
|
||||
if (!enabled_) {
|
||||
return WidgetMetrics{};
|
||||
}
|
||||
|
||||
WidgetMetrics metrics;
|
||||
metrics.widget_id = widget_id;
|
||||
metrics.type = type;
|
||||
|
||||
// Get item rect (bounding box)
|
||||
metrics.rect_min = ImGui::GetItemRectMin();
|
||||
metrics.rect_max = ImGui::GetItemRectMax();
|
||||
|
||||
// Calculate size
|
||||
metrics.size = ImVec2(metrics.rect_max.x - metrics.rect_min.x,
|
||||
metrics.rect_max.y - metrics.rect_min.y);
|
||||
|
||||
// Store position
|
||||
metrics.position = metrics.rect_min;
|
||||
|
||||
// Get content region
|
||||
metrics.content_size = ImGui::GetContentRegionAvail();
|
||||
|
||||
// Get cursor position after item
|
||||
metrics.cursor_pos_x = ImGui::GetCursorPosX();
|
||||
|
||||
// Store in current toolbar if active
|
||||
if (!current_toolbar_id_.empty()) {
|
||||
toolbar_metrics_[current_toolbar_id_].push_back(metrics);
|
||||
}
|
||||
|
||||
// Store in frame metrics
|
||||
frame_metrics_.push_back(metrics);
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
void WidgetMeasurement::BeginToolbarMeasurement(const std::string& toolbar_id) {
|
||||
current_toolbar_id_ = toolbar_id;
|
||||
current_toolbar_start_x_ = ImGui::GetCursorPosX();
|
||||
current_toolbar_width_ = 0.0f;
|
||||
|
||||
// Clear previous measurements for this toolbar
|
||||
toolbar_metrics_[toolbar_id].clear();
|
||||
}
|
||||
|
||||
void WidgetMeasurement::EndToolbarMeasurement() {
|
||||
if (current_toolbar_id_.empty()) return;
|
||||
|
||||
// Calculate total width from cursor movement
|
||||
float end_x = ImGui::GetCursorPosX();
|
||||
current_toolbar_width_ = end_x - current_toolbar_start_x_;
|
||||
|
||||
// Store the width
|
||||
toolbar_widths_[current_toolbar_id_] = current_toolbar_width_;
|
||||
|
||||
// Reset state
|
||||
current_toolbar_id_.clear();
|
||||
current_toolbar_width_ = 0.0f;
|
||||
current_toolbar_start_x_ = 0.0f;
|
||||
}
|
||||
|
||||
float WidgetMeasurement::GetToolbarWidth(const std::string& toolbar_id) const {
|
||||
auto it = toolbar_widths_.find(toolbar_id);
|
||||
if (it != toolbar_widths_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool WidgetMeasurement::WouldToolbarOverflow(const std::string& toolbar_id,
|
||||
float available_width) const {
|
||||
float toolbar_width = GetToolbarWidth(toolbar_id);
|
||||
return toolbar_width > available_width;
|
||||
}
|
||||
|
||||
const std::vector<WidgetMetrics>& WidgetMeasurement::GetToolbarMetrics(
|
||||
const std::string& toolbar_id) const {
|
||||
static const std::vector<WidgetMetrics> empty;
|
||||
auto it = toolbar_metrics_.find(toolbar_id);
|
||||
if (it != toolbar_metrics_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
void WidgetMeasurement::ClearFrame() {
|
||||
frame_metrics_.clear();
|
||||
// Don't clear toolbar metrics - they persist across frames for debugging
|
||||
}
|
||||
|
||||
std::string WidgetMeasurement::ExportMetricsJSON() const {
|
||||
std::string json = "{\n \"toolbars\": {\n";
|
||||
|
||||
bool first_toolbar = true;
|
||||
for (const auto& [toolbar_id, metrics] : toolbar_metrics_) {
|
||||
if (!first_toolbar) json += ",\n";
|
||||
first_toolbar = false;
|
||||
|
||||
json += absl::StrFormat(" \"%s\": {\n", toolbar_id);
|
||||
json += absl::StrFormat(" \"total_width\": %.1f,\n",
|
||||
GetToolbarWidth(toolbar_id));
|
||||
json += " \"widgets\": [\n";
|
||||
|
||||
bool first_widget = true;
|
||||
for (const auto& metric : metrics) {
|
||||
if (!first_widget) json += ",\n";
|
||||
first_widget = false;
|
||||
|
||||
json += " {\n";
|
||||
json += absl::StrFormat(" \"id\": \"%s\",\n", metric.widget_id);
|
||||
json += absl::StrFormat(" \"type\": \"%s\",\n", metric.type);
|
||||
json += absl::StrFormat(" \"width\": %.1f,\n", metric.size.x);
|
||||
json += absl::StrFormat(" \"height\": %.1f,\n", metric.size.y);
|
||||
json += absl::StrFormat(" \"x\": %.1f,\n", metric.position.x);
|
||||
json += absl::StrFormat(" \"y\": %.1f\n", metric.position.y);
|
||||
json += " }";
|
||||
}
|
||||
|
||||
json += "\n ]\n";
|
||||
json += " }";
|
||||
}
|
||||
|
||||
json += "\n }\n}\n";
|
||||
return json;
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
119
src/app/gui/widgets/widget_measurement.h
Normal file
119
src/app/gui/widgets/widget_measurement.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef YAZE_APP_GUI_WIDGET_MEASUREMENT_H
|
||||
#define YAZE_APP_GUI_WIDGET_MEASUREMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class WidgetMeasurement
|
||||
* @brief Tracks widget dimensions for debugging and test automation
|
||||
*
|
||||
* Integrates with ImGui Test Engine to provide accurate measurements
|
||||
* of UI elements, helping prevent layout issues and enabling automated
|
||||
* testing of widget sizes and positions.
|
||||
*/
|
||||
struct WidgetMetrics {
|
||||
ImVec2 size; // Width and height
|
||||
ImVec2 position; // Screen position
|
||||
ImVec2 content_size; // Available content region
|
||||
ImVec2 rect_min; // Bounding box min
|
||||
ImVec2 rect_max; // Bounding box max
|
||||
float cursor_pos_x; // Cursor X after rendering
|
||||
std::string widget_id; // Widget identifier
|
||||
std::string type; // Widget type (button, input, combo, etc.)
|
||||
|
||||
std::string ToString() const {
|
||||
return absl::StrFormat(
|
||||
"Widget '%s' (%s): size=(%.1f,%.1f) pos=(%.1f,%.1f) content=(%.1f,%.1f) cursor_x=%.1f",
|
||||
widget_id, type, size.x, size.y, position.x, position.y,
|
||||
content_size.x, content_size.y, cursor_pos_x);
|
||||
}
|
||||
};
|
||||
|
||||
class WidgetMeasurement {
|
||||
public:
|
||||
static WidgetMeasurement& Instance() {
|
||||
static WidgetMeasurement instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Measure the last rendered ImGui item
|
||||
* @param widget_id Unique identifier for this widget
|
||||
* @param type Widget type (button, input, etc.)
|
||||
* @return WidgetMetrics containing all measurements
|
||||
*/
|
||||
WidgetMetrics MeasureLastItem(const std::string& widget_id,
|
||||
const std::string& type = "unknown");
|
||||
|
||||
/**
|
||||
* @brief Begin measuring a toolbar section
|
||||
*/
|
||||
void BeginToolbarMeasurement(const std::string& toolbar_id);
|
||||
|
||||
/**
|
||||
* @brief End measuring a toolbar section and store total width
|
||||
*/
|
||||
void EndToolbarMeasurement();
|
||||
|
||||
/**
|
||||
* @brief Get total measured width of a toolbar
|
||||
*/
|
||||
float GetToolbarWidth(const std::string& toolbar_id) const;
|
||||
|
||||
/**
|
||||
* @brief Check if toolbar would overflow given window width
|
||||
*/
|
||||
bool WouldToolbarOverflow(const std::string& toolbar_id,
|
||||
float available_width) const;
|
||||
|
||||
/**
|
||||
* @brief Get all measurements for a specific toolbar
|
||||
*/
|
||||
const std::vector<WidgetMetrics>& GetToolbarMetrics(
|
||||
const std::string& toolbar_id) const;
|
||||
|
||||
/**
|
||||
* @brief Clear all measurements (call once per frame)
|
||||
*/
|
||||
void ClearFrame();
|
||||
|
||||
/**
|
||||
* @brief Export measurements for test automation
|
||||
*/
|
||||
std::string ExportMetricsJSON() const;
|
||||
|
||||
/**
|
||||
* @brief Enable/disable measurement (performance option)
|
||||
*/
|
||||
void SetEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool IsEnabled() const { return enabled_; }
|
||||
|
||||
private:
|
||||
WidgetMeasurement() = default;
|
||||
|
||||
bool enabled_ = true;
|
||||
std::string current_toolbar_id_;
|
||||
float current_toolbar_width_ = 0.0f;
|
||||
float current_toolbar_start_x_ = 0.0f;
|
||||
|
||||
// Store measurements per toolbar
|
||||
std::unordered_map<std::string, std::vector<WidgetMetrics>> toolbar_metrics_;
|
||||
std::unordered_map<std::string, float> toolbar_widths_;
|
||||
|
||||
// All measurements from current frame
|
||||
std::vector<WidgetMetrics> frame_metrics_;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_WIDGET_MEASUREMENT_H
|
||||
|
||||
Reference in New Issue
Block a user