From 17e588904b633a4d4b22fd5127a7e219896f61f0 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 23 Jul 2018 09:45:25 -0500 Subject: [PATCH] Continued implementation for UI_Slider Added enable/disable methods and set_range() text control should update as well. Added tests to build. --- src/CMakeLists.txt | 1 + src/GUI/OptionsGroup/Field.hpp | 14 ++++- src/GUI/OptionsGroup/UI_Slider.cpp | 82 +++++++++++++++++++++++++++--- src/test/GUI/test_field_slider.cpp | 29 +++++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cccf4f2ed..b45e5eee4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -193,6 +193,7 @@ set(UI_TEST_SOURCES ${GUI_TESTDIR}/test_field_point.cpp ${GUI_TESTDIR}/test_field_point3.cpp ${GUI_TESTDIR}/test_field_colorpicker.cpp + ${GUI_TESTDIR}/test_field_slider.cpp ${GUI_TESTDIR}/test_misc_ui.cpp ) diff --git a/src/GUI/OptionsGroup/Field.hpp b/src/GUI/OptionsGroup/Field.hpp index e831bf40e..b83774288 100644 --- a/src/GUI/OptionsGroup/Field.hpp +++ b/src/GUI/OptionsGroup/Field.hpp @@ -421,8 +421,7 @@ private: class UI_Slider : public UI_Sizer { public: - UI_Slider(wxWindow* parent, Slic3r::ConfigOptionDef _opt); - UI_Slider(wxWindow* parent, Slic3r::ConfigOptionDef _opt, size_t scale); + UI_Slider(wxWindow* parent, Slic3r::ConfigOptionDef _opt, size_t scale = 10); ~UI_Slider(); @@ -431,11 +430,21 @@ public: double get_double() override; int get_int() override; + void enable() override; + void disable() override; + + /// change the min/max of the built-in slider + template void set_range(T min, T max); + /// Change the scale of the slider bar. Return value from get_X functions does not change. void set_scale(size_t new_scale); + /// Returns pointer to owned wxSlider. wxSlider* slider() { return _slider;} + /// Returns pointer to owned wxTextCtrl. wxTextCtrl* textctrl() { return _textctrl;} + + /// Registered on_change callback. std::function on_change {nullptr}; protected: virtual std::string LogChannel() override { return "UI_Slider"s; } @@ -445,6 +454,7 @@ private: this->on_change(opt_id, _slider->GetValue() / _scale); } } + void _update_textctrl(); wxTextCtrl* _textctrl {nullptr}; wxSlider* _slider {nullptr}; size_t _scale {10}; diff --git a/src/GUI/OptionsGroup/UI_Slider.cpp b/src/GUI/OptionsGroup/UI_Slider.cpp index be53650b9..8bddf2a1f 100644 --- a/src/GUI/OptionsGroup/UI_Slider.cpp +++ b/src/GUI/OptionsGroup/UI_Slider.cpp @@ -3,28 +3,98 @@ namespace Slic3r { namespace GUI { -UI_Slider::UI_Slider(wxWindow* parent, Slic3r::ConfigOptionDef _opt) : UI_Sizer(parent, _opt), _scale(10) { -} -UI_Slider::UI_Slider(wxWindow* parent, Slic3r::ConfigOptionDef _opt, size_t scale ) : UI_Sizer(parent, _opt), _scale(scale) { +UI_Slider::UI_Slider(wxWindow* parent, Slic3r::ConfigOptionDef _opt, size_t scale) : UI_Sizer(parent, _opt), _scale(scale) { + double default_value {0.0}; + + sizer = new wxBoxSizer(wxHORIZONTAL); + _slider = new wxSlider(parent, wxID_ANY, + (default_value < _opt.min ? _opt.min : default_value) * this->_scale, + (_opt.min > _opt.max || _opt.min == INT_MIN ? 0 : _opt.min) * this->_scale, + (_opt.max <= _opt.min || _opt.max == INT_MAX ? 100 : _opt.max) * this->_scale, + wxDefaultPosition, + wxSize(_opt.width, _opt.height)); + + _textctrl = new wxTextCtrl(parent, wxID_ANY, + static_cast(_slider->GetValue()) / this->_scale, + wxDefaultPosition, + wxSize(50, -1)); + + + sizer->Add(_slider, 1, wxALIGN_CENTER_VERTICAL, 0); + sizer->Add(_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); + + _textctrl->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent& e) { this->_on_change(""); e.Skip(); }); + _textctrl->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { if (this->on_kill_focus != nullptr) {this->on_kill_focus(""); this->_on_change("");} e.Skip(); }); + + _slider->Bind(wxEVT_SLIDER, [this](wxCommandEvent& e) { this->_on_change(""); e.Skip(); }); + + } UI_Slider::~UI_Slider() { _slider->Destroy(); _textctrl->Destroy(); } void UI_Slider::set_value(boost::any value) { + this->disable_change_event = true; + if (value.type() == typeid(int)) { + this->_slider->SetValue(boost::any_cast(value) * this->_scale); + } else if (value.type() == typeid(double)) { + this->_slider->SetValue(boost::any_cast(value) * this->_scale); + } else if (value.type() == typeid(float)) { + this->_slider->SetValue(boost::any_cast(value) * this->_scale); + } else if (value.type() == typeid(std::string)) { + std::string _val = boost::any_cast(value); + try { + this->_slider->SetValue(std::stod(_val) * this->_scale); + } catch (std::invalid_argument &e) { + Slic3r::Log::error(this->LogChannel()) << "Conversion to numeric from string failed.\n"; + } + } else if (value.type() == typeid(wxString)) { + std::string _val = boost::any_cast(value).ToStdString(); + try { + this->_slider->SetValue(std::stod(_val) * this->_scale); + } catch (std::invalid_argument &e) { + Slic3r::Log::error(this->LogChannel()) << "Conversion to numeric from string failed.\n"; + } + } else { + Slic3r::Log::warn(this->LogChannel(), LOG_WSTRING("Type " << value.type().name() << " is not handled in set_value.")); + } + this->_update_textctrl(); + this->disable_change_event = false; } double UI_Slider::get_double() { - return 0.0; + return static_cast(this->_slider->GetValue()) / this->_scale; } int UI_Slider::get_int() { - return 0; + return static_cast(this->_slider->GetValue()) / this->_scale; } std::string UI_Slider::get_string() { - return std::string(); + return trim_zeroes(std::to_string(static_cast(this->_slider->GetValue()) / this->_scale)); } void UI_Slider::set_scale(size_t new_scale) { + this->disable_change_event = true; + auto current_value {this->get_double()}; + + this->_slider->SetRange( + this->_slider->GetMin() / this->_scale * new_scale, + this->_slider->GetMax() / this->_scale * new_scale); + this->_scale = new_scale; + this->set_value(current_value); + + this->disable_change_event = false; } + +void UI_Slider::_update_textctrl() { + this->_textctrl->ChangeValue(this->get_string()); + this->_textctrl->SetInsertionPointEnd(); +} + +template +void UI_Slider::set_range(T min, T max) { + this->_slider->SetRange(static_cast(min * static_cast(self->_scale)), static_cast(max * static_cast(self->_scale))); +} + } } // Namespace Slic3r::GUI diff --git a/src/test/GUI/test_field_slider.cpp b/src/test/GUI/test_field_slider.cpp index 31cdb58c0..e8fed9370 100644 --- a/src/test/GUI/test_field_slider.cpp +++ b/src/test/GUI/test_field_slider.cpp @@ -77,6 +77,34 @@ SCENARIO( "UI_Slider: Defaults, Min/max handling, accessors.") { REQUIRE(test_field.textctrl()->GetValue() == "0.0"); } } + WHEN("disable() is called") { + test_field.slider()->Enable(); + test_field.textctrl()->Enable(); + test_field.textctrl()->SetEditable(true); + + test_field.disable(); + THEN("Internal slider is disabled.") { + REQUIRE(test_field.slider()->IsEnabled() == false); + } + THEN("Internal textctrl is disabled.") { + REQUIRE(test_field.textctrl()->IsEnabled() == false); + REQUIRE(test_field.textctrl()->IsEditable() == false); + } + } + WHEN("enable() is called") { + test_field.slider()->Disable(); + test_field.textctrl()->Disable(); + test_field.textctrl()->SetEditable(false); + + test_field.enable(); + THEN("Internal slider is enabled.") { + REQUIRE(test_field.slider()->IsEnabled() == true); + } + THEN("Internal textctrl is enabled.") { + REQUIRE(test_field.textctrl()->IsEnabled() == true); + REQUIRE(test_field.textctrl()->IsEditable() == true); + } + } } GIVEN("A UI Slider with scale of 1") { @@ -98,6 +126,7 @@ SCENARIO( "UI_Slider: Defaults, Min/max handling, accessors.") { } } } + SCENARIO( "UI_Slider: Event handlers") { wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); old->Destroy();