Fixed destructors for wxwidget objects, moved on_change to the derived objects.

Added test and initial implementation for textctrl
This commit is contained in:
Joseph Lenox 2018-06-27 00:18:15 -05:00 committed by Joseph Lenox
parent e13eb18455
commit 3b938d13b7
4 changed files with 278 additions and 6 deletions

View File

@ -178,6 +178,7 @@ set(UI_TEST_SOURCES
${GUI_TESTDIR}/test_harness_gui.cpp
${GUI_TESTDIR}/test_field_checkbox.cpp
${GUI_TESTDIR}/test_field_spinctrl.cpp
${GUI_TESTDIR}/test_field_textbox.cpp
)
set(SLIC3R_TEST_SOURCES
${TESTDIR}/test_harness.cpp

View File

@ -9,6 +9,8 @@
#include "Log.hpp"
#include "wx/spinctrl.h"
#include "wx/checkbox.h"
#include "wx/textctrl.h"
namespace Slic3r { namespace GUI {
@ -37,9 +39,9 @@ public:
/// Getter functions for UI_Window items.
virtual bool get_bool() { Slic3r::Log::warn(this->LogChannel(), "get_bool does not exist"s); return false; } //< return false all the time if this is not implemented.
virtual int get_int() { Slic3r::Log::warn(this->LogChannel(), "get_int does not exist"s); return 0; } //< return 0 all the time if this is not implemented.
virtual std::string get_string() { Slic3r::Log::warn(this->LogChannel(), "get_string does not exist"s); return 0; } //< return 0 all the time if this is not implemented.
/// Function to call when the contents of this change.
std::function<void (const std::string&, bool value)> on_change {nullptr};
/// Function to call when focus leaves.
std::function<void (const std::string&)> on_kill_focus {nullptr};
protected:
@ -71,7 +73,7 @@ public:
_check->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { if (this->on_kill_focus != nullptr) {this->on_kill_focus("");} e.Skip(); });
}
~UI_Checkbox() = default;
~UI_Checkbox() { _check->Destroy(); }
/// Returns a bare pointer to the underlying checkbox, usually for test interface
wxCheckBox* check() { return _check; }
@ -84,6 +86,9 @@ public:
/// implements set_value
virtual void set_value(boost::any value) override { this->_check->SetValue(boost::any_cast<bool>(value)); }
/// Function to call when the contents of this change.
std::function<void (const std::string&, bool value)> on_change {nullptr};
protected:
wxCheckBox* _check {nullptr};
@ -113,12 +118,15 @@ public:
_spin->Bind(wxEVT_SPINCTRL, [this](wxCommandEvent& e) { this->_on_change(""); e.Skip(); });
_spin->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { if (this->on_kill_focus != nullptr) {this->on_kill_focus("");} e.Skip(); });
}
~UI_SpinCtrl() = default;
~UI_SpinCtrl() { _spin->Destroy();}
int get_int() { return this->_spin->GetValue(); }
void set_value(boost::any value) { this->_spin->SetValue(boost::any_cast<int>(value)); }
/// Access method for the underlying SpinCtrl
wxSpinCtrl* spinctrl() { return _spin; }
/// Function to call when the contents of this change.
std::function<void (const std::string&, int value)> on_change {nullptr};
protected:
virtual std::string LogChannel() { return "UI_SpinCtrl"s; }
@ -134,6 +142,53 @@ private:
int _tmp {0};
};
class UI_TextCtrl : public UI_Window {
public:
UI_TextCtrl(wxWindow* parent, Slic3r::ConfigOptionDef _opt, wxWindowID id = wxID_ANY) : UI_Window(parent, _opt) {
int style {0};
if (opt.multiline) {
style |= wxHSCROLL;
style |= wxTE_MULTILINE;
} else {
style |= wxTE_PROCESS_ENTER;
}
/// Initialize and set defaults, if available.
_text = new wxTextCtrl(parent, id,
(opt.default_value != NULL ? wxString(opt.default_value->getString()) : wxString()),
wxDefaultPosition,
_default_size(),
style);
window = _text;
// Set up event handlers
_text->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent& e) { this->_on_change(""); e.Skip(); });
_text->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { if (this->on_kill_focus != nullptr) {this->on_kill_focus("");} e.Skip(); });
}
~UI_TextCtrl() { _text->Destroy(); }
std::string get_string() { return this->_text->GetValue().ToStdString(); }
void set_value(boost::any value) { this->_text->SetValue(boost::any_cast<std::string>(value)); }
/// Access method for the underlying SpinCtrl
wxTextCtrl* textctrl() { return _text; }
/// 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() { return "UI_TextCtrl"s; }
void _on_change(std::string opt_id) {
if (!this->disable_change_event && this->window->IsEnabled() && this->on_change != nullptr) {
this->on_change(opt_id, this->get_string());
}
}
private:
wxTextCtrl* _text {nullptr};
int _tmp {0};
};
} } // Namespace Slic3r::GUI
#endif // SLIC3R_FIELD_HPP

