#include "Selection_Rectangle_Overlay.h" #include "../render/Blend2D_Cache.h" #include #include #include namespace renderive { namespace detail { namespace { struct Selection_Runtime_Base {}; struct Selection_Runtime { std::vector regions; bool selecting{}; PointF selection_start{}; PointF selection_current{}; }; using Selection_Runtime_State = Double_State_Strategy; RectF axis_content_rect(const Axis_Transform& horizontal, const Axis_Transform& vertical) { const double x1 = horizontal.coord_to_pixel(horizontal.coordinate_range.origin); const double x2 = horizontal.coord_to_pixel(horizontal.coordinate_range.target); const double y1 = vertical.coord_to_pixel(vertical.coordinate_range.origin); const double y2 = vertical.coord_to_pixel(vertical.coordinate_range.target); return {std::min(x1, x2), std::min(y1, y2), std::abs(x2 - x1), std::abs(y2 - y1)}; } } struct Selection_Rectangle_Overlay_Control::Impl { Impl(std::shared_ptr horizontal, std::shared_ptr vertical) : horizontal_axis(std::move(horizontal)), vertical_axis(std::move(vertical)) {} std::shared_ptr horizontal_axis; std::shared_ptr vertical_axis; Selection_Runtime_State runtime; }; Selection_Rectangle_Overlay_Control::Selection_Rectangle_Overlay_Control(Plot_Core& plot, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr horizontal_axis, std::shared_ptr vertical_axis) : Plottable_State(plot, properties), impl_(std::make_unique(std::move(horizontal_axis), std::move(vertical_axis))) {} Selection_Rectangle_Overlay_Control::~Selection_Rectangle_Overlay_Control() = default; std::vector Selection_Rectangle_Overlay_Control::selected_regions() const { return impl_->runtime.get<&Selection_Runtime::regions>(); } void Selection_Rectangle_Overlay_Control::clear_selected_regions() { impl_->runtime.update([](Selection_Runtime& runtime) { runtime.regions.clear(); runtime.selecting = false; }); changed(); } void Selection_Rectangle_Overlay_Control::handle_event(const Event& event) { const RectF content = axis_content_rect(impl_->horizontal_axis->transform(), impl_->vertical_axis->transform()); if(event.type == Event_Type::Pointer_Press) { const auto& pointer = static_cast(event); if(pointer.button != Mouse_Button::Left || !content.contains(pointer.position)) return; impl_->runtime.update([&](Selection_Runtime& runtime) { runtime.selecting = true; runtime.selection_start = runtime.selection_current = pointer.position; }); event.accept(); changed(); return; } if(event.type == Event_Type::Pointer_Move) { const auto& pointer = static_cast(event); bool selecting{}; impl_->runtime.update([&](Selection_Runtime& runtime) { selecting = runtime.selecting; if(selecting) runtime.selection_current = pointer.position; }); if(!selecting) return; event.accept(); changed(); return; } if(event.type != Event_Type::Pointer_Release) return; const auto& pointer = static_cast(event); PointF start; bool selecting{}; impl_->runtime.update([&](Selection_Runtime& runtime) { selecting = runtime.selecting; if(!selecting) return; runtime.selecting = false; start = runtime.selection_start; runtime.selection_current = pointer.position; }); if(!selecting) return; const RectF region{impl_->horizontal_axis->pixel_to_coord(start.x), impl_->vertical_axis->pixel_to_coord(start.y), impl_->horizontal_axis->pixel_to_coord(pointer.position.x) - impl_->horizontal_axis->pixel_to_coord(start.x), impl_->vertical_axis->pixel_to_coord(pointer.position.y) - impl_->vertical_axis->pixel_to_coord(start.y)}; if(std::abs(region.width) > 1e-9 && std::abs(region.height) > 1e-9) impl_->runtime.update([region](Selection_Runtime& runtime) { runtime.regions.push_back(region.normalized()); }); event.accept(); changed(); } void Selection_Rectangle_Overlay_Control::publish() { publish_properties(); impl_->runtime.publish(); } void Selection_Rectangle_Overlay_Control::paint(Painter& painter) { const auto state = render_properties(); const auto runtime = impl_->runtime.render_use_state(); for(const RectF& region : runtime.regions) { RectF pixels{impl_->horizontal_axis->coord_to_pixel(region.x), impl_->vertical_axis->coord_to_pixel(region.y), impl_->horizontal_axis->coord_to_pixel(region.right()) - impl_->horizontal_axis->coord_to_pixel(region.x), impl_->vertical_axis->coord_to_pixel(region.bottom()) - impl_->vertical_axis->coord_to_pixel(region.y)}; painter.rect(pixels, state.selection_border_pen, state.selection_brush); std::ostringstream text; text << region.width << " x " << region.height; const RectF normalized = pixels.normalized(); painter.text({normalized.x + 3.0, normalized.y + 3.0}, text.str(), state.label_font, state.label_pen); } if(runtime.selecting) painter.rect({runtime.selection_start.x, runtime.selection_start.y, runtime.selection_current.x - runtime.selection_start.x, runtime.selection_current.y - runtime.selection_start.y}, state.selection_border_pen, state.selection_brush); } } }