Files
Renderive/Core/primitive/Label.cpp
T
2026-08-01 20:09:45 +08:00

139 lines
6.0 KiB
C++

#include "Label.h"
#include "../Axis/Abs_Axis_p.h"
#include "../base/Memory.h"
#include "../render/Canvas.h"
namespace renderive {
struct Label_Render_State : Render_State, Label_Prop {};
struct Label_Input_Data : Latest_Input_Data {
std::pmr::vector<Label_Item> items{memory_resource(Memory_Domain::Other)};
void push(std::span<const Label_Item> value) {
items.assign(value.begin(), value.end());
mark_latest_pushed();
}
void push(std::pmr::vector<Label_Item>&& value) {
items = std::move(value);
mark_latest_pushed();
}
void clear() override {
items.clear();
clear_latest();
}
};
struct Label_Private : Typed_Render_Data<Label, Label_Render_State, Label_Input_Data> {
std::pmr::vector<Label_Item> prepared_items{memory_resource(Memory_Domain::Other)};
bool use_prepared_items = false;
void prepare_data(const Plot_Render_Snapshot&) override {
sync_state_pipeline();
Label_Input_Data* input = render_input_data();
if (!input->pending)
return;
prepared_items.swap(input->items);
use_prepared_items = true;
clear_render_input();
}
void draw_item(Canvas& canvas, const Label_Item& item, const Axis_Mapping_2D& mapping) {
if (item.text.empty())
return;
canvas.save();
canvas.set_font(item.font);
Text_Metrics tm = canvas.measure_text(item.text);
double text_w = tm.width;
double text_h = tm.height;
PointF anchor = mapping.map(item.position);
PointF top_left{anchor.x + item.offset.x, anchor.y + item.offset.y};
if ((item.alignment & Text_Align::HCenter) == Text_Align::HCenter)
top_left.x -= text_w / 2.0;
else if ((item.alignment & Text_Align::Right) == Text_Align::Right)
top_left.x -= text_w;
if ((item.alignment & Text_Align::VCenter) == Text_Align::VCenter)
top_left.y -= text_h / 2.0;
else if ((item.alignment & Text_Align::Bottom) == Text_Align::Bottom)
top_left.y -= text_h;
RectF rect{top_left.x, top_left.y, text_w, text_h};
if (item.background_brush.style != Brush_Style::None || item.border_pen.style != Line_Style::None) {
canvas.set_pen(item.border_pen);
canvas.set_brush(item.background_brush);
canvas.draw_rect(rect);
}
canvas.set_pen(item.text_pen);
canvas.draw_text(rect, Text_Align::Left | Text_Align::VCenter, item.text);
canvas.restore();
}
void draw(Canvas& canvas, const Plot_Render_Snapshot&) override {
Label_Render_State* s = render_state();
auto domain_axis = s->domain_axis.lock();
auto value_axis = s->value_axis.lock();
if (!domain_axis || !value_axis)
return;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get());
if (use_prepared_items) {
for (const Label_Item& item : prepared_items)
draw_item(canvas, item, mapping);
}
else {
Label_Item item;
item.position = s->position;
item.offset = s->offset;
item.text = s->text;
item.alignment = s->alignment;
item.font = s->font;
item.text_pen = s->text_pen;
item.background_brush = s->background_brush;
item.border_pen = s->border_pen;
draw_item(canvas, item, mapping);
}
}
};
RENDERIVE_DEFINE_RENDERABLE_BINDING(Label, Label_Private, Label_Render_State, Label_Input_Data, "Label")
void Label::give_items(std::span<const Label_Item> items) {
if (!ok())
return;
Render_Input_Lease<Label_Private, Label_Input_Data> input(d());
input->push(items);
}
void Label::give_items(std::pmr::vector<Label_Item>&& items) {
if (!ok())
return;
Render_Input_Lease<Label_Private, Label_Input_Data> input(d());
input->push(std::move(items));
}
RENDERIVE_DEFINE_WEAK_STATE_PROP(Label, Label_Render_State, Abs_Axis, domain_axis)
RENDERIVE_DEFINE_WEAK_STATE_PROP(Label, Label_Render_State, Abs_Axis, value_axis)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, PointF, position)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, PointF, offset)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, std::string, text)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Text_Align, alignment)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Font, font)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Pen, text_pen)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Brush, background_brush)
RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Pen, border_pen)
Label::Builder::Builder(std::shared_ptr<Renderable> parent, std::shared_ptr<Abs_Axis> domain_axis, std::shared_ptr<Abs_Axis> value_axis) {
ASSERT(parent, "Label build error! parent == nullptr");
ASSERT(domain_axis && value_axis, "Label build error! axis == nullptr");
ASSERT(domain_axis->attached_to_plot(), "Label build error! domain_axis->plot == nullptr");
ASSERT(domain_axis->shares_plot_with(*value_axis), "Label build error! domain_axis->plot != value_axis->plot");
ASSERT(parent->shares_plot_with(*domain_axis), "Label build error! parent->plot != axis->plot");
ASSERT(domain_axis->orientation() != value_axis->orientation(), "Label build error! axes must be orthogonal");
this->parent = parent;
this->domain_axis = domain_axis;
this->value_axis = value_axis;
}
std::shared_ptr<Label> Label::Builder::build() {
auto ret = renderive::make_shared<Label>();
ret->init(parent);
Label_Private* pd = ret->d();
Label_Render_State* sc = reinterpret_cast<Label_Render_State*>(pd->state(State_Edit));
PROP_R(std::weak_ptr<Abs_Axis>, domain_axis)
PROP_R(std::weak_ptr<Abs_Axis>, value_axis)
PROP_R(PointF, position)
PROP_R(PointF, offset)
PROP_R(std::string, text)
PROP_R(Text_Align, alignment)
PROP_R(Font, font)
PROP_R(Pen, text_pen)
PROP_R(Brush, background_brush)
PROP_R(Pen, border_pen)
return ret;
}
} // namespace renderive