PrusaSlicer-NonPlainar/src/slic3r/GUI/Field.hpp

523 lines
17 KiB
C++
Raw Normal View History

#ifndef SLIC3R_GUI_FIELD_HPP
#define SLIC3R_GUI_FIELD_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <memory>
#include <functional>
#include <boost/any.hpp>
#include <wx/spinctrl.h>
#include <wx/bmpcbox.h>
#include <wx/clrpicker.h>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Config.hpp"
#include "libslic3r/Utils.hpp"
#include "GUI.hpp"
#include "wxExtensions.hpp"
#ifdef __WXMSW__
#define wxMSW true
#else
#define wxMSW false
#endif
namespace Slic3r { namespace GUI {
class Field;
using t_field = std::unique_ptr<Field>;
using t_kill_focus = std::function<void(const std::string&)>;
using t_change = std::function<void(const t_config_option_key&, const boost::any&)>;
2018-04-13 10:35:04 +00:00
using t_back_to_init = std::function<void(const std::string&)>;
wxString double_to_string(double const value, const int max_precision = 4);
2018-02-19 08:15:15 +00:00
class RevertButton : public ScalableButton
2018-04-26 13:11:02 +00:00
{
2018-08-07 15:23:48 +00:00
bool hidden = false; // never show button if it's hidden ones
2018-04-26 13:11:02 +00:00
public:
// RevertButton() {}
// RevertButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString,
// const wxPoint& pos = wxDefaultPosition,
// const wxSize& size = wxDefaultSize, long style = 0,
// const wxValidator& validator = wxDefaultValidator,
// const wxString& name = wxTextCtrlNameStr)
// {
// this->Create(parent, id, label, pos, size, style, validator, name);
// }
RevertButton(
wxWindow *parent,
const std::string& icon_name = ""
) :
ScalableButton(parent, wxID_ANY, icon_name) {}
2018-04-26 13:11:02 +00:00
// overridden from wxWindow base class
virtual bool
AcceptsFocusFromKeyboard() const { return false; }
2018-08-07 15:23:48 +00:00
void set_as_hidden() {
Hide();
hidden = true;
}
2018-08-07 15:23:48 +00:00
virtual bool Show(bool show = true) override {
return wxButton::Show(hidden ? false : show);
2018-08-07 15:23:48 +00:00
}
2018-04-26 13:11:02 +00:00
};
2017-12-26 22:04:54 +00:00
class Field {
protected:
// factory function to defer and enforce creation of derived type.
2018-03-06 11:34:20 +00:00
virtual void PostInitialize();
/// Finish constructing the Field's wxWidget-related properties, including setting its own sizer, etc.
virtual void BUILD() = 0;
/// Call the attached on_kill_focus method.
//! It's important to use wxEvent instead of wxFocusEvent,
//! in another case we can't unfocused control at all
void on_kill_focus();
/// Call the attached on_change method.
void on_set_focus(wxEvent& event);
/// Call the attached on_change method.
void on_change_field();
2018-03-06 11:34:20 +00:00
/// Call the attached m_back_to_initial_value method.
void on_back_to_initial_value();
2018-03-16 16:25:11 +00:00
/// Call the attached m_back_to_sys_value method.
void on_back_to_sys_value();
2017-12-26 22:04:54 +00:00
public:
/// parent wx item, opportunity to refactor (probably not necessary - data duplication)
wxWindow* m_parent {nullptr};
/// Function object to store callback passed in from owning object.
t_kill_focus m_on_kill_focus {nullptr};
/// Function object to store callback passed in from owning object.
t_kill_focus m_on_set_focus {nullptr};
/// Function object to store callback passed in from owning object.
t_change m_on_change {nullptr};
2018-03-16 16:25:11 +00:00
/// Function object to store callback passed in from owning object.
2018-03-06 11:34:20 +00:00
t_back_to_init m_back_to_initial_value{ nullptr };
2018-03-16 16:25:11 +00:00
t_back_to_init m_back_to_sys_value{ nullptr };
2018-03-06 11:34:20 +00:00
2018-01-27 16:39:00 +00:00
// This is used to avoid recursive invocation of the field change/update by wxWidgets.
bool m_disable_change_event {false};
bool m_is_modified_value {false};
2018-03-16 16:25:11 +00:00
bool m_is_nonsys_value {true};
/// Copy of ConfigOption for deduction purposes
const ConfigOptionDef m_opt {ConfigOptionDef()};
const t_config_option_key m_opt_id;//! {""};
int m_opt_idx = 0;
/// Sets a value for this control.
/// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event.
2018-04-13 10:35:04 +00:00
virtual void set_value(const boost::any& value, bool change_event) = 0;
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
virtual boost::any& get_value() = 0;
virtual void enable() = 0;
virtual void disable() = 0;
2018-04-17 08:15:48 +00:00
/// Fires the enable or disable function, based on the input.
inline void toggle(bool en) { en ? enable() : disable(); }
virtual wxString get_tooltip_text(const wxString& default_string);
void field_changed() { on_change_field(); }
// set icon to "UndoToSystemValue" button according to an inheritance of preset
// void set_nonsys_btn_icon(const wxBitmap& icon);
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {};
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {};
virtual ~Field() {}
/// If you don't know what you are getting back, check both methods for nullptr.
virtual wxSizer* getSizer() { return nullptr; }
virtual wxWindow* getWindow() { return nullptr; }
2018-04-13 10:35:04 +00:00
bool is_matched(const std::string& string, const std::string& pattern);
void get_value_by_opt_type(wxString& str);
/// Factory method for generating new derived classes.
template<class T>
static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id)// interface for creating shared objects
{
auto p = Slic3r::make_unique<T>(parent, opt, id);
p->PostInitialize();
return std::move(p); //!p;
}
bool set_undo_bitmap(const ScalableBitmap *bmp) {
if (m_undo_bitmap != bmp) {
m_undo_bitmap = bmp;
m_Undo_btn->SetBitmap_(*bmp);
return true;
}
return false;
}
bool set_undo_to_sys_bitmap(const ScalableBitmap *bmp) {
if (m_undo_to_sys_bitmap != bmp) {
m_undo_to_sys_bitmap = bmp;
m_Undo_to_sys_btn->SetBitmap_(*bmp);
return true;
}
return false;
}
2018-04-17 08:15:48 +00:00
bool set_label_colour(const wxColour *clr) {
if (m_Label == nullptr) return false;
if (m_label_color != clr) {
m_label_color = clr;
m_Label->SetForegroundColour(*clr);
m_Label->Refresh(true);
}
return false;
}
bool set_label_colour_force(const wxColour *clr) {
if (m_Label == nullptr) return false;
m_Label->SetForegroundColour(*clr);
m_Label->Refresh(true);
return false;
}
bool set_undo_tooltip(const wxString *tip) {
if (m_undo_tooltip != tip) {
m_undo_tooltip = tip;
m_Undo_btn->SetToolTip(*tip);
return true;
}
return false;
}
bool set_undo_to_sys_tooltip(const wxString *tip) {
if (m_undo_to_sys_tooltip != tip) {
m_undo_to_sys_tooltip = tip;
m_Undo_to_sys_btn->SetToolTip(*tip);
return true;
}
return false;
}
void set_side_text_ptr(wxStaticText* side_text) {
m_side_text = side_text;
}
virtual void msw_rescale() {
m_Undo_to_sys_btn->msw_rescale();
m_Undo_btn->msw_rescale();
// update em_unit value
m_em_unit = em_unit(m_parent);
}
bool get_enter_pressed() const { return bEnterPressed; }
void set_enter_pressed(bool pressed) { bEnterPressed = pressed; }
protected:
RevertButton* m_Undo_btn = nullptr;
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* m_undo_bitmap = nullptr;
const wxString* m_undo_tooltip = nullptr;
RevertButton* m_Undo_to_sys_btn = nullptr;
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* m_undo_to_sys_bitmap = nullptr;
const wxString* m_undo_to_sys_tooltip = nullptr;
2018-04-17 08:15:48 +00:00
wxStaticText* m_Label = nullptr;
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
const wxColour* m_label_color = nullptr;
2018-04-17 08:15:48 +00:00
wxStaticText* m_side_text = nullptr;
// current value
boost::any m_value;
int m_em_unit;
bool bEnterPressed = false;
friend class OptionsGroup;
};
/// Convenience function, accepts a const reference to t_field and checks to see whether
/// or not both wx pointers are null.
inline bool is_bad_field(const t_field& obj) { return obj->getSizer() == nullptr && obj->getWindow() == nullptr; }
/// Covenience function to determine whether this field is a valid window field.
2018-06-16 01:04:59 +00:00
inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr && obj->getSizer() == nullptr; }
/// Covenience function to determine whether this field is a valid sizer field.
inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && obj->getSizer() != nullptr; }
class TextCtrl : public Field {
using Field::Field;
2018-06-11 14:23:10 +00:00
#ifdef __WXGTK__
bool bChangedValueEvent = true;
2018-09-05 07:47:36 +00:00
void change_field_value(wxEvent& event);
2018-06-11 14:23:10 +00:00
#endif //__WXGTK__
public:
TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
2018-04-13 10:35:04 +00:00
~TextCtrl() {}
void BUILD();
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value();
wxWindow* window {nullptr};
2018-04-13 10:35:04 +00:00
virtual void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
2018-04-13 10:35:04 +00:00
virtual void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
boost::any& get_value() override;
void msw_rescale() override;
virtual void enable();
virtual void disable();
2017-12-26 22:04:54 +00:00
virtual wxWindow* getWindow() { return window; }
};
class CheckBox : public Field {
using Field::Field;
public:
CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
2018-04-13 10:35:04 +00:00
~CheckBox() {}
wxWindow* window{ nullptr };
void BUILD() override;
void set_value(const bool value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
2018-04-13 10:35:04 +00:00
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
boost::any& get_value() override;
void msw_rescale() override;
void enable() override { dynamic_cast<wxCheckBox*>(window)->Enable(); }
void disable() override { dynamic_cast<wxCheckBox*>(window)->Disable(); }
wxWindow* getWindow() override { return window; }
};
class SpinCtrl : public Field {
using Field::Field;
public:
SpinCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id), tmp_value(-9999) {}
SpinCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id), tmp_value(-9999) {}
2018-04-13 10:35:04 +00:00
~SpinCtrl() {}
int tmp_value;
wxWindow* window{ nullptr };
void BUILD() override;
/// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value() ;
2018-04-13 10:35:04 +00:00
void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
2018-04-13 10:35:04 +00:00
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
tmp_value = boost::any_cast<int>(value);
m_value = value;
2018-04-13 10:35:04 +00:00
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
boost::any& get_value() override {
// return boost::any(tmp_value);
return m_value = tmp_value;
}
void msw_rescale() override;
void enable() override { dynamic_cast<wxSpinCtrl*>(window)->Enable(); }
void disable() override { dynamic_cast<wxSpinCtrl*>(window)->Disable(); }
wxWindow* getWindow() override { return window; }
};
class Choice : public Field {
using Field::Field;
int m_width{ 15 };
public:
Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
2018-04-13 10:35:04 +00:00
~Choice() {}
wxWindow* window{ nullptr };
2017-12-26 22:04:54 +00:00
void BUILD() override;
2019-03-07 15:36:39 +00:00
/* Under OSX: wxBitmapComboBox->GetWindowStyle() returns some weard value,
* so let use a flag, which has TRUE value for a control without wxCB_READONLY style
*/
bool m_is_editable { false };
void set_selection();
2018-04-13 10:35:04 +00:00
void set_value(const std::string& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false);
void set_values(const std::vector<std::string> &values);
boost::any& get_value() override;
void msw_rescale() override;
void enable() override { dynamic_cast<wxBitmapComboBox*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxBitmapComboBox*>(window)->Disable(); };
wxWindow* getWindow() override { return window; }
};
class ColourPicker : public Field {
using Field::Field;
public:
ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
2018-04-13 10:35:04 +00:00
~ColourPicker() {}
wxWindow* window{ nullptr };
void BUILD() override;
2018-04-13 10:35:04 +00:00
void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
2018-01-27 16:39:00 +00:00
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
m_disable_change_event = false;
}
2018-04-13 10:35:04 +00:00
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
2018-01-27 16:39:00 +00:00
m_disable_change_event = false;
}
boost::any& get_value() override;
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
wxWindow* getWindow() override { return window; }
};
class PointCtrl : public Field {
using Field::Field;
public:
PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
2018-04-13 10:35:04 +00:00
~PointCtrl() {}
wxSizer* sizer{ nullptr };
2018-04-13 10:35:04 +00:00
wxTextCtrl* x_textctrl{ nullptr };
wxTextCtrl* y_textctrl{ nullptr };
void BUILD() override;
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value(wxTextCtrl* win);
void set_value(const Vec2d& value, bool change_event = false);
2018-04-13 10:35:04 +00:00
void set_value(const boost::any& value, bool change_event = false);
boost::any& get_value() override;
void msw_rescale() override;
2017-12-26 22:04:54 +00:00
void enable() override {
x_textctrl->Enable();
2018-04-13 10:35:04 +00:00
y_textctrl->Enable(); }
2017-12-26 22:04:54 +00:00
void disable() override{
x_textctrl->Disable();
2018-04-13 10:35:04 +00:00
y_textctrl->Disable(); }
wxSizer* getSizer() override { return sizer; }
};
2018-06-21 14:15:56 +00:00
class StaticText : public Field {
using Field::Field;
public:
StaticText(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
StaticText(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~StaticText() {}
wxWindow* window{ nullptr };
void BUILD() override;
void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxStaticText*>(window)->SetLabel(value);
m_disable_change_event = false;
}
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value));
m_disable_change_event = false;
}
boost::any& get_value()override { return m_value; }
void msw_rescale() override;
void enable() override { dynamic_cast<wxStaticText*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxStaticText*>(window)->Disable(); };
2018-06-21 14:15:56 +00:00
wxWindow* getWindow() override { return window; }
};
class SliderCtrl : public Field {
using Field::Field;
public:
SliderCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
SliderCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~SliderCtrl() {}
wxSizer* m_sizer{ nullptr };
wxTextCtrl* m_textctrl{ nullptr };
wxSlider* m_slider{ nullptr };
int m_scale = 10;
void BUILD() override;
void set_value(const int value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false);
boost::any& get_value() override;
void enable() override {
m_slider->Enable();
m_textctrl->Enable();
m_textctrl->SetEditable(true);
}
void disable() override{
m_slider->Disable();
m_textctrl->Disable();
m_textctrl->SetEditable(false);
}
wxSizer* getSizer() override { return m_sizer; }
2018-06-16 01:04:59 +00:00
wxWindow* getWindow() override { return dynamic_cast<wxWindow*>(m_slider); }
};
} // GUI
} // Slic3r
#endif /* SLIC3R_GUI_FIELD_HPP */