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:
scawful
2025-10-05 17:25:46 -04:00
parent 6daf0adf84
commit c1d93ce0d2
5 changed files with 30 additions and 18 deletions

View 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

View 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