158 lines
5.0 KiB
C++
158 lines
5.0 KiB
C++
#include "Numeric_Axis.h"
|
|
|
|
#include "Axis_Format.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace renderive::detail {
|
|
namespace {
|
|
|
|
bool valid_range(Range range) {
|
|
return std::isfinite(range.origin) && std::isfinite(range.target) && range.size() > 0.0;
|
|
}
|
|
|
|
Numeric_Axis_State numeric_state_from(const Axis_Properties& properties) {
|
|
return {
|
|
properties.coordinates,
|
|
std::clamp(properties.precision, 0, 12),
|
|
properties.wheel,
|
|
properties.drag
|
|
};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Axis_Control::Axis_Control(Plot_Core& plot, const Axis_Properties& properties)
|
|
: Abs_Axis(plot, properties), numeric_(numeric_state_from(properties)) {}
|
|
|
|
Range Axis_Control::coord_range() const {
|
|
return numeric_.get<&Numeric_Axis_State::coordinates>();
|
|
}
|
|
|
|
int Axis_Control::label_precision() const {
|
|
return numeric_.get<&Numeric_Axis_State::precision>();
|
|
}
|
|
|
|
void Axis_Control::set_label_precision(int value) {
|
|
numeric_.set<&Numeric_Axis_State::precision>(std::clamp(value, 0, 12));
|
|
changed();
|
|
}
|
|
|
|
double Axis_Control::coord_start() const { return coord_range().origin; }
|
|
|
|
void Axis_Control::set_coord_start(double value) {
|
|
auto range = coord_range();
|
|
set_coord_range({value, value + range.length()});
|
|
}
|
|
|
|
double Axis_Control::coord_length() const { return coord_range().length(); }
|
|
|
|
void Axis_Control::set_coord_length(double value) {
|
|
auto range = coord_range();
|
|
set_coord_range({range.origin, range.origin + value});
|
|
}
|
|
|
|
void Axis_Control::set_coord_range(Range range) {
|
|
if (!valid_range(range))
|
|
return;
|
|
numeric_.set<&Numeric_Axis_State::coordinates>(range);
|
|
changed();
|
|
}
|
|
|
|
void Axis_Control::set_use_wheel(bool value) {
|
|
numeric_.set<&Numeric_Axis_State::wheel>(value);
|
|
changed();
|
|
}
|
|
|
|
void Axis_Control::set_use_drag(bool value) {
|
|
numeric_.set<&Numeric_Axis_State::drag>(value);
|
|
changed();
|
|
}
|
|
|
|
bool Axis_Control::use_wheel() const {
|
|
return numeric_.get<&Numeric_Axis_State::wheel>();
|
|
}
|
|
|
|
bool Axis_Control::use_drag() const {
|
|
return numeric_.get<&Numeric_Axis_State::drag>();
|
|
}
|
|
|
|
void Axis_Control::handle_event(const Event& event) {
|
|
if (event.type == Event_Type::Wheel && use_wheel()) {
|
|
const auto& wheel = static_cast<const Wheel_Event&>(event);
|
|
Range range = coord_range();
|
|
const double anchor_pixel = orientation() == Orientation::Horizontal ? wheel.position.x : wheel.position.y;
|
|
const double anchor = pixel_to_coord(anchor_pixel);
|
|
const double factor = wheel.angle_delta_y >= 0.0 ? 0.9 : 1.1;
|
|
set_coord_range({anchor + (range.origin - anchor) * factor,
|
|
anchor + (range.target - anchor) * factor});
|
|
event.accept();
|
|
return;
|
|
}
|
|
if (!use_drag())
|
|
return;
|
|
if (event.type == Event_Type::Pointer_Press) {
|
|
const auto& pointer = static_cast<const Pointer_Event&>(event);
|
|
if (pointer.button == Mouse_Button::Left) {
|
|
interaction_.update([&](Axis_Interaction_State& interaction) {
|
|
interaction.dragging = true;
|
|
interaction.last_pointer = pointer.position;
|
|
});
|
|
event.accept();
|
|
}
|
|
} else if (event.type == Event_Type::Pointer_Move) {
|
|
const auto& pointer = static_cast<const Pointer_Event&>(event);
|
|
PointF previous;
|
|
bool dragging{};
|
|
interaction_.update([&](Axis_Interaction_State& interaction) {
|
|
dragging = interaction.dragging;
|
|
if (!dragging)
|
|
return;
|
|
previous = interaction.last_pointer;
|
|
interaction.last_pointer = pointer.position;
|
|
});
|
|
if (!dragging)
|
|
return;
|
|
const double delta = orientation() == Orientation::Horizontal
|
|
? pointer.position.x - previous.x
|
|
: pointer.position.y - previous.y;
|
|
Range range = coord_range();
|
|
const double shift = pixel_length() == 0 ? 0.0 : -delta * range.length() / pixel_length();
|
|
set_coord_range({range.origin + shift, range.target + shift});
|
|
event.accept();
|
|
} else if (event.type == Event_Type::Pointer_Release) {
|
|
bool accepted{};
|
|
interaction_.update([&](Axis_Interaction_State& interaction) {
|
|
accepted = interaction.dragging;
|
|
interaction.dragging = false;
|
|
});
|
|
if (accepted)
|
|
event.accept();
|
|
}
|
|
}
|
|
|
|
std::string Axis_Control::tick_label(double tick) const {
|
|
return localized_axis_number(tick, label_precision(), locale());
|
|
}
|
|
|
|
void Axis_Control::publish() {
|
|
Abs_Axis::publish();
|
|
numeric_.publish();
|
|
interaction_.publish();
|
|
}
|
|
|
|
std::uint64_t Axis_Control::state_revision() const {
|
|
return Abs_Axis::state_revision() + numeric_.state_revision() + interaction_.state_revision();
|
|
}
|
|
|
|
Numeric_Axis_State Axis_Control::numeric_state() const {
|
|
return numeric_.read([](const Numeric_Axis_State& value) { return value; });
|
|
}
|
|
|
|
Numeric_Axis_State Axis_Control::render_numeric_state() const {
|
|
return numeric_.render_use_state();
|
|
}
|
|
|
|
} // namespace renderive::detail
|