diff --git a/MANIFEST b/MANIFEST index 136772e5e..181a9db4e 100644 --- a/MANIFEST +++ b/MANIFEST @@ -34,6 +34,7 @@ lib/Slic3r/GUI/OptionsGroup.pm lib/Slic3r/GUI/Plater.pm lib/Slic3r/GUI/Preferences.pm lib/Slic3r/GUI/SkeinPanel.pm +lib/Slic3r/GUI/SimpleTab.pm lib/Slic3r/GUI/Tab.pm lib/Slic3r/Layer.pm lib/Slic3r/Layer/Region.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index d1e36babd..eaa7e45bc 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -10,6 +10,7 @@ use Slic3r::GUI::Plater; use Slic3r::GUI::Preferences; use Slic3r::GUI::OptionsGroup; use Slic3r::GUI::SkeinPanel; +use Slic3r::GUI::SimpleTab; use Slic3r::GUI::Tab; use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow); diff --git a/lib/Slic3r/GUI/SimpleTab.pm b/lib/Slic3r/GUI/SimpleTab.pm new file mode 100644 index 000000000..29044e4a1 --- /dev/null +++ b/lib/Slic3r/GUI/SimpleTab.pm @@ -0,0 +1,224 @@ +package Slic3r::GUI::SimpleTab; +use strict; +use warnings; +use utf8; + +use File::Basename qw(basename); +use List::Util qw(first); +use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :window :systemsettings); +use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED); +use base 'Wx::ScrolledWindow'; + +sub new { + my $class = shift; + my ($parent, %params) = @_; + my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); + $self->{options} = []; # array of option names handled by this tab + $self->{$_} = $params{$_} for qw(on_value_change); + + $self->SetScrollbars(1, 1, 1, 1); + + $self->{config} = Slic3r::Config->new; + $self->{optgroups} = []; + + $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL); + $self->SetSizer($self->{vsizer}); + $self->build; + + { + my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize); + $label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + $self->{vsizer}->Add($label, 0, wxEXPAND | wxALL, 10); + } + + return $self; +} + +sub append_optgroup { + my $self = shift; + my %params = @_; + + # apply default values + { + my @options = @{$params{options}}; + $_ =~ s/#.+// for @options; + my $config = Slic3r::Config->new_from_defaults(@options); + $self->{config}->apply($config); + } + + my $class = $params{class} || 'Slic3r::GUI::ConfigOptionsGroup'; + my $optgroup = $class->new( + parent => $self, + config => $self->{config}, + label_width => 200, + on_change => sub { $self->on_value_change(@_) }, + %params, + ); + + push @{$self->{optgroups}}, $optgroup; + ($params{sizer} || $self->{vsizer})->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10); +} + +sub load_config_file { + my $self = shift; + my ($file) = @_; + + my $config = Slic3r::Config->load($file); + $self->load_config($config); +} + +sub load_config { + my $self = shift; + my ($config) = @_; + + foreach my $opt_key (grep $self->{config}->has($_), keys %$config) { + my $value = $config->get($opt_key); + $self->{config}->set($opt_key, $value); + $_->set_value($opt_key, $value) for @{$self->{optgroups}}; + } +} + +sub is_dirty { 0 } +sub config { $_[0]->{config}->clone } + +# propagate event to the parent +sub on_value_change { + my $self = shift; + $self->{on_value_change}->(@_) if $self->{on_value_change}; +} + +package Slic3r::GUI::SimpleTab::Print; +use base 'Slic3r::GUI::SimpleTab'; + +use Wx qw(:sizer); + +sub name { 'print' } +sub title { 'Print Settings' } + +sub build { + my $self = shift; + + $self->append_optgroup( + title => 'General', + options => [qw(layer_height perimeters top_solid_layers bottom_solid_layers)], + lines => [ + Slic3r::GUI::OptionsGroup->single_option_line('layer_height'), + Slic3r::GUI::OptionsGroup->single_option_line('perimeters'), + { + label => 'Solid layers', + options => [qw(top_solid_layers bottom_solid_layers)], + }, + ], + ); + + $self->append_optgroup( + title => 'Infill', + options => [qw(fill_density fill_pattern)], + ); + + $self->append_optgroup( + title => 'Support material', + options => [qw(support_material support_material_spacing raft_layers)], + ); + + $self->append_optgroup( + title => 'Speed', + options => [qw(perimeter_speed infill_speed travel_speed)], + ); + + $self->append_optgroup( + title => 'Brim', + options => [qw(brim_width)], + ); + + $self->append_optgroup( + title => 'Sequential printing', + options => [qw(complete_objects extruder_clearance_radius extruder_clearance_height)], + lines => [ + Slic3r::GUI::OptionsGroup->single_option_line('complete_objects'), + { + label => 'Extruder clearance (mm)', + options => [qw(extruder_clearance_radius extruder_clearance_height)], + }, + ], + ); +} + +package Slic3r::GUI::SimpleTab::Filament; +use base 'Slic3r::GUI::SimpleTab'; + +sub name { 'filament' } +sub title { 'Filament Settings' } + +sub build { + my $self = shift; + + $self->append_optgroup( + title => 'Filament', + options => ['filament_diameter#0', 'extrusion_multiplier#0'], + ); + + $self->append_optgroup( + title => 'Temperature (°C)', + options => ['temperature#0', 'first_layer_temperature#0', qw(bed_temperature first_layer_bed_temperature)], + lines => [ + { + label => 'Extruder', + options => ['first_layer_temperature#0', 'temperature#0'], + }, + { + label => 'Bed', + options => [qw(first_layer_bed_temperature bed_temperature)], + }, + ], + ); + + $self->append_optgroup( + title => 'Cooling', + options => [qw(cooling)], + ); +} + +package Slic3r::GUI::SimpleTab::Printer; +use base 'Slic3r::GUI::SimpleTab'; + +sub name { 'printer' } +sub title { 'Printer Settings' } + +sub build { + my $self = shift; + + $self->append_optgroup( + title => 'Size and coordinates', + options => [qw(bed_size print_center z_offset)], + ); + + $self->append_optgroup( + title => 'Firmware', + options => [qw(gcode_flavor)], + ); + + $self->append_optgroup( + title => 'Extruder', + options => ['nozzle_diameter#0'], + ); + + $self->append_optgroup( + title => 'Retraction', + options => ['retract_length#0', 'retract_lift#0'], + ); + + $self->append_optgroup( + title => 'Start G-code', + no_labels => 1, + options => [qw(start_gcode)], + ); + + $self->append_optgroup( + title => 'End G-code', + no_labels => 1, + options => [qw(end_gcode)], + ); +} + +1; diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 9c81d1ed5..fa7f078f3 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -33,26 +33,30 @@ sub new { $self->{tabpanel}->AddPage($self->{plater} = Slic3r::GUI::Plater->new($self->{tabpanel}), "Plater"); $self->{options_tabs} = {}; - my $config; - $config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini") - if -e "$Slic3r::GUI::datadir/simple.ini"; + my $simple_config; + if ($self->{mode} eq 'simple') { + $simple_config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini") + if -e "$Slic3r::GUI::datadir/simple.ini"; + } + my $class_prefix = $self->{mode} eq 'simple' ? "Slic3r::GUI::SimpleTab::" : "Slic3r::GUI::Tab::"; + my $init = 0; for my $tab_name (qw(print filament printer)) { - $self->{options_tabs}{$tab_name} = ("Slic3r::GUI::Tab::" . ucfirst $tab_name)->new( + my $tab = $self->{options_tabs}{$tab_name} = ($class_prefix . ucfirst $tab_name)->new( $self->{tabpanel}, - mode => $self->{mode}, plater => $self->{plater}, - config => $config, on_value_change => sub { $self->{plater}->on_config_change(@_); # propagate config change events to the plater - if ($self->{mode} eq 'simple') { + if ($self->{mode} eq 'simple' && $init) { # don't save while loading for the first time # save config $self->config->save("$Slic3r::GUI::datadir/simple.ini"); } }, ); - $self->{tabpanel}->AddPage($self->{options_tabs}{$tab_name}, $self->{options_tabs}{$tab_name}->title); + $self->{tabpanel}->AddPage($tab, $tab->title); + $tab->load_config($simple_config); } + $init = 1; my $sizer = Wx::BoxSizer->new(wxVERTICAL); $sizer->Add($self->{tabpanel}, 1, wxEXPAND); @@ -217,7 +221,7 @@ sub load_config_file { $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); Slic3r::GUI->save_settings; $last_config = $file; - $_->load_external_config($file) for values %{$self->{options_tabs}}; + $_->load_config_file($file) for values %{$self->{options_tabs}}; } sub load_config { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 13ae7d729..052fdbba1 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -14,133 +14,126 @@ sub new { my ($parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); $self->{options} = []; # array of option names handled by this tab - $self->{$_} = $params{$_} for qw(mode plater on_value_change); + $self->{$_} = $params{$_} for qw(plater on_value_change); # horizontal sizer $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer}->SetSizeHints($self); $self->SetSizer($self->{sizer}); - if ($self->{mode} eq 'expert') { - # left vertical sizer - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3); + # left vertical sizer + my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); + $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3); + + my $left_col_width = 150; + + # preset chooser + { - my $left_col_width = 150; + # choice menu + $self->{presets_choice} = Wx::Choice->new($self, -1, wxDefaultPosition, [$left_col_width, -1], []); + $self->{presets_choice}->SetFont($Slic3r::GUI::small_font); - # preset chooser - { - - # choice menu - $self->{presets_choice} = Wx::Choice->new($self, -1, wxDefaultPosition, [$left_col_width, -1], []); - $self->{presets_choice}->SetFont($Slic3r::GUI::small_font); - - # buttons - $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG)); - $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG)); - $self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title)); - $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); - $self->{btn_delete_preset}->Disable; - - ### These cause GTK warnings: - ###my $box = Wx::StaticBox->new($self, -1, "Presets:", wxDefaultPosition, [$left_col_width, 50]); - ###my $hsizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL); - - my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); - - $left_sizer->Add($hsizer, 0, wxEXPAND | wxBOTTOM, 5); - $hsizer->Add($self->{presets_choice}, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 3); - $hsizer->Add($self->{btn_save_preset}, 0, wxALIGN_CENTER_VERTICAL); - $hsizer->Add($self->{btn_delete_preset}, 0, wxALIGN_CENTER_VERTICAL); - } + # buttons + $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG)); + $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG)); + $self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title)); + $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); + $self->{btn_delete_preset}->Disable; - # tree - $self->{treectrl} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [$left_col_width, -1], wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); - $left_sizer->Add($self->{treectrl}, 1, wxEXPAND); - $self->{icons} = Wx::ImageList->new(16, 16, 1); - $self->{treectrl}->AssignImageList($self->{icons}); - $self->{iconcount} = -1; - $self->{treectrl}->AddRoot("root"); - $self->{pages} = []; - $self->{treectrl}->SetIndent(0); - EVT_TREE_SEL_CHANGED($parent, $self->{treectrl}, sub { - my $page = first { $_->{title} eq $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection) } @{$self->{pages}} - or return; - $_->Hide for @{$self->{pages}}; - $page->Show; - $self->{sizer}->Layout; - $self->Refresh; - }); - EVT_KEY_DOWN($self->{treectrl}, sub { - my ($treectrl, $event) = @_; - if ($event->GetKeyCode == WXK_TAB) { - $treectrl->Navigate($event->ShiftDown ? &Wx::wxNavigateBackward : &Wx::wxNavigateForward); - } else { - $event->Skip; - } - }); + ### These cause GTK warnings: + ###my $box = Wx::StaticBox->new($self, -1, "Presets:", wxDefaultPosition, [$left_col_width, 50]); + ###my $hsizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL); - EVT_CHOICE($parent, $self->{presets_choice}, sub { - $self->on_select_preset; - $self->sync_presets; - }); + my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); - EVT_BUTTON($self, $self->{btn_save_preset}, sub { - - # since buttons (and choices too) don't get focus on Mac, we set focus manually - # to the treectrl so that the EVT_* events are fired for the input field having - # focus currently. is there anything better than this? - $self->{treectrl}->SetFocus; - - my $preset = $self->current_preset; - my $default_name = $preset->{default} ? 'Untitled' : basename($preset->{name}); - $default_name =~ s/\.ini$//i; - - my $dlg = Slic3r::GUI::SavePresetWindow->new($self, - title => lc($self->title), - default => $default_name, - values => [ map { my $name = $_->{name}; $name =~ s/\.ini$//i; $name } @{$self->{presets}} ], - ); - return unless $dlg->ShowModal == wxID_OK; - - my $file = sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $dlg->get_name; - $self->config->save($file); - $self->set_dirty(0); - $self->load_presets; - $self->{presets_choice}->SetSelection(first { basename($self->{presets}[$_]{file}) eq $dlg->get_name . ".ini" } 1 .. $#{$self->{presets}}); - $self->on_select_preset; - $self->sync_presets; - }); - - EVT_BUTTON($self, $self->{btn_delete_preset}, sub { - my $i = $self->{presets_choice}->GetSelection; - return if $i == 0; # this shouldn't happen but let's trap it anyway - my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; - return unless $res == wxID_YES; - if (-e $self->{presets}[$i]{file}) { - unlink $self->{presets}[$i]{file}; - } - splice @{$self->{presets}}, $i, 1; - $self->set_dirty(0); - $self->{presets_choice}->Delete($i); - $self->{presets_choice}->SetSelection(0); - $self->on_select_preset; - $self->sync_presets; - }); + $left_sizer->Add($hsizer, 0, wxEXPAND | wxBOTTOM, 5); + $hsizer->Add($self->{presets_choice}, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 3); + $hsizer->Add($self->{btn_save_preset}, 0, wxALIGN_CENTER_VERTICAL); + $hsizer->Add($self->{btn_delete_preset}, 0, wxALIGN_CENTER_VERTICAL); } - if ($self->{mode} eq 'expert') { - $self->{config} = Slic3r::Config->new; - $self->build; - if ($self->hidden_options) { - $self->{config}->apply(Slic3r::Config->new_from_defaults($self->hidden_options)); - push @{$self->{options}}, $self->hidden_options; + # tree + $self->{treectrl} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [$left_col_width, -1], wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); + $left_sizer->Add($self->{treectrl}, 1, wxEXPAND); + $self->{icons} = Wx::ImageList->new(16, 16, 1); + $self->{treectrl}->AssignImageList($self->{icons}); + $self->{iconcount} = -1; + $self->{treectrl}->AddRoot("root"); + $self->{pages} = []; + $self->{treectrl}->SetIndent(0); + EVT_TREE_SEL_CHANGED($parent, $self->{treectrl}, sub { + my $page = first { $_->{title} eq $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection) } @{$self->{pages}} + or return; + $_->Hide for @{$self->{pages}}; + $page->Show; + $self->{sizer}->Layout; + $self->Refresh; + }); + EVT_KEY_DOWN($self->{treectrl}, sub { + my ($treectrl, $event) = @_; + if ($event->GetKeyCode == WXK_TAB) { + $treectrl->Navigate($event->ShiftDown ? &Wx::wxNavigateBackward : &Wx::wxNavigateForward); + } else { + $event->Skip; } + }); + + EVT_CHOICE($parent, $self->{presets_choice}, sub { + $self->on_select_preset; + $self->sync_presets; + }); + + EVT_BUTTON($self, $self->{btn_save_preset}, sub { + + # since buttons (and choices too) don't get focus on Mac, we set focus manually + # to the treectrl so that the EVT_* events are fired for the input field having + # focus currently. is there anything better than this? + $self->{treectrl}->SetFocus; + + my $preset = $self->current_preset; + my $default_name = $preset->{default} ? 'Untitled' : basename($preset->{name}); + $default_name =~ s/\.ini$//i; + + my $dlg = Slic3r::GUI::SavePresetWindow->new($self, + title => lc($self->title), + default => $default_name, + values => [ map { my $name = $_->{name}; $name =~ s/\.ini$//i; $name } @{$self->{presets}} ], + ); + return unless $dlg->ShowModal == wxID_OK; + + my $file = sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $dlg->get_name; + $self->config->save($file); + $self->set_dirty(0); $self->load_presets; - } else { - $self->{config} = $params{config} || Slic3r::Config->new; - $self->build_simple; + $self->{presets_choice}->SetSelection(first { basename($self->{presets}[$_]{file}) eq $dlg->get_name . ".ini" } 1 .. $#{$self->{presets}}); + $self->on_select_preset; + $self->sync_presets; + }); + + EVT_BUTTON($self, $self->{btn_delete_preset}, sub { + my $i = $self->{presets_choice}->GetSelection; + return if $i == 0; # this shouldn't happen but let's trap it anyway + my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; + return unless $res == wxID_YES; + if (-e $self->{presets}[$i]{file}) { + unlink $self->{presets}[$i]{file}; + } + splice @{$self->{presets}}, $i, 1; + $self->set_dirty(0); + $self->{presets_choice}->Delete($i); + $self->{presets_choice}->SetSelection(0); + $self->on_select_preset; + $self->sync_presets; + }); + + $self->{config} = Slic3r::Config->new; + $self->build; + if ($self->hidden_options) { + $self->{config}->apply(Slic3r::Config->new_from_defaults($self->hidden_options)); + push @{$self->{options}}, $self->hidden_options; } + $self->load_presets; return $self; } @@ -178,7 +171,6 @@ sub select_preset { sub on_select_preset { my $self = shift; - return if $self->{mode} ne 'expert'; if (defined $self->{dirty}) { my $name = $self->{dirty} == 0 ? 'Default preset' : "Preset \"$self->{presets}[$self->{dirty}]{name}\""; @@ -224,7 +216,7 @@ sub get_preset_config { return; } - # apply preset values on top of defaults + # apply preset values on top of defaults my $external_config = Slic3r::Config->load($preset->{file}); my $config = Slic3r::Config->new; $config->set($_, $external_config->get($_)) @@ -238,7 +230,7 @@ sub add_options_page { my $self = shift; my ($title, $icon, %params) = @_; - if ($icon && $self->{icons}) { + if ($icon) { my $bitmap = Wx::Bitmap->new("$Slic3r::var/$icon", wxBITMAP_TYPE_PNG); $self->{icons}->Add($bitmap); $self->{iconcount}++; @@ -250,7 +242,7 @@ sub add_options_page { my %defaults_to_set = map { $_ => 1 } @options; # apply default values for the options we don't have already - delete $defaults_to_set{$_} for @{$self->{options}}, keys %{$self->{config}}; + delete $defaults_to_set{$_} for @{$self->{options}}; $self->{config}->apply(Slic3r::Config->new_from_defaults(keys %defaults_to_set)) if %defaults_to_set; # append such options to our list @@ -262,7 +254,7 @@ sub add_options_page { $self->set_dirty(1); $self->sync_presets; }); - $page->Hide if $self->{mode} eq 'expert'; + $page->Hide; $self->{sizer}->Add($page, 1, wxEXPAND | wxLEFT, 5); push @{$self->{pages}}, $page; $self->update_tree; @@ -289,7 +281,6 @@ sub reload_values { sub update_tree { my $self = shift; my ($select) = @_; - return if !$self->{treectrl}; $select //= 0; #/ @@ -304,7 +295,6 @@ sub update_tree { sub set_dirty { my $self = shift; my ($dirty) = @_; - return if $self->{mode} ne 'expert'; my $selection = $self->{presets_choice}->GetSelection; my $i = $self->{dirty} // $selection; #/ @@ -360,38 +350,29 @@ sub load_presets { $self->sync_presets; } -sub load_external_config { +sub load_config_file { my $self = shift; my ($file) = @_; - if ($self->{mode} eq 'expert') { - # look for the loaded config among the existing menu items - my $i = first { $self->{presets}[$_]{file} eq $file && $self->{presets}[$_]{external} } 1..$#{$self->{presets}}; - if (!$i) { - my $preset_name = basename($file); # keep the .ini suffix - push @{$self->{presets}}, { - file => $file, - name => $preset_name, - external => 1, - }; - $self->{presets_choice}->Append($preset_name) if $self->{presets_choice}; - $i = $#{$self->{presets}}; - } - $self->{presets_choice}->SetSelection($i) if $self->{presets_choice}; - $self->on_select_preset; - $self->sync_presets; - } else { - my $config = Slic3r::Config->load($file); - foreach my $opt_key (keys %{$self->{config}}) { - $self->{config}->set($opt_key, $config->get($opt_key)); - } - $self->reload_values; + # look for the loaded config among the existing menu items + my $i = first { $self->{presets}[$_]{file} eq $file && $self->{presets}[$_]{external} } 1..$#{$self->{presets}}; + if (!$i) { + my $preset_name = basename($file); # keep the .ini suffix + push @{$self->{presets}}, { + file => $file, + name => $preset_name, + external => 1, + }; + $self->{presets_choice}->Append($preset_name); + $i = $#{$self->{presets}}; } + $self->{presets_choice}->SetSelection($i); + $self->on_select_preset; + $self->sync_presets; } sub sync_presets { my $self = shift; - return if $self->{mode} ne 'expert'; $self->{plater}->update_presets($self->name, [$self->{presets_choice}->GetStrings], $self->{presets_choice}->GetSelection); } @@ -401,54 +382,6 @@ use base 'Slic3r::GUI::Tab'; sub name { 'print' } sub title { 'Print Settings' } -sub build_simple { - my $self = shift; - - $self->add_options_page('', '', optgroups => [ - { - title => 'General', - column => 0, - options => [qw(layer_height perimeters top_solid_layers bottom_solid_layers)], - lines => [ - Slic3r::GUI::OptionsGroup->single_option_line('layer_height'), - Slic3r::GUI::OptionsGroup->single_option_line('perimeters'), - { - label => 'Solid layers', - options => [qw(top_solid_layers bottom_solid_layers)], - }, - ], - }, - { - title => 'Infill', - column => 0, - options => [qw(fill_density fill_pattern)], - }, - { - title => 'Support material', - options => [qw(support_material support_material_spacing raft_layers)], - }, - { - title => 'Speed', - options => [qw(perimeter_speed infill_speed travel_speed)], - }, - { - title => 'Brim', - options => [qw(brim_width)], - }, - { - title => 'Sequential printing', - options => [qw(complete_objects extruder_clearance_radius extruder_clearance_height)], - lines => [ - Slic3r::GUI::OptionsGroup->single_option_line('complete_objects'), - { - label => 'Extruder clearance (mm)', - options => [qw(extruder_clearance_radius extruder_clearance_height)], - }, - ], - }, - ]); -} - sub build { my $self = shift; @@ -598,35 +531,6 @@ use base 'Slic3r::GUI::Tab'; sub name { 'filament' } sub title { 'Filament Settings' } -sub build_simple { - my $self = shift; - - $self->add_options_page('', '', optgroups => [ - { - title => 'Filament', - options => ['filament_diameter#0', 'extrusion_multiplier#0'], - }, - { - title => 'Temperature (°C)', - options => ['temperature#0', 'first_layer_temperature#0', qw(bed_temperature first_layer_bed_temperature)], - lines => [ - { - label => 'Extruder', - options => ['first_layer_temperature#0', 'temperature#0'], - }, - { - label => 'Bed', - options => [qw(first_layer_bed_temperature bed_temperature)], - }, - ], - }, - { - title => 'Cooling', - options => [qw(cooling)], - }, - ]); -} - sub build { my $self = shift; @@ -703,7 +607,7 @@ sub _update_description { $msg .= "\nDuring the other layers, fan will be turned off." } } - $self->{description_line}->SetText($msg) if $self->{description_line}; + $self->{description_line}->SetText($msg); } sub on_value_change { @@ -720,39 +624,6 @@ use base 'Slic3r::GUI::Tab'; sub name { 'printer' } sub title { 'Printer Settings' } -sub build_simple { - my $self = shift; - - $self->add_options_page('', '', optgroups => [ - { - title => 'Size and coordinates', - options => [qw(bed_size print_center z_offset)], - }, - { - title => 'Firmware', - options => [qw(gcode_flavor)], - }, - { - title => 'Extruder', - options => ['nozzle_diameter#0'], - }, - { - title => 'Retraction', - options => ['retract_length#0', 'retract_lift#0'], - }, - { - title => 'Start G-code', - no_labels => 1, - options => [qw(start_gcode)], - }, - { - title => 'End G-code', - no_labels => 1, - options => [qw(end_gcode)], - }, - ]); -} - sub build { my $self = shift; @@ -822,11 +693,9 @@ sub config { my $config = $self->SUPER::config(@_); - if ($self->{mode} eq 'expert') { - # remove all unused values - foreach my $opt_key ($self->_extruder_options) { - splice @{ $config->{$opt_key} }, $self->{extruders_count}; - } + # remove all unused values + foreach my $opt_key ($self->_extruder_options) { + splice @{ $config->{$opt_key} }, $self->{extruders_count}; } return $config; @@ -906,9 +775,9 @@ sub on_preset_loaded { } } -sub load_external_config { +sub load_config_file { my $self = shift; - $self->SUPER::load_external_config(@_); + $self->SUPER::load_config_file(@_); Slic3r::GUI::warning_catcher($self)->( "Your configuration was imported. However, Slic3r is currently only able to import settings " @@ -918,11 +787,9 @@ sub load_external_config { } package Slic3r::GUI::Tab::Page; -use Wx qw(:misc :panel :sizer :systemsettings); +use Wx qw(:misc :panel :sizer); use base 'Wx::ScrolledWindow'; -use List::Util qw(max); - sub new { my $class = shift; my ($parent, $title, $iconID, %params) = @_; @@ -936,27 +803,12 @@ sub new { $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL); $self->SetSizer($self->{vsizer}); - $self->{hsizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{vsizer}->Add($self->{hsizer}, 0, wxEXPAND | wxALL, 0); - if ($params{optgroups}) { - $_->{column} //= 0 for @{$params{optgroups}}; - for my $col (0 .. max(map $_->{column}, @{$params{optgroups}})) { - my $vertical_sizer = Wx::BoxSizer->new(wxVERTICAL); - $self->{hsizer}->Add($vertical_sizer, 1, wxEXPAND | wxALL, 0); - $self->append_optgroup( - %$_, - sizer => $vertical_sizer, - config => $parent->{config}, - on_change => $params{on_change}, - ) for grep $_->{column} == $col, @{$params{optgroups}}; - } - } - - if ($parent->{mode} ne 'expert') { - my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize); - $label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - $self->{vsizer}->Add($label, 0, wxEXPAND | wxALL, 10); + $self->append_optgroup( + %$_, + config => $parent->{config}, + on_change => $params{on_change}, + ) for @{$params{optgroups}}; } return $self; @@ -973,7 +825,7 @@ sub append_optgroup { label_width => 200, %params, ); - $params{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 5); + $self->{vsizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 5); push @{$self->{optgroups}}, $optgroup; }