Partially implemented UI_Point. Tests do not all pass (set_value is inserting garbage when a serialized string is passed in).

This commit is contained in:
Joseph Lenox 2018-07-04 01:05:09 -05:00 committed by Joseph Lenox
parent 04bc767421
commit 2c7b39f0fd
4 changed files with 359 additions and 0 deletions

View File

@ -181,6 +181,7 @@ set(UI_TEST_SOURCES
${GUI_TESTDIR}/test_field_textbox.cpp ${GUI_TESTDIR}/test_field_textbox.cpp
${GUI_TESTDIR}/test_field_choice.cpp ${GUI_TESTDIR}/test_field_choice.cpp
${GUI_TESTDIR}/test_field_numchoice.cpp ${GUI_TESTDIR}/test_field_numchoice.cpp
${GUI_TESTDIR}/test_field_point.cpp
) )
set(SLIC3R_TEST_SOURCES set(SLIC3R_TEST_SOURCES
${TESTDIR}/test_harness.cpp ${TESTDIR}/test_harness.cpp
@ -261,6 +262,7 @@ IF(wxWidgets_FOUND)
${GUI_LIBDIR}/misc_ui.cpp ${GUI_LIBDIR}/misc_ui.cpp
${GUI_LIBDIR}/OptionsGroup/UI_NumChoice.cpp ${GUI_LIBDIR}/OptionsGroup/UI_NumChoice.cpp
${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp ${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp
${GUI_LIBDIR}/OptionsGroup/UI_Point.cpp
) )
target_compile_features(slic3r_gui PUBLIC cxx_std_14) target_compile_features(slic3r_gui PUBLIC cxx_std_14)
#only build GUI lib if building with wx #only build GUI lib if building with wx

View File

@ -15,6 +15,8 @@
#include "wx/textctrl.h" #include "wx/textctrl.h"
#include "wx/combobox.h" #include "wx/combobox.h"
#include "wx/arrstr.h" #include "wx/arrstr.h"
#include "wx/stattext.h"
#include "wx/sizer.h"
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
@ -245,6 +247,7 @@ public:
/// Function to call when the contents of this change. /// Function to call when the contents of this change.
std::function<void (const std::string&, std::string value)> on_change {nullptr}; std::function<void (const std::string&, std::string value)> on_change {nullptr};
protected: protected:
virtual std::string LogChannel() override { return "UI_NumChoice"s; } virtual std::string LogChannel() override { return "UI_NumChoice"s; }
@ -262,7 +265,63 @@ private:
std::regex show_value_flag {"\bshow_value\b"}; std::regex show_value_flag {"\bshow_value\b"};
}; };
class UI_Point {
public:
UI_Point(wxWindow* parent, Slic3r::ConfigOptionDef _opt, wxWindowID id = wxID_ANY);
~UI_Point() { _lbl_x->Destroy(); _lbl_y->Destroy(); _ctrl_x->Destroy(); _ctrl_y->Destroy(); }
std::string get_string();
Pointf get_point();
/// Return the underlying sizer.
wxSizer* get_sizer() { return _sizer; };
void set_value(boost::any value);
/// Function to call when the contents of this change.
std::function<void (const std::string&, std::tuple<std::string, std::string> value)> on_change {nullptr};
std::function<void (const std::string&)> on_kill_focus {nullptr};
wxTextCtrl* ctrl_x() { return _ctrl_x;}
wxTextCtrl* ctrl_y() { return _ctrl_y;}
wxStaticText* lbl_x() { return _lbl_x;}
wxStaticText* lbl_y() { return _lbl_y;}
void enable() { _ctrl_x->Enable(); _ctrl_y->Enable(); }
void disable() { _ctrl_x->Disable(); _ctrl_y->Disable(); }
void toggle(bool en = true) { en ? this->enable() : this->disable(); }
protected:
virtual std::string LogChannel() { return "UI_Point"s; }
private:
wxSize field_size {40, 1};
wxStaticText* _lbl_x {nullptr};
wxStaticText* _lbl_y {nullptr};
wxTextCtrl* _ctrl_x {nullptr};
wxTextCtrl* _ctrl_y {nullptr};
wxBoxSizer* _sizer {nullptr};
void _set_value(Slic3r::Pointf value);
void _set_value(std::string value);
/// Remove extra zeroes generated from std::to_string on doubles
std::string trim_zeroes(std::string in) {
std::string result {""};
std::regex strip_zeroes("(0*)$");
std::regex_replace (std::back_inserter(result), in.begin(), in.end(), strip_zeroes, "");
if (result.back() == '.') result.append("0");
return result;
}
wxString trim_zeroes(wxString in) { return wxString(trim_zeroes(in.ToStdString())); }
};
} } // Namespace Slic3r::GUI } } // Namespace Slic3r::GUI

