diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 626d01802..582113cd8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,6 +195,7 @@ set(UI_TEST_SOURCES ${GUI_TESTDIR}/test_field_numchoice.cpp ${GUI_TESTDIR}/test_field_point.cpp ${GUI_TESTDIR}/test_field_point3.cpp + ${GUI_TESTDIR}/test_field_colorpicker.cpp ${GUI_TESTDIR}/test_misc_ui.cpp ) @@ -311,6 +312,7 @@ IF(wxWidgets_FOUND) ${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp ${GUI_LIBDIR}/OptionsGroup/UI_Point.cpp ${GUI_LIBDIR}/OptionsGroup/UI_Point3.cpp + ${GUI_LIBDIR}/OptionsGroup/UI_Color.cpp ${LIBDIR}/slic3r/GUI/3DScene.cpp ) target_compile_features(slic3r_gui PUBLIC cxx_std_14) diff --git a/src/GUI/OptionsGroup/Field.hpp b/src/GUI/OptionsGroup/Field.hpp index ccfab0fdb..fce554d77 100644 --- a/src/GUI/OptionsGroup/Field.hpp +++ b/src/GUI/OptionsGroup/Field.hpp @@ -18,6 +18,8 @@ #include "wx/arrstr.h" #include "wx/stattext.h" #include "wx/sizer.h" +#include +#include namespace Slic3r { namespace GUI { @@ -395,6 +397,27 @@ private: }; +class UI_Color : public UI_Window { +public: + UI_Color(wxWindow* parent, Slic3r::ConfigOptionDef _opt ); + ~UI_Color() { _picker->Destroy(); } + wxColourPickerCtrl* picker() { return this->_picker; } + + void set_value(boost::any value) override; + std::string get_string() override; + std::function on_change {nullptr}; +protected: + virtual std::string LogChannel() override { return "UI_Color"s; } + void _on_change(std::string opt_id) override { + if (!this->disable_change_event && this->_picker->IsEnabled() && this->on_change != nullptr) { + this->on_change(opt_id, _picker->GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString()); + } + } +private: + wxColour _string_to_color(const std::string& _color); + wxColourPickerCtrl* _picker {nullptr}; +}; + } } // Namespace Slic3r::GUI #endif // SLIC3R_FIELD_HPP diff --git a/src/GUI/OptionsGroup/UI_Color.cpp b/src/GUI/OptionsGroup/UI_Color.cpp new file mode 100644 index 000000000..9577b9dcd --- /dev/null +++ b/src/GUI/OptionsGroup/UI_Color.cpp @@ -0,0 +1,42 @@ +#include "OptionsGroup/Field.hpp" +#include "misc_ui.hpp" + +namespace Slic3r { namespace GUI { + +UI_Color::UI_Color(wxWindow* parent, Slic3r::ConfigOptionDef _opt ) : UI_Window(parent, _opt) { + wxColour default_color(255,255,255,255); + if (_opt.default_value != nullptr) { + default_color = _string_to_color(_opt.default_value->getString()); + } + this->_picker = new wxColourPickerCtrl(parent, wxID_ANY, default_color, wxDefaultPosition, this->_default_size()); + this->window = dynamic_cast(this->_picker); + +} + +void UI_Color::set_value(boost::any value) { + if (value.type() == typeid(wxColour)) { + _picker->SetColour(boost::any_cast(value)); + } else if (value.type() == typeid(std::string)) { + _picker->SetColour(wxString(boost::any_cast(value))); + } else if (value.type() == typeid(const char*)) { + _picker->SetColour(wxString(boost::any_cast(value))); + } else if (value.type() == typeid(wxString)) { + _picker->SetColour(boost::any_cast(value)); + } else { + Slic3r::Log::warn(this->LogChannel(), LOG_WSTRING("Type " << value.type().name() << " is not handled in set_value.")); + } +} + +std::string UI_Color::get_string() { + return _picker->GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); +} + +wxColour UI_Color::_string_to_color(const std::string& color) { + // if invalid color string sent, use the default + wxColour col(255,255,255,255); + if (col.Set(wxString(color))) + return col; + return wxColor(); +} + +} } // Namespace Slic3r::GUI diff --git a/src/test/GUI/test_field_colorpicker.cpp b/src/test/GUI/test_field_colorpicker.cpp new file mode 100644 index 000000000..f09a413f6 --- /dev/null +++ b/src/test/GUI/test_field_colorpicker.cpp @@ -0,0 +1,110 @@ +#include + +#ifndef WX_PRECOMP +#include +#include +#include +#include +#include +#endif // WX_PRECOMP + +#include +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" +using namespace std::string_literals; + +SCENARIO( "UI_ColorPick: default values from options and basic accessor methods") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + auto simple_option {ConfigOptionDef()}; + auto* default_color {new ConfigOptionString("#FFFF00")}; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, const std::string& color) { event_count++; }}; + GIVEN("A Color Picker") { + auto test_field {Slic3r::GUI::UI_Color(wxTheApp->GetTopWindow(), simple_option)}; + WHEN("Object is constructed with default_value of '#FFFF00'.") { + THEN("get_string() returns '#FFFF00'") { + REQUIRE(test_field.get_string() == "#FFFF00"s); + } + THEN("get_int() returns 0") { + REQUIRE(test_field.get_int() == 0); + } + } + WHEN("Color picker receives a color picked event") { + event_count = 0; + test_field.disable_change_event = false; + auto ev {wxFocusEvent(wxEVT_COLOURPICKER_CHANGED, test_field.picker()->GetId())}; + ev.SetEventObject(test_field.picker()); + test_field.picker()->ProcessWindowEvent(ev); + THEN("_on_change fires.") { + REQUIRE(event_count == 1); + } + } + } +} +SCENARIO( "Color string value tests") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + auto simple_option {ConfigOptionDef()}; + auto* default_color {new ConfigOptionString("#FFFFFF")}; + GIVEN("A Color Picker") { + auto test_field {Slic3r::GUI::UI_Color(wxTheApp->GetTopWindow(), simple_option)}; + WHEN("Set_value is called with a string of '#FFFFFF'") { + test_field.set_value("#FFFFFF"); + THEN("Internal wxColor is equal to wxWhite") { + REQUIRE(test_field.picker()->GetColour() == wxColour(*wxWHITE)); + } + } + WHEN("Set_value is called with a string of '#FFAACC'") { + test_field.set_value("#FFAACC"); + THEN("Internal wxColor is equal to wxColor(255, 170, 204)") { + REQUIRE(test_field.picker()->GetColour() == wxColour(255, 170, 204)); + } + } + WHEN("Set_value is called with a string of '#3020FF'") { + test_field.set_value("#3020FF"); + THEN("Internal wxColor is equal to wxColor(48, 32, 255)") { + REQUIRE(test_field.picker()->GetColour() == wxColour(48,32,255)); + } + } + WHEN("Set_value is called with a string of '#01A06D'") { + test_field.set_value("#01A06D"); + THEN("Internal wxColor is equal to wxColor(01, 160, 109)") { + REQUIRE(test_field.picker()->GetColour() == wxColour(1,160,109)); + } + } + WHEN("Internal color is set to wxWHITE") { + test_field.picker()->SetColour(wxColour(*wxWHITE)); + THEN("String value is #FFFFFF") { + REQUIRE(test_field.get_string() == "#FFFFFF"s); + } + } + WHEN("Internal color is set to wxRED") { + test_field.picker()->SetColour(wxColour(*wxRED)); + THEN("String value is #FF0000") { + REQUIRE(test_field.get_string() == "#FF0000"s); + } + } + WHEN("Internal color is set to wxGREEN") { + test_field.picker()->SetColour(wxColour(*wxGREEN)); + THEN("String value is #00FF00") { + REQUIRE(test_field.get_string() == "#00FF00"s); + } + } + WHEN("Internal color is set to wxBLUE") { + test_field.picker()->SetColour(wxColour(*wxBLUE)); + THEN("String value is #0000FF") { + REQUIRE(test_field.get_string() == "#0000FF"s); + } + } + } + delete default_color; +} +