139 lines
6.0 KiB
C++
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
|