View File

@ -0,0 +1,69 @@
#include "OptionsGroup/Field.hpp"
#include "misc_ui.hpp"
#include "Log.hpp"
#include <regex>
namespace Slic3r { namespace GUI {
using namespace std::string_literals;
std::string UI_Point::get_string() {
std::string result {""};
result.append(trim_zeroes(_ctrl_x->GetValue().ToStdString()));
result.append(";"s);
result.append(trim_zeroes(_ctrl_y->GetValue().ToStdString()));
return result;
}
Slic3r::Pointf UI_Point::get_point() {
return Pointf(std::stod(this->_ctrl_x->GetValue().ToStdString()), std::stod(this->_ctrl_y->GetValue().ToStdString()));
}
void UI_Point::set_value(boost::any value) {
// type detection and handing off to children
if (value.type() == typeid(Slic3r::Pointf)) {
this->_set_value(boost::any_cast<Pointf>(value));
} else if (value.type() == typeid(std::string)) {
this->_set_value(boost::any_cast<std::string>(value));
} else if (value.type() == typeid(wxString)) {
this->_set_value(boost::any_cast<wxString>(value).ToStdString());
} else {
Slic3r::Log::warn(this->LogChannel(), LOG_WSTRING("Type " << value.type().name() << " is not handled in set_value."));
}
}
void UI_Point::_set_value(Slic3r::Pointf value) {
/// load the controls directly from the value
this->_ctrl_x->SetValue(trim_zeroes(std::to_string(value.x)));
this->_ctrl_y->SetValue(trim_zeroes(std::to_string(value.y)));
}
void UI_Point::_set_value(std::string value) {
/// parse the string into the two parts.
std::regex format_regex("([0-9.]+);([0-9.]+)");
auto f_begin { std::sregex_iterator(value.begin(), value.end(), format_regex) };
auto f_end { std::sregex_iterator() };
if (f_begin != f_end) {
auto iter = f_begin;
this->_ctrl_x->SetValue(trim_zeroes(iter->str()));
iter++;
if (iter != f_end)
this->_ctrl_y->SetValue(trim_zeroes(iter->str()));
}
}
UI_Point::UI_Point(wxWindow* parent, Slic3r::ConfigOptionDef _opt, wxWindowID id) {
Slic3r::Pointf def_val {_opt.default_value == nullptr ? Pointf() : Pointf(*(dynamic_cast<ConfigOptionPoint*>(_opt.default_value))) };
this->_ctrl_x = new wxTextCtrl(parent, wxID_ANY, trim_zeroes(wxString::FromDouble(def_val.x)), wxDefaultPosition, this->field_size);
this->_ctrl_y = new wxTextCtrl(parent, wxID_ANY, trim_zeroes(wxString::FromDouble(def_val.y)), wxDefaultPosition, this->field_size);
this->_lbl_x = new wxStaticText(parent, wxID_ANY, wxString("x:"));
this->_lbl_y = new wxStaticText(parent, wxID_ANY, wxString("y:"));
}
} } // Namespace Slic3r::GUI

View File

