按钮单独提取出来
This commit is contained in:
@@ -322,8 +322,13 @@ bool performance_overlay_handle_pointer_release(Plot_Core& plot, PointF position
|
||||
auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot));
|
||||
return shower && shower->enabled() && shower->handle_pointer_release(position);
|
||||
}
|
||||
bool performance_overlay_handle_pointer_leave(Plot_Core& plot) {
|
||||
auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot));
|
||||
return shower && shower->enabled() && shower->handle_pointer_leave();
|
||||
}
|
||||
void Performance_Overlay::set_enabled(bool enabled) {
|
||||
auto worker = d()->metrics_worker;
|
||||
d()->reset_button_states();
|
||||
d()->enabled_value.store(enabled, std::memory_order_release);
|
||||
worker->enabled.store(enabled, std::memory_order_release);
|
||||
if (!enabled) {
|
||||
@@ -434,14 +439,11 @@ bool Performance_Overlay::handle_pointer_press(PointF position) {
|
||||
auto snapshot = display_snapshot();
|
||||
if (!snapshot)
|
||||
return false;
|
||||
if (!snapshot->copy_button_rect.empty() && snapshot->copy_button_rect.contains(position)) {
|
||||
d()->copy_to_clipboard(performance_snapshot_text(*snapshot));
|
||||
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_press(position, snapshot->copy_button_rect);
|
||||
Button_Event_Result close_result = d()->close_button_interaction.pointer_press(position, snapshot->close_button_rect);
|
||||
d()->publish_button_states(copy_result.changed || close_result.changed);
|
||||
if (copy_result.consumed || close_result.consumed)
|
||||
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)) {
|
||||
@@ -461,13 +463,32 @@ bool Performance_Overlay::handle_pointer_press(PointF position) {
|
||||
return false;
|
||||
}
|
||||
bool Performance_Overlay::handle_pointer_move(PointF position) {
|
||||
if (!d()->metrics_worker->drag_active.load(std::memory_order_acquire))
|
||||
if (d()->metrics_worker->drag_active.load(std::memory_order_acquire)) {
|
||||
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
}
|
||||
auto snapshot = display_snapshot();
|
||||
if (!snapshot)
|
||||
return false;
|
||||
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_move(position, snapshot->copy_button_rect);
|
||||
Button_Event_Result close_result = d()->close_button_interaction.pointer_move(position, snapshot->close_button_rect);
|
||||
d()->publish_button_states(copy_result.changed || close_result.changed);
|
||||
return copy_result.consumed || close_result.consumed;
|
||||
}
|
||||
bool Performance_Overlay::handle_pointer_release(PointF position) {
|
||||
auto snapshot = display_snapshot();
|
||||
if (snapshot) {
|
||||
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_release(position, snapshot->copy_button_rect);
|
||||
Button_Event_Result close_result = d()->close_button_interaction.pointer_release(position, snapshot->close_button_rect);
|
||||
d()->publish_button_states(copy_result.changed || close_result.changed);
|
||||
if (copy_result.clicked)
|
||||
d()->copy_to_clipboard(performance_snapshot_text(*snapshot));
|
||||
if (close_result.clicked)
|
||||
set_enabled(false);
|
||||
if (copy_result.consumed || close_result.consumed)
|
||||
return true;
|
||||
}
|
||||
if (!d()->metrics_worker->drag_active.load(std::memory_order_acquire))
|
||||
return false;
|
||||
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
||||
@@ -475,6 +496,12 @@ bool Performance_Overlay::handle_pointer_release(PointF position) {
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
}
|
||||
bool Performance_Overlay::handle_pointer_leave() {
|
||||
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_leave();
|
||||
Button_Event_Result close_result = d()->close_button_interaction.pointer_leave();
|
||||
d()->publish_button_states(copy_result.changed || close_result.changed);
|
||||
return copy_result.consumed || close_result.consumed;
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
bool handle_pointer_press(PointF position);
|
||||
bool handle_pointer_move(PointF position);
|
||||
bool handle_pointer_release(PointF position);
|
||||
bool handle_pointer_leave();
|
||||
void set_update_callback(std::function<void()> callback);
|
||||
void set_clipboard_callback(std::function<void(std::string)> callback);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../base/Style.h"
|
||||
#include "../base/String_Format.h"
|
||||
#include "../base/Text.h"
|
||||
#include "../render/Button.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "Performance_Overlay.h"
|
||||
#include "Psc_Cpp_Core/Statistics/Statistics.h"
|
||||
@@ -383,6 +384,8 @@ struct Performance_Worker_State {
|
||||
std::atomic_bool cancelled{false};
|
||||
std::atomic_bool reset_requested{false};
|
||||
std::atomic_bool snapshot_rebuild_requested{false};
|
||||
std::atomic<Button_State> copy_button_state{Button_State::Normal};
|
||||
std::atomic<Button_State> close_button_state{Button_State::Normal};
|
||||
std::atomic<double> scroll_offset{0.0};
|
||||
std::atomic<double> pending_wheel_delta_px{0.0};
|
||||
std::atomic<int> pending_page_delta{0};
|
||||
@@ -453,11 +456,9 @@ struct Performance_Layout_Builder {
|
||||
RectF panel_rect;
|
||||
RectF scroll_bar_rect;
|
||||
RectF scroll_thumb_rect;
|
||||
RectF copy_button_rect;
|
||||
RectF close_button_rect;
|
||||
Button copy_button{"Copy"};
|
||||
Button close_button{"Close"};
|
||||
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) {
|
||||
@@ -476,22 +477,22 @@ struct Performance_Layout_Builder {
|
||||
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 = {};
|
||||
copy_button.set_rect({});
|
||||
close_button.set_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);
|
||||
double close_width = std::min(close_button.preferred_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);
|
||||
close_button.set_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.preferred_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};
|
||||
copy_button.set_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) {
|
||||
@@ -1065,7 +1066,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);
|
||||
max_width = std::max(max_width, copy_button.preferred_width() + button_gap + close_button.preferred_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);
|
||||
@@ -1231,6 +1232,8 @@ struct Performance_Overlay_Private {
|
||||
Performance_Log_Plot_Identity log_identity;
|
||||
mutable std::mutex clipboard_callback_mutex;
|
||||
std::function<void(std::string)> clipboard_callback;
|
||||
Button_Interaction copy_button_interaction;
|
||||
Button_Interaction close_button_interaction;
|
||||
|
||||
Performance_Overlay_Private()
|
||||
: metrics_worker(std::make_shared<Performance_Worker_State>()) {}
|
||||
@@ -1278,6 +1281,23 @@ struct Performance_Overlay_Private {
|
||||
callback(std::move(text));
|
||||
}
|
||||
|
||||
void publish_button_states(bool changed) {
|
||||
if (!changed)
|
||||
return;
|
||||
metrics_worker->copy_button_state.store(copy_button_interaction.state(), std::memory_order_release);
|
||||
metrics_worker->close_button_state.store(close_button_interaction.state(), std::memory_order_release);
|
||||
metrics_worker->snapshot_rebuild_requested.store(true, std::memory_order_release);
|
||||
if (enabled_value.load(std::memory_order_acquire))
|
||||
schedule_performance_metrics_worker(metrics_worker);
|
||||
}
|
||||
|
||||
void reset_button_states() {
|
||||
copy_button_interaction.reset();
|
||||
close_button_interaction.reset();
|
||||
metrics_worker->copy_button_state.store(Button_State::Normal, std::memory_order_release);
|
||||
metrics_worker->close_button_state.store(Button_State::Normal, std::memory_order_release);
|
||||
}
|
||||
|
||||
static const char* feedback_outcome_event(Frame_Outcome outcome) {
|
||||
switch (outcome) {
|
||||
case Frame_Outcome::Presented:
|
||||
@@ -1379,11 +1399,16 @@ 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);
|
||||
Button_Style close_style = layout.close_button.style();
|
||||
close_style.text = state.warning_color;
|
||||
close_style.hover_text = state.warning_color;
|
||||
close_style.pressed_text = state.warning_color;
|
||||
layout.close_button.set_style(std::move(close_style));
|
||||
layout.copy_button.set_state(worker_state.copy_button_state.load(std::memory_order_acquire));
|
||||
layout.close_button.set_state(worker_state.close_button_state.load(std::memory_order_acquire));
|
||||
layout.copy_button.measure(measure_canvas);
|
||||
layout.close_button.measure(measure_canvas);
|
||||
layout.toolbar_height = std::max(layout.copy_button.preferred_height(), layout.close_button.preferred_height());
|
||||
layout.seed_static_field_widths(measure_canvas);
|
||||
layout.build_render_lines(measure_canvas);
|
||||
}
|
||||
@@ -1427,22 +1452,8 @@ 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();
|
||||
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.copy_button.draw(canvas);
|
||||
layout.close_button.draw(canvas);
|
||||
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});
|
||||
@@ -1454,8 +1465,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->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;
|
||||
|
||||
@@ -29,4 +29,5 @@ LIB_DECL bool performance_overlay_handle_wheel(Plot_Core& plot, PointF position,
|
||||
LIB_DECL bool performance_overlay_handle_pointer_press(Plot_Core& plot, PointF position);
|
||||
LIB_DECL bool performance_overlay_handle_pointer_move(Plot_Core& plot, PointF position);
|
||||
LIB_DECL bool performance_overlay_handle_pointer_release(Plot_Core& plot, PointF position);
|
||||
LIB_DECL bool performance_overlay_handle_pointer_leave(Plot_Core& plot);
|
||||
} // namespace renderive
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#pragma once
|
||||
#include "../base/Color.h"
|
||||
#include "../base/Geometry.h"
|
||||
#include "../base/Style.h"
|
||||
#include "Canvas.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
namespace renderive {
|
||||
enum class Button_State : std::uint8_t {
|
||||
Normal,
|
||||
Hovered,
|
||||
Pressed
|
||||
};
|
||||
struct Button_Style {
|
||||
Color background{48, 48, 48};
|
||||
Color hover_background{68, 68, 68};
|
||||
Color pressed_background{32, 32, 32};
|
||||
Color border{96, 96, 96};
|
||||
Color hover_border{64, 142, 255};
|
||||
Color pressed_border{32, 104, 220};
|
||||
Color text{235, 235, 235};
|
||||
Color hover_text{255, 255, 255};
|
||||
Color pressed_text{255, 255, 255};
|
||||
double horizontal_padding{8.0};
|
||||
double vertical_padding{2.0};
|
||||
double minimum_width{48.0};
|
||||
double minimum_height{20.0};
|
||||
};
|
||||
struct Button_Event_Result {
|
||||
bool consumed{};
|
||||
bool changed{};
|
||||
bool clicked{};
|
||||
};
|
||||
class Button {
|
||||
public:
|
||||
Button() = default;
|
||||
explicit Button(std::string text)
|
||||
: text_(std::move(text)) {}
|
||||
void set_text(std::string text) {
|
||||
text_ = std::move(text);
|
||||
}
|
||||
const std::string& text() const {
|
||||
return text_;
|
||||
}
|
||||
void set_style(Button_Style style) {
|
||||
style_ = std::move(style);
|
||||
}
|
||||
const Button_Style& style() const {
|
||||
return style_;
|
||||
}
|
||||
void set_rect(RectF rect) {
|
||||
rect_ = rect;
|
||||
}
|
||||
const RectF& rect() const {
|
||||
return rect_;
|
||||
}
|
||||
void set_state(Button_State state) {
|
||||
state_ = state;
|
||||
}
|
||||
Button_State state() const {
|
||||
return state_;
|
||||
}
|
||||
void measure(const Canvas& canvas) {
|
||||
Text_Metrics metrics = canvas.measure_text(text_);
|
||||
preferred_width_ = std::max(style_.minimum_width, metrics.width + style_.horizontal_padding * 2.0);
|
||||
preferred_height_ = std::max(style_.minimum_height, metrics.line_height() + style_.vertical_padding * 2.0);
|
||||
}
|
||||
double preferred_width() const {
|
||||
return preferred_width_;
|
||||
}
|
||||
double preferred_height() const {
|
||||
return preferred_height_;
|
||||
}
|
||||
bool contains(PointF position) const {
|
||||
return !rect_.empty() && rect_.contains(position);
|
||||
}
|
||||
void draw(Canvas& canvas) const {
|
||||
if (rect_.empty())
|
||||
return;
|
||||
canvas.set_brush(Brush{background_color(), Brush_Style::Solid});
|
||||
canvas.fill_rect(rect_);
|
||||
canvas.set_pen(Pen{border_color()});
|
||||
canvas.draw_rect(rect_);
|
||||
canvas.set_pen(Pen{text_color()});
|
||||
canvas.draw_text(rect_, Text_Align::Center, text_);
|
||||
}
|
||||
private:
|
||||
Color background_color() const {
|
||||
if (state_ == Button_State::Pressed)
|
||||
return style_.pressed_background;
|
||||
if (state_ == Button_State::Hovered)
|
||||
return style_.hover_background;
|
||||
return style_.background;
|
||||
}
|
||||
Color border_color() const {
|
||||
if (state_ == Button_State::Pressed)
|
||||
return style_.pressed_border;
|
||||
if (state_ == Button_State::Hovered)
|
||||
return style_.hover_border;
|
||||
return style_.border;
|
||||
}
|
||||
Color text_color() const {
|
||||
if (state_ == Button_State::Pressed)
|
||||
return style_.pressed_text;
|
||||
if (state_ == Button_State::Hovered)
|
||||
return style_.hover_text;
|
||||
return style_.text;
|
||||
}
|
||||
std::string text_;
|
||||
Button_Style style_;
|
||||
RectF rect_;
|
||||
Button_State state_{Button_State::Normal};
|
||||
double preferred_width_{};
|
||||
double preferred_height_{};
|
||||
};
|
||||
class Button_Interaction {
|
||||
public:
|
||||
Button_Event_Result pointer_move(PointF position, const RectF& rect) {
|
||||
bool inside = !rect.empty() && rect.contains(position);
|
||||
Button_State next = pressed_ && inside ? Button_State::Pressed : inside ? Button_State::Hovered : Button_State::Normal;
|
||||
bool changed = set_state(next);
|
||||
return {inside || pressed_ || changed, changed, false};
|
||||
}
|
||||
Button_Event_Result pointer_press(PointF position, const RectF& rect) {
|
||||
if (rect.empty() || !rect.contains(position))
|
||||
return {};
|
||||
pressed_ = true;
|
||||
bool changed = set_state(Button_State::Pressed);
|
||||
return {true, changed, false};
|
||||
}
|
||||
Button_Event_Result pointer_release(PointF position, const RectF& rect) {
|
||||
bool inside = !rect.empty() && rect.contains(position);
|
||||
if (!pressed_) {
|
||||
bool changed = set_state(inside ? Button_State::Hovered : Button_State::Normal);
|
||||
return {false, changed, false};
|
||||
}
|
||||
pressed_ = false;
|
||||
bool changed = set_state(inside ? Button_State::Hovered : Button_State::Normal);
|
||||
return {true, changed, inside};
|
||||
}
|
||||
Button_Event_Result pointer_leave() {
|
||||
bool changed = set_state(Button_State::Normal);
|
||||
return {pressed_ || changed, changed, false};
|
||||
}
|
||||
bool reset() {
|
||||
pressed_ = false;
|
||||
return set_state(Button_State::Normal);
|
||||
}
|
||||
Button_State state() const {
|
||||
return state_;
|
||||
}
|
||||
private:
|
||||
bool set_state(Button_State state) {
|
||||
if (state_ == state)
|
||||
return false;
|
||||
state_ = state;
|
||||
return true;
|
||||
}
|
||||
bool pressed_{};
|
||||
Button_State state_{Button_State::Normal};
|
||||
};
|
||||
} // namespace renderive
|
||||
Binary file not shown.
@@ -190,6 +190,8 @@ bool Abs_Plot_Private::event(Abs_Plot* plot, QEvent* event, bool base_result) {
|
||||
break;
|
||||
}
|
||||
case QEvent::Leave: {
|
||||
if (performance_overlay_handle_pointer_leave(*core))
|
||||
request_performance_overlay_update(plot);
|
||||
Event leave_event(Event_Type::Leave);
|
||||
core->dispatch_event(leave_event);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user