View File

@ -40,7 +40,7 @@ SCENARIO( "Receiving a Spinctrl event") {
wxMilliSleep(250);
GIVEN ( "A UI Spinctrl") {
auto exec_counter {0};
auto changefunc {[&exec_counter] (const std::string& opt_id, bool value) { exec_counter++; }};
auto changefunc {[&exec_counter] (const std::string& opt_id, int value) { exec_counter++; }};
auto test_field {Slic3r::GUI::UI_SpinCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())};
test_field.on_change = changefunc;
@ -81,7 +81,7 @@ SCENARIO( "Changing the text via entry works on pressing enter") {
wxMilliSleep(500);
GIVEN ( "A UI Spinctrl") {
auto exec_counter {0};
auto changefunc {[&exec_counter] (const std::string& opt_id, bool value) { exec_counter++; }};
auto changefunc {[&exec_counter] (const std::string& opt_id, int value) { exec_counter++; }};
auto test_field {Slic3r::GUI::UI_SpinCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())};
test_field.on_change = changefunc;

View File

@ -0,0 +1,216 @@
#include <catch.hpp>
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/sizer.h"
#include "wx/uiaction.h"
#endif // WX_PRECOMP
#include <iostream>
#include <string>
#include "testableframe.h"
#include "OptionsGroup/Field.hpp"
#include "ConfigBase.hpp"
using namespace Slic3r::GUI;
using namespace std::string_literals;
SCENARIO( "TextCtrl initializes with default value if available.") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxMilliSleep(250);
Slic3r::ConfigOptionDef simple_option;
simple_option.type = coInt;
auto* intopt { new Slic3r::ConfigOptionInt(7) };
simple_option.default_value = intopt; // no delete, it's taken care of by ConfigOptionDef
GIVEN ( "A UI Textctrl") {
auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), simple_option)};
wxTheApp->GetTopWindow()->Show();
wxTheApp->GetTopWindow()->Fit();
REQUIRE(test_field.get_string() == "7"s);
}
}
SCENARIO( "Receiving a Textctrl event") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxMilliSleep(250);
GIVEN ( "A UI Textctrl") {
auto exec_counter {0};
auto changefunc {[&exec_counter] (const std::string& opt_id, std::string value) { exec_counter++; }};
auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())};
test_field.on_change = changefunc;
wxTheApp->GetTopWindow()->Show();
wxTheApp->GetTopWindow()->Fit();
WHEN( "A text event occurs") {
exec_counter = 0;
auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.textctrl()->GetId())};
ev.SetEventObject(test_field.textctrl());
test_field.textctrl()->ProcessWindowEvent(ev);
wxYield();
wxMilliSleep(250);
THEN( "on_change is executed.") {
REQUIRE(exec_counter == 1);
}
}
WHEN( "A text event occurs and change event is disabled") {
exec_counter = 0;
test_field.disable_change_event = false;
auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.textctrl()->GetId())};
ev.SetEventObject(test_field.textctrl());
test_field.textctrl()->ProcessWindowEvent(ev);
wxYield();
wxMilliSleep(250);
THEN( "on_change is not executed.") {
REQUIRE(exec_counter == 1);
}
}
}
}
SCENARIO( "TextCtrl: Changing the text via entry works on pressing enter") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxUIActionSimulator sim;
wxMilliSleep(500);
GIVEN ( "A UI Textctrl") {
auto exec_counter {0};
auto changefunc {[&exec_counter] (const std::string& opt_id, std::string value) { exec_counter++; }};
auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())};
test_field.on_change = changefunc;
wxTheApp->GetTopWindow()->Show();
wxTheApp->GetTopWindow()->Fit();
WHEN( "A number is entered followed by enter key") {
exec_counter = 0;
test_field.textctrl()->SetFocus();
wxYield();
wxMilliSleep(250);
sim.Char('3');
wxMilliSleep(250);
sim.Char(WXK_RETURN);
wxMilliSleep(250);
wxYield();
THEN( "on_change is executed.") {
REQUIRE(exec_counter == 1);
}
THEN( "get_string returns entered value.") {
REQUIRE(test_field.get_string() == "3"s);
}
}
WHEN( "A number is entered followed by enter key and change event is disabled") {
exec_counter = 0;
test_field.disable_change_event = true;
test_field.textctrl()->SetFocus();
wxYield();
wxMilliSleep(250);
sim.Char('3');
wxMilliSleep(250);
sim.Char(WXK_RETURN);
wxMilliSleep(250);
wxYield();
THEN( "on_change is not executed.") {
REQUIRE(exec_counter == 0);
}
THEN( "get_string returns entered value.") {
REQUIRE(test_field.get_string() == "3"s);
}
}
WHEN( "A number is entered and focus is lost") {
auto killfunc {[&exec_counter](const std::string& opt_id) { exec_counter += 1; }};
test_field.on_kill_focus = killfunc;
auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.textctrl()->GetId())};
ev.SetEventObject(test_field.textctrl());
exec_counter = 0;
test_field.textctrl()->SetValue("3");
test_field.textctrl()->SetFocus();
wxYield();
wxMilliSleep(250);
sim.Char('7');
wxYield();
wxMilliSleep(250);
test_field.textctrl()->ProcessWindowEvent(ev);
wxMilliSleep(250);
wxYield();
THEN( "on_kill_focus is executed and on_change are both executed.") {
REQUIRE(exec_counter == 2);
}
THEN( "get_string returns updated value.") {
REQUIRE(test_field.get_string() == "7"s);
}
THEN( "get_bool returns 0.") {
REQUIRE(test_field.get_bool() == 0);
}
THEN( "get_int returns 0.") {
REQUIRE(test_field.get_int() == 0);
}
}
}
}
SCENARIO( "Multiline doesn't update other than on focus change.") {
wxTestableFrame* old = dynamic_cast<wxTestableFrame*>(wxTheApp->GetTopWindow());
old->Destroy();
wxTheApp->SetTopWindow(new wxTestableFrame());
wxUIActionSimulator sim;
wxMilliSleep(500);
Slic3r::ConfigOptionDef simple_option;
simple_option.type = coInt;
simple_option.multiline = true;
auto* stropt { new Slic3r::ConfigOptionString("7") };
GIVEN ( "A UI Textctrl") {
auto exec_counter {0};
auto changefunc {[&exec_counter] (const std::string& opt_id, std::string value) { exec_counter++; }};
auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())};
test_field.on_change = changefunc;
wxTheApp->GetTopWindow()->Show();
wxTheApp->GetTopWindow()->Fit();
WHEN( "pressing enter") {
auto killfunc {[&exec_counter](const std::string& opt_id) { exec_counter += 1; }};
test_field.on_kill_focus = killfunc;
auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.textctrl()->GetId())};
ev.SetEventObject(test_field.textctrl());
exec_counter = 0;
test_field.textctrl()->SetValue("3");
test_field.textctrl()->SetFocus();
wxYield();
wxMilliSleep(250);
sim.Char('7');
wxYield();
wxMilliSleep(250);
test_field.textctrl()->ProcessWindowEvent(ev);
wxMilliSleep(250);
wxYield();
THEN( "on_kill_focus is executed and on_change are both executed.") {
REQUIRE(exec_counter == 2);
}
THEN( "get_string returns updated value.") {
REQUIRE(test_field.get_string() == "77"s);
}
THEN( "get_bool returns 0.") {
REQUIRE(test_field.get_bool() == 0);
}
THEN( "get_int returns 0.") {
REQUIRE(test_field.get_int() == 0);
}
}
}
}