@ -0,0 +1,229 @@
#include <catch.hpp>
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/sizer.h"
#include "wx/uiaction.h"
#endif // WX_PRECOMP
#include <iostream>
#include <tuple>
#include "testableframe.h"
#include "OptionsGroup/Field.hpp"
#include "ConfigBase.hpp"
#include "Point.hpp"
using namespace std::string_literals;
SCENARIO( "UI_Point: default values from options and basic accessor methods") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxUIActionSimulator sim;
wxMilliSleep(500);
GIVEN( "A UI point method and a X,Y coordinate (3.2, 10.2) as the default_value") {
auto simple_option {ConfigOptionDef()};
auto* default_point {new ConfigOptionPoint(Pointf(3.2, 10.2))};
simple_option.default_value = default_point;
auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)};
THEN( "get_string() returns '3.2;10.2'.") {
REQUIRE(test_field.get_string() == "3.2;10.2"s);
}
THEN( "get_point() yields a Pointf structure with x = 3.2, y = 10.2") {
REQUIRE(test_field.get_point().x == 3.2);
REQUIRE(test_field.get_point().y == 10.2);
}
}
GIVEN( "A UI point method and a tooltip in simple_option") {
auto simple_option {ConfigOptionDef()};
auto* default_point {new ConfigOptionPoint(Pointf(3.2, 10.2))};
simple_option.default_value = default_point;
auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)};
THEN( "Tooltip for both labels and textctrls matches simple_option") {
REQUIRE(test_field.ctrl_x()->GetToolTipText().ToStdString() == simple_option.tooltip );
REQUIRE(test_field.lbl_x()->GetToolTipText().ToStdString() == simple_option.tooltip );
REQUIRE(test_field.ctrl_y()->GetToolTipText().ToStdString() == simple_option.tooltip );
REQUIRE(test_field.lbl_y()->GetToolTipText().ToStdString() == simple_option.tooltip );
}
THEN( "get_point() yields a Pointf structure with x = 3.2, y = 10.2") {
REQUIRE(test_field.get_point().x == 3.2);
REQUIRE(test_field.get_point().y == 10.2);
}
}
}
SCENARIO( "UI_Point: set_value works with several types of inputs") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxUIActionSimulator sim;
wxMilliSleep(500);
GIVEN( "A UI point method with no default value.") {
auto simple_option {ConfigOptionDef()};
auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)};
WHEN( "set_value is called with a Pointf(19.0, 2.1)") {
test_field.set_value(Pointf(19.0, 2.1));
THEN( "get_point() returns a Pointf(19.0, 2.1)") {
REQUIRE(test_field.get_point() == Pointf(19.0, 2.1));
}
THEN( "get_string() returns '19.0;2.1'") {
REQUIRE(test_field.get_string() == "19.0;2.1"s);
}
THEN( "X TextCtrl contains X coordinate") {
REQUIRE(test_field.ctrl_x()->GetValue() == wxString("19.0"s));
}
THEN( "Y TextCtrl contains Y coordinate") {
REQUIRE(test_field.ctrl_y()->GetValue() == wxString("2.1"s));
}
}
WHEN( "set_value is called with a string of the form '30.9;211.2'") {
test_field.set_value("30.9;211.2"s);
THEN( "get_point() returns a Pointf(30.9, 211.2)") {
REQUIRE(test_field.get_point() == Pointf(30.9, 211.2));
}
THEN( "get_string() returns '30.9;211.2'") {
REQUIRE(test_field.get_string() == "30.9;211.2"s);
}
THEN( "X TextCtrl contains X coordinate") {
REQUIRE(test_field.ctrl_x()->GetValue() == wxString("30.9"s));
}
THEN( "Y TextCtrl contains Y coordinate") {
REQUIRE(test_field.ctrl_y()->GetValue() == wxString("211.2"s));
}
}
WHEN( "set_value is called with a wxString of the form '30.9;211.2'") {
test_field.set_value(wxString("30.9;211.2"s));
THEN( "get_point() returns a Pointf(30.9, 211.2)") {
REQUIRE(test_field.get_point() == Pointf(30.9, 211.2));
}
THEN( "get_string() returns '30.9;211.2'") {
REQUIRE(test_field.get_string() == "30.9;211.2"s);
}
THEN( "X TextCtrl contains X coordinate") {
REQUIRE(test_field.ctrl_x()->GetValue() == wxString("30.9"s));
}
THEN( "Y TextCtrl contains Y coordinate") {
REQUIRE(test_field.ctrl_y()->GetValue() == wxString("211.2"s));
}
}
}
}
SCENARIO( "UI_Point: Event responses") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxMilliSleep(250);
GIVEN ( "A UI_Point with no default value and a registered on_change method that increments a counter.") {
auto simple_option {ConfigOptionDef()};
auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)};
auto event_count {0};
auto changefunc {[&event_count] (const std::string& opt_id, std::tuple<std::string, std::string> value) { event_count++; }};
auto killfunc {[&event_count](const std::string& opt_id) { event_count += 1; }};
test_field.on_change = changefunc;
test_field.on_kill_focus = killfunc;
WHEN( "kill focus event is received on X") {
event_count = 0;
THEN( "on_kill_focus is executed.") {
REQUIRE(event_count == 1);
}
}
WHEN( "kill focus event is received on Y") {
event_count = 0;
THEN( "on_kill_focus is executed.") {
REQUIRE(event_count == 1);
}
}
WHEN( "enter key pressed event is received on X") {
event_count = 0;
THEN( "on_change is executed.") {
REQUIRE(event_count == 1);
}
}
WHEN( "enter key pressed event is received on Y") {
event_count = 0;
THEN( "on_change is executed.") {
REQUIRE(event_count == 1);
}
}
}
}
SCENARIO( "UI_Point: Enable/Disable") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxMilliSleep(250);
GIVEN ( "A UI_Point with no default value.") {
auto simple_option {ConfigOptionDef()};
auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)};
WHEN( "disable() is called") {
test_field.disable();
THEN( "IsEnabled == False for X and Y textctrls") {
REQUIRE(test_field.ctrl_x()->IsEnabled() == false);
REQUIRE(test_field.ctrl_y()->IsEnabled() == false);
}
}
WHEN( "enable() is called") {
test_field.enable();
THEN( "IsEnabled == True for X and Y textctrls") {
REQUIRE(test_field.ctrl_x()->IsEnabled() == true);
REQUIRE(test_field.ctrl_y()->IsEnabled() == true);
}
}
WHEN( "toggle() is called with false argument") {
test_field.toggle(false);
THEN( "IsEnabled == False for X and Y textctrls") {
REQUIRE(test_field.ctrl_x()->IsEnabled() == false);
REQUIRE(test_field.ctrl_y()->IsEnabled() == false);
}
}
WHEN( "toggle() is called with true argument") {
test_field.toggle(true);
THEN( "IsEnabled == True for X and Y textctrls") {
REQUIRE(test_field.ctrl_x()->IsEnabled() == true);
REQUIRE(test_field.ctrl_y()->IsEnabled() == true);
}
}
}
}
SCENARIO( "UI_Point: get_sizer()") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxMilliSleep(250);
GIVEN ( "A UI_Point with no default value.") {
auto simple_option {ConfigOptionDef()};
auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)};
WHEN( "get_sizer() is called") {
THEN( "get_sizer() returns a wxSizer that has 2 direct children in it that are sizers.") {
REQUIRE(test_field.get_sizer()->GetItemCount() == 2);
auto tmp {test_field.get_sizer()->GetChildren().begin()};
REQUIRE((*tmp)->IsSizer() == true);
tmp++;
REQUIRE((*tmp)->IsSizer() == true);
}
THEN( "The two children have two wxWindows as their children") {
auto tmp_sizer {test_field.get_sizer()->GetChildren().begin()};
auto tmp {(*tmp_sizer)->GetSizer()->GetChildren().begin()};
REQUIRE((*tmp)->IsWindow() == true);
tmp++;
REQUIRE((*tmp)->IsWindow() == true);
// now for the other one
tmp_sizer++;
tmp = (*tmp_sizer)->GetSizer()->GetChildren().begin();
REQUIRE((*tmp)->IsWindow() == true);
tmp++;
REQUIRE((*tmp)->IsWindow() == true);
}
}
}
}