performance 复制按钮

This commit is contained in:
2026-08-03 09:30:03 +08:00
parent 43bbe54b72
commit b2eb81448b
7 changed files with 136 additions and 8 deletions
+2
View File
@@ -24,6 +24,8 @@ struct Plot_Render_Context {
std::shared_ptr<Frame_Lifecycle_Observer> frame_lifecycle_observer;
std::mutex performance_update_callback_mutex;
std::function<void()> performance_update_callback;
std::mutex performance_clipboard_callback_mutex;
std::function<void(std::string)> performance_clipboard_callback;
std::atomic<std::uint64_t> performance_plot_id{0};
mutable std::mutex performance_plot_name_mutex;
std::string performance_plot_name;
+46 -1
View File
@@ -152,6 +152,25 @@ static std::function<void()> performance_update_callback(const std::shared_ptr<P
std::lock_guard<std::mutex> lock(ctx->performance_update_callback_mutex);
return ctx->performance_update_callback;
}
static std::function<void(std::string)> performance_clipboard_callback(const std::shared_ptr<Plot_Render_Context>& ctx) {
if (!ctx)
return {};
std::lock_guard<std::mutex> lock(ctx->performance_clipboard_callback_mutex);
return ctx->performance_clipboard_callback;
}
static std::string performance_snapshot_text(const Performance_Display_Snapshot& snapshot) {
std::size_t size = 0;
for (const auto& line : snapshot.lines)
size += line.size() + 1;
std::string text;
text.reserve(size);
for (std::size_t i = 0; i < snapshot.lines.size(); ++i) {
if (i)
text.push_back('\n');
text += snapshot.lines[i];
}
return text;
}
static void request_performance_update(const std::shared_ptr<Performance_Worker_State>& worker) {
if (!worker)
return;
@@ -198,6 +217,7 @@ static std::shared_ptr<Performance_Overlay> attach_performance_overlay_impl(Plot
if (options)
created_shower->set_options(*options);
created_shower->set_update_callback(performance_update_callback(ctx));
created_shower->set_clipboard_callback(performance_clipboard_callback(ctx));
std::atomic_store_explicit(&ctx->frame_lifecycle_observer, std::static_pointer_cast<Frame_Lifecycle_Observer>(created_shower), std::memory_order_release);
return created_shower;
}
@@ -267,6 +287,18 @@ void set_performance_overlay_update_callback(Plot_Core& plot, std::function<void
if (shower)
shower->set_update_callback(performance_update_callback(ctx));
}
void set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function<void(std::string)> callback) {
auto ctx = Plot_Core_Context_Access::render_context(plot);
if (!ctx)
return;
{
std::lock_guard<std::mutex> lock(ctx->performance_clipboard_callback_mutex);
ctx->performance_clipboard_callback = std::move(callback);
}
auto shower = performance_overlay_owner(ctx);
if (shower)
shower->set_clipboard_callback(performance_clipboard_callback(ctx));
}
void notify_performance_overlay_viewport_changed(Plot_Core& plot) {
auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot));
if (!shower || !shower->enabled())
@@ -400,7 +432,17 @@ bool Performance_Overlay::handle_wheel(PointF position, double angle_delta_y, do
}
bool Performance_Overlay::handle_pointer_press(PointF position) {
auto snapshot = display_snapshot();
if (!snapshot || snapshot->max_scroll_offset <= 0.0)
if (!snapshot)
return false;
if (!snapshot->copy_button_rect.empty() && snapshot->copy_button_rect.contains(position)) {
d()->copy_to_clipboard(performance_snapshot_text(*snapshot));
return true;
}
if (!snapshot->close_button_rect.empty() && snapshot->close_button_rect.contains(position)) {
set_enabled(false);
return true;
}
if (snapshot->max_scroll_offset <= 0.0)
return false;
if (snapshot->scroll_thumb_rect.contains(position)) {
d()->metrics_worker->drag_anchor_y.store(position.y - snapshot->scroll_thumb_rect.y, std::memory_order_release);
@@ -437,6 +479,9 @@ void Performance_Overlay::set_update_callback(std::function<void()> callback) {
std::lock_guard<std::mutex> lock(d()->metrics_worker->update_callback_mutex);
d()->metrics_worker->update_callback = std::move(callback);
}
void Performance_Overlay::set_clipboard_callback(std::function<void(std::string)> callback) {
d()->set_clipboard_callback(std::move(callback));
}
void write_frame_performance_log(std::shared_ptr<const Frame_Lifecycle_Record> frame) {
if (!frame)
return;
+2
View File
@@ -2,6 +2,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include "../architecture/Frame_Scheduler.h"
#include "../base/Color.h"
#include "../base/Geometry.h"
@@ -63,6 +64,7 @@ public:
bool handle_pointer_move(PointF position);
bool handle_pointer_release(PointF position);
void set_update_callback(std::function<void()> callback);
void set_clipboard_callback(std::function<void(std::string)> callback);
private:
Performance_Overlay_Private* d();
+77 -7
View File
@@ -453,18 +453,45 @@ struct Performance_Layout_Builder {
RectF panel_rect;
RectF scroll_bar_rect;
RectF scroll_thumb_rect;
RectF copy_button_rect;
RectF close_button_rect;
double content_width{};
double copy_button_width{};
double close_button_width{};
double toolbar_height{};
double button_gap{4.0};
static std::string value(double number, int precision = 2) {
return format_number(number, {.precision = precision});
}
double panel_height(double line_height, Performance_Overlay_Render_State* state, int plot_height) const {
return std::min(content_height(line_height, state), static_cast<double>(std::max(0, plot_height)));
}
double text_top(Performance_Overlay_Render_State* state) const {
return static_cast<double>(state->top_margin) + toolbar_height + 4.0;
}
double content_height(double line_height, Performance_Overlay_Render_State* state) const {
return line_height * static_cast<double>(render_lines.size()) + state->top_margin + state->bottom_margin;
return line_height * static_cast<double>(render_lines.size()) + text_top(state) + state->bottom_margin;
}
double content_viewport_height(Performance_Overlay_Render_State* state, double height) const {
return std::max(0.0, height - state->top_margin - state->bottom_margin);
return std::max(0.0, height - text_top(state) - state->bottom_margin);
}
void update_toolbar_button_rects(Performance_Overlay_Render_State* state) {
copy_button_rect = {};
close_button_rect = {};
if (panel_rect.empty() || toolbar_height <= 0.0)
return;
double right = panel_rect.width - static_cast<double>(state->right_margin + state->scroll_bar_width + state->scroll_bar_margin * 2);
double left = static_cast<double>(state->left_margin);
double available_width = std::max(0.0, right - left);
double close_width = std::min(close_button_width, available_width);
if (close_width <= 0.0)
return;
close_button_rect = RectF{right - close_width, static_cast<double>(state->top_margin), close_width, toolbar_height};
available_width = std::max(0.0, close_button_rect.x - button_gap - left);
double copy_width = std::min(copy_button_width, available_width);
if (copy_width <= 0.0)
return;
copy_button_rect = RectF{close_button_rect.x - button_gap - copy_width, static_cast<double>(state->top_margin), copy_width, toolbar_height};
}
void update_scroll_bar_rects(Performance_Overlay_Render_State* state, double max_scroll, double scroll, double visible_height, double total_height) {
if (max_scroll <= 0.0 || panel_rect.width <= 0.0 || panel_rect.height <= 0.0 || total_height <= 0.0) {
@@ -1038,6 +1065,7 @@ struct Performance_Layout_Builder {
double max_width = 0.0;
for (const auto& line : render_lines)
max_width = std::max(max_width, line.width);
max_width = std::max(max_width, copy_button_width + button_gap + close_button_width);
double measured_width = max_width
+ static_cast<double>(state->left_margin + state->right_margin + state->scroll_bar_width + state->scroll_bar_margin * 2);
held_width = std::max(held_width, measured_width);
@@ -1201,6 +1229,8 @@ struct Performance_Overlay_Private {
std::shared_ptr<Performance_Worker_State> metrics_worker;
mutable std::mutex identity_mutex;
Performance_Log_Plot_Identity log_identity;
mutable std::mutex clipboard_callback_mutex;
std::function<void(std::string)> clipboard_callback;
Performance_Overlay_Private()
: metrics_worker(std::make_shared<Performance_Worker_State>()) {}
@@ -1233,6 +1263,21 @@ struct Performance_Overlay_Private {
return log_identity;
}
void set_clipboard_callback(std::function<void(std::string)> callback) {
std::lock_guard<std::mutex> lock(clipboard_callback_mutex);
clipboard_callback = std::move(callback);
}
void copy_to_clipboard(std::string text) const {
std::function<void(std::string)> callback;
{
std::lock_guard<std::mutex> lock(clipboard_callback_mutex);
callback = clipboard_callback;
}
if (callback)
callback(std::move(text));
}
static const char* feedback_outcome_event(Frame_Outcome outcome) {
switch (outcome) {
case Frame_Outcome::Presented:
@@ -1334,6 +1379,11 @@ struct Performance_Overlay_Private {
metrics = measure_canvas.measure_text("0123456789 ABC \xE4\xB8\xAD\xE6\x96\x87");
if (metrics.line_height() <= 0.0)
metrics = measure_canvas.measure_text("M");
Text_Metrics copy_metrics = measure_canvas.measure_text("Copy");
Text_Metrics close_metrics = measure_canvas.measure_text("Close");
layout.copy_button_width = std::max(48.0, copy_metrics.width + 16.0);
layout.close_button_width = std::max(48.0, close_metrics.width + 16.0);
layout.toolbar_height = std::max(20.0, metrics.line_height() + 4.0);
layout.seed_static_field_widths(measure_canvas);
layout.build_render_lines(measure_canvas);
}
@@ -1346,11 +1396,13 @@ struct Performance_Overlay_Private {
if (panel_width <= 0.0 || height <= 0.0)
return snapshot;
double content_height = layout.content_height(line_height, &state);
double text_content_height = line_height * static_cast<double>(layout.render_lines.size());
double content_viewport_height = layout.content_viewport_height(&state, height);
double max_scroll = std::max(0.0, content_height - content_viewport_height);
double max_scroll = std::max(0.0, text_content_height - content_viewport_height);
double scroll = performance_clamp_scroll_offset(worker_state.scroll_offset.load(std::memory_order_acquire), max_scroll);
worker_state.scroll_offset.store(scroll, std::memory_order_release);
layout.panel_rect = RectF{0.0, 0.0, panel_width, height};
layout.update_toolbar_button_rects(&state);
Image image(static_cast<int>(std::ceil(panel_width)), static_cast<int>(std::ceil(height)));
image.fill(Color::transparent());
Canvas canvas(image);
@@ -1361,13 +1413,13 @@ struct Performance_Overlay_Private {
canvas.set_pen(Pen{state.foreground});
RectF text_rect{
static_cast<double>(state.left_margin),
static_cast<double>(state.top_margin),
layout.text_top(&state),
std::max(0.0, panel_width - state.left_margin - state.right_margin - state.scroll_bar_width - state.scroll_bar_margin * 2),
std::max(0.0, height - state.top_margin - state.bottom_margin)
std::max(0.0, height - layout.text_top(&state) - state.bottom_margin)
};
canvas.set_clip_rect(text_rect);
int first_line = std::max(0, static_cast<int>(std::floor(scroll / line_height)));
double y = static_cast<double>(state.top_margin) - (scroll - static_cast<double>(first_line) * line_height);
double y = layout.text_top(&state) - (scroll - static_cast<double>(first_line) * line_height);
layout.text_runs.clear();
for (int i = first_line; i < static_cast<int>(layout.render_lines.size()) && y < text_rect.bottom(); ++i, y += line_height) {
if (y + line_height < text_rect.y)
@@ -1375,7 +1427,23 @@ struct Performance_Overlay_Private {
layout.draw_render_line(canvas, layout.render_lines[static_cast<std::size_t>(i)], text_rect.x, y, line_height, state);
}
canvas.reset_clip();
layout.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, content_height);
if (!layout.copy_button_rect.empty()) {
canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid});
canvas.fill_rect(layout.copy_button_rect);
canvas.set_pen(Pen{state.scroll_thumb});
canvas.draw_rect(layout.copy_button_rect);
canvas.set_pen(Pen{state.foreground});
canvas.draw_text(layout.copy_button_rect, Text_Align::Center, "Copy");
}
if (!layout.close_button_rect.empty()) {
canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid});
canvas.fill_rect(layout.close_button_rect);
canvas.set_pen(Pen{state.scroll_thumb});
canvas.draw_rect(layout.close_button_rect);
canvas.set_pen(Pen{state.warning_color});
canvas.draw_text(layout.close_button_rect, Text_Align::Center, "Close");
}
layout.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, text_content_height);
if (!layout.scroll_bar_rect.empty()) {
canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid});
canvas.fill_rect(layout.scroll_bar_rect);
@@ -1386,6 +1454,8 @@ struct Performance_Overlay_Private {
snapshot->destination_rect = layout.panel_rect;
snapshot->scroll_track_rect = layout.scroll_bar_rect;
snapshot->scroll_thumb_rect = layout.scroll_thumb_rect;
snapshot->copy_button_rect = layout.copy_button_rect;
snapshot->close_button_rect = layout.close_button_rect;
snapshot->content_height = content_height;
snapshot->scroll_offset = scroll;
snapshot->max_scroll_offset = max_scroll;
+2
View File
@@ -29,6 +29,8 @@ struct Performance_Display_Snapshot {
RectF viewport_rect;
RectF scroll_track_rect;
RectF scroll_thumb_rect;
RectF copy_button_rect;
RectF close_button_rect;
Size viewport_size;
double content_height{};
double scroll_offset{};
+1
View File
@@ -23,6 +23,7 @@ LIB_DECL bool performance_overlay_enabled(const Plot_Core& plot);
LIB_DECL void set_performance_overlay_enabled(Plot_Core& plot, bool enabled);
LIB_DECL std::shared_ptr<const Performance_Display_Snapshot> performance_overlay_snapshot(const Plot_Core& plot);
LIB_DECL void set_performance_overlay_update_callback(Plot_Core& plot, std::function<void()> callback);
LIB_DECL void set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function<void(std::string)> callback);
LIB_DECL void notify_performance_overlay_viewport_changed(Plot_Core& plot);
LIB_DECL bool performance_overlay_handle_wheel(Plot_Core& plot, PointF position, double angle_delta_y, double pixel_delta_y);
LIB_DECL bool performance_overlay_handle_pointer_press(Plot_Core& plot, PointF position);
+6
View File
@@ -7,6 +7,8 @@
#include "Core/plottable/export.h"
#include "Core/plot/Plot_Core_Access.h"
#include "Core/architecture/Plot_Render_Context.h"
#include <QClipboard>
#include <QGuiApplication>
#include <QPainter>
#include <QPointer>
#include <QResizeEvent>
@@ -60,6 +62,9 @@ void Abs_Plot_Private::attach_widget(Abs_Plot* plot) {
guarded_plot->update();
});
});
set_performance_overlay_clipboard_callback(*core, [](std::string text) {
QGuiApplication::clipboard()->setText(QString::fromUtf8(text.data(), static_cast<int>(text.size())));
});
}
Abs_Plot::Abs_Plot(Abs_Plot_Private* private_data)
@@ -73,6 +78,7 @@ Abs_Plot::Abs_Plot(Abs_Plot_Private* private_data)
Abs_Plot::~Abs_Plot() {
if (d) {
set_performance_overlay_update_callback(*d->core, {});
set_performance_overlay_clipboard_callback(*d->core, {});
d->core->deactivate_view();
delete d;
d = nullptr;