diff --git a/src/interface/Interface_graphic.cpp b/src/interface/Interface_graphic.cpp index 55419f54ea591d62b1ed214665d918e317f93125..79d38e81ad72e1cde913ddf0ea6ee965c635cce3 100644 --- a/src/interface/Interface_graphic.cpp +++ b/src/interface/Interface_graphic.cpp @@ -990,7 +990,7 @@ void Interface_graphic::init_render_area() { /* Bind the render area to a rander layout */ // The render layout is directly added and visible on the window - _render_layout = new RenderLayout(_ui_render_area_container_widget, _ui_render_area_container_layout, render_area); + _render_layout = new RenderLayout(this, _ui_render_area_container_widget, _ui_render_area_container_layout, render_area); render_area->set_layout(_render_layout); } @@ -1195,4 +1195,10 @@ void Interface_graphic::set_axis_icon(const bool is_along_y_axis) { goto_end->setIcon(icon_end_x); } +void Interface_graphic::update_interval_select() { + if (nullptr != _ui_interval_selection) { + _ui_interval_selection->update_values(); + } +} + #include "moc_Interface_graphic.cpp" diff --git a/src/interface/Interface_graphic.hpp b/src/interface/Interface_graphic.hpp index f6fdd85407afdf5d62322764cc84068a83f81cb8..1a87303704386e5e6525eacb12967f6b3159a554 100644 --- a/src/interface/Interface_graphic.hpp +++ b/src/interface/Interface_graphic.hpp @@ -384,6 +384,11 @@ public: */ Interval_select *get_interval_select(); + /** + * \brief Called by RenderLayout to update slider value in IntervalSelect when it is change from the view + */ + void update_interval_select(); + /*! * \fn update_recent_files_menu() * \brief update the recent files opened menu diff --git a/src/interface/Interval_select.cpp b/src/interface/Interval_select.cpp index 593592355d537af0fe33cbc35b9c83602557be4d..872e4ee97f2ec8c953f040d7d3b0e1fe3b41dacf 100644 --- a/src/interface/Interval_select.cpp +++ b/src/interface/Interval_select.cpp @@ -71,6 +71,8 @@ Interval_select::Interval_select(Interface_graphic *interface_graphic) : // Saving this value prevents it to be computed again several times in the code. _range_span = _range_slider.maximum() - _range_slider.minimum(); + _max_possible_value = 0.; + _is_slider_moving = false; } Interval_select::~Interval_select() = default; @@ -79,11 +81,12 @@ void Interval_select::init() { min_value->setText(QString().setNum(Info::Entity::x_min)); max_value->setText(QString().setNum(Info::Entity::x_max)); + _max_possible_value = Info::Entity::x_max; minSpinBox->setMinimum(Info::Entity::x_min); - minSpinBox->setMaximum(Info::Entity::x_max); + minSpinBox->setMaximum(INT32_MAX); maxSpinBox->setMinimum(Info::Entity::x_min); - maxSpinBox->setMaximum(Info::Entity::x_max); + maxSpinBox->setMaximum(INT32_MAX); double step = 0.001 * (Info::Entity::x_max - Info::Entity::x_min); // make steps of .1% for spinboxes minSpinBox->setSingleStep(step); @@ -119,22 +122,31 @@ void Interval_select::update_values() { v = maxSpinBox->blockSignals(true); maxSpinBox->setValue(_max); + if (!_is_slider_moving) { + max_value->setText(QString().setNum(max(maxSpinBox->value(), (double)Info::Entity::x_max))); + _max_possible_value = max(maxSpinBox->value(), (double)Info::Entity::x_max); + } maxSpinBox->blockSignals(v); v = _range_slider.blockSignals(true); - _range_slider.setLow(int(_min * _range_span / (_interface_graphic->get_trace()->get_max_date() - minSpinBox->minimum()))); - _range_slider.setHigh(int(_max * _range_span / (_interface_graphic->get_trace()->get_max_date() - minSpinBox->minimum()))); + _range_slider.setLow(int(_min * _range_span / (_max_possible_value - maxSpinBox->minimum()))); + _range_slider.setHigh(int(_max * _range_span / (_max_possible_value - maxSpinBox->minimum()))); _range_slider.blockSignals(v); } } void Interval_select::on_minSpinBox_valueChanged(double value) { + /** + * This method can also change the maxSpinBox value on large trace + * This is due to scaled float precision error in the apply_zoom_box function (called by apply_settings) + */ + QPalette myPalette(minSpinBox->palette()); if (value < maxSpinBox->value()) { // Change the value of the slider bool v = _range_slider.blockSignals(true); - _range_slider.setLow(int(value * _range_span / (minSpinBox->maximum() - minSpinBox->minimum()))); + _range_slider.setLow(int(value * _range_span / (_max_possible_value - maxSpinBox->minimum()))); _range_slider.blockSignals(v); myPalette.setColor(QPalette::Active, QPalette::Text, Qt::black); @@ -158,12 +170,19 @@ void Interval_select::on_minSpinBox_valueChanged(double value) { } void Interval_select::on_maxSpinBox_valueChanged(double value) { + /** + * This method can also change the minSpinBox value on large trace + * This is due to scaled float precision error in the apply_zoom_box function (called by apply_settings) + */ QPalette myPalette(minSpinBox->palette()); if (value > minSpinBox->value()) { + max_value->setText(QString().setNum(max(maxSpinBox->value(), (double)Info::Entity::x_max))); + _max_possible_value = max(maxSpinBox->value(), (double)Info::Entity::x_max); + bool v = _range_slider.blockSignals(true); - _range_slider.setHigh(int(value * _range_span / (maxSpinBox->maximum() - maxSpinBox->minimum()))); + _range_slider.setHigh(int(value * _range_span / (_max_possible_value - maxSpinBox->minimum()))); _range_slider.blockSignals(v); myPalette.setColor(QPalette::Active, QPalette::Text, Qt::black); @@ -192,15 +211,21 @@ void Interval_select::on_sliderMoved(int low, int high) { // thus changing the value of the slider creating an infinite loop // The signal is block to prevent these infinite loops bool v = minSpinBox->blockSignals(true); - minSpinBox->setValue(low * (_interface_graphic->get_trace()->get_max_date() - minSpinBox->minimum()) / _range_span); + minSpinBox->setValue(low * (_max_possible_value - maxSpinBox->minimum()) / _range_span); minSpinBox->blockSignals(v); v = maxSpinBox->blockSignals(true); - maxSpinBox->setValue(high * (_interface_graphic->get_trace()->get_max_date() - maxSpinBox->minimum()) / _range_span); + maxSpinBox->setValue(high * (_max_possible_value - maxSpinBox->minimum()) / _range_span); maxSpinBox->blockSignals(v); if (auto_refresh_box->isChecked()) { + // apply_settings refresh the scroll bar which calls update_values + // This is because we want to update the values when the scroll bar is changed by the user + // But this changes _max_possible_value + // A bool is then set so _max_possible_value is not changed when slider is moved + _is_slider_moving = true; apply_settings(); + _is_slider_moving = false; return; } @@ -230,6 +255,8 @@ void Interval_select::apply_settings() { void Interval_select::on_reset_button_clicked() { Info::Render::_x_min = Info::Entity::x_min; Info::Render::_x_max = Info::Entity::x_max; + max_value->setText(QString().setNum(Info::Entity::x_max)); + _max_possible_value = Info::Entity::x_max; update_values(); if (auto_refresh_box->isChecked()) { diff --git a/src/interface/Interval_select.hpp b/src/interface/Interval_select.hpp index 63400726d58a4acdba13050cf8679053f7cfdbdb..407927ebdcdad1eea1fd0c67ef01d20da22273b5 100644 --- a/src/interface/Interval_select.hpp +++ b/src/interface/Interval_select.hpp @@ -59,6 +59,16 @@ private: RangeSliderWidget _range_slider; int _range_span; + /*! + * \brief Maximum time reachable by the slider + */ + double _max_possible_value; + + /*! + * \brief Boolean preventing _max_possible_value changing when the slider is moved + */ + bool _is_slider_moving; + public: /*! * Default constructor diff --git a/src/render/Geometry.cpp b/src/render/Geometry.cpp index 8fbdd711927556b608406b78dc1cfa93581c18ae..e3f1c74c56b60b8d949935c433436a3dfdc95c82 100644 --- a/src/render/Geometry.cpp +++ b/src/render/Geometry.cpp @@ -160,6 +160,6 @@ Element_pos Geometry::coeff_trace_render_y() const { void Geometry::update_visible_interval_value() const { Info::Render::_x_min_visible = max(Info::Entity::x_min, (_x_state_translate - _default_entity_x_translate) / coeff_trace_render_x()); Info::Render::_x_min = max(Info::Entity::x_min, _x_state_translate / coeff_trace_render_x()); - Info::Render::_x_max = min(Info::Entity::x_max, (_x_state_translate - _default_entity_x_translate + Info::Render::width) / coeff_trace_render_x()); + Info::Render::_x_max = (_x_state_translate - _default_entity_x_translate + Info::Render::width) / coeff_trace_render_x(); Info::Render::_x_max_visible = min(Info::Entity::x_max, (_x_state_translate - _default_entity_x_translate + Info::Render::width) / coeff_trace_render_x()); } diff --git a/src/render/RenderLayout.cpp b/src/render/RenderLayout.cpp index 5765f43d309a16746482d5c755d49689eb7f091c..822f5779ea0bf44f8ec5cce2cf09d9f552834d1a 100644 --- a/src/render/RenderLayout.cpp +++ b/src/render/RenderLayout.cpp @@ -24,8 +24,9 @@ /* -- */ #include "trace/Trace.hpp" -RenderLayout::RenderLayout(QWidget *parent_widget, QVBoxLayout *parent_layout, Render_windowed *render_area) : +RenderLayout::RenderLayout(Interface_graphic *interface_graphic, QWidget *parent_widget, QVBoxLayout *parent_layout, Render_windowed *render_area) : QObject(), + _interface_graphic(interface_graphic), _slider_x_position(0), _slider_y_position(0) { // In the following code Qt objects are allocated using new @@ -119,8 +120,9 @@ RenderLayout::RenderLayout(QWidget *parent_widget, QVBoxLayout *parent_layout, R font.setItalic(true); _zoom_box_x->setFont(font); _zoom_box_x->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - _zoom_box_x->setMinimum(100); + _zoom_box_x->setMinimum(Render_abstract::_MIN_X_ZOOM_VALUE * 100); _zoom_box_x->setMaximum(Render_abstract::_MAX_ZOOM_VALUE * 100); + _zoom_box_x->setValue(100); _zoom_box_x->setStepType(QAbstractSpinBox::AdaptiveDecimalStepType); scroll_widget_layout->addWidget(_zoom_box_x); @@ -266,12 +268,13 @@ void RenderLayout::scroll_x_value_changed(int new_value) { // At scale 1, slider can't move -> maximum == 0 // At scale 2, slider is half the size of the entire range -> maximum == _SCROLL_PAGE_STEP // This interpolates the default maximum value - const int x_default_max = (x_scale_factor - 1) * _SCROLL_PAGE_STEP; + const int x_default_max = std::max((x_scale_factor - 1) * _SCROLL_PAGE_STEP, 0.f); const int &margin = _render_area->get_scroll_margin_x(); // Default behaviour when slider is not past the left nor the right if (_scroll_bar_x->maximum() == x_default_max) { _render_area->replace_translate_x(((Element_pos)new_value / _SCROLL_PAGE_STEP) * (2 * margin)); + _interface_graphic->update_interval_select(); return; } @@ -295,6 +298,7 @@ void RenderLayout::scroll_x_value_changed(int new_value) { const float margin_coeff = (float)(_scroll_bar_x->maximum() - x_default_max) / (_SCROLL_PAGE_STEP * x_scale_factor); const Element_pos x_value = -margin_coeff * margin; _render_area->replace_translate_x(x_value); + _interface_graphic->update_interval_select(); return; } @@ -312,8 +316,9 @@ void RenderLayout::scroll_x_value_changed(int new_value) { // Move the view // margin_coeff goes from 0 to 1 and count how much the slider is too far right const float margin_coeff = (_scroll_bar_x->maximum() - x_default_max) / (_SCROLL_PAGE_STEP * x_scale_factor); - const Element_pos x_value = (margin_coeff + ((x_scale_factor - 1) * 2)) * margin; + const Element_pos x_value = (margin_coeff + std::max(x_scale_factor - 1, 0.f) * 2) * margin; _render_area->replace_translate_x(x_value); + _interface_graphic->update_interval_select(); return; } @@ -393,8 +398,8 @@ void RenderLayout::refresh_scroll_bars(const Element_pos &x_value, const Element // Change x scroll bar const int &margin_x = _render_area->get_scroll_margin_x(); const Element_pos x_scale_factor = (float)_zoom_box_x->value() / 100; - const int x_default_max = (x_scale_factor - 1) * _SCROLL_PAGE_STEP; - const Element_pos x_translation_range = (x_scale_factor - 1) * 2 * margin_x; + const int x_default_max = std::max((x_scale_factor - 1) * _SCROLL_PAGE_STEP, 0.f); + const Element_pos x_translation_range = std::max((x_scale_factor - 1) * 2 * margin_x, 0.f); if (x_value < 0) { // If the view is too far left, resize down the scroll bar const float margin_coeff = (-x_value / margin_x); @@ -435,6 +440,7 @@ void RenderLayout::refresh_scroll_bars(const Element_pos &x_value, const Element } _scroll_bar_y->blockSignals(last_block_state); + _interface_graphic->update_interval_select(); } void RenderLayout::scale_container_value_changed(const int &new_value) { diff --git a/src/render/RenderLayout.hpp b/src/render/RenderLayout.hpp index 67c6e1bd3ea0e1a0e94539fd6cd87ca1d1a867bc..398817271d328afaf5947e3c00964ed510b9a0a3 100644 --- a/src/render/RenderLayout.hpp +++ b/src/render/RenderLayout.hpp @@ -17,6 +17,8 @@ #ifndef _RENDER_LAYOUT_ #define _RENDER_LAYOUT_ +#include "interface/Interface_graphic.hpp" +/* -- */ #include "Render_windowed.hpp" /* -- */ #include <QScrollBar> @@ -32,6 +34,8 @@ class RenderLayout : public QObject Q_OBJECT private: + Interface_graphic *_interface_graphic; + int _slider_x_position; int _slider_y_position; @@ -65,7 +69,7 @@ private: static const int _SCROLL_PAGE_STEP = 1000; public: - RenderLayout(QWidget *parent_widget, QVBoxLayout *parent_layout, Render_windowed *render_area); + RenderLayout(Interface_graphic *interface_graphic, QWidget *parent_widget, QVBoxLayout *parent_layout, Render_windowed *render_area); ~RenderLayout(); diff --git a/src/render/Render_abstract.cpp b/src/render/Render_abstract.cpp index e8e4b9ae7396dc34db0695e02cd56b597dfaa941..794fe6ade9f5323b874ac195e7068fcd07f170a2 100644 --- a/src/render/Render_abstract.cpp +++ b/src/render/Render_abstract.cpp @@ -39,6 +39,8 @@ using namespace std; * **********************************/ +const float Render_abstract::_MIN_X_ZOOM_VALUE = 0.01f; + Render_abstract::Render_abstract(Interface_graphic *interface_graphic) : _interface_graphic(interface_graphic), _layout(nullptr), _scroll_margin_x((Info::Render::width - _default_entity_x_translate) / 2), _scroll_margin_y((Info::Render::height - _ruler_height) / 2) { @@ -183,8 +185,8 @@ void Render_abstract::change_scale_y(const Element_pos &scale_coeff) { void Render_abstract::replace_scale_x(Element_pos new_scale) { // Clamp scale value - if (new_scale < 1.0) { - new_scale = 1.0; + if (new_scale < _MIN_X_ZOOM_VALUE) { + new_scale = _MIN_X_ZOOM_VALUE; } else if (new_scale > _MAX_ZOOM_VALUE) { new_scale = _MAX_ZOOM_VALUE; @@ -245,8 +247,8 @@ void Render_abstract::replace_translate_x(const Element_pos &new_translate) { if (new_translate < -_scroll_margin_x) { _x_state_translate = -_scroll_margin_x; } - else if (new_translate > _scroll_margin_x + (_x_state_scale - 1) * (Info::Render::width - _default_entity_x_translate)) { - _x_state_translate = _scroll_margin_x + (_x_state_scale - 1) * (Info::Render::width - _default_entity_x_translate); + else if (new_translate > max(_scroll_margin_x + (_x_state_scale - 1) * (Info::Render::width - _default_entity_x_translate), 0.f)) { + _x_state_translate = max(_scroll_margin_x + (_x_state_scale - 1) * (Info::Render::width - _default_entity_x_translate), 0.f); } else { _x_state_translate = new_translate; @@ -288,7 +290,7 @@ void Render_abstract::registered_translate(int id) { _y_state_translate = -((Element_pos)Info::Render::height - _ruler_height) / 2.0; break; case Info::Render::X_TRACE_ENDING: /* show the ending entities */ - _x_state_translate = _scroll_margin_x + (_x_state_scale - 1) * (Info::Render::width - _default_entity_x_translate); + _x_state_translate = max(_scroll_margin_x + (_x_state_scale - 1) * (Info::Render::width - _default_entity_x_translate), 0.f); break; case Info::Render::Y_TRACE_ENDING: /* show the ending entities */ _y_state_translate = _scroll_margin_y + (_y_state_scale - 1) * (Info::Render::height - _ruler_height); diff --git a/src/render/Render_abstract.hpp b/src/render/Render_abstract.hpp index 35b183d5325dfafc03a8685f6db86584ad67ec1c..7bec5f7e12e3e5f033966cdd02e1b029a404744d 100644 --- a/src/render/Render_abstract.hpp +++ b/src/render/Render_abstract.hpp @@ -75,6 +75,14 @@ public: */ static const int _MAX_ZOOM_VALUE = 200000; // Max value above which the application stops working + /*! + * \brief Minimum x scale factor + * The value is set in Render_abstract.cpp because of cpp specification + * related to static const definitions + * Only static const int and enum can be declared inside the class definition. + */ + static const float _MIN_X_ZOOM_VALUE; + /*********************************** * * Constructor and destructor.