2019-05-28 14:38:04 +00:00
|
|
|
#include "GUI_ObjectLayers.hpp"
|
|
|
|
#include "GUI_ObjectList.hpp"
|
|
|
|
|
|
|
|
#include "OptionsGroup.hpp"
|
|
|
|
#include "PresetBundle.hpp"
|
|
|
|
#include "libslic3r/Model.hpp"
|
|
|
|
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
|
|
|
|
#include "I18N.hpp"
|
|
|
|
|
|
|
|
#include <wx/wupdlock.h>
|
|
|
|
|
|
|
|
namespace Slic3r
|
|
|
|
{
|
|
|
|
namespace GUI
|
2019-05-30 10:41:16 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
#define field_width 8
|
2019-05-28 14:38:04 +00:00
|
|
|
|
|
|
|
ObjectLayers::ObjectLayers(wxWindow* parent) :
|
|
|
|
OG_Settings(parent, true)
|
|
|
|
{
|
2019-06-04 09:51:25 +00:00
|
|
|
m_grid_sizer = new wxFlexGridSizer(3, 5, 5); // "Min Z", "Max Z", "Layer height" & buttons sizer
|
|
|
|
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
|
2019-06-03 13:35:21 +00:00
|
|
|
|
2019-05-28 14:38:04 +00:00
|
|
|
// Legend for object layers
|
2019-06-03 13:35:21 +00:00
|
|
|
for (const std::string col : { "Min Z", "Max Z", "Layer height" }) {
|
2019-06-04 09:51:25 +00:00
|
|
|
auto temp = new wxStaticText(m_parent, wxID_ANY, _(L(col)), wxDefaultPosition, /*size*/wxDefaultSize, wxST_ELLIPSIZE_MIDDLE);
|
|
|
|
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
|
|
|
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
|
|
temp->SetFont(wxGetApp().bold_font());
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
m_grid_sizer->Add(temp);
|
2019-05-28 14:38:04 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
m_og->sizer->Clear(true);
|
|
|
|
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
|
2019-05-28 14:38:04 +00:00
|
|
|
|
2019-05-30 14:53:17 +00:00
|
|
|
m_bmp_delete = ScalableBitmap(parent, "remove_copies"/*"cross"*/);
|
2019-05-28 14:38:04 +00:00
|
|
|
m_bmp_add = ScalableBitmap(parent, "add_copies");
|
|
|
|
}
|
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
wxSizer* ObjectLayers::create_layer_without_buttons(const t_layer_config_ranges::value_type& layer)
|
2019-05-30 10:41:16 +00:00
|
|
|
{
|
2019-06-05 09:03:46 +00:00
|
|
|
const bool is_last_edited_range = layer.first == m_last_edited_range;
|
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
// Add control for the "Min Z"
|
2019-06-05 09:03:46 +00:00
|
|
|
|
2019-06-04 15:30:44 +00:00
|
|
|
auto temp = new LayerRangeEditor(m_parent, double_to_string(layer.first.first),
|
2019-06-05 09:03:46 +00:00
|
|
|
[layer, this](coordf_t min_z)
|
|
|
|
{
|
|
|
|
if (fabs(min_z - layer.first.first) < EPSILON) {
|
|
|
|
m_selection_type = sitUndef;
|
|
|
|
return false; // LayersList would not be updated/recreated
|
|
|
|
}
|
|
|
|
|
|
|
|
// data for next focusing
|
|
|
|
m_last_edited_range = { min_z, layer.first.second };
|
|
|
|
m_selection_type = sitMinZ;
|
|
|
|
|
|
|
|
wxGetApp().obj_list()->edit_layer_range(layer.first, m_last_edited_range);
|
|
|
|
return true; // LayersList will be updated/recreated
|
|
|
|
} );
|
|
|
|
|
|
|
|
if (is_last_edited_range && m_selection_type == sitMinZ) {
|
|
|
|
temp->SetFocus();
|
|
|
|
temp->SetInsertionPointEnd();
|
|
|
|
}
|
2019-06-04 15:30:44 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
m_grid_sizer->Add(temp);
|
|
|
|
|
|
|
|
// Add control for the "Max Z"
|
2019-06-05 09:03:46 +00:00
|
|
|
|
2019-06-04 15:30:44 +00:00
|
|
|
temp = new LayerRangeEditor(m_parent, double_to_string(layer.first.second),
|
2019-06-05 09:03:46 +00:00
|
|
|
[layer, this](coordf_t max_z)
|
|
|
|
{
|
|
|
|
if (fabs(max_z - layer.first.second) < EPSILON) {
|
|
|
|
m_selection_type = sitUndef;
|
|
|
|
return false; // LayersList would not be updated/recreated
|
|
|
|
}
|
|
|
|
|
|
|
|
// data for next focusing
|
|
|
|
m_last_edited_range = { layer.first.first, max_z };
|
|
|
|
m_selection_type = sitMaxZ;
|
|
|
|
|
|
|
|
wxGetApp().obj_list()->edit_layer_range(layer.first, m_last_edited_range);
|
|
|
|
return true; // LayersList will not be updated/recreated
|
2019-06-04 15:30:44 +00:00
|
|
|
});
|
2019-06-05 09:03:46 +00:00
|
|
|
|
|
|
|
if (is_last_edited_range && m_selection_type == sitMaxZ) {
|
|
|
|
temp->SetFocus();
|
|
|
|
temp->SetInsertionPointEnd();
|
|
|
|
}
|
2019-06-04 15:30:44 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
m_grid_sizer->Add(temp);
|
|
|
|
|
|
|
|
// Add control for the "Layer height"
|
2019-06-05 09:03:46 +00:00
|
|
|
|
2019-06-04 15:30:44 +00:00
|
|
|
temp = new LayerRangeEditor(m_parent,
|
|
|
|
double_to_string(layer.second.option("layer_height")->getFloat()),
|
2019-06-05 09:03:46 +00:00
|
|
|
[layer, this](coordf_t layer_height)
|
|
|
|
{
|
2019-06-04 13:22:29 +00:00
|
|
|
wxGetApp().obj_list()->edit_layer_range(layer.first, layer_height);
|
2019-06-05 09:03:46 +00:00
|
|
|
return false; // LayersList would not be updated/recreated
|
|
|
|
});
|
2019-06-04 09:51:25 +00:00
|
|
|
|
2019-06-04 15:30:44 +00:00
|
|
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
sizer->Add(temp);
|
2019-06-04 09:51:25 +00:00
|
|
|
m_grid_sizer->Add(sizer);
|
|
|
|
|
|
|
|
return sizer;
|
2019-05-30 10:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ObjectLayers::create_layers_list()
|
|
|
|
{
|
2019-06-03 13:35:21 +00:00
|
|
|
for (const auto layer : m_object->layer_config_ranges)
|
2019-05-31 08:54:52 +00:00
|
|
|
{
|
2019-06-04 09:51:25 +00:00
|
|
|
auto sizer = create_layer_without_buttons(layer);
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
wxWindow* parent = m_parent;
|
|
|
|
auto del_btn = new ScalableButton(parent, wxID_ANY, m_bmp_delete);
|
|
|
|
del_btn->SetToolTip(_(L("Remove layer")));
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(parent));
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
del_btn->Bind(wxEVT_BUTTON, [this, layer](wxEvent &event) {
|
|
|
|
wxGetApp().obj_list()->del_layer_range(layer.first);
|
|
|
|
});
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
auto add_btn = new ScalableButton(parent, wxID_ANY, m_bmp_add);
|
|
|
|
add_btn->SetToolTip(_(L("Add layer")));
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
sizer->Add(add_btn, 0, wxRIGHT, em_unit(parent));
|
2019-05-30 10:41:16 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
add_btn->Bind(wxEVT_BUTTON, [this, layer](wxEvent &event) {
|
|
|
|
wxGetApp().obj_list()->add_layer_range(layer.first);
|
|
|
|
});
|
2019-05-30 10:41:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 12:41:16 +00:00
|
|
|
void ObjectLayers::create_layer(int id)
|
2019-05-30 10:41:16 +00:00
|
|
|
{
|
2019-06-03 13:35:21 +00:00
|
|
|
t_layer_config_ranges::iterator layer_range = m_object->layer_config_ranges.begin();
|
2019-05-30 12:41:16 +00:00
|
|
|
|
2019-05-31 08:54:52 +00:00
|
|
|
// May be not a best solution #ys_FIXME
|
2019-06-03 13:35:21 +00:00
|
|
|
while (id > 0 && layer_range != m_object->layer_config_ranges.end()) {
|
|
|
|
++layer_range;
|
2019-05-30 12:41:16 +00:00
|
|
|
id--;
|
2019-05-30 10:41:16 +00:00
|
|
|
}
|
2019-06-04 09:51:25 +00:00
|
|
|
|
|
|
|
create_layer_without_buttons(*layer_range);
|
2019-05-30 10:41:16 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 14:38:04 +00:00
|
|
|
void ObjectLayers::update_layers_list()
|
|
|
|
{
|
|
|
|
ObjectList* objects_ctrl = wxGetApp().obj_list();
|
|
|
|
if (objects_ctrl->multiple_selection()) return;
|
|
|
|
|
|
|
|
const auto item = objects_ctrl->GetSelection();
|
|
|
|
if (!item) return;
|
|
|
|
|
|
|
|
const int obj_idx = objects_ctrl->get_selected_obj_idx();
|
|
|
|
if (obj_idx < 0) return;
|
|
|
|
|
|
|
|
const ItemType type = objects_ctrl->GetModel()->GetItemType(item);
|
|
|
|
if (!(type & (itLayerRoot | itLayer))) return;
|
|
|
|
|
2019-05-30 10:41:16 +00:00
|
|
|
m_object = objects_ctrl->object(obj_idx);
|
2019-06-03 13:35:21 +00:00
|
|
|
if (!m_object || m_object->layer_config_ranges.empty()) return;
|
2019-05-28 14:38:04 +00:00
|
|
|
|
2019-05-30 10:41:16 +00:00
|
|
|
// Delete all controls from options group except of the legends
|
2019-05-28 14:38:04 +00:00
|
|
|
|
2019-06-04 09:51:25 +00:00
|
|
|
const int cols = m_grid_sizer->GetEffectiveColsCount();
|
|
|
|
const int rows = m_grid_sizer->GetEffectiveRowsCount();
|
2019-05-28 14:38:04 +00:00
|
|
|
for (int idx = cols*rows-1; idx >= cols; idx--) {
|
2019-06-04 09:51:25 +00:00
|
|
|
wxSizerItem* t = m_grid_sizer->GetItem(idx);
|
2019-05-30 10:41:16 +00:00
|
|
|
if (t->IsSizer())
|
|
|
|
t->GetSizer()->Clear(true);
|
2019-06-04 09:51:25 +00:00
|
|
|
else
|
|
|
|
t->DeleteWindows();
|
|
|
|
m_grid_sizer->Remove(idx);
|
2019-05-28 14:38:04 +00:00
|
|
|
}
|
|
|
|
|
2019-05-30 10:41:16 +00:00
|
|
|
// Add new control according to the selected item
|
|
|
|
|
|
|
|
if (type & itLayerRoot)
|
|
|
|
create_layers_list();
|
|
|
|
else
|
2019-05-30 12:41:16 +00:00
|
|
|
create_layer(objects_ctrl->GetModel()->GetLayerIdByItem(item));
|
2019-05-30 10:41:16 +00:00
|
|
|
|
|
|
|
m_parent->Layout();
|
2019-05-28 14:38:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ObjectLayers::UpdateAndShow(const bool show)
|
|
|
|
{
|
|
|
|
if (show)
|
|
|
|
update_layers_list();
|
|
|
|
|
|
|
|
OG_Settings::UpdateAndShow(show);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ObjectLayers::msw_rescale()
|
|
|
|
{
|
|
|
|
m_bmp_delete.msw_rescale();
|
|
|
|
m_bmp_add.msw_rescale();
|
|
|
|
}
|
|
|
|
|
2019-06-04 13:22:29 +00:00
|
|
|
LayerRangeEditor::LayerRangeEditor( wxWindow* parent,
|
|
|
|
const wxString& value,
|
2019-06-05 09:03:46 +00:00
|
|
|
std::function<bool(coordf_t)> edit_fn
|
2019-06-04 13:22:29 +00:00
|
|
|
) :
|
2019-06-04 15:30:44 +00:00
|
|
|
wxTextCtrl(parent, wxID_ANY, value, wxDefaultPosition,
|
|
|
|
wxSize(field_width * em_unit(parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
|
2019-06-04 13:22:29 +00:00
|
|
|
{
|
|
|
|
this->SetFont(wxGetApp().normal_font());
|
|
|
|
|
|
|
|
this->Bind(wxEVT_TEXT_ENTER, ([this, edit_fn](wxEvent& e)
|
|
|
|
{
|
2019-06-05 09:03:46 +00:00
|
|
|
m_enter_pressed = true;
|
|
|
|
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
|
|
|
|
if ( !edit_fn(get_value()) )
|
|
|
|
m_call_kill_focus = true;
|
2019-06-04 13:22:29 +00:00
|
|
|
}), this->GetId());
|
|
|
|
|
2019-06-05 09:03:46 +00:00
|
|
|
this->Bind(wxEVT_KILL_FOCUS, ([this, edit_fn](wxEvent& e)
|
2019-06-04 13:22:29 +00:00
|
|
|
{
|
2019-06-04 15:30:44 +00:00
|
|
|
if (!m_enter_pressed) {
|
|
|
|
m_enter_pressed = false;
|
2019-06-05 09:03:46 +00:00
|
|
|
|
|
|
|
// If LayersList wasn't updated/recreated, we should call e.Skip()
|
|
|
|
if ( !edit_fn(get_value()) )
|
|
|
|
e.Skip();
|
2019-06-04 15:30:44 +00:00
|
|
|
}
|
2019-06-05 09:03:46 +00:00
|
|
|
else if (m_call_kill_focus)
|
|
|
|
e.Skip();
|
2019-06-04 13:22:29 +00:00
|
|
|
}), this->GetId());
|
|
|
|
|
|
|
|
|
|
|
|
this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event)
|
|
|
|
{
|
|
|
|
// select all text using Ctrl+A
|
|
|
|
if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
|
|
|
|
this->SetSelection(-1, -1); //select all
|
|
|
|
event.Skip();
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
coordf_t LayerRangeEditor::get_value()
|
|
|
|
{
|
|
|
|
wxString str = GetValue();
|
|
|
|
|
|
|
|
coordf_t layer_height;
|
|
|
|
// Replace the first occurence of comma in decimal number.
|
|
|
|
str.Replace(",", ".", false);
|
|
|
|
if (str == ".")
|
|
|
|
layer_height = 0.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!str.ToCDouble(&layer_height) || layer_height < 0.0f)
|
|
|
|
{
|
|
|
|
show_error(m_parent, _(L("Invalid numeric input.")));
|
|
|
|
SetValue(double_to_string(layer_height));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return layer_height;
|
|
|
|
}
|
|
|
|
|
2019-05-28 14:38:04 +00:00
|
|
|
} //namespace GUI
|
|
|
|
} //namespace Slic3r
|