diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ba6e71b7ea..256b14695f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -720,6 +720,7 @@ namespace DoExport { const FullPrintConfig &config, const std::vector &extruders, unsigned int initial_extruder_id, + int total_toolchanges, PrintStatistics &print_statistics, bool export_binary_data, bgcode::binarize::BinaryData &binary_data) @@ -727,7 +728,7 @@ namespace DoExport { std::string filament_stats_string_out; print_statistics.clear(); - print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); + print_statistics.total_toolchanges = total_toolchanges; print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; if (! extruders.empty()) { @@ -1161,7 +1162,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. this->placeholder_parser().set("has_wipe_tower", has_wipe_tower); this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); - this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + this->placeholder_parser().set("total_toolchanges", tool_ordering.toolchanges_count()); { BoundingBoxf bbox(print.config().bed_shape.values); assert(bbox.defined); @@ -1389,6 +1390,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail this->config(), m_writer.extruders(), initial_extruder_id, + tool_ordering.toolchanges_count(), // Modifies print.m_print_statistics, export_to_binary_gcode, diff --git a/src/libslic3r/GCode/LabelObjects.cpp b/src/libslic3r/GCode/LabelObjects.cpp index c7eb8c2fb7..7cc7fc57c7 100644 --- a/src/libslic3r/GCode/LabelObjects.cpp +++ b/src/libslic3r/GCode/LabelObjects.cpp @@ -109,7 +109,7 @@ std::string LabelObjects::all_objects_header() const out += "\n"; for (const auto& [print_instance, label] : label_data_sorted) { - if (m_flavor == gcfKlipper) { + if (m_label_objects_style == LabelObjectsStyle::Firmware && m_flavor == gcfKlipper) { char buffer[64]; out += "EXCLUDE_OBJECT_DEFINE NAME=" + label.name; Polygon outline = instance_outline(print_instance); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 6bd092204c..2eba21de8c 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -479,6 +479,9 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ bool ToolOrdering::insert_wipe_tower_extruder() { + if (!m_print_config_ptr->wipe_tower) + return false; + // In case that wipe_tower_extruder is set to non-zero, we must make sure that the extruder will be in the list. bool changed = false; if (m_print_config_ptr->wipe_tower_extruder != 0) { @@ -836,4 +839,20 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print, const } } + +int ToolOrdering::toolchanges_count() const +{ + std::vector tools_in_order; + for (const LayerTools& lt : m_layer_tools) + tools_in_order.insert(tools_in_order.end(), lt.extruders.begin(), lt.extruders.end()); + assert(std::find(tools_in_order.begin(), tools_in_order.end(), (unsigned int)(-1)) == tools_in_order.end()); + for (size_t i=1; i 1 && tools_in_order.back() == tools_in_order[tools_in_order.size()-2]) + tools_in_order.pop_back(); + return std::max(0, int(tools_in_order.size())-1); // 5 tools = 4 toolchanges +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 3196a7012d..028663660b 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -165,6 +165,7 @@ public: bool empty() const { return m_layer_tools.empty(); } std::vector& layer_tools() { return m_layer_tools; } bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; } + int toolchanges_count() const; private: void initialize_layers(std::vector &zs); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 464d0fe755..a899f2f2be 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -793,12 +793,13 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool) .set_initial_tool(m_current_tool) .set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f)) .append(";--------------------\n" - "; CP TOOLCHANGE START\n") - .comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based + "; CP TOOLCHANGE START\n"); - if (tool != (unsigned)(-1)) + if (tool != (unsigned)(-1)) { + writer.comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str()) .append(";--------------------\n"); + } writer.speed_override_backup(); writer.speed_override(100); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7e5e5fecdf..3d555bdbce 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -474,7 +474,9 @@ struct WipeTowerData used_filament.clear(); number_of_toolchanges = -1; depth = 0.f; + z_and_depth_pairs.clear(); brim_width = 0.f; + height = 0.f; width = 0.f; first_layer_height = 0.f; cone_angle = 0.f; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6c4c5e1c3a..748412d770 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1163,7 +1163,7 @@ void Sidebar::update_reslice_btn_tooltip() const void Sidebar::msw_rescale() { - SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1)); + SetMinSize(wxSize(42 * wxGetApp().em_unit(), -1)); for (PlaterPresetComboBox* combo : std::vector { p->combo_print, p->combo_sla_print, @@ -1502,8 +1502,7 @@ void Sidebar::update_sliced_info_sizer() p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); } - // if there is a wipe tower, insert number of toolchanges info into the array: - p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", ps.total_toolchanges) : "N/A"); + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, ps.total_toolchanges > 0 ? wxString::Format("%.d", ps.total_toolchanges) : "N/A"); // Hide non-FFF sliced info parameters p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index eb4dde3d1c..dc979720a6 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -74,15 +74,19 @@ PreferencesDialog::PreferencesDialog(wxWindow* parent) : build(); wxSize sz = GetSize(); - sz.x += em_unit(); + bool is_scrollbar_shown = false; const size_t pages_cnt = tabs->GetPageCount(); for (size_t tab_id = 0; tab_id < pages_cnt; tab_id++) { wxSizer* tab_sizer = tabs->GetPage(tab_id)->GetSizer(); wxScrolledWindow* scrolled = static_cast(tab_sizer->GetItem(size_t(0))->GetWindow()); scrolled->SetScrollRate(0, 5); + + is_scrollbar_shown |= scrolled->GetScrollLines(wxVERTICAL) > 0; } + if (is_scrollbar_shown) + sz.x += 2*em_unit(); SetSize(sz); m_highlighter.set_timer_owner(this, 0); diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index 5320beefcc..f696fc7945 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -61,7 +61,10 @@ ComboBox::ComboBox(wxWindow * parent, for (int i = 0; i < n; ++i) Append(choices[i]); } -int ComboBox::GetSelection() const { return drop.GetSelection(); } +int ComboBox::GetSelection() const +{ + return drop.GetSelection(); +} void ComboBox::SetSelection(int n) { @@ -233,6 +236,11 @@ wxBitmap ComboBox::GetItemBitmap(unsigned int n) return icons[n].GetBitmapFor(m_parent); } +void ComboBox::OnKeyDown(wxKeyEvent &event) +{ + keyDown(event); +} + int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items, unsigned int pos, void ** clientData, @@ -290,10 +298,15 @@ void ComboBox::keyDown(wxKeyEvent& event) { int key_code = event.GetKeyCode(); switch (key_code) { -#ifndef __WXOSX__ case WXK_RETURN: if (drop_down) { drop.DismissAndNotify(); + + wxCommandEvent e(wxEVT_COMBOBOX); + e.SetEventObject(this); + e.SetId(GetId()); + e.SetInt(GetSelection()); + GetEventHandler()->ProcessEvent(e); } else if (drop.HasDismissLongTime()) { drop.autoPosition(); drop_down = true; @@ -302,32 +315,53 @@ void ComboBox::keyDown(wxKeyEvent& event) GetEventHandler()->ProcessEvent(e); } break; -#endif - case WXK_UP: - case WXK_DOWN: - case WXK_LEFT: - case WXK_RIGHT: - if ((event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) && GetSelection() > 0) { + case WXK_UP: { + if (GetSelection() > 0) SetSelection(GetSelection() - 1); - } else if ((event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_RIGHT) && GetSelection() + 1 < int(texts.size())) { + break; + } + case WXK_DOWN: { + if (GetSelection() + 1 < int(texts.size())) SetSelection(GetSelection() + 1); - } else { + break; + } + case WXK_LEFT: { + if (HasFlag(wxCB_READONLY)) { + if(GetSelection() > 0) + SetSelection(GetSelection() - 1); break; } - { - wxCommandEvent e(wxEVT_COMBOBOX); - e.SetEventObject(this); - e.SetId(GetId()); - e.SetInt(GetSelection()); - GetEventHandler()->ProcessEvent(e); - } + const auto pos = GetTextCtrl()->GetInsertionPoint(); + if(pos > 0) + GetTextCtrl()->SetInsertionPoint(pos - 1); break; + } + case WXK_RIGHT: { + if (HasFlag(wxCB_READONLY)) { + if (GetSelection() + 1 < int(texts.size())) + SetSelection(GetSelection() + 1); + break; + } + const size_t pos = size_t(GetTextCtrl()->GetInsertionPoint()); + if (pos < GetLabel().Length()) + GetTextCtrl()->SetInsertionPoint(pos + 1); + break; + } case WXK_TAB: HandleAsNavigationKey(event); break; - default: + default: { + if (drop.IsShown() && HasFlag(wxCB_READONLY)) { + for (size_t n = 0; n < texts.size(); n++) { + if (texts[n].StartsWith(wxString(static_cast(key_code)))) { + SetSelection(int(n)); + break; + } + } + } event.Skip(); break; + } } } diff --git a/src/slic3r/GUI/Widgets/ComboBox.hpp b/src/slic3r/GUI/Widgets/ComboBox.hpp index 1268acec04..b94acf9312 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.hpp +++ b/src/slic3r/GUI/Widgets/ComboBox.hpp @@ -66,6 +66,8 @@ public: wxBitmap GetItemBitmap(unsigned int n); + void OnKeyDown(wxKeyEvent& event); + protected: virtual int DoInsertItems(const wxArrayStringsAdapter &items, unsigned int pos, diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index 4aac437458..6eccc07e35 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -1,4 +1,5 @@ #include "DropDown.hpp" +#include "ComboBox.hpp" #include "../GUI_App.hpp" #include "../OptionsGroup.hpp" @@ -60,10 +61,60 @@ DropDown::DropDown(wxWindow * parent, Create(parent, style); } +#ifdef __WXGTK__ +static gint gtk_popup_key_press (GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win ) +{ + // Ignore events sent out before we connected to the signal + if (win->m_time >= ((GdkEventKey*)gdk_event)->time) + return FALSE; + + GtkWidget *child = gtk_get_event_widget (gdk_event); + + /* We don't ask for button press events on the grab widget, so + * if an event is reported directly to the grab widget, it must + * be on a window outside the application (and thus we remove + * the popup window). Otherwise, we check if the widget is a child + * of the grab widget, and only remove the popup window if it + * is not. */ + if (child != widget) { + while (child) { + if (child == widget) + return FALSE; + child = gtk_widget_get_parent(child); + } + } + + gchar* keyval = gdk_keyval_name(((GdkEventKey*)gdk_event)->keyval); + const long keyCode = strcmp(keyval, "Up") == 0 ? WXK_UP : + strcmp(keyval, "Down") == 0 ? WXK_DOWN : + strcmp(keyval, "Left") == 0 ? WXK_LEFT : + strcmp(keyval, "Right") == 0 ? WXK_RIGHT : + strcmp(keyval, "Return") == 0 ? WXK_RETURN : WXK_NONE; + + if (keyCode != WXK_NONE) { + wxKeyEvent event( wxEVT_KEY_DOWN, win->GetId()); + event.m_keyCode = keyCode; + event.SetEventObject( win ); + (void)win->HandleWindowEvent( event ); + } + + return TRUE; +} +#endif + void DropDown::Create(wxWindow * parent, long style) { wxPopupTransientWindow::Create(parent); +#ifdef __WXGTK__ + g_signal_connect (m_widget, "key_press_event", G_CALLBACK (gtk_popup_key_press), this); + + Bind(wxEVT_KEY_DOWN, [parent](wxKeyEvent &e) { + if (ComboBox* cb = dynamic_cast(parent)) + cb->OnKeyDown(e); + }); +#endif + if (!wxOSX) SetBackgroundStyle(wxBG_STYLE_PAINT); state_handler.attach({&border_color, &text_color, &selector_border_color, &selector_background_color}); state_handler.update_binds(); @@ -95,6 +146,8 @@ void DropDown::SetSelection(int n) n = -1; if (selection == n) return; selection = n; + if (IsShown()) + autoPosition(); paintNow(); } @@ -212,7 +265,11 @@ void DropDown::SetTransparentBG(wxDC& dc, wxWindow* win) } constexpr int slider_width = 12; +#ifdef __WXOSX__ +constexpr int slider_step = 1; +#else constexpr int slider_step = 5; +#endif constexpr int items_padding = 2; /* @@ -423,14 +480,14 @@ void DropDown::autoPosition() if (use_content_width && texts.size() <= 15) size.x += 6; size.y = drect.GetBottom() - GetPosition().y - 10; wxWindow::SetSize(size); - if (selection >= 0) { - if (offset.y + rowSize.y * (selection + 1) > size.y) - offset.y = size.y - rowSize.y * (selection + 1); - else if (offset.y + rowSize.y * selection < 0) - offset.y = -rowSize.y * selection; - } } } + if (selection >= 0) { + if (offset.y + rowSize.y * (selection + 1) > size.y) + offset.y = size.y - rowSize.y * (selection + 3); + else if (offset.y + rowSize.y * selection < 0) + offset.y = -rowSize.y * selection; + } } void DropDown::mouseDown(wxMouseEvent& event) @@ -461,6 +518,10 @@ void DropDown::mouseReleased(wxMouseEvent& event) if (HasCapture()) ReleaseMouse(); if (hover_item >= 0) { // not moved +#ifdef __WXOSX__ + // To avoid cases, when some dialog appears after item selection, but DropDown is still shown + Hide(); +#endif sendDropDownEvent(); DismissAndNotify(); } @@ -507,6 +568,8 @@ void DropDown::mouseMove(wxMouseEvent &event) void DropDown::mouseWheelMoved(wxMouseEvent &event) { + if (event.GetWheelRotation() == 0) + return; auto delta = event.GetWheelRotation() > 0 ? rowSize.y : -rowSize.y; wxPoint pt2 = offset + wxPoint{0, slider_step * delta}; int text_size = int(texts.size());