mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-12 02:29:03 +08:00
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:
parent
04bc767421
commit
2c7b39f0fd
@ -181,6 +181,7 @@ set(UI_TEST_SOURCES
|
||||
${GUI_TESTDIR}/test_field_textbox.cpp
|
||||
${GUI_TESTDIR}/test_field_choice.cpp
|
||||
${GUI_TESTDIR}/test_field_numchoice.cpp
|
||||
${GUI_TESTDIR}/test_field_point.cpp
|
||||
)
|
||||
set(SLIC3R_TEST_SOURCES
|
||||
${TESTDIR}/test_harness.cpp
|
||||
@ -261,6 +262,7 @@ IF(wxWidgets_FOUND)
|
||||
${GUI_LIBDIR}/misc_ui.cpp
|
||||
${GUI_LIBDIR}/OptionsGroup/UI_NumChoice.cpp
|
||||
${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp
|
||||
${GUI_LIBDIR}/OptionsGroup/UI_Point.cpp
|
||||
)
|
||||
target_compile_features(slic3r_gui PUBLIC cxx_std_14)
|
||||
#only build GUI lib if building with wx
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "wx/textctrl.h"
|
||||
#include "wx/combobox.h"
|
||||
#include "wx/arrstr.h"
|
||||
#include "wx/stattext.h"
|
||||
#include "wx/sizer.h"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
@ -245,6 +247,7 @@ public:
|
||||
|
||||
/// Function to call when the contents of this change.
|
||||
std::function<void (const std::string&, std::string value)> on_change {nullptr};
|
||||
|
||||
protected:
|
||||
virtual std::string LogChannel() override { return "UI_NumChoice"s; }
|
||||
|
||||
@ -262,7 +265,63 @@ private:
|
||||
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
|
||||
|
||||
|
69
src/GUI/OptionsGroup/UI_Point.cpp
Normal file
69
src/GUI/OptionsGroup/UI_Point.cpp
Normal 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
|
229
src/test/GUI/test_field_point.cpp
Normal file
229
src/test/GUI/test_field_point.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user