Merged the C++ port of the GUI Tabs / OptionGroup / Option classes
by @YuSanka, thanks @lordofhyphens for the initial port of the OptionGroup / Option.
This commit is contained in:
commit
f1840a52db
@ -43,6 +43,7 @@ use FindBin;
|
||||
# Let the XS module know where the GUI resources reside.
|
||||
set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources'));
|
||||
set_var_dir(resources_dir() . "/icons");
|
||||
set_local_dir(resources_dir() . "/localization/");
|
||||
|
||||
use Moo 1.003001;
|
||||
|
||||
|
@ -68,6 +68,10 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$medium_font->SetPointSize(12);
|
||||
our $grey = Wx::Colour->new(200,200,200);
|
||||
|
||||
# Events to be sent from a C++ menu implementation:
|
||||
# 1) To inform about a change of the application language.
|
||||
our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType;
|
||||
|
||||
sub OnInit {
|
||||
my ($self) = @_;
|
||||
|
||||
@ -81,6 +85,7 @@ sub OnInit {
|
||||
# Mac: "~/Library/Application Support/Slic3r"
|
||||
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
||||
Slic3r::GUI::set_wxapp($self);
|
||||
Slic3r::GUI::load_language();
|
||||
|
||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||
$self->{app_config} = Slic3r::GUI::AppConfig->new;
|
||||
@ -115,10 +120,12 @@ sub OnInit {
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
no_controller => $self->{app_config}->get('no_controller'),
|
||||
no_plater => $no_plater,
|
||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||
);
|
||||
$self->SetTopWindow($frame);
|
||||
|
||||
EVT_IDLE($frame, sub {
|
||||
#EVT_IDLE($frame, sub {
|
||||
EVT_IDLE($self->{mainframe}, sub {
|
||||
while (my $cb = shift @cb) {
|
||||
$cb->();
|
||||
}
|
||||
@ -134,9 +141,41 @@ sub OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
# The following event is emited by the C++ menu implementation of application language change.
|
||||
EVT_COMMAND($self, -1, $LANGUAGE_CHANGE_EVENT, sub{
|
||||
$self->recreate_GUI;
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub recreate_GUI{
|
||||
my ($self) = @_;
|
||||
my $topwindow = $self->GetTopWindow();
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
no_controller => $self->{app_config}->get('no_controller'),
|
||||
no_plater => $no_plater,
|
||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||
);
|
||||
|
||||
if($topwindow)
|
||||
{
|
||||
$self->SetTopWindow($frame);
|
||||
$topwindow->Destroy;
|
||||
}
|
||||
|
||||
my $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
|
||||
if ($run_wizard) {
|
||||
# On OSX the UI was not initialized correctly if the wizard was called
|
||||
# before the UI was up and running.
|
||||
$self->CallAfter(sub {
|
||||
# Run the config wizard, don't offer the "reset user profile" checkbox.
|
||||
$self->{mainframe}->config_wizard(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sub about {
|
||||
my ($self) = @_;
|
||||
my $about = Slic3r::GUI::AboutDialog->new(undef);
|
||||
|
@ -11,13 +11,23 @@ use List::Util qw(min first);
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
|
||||
:font :icon wxTheApp);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Frame';
|
||||
|
||||
our $qs_last_input_file;
|
||||
our $qs_last_output_file;
|
||||
our $last_config;
|
||||
|
||||
# Events to be sent from a C++ Tab implementation:
|
||||
# 1) To inform about a change of a configuration value.
|
||||
our $VALUE_CHANGE_EVENT = Wx::NewEventType;
|
||||
# 2) To inform about a preset selection change or a "modified" status change.
|
||||
our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 3) To inform about a click on Browse button
|
||||
our $BUTTON_BROWSE_EVENT = Wx::NewEventType;
|
||||
# 4) To inform about a click on Test button
|
||||
our $BUTTON_TEST_EVENT = Wx::NewEventType;
|
||||
|
||||
sub new {
|
||||
my ($class, %params) = @_;
|
||||
|
||||
@ -37,6 +47,7 @@ sub new {
|
||||
$self->{no_controller} = $params{no_controller};
|
||||
$self->{no_plater} = $params{no_plater};
|
||||
$self->{loaded} = 0;
|
||||
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||
|
||||
# initialize tabpanel and menubar
|
||||
$self->_init_tabpanel;
|
||||
@ -106,33 +117,41 @@ sub _init_tabpanel {
|
||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
||||
}
|
||||
}
|
||||
$self->{options_tabs} = {};
|
||||
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
my $tab;
|
||||
$tab = $self->{options_tabs}{$tab_name} = ("Slic3r::GUI::Tab::" . ucfirst $tab_name)->new(
|
||||
$panel,
|
||||
no_controller => $self->{no_controller});
|
||||
# Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value.
|
||||
$tab->on_value_change(sub {
|
||||
my ($opt_key, $value) = @_;
|
||||
my $config = $tab->{presets}->get_current_preset->config;
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_config_change($config); # propagate config change events to the plater
|
||||
$self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count';
|
||||
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
|
||||
# The following event is emited by the C++ Tab implementation on config value change.
|
||||
EVT_COMMAND($self, -1, $VALUE_CHANGE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $str = $event->GetString;
|
||||
my ($opt_key, $name) = ($str =~ /(.*) (.*)/);
|
||||
#print "VALUE_CHANGE_EVENT: ", $opt_key, "\n";
|
||||
my $tab = Slic3r::GUI::get_preset_tab($name);
|
||||
my $config = $tab->get_config;
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_config_change($config); # propagate config change events to the plater
|
||||
if ($opt_key eq 'extruders_count'){
|
||||
my $value = $event->GetInt();
|
||||
$self->{plater}->on_extruders_change($value);
|
||||
}
|
||||
# don't save while loading for the first time
|
||||
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};
|
||||
});
|
||||
# Install a callback for the tab to update the platter and print controller presets, when
|
||||
# a preset changes at Slic3r::GUI::Tab.
|
||||
$tab->on_presets_changed(sub {
|
||||
if ($self->{plater}) {
|
||||
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
|
||||
$self->{plater}->update_presets($tab_name, @_);
|
||||
}
|
||||
# don't save while loading for the first time
|
||||
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};
|
||||
});
|
||||
# The following event is emited by the C++ Tab implementation on preset selection,
|
||||
# or when the preset's "modified" status changes.
|
||||
EVT_COMMAND($self, -1, $PRESETS_CHANGED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $tab_name = $event->GetString;
|
||||
|
||||
my $tab = Slic3r::GUI::get_preset_tab($tab_name);
|
||||
if ($self->{plater}) {
|
||||
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
|
||||
my $presets = $tab->get_presets;
|
||||
if (defined $presets){
|
||||
my $reload_dependent_tabs = $tab->get_dependent_tabs;
|
||||
$self->{plater}->update_presets($tab_name, $reload_dependent_tabs, $presets);
|
||||
if ($tab_name eq 'printer') {
|
||||
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
|
||||
my ($presets, $reload_dependent_tabs) = @_;
|
||||
for my $tab_name_other (qw(print filament)) {
|
||||
# If the printer tells us that the print or filament preset has been switched or invalidated,
|
||||
# refresh the print or filament tab page. Otherwise just refresh the combo box.
|
||||
@ -141,23 +160,76 @@ sub _init_tabpanel {
|
||||
$self->{options_tabs}{$tab_name_other}->$update_action;
|
||||
}
|
||||
# Update the controller printers.
|
||||
$self->{controller}->update_presets(@_) if $self->{controller};
|
||||
$self->{controller}->update_presets($presets) if $self->{controller};
|
||||
}
|
||||
$self->{plater}->on_config_change($tab->{presets}->get_current_preset->config);
|
||||
$self->{plater}->on_config_change($tab->get_config);
|
||||
}
|
||||
});
|
||||
# Load the currently selected preset into the GUI, update the preset selection box.
|
||||
$tab->load_current_preset;
|
||||
$panel->AddPage($tab, $tab->title);
|
||||
}
|
||||
}
|
||||
});
|
||||
# The following event is emited by the C++ Tab implementation ,
|
||||
# when the Browse button was clicked
|
||||
EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $msg = $event->GetString;
|
||||
print "BUTTON_BROWSE_EVENT: ", $msg, "\n";
|
||||
|
||||
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
|
||||
# Slic3r::GUI::create_preset_tab("print");
|
||||
# look for devices
|
||||
my $entries;
|
||||
{
|
||||
my $res = Net::Bonjour->new('http');
|
||||
$res->discover;
|
||||
$entries = [ $res->entries ];
|
||||
}
|
||||
if (@{$entries}) {
|
||||
my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
|
||||
my $tab = Slic3r::GUI::get_preset_tab("printer");
|
||||
$tab->load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort)
|
||||
if $dlg->ShowModal == wxID_OK;
|
||||
} else {
|
||||
Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal;
|
||||
}
|
||||
});
|
||||
# The following event is emited by the C++ Tab implementation ,
|
||||
# when the Test button was clicked
|
||||
EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $msg = $event->GetString;
|
||||
print "BUTTON_TEST_EVENT: ", $msg, "\n";
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->timeout(10);
|
||||
|
||||
my $config = Slic3r::GUI::get_preset_tab("printer")->get_config;
|
||||
my $res = $ua->get(
|
||||
"http://" . $config->octoprint_host . "/api/version",
|
||||
'X-Api-Key' => $config->octoprint_apikey,
|
||||
);
|
||||
if ($res->is_success) {
|
||||
Slic3r::GUI::show_info($self, "Connection to OctoPrint works correctly.", "Success!");
|
||||
} else {
|
||||
Slic3r::GUI::show_error($self,
|
||||
"I wasn't able to connect to OctoPrint (" . $res->status_line . "). "
|
||||
. "Check hostname and OctoPrint version (at least 1.1.0 is required).");
|
||||
}
|
||||
});
|
||||
# A variable to inform C++ Tab implementation about disabling of Browse button
|
||||
$self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ;
|
||||
# A variable to inform C++ Tab implementation about user_agent
|
||||
$self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;
|
||||
Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, wxTheApp->{app_config},
|
||||
$self->{no_controller}, $self->{is_disabled_button_browse},
|
||||
$self->{is_user_agent},
|
||||
$VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT,
|
||||
$BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT);
|
||||
$self->{options_tabs} = {};
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
||||
}
|
||||
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_select_preset(sub {
|
||||
my ($group, $name) = @_;
|
||||
$self->{options_tabs}{$group}->select_preset($name);
|
||||
$self->{options_tabs}{$group}->select_preset($name);
|
||||
});
|
||||
# load initial config
|
||||
my $full_config = wxTheApp->{preset_bundle}->full_config;
|
||||
@ -344,9 +416,11 @@ sub _init_menubar {
|
||||
$menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu};
|
||||
$menubar->Append($windowMenu, "&Window");
|
||||
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
|
||||
# Add an optional debug menu
|
||||
# (Select application language from the list of installed languages)
|
||||
# In production code, the add_debug_menu() call should do nothing.
|
||||
Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event});
|
||||
$menubar->Append($helpMenu, "&Help");
|
||||
# Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
|
||||
Slic3r::GUI::add_debug_menu($menubar);
|
||||
$self->SetMenuBar($menubar);
|
||||
}
|
||||
}
|
||||
@ -664,7 +738,7 @@ sub check_unsaved_changes {
|
||||
|
||||
my @dirty = ();
|
||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
||||
push @dirty, $tab->title if $tab->{presets}->current_is_dirty;
|
||||
push @dirty, $tab->title if $tab->current_preset_is_dirty;
|
||||
}
|
||||
|
||||
if (@dirty) {
|
||||
|
@ -158,7 +158,9 @@ sub _build_field {
|
||||
|
||||
my $opt_id = $opt->opt_id;
|
||||
my $on_change = sub {
|
||||
#! This function will be called from Field.
|
||||
my ($opt_id, $value) = @_;
|
||||
#! Call OptionGroup._on_change(...)
|
||||
$self->_on_change($opt_id, $value)
|
||||
unless $self->_disabled;
|
||||
};
|
||||
@ -213,6 +215,8 @@ sub _build_field {
|
||||
}
|
||||
return undef if !$field;
|
||||
|
||||
#! setting up a function that will be triggered when the field changes
|
||||
#! think of it as $field->on_change = ($on_change)
|
||||
$field->on_change($on_change);
|
||||
$field->on_kill_focus($on_kill_focus);
|
||||
$self->_fields->{$opt_id} = $field;
|
||||
|
@ -93,6 +93,8 @@ sub export_gcode {
|
||||
$self->status_cb->(95, "Running post-processing scripts");
|
||||
$self->config->setenv;
|
||||
for my $script (@{$self->config->post_process}) {
|
||||
# Ignore empty post processing script lines.
|
||||
next if $script =~ /^\s*$/;
|
||||
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
|
||||
# -x doesn't return true on Windows except for .exe files
|
||||
if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) {
|
||||
|
BIN
resources/localization/cs_CZ/Slic3rPE.mo
Normal file
BIN
resources/localization/cs_CZ/Slic3rPE.mo
Normal file
Binary file not shown.
2714
resources/localization/cs_CZ/Slic3rPE_cs.po
Normal file
2714
resources/localization/cs_CZ/Slic3rPE_cs.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/localization/en_US/Slic3rPE.mo
Normal file
BIN
resources/localization/en_US/Slic3rPE.mo
Normal file
Binary file not shown.
3016
resources/localization/en_US/Slic3rPE_en.po
Normal file
3016
resources/localization/en_US/Slic3rPE_en.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/localization/uk/Slic3rPE.mo
Normal file
BIN
resources/localization/uk/Slic3rPE.mo
Normal file
Binary file not shown.
538
resources/localization/uk/Slic3rPE_uk.po
Normal file
538
resources/localization/uk/Slic3rPE_uk.po
Normal file
@ -0,0 +1,538 @@
|
||||
# This file is distributed under the same license as the Slic3rPE package.
|
||||
# Oleksandra Iushchenko <yusanka@gmail.com>, 2018.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-02-07 20:20+0100\n"
|
||||
"PO-Revision-Date: 2018-02-08 01:41+0100\n"
|
||||
"Last-Translator: Oleksandra Iushchenko <yusanka@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Language: uk\n"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:81
|
||||
msgid "Default"
|
||||
msgstr "За замовчуванням"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:109
|
||||
msgid "Shape"
|
||||
msgstr "Вигляд (Форма)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:116
|
||||
msgid "Rectangular"
|
||||
msgstr "Прямокутний"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:132
|
||||
msgid "Circular"
|
||||
msgstr "Круговий"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:141
|
||||
msgid "Custom"
|
||||
msgstr "Користувацький"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:145
|
||||
msgid "Load shape from STL..."
|
||||
msgstr "Завантажте форму з STL ..."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:190
|
||||
msgid "Settings"
|
||||
msgstr "Налаштування"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:368
|
||||
msgid "Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):"
|
||||
msgstr "Виберіть файл, щоб імпортувати форму подложки з (STL/OBJ/AMF/PRUSA):"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:385
|
||||
msgid "Error! "
|
||||
msgstr "Помилка! "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:394
|
||||
msgid "The selected file contains no geometry."
|
||||
msgstr "Обратний файл не містить геометрії."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:398
|
||||
msgid ""
|
||||
"The selected file contains several disjoint areas. This is not supported."
|
||||
msgstr "Обраний файл містить декілька непересічних областей. Не підтримується."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.hpp:45
|
||||
msgid "Bed Shape"
|
||||
msgstr "Форма полотна"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\GUI.cpp:318
|
||||
msgid "Error"
|
||||
msgstr "Помилка"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\GUI.cpp:323
|
||||
msgid "Notice"
|
||||
msgstr "Зауваження"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:51
|
||||
msgid "Save current "
|
||||
msgstr "Зберегти поточний "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:52
|
||||
msgid "Delete this preset"
|
||||
msgstr "Видалити це налаштування"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:324
|
||||
msgid "Layers and perimeters"
|
||||
msgstr "Шари та периметри"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:325
|
||||
msgid "Layer height"
|
||||
msgstr "Висота шару"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:329
|
||||
msgid "Vertical shells"
|
||||
msgstr "Вертикальні оболонки"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:340
|
||||
msgid "Horizontal shells"
|
||||
msgstr "Горизонтальні оболонки"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:341
|
||||
msgid "Solid layers"
|
||||
msgstr "Тверді шари"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:346
|
||||
msgid "Quality (slower slicing)"
|
||||
msgstr "Якість (повільне нарізання)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:353
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:367
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:460
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:463
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:839
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1122
|
||||
msgid "Advanced"
|
||||
msgstr "Розширений"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:357
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:358
|
||||
msgid "Infill"
|
||||
msgstr "Заповнення"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:363
|
||||
msgid "Reducing printing time"
|
||||
msgstr "Зниження часу друку"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:375
|
||||
msgid "Skirt and brim"
|
||||
msgstr "Плінтус та край"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:376
|
||||
msgid "Skirt"
|
||||
msgstr "Плінтус"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:382
|
||||
msgid "Brim"
|
||||
msgstr "Край"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:385
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:386
|
||||
msgid "Support material"
|
||||
msgstr "Опорний матеріал"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:391
|
||||
msgid "Raft"
|
||||
msgstr "Пліт"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:395
|
||||
msgid "Options for support material and raft"
|
||||
msgstr "Варіанти для опорного матеріалу та плоту"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:409
|
||||
msgid "Speed"
|
||||
msgstr "Швидкість"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:410
|
||||
msgid "Speed for print moves"
|
||||
msgstr "Швидкість друкарських рухів"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:422
|
||||
msgid "Speed for non-print moves"
|
||||
msgstr "Швидкість недрукарських рухів"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:425
|
||||
msgid "Modifiers"
|
||||
msgstr "Модифікатори"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:428
|
||||
msgid "Acceleration control (advanced)"
|
||||
msgstr "Контроль прискорення (розширений)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:435
|
||||
msgid "Autospeed (advanced)"
|
||||
msgstr "Автоматична швидкість (розширена)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:441
|
||||
msgid "Multiple Extruders"
|
||||
msgstr "Кілька екструдерів"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:442
|
||||
msgid "Extruders"
|
||||
msgstr "Екструдери"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:449
|
||||
msgid "Ooze prevention"
|
||||
msgstr "Профілактика ?Ooze?"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:453
|
||||
msgid "Wipe tower"
|
||||
msgstr "Вежа очищення"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:464
|
||||
msgid "Extrusion width"
|
||||
msgstr "Ширина екструзії"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:474
|
||||
msgid "Overlap"
|
||||
msgstr "Перекриття"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:477
|
||||
msgid "Flow"
|
||||
msgstr "Потік"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:480
|
||||
msgid "Other"
|
||||
msgstr "Інше"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:487
|
||||
msgid "Output options"
|
||||
msgstr "Параметри виводу"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:488
|
||||
msgid "Sequential printing"
|
||||
msgstr "Послідовне друкування"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:499
|
||||
msgid "Output file"
|
||||
msgstr "Вихідний файл"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:505
|
||||
msgid "Post-processing scripts"
|
||||
msgstr "Скрипти пост-обробки"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:511
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:512
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:867
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:868
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1165
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1166
|
||||
msgid "Notes"
|
||||
msgstr "Примітки"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:518
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:875
|
||||
msgid "Dependencies"
|
||||
msgstr "Залежності"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:519
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:876
|
||||
msgid "Profile dependencies"
|
||||
msgstr "Залежності профілю"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:794
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:795
|
||||
msgid "Filament"
|
||||
msgstr "Філаметн"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:802
|
||||
msgid "Temperature"
|
||||
msgstr "Температура"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:803
|
||||
msgid "Extruder"
|
||||
msgstr "Екструдер"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:808
|
||||
msgid "Bed"
|
||||
msgstr "Полотно"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:813
|
||||
msgid "Cooling"
|
||||
msgstr "Охолодження"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:814
|
||||
msgid "Enable"
|
||||
msgstr "Увімкнути"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:825
|
||||
msgid "Fan settings"
|
||||
msgstr "Налаштування вентилятора"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:834
|
||||
msgid "Cooling thresholds"
|
||||
msgstr "Пороги охолодження"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:840
|
||||
msgid "Filament properties"
|
||||
msgstr "Властивості філаменту"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:844
|
||||
msgid "Print speed override"
|
||||
msgstr "Перевизначення швидкості друку"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:854
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1128
|
||||
msgid "Custom G-code"
|
||||
msgstr "Користувацький G-код"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:855
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1129
|
||||
msgid "Start G-code"
|
||||
msgstr "Початок G-коду"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:861
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1135
|
||||
msgid "End G-code"
|
||||
msgstr "Закінчення G-коду"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:945
|
||||
msgid "General"
|
||||
msgstr "Загальне"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:946
|
||||
msgid "Size and coordinates"
|
||||
msgstr "Розмір і координати"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:948
|
||||
msgid "Bed shape"
|
||||
msgstr "Форма полотна"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:950
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1649
|
||||
msgid "Set"
|
||||
msgstr "Встановити"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:971
|
||||
msgid "Capabilities"
|
||||
msgstr "Можливості"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:998
|
||||
msgid "USB/Serial connection"
|
||||
msgstr "USB/послідовне з'єднання"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1004
|
||||
msgid "Rescan serial ports"
|
||||
msgstr "Сканувати ще раз послідовні порти"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1013
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1081
|
||||
msgid "Test"
|
||||
msgstr "Перевірити"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1026
|
||||
msgid "Connection to printer works correctly."
|
||||
msgstr "Підключення до принтера працює коректно."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1026
|
||||
msgid "Success!"
|
||||
msgstr "Успіх!"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1029
|
||||
msgid "Connection failed."
|
||||
msgstr "Підключення не вдалося."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1041
|
||||
msgid "OctoPrint upload"
|
||||
msgstr "Завантаження OctoPrint"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1044
|
||||
msgid "Browse"
|
||||
msgstr "Переглянути"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1119
|
||||
msgid "Firmware"
|
||||
msgstr "Прошивка"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1141
|
||||
msgid "Before layer change G-code"
|
||||
msgstr "G-код перед зміною шару "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1147
|
||||
msgid "After layer change G-code"
|
||||
msgstr "G-код після зміни шару"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1153
|
||||
msgid "Tool change G-code"
|
||||
msgstr "G-код зміни інструменту "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1159
|
||||
msgid "Between objects G-code (for sequential printing)"
|
||||
msgstr "G-код між об'єктами (для послідовного друку)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1195
|
||||
msgid "Extruder "
|
||||
msgstr "Екструдер "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1198
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:120
|
||||
msgid "Size"
|
||||
msgstr "Розмір"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1201
|
||||
msgid "Layer height limits"
|
||||
msgstr "Межі висоти шару"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1206
|
||||
msgid "Position (for multi-extruder printers)"
|
||||
msgstr "Позиція (для мульти-екструдерних принтерів)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1209
|
||||
msgid "Retraction"
|
||||
msgstr "Утягування/відкликання"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1212
|
||||
msgid "Only lift Z"
|
||||
msgstr "Межі підняття Z"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1225
|
||||
msgid ""
|
||||
"Retraction when tool is disabled (advanced settings for multi-extruder "
|
||||
"setups)"
|
||||
msgstr ""
|
||||
"Утягування/відкликання при відключенні інструмента (додаткові налаштування "
|
||||
"для налагодження мульти-екструдерів)"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1229
|
||||
msgid "Preview"
|
||||
msgstr "Попередній перегляд"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1320
|
||||
msgid ""
|
||||
"The Wipe option is not available when using the Firmware Retraction mode.\n"
|
||||
"\n"
|
||||
"Shall I disable it in order to enable Firmware Retraction?"
|
||||
msgstr ""
|
||||
"Параметр «Очистити» недоступний при використанні режиму програмного "
|
||||
"утягування/відкликання.\n"
|
||||
"\n"
|
||||
"Відключити його для увімкнення програмного утягування/відкликання?"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1322
|
||||
msgid "Firmware Retraction"
|
||||
msgstr "Програмне утягування/відкликання"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1565
|
||||
msgid "The supplied name is empty. It can't be saved."
|
||||
msgstr "Надане ім'я порожнє. Не вдається зберегти."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1576
|
||||
msgid "Something is wrong. It can't be saved."
|
||||
msgstr "Щось не так. Не вдається зберегти."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1593
|
||||
msgid "remove"
|
||||
msgstr "перемістити"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1593
|
||||
msgid "delete"
|
||||
msgstr "видалити"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1594
|
||||
msgid "Are you sure you want to "
|
||||
msgstr "Ви впевнені, що хочете "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1594
|
||||
msgid " the selected preset?"
|
||||
msgstr "вибране налаштування?"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1595
|
||||
msgid "Remove"
|
||||
msgstr "Перемістити"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1595
|
||||
msgid "Delete"
|
||||
msgstr "Видалити"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1596
|
||||
msgid " Preset"
|
||||
msgstr " Налаштування"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1648
|
||||
msgid "All"
|
||||
msgstr "Всі"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1679
|
||||
msgid "Select the printers this profile is compatible with."
|
||||
msgstr "Оберіть принтери, сумісні з цим профілем."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1680
|
||||
msgid "Compatible printers"
|
||||
msgstr "Сумісні принтери"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1763
|
||||
msgid "Save "
|
||||
msgstr "Зберегти "
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1763
|
||||
msgid " as:"
|
||||
msgstr " як:"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1797
|
||||
msgid ""
|
||||
"The supplied name is not valid; the following characters are not allowed:"
|
||||
msgstr "Надане ім'я недійсне; такі символи не допускаються:"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1800
|
||||
msgid "The supplied name is not available."
|
||||
msgstr "Надане ім'я недійсне."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.hpp:178
|
||||
msgid "Print Settings"
|
||||
msgstr "Налаштування друку"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.hpp:198
|
||||
msgid "Filament Settings"
|
||||
msgstr "Налаштування філаменту"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.hpp:224
|
||||
msgid "Printer Settings"
|
||||
msgstr "Налаштування принтеру"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.hpp:244
|
||||
msgid "Save preset"
|
||||
msgstr "Зберегти налаштування"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\Field.cpp:35
|
||||
msgid "default"
|
||||
msgstr "за замовчуванням"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:121
|
||||
msgid "Size in X and Y of the rectangular plate."
|
||||
msgstr "Розмір прямокутної подложки за X та Y."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:127
|
||||
msgid "Origin"
|
||||
msgstr "Початок координат"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:128
|
||||
msgid ""
|
||||
"Distance of the 0,0 G-code coordinate from the front left corner of the "
|
||||
"rectangle."
|
||||
msgstr "Відстань координат 0,0 G-коду від нижнього лівого кута прямокутника."
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:135
|
||||
msgid "mm"
|
||||
msgstr "мм"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:136
|
||||
msgid "Diameter"
|
||||
msgstr "Діаметр"
|
||||
|
||||
#: c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp:137
|
||||
msgid ""
|
||||
"Diameter of the print bed. It is assumed that origin (0,0) is located in the "
|
||||
"center."
|
||||
msgstr ""
|
||||
"Діаметр подложки. Передбачається, що початок координат (0,0) знаходиться в "
|
||||
"центрі."
|
@ -181,6 +181,18 @@ add_library(libslic3r_gui STATIC
|
||||
${LIBDIR}/slic3r/GUI/PresetHints.hpp
|
||||
${LIBDIR}/slic3r/GUI/GUI.cpp
|
||||
${LIBDIR}/slic3r/GUI/GUI.hpp
|
||||
${LIBDIR}/slic3r/GUI/Tab.cpp
|
||||
${LIBDIR}/slic3r/GUI/Tab.hpp
|
||||
${LIBDIR}/slic3r/GUI/TabIface.cpp
|
||||
${LIBDIR}/slic3r/GUI/TabIface.hpp
|
||||
${LIBDIR}/slic3r/GUI/Field.cpp
|
||||
${LIBDIR}/slic3r/GUI/Field.hpp
|
||||
${LIBDIR}/slic3r/GUI/OptionsGroup.cpp
|
||||
${LIBDIR}/slic3r/GUI/OptionsGroup.hpp
|
||||
${LIBDIR}/slic3r/GUI/BedShapeDialog.cpp
|
||||
${LIBDIR}/slic3r/GUI/BedShapeDialog.hpp
|
||||
${LIBDIR}/slic3r/GUI/2DBed.cpp
|
||||
${LIBDIR}/slic3r/GUI/2DBed.hpp
|
||||
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
|
||||
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
||||
)
|
||||
@ -307,6 +319,7 @@ set(XS_XSP_FILES
|
||||
${XSP_DIR}/GUI_AppConfig.xsp
|
||||
${XSP_DIR}/GUI_3DScene.xsp
|
||||
${XSP_DIR}/GUI_Preset.xsp
|
||||
${XSP_DIR}/GUI_Tab.xsp
|
||||
${XSP_DIR}/Layer.xsp
|
||||
${XSP_DIR}/Line.xsp
|
||||
${XSP_DIR}/Model.xsp
|
||||
|
@ -283,6 +283,7 @@ for my $class (qw(
|
||||
Slic3r::GUI::_3DScene::GLVolume
|
||||
Slic3r::GUI::Preset
|
||||
Slic3r::GUI::PresetCollection
|
||||
Slic3r::GUI::Tab2
|
||||
Slic3r::Layer
|
||||
Slic3r::Layer::Region
|
||||
Slic3r::Layer::Support
|
||||
|
@ -24,6 +24,12 @@ public:
|
||||
explicit Polygon(const Points &points): MultiPoint(points) {}
|
||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||
Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {}
|
||||
static Polygon new_scale(std::vector<Pointf> points) {
|
||||
Points int_points;
|
||||
for (auto pt : points)
|
||||
int_points.push_back(Point::new_scale(pt.x, pt.y));
|
||||
return Polygon(int_points);
|
||||
}
|
||||
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
|
||||
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
|
||||
|
||||
|
@ -21,6 +21,14 @@ public:
|
||||
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
|
||||
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
|
||||
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
|
||||
static Polyline new_scale(std::vector<Pointf> points) {
|
||||
Polyline pl;
|
||||
Points int_points;
|
||||
for (auto pt : points)
|
||||
int_points.push_back(Point::new_scale(pt.x, pt.y));
|
||||
pl.append(int_points);
|
||||
return pl;
|
||||
}
|
||||
|
||||
void append(const Point &point) { this->points.push_back(point); }
|
||||
void append(const Points &src) { this->append(src.begin(), src.end()); }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,11 @@ void set_resources_dir(const std::string &path);
|
||||
// Return a full path to the resources directory.
|
||||
const std::string& resources_dir();
|
||||
|
||||
// Set a path with GUI localization files.
|
||||
void set_local_dir(const std::string &path);
|
||||
// Return a full path to the localization directory.
|
||||
const std::string& localization_dir();
|
||||
|
||||
// Set a path with preset files.
|
||||
void set_data_dir(const std::string &path);
|
||||
// Return a full path to the GUI resource files.
|
||||
|
@ -103,6 +103,18 @@ const std::string& resources_dir()
|
||||
return g_resources_dir;
|
||||
}
|
||||
|
||||
static std::string g_local_dir;
|
||||
|
||||
void set_local_dir(const std::string &dir)
|
||||
{
|
||||
g_local_dir = dir;
|
||||
}
|
||||
|
||||
const std::string& localization_dir()
|
||||
{
|
||||
return g_local_dir;
|
||||
}
|
||||
|
||||
static std::string g_data_dir;
|
||||
|
||||
void set_data_dir(const std::string &dir)
|
||||
|
@ -63,6 +63,7 @@ REGISTER_CLASS(Preset, "GUI::Preset");
|
||||
REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
|
||||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
||||
REGISTER_CLASS(TabIface, "GUI::Tab2");
|
||||
|
||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
||||
{
|
||||
|
189
xs/src/slic3r/GUI/2DBed.cpp
Normal file
189
xs/src/slic3r/GUI/2DBed.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
#include "2DBed.hpp";
|
||||
|
||||
#include <wx/dcbuffer.h>
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void Bed_2D::repaint()
|
||||
{
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
auto cw = GetSize().GetWidth();
|
||||
auto ch = GetSize().GetHeight();
|
||||
// when canvas is not rendered yet, size is 0, 0
|
||||
if (cw == 0) return ;
|
||||
|
||||
if (m_user_drawn_background) {
|
||||
// On all systems the AutoBufferedPaintDC() achieves double buffering.
|
||||
// On MacOS the background is erased, on Windows the background is not erased
|
||||
// and on Linux / GTK the background is erased to gray color.
|
||||
// Fill DC with the background on Windows & Linux / GTK.
|
||||
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour
|
||||
dc.SetPen(*new wxPen(color, 1, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(*new wxBrush(color, wxBRUSHSTYLE_SOLID));
|
||||
auto rect = GetUpdateRegion().GetBox();
|
||||
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
|
||||
// turn cw and ch from sizes to max coordinates
|
||||
cw--;
|
||||
ch--;
|
||||
|
||||
auto cbb = BoundingBoxf(Pointf(0, 0),Pointf(cw, ch));
|
||||
// leave space for origin point
|
||||
cbb.min.translate(4, 0);
|
||||
cbb.max.translate(-4, -4);
|
||||
|
||||
// leave space for origin label
|
||||
cbb.max.translate(0, -13);
|
||||
|
||||
// read new size
|
||||
cw = cbb.size().x;
|
||||
ch = cbb.size().y;
|
||||
|
||||
auto ccenter = cbb.center();
|
||||
|
||||
// get bounding box of bed shape in G - code coordinates
|
||||
auto bed_shape = m_bed_shape;
|
||||
auto bed_polygon = Polygon::new_scale(m_bed_shape);
|
||||
auto bb = BoundingBoxf(m_bed_shape);
|
||||
bb.merge(Pointf(0, 0)); // origin needs to be in the visible area
|
||||
auto bw = bb.size().x;
|
||||
auto bh = bb.size().y;
|
||||
auto bcenter = bb.center();
|
||||
|
||||
// calculate the scaling factor for fitting bed shape in canvas area
|
||||
auto sfactor = std::min(cw/bw, ch/bh);
|
||||
auto shift = Pointf(
|
||||
ccenter.x - bcenter.x * sfactor,
|
||||
ccenter.y - bcenter.y * sfactor
|
||||
);
|
||||
m_scale_factor = sfactor;
|
||||
m_shift = Pointf(shift.x + cbb.min.x,
|
||||
shift.y - (cbb.max.y - GetSize().GetHeight()));
|
||||
|
||||
// draw bed fill
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(255, 255, 255), wxSOLID));
|
||||
wxPointList pt_list;
|
||||
for (auto pt: m_bed_shape)
|
||||
{
|
||||
Point pt_pix = to_pixels(pt);
|
||||
pt_list.push_back(new wxPoint(pt_pix.x, pt_pix.y));
|
||||
}
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
// draw grid
|
||||
auto step = 10; // 1cm grid
|
||||
Polylines polylines;
|
||||
for (auto x = bb.min.x - fmod(bb.min.x, step) + step; x < bb.max.x; x += step) {
|
||||
Polyline pl = Polyline::new_scale({ Pointf(x, bb.min.y), Pointf(x, bb.max.y) });
|
||||
polylines.push_back(pl);
|
||||
}
|
||||
for (auto y = bb.min.y - fmod(bb.min.y, step) + step; y < bb.max.y; y += step) {
|
||||
polylines.push_back(Polyline::new_scale({ Pointf(bb.min.x, y), Pointf(bb.max.x, y) }));
|
||||
}
|
||||
polylines = intersection_pl(polylines, bed_polygon);
|
||||
|
||||
dc.SetPen(*new wxPen(*new wxColour(230, 230, 230), 1, wxSOLID));
|
||||
for (auto pl : polylines)
|
||||
{
|
||||
for (size_t i = 0; i < pl.points.size()-1; i++){
|
||||
Point pt1 = to_pixels(Pointf::new_unscale(pl.points[i]));
|
||||
Point pt2 = to_pixels(Pointf::new_unscale(pl.points[i+1]));
|
||||
dc.DrawLine(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||
}
|
||||
}
|
||||
|
||||
// draw bed contour
|
||||
dc.SetPen(*new wxPen(*new wxColour(0, 0, 0), 1, wxSOLID));
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(0, 0, 0), wxTRANSPARENT));
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
auto origin_px = to_pixels(Pointf(0, 0));
|
||||
|
||||
// draw axes
|
||||
auto axes_len = 50;
|
||||
auto arrow_len = 6;
|
||||
auto arrow_angle = Geometry::deg2rad(45.0);
|
||||
dc.SetPen(*new wxPen(*new wxColour(255, 0, 0), 2, wxSOLID)); // red
|
||||
auto x_end = Pointf(origin_px.x + axes_len, origin_px.y);
|
||||
dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(x_end.x, x_end.y));
|
||||
for (auto angle : { -arrow_angle, arrow_angle }){
|
||||
auto end = x_end;
|
||||
end.translate(-arrow_len, 0);
|
||||
end.rotate(angle, x_end);
|
||||
dc.DrawLine(wxPoint(x_end.x, x_end.y), wxPoint(end.x, end.y));
|
||||
}
|
||||
|
||||
dc.SetPen(*new wxPen(*new wxColour(0, 255, 0), 2, wxSOLID)); // green
|
||||
auto y_end = Pointf(origin_px.x, origin_px.y - axes_len);
|
||||
dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(y_end.x, y_end.y));
|
||||
for (auto angle : { -arrow_angle, arrow_angle }) {
|
||||
auto end = y_end;
|
||||
end.translate(0, +arrow_len);
|
||||
end.rotate(angle, y_end);
|
||||
dc.DrawLine(wxPoint(y_end.x, y_end.y), wxPoint(end.x, end.y));
|
||||
}
|
||||
|
||||
// draw origin
|
||||
dc.SetPen(*new wxPen(*new wxColour(0, 0, 0), 1, wxSOLID));
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(0, 0, 0), wxSOLID));
|
||||
dc.DrawCircle(origin_px.x, origin_px.y, 3);
|
||||
|
||||
dc.SetTextForeground(*new wxColour(0, 0, 0));
|
||||
dc.SetFont(*new wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||
dc.DrawText("(0,0)", origin_px.x + 1, origin_px.y + 2);
|
||||
|
||||
// draw current position
|
||||
if (m_pos!= Pointf(0, 0)) {
|
||||
auto pos_px = to_pixels(m_pos);
|
||||
dc.SetPen(*new wxPen(*new wxColour(200, 0, 0), 2, wxSOLID));
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(200, 0, 0), wxTRANSPARENT));
|
||||
dc.DrawCircle(pos_px.x, pos_px.y, 5);
|
||||
|
||||
dc.DrawLine(pos_px.x - 15, pos_px.y, pos_px.x + 15, pos_px.y);
|
||||
dc.DrawLine(pos_px.x, pos_px.y - 15, pos_px.x, pos_px.y + 15);
|
||||
}
|
||||
|
||||
m_painted = true;
|
||||
}
|
||||
|
||||
// convert G - code coordinates into pixels
|
||||
Point Bed_2D::to_pixels(Pointf point){
|
||||
auto p = Pointf(point);
|
||||
p.scale(m_scale_factor);
|
||||
p.translate(m_shift);
|
||||
return Point(p.x, GetSize().GetHeight() - p.y);
|
||||
}
|
||||
|
||||
void Bed_2D::mouse_event(wxMouseEvent event){
|
||||
if (!m_interactive) return;
|
||||
if (!m_painted) return;
|
||||
|
||||
auto pos = event.GetPosition();
|
||||
auto point = to_units(Point(pos.x, pos.y));
|
||||
if (event.LeftDown() || event.Dragging()) {
|
||||
if (m_on_move)
|
||||
m_on_move(point) ;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// convert pixels into G - code coordinates
|
||||
Pointf Bed_2D::to_units(Point point){
|
||||
auto p = Pointf(point.x, GetSize().GetHeight() - point.y);
|
||||
p.translate(m_shift.negative());
|
||||
p.scale(1 / m_scale_factor);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Bed_2D::set_pos(Pointf pos){
|
||||
m_pos = pos;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
45
xs/src/slic3r/GUI/2DBed.hpp
Normal file
45
xs/src/slic3r/GUI/2DBed.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <wx/wx.h>
|
||||
#include "Config.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class Bed_2D : public wxPanel
|
||||
{
|
||||
bool m_user_drawn_background = false;
|
||||
|
||||
bool m_painted = false;
|
||||
bool m_interactive = false;
|
||||
double m_scale_factor;
|
||||
Pointf m_shift;
|
||||
Pointf m_pos;
|
||||
std::function<void(Pointf)> m_on_move = nullptr;
|
||||
|
||||
Point to_pixels(Pointf point);
|
||||
Pointf to_units(Point point);
|
||||
void repaint();
|
||||
void mouse_event(wxMouseEvent event);
|
||||
void set_pos(Pointf pos);
|
||||
|
||||
public:
|
||||
Bed_2D(wxWindow* parent)
|
||||
{
|
||||
Create(parent, wxID_ANY, wxDefaultPosition, wxSize(250, -1), wxTAB_TRAVERSAL);
|
||||
// m_user_drawn_background = $^O ne 'darwin';
|
||||
m_user_drawn_background = true;
|
||||
Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); }));
|
||||
// EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background};
|
||||
// Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent event){/*mouse_event()*/; }));
|
||||
Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent event){ mouse_event(event); }));
|
||||
Bind(wxEVT_MOTION, ([this](wxMouseEvent event){ mouse_event(event); }));
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent e) { Refresh(); }));
|
||||
}
|
||||
~Bed_2D(){}
|
||||
|
||||
std::vector<Pointf> m_bed_shape;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
341
xs/src/slic3r/GUI/BedShapeDialog.cpp
Normal file
341
xs/src/slic3r/GUI/BedShapeDialog.cpp
Normal file
@ -0,0 +1,341 @@
|
||||
#include "BedShapeDialog.hpp"
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/wx.h>
|
||||
#include "Polygon.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include <wx/numformatter.h>
|
||||
#include "Model.hpp"
|
||||
#include "boost/nowide/iostream.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
|
||||
{
|
||||
m_panel = new BedShapePanel(this);
|
||||
m_panel->build_panel(default_pt);
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_panel, 1, wxEXPAND);
|
||||
main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
SetMinSize(GetSize());
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
||||
// needed to actually free memory
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e){
|
||||
EndModal(wxID_OK);
|
||||
Destroy();
|
||||
}));
|
||||
}
|
||||
|
||||
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
||||
{
|
||||
// on_change(nullptr);
|
||||
|
||||
auto box = new wxStaticBox(this, wxID_ANY, _L("Shape"));
|
||||
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
// shape options
|
||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(300, -1), wxCHB_TOP);
|
||||
sbsizer->Add(m_shape_options_book);
|
||||
|
||||
auto optgroup = init_shape_options_page(_L("Rectangular"));
|
||||
ConfigOptionDef def;
|
||||
def.type = coPoints;
|
||||
def.default_value = new ConfigOptionPoints{ Pointf(200, 200) };
|
||||
def.label = _LU8("Size");
|
||||
def.tooltip = _LU8("Size in X and Y of the rectangular plate.");
|
||||
Option option(def, "rect_size");
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
def.type = coPoints;
|
||||
def.default_value = new ConfigOptionPoints{ Pointf(0, 0) };
|
||||
def.label = _LU8("Origin");
|
||||
def.tooltip = _LU8("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
option = Option(def, "rect_origin");
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = init_shape_options_page(_L("Circular"));
|
||||
def.type = coFloat;
|
||||
def.default_value = new ConfigOptionFloat(200);
|
||||
def.sidetext = _LU8("mm");
|
||||
def.label = _LU8("Diameter");
|
||||
def.tooltip = _LU8("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
option = Option(def, "diameter");
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = init_shape_options_page(_L("Custom"));
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
auto btn = new wxButton(parent, wxID_ANY, _L("Load shape from STL..."), wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
load_stl();
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
|
||||
{
|
||||
update_shape();
|
||||
}));
|
||||
|
||||
// right pane with preview canvas
|
||||
m_canvas = new Bed_2D(this);
|
||||
m_canvas->m_bed_shape = default_pt->values;
|
||||
|
||||
// main sizer
|
||||
auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
top_sizer->Add(sbsizer, 0, wxEXPAND | wxLeft | wxTOP | wxBOTTOM, 10);
|
||||
if (m_canvas)
|
||||
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ;
|
||||
|
||||
SetSizerAndFit(top_sizer);
|
||||
|
||||
set_shape(default_pt);
|
||||
update_preview();
|
||||
}
|
||||
|
||||
#define SHAPE_RECTANGULAR 0
|
||||
#define SHAPE_CIRCULAR 1
|
||||
#define SHAPE_CUSTOM 2
|
||||
|
||||
// Called from the constructor.
|
||||
// Create a panel for a rectangular / circular / custom bed shape.
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title){
|
||||
|
||||
auto panel = new wxPanel(m_shape_options_book);
|
||||
ConfigOptionsGroupShp optgroup;
|
||||
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Settings"));
|
||||
|
||||
optgroup->label_width = 100;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||
update_shape();
|
||||
};
|
||||
|
||||
m_optgroups.push_back(optgroup);
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
m_shape_options_book->AddPage(panel, title);
|
||||
|
||||
return optgroup;
|
||||
}
|
||||
|
||||
// Called from the constructor.
|
||||
// Set the initial bed shape from a list of points.
|
||||
// Deduce the bed shape type(rect, circle, custom)
|
||||
// This routine shall be smart enough if the user messes up
|
||||
// with the list of points in the ini file directly.
|
||||
void BedShapePanel::set_shape(ConfigOptionPoints* points)
|
||||
{
|
||||
auto polygon = Polygon::new_scale(points->values);
|
||||
|
||||
// is this a rectangle ?
|
||||
if (points->size() == 4) {
|
||||
auto lines = polygon.lines();
|
||||
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
|
||||
// okay, it's a rectangle
|
||||
// find origin
|
||||
// the || 0 hack prevents "-0" which might confuse the user
|
||||
int x_min, x_max, y_min, y_max;
|
||||
x_max = x_min = points->values[0].x;
|
||||
y_max = y_min = points->values[0].y;
|
||||
for (auto pt : points->values){
|
||||
if (x_min > pt.x) x_min = pt.x;
|
||||
if (x_max < pt.x) x_max = pt.x;
|
||||
if (y_min > pt.y) y_min = pt.y;
|
||||
if (y_max < pt.y) y_max = pt.y;
|
||||
}
|
||||
if (x_min < 0) x_min = 0;
|
||||
if (x_max < 0) x_max = 0;
|
||||
if (y_min < 0) y_min = 0;
|
||||
if (y_max < 0) y_max = 0;
|
||||
auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) };
|
||||
|
||||
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
|
||||
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
|
||||
optgroup->set_value("rect_size", new ConfigOptionPoints{ Pointf(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]);
|
||||
optgroup->set_value("rect_origin", origin);
|
||||
update_shape();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// is this a circle ?
|
||||
{
|
||||
// Analyze the array of points.Do they reside on a circle ?
|
||||
auto center = polygon.bounding_box().center();
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt: polygon.points)
|
||||
{
|
||||
double distance = center.distance_to(pt);
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
bool defined_value = true;
|
||||
for (auto el: vertex_distances)
|
||||
{
|
||||
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
|
||||
defined_value = false;
|
||||
break;
|
||||
}
|
||||
if (defined_value) {
|
||||
// all vertices are equidistant to center
|
||||
m_shape_options_book->SetSelection(SHAPE_CIRCULAR);
|
||||
auto optgroup = m_optgroups[SHAPE_CIRCULAR];
|
||||
boost::any ret = wxNumberFormatter::ToString(unscale(avg_dist * 2), 0);
|
||||
optgroup->set_value("diameter", ret);
|
||||
update_shape();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (points->size() < 3) {
|
||||
// Invalid polygon.Revert to default bed dimensions.
|
||||
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
|
||||
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
|
||||
optgroup->set_value("rect_size", new ConfigOptionPoints{ Pointf(200, 200) });
|
||||
optgroup->set_value("rect_origin", new ConfigOptionPoints{ Pointf(0, 0) });
|
||||
update_shape();
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_shape_options_book->SetSelection(SHAPE_CUSTOM);
|
||||
// Copy the polygon to the canvas, make a copy of the array.
|
||||
m_canvas->m_bed_shape = points->values;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
void BedShapePanel::update_preview()
|
||||
{
|
||||
if (m_canvas) m_canvas->Refresh();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Update the bed shape from the dialog fields.
|
||||
void BedShapePanel::update_shape()
|
||||
{
|
||||
auto page_idx = m_shape_options_book->GetSelection();
|
||||
if (page_idx == SHAPE_RECTANGULAR) {
|
||||
Pointf rect_size, rect_origin;
|
||||
try{
|
||||
rect_size = boost::any_cast<Pointf>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); }
|
||||
catch (const std::exception &e){
|
||||
return;}
|
||||
try{
|
||||
rect_origin = boost::any_cast<Pointf>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin"));
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
return;}
|
||||
|
||||
auto x = rect_size.x;
|
||||
auto y = rect_size.y;
|
||||
// empty strings or '-' or other things
|
||||
if (x == 0 || y == 0) return;
|
||||
double x0 = 0.0;
|
||||
double y0 = 0.0;
|
||||
double x1 = x;
|
||||
double y1 = y;
|
||||
|
||||
auto dx = rect_origin.x;
|
||||
auto dy = rect_origin.y;
|
||||
|
||||
x0 -= dx;
|
||||
x1 -= dx;
|
||||
y0 -= dy;
|
||||
y1 -= dy;
|
||||
m_canvas->m_bed_shape = { Pointf(x0, y0),
|
||||
Pointf(x1, y0),
|
||||
Pointf(x1, y1),
|
||||
Pointf(x0, y1)};
|
||||
}
|
||||
else if(page_idx == SHAPE_CIRCULAR) {
|
||||
double diameter;
|
||||
try{
|
||||
diameter = boost::any_cast<double>(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter"));
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
return;
|
||||
}
|
||||
if (diameter == 0.0) return ;
|
||||
auto r = diameter / 2;
|
||||
auto twopi = 2 * PI;
|
||||
auto edges = 60;
|
||||
std::vector<Pointf> points;
|
||||
for (size_t i = 1; i <= 60; ++i){
|
||||
auto angle = i * twopi / edges;
|
||||
points.push_back(Pointf(r*cos(angle), r*sin(angle)));
|
||||
}
|
||||
m_canvas->m_bed_shape = points;
|
||||
}
|
||||
|
||||
// $self->{on_change}->();
|
||||
update_preview();
|
||||
}
|
||||
|
||||
// Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
void BedShapePanel::load_stl()
|
||||
{
|
||||
t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card();
|
||||
std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "prusa"};
|
||||
wxString MODEL_WILDCARD;
|
||||
for (auto file_type: file_types)
|
||||
MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|";
|
||||
|
||||
auto dialog = new wxFileDialog(this, _L("Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):"), "", "",
|
||||
MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dialog->ShowModal() != wxID_OK) {
|
||||
dialog->Destroy();
|
||||
return;
|
||||
}
|
||||
wxArrayString input_file;
|
||||
dialog->GetPaths(input_file);
|
||||
dialog->Destroy();
|
||||
|
||||
std::string file_name = input_file[0].ToStdString();
|
||||
|
||||
Model model;
|
||||
try {
|
||||
model = Model::read_from_file(file_name);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
auto msg = _L("Error! ") + file_name + " : " + e.what() + ".";
|
||||
show_error(this, msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto mesh = model.mesh();
|
||||
auto expolygons = mesh.horizontal_projection();
|
||||
|
||||
if (expolygons.size() == 0) {
|
||||
show_error(this, _L("The selected file contains no geometry."));
|
||||
return;
|
||||
}
|
||||
if (expolygons.size() > 1) {
|
||||
show_error(this, _L("The selected file contains several disjoint areas. This is not supported."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto polygon = expolygons[0].contour;
|
||||
std::vector<Pointf> points;
|
||||
for (auto pt : polygon.points)
|
||||
points.push_back(Pointf::new_unscale(pt));
|
||||
m_canvas->m_bed_shape = points;
|
||||
update_preview();
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
51
xs/src/slic3r/GUI/BedShapeDialog.hpp
Normal file
51
xs/src/slic3r/GUI/BedShapeDialog.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
// The bed shape dialog.
|
||||
// The dialog opens from Print Settins tab->Bed Shape : Set...
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "2DBed.hpp"
|
||||
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/choicebk.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
class BedShapePanel : public wxPanel
|
||||
{
|
||||
wxChoicebook* m_shape_options_book;
|
||||
Bed_2D* m_canvas;
|
||||
|
||||
std::vector <ConfigOptionsGroupShp> m_optgroups;
|
||||
|
||||
public:
|
||||
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY){}
|
||||
~BedShapePanel(){}
|
||||
|
||||
void build_panel(ConfigOptionPoints* default_pt);
|
||||
|
||||
ConfigOptionsGroupShp init_shape_options_page(wxString title);
|
||||
void set_shape(ConfigOptionPoints* points);
|
||||
void update_preview();
|
||||
void update_shape();
|
||||
void load_stl();
|
||||
|
||||
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
|
||||
std::vector<Pointf> GetValue() { return m_canvas->m_bed_shape; }
|
||||
};
|
||||
|
||||
class BedShapeDialog : public wxDialog
|
||||
{
|
||||
BedShapePanel* m_panel;
|
||||
public:
|
||||
BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _L("Bed Shape"),
|
||||
wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER){}
|
||||
~BedShapeDialog(){ }
|
||||
|
||||
void build_dialog(ConfigOptionPoints* default_pt);
|
||||
std::vector<Pointf> GetValue() { return m_panel->GetValue(); }
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
15
xs/src/slic3r/GUI/ConfigExceptions.hpp
Normal file
15
xs/src/slic3r/GUI/ConfigExceptions.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include <exception>
|
||||
namespace Slic3r {
|
||||
|
||||
class ConfigError : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class ConfigGUITypeError : public ConfigError {
|
||||
using ConfigError::ConfigError;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
570
xs/src/slic3r/GUI/Field.cpp
Normal file
570
xs/src/slic3r/GUI/Field.cpp
Normal file
@ -0,0 +1,570 @@
|
||||
#include "GUI.hpp"//"slic3r_gui.hpp"
|
||||
#include "Field.hpp"
|
||||
|
||||
//#include <wx/event.h>
|
||||
#include <regex>
|
||||
#include <wx/numformatter.h>
|
||||
#include <wx/tooltip.h>
|
||||
#include "PrintConfig.hpp"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
void Field::on_kill_focus(wxEvent& event) {
|
||||
// Without this, there will be nasty focus bugs on Windows.
|
||||
// Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all
|
||||
// non-command events to allow the default handling to take place."
|
||||
event.Skip();
|
||||
std::cerr << "calling Field::on_kill_focus from " << m_opt_id<< "\n";
|
||||
// call the registered function if it is available
|
||||
if (m_on_kill_focus!=nullptr)
|
||||
m_on_kill_focus();
|
||||
}
|
||||
void Field::on_change_field()
|
||||
{
|
||||
// std::cerr << "calling Field::_on_change \n";
|
||||
if (m_on_change != nullptr && !m_disable_change_event)
|
||||
m_on_change(m_opt_id, get_value());
|
||||
}
|
||||
|
||||
wxString Field::get_tooltip_text(const wxString& default_string)
|
||||
{
|
||||
wxString tooltip_text("");
|
||||
wxString tooltip = wxString::FromUTF8(m_opt.tooltip.c_str());
|
||||
if (tooltip.length() > 0)
|
||||
tooltip_text = tooltip + "(" + _L("default") + ": " +
|
||||
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") +
|
||||
default_string + ")";
|
||||
|
||||
return tooltip_text;
|
||||
}
|
||||
|
||||
bool Field::is_matched(std::string string, std::string pattern)
|
||||
{
|
||||
std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl
|
||||
return std::regex_match(string, regex_pattern);
|
||||
}
|
||||
|
||||
boost::any Field::get_value_by_opt_type(wxString str, ConfigOptionType type)
|
||||
{
|
||||
boost::any ret_val;
|
||||
switch (m_opt.type){
|
||||
case coInt:
|
||||
ret_val = wxAtoi(str);
|
||||
break;
|
||||
case coPercent:
|
||||
case coPercents:
|
||||
case coFloats:
|
||||
case coFloat:{
|
||||
if (m_opt.type == coPercent) str.RemoveLast();
|
||||
double val;
|
||||
str.ToCDouble(&val);
|
||||
ret_val = val;
|
||||
break; }
|
||||
case coString:
|
||||
case coStrings:
|
||||
ret_val = str.ToStdString();
|
||||
break;
|
||||
case coFloatOrPercent:{
|
||||
if (str.Last() == '%')
|
||||
str.RemoveLast();
|
||||
double val;
|
||||
str.ToCDouble(&val);
|
||||
ret_val = val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void TextCtrl::BUILD() {
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
wxString text_value = wxString("");
|
||||
|
||||
switch (m_opt.type) {
|
||||
case coFloatOrPercent:
|
||||
{
|
||||
if (static_cast<const ConfigOptionFloatOrPercent*>(m_opt.default_value)->percent)
|
||||
{
|
||||
text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat()));
|
||||
text_value += "%";
|
||||
}
|
||||
else
|
||||
text_value = wxNumberFormatter::ToString(m_opt.default_value->getFloat(), 2);
|
||||
break;
|
||||
}
|
||||
case coPercent:
|
||||
{
|
||||
text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat()));
|
||||
text_value += "%";
|
||||
break;
|
||||
}
|
||||
case coPercents:
|
||||
{
|
||||
const ConfigOptionPercents *vec = static_cast<const ConfigOptionPercents*>(m_opt.default_value);
|
||||
if (vec == nullptr || vec->empty()) break;
|
||||
if (vec->size() > 1)
|
||||
break;
|
||||
double val = vec->get_at(0);
|
||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
||||
break;
|
||||
}
|
||||
case coFloat:
|
||||
{
|
||||
double val = m_opt.default_value->getFloat();
|
||||
text_value = (val - int(val)) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
||||
break;
|
||||
}
|
||||
case coFloats:
|
||||
{
|
||||
const ConfigOptionFloats *vec = static_cast<const ConfigOptionFloats*>(m_opt.default_value);
|
||||
if (vec == nullptr || vec->empty()) break;
|
||||
if (vec->size() > 1)
|
||||
break;
|
||||
double val = vec->get_at(0);
|
||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
||||
break;
|
||||
}
|
||||
case coString:
|
||||
text_value = static_cast<const ConfigOptionString*>(m_opt.default_value)->value;
|
||||
break;
|
||||
case coStrings:
|
||||
{
|
||||
const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value);
|
||||
if (vec == nullptr || vec->empty()) break;
|
||||
if (vec->size() > 1)
|
||||
break;
|
||||
text_value = vec->values.at(0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, (m_opt.multiline ? wxTE_MULTILINE : 0));
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(text_value));
|
||||
|
||||
temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event)
|
||||
{
|
||||
//! to allow the default handling
|
||||
event.Skip();
|
||||
//! eliminating the g-code pop up text description
|
||||
temp->GetToolTip()->Enable(false);
|
||||
}), temp->GetId());
|
||||
|
||||
temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
|
||||
{
|
||||
on_kill_focus(e);
|
||||
temp->GetToolTip()->Enable(true);
|
||||
}), temp->GetId());
|
||||
|
||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent) { on_change_field(); }), temp->GetId());
|
||||
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
}
|
||||
|
||||
boost::any TextCtrl::get_value()
|
||||
{
|
||||
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
||||
boost::any ret_val = get_value_by_opt_type(ret_str, m_opt.type);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
|
||||
void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(false); }
|
||||
|
||||
void CheckBox::BUILD() {
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
bool check_value = m_opt.type == coBool ?
|
||||
m_opt.default_value->getBool() : m_opt.type == coBools ?
|
||||
static_cast<ConfigOptionBools*>(m_opt.default_value)->values.at(0) :
|
||||
false;
|
||||
|
||||
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
|
||||
temp->SetValue(check_value);
|
||||
if (m_opt.readonly) temp->Disable();
|
||||
|
||||
temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(check_value ? "true" : "false"));
|
||||
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
}
|
||||
|
||||
int undef_spin_val = -9999; //! Probably, It's not necessary
|
||||
|
||||
void SpinCtrl::BUILD() {
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
wxString text_value = wxString("");
|
||||
int default_value = 0;
|
||||
|
||||
switch (m_opt.type) {
|
||||
case coInt:
|
||||
default_value = m_opt.default_value->getInt();
|
||||
text_value = wxString::Format(_T("%i"), default_value);
|
||||
break;
|
||||
case coInts:
|
||||
{
|
||||
const ConfigOptionInts *vec = static_cast<const ConfigOptionInts*>(m_opt.default_value);
|
||||
if (vec == nullptr || vec->empty()) break;
|
||||
for (size_t id = 0; id < vec->size(); ++id)
|
||||
{
|
||||
default_value = vec->get_at(id);
|
||||
text_value += wxString::Format(_T("%i"), default_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
|
||||
0, m_opt.min >0 ? m_opt.min : 0, m_opt.max < 2147483647 ? m_opt.max : 2147483647, default_value);
|
||||
|
||||
temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId());
|
||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { tmp_value = undef_spin_val; on_kill_focus(e); }), temp->GetId());
|
||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
||||
{
|
||||
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
||||
// # when it was changed from the text control, so the on_change callback
|
||||
// # gets the old one, and on_kill_focus resets the control to the old value.
|
||||
// # As a workaround, we get the new value from $event->GetString and store
|
||||
// # here temporarily so that we can return it from $self->get_value
|
||||
std::string value = e.GetString().utf8_str().data();
|
||||
if (is_matched(value, "^\\d+$"))
|
||||
tmp_value = std::stoi(value);
|
||||
on_change_field();
|
||||
// # We don't reset tmp_value here because _on_change might put callbacks
|
||||
// # in the CallAfter queue, and we want the tmp value to be available from
|
||||
// # them as well.
|
||||
}), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(text_value));
|
||||
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
}
|
||||
|
||||
void Choice::BUILD() {
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
wxComboBox* temp;
|
||||
if (!m_opt.gui_type.empty() && m_opt.gui_type.compare("select_open") != 0)
|
||||
temp = new wxComboBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
|
||||
else
|
||||
temp = new wxComboBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size, 0, NULL, wxCB_READONLY);
|
||||
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()){
|
||||
}
|
||||
else{
|
||||
for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels)
|
||||
temp->Append(wxString(el));
|
||||
set_selection();
|
||||
}
|
||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
||||
}
|
||||
|
||||
void Choice::set_selection()
|
||||
{
|
||||
wxString text_value = wxString("");
|
||||
switch (m_opt.type){
|
||||
case coFloat:
|
||||
case coPercent: {
|
||||
double val = m_opt.default_value->getFloat();
|
||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 1);
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el.compare(text_value) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
if (m_opt.type == coPercent) text_value += "%";
|
||||
idx == m_opt.enum_values.size() ?
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
case coEnum:{
|
||||
int id_value = static_cast<const ConfigOptionEnum<SeamPosition>*>(m_opt.default_value)->value; //!!
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(id_value);
|
||||
break;
|
||||
}
|
||||
case coInt:{
|
||||
int val = m_opt.default_value->getInt(); //!!
|
||||
text_value = wxString::Format(_T("%i"), int(val));
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el.compare(text_value) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
idx == m_opt.enum_values.size() ?
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
case coStrings:{
|
||||
text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->values.at(0);
|
||||
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el.compare(text_value) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
idx == m_opt.enum_values.size() ?
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Choice::set_value(const std::string value) //! Redundant?
|
||||
{
|
||||
m_disable_change_event = true;
|
||||
|
||||
size_t idx=0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el.compare(value) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
|
||||
idx == m_opt.enum_values.size() ?
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(value) :
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
void Choice::set_value(boost::any value)
|
||||
{
|
||||
m_disable_change_event = true;
|
||||
|
||||
switch (m_opt.type){
|
||||
case coInt:
|
||||
case coFloat:
|
||||
case coPercent:
|
||||
case coStrings:{
|
||||
wxString text_value;
|
||||
if (m_opt.type == coInt)
|
||||
text_value = wxString::Format(_T("%i"), int(boost::any_cast<int>(value)));
|
||||
else
|
||||
text_value = boost::any_cast<wxString>(value);
|
||||
auto idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el.compare(text_value) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
if (m_opt.type == coPercent) text_value += "%";
|
||||
idx == m_opt.enum_values.size() ?
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
case coEnum:{
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(boost::any_cast<int>(value));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
//! it's needed for _update_serial_ports()
|
||||
void Choice::set_values(const std::vector<std::string> values)
|
||||
{
|
||||
if (values.empty())
|
||||
return;
|
||||
m_disable_change_event = true;
|
||||
|
||||
// # it looks that Clear() also clears the text field in recent wxWidgets versions,
|
||||
// # but we want to preserve it
|
||||
auto ww = dynamic_cast<wxComboBox*>(window);
|
||||
auto value = ww->GetValue();
|
||||
ww->Clear();
|
||||
for (auto el : values)
|
||||
ww->Append(wxString(el));
|
||||
ww->SetValue(value);
|
||||
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any Choice::get_value()
|
||||
{
|
||||
boost::any ret_val;
|
||||
wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();
|
||||
|
||||
if (m_opt.type != coEnum)
|
||||
ret_val = get_value_by_opt_type(ret_str, m_opt.type);
|
||||
else
|
||||
{
|
||||
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
||||
if (m_opt_id.compare("external_fill_pattern") == 0)
|
||||
{
|
||||
if (!m_opt.enum_values.empty()){
|
||||
std::string key = m_opt.enum_values[ret_enum];
|
||||
t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
int value = map_names.at(key);
|
||||
|
||||
ret_val = static_cast<InfillPattern>(value);
|
||||
}
|
||||
else
|
||||
ret_val = static_cast<InfillPattern>(0);
|
||||
}
|
||||
if (m_opt_id.compare("fill_pattern") == 0)
|
||||
ret_val = static_cast<InfillPattern>(ret_enum);
|
||||
else if (m_opt_id.compare("gcode_flavor") == 0)
|
||||
ret_val = static_cast<GCodeFlavor>(ret_enum);
|
||||
else if (m_opt_id.compare("support_material_pattern") == 0)
|
||||
ret_val = static_cast<SupportMaterialPattern>(ret_enum);
|
||||
else if (m_opt_id.compare("seam_position") == 0)
|
||||
ret_val = static_cast<SeamPosition>(ret_enum);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void ColourPicker::BUILD()
|
||||
{
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->values.at(0));
|
||||
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
|
||||
|
||||
// // recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
temp->Bind(wxEVT_COLOURPICKER_CHANGED, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(clr));
|
||||
}
|
||||
|
||||
boost::any ColourPicker::get_value(){
|
||||
boost::any ret_val;
|
||||
|
||||
auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour();
|
||||
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
|
||||
ret_val = clr_str.ToStdString();
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void PointCtrl::BUILD()
|
||||
{
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
auto temp = new wxBoxSizer(wxHORIZONTAL);
|
||||
// $self->wxSizer($sizer);
|
||||
//
|
||||
wxSize field_size(40, -1);
|
||||
|
||||
auto default_pt = static_cast<ConfigOptionPoints*>(m_opt.default_value)->values.at(0);
|
||||
double val = default_pt.x;
|
||||
wxString X = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
||||
val = default_pt.y;
|
||||
wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
||||
|
||||
x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size);
|
||||
y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size);
|
||||
|
||||
temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
temp->Add(x_textctrl);
|
||||
temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
temp->Add(y_textctrl);
|
||||
|
||||
x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
|
||||
y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
|
||||
|
||||
// // recast as a wxWindow to fit the calling convention
|
||||
sizer = dynamic_cast<wxSizer*>(temp);
|
||||
|
||||
x_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||
}
|
||||
|
||||
void PointCtrl::set_value(const Pointf value)
|
||||
{
|
||||
m_disable_change_event = true;
|
||||
|
||||
double val = value.x;
|
||||
x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None));
|
||||
val = value.y;
|
||||
y_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None));
|
||||
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
void PointCtrl::set_value(boost::any value)
|
||||
{
|
||||
Pointf pt;
|
||||
try
|
||||
{
|
||||
pt = boost::any_cast<ConfigOptionPoints*>(value)->values.at(0);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
try{
|
||||
pt = boost::any_cast<Pointf>(value);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "Error! Can't cast PointCtrl value" << m_opt_id << "\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_value(pt);
|
||||
}
|
||||
|
||||
boost::any PointCtrl::get_value()
|
||||
{
|
||||
Pointf ret_point;
|
||||
double val;
|
||||
x_textctrl->GetValue().ToDouble(&val);
|
||||
ret_point.x = val;
|
||||
y_textctrl->GetValue().ToDouble(&val);
|
||||
ret_point.y = val;
|
||||
return ret_point;
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
|
270
xs/src/slic3r/GUI/Field.hpp
Normal file
270
xs/src/slic3r/GUI/Field.hpp
Normal file
@ -0,0 +1,270 @@
|
||||
#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/clrpicker.h>
|
||||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
#include "../../libslic3r/Config.hpp"
|
||||
|
||||
//#include "slic3r_gui.hpp"
|
||||
#include "GUI.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class Field;
|
||||
using t_field = std::unique_ptr<Field>;
|
||||
using t_kill_focus = std::function<void()>;
|
||||
using t_change = std::function<void(t_config_option_key, boost::any)>;
|
||||
|
||||
class Field {
|
||||
protected:
|
||||
// factory function to defer and enforce creation of derived type.
|
||||
virtual void PostInitialize() { BUILD(); }
|
||||
|
||||
/// 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(wxEvent& event);
|
||||
/// Call the attached on_change method.
|
||||
void on_change_field();
|
||||
|
||||
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_change m_on_change {nullptr};
|
||||
|
||||
// This is used to avoid recursive invocation of the field change/update by wxWidgets.
|
||||
bool m_disable_change_event {false};
|
||||
|
||||
/// Copy of ConfigOption for deduction purposes
|
||||
const ConfigOptionDef m_opt {ConfigOptionDef()};
|
||||
const t_config_option_key m_opt_id;//! {""};
|
||||
|
||||
/// Sets a value for this control.
|
||||
/// subclasses should overload with a specific version
|
||||
/// Postcondition: Method does not fire the on_change event.
|
||||
virtual void set_value(boost::any value) = 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;
|
||||
|
||||
/// 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);
|
||||
|
||||
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) {};
|
||||
|
||||
/// 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; }
|
||||
|
||||
bool is_matched(std::string string, std::string pattern);
|
||||
boost::any get_value_by_opt_type(wxString str, ConfigOptionType type);
|
||||
|
||||
/// 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;
|
||||
}
|
||||
};
|
||||
|
||||
/// 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.
|
||||
inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != 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;
|
||||
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) {}
|
||||
|
||||
void BUILD();
|
||||
wxWindow* window {nullptr};
|
||||
|
||||
virtual void set_value(std::string value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
virtual void set_value(boost::any value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any get_value() override;
|
||||
|
||||
virtual void enable();
|
||||
virtual void disable();
|
||||
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) {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const bool value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(boost::any value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
boost::any get_value() override {
|
||||
return boost::any(dynamic_cast<wxCheckBox*>(window)->GetValue());
|
||||
}
|
||||
|
||||
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) {}
|
||||
|
||||
int tmp_value;
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const std::string value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(boost::any value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(boost::any_cast<int>(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
boost::any get_value() override {
|
||||
return boost::any(dynamic_cast<wxSpinCtrl*>(window)->GetValue());
|
||||
}
|
||||
|
||||
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;
|
||||
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) {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_selection();
|
||||
void set_value(const std::string value);
|
||||
void set_value(boost::any value);
|
||||
void set_values(const std::vector<std::string> values);
|
||||
boost::any get_value() override;
|
||||
|
||||
void enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); };
|
||||
void disable() override{ dynamic_cast<wxComboBox*>(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) {}
|
||||
|
||||
wxWindow* window{ nullptr };
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const std::string value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(boost::any value) {
|
||||
m_disable_change_event = true;
|
||||
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
|
||||
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) {}
|
||||
|
||||
wxSizer* sizer{ nullptr };
|
||||
wxTextCtrl* x_textctrl;
|
||||
wxTextCtrl* y_textctrl;
|
||||
|
||||
void BUILD() override;
|
||||
|
||||
void set_value(const Pointf value);
|
||||
void set_value(boost::any value);
|
||||
boost::any get_value() override;
|
||||
|
||||
void enable() override {
|
||||
x_textctrl->Enable();
|
||||
y_textctrl->Enable(); };
|
||||
void disable() override{
|
||||
x_textctrl->Disable();
|
||||
y_textctrl->Disable(); };
|
||||
wxSizer* getSizer() override { return sizer; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
@ -12,21 +13,39 @@
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#elif _WIN32
|
||||
#include <Windows.h>
|
||||
// Undefine min/max macros incompatible with the standard library
|
||||
// For example, std::numeric_limits<std::streamsize>::max()
|
||||
// produces some weird errors
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#include "boost/nowide/convert.hpp"
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#endif
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/config.h>
|
||||
#include <wx/dir.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/frame.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/combo.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include "Tab.hpp"
|
||||
#include "TabIface.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
#if __APPLE__
|
||||
@ -153,6 +172,10 @@ wxApp *g_wxApp = nullptr;
|
||||
wxFrame *g_wxMainFrame = nullptr;
|
||||
wxNotebook *g_wxTabPanel = nullptr;
|
||||
|
||||
std::vector<Tab *> g_tabs_list;
|
||||
|
||||
wxLocale* g_wxLocale;
|
||||
|
||||
void set_wxapp(wxApp *app)
|
||||
{
|
||||
g_wxApp = app;
|
||||
@ -168,25 +191,292 @@ void set_tab_panel(wxNotebook *tab_panel)
|
||||
g_wxTabPanel = tab_panel;
|
||||
}
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu)
|
||||
std::vector<Tab *>& get_tabs_list()
|
||||
{
|
||||
#if 0
|
||||
auto debug_menu = new wxMenu();
|
||||
debug_menu->Append(wxWindow::NewControlId(1), "Some debug");
|
||||
menu->Append(debug_menu, _T("&Debug"));
|
||||
#endif
|
||||
return g_tabs_list;
|
||||
}
|
||||
|
||||
void create_preset_tab(const char *name)
|
||||
bool checked_tab(Tab* tab)
|
||||
{
|
||||
auto *panel = new wxPanel(g_wxTabPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
||||
// Vertical sizer to hold the choice menu and the rest of the page.
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->SetSizeHints(panel);
|
||||
panel->SetSizer(sizer);
|
||||
auto *button = new wxButton(panel, wxID_ANY, "Hello World", wxDefaultPosition, wxDefaultSize, 0);
|
||||
sizer->Add(button, 0, 0, 0);
|
||||
g_wxTabPanel->AddPage(panel, name);
|
||||
bool ret = true;
|
||||
if (find(g_tabs_list.begin(), g_tabs_list.end(), tab) == g_tabs_list.end())
|
||||
ret = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void delete_tab_from_list(Tab* tab)
|
||||
{
|
||||
std::vector<Tab *>::iterator itr = find(g_tabs_list.begin(), g_tabs_list.end(), tab);
|
||||
if (itr != g_tabs_list.end())
|
||||
g_tabs_list.erase(itr);
|
||||
}
|
||||
|
||||
bool select_language(wxArrayString & names,
|
||||
wxArrayLong & identifiers)
|
||||
{
|
||||
wxCHECK_MSG(names.Count() == identifiers.Count(), false,
|
||||
_L("Array of language names and identifiers should have the same size."));
|
||||
int init_selection = 0;
|
||||
long current_language = g_wxLocale ? g_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN;
|
||||
for (auto lang : identifiers){
|
||||
if (lang == current_language)
|
||||
break;
|
||||
else
|
||||
++init_selection;
|
||||
}
|
||||
if (init_selection == identifiers.size())
|
||||
init_selection = 0;
|
||||
long index = wxGetSingleChoiceIndex(_L("Select the language"), _L("Language"),
|
||||
names, init_selection);
|
||||
if (index != -1)
|
||||
{
|
||||
g_wxLocale = new wxLocale;
|
||||
g_wxLocale->Init(identifiers[index]);
|
||||
g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir()));
|
||||
g_wxLocale->AddCatalog(g_wxApp->GetAppName());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool load_language()
|
||||
{
|
||||
wxConfig config(g_wxApp->GetAppName());
|
||||
long language;
|
||||
if (!config.Read(wxT("wxTranslation_Language"),
|
||||
&language, wxLANGUAGE_UNKNOWN))
|
||||
{
|
||||
language = wxLANGUAGE_UNKNOWN;
|
||||
}
|
||||
if (language == wxLANGUAGE_UNKNOWN)
|
||||
return false;
|
||||
wxArrayString names;
|
||||
wxArrayLong identifiers;
|
||||
get_installed_languages(names, identifiers);
|
||||
for (size_t i = 0; i < identifiers.Count(); i++)
|
||||
{
|
||||
if (identifiers[i] == language)
|
||||
{
|
||||
g_wxLocale = new wxLocale;
|
||||
g_wxLocale->Init(identifiers[i]);
|
||||
g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir()));
|
||||
g_wxLocale->AddCatalog(g_wxApp->GetAppName());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void save_language()
|
||||
{
|
||||
wxConfig config(g_wxApp->GetAppName());
|
||||
long language = wxLANGUAGE_UNKNOWN;
|
||||
if (g_wxLocale) {
|
||||
language = g_wxLocale->GetLanguage();
|
||||
}
|
||||
config.Write(wxT("wxTranslation_Language"), language);
|
||||
config.Flush();
|
||||
}
|
||||
|
||||
void get_installed_languages(wxArrayString & names,
|
||||
wxArrayLong & identifiers)
|
||||
{
|
||||
names.Clear();
|
||||
identifiers.Clear();
|
||||
|
||||
wxDir dir(wxPathOnly(localization_dir()));
|
||||
wxString filename;
|
||||
const wxLanguageInfo * langinfo;
|
||||
wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT);
|
||||
if (!name.IsEmpty())
|
||||
{
|
||||
names.Add(_L("Default"));
|
||||
identifiers.Add(wxLANGUAGE_DEFAULT);
|
||||
}
|
||||
for (bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
|
||||
cont; cont = dir.GetNext(&filename))
|
||||
{
|
||||
wxLogTrace(wxTraceMask(),
|
||||
"L10n: Directory found = \"%s\"",
|
||||
filename.GetData());
|
||||
langinfo = wxLocale::FindLanguageInfo(filename);
|
||||
if (langinfo != NULL)
|
||||
{
|
||||
auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() +
|
||||
filename + wxFileName::GetPathSeparator() +
|
||||
g_wxApp->GetAppName() + wxT(".mo");
|
||||
if (wxFileExists(full_file_name))
|
||||
{
|
||||
names.Add(langinfo->Description);
|
||||
identifiers.Add(langinfo->Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu, int event_language_change)
|
||||
{
|
||||
//#if 0
|
||||
auto local_menu = new wxMenu();
|
||||
local_menu->Append(wxWindow::NewControlId(1), _L("Change Application Language"));
|
||||
local_menu->Bind(wxEVT_MENU, [event_language_change](wxEvent&){
|
||||
wxArrayString names;
|
||||
wxArrayLong identifiers;
|
||||
get_installed_languages(names, identifiers);
|
||||
if (select_language(names, identifiers)){
|
||||
save_language();
|
||||
show_info(g_wxTabPanel, "Application will be restarted", "Attention!");
|
||||
if (event_language_change > 0) {
|
||||
wxCommandEvent event(event_language_change);
|
||||
g_wxApp->ProcessEvent(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
menu->Append(local_menu, _T("&Localization"));
|
||||
//#endif
|
||||
}
|
||||
|
||||
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
||||
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
||||
int event_value_change, int event_presets_changed,
|
||||
int event_button_browse, int event_button_test)
|
||||
{
|
||||
add_created_tab(new TabPrint (g_wxTabPanel, no_controller), preset_bundle, app_config);
|
||||
add_created_tab(new TabFilament (g_wxTabPanel, no_controller), preset_bundle, app_config);
|
||||
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent),
|
||||
preset_bundle, app_config);
|
||||
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
|
||||
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
|
||||
if (! tab)
|
||||
continue;
|
||||
tab->set_event_value_change(wxEventType(event_value_change));
|
||||
tab->set_event_presets_changed(wxEventType(event_presets_changed));
|
||||
if (tab->name() == "printer"){
|
||||
TabPrinter* tab_printer = static_cast<TabPrinter*>(tab);
|
||||
tab_printer->set_event_button_browse(wxEventType(event_button_browse));
|
||||
tab_printer->set_event_button_test(wxEventType(event_button_test));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TabIface* get_preset_tab_iface(char *name)
|
||||
{
|
||||
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
|
||||
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
|
||||
if (! tab)
|
||||
continue;
|
||||
if (tab->name() == name) {
|
||||
return new TabIface(tab);
|
||||
}
|
||||
}
|
||||
return new TabIface(nullptr);
|
||||
}
|
||||
|
||||
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)
|
||||
void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index /*= 0*/)
|
||||
{
|
||||
try{
|
||||
switch (config.def()->get(opt_key)->type){
|
||||
case coFloatOrPercent:{
|
||||
const auto &val = *config.option<ConfigOptionFloatOrPercent>(opt_key);
|
||||
config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(boost::any_cast<double>(value), val.percent));
|
||||
break;}
|
||||
case coPercent:
|
||||
config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast<double>(value)));
|
||||
break;
|
||||
case coFloat:{
|
||||
double& val = config.opt_float(opt_key);
|
||||
val = boost::any_cast<double>(value);
|
||||
break;
|
||||
}
|
||||
case coPercents:
|
||||
case coFloats:{
|
||||
double& val = config.opt_float(opt_key, 0);
|
||||
val = boost::any_cast<double>(value);
|
||||
break;
|
||||
}
|
||||
case coString:
|
||||
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
||||
break;
|
||||
case coStrings:{
|
||||
if (opt_key.compare("compatible_printers") == 0){
|
||||
config.option<ConfigOptionStrings>(opt_key)->values.resize(0);
|
||||
for (auto el : boost::any_cast<std::vector<std::string>>(value))
|
||||
config.option<ConfigOptionStrings>(opt_key)->values.push_back(el);
|
||||
}
|
||||
else{
|
||||
ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast<std::string>(value) };
|
||||
config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case coBool:
|
||||
config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast<bool>(value)));
|
||||
break;
|
||||
case coBools:{
|
||||
ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast<bool>(value) };
|
||||
config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||
break;}
|
||||
case coInt:
|
||||
config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast<int>(value)));
|
||||
break;
|
||||
case coInts:{
|
||||
ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast<int>(value) };
|
||||
config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||
}
|
||||
break;
|
||||
case coEnum:{
|
||||
if (opt_key.compare("external_fill_pattern") == 0 ||
|
||||
opt_key.compare("fill_pattern") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<InfillPattern>(boost::any_cast<InfillPattern>(value)));
|
||||
else if (opt_key.compare("gcode_flavor") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value)));
|
||||
else if (opt_key.compare("support_material_pattern") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value)));
|
||||
else if (opt_key.compare("seam_position") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
|
||||
}
|
||||
break;
|
||||
case coPoints:{
|
||||
ConfigOptionPoints points;
|
||||
points.values = boost::any_cast<std::vector<Pointf>>(value);
|
||||
config.set_key_value(opt_key, new ConfigOptionPoints(points));
|
||||
}
|
||||
break;
|
||||
case coNone:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
int i = 0;//no reason, just experiment
|
||||
}
|
||||
}
|
||||
|
||||
void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config)
|
||||
{
|
||||
panel->m_show_btn_incompatible_presets = app_config->get("show_incompatible_presets").empty();
|
||||
panel->create_preset_tab(preset_bundle);
|
||||
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
panel->load_current_preset();
|
||||
g_wxTabPanel->AddPage(panel, panel->title());
|
||||
}
|
||||
|
||||
void show_error(wxWindow* parent, wxString message){
|
||||
auto msg_wingow = new wxMessageDialog(parent, message, _L("Error"), wxOK | wxICON_ERROR);
|
||||
msg_wingow->ShowModal();
|
||||
}
|
||||
|
||||
void show_info(wxWindow* parent, wxString message, wxString title){
|
||||
auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _L("Notice") : title, wxOK | wxICON_INFORMATION);
|
||||
msg_wingow->ShowModal();
|
||||
}
|
||||
|
||||
wxApp* get_app(){
|
||||
return g_wxApp;
|
||||
}
|
||||
|
||||
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
|
||||
|
@ -3,14 +3,50 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Config.hpp"
|
||||
|
||||
class wxApp;
|
||||
class wxFrame;
|
||||
class wxWindow;
|
||||
class wxMenuBar;
|
||||
class wxNotebook;
|
||||
class wxComboCtrl;
|
||||
class wxString;
|
||||
class wxArrayString;
|
||||
class wxArrayLong;
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
namespace Slic3r {
|
||||
|
||||
class PresetBundle;
|
||||
class PresetCollection;
|
||||
class AppConfig;
|
||||
class DynamicPrintConfig;
|
||||
class TabIface;
|
||||
|
||||
//! macro used to localization, return wxString
|
||||
#define _L(s) wxGetTranslation(s)
|
||||
//! macro used to localization, return const CharType *
|
||||
#define _LU8(s) wxGetTranslation(s).ToUTF8().data()
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class Tab;
|
||||
// Map from an file_type name to full file wildcard name.
|
||||
typedef std::map<std::string, std::string> t_file_wild_card;
|
||||
inline t_file_wild_card& get_file_wild_card() {
|
||||
static t_file_wild_card FILE_WILDCARDS;
|
||||
if (FILE_WILDCARDS.empty()){
|
||||
FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
|
||||
FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL";
|
||||
FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ";
|
||||
FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML";
|
||||
FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
|
||||
FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI";
|
||||
FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC";
|
||||
FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG";
|
||||
}
|
||||
return FILE_WILDCARDS;
|
||||
}
|
||||
|
||||
void disable_screensaver();
|
||||
void enable_screensaver();
|
||||
@ -23,10 +59,34 @@ void set_wxapp(wxApp *app);
|
||||
void set_main_frame(wxFrame *main_frame);
|
||||
void set_tab_panel(wxNotebook *tab_panel);
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu);
|
||||
// Create a new preset tab (print, filament or printer),
|
||||
void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
||||
// Create a new preset tab (print, filament and printer),
|
||||
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
||||
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
||||
int event_value_change, int event_presets_changed,
|
||||
int event_button_browse, int event_button_test);
|
||||
TabIface* get_preset_tab_iface(char *name);
|
||||
|
||||
// add it at the end of the tab panel.
|
||||
void create_preset_tab(const char *name);
|
||||
void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config);
|
||||
// Change option value in config
|
||||
void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index = 0);
|
||||
|
||||
void show_error(wxWindow* parent, wxString message);
|
||||
void show_info(wxWindow* parent, wxString message, wxString title);
|
||||
|
||||
// load language saved at application config
|
||||
bool load_language();
|
||||
// save language at application config
|
||||
void save_language();
|
||||
// get list of installed languages
|
||||
void get_installed_languages(wxArrayString & names, wxArrayLong & identifiers);
|
||||
// select language from the list of installed languages
|
||||
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
||||
|
||||
std::vector<Tab *>& get_tabs_list();
|
||||
bool checked_tab(Tab* tab);
|
||||
void delete_tab_from_list(Tab* tab);
|
||||
|
||||
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
|
||||
// Items are all initialized to the given value.
|
||||
|
410
xs/src/slic3r/GUI/OptionsGroup.cpp
Normal file
410
xs/src/slic3r/GUI/OptionsGroup.cpp
Normal file
@ -0,0 +1,410 @@
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "ConfigExceptions.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <wx/tooltip.h>
|
||||
#include <wx/numformatter.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
const t_field& OptionsGroup::build_field(const Option& opt) {
|
||||
return build_field(opt.opt_id, opt.opt);
|
||||
}
|
||||
const t_field& OptionsGroup::build_field(const t_config_option_key& id) {
|
||||
const ConfigOptionDef& opt = m_options.at(id).opt;
|
||||
return build_field(id, opt);
|
||||
}
|
||||
|
||||
const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt) {
|
||||
// Check the gui_type field first, fall through
|
||||
// is the normal type.
|
||||
if (opt.gui_type.compare("select") == 0) {
|
||||
} else if (opt.gui_type.compare("select_open") == 0) {
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id)));
|
||||
} else if (opt.gui_type.compare("color") == 0) {
|
||||
m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(m_parent, opt, id)));
|
||||
} else if (opt.gui_type.compare("f_enum_open") == 0 ||
|
||||
opt.gui_type.compare("i_enum_open") == 0 ||
|
||||
opt.gui_type.compare("i_enum_closed") == 0) {
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id)));
|
||||
} else if (opt.gui_type.compare("slider") == 0) {
|
||||
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
|
||||
} else {
|
||||
switch (opt.type) {
|
||||
case coFloatOrPercent:
|
||||
case coFloat:
|
||||
case coFloats:
|
||||
case coPercent:
|
||||
case coPercents:
|
||||
case coString:
|
||||
case coStrings:
|
||||
m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(m_parent, opt, id)));
|
||||
break;
|
||||
case coBool:
|
||||
case coBools:
|
||||
m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(m_parent, opt, id)));
|
||||
break;
|
||||
case coInt:
|
||||
case coInts:
|
||||
m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(m_parent, opt, id)));
|
||||
break;
|
||||
case coEnum:
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id)));
|
||||
break;
|
||||
case coPoints:
|
||||
m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(m_parent, opt, id)));
|
||||
break;
|
||||
case coNone: break;
|
||||
default:
|
||||
throw /*//!ConfigGUITypeError("")*/std::logic_error("This control doesn't exist till now"); break;
|
||||
}
|
||||
}
|
||||
// Grab a reference to fields for convenience
|
||||
const t_field& field = m_fields[id];
|
||||
field->m_on_change = [this](std::string opt_id, boost::any value){
|
||||
//! This function will be called from Field.
|
||||
//! Call OptionGroup._on_change(...)
|
||||
if (!this->m_disabled)
|
||||
this->on_change_OG(opt_id, value);
|
||||
};
|
||||
field->m_on_kill_focus = [this](){
|
||||
//! This function will be called from Field.
|
||||
if (!this->m_disabled)
|
||||
this->on_kill_focus();
|
||||
};
|
||||
field->m_parent = parent();
|
||||
// assign function objects for callbacks, etc.
|
||||
return field;
|
||||
}
|
||||
|
||||
void OptionsGroup::append_line(const Line& line) {
|
||||
//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){
|
||||
if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){
|
||||
if (line.sizer != nullptr) {
|
||||
sizer->Add(line.sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
|
||||
return;
|
||||
}
|
||||
if (line.widget != nullptr) {
|
||||
sizer->Add(line.widget(m_parent), 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto option_set = line.get_options();
|
||||
for (auto opt : option_set)
|
||||
m_options.emplace(opt.opt_id, opt);
|
||||
|
||||
// if we have a single option with no label, no sidetext just add it directly to sizer
|
||||
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
|
||||
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
|
||||
line.get_extra_widgets().size() == 0) {
|
||||
const auto& option = option_set.front();
|
||||
const auto& field = build_field(option);
|
||||
|
||||
if (is_window_field(field))
|
||||
sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
|
||||
if (is_sizer_field(field))
|
||||
sizer->Add(field->getSizer(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
|
||||
return;
|
||||
}
|
||||
|
||||
auto grid_sizer = m_grid_sizer;
|
||||
|
||||
// Build a label if we have it
|
||||
if (label_width != 0) {
|
||||
auto label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"),
|
||||
wxDefaultPosition, wxSize(label_width, -1));
|
||||
label->SetFont(label_font);
|
||||
label->Wrap(label_width); // avoid a Linux/GTK bug
|
||||
grid_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL,0);
|
||||
if (line.label_tooltip.compare("") != 0)
|
||||
label->SetToolTip(line.label_tooltip);
|
||||
}
|
||||
|
||||
// If there's a widget, build it and add the result to the sizer.
|
||||
if (line.widget != nullptr) {
|
||||
auto wgt = line.widget(parent());
|
||||
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have a single option with no sidetext just add it directly to the grid sizer
|
||||
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
|
||||
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
|
||||
const auto& option = option_set.front();
|
||||
const auto& field = build_field(option);
|
||||
//! std::cerr << "single option, no sidetext.\n";
|
||||
//! std::cerr << "field parent is not null?: " << (field->parent != nullptr) << "\n";
|
||||
|
||||
if (is_window_field(field))
|
||||
grid_sizer->Add(field->getWindow(), 0, (option.opt.full_width ? wxEXPAND : 0) |
|
||||
wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, wxOSX ? 0 : 2);
|
||||
if (is_sizer_field(field))
|
||||
grid_sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're here, we have more than one option or a single option with sidetext
|
||||
// so we need a horizontal sizer to arrange these things
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
grid_sizer->Add(sizer, 0, wxEXPAND | wxALL, 0);
|
||||
for (auto opt : option_set) {
|
||||
ConfigOptionDef option = opt.opt;
|
||||
// add label if any
|
||||
if (option.label != "") {
|
||||
auto field_label = new wxStaticText(parent(), wxID_ANY, wxString::FromUTF8(option.label.c_str()) + ":", wxDefaultPosition, wxDefaultSize);
|
||||
field_label->SetFont(label_font);
|
||||
sizer->Add(field_label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
}
|
||||
|
||||
// add field
|
||||
const Option& opt_ref = opt;
|
||||
auto& field = build_field(opt_ref);
|
||||
is_sizer_field(field) ?
|
||||
sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
|
||||
sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
// add sidetext if any
|
||||
if (option.sidetext != "") {
|
||||
auto sidetext = new wxStaticText(parent(), wxID_ANY, wxString::FromUTF8(option.sidetext.c_str()), wxDefaultPosition, wxDefaultSize);
|
||||
sidetext->SetFont(sidetext_font);
|
||||
sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
||||
}
|
||||
|
||||
// add side widget if any
|
||||
if (opt.side_widget != nullptr) {
|
||||
sizer->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification
|
||||
}
|
||||
|
||||
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
|
||||
{
|
||||
sizer->AddSpacer(6);
|
||||
}
|
||||
}
|
||||
// add extra sizers if any
|
||||
for (auto extra_widget : line.get_extra_widgets()) {
|
||||
sizer->Add(extra_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification
|
||||
}
|
||||
}
|
||||
|
||||
Line OptionsGroup::create_single_option_line(const Option& option) const {
|
||||
Line retval{ wxString::FromUTF8(option.opt.label.c_str()), wxString::FromUTF8(option.opt.tooltip.c_str()) };
|
||||
Option tmp(option);
|
||||
tmp.opt.label = std::string("");
|
||||
retval.append_option(tmp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void OptionsGroup::on_change_OG(t_config_option_key id, /*config_value*/boost::any value) {
|
||||
if (m_on_change != nullptr)
|
||||
m_on_change(id, value);
|
||||
}
|
||||
|
||||
Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /*= -1*/)
|
||||
{
|
||||
if (!m_config->has(opt_key)) {
|
||||
//! exception ("No $opt_key in ConfigOptionsGroup config");
|
||||
}
|
||||
|
||||
std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);
|
||||
std::pair<std::string, int> pair(opt_key, opt_index);
|
||||
m_opt_map.emplace(opt_id, pair);
|
||||
|
||||
return Option(*m_config->def()->get(opt_key), opt_id);
|
||||
}
|
||||
|
||||
void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any value)
|
||||
{
|
||||
if (!m_opt_map.empty())
|
||||
{
|
||||
auto it = m_opt_map.find(opt_id);
|
||||
if (it == m_opt_map.end())
|
||||
{
|
||||
OptionsGroup::on_change_OG(opt_id, value);
|
||||
return;
|
||||
}
|
||||
|
||||
auto itOption = it->second;
|
||||
std::string opt_key = itOption.first;
|
||||
int opt_index = itOption.second;
|
||||
|
||||
auto option = m_options.at(opt_id).opt;
|
||||
|
||||
// get value
|
||||
//! auto field_value = get_value(opt_id);
|
||||
if (option.gui_flags.compare("serialized")==0) {
|
||||
if (opt_index != -1){
|
||||
// die "Can't set serialized option indexed value" ;
|
||||
}
|
||||
// # Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
|
||||
// # Currently used for the post_process config value only.
|
||||
// my @values = split / ; / , $field_value;
|
||||
// $self->config->set($opt_key, \@values);
|
||||
}
|
||||
else {
|
||||
if (opt_index == -1) {
|
||||
// change_opt_value(*m_config, opt_key, field_value);
|
||||
//!? why field_value?? in this case changed value will be lose! No?
|
||||
change_opt_value(*m_config, opt_key, value);
|
||||
}
|
||||
else {
|
||||
change_opt_value(*m_config, opt_key, value, opt_index);
|
||||
// auto value = m_config->get($opt_key);
|
||||
// $value->[$opt_index] = $field_value;
|
||||
// $self->config->set($opt_key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this
|
||||
}
|
||||
|
||||
void ConfigOptionsGroup::reload_config(){
|
||||
for (std::map< std::string, std::pair<std::string, int> >::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
|
||||
auto opt_id = it->first;
|
||||
std::string opt_key = m_opt_map.at(opt_id).first;
|
||||
int opt_index = m_opt_map.at(opt_id).second;
|
||||
auto option = m_options.at(opt_id).opt;
|
||||
set_value(opt_id, config_value(opt_key, opt_index, option.gui_flags.compare("serialized") == 0 ));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boost::any ConfigOptionsGroup::config_value(std::string opt_key, int opt_index, bool deserialize){
|
||||
|
||||
if (deserialize) {
|
||||
// Want to edit a vector value(currently only multi - strings) in a single edit box.
|
||||
// Aggregate the strings the old way.
|
||||
// Currently used for the post_process config value only.
|
||||
if (opt_index != -1)
|
||||
throw std::out_of_range("Can't deserialize option indexed value");
|
||||
// return join(';', m_config->get(opt_key)});
|
||||
return get_config_value(*m_config, opt_key);
|
||||
}
|
||||
else {
|
||||
// return opt_index == -1 ? m_config->get(opt_key) : m_config->get_at(opt_key, opt_index);
|
||||
return get_config_value(*m_config, opt_key, opt_index);
|
||||
}
|
||||
}
|
||||
|
||||
wxString double_to_string(double const value)
|
||||
{
|
||||
int precision = 10 * value - int(10 * value) == 0 ? 1 : 2;
|
||||
return value - int(value) == 0 ?
|
||||
wxString::Format(_T("%i"), int(value)) :
|
||||
wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
|
||||
}
|
||||
|
||||
boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std::string opt_key, int opt_index/* = -1*/)
|
||||
{
|
||||
size_t idx = opt_index == -1 ? 0 : opt_index;
|
||||
|
||||
boost::any ret;
|
||||
wxString text_value = wxString("");
|
||||
const ConfigOptionDef* opt = config.def()->get(opt_key);
|
||||
switch (opt->type){
|
||||
case coFloatOrPercent:{
|
||||
const auto &value = *config.option<ConfigOptionFloatOrPercent>(opt_key);
|
||||
if (value.percent)
|
||||
{
|
||||
text_value = wxString::Format(_T("%i"), int(value.value));
|
||||
text_value += "%";
|
||||
}
|
||||
else
|
||||
text_value = double_to_string(value.value);
|
||||
ret = text_value;
|
||||
break;
|
||||
}
|
||||
case coPercent:{
|
||||
double val = config.option<ConfigOptionPercent>(opt_key)->value;
|
||||
text_value = wxString::Format(_T("%i"), int(val));
|
||||
ret = text_value;// += "%";
|
||||
}
|
||||
break;
|
||||
case coPercents:
|
||||
case coFloats:
|
||||
case coFloat:{
|
||||
double val = opt->type == coFloats ?
|
||||
config.opt_float(opt_key, idx/*0opt_index*/) :
|
||||
opt->type == coFloat ? config.opt_float(opt_key) :
|
||||
config.option<ConfigOptionPercents>(opt_key)->values.at(idx/*0*/);
|
||||
ret = double_to_string(val);
|
||||
}
|
||||
break;
|
||||
case coString:
|
||||
ret = static_cast<wxString>(config.opt_string(opt_key));
|
||||
break;
|
||||
case coStrings:
|
||||
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
|
||||
ret = text_value;
|
||||
else
|
||||
ret = static_cast<wxString>(config.opt_string(opt_key, static_cast<unsigned int>(idx/*0*/)/*opt_index*/));
|
||||
break;
|
||||
case coBool:
|
||||
ret = config.opt_bool(opt_key);
|
||||
break;
|
||||
case coBools:
|
||||
ret = config.opt_bool(opt_key, idx/*0opt_index*/);
|
||||
break;
|
||||
case coInt:
|
||||
ret = config.opt_int(opt_key);
|
||||
break;
|
||||
case coInts:
|
||||
ret = config.opt_int(opt_key, idx/*0/*opt_index*/);
|
||||
break;
|
||||
case coEnum:{
|
||||
if (opt_key.compare("external_fill_pattern") == 0 ||
|
||||
opt_key.compare("fill_pattern") == 0 ){
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<InfillPattern>>(opt_key)->value);
|
||||
}
|
||||
else if (opt_key.compare("gcode_flavor") == 0 ){
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<GCodeFlavor>>(opt_key)->value);
|
||||
}
|
||||
else if (opt_key.compare("support_material_pattern") == 0){
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value);
|
||||
}
|
||||
else if (opt_key.compare("seam_position") == 0)
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value);
|
||||
}
|
||||
break;
|
||||
case coPoints:{
|
||||
const auto &value = *config.option<ConfigOptionPoints>(opt_key);
|
||||
ret = value.values.at(idx/*0*/);
|
||||
}
|
||||
break;
|
||||
case coNone:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Field* ConfigOptionsGroup::get_fieldc(t_config_option_key opt_key, int opt_index){
|
||||
std::string opt_id = "";
|
||||
for (std::map< std::string, std::pair<std::string, int> >::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
|
||||
if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second){
|
||||
opt_id = it->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return opt_id.empty() ? nullptr : get_field(opt_id);
|
||||
}
|
||||
|
||||
void ogStaticText::SetText(wxString value)
|
||||
{
|
||||
SetLabel(value);
|
||||
Wrap(400);
|
||||
GetParent()->Layout();
|
||||
}
|
||||
|
||||
void Option::translate()
|
||||
{
|
||||
opt.label = _LU8(opt.label);
|
||||
opt.tooltip = _LU8(opt.tooltip);
|
||||
opt.sidetext = _LU8(opt.sidetext);
|
||||
opt.full_label = _LU8(opt.full_label);
|
||||
opt.category = _LU8(opt.category);
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
181
xs/src/slic3r/GUI/OptionsGroup.hpp
Normal file
181
xs/src/slic3r/GUI/OptionsGroup.hpp
Normal file
@ -0,0 +1,181 @@
|
||||
#include <wx/wx.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/settings.h>
|
||||
//#include <wx/window.h>
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "Field.hpp"
|
||||
|
||||
// Translate the ifdef
|
||||
#ifdef __WXOSX__
|
||||
#define wxOSX true
|
||||
#else
|
||||
#define wxOSX false
|
||||
#endif
|
||||
|
||||
#define BORDER(a, b) ((wxOSX ? a : b))
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
/// Widget type describes a function object that returns a wxWindow (our widget) and accepts a wxWidget (parent window).
|
||||
using widget_t = std::function<wxSizer*(wxWindow*)>;//!std::function<wxWindow*(wxWindow*)>;
|
||||
using column_t = std::function<wxSizer*(const Line&)>;
|
||||
|
||||
/// Wraps a ConfigOptionDef and adds function object for creating a side_widget.
|
||||
struct Option {
|
||||
ConfigOptionDef opt { ConfigOptionDef() };
|
||||
t_config_option_key opt_id;//! {""};
|
||||
widget_t side_widget {nullptr};
|
||||
bool readonly {false};
|
||||
|
||||
Option(const ConfigOptionDef& _opt, t_config_option_key id) :
|
||||
opt(_opt), opt_id(id) { translate(); }
|
||||
void translate();
|
||||
};
|
||||
using t_option = std::unique_ptr<Option>; //!
|
||||
|
||||
/// Represents option lines
|
||||
class Line {
|
||||
public:
|
||||
wxString label {wxString("")};
|
||||
wxString label_tooltip {wxString("")};
|
||||
size_t full_width {0};
|
||||
wxSizer* sizer {nullptr};
|
||||
widget_t widget {nullptr};
|
||||
|
||||
void append_option(const Option& option) {
|
||||
m_options.push_back(option);
|
||||
}
|
||||
void append_widget(const widget_t widget) {
|
||||
m_extra_widgets.push_back(widget);
|
||||
}
|
||||
Line(wxString label, wxString tooltip) :
|
||||
label(label), label_tooltip(tooltip) {}
|
||||
|
||||
const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;}
|
||||
const std::vector<Option>& get_options() const { return m_options; }
|
||||
|
||||
private:
|
||||
std::vector<Option> m_options;//! {std::vector<Option>()};
|
||||
std::vector<widget_t> m_extra_widgets;//! {std::vector<widget_t>()};
|
||||
};
|
||||
|
||||
using t_optionfield_map = std::map<t_config_option_key, t_field>;
|
||||
|
||||
class OptionsGroup {
|
||||
public:
|
||||
const bool staticbox {true};
|
||||
const wxString title {wxString("")};
|
||||
size_t label_width {200};
|
||||
wxSizer* sizer {nullptr};
|
||||
column_t extra_column {nullptr};
|
||||
t_change m_on_change {nullptr};
|
||||
|
||||
wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
|
||||
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
|
||||
|
||||
/// Returns a copy of the pointer of the parent wxWindow.
|
||||
/// Accessor function is because users are not allowed to change the parent
|
||||
/// but defining it as const means a lot of const_casts to deal with wx functions.
|
||||
inline wxWindow* parent() const { return m_parent; }
|
||||
|
||||
void append_line(const Line& line);
|
||||
Line create_single_option_line(const Option& option) const;
|
||||
void append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
|
||||
|
||||
// return a non-owning pointer reference
|
||||
inline /*const*/ Field* get_field(t_config_option_key id) const { try { return m_fields.at(id).get(); } catch (std::out_of_range e) { return nullptr; } }
|
||||
bool set_value(t_config_option_key id, boost::any value) { try { m_fields.at(id)->set_value(value); return true; } catch (std::out_of_range e) { return false; } }
|
||||
boost::any get_value(t_config_option_key id) { boost::any out; try { out = m_fields.at(id)->get_value(); } catch (std::out_of_range e) { ; } return out; }
|
||||
|
||||
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
|
||||
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
|
||||
|
||||
OptionsGroup(wxWindow* _parent, wxString title) :
|
||||
m_parent(_parent), title(title) {
|
||||
sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
|
||||
auto num_columns = 1U;
|
||||
if (label_width != 0) num_columns++;
|
||||
if (extra_column != nullptr) num_columns++;
|
||||
m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0);
|
||||
static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL);
|
||||
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0);
|
||||
|
||||
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0: 5);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::map<t_config_option_key, Option> m_options;
|
||||
wxWindow* m_parent {nullptr};
|
||||
|
||||
/// Field list, contains unique_ptrs of the derived type.
|
||||
/// using types that need to know what it is beyond the public interface
|
||||
/// need to cast based on the related ConfigOptionDef.
|
||||
t_optionfield_map m_fields;
|
||||
bool m_disabled {false};
|
||||
wxGridSizer* m_grid_sizer {nullptr};
|
||||
|
||||
/// Generate a wxSizer or wxWindow from a configuration option
|
||||
/// Precondition: opt resolves to a known ConfigOption
|
||||
/// Postcondition: fields contains a wx gui object.
|
||||
const t_field& build_field(const t_config_option_key& id, const ConfigOptionDef& opt);
|
||||
const t_field& build_field(const t_config_option_key& id);
|
||||
const t_field& build_field(const Option& opt);
|
||||
|
||||
virtual void on_kill_focus (){};
|
||||
virtual void on_change_OG(t_config_option_key opt_id, boost::any value);
|
||||
};
|
||||
|
||||
class ConfigOptionsGroup: public OptionsGroup {
|
||||
public:
|
||||
ConfigOptionsGroup(wxWindow* parent, wxString title, DynamicPrintConfig* _config = nullptr) :
|
||||
OptionsGroup(parent, title), m_config(_config) {}
|
||||
|
||||
/// reference to libslic3r config, non-owning pointer (?).
|
||||
DynamicPrintConfig* m_config {nullptr};
|
||||
bool m_full_labels {0};
|
||||
std::map< std::string, std::pair<std::string, int> > m_opt_map;
|
||||
|
||||
Option get_option(const std::string opt_key, int opt_index = -1);
|
||||
Line create_single_option_line(const std::string title, int idx = -1) /*const*/{
|
||||
Option option = get_option(title, idx);
|
||||
return OptionsGroup::create_single_option_line(option);
|
||||
}
|
||||
void append_single_option_line(const Option& option) {
|
||||
OptionsGroup::append_single_option_line(option);
|
||||
}
|
||||
void append_single_option_line(const std::string title, int idx = -1)
|
||||
{
|
||||
Option option = get_option(title, idx);
|
||||
append_single_option_line(option);
|
||||
}
|
||||
|
||||
void on_change_OG(t_config_option_key opt_id, boost::any value) override;
|
||||
void on_kill_focus() override
|
||||
{
|
||||
reload_config();
|
||||
}
|
||||
void reload_config();
|
||||
boost::any config_value(std::string opt_key, int opt_index, bool deserialize);
|
||||
// return option value from config
|
||||
boost::any get_config_value(DynamicPrintConfig& config, std::string opt_key, int opt_index = -1);
|
||||
Field* get_fieldc(t_config_option_key opt_key, int opt_index);
|
||||
};
|
||||
|
||||
// Static text shown among the options.
|
||||
class ogStaticText :public wxStaticText{
|
||||
public:
|
||||
ogStaticText() {}
|
||||
ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){}
|
||||
~ogStaticText(){}
|
||||
|
||||
void SetText(wxString value);
|
||||
};
|
||||
|
||||
}}
|
1801
xs/src/slic3r/GUI/Tab.cpp
Normal file
1801
xs/src/slic3r/GUI/Tab.cpp
Normal file
File diff suppressed because it is too large
Load Diff
260
xs/src/slic3r/GUI/Tab.hpp
Normal file
260
xs/src/slic3r/GUI/Tab.hpp
Normal file
@ -0,0 +1,260 @@
|
||||
// The "Expert" tab at the right of the main tabbed window.
|
||||
//
|
||||
// This file implements following packages:
|
||||
// Slic3r::GUI::Tab;
|
||||
// Slic3r::GUI::Tab::Print;
|
||||
// Slic3r::GUI::Tab::Filament;
|
||||
// Slic3r::GUI::Tab::Printer;
|
||||
// Slic3r::GUI::Tab::Page
|
||||
// - Option page: For example, the Slic3r::GUI::Tab::Print has option pages "Layers and perimeters", "Infill", "Skirt and brim" ...
|
||||
// Slic3r::GUI::SavePresetWindow
|
||||
// - Dialog to select a new preset name to store the configuration.
|
||||
// Slic3r::GUI::Tab::Preset;
|
||||
// - Single preset item: name, file is default or external.
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/treectrl.h>
|
||||
#include <wx/imaglist.h>
|
||||
#include <wx/statbox.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "BedShapeDialog.hpp"
|
||||
|
||||
//!enum { ID_TAB_TREE = wxID_HIGHEST + 1 };
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
// Single Tab page containing a{ vsizer } of{ optgroups }
|
||||
// package Slic3r::GUI::Tab::Page;
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
class Page : public wxScrolledWindow
|
||||
{
|
||||
wxWindow* m_parent;
|
||||
wxString m_title;
|
||||
size_t m_iconID;
|
||||
wxBoxSizer* m_vsizer;
|
||||
public:
|
||||
Page(wxWindow* parent, const wxString title, const int iconID) :
|
||||
m_parent(parent),
|
||||
m_title(title),
|
||||
m_iconID(iconID)
|
||||
{
|
||||
Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
SetSizer(m_vsizer);
|
||||
}
|
||||
~Page(){}
|
||||
|
||||
public:
|
||||
std::vector <ConfigOptionsGroupShp> m_optgroups;
|
||||
DynamicPrintConfig* m_config;
|
||||
|
||||
wxBoxSizer* vsizer() const { return m_vsizer; }
|
||||
wxWindow* parent() const { return m_parent; }
|
||||
wxString title() const { return m_title; }
|
||||
size_t iconID() const { return m_iconID; }
|
||||
void set_config(DynamicPrintConfig* config_in) { m_config = config_in; }
|
||||
void reload_config();
|
||||
Field* get_field(t_config_option_key opt_key, int opt_index = -1) const;
|
||||
bool set_value(t_config_option_key opt_key, boost::any value);
|
||||
ConfigOptionsGroupShp new_optgroup(wxString title, int noncommon_label_width = -1);
|
||||
};
|
||||
|
||||
// Slic3r::GUI::Tab;
|
||||
|
||||
using PageShp = std::shared_ptr<Page>;
|
||||
class Tab: public wxPanel
|
||||
{
|
||||
wxNotebook* m_parent;
|
||||
protected:
|
||||
std::string m_name;
|
||||
const wxString m_title;
|
||||
wxBitmapComboBox* m_presets_choice;
|
||||
wxBitmapButton* m_btn_save_preset;
|
||||
wxBitmapButton* m_btn_delete_preset;
|
||||
wxBitmap* m_bmp_show_incompatible_presets;
|
||||
wxBitmap* m_bmp_hide_incompatible_presets;
|
||||
wxBitmapButton* m_btn_hide_incompatible_presets;
|
||||
wxBoxSizer* m_hsizer;
|
||||
wxBoxSizer* m_left_sizer;
|
||||
wxTreeCtrl* m_treectrl;
|
||||
wxImageList* m_icons;
|
||||
wxCheckBox* m_compatible_printers_checkbox;
|
||||
wxButton* m_compatible_printers_btn;
|
||||
|
||||
int m_icon_count;
|
||||
std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index in $self->{icons}.
|
||||
std::vector<PageShp> m_pages; // $self->{pages} = [];
|
||||
bool m_disable_tree_sel_changed_event;
|
||||
bool m_show_incompatible_presets;
|
||||
bool m_no_controller;
|
||||
|
||||
std::vector<std::string> m_reload_dependent_tabs = {};
|
||||
|
||||
// The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType.
|
||||
wxEventType m_event_value_change = 0;
|
||||
wxEventType m_event_presets_changed = 0;
|
||||
|
||||
public:
|
||||
PresetBundle* m_preset_bundle;
|
||||
bool m_show_btn_incompatible_presets;
|
||||
PresetCollection* m_presets;
|
||||
DynamicPrintConfig* m_config;
|
||||
|
||||
public:
|
||||
Tab() {}
|
||||
Tab(wxNotebook* parent, wxString title, const char* name, bool no_controller) :
|
||||
m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) {
|
||||
Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
||||
get_tabs_list().push_back(this);
|
||||
}
|
||||
~Tab() { delete_tab_from_list(this); }
|
||||
|
||||
wxWindow* parent() const { return m_parent; }
|
||||
wxString title() const { return m_title; }
|
||||
std::string name() const { return m_name; }
|
||||
|
||||
// Set the events to the callbacks posted to the main frame window (currently implemented in Perl).
|
||||
void set_event_value_change(wxEventType evt) { m_event_value_change = evt; }
|
||||
void set_event_presets_changed(wxEventType evt) { m_event_presets_changed = evt; }
|
||||
|
||||
void create_preset_tab(PresetBundle *preset_bundle);
|
||||
void load_current_preset();
|
||||
void rebuild_page_tree();
|
||||
void select_preset(std::string preset_name = "");
|
||||
bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, std::string new_printer_name = "");
|
||||
wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
|
||||
|
||||
void load_key_value(std::string opt_key, boost::any value);
|
||||
void reload_compatible_printers_widget();
|
||||
|
||||
void OnTreeSelChange(wxTreeEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
|
||||
void save_preset(std::string name = "");
|
||||
void delete_preset();
|
||||
void toggle_show_hide_incompatible();
|
||||
void update_show_hide_incompatible_button();
|
||||
void update_ui_from_settings();
|
||||
|
||||
PageShp add_options_page(wxString title, std::string icon, bool is_extruder_pages = false);
|
||||
|
||||
virtual void OnActivate(){}
|
||||
virtual void on_preset_loaded(){}
|
||||
virtual void build() = 0;
|
||||
virtual void update() = 0;
|
||||
void update_dirty();
|
||||
void update_tab_ui();
|
||||
void load_config(DynamicPrintConfig config);
|
||||
virtual void reload_config();
|
||||
Field* get_field(t_config_option_key opt_key, int opt_index = -1) const;
|
||||
bool set_value(t_config_option_key opt_key, boost::any value);
|
||||
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText);
|
||||
bool current_preset_is_dirty();
|
||||
DynamicPrintConfig* get_config() { return m_config; }
|
||||
PresetCollection* get_presets()
|
||||
{
|
||||
return m_presets;
|
||||
}
|
||||
std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; }
|
||||
|
||||
void on_value_change(std::string opt_key, boost::any value);
|
||||
|
||||
protected:
|
||||
void on_presets_changed();
|
||||
};
|
||||
|
||||
//Slic3r::GUI::Tab::Print;
|
||||
class TabPrint : public Tab
|
||||
{
|
||||
public:
|
||||
TabPrint() {}
|
||||
TabPrint(wxNotebook* parent, bool no_controller) :
|
||||
Tab(parent, _L("Print Settings"), "print", no_controller) {}
|
||||
~TabPrint(){}
|
||||
|
||||
ogStaticText* m_recommended_thin_wall_thickness_description_line;
|
||||
bool m_support_material_overhangs_queried = false;
|
||||
|
||||
void build() override;
|
||||
void reload_config() override;
|
||||
void update() override;
|
||||
void OnActivate() override;
|
||||
};
|
||||
|
||||
//Slic3r::GUI::Tab::Filament;
|
||||
class TabFilament : public Tab
|
||||
{
|
||||
ogStaticText* m_volumetric_speed_description_line;
|
||||
ogStaticText* m_cooling_description_line;
|
||||
public:
|
||||
TabFilament() {}
|
||||
TabFilament(wxNotebook* parent, bool no_controller) :
|
||||
Tab(parent, _L("Filament Settings"), "filament", no_controller) {}
|
||||
~TabFilament(){}
|
||||
|
||||
void build() override;
|
||||
void reload_config() override;
|
||||
void update() override;
|
||||
void OnActivate() override;
|
||||
};
|
||||
|
||||
//Slic3r::GUI::Tab::Printer;
|
||||
class TabPrinter : public Tab
|
||||
{
|
||||
bool m_is_disabled_button_browse;
|
||||
bool m_is_user_agent;
|
||||
// similar event by clicking Buttons "Browse" & "Test"
|
||||
wxEventType m_event_button_browse = 0;
|
||||
wxEventType m_event_button_test = 0;
|
||||
public:
|
||||
wxButton* m_serial_test_btn;
|
||||
wxButton* m_octoprint_host_test_btn;
|
||||
|
||||
size_t m_extruders_count;
|
||||
std::vector<PageShp> m_extruder_pages;
|
||||
|
||||
TabPrinter() {}
|
||||
TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) :
|
||||
Tab(parent, _L("Printer Settings"), "printer", no_controller),
|
||||
m_is_disabled_button_browse(is_disabled_btn_browse),
|
||||
m_is_user_agent(is_user_agent) {}
|
||||
~TabPrinter(){}
|
||||
|
||||
void build() override;
|
||||
void update() override;
|
||||
void update_serial_ports();
|
||||
void extruders_count_changed(size_t extruders_count);
|
||||
void build_extruder_pages();
|
||||
void on_preset_loaded() override;
|
||||
|
||||
// Set the events to the callbacks posted to the main frame window (currently implemented in Perl).
|
||||
void set_event_button_browse(wxEventType evt) { m_event_button_browse = evt; }
|
||||
void set_event_button_test(wxEventType evt) { m_event_button_test = evt; }
|
||||
};
|
||||
|
||||
class SavePresetWindow :public wxDialog
|
||||
{
|
||||
public:
|
||||
SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _L("Save preset")){}
|
||||
~SavePresetWindow(){}
|
||||
|
||||
std::string m_chosen_name;
|
||||
wxComboBox* m_combo;
|
||||
|
||||
void build(wxString title, std::string default_name, std::vector<std::string> &values);
|
||||
void accept();
|
||||
std::string get_name() { return m_chosen_name; }
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
19
xs/src/slic3r/GUI/TabIface.cpp
Normal file
19
xs/src/slic3r/GUI/TabIface.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "TabIface.hpp"
|
||||
#include "Tab.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void TabIface::load_current_preset() { m_tab->load_current_preset(); }
|
||||
void TabIface::update_tab_ui() { m_tab->update_tab_ui(); }
|
||||
void TabIface::update_ui_from_settings() { m_tab->update_ui_from_settings();}
|
||||
void TabIface::select_preset(char* name) { m_tab->select_preset(name);}
|
||||
void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*config);}
|
||||
void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); }
|
||||
bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();}
|
||||
void TabIface::OnActivate() { return m_tab->OnActivate();}
|
||||
std::string TabIface::title() { return m_tab->title().ToStdString();}
|
||||
DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); }
|
||||
PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; }
|
||||
std::vector<std::string> TabIface::get_dependent_tabs() { return m_tab->get_dependent_tabs(); }
|
||||
|
||||
}; // namespace Slic3r
|
35
xs/src/slic3r/GUI/TabIface.hpp
Normal file
35
xs/src/slic3r/GUI/TabIface.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
class DynamicPrintConfig;
|
||||
class PresetCollection;
|
||||
|
||||
namespace GUI {
|
||||
class Tab;
|
||||
}
|
||||
|
||||
class TabIface {
|
||||
public:
|
||||
TabIface() : m_tab(nullptr) {}
|
||||
TabIface(GUI::Tab *tab) : m_tab(tab) {}
|
||||
// TabIface(const TabIface &rhs) : m_tab(rhs.m_tab) {}
|
||||
|
||||
void load_current_preset();
|
||||
void update_tab_ui();
|
||||
void update_ui_from_settings();
|
||||
void select_preset(char* name);
|
||||
std::string title();
|
||||
void load_config(DynamicPrintConfig* config);
|
||||
void load_key_value(char* opt_key, char* value);
|
||||
bool current_preset_is_dirty();
|
||||
void OnActivate();
|
||||
DynamicPrintConfig* get_config();
|
||||
PresetCollection* get_presets();
|
||||
std::vector<std::string> get_dependent_tabs();
|
||||
|
||||
protected:
|
||||
GUI::Tab *m_tab;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
16
xs/src/slic3r/GUI/Widget.hpp
Normal file
16
xs/src/slic3r/GUI/Widget.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef WIDGET_HPP
|
||||
#define WIDGET_HPP
|
||||
#include <wx/wxprec.h>
|
||||
#ifndef WX_PRECOM
|
||||
#include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
class Widget {
|
||||
protected:
|
||||
wxSizer* _sizer;
|
||||
public:
|
||||
Widget(): _sizer(nullptr) { }
|
||||
bool valid() const { return _sizer != nullptr; }
|
||||
wxSizer* sizer() const { return _sizer; }
|
||||
};
|
||||
#endif
|
25
xs/src/slic3r/GUI/wxinit.h
Normal file
25
xs/src/slic3r/GUI/wxinit.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef slic3r_wxinit_hpp_
|
||||
#define slic3r_wxinit_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
// Perl redefines a _ macro, so we undef this one
|
||||
#undef _
|
||||
|
||||
// We do want to use translation however, so define it as __ so we can do a find/replace
|
||||
// later when we no longer need to undef _
|
||||
#define __(s) wxGetTranslation((s))
|
||||
|
||||
// legacy macros
|
||||
// https://wiki.wxwidgets.org/EventTypes_and_Event-Table_Macros
|
||||
#ifndef wxEVT_BUTTON
|
||||
#define wxEVT_BUTTON wxEVT_COMMAND_BUTTON_CLICKED
|
||||
#endif
|
||||
|
||||
#ifndef wxEVT_HTML_LINK_CLICKED
|
||||
#define wxEVT_HTML_LINK_CLICKED wxEVT_COMMAND_HTML_LINK_CLICKED
|
||||
#endif
|
||||
|
||||
#endif
|
@ -32,15 +32,26 @@ void set_main_frame(SV *ui)
|
||||
void set_tab_panel(SV *ui)
|
||||
%code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %};
|
||||
|
||||
void add_debug_menu(SV *ui)
|
||||
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar")); %};
|
||||
void add_debug_menu(SV *ui, int event_language_change)
|
||||
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
|
||||
|
||||
void create_preset_tab(const char *name)
|
||||
%code%{ Slic3r::GUI::create_preset_tab(name); %};
|
||||
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
||||
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
||||
int event_value_change, int event_presets_changed,
|
||||
int event_button_browse, int event_button_test)
|
||||
%code%{ Slic3r::GUI::create_preset_tabs(preset_bundle, app_config, no_controller,
|
||||
is_disabled_button_browse, is_user_agent,
|
||||
event_value_change, event_presets_changed,
|
||||
event_button_browse, event_button_test); %};
|
||||
|
||||
Ref<TabIface> get_preset_tab(char *name)
|
||||
%code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %};
|
||||
|
||||
bool load_language()
|
||||
%code%{ RETVAL=Slic3r::GUI::load_language(); %};
|
||||
|
||||
void create_combochecklist(SV *ui, std::string text, std::string items, bool initial_value)
|
||||
%code%{ Slic3r::GUI::create_combochecklist((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl"), text, items, initial_value); %};
|
||||
|
||||
int combochecklist_get_flags(SV *ui)
|
||||
%code%{ RETVAL=Slic3r::GUI::combochecklist_get_flags((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl")); %};
|
||||
|
23
xs/xsp/GUI_Tab.xsp
Normal file
23
xs/xsp/GUI_Tab.xsp
Normal file
@ -0,0 +1,23 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "slic3r/GUI/TabIface.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::GUI::Tab2} class TabIface {
|
||||
TabIface();
|
||||
~TabIface();
|
||||
void load_current_preset();
|
||||
void update_tab_ui();
|
||||
void update_ui_from_settings();
|
||||
void select_preset(char* name);
|
||||
void load_config(DynamicPrintConfig* config);
|
||||
bool current_preset_is_dirty();
|
||||
void load_key_value(char* opt_key, char* value);
|
||||
void OnActivate();
|
||||
std::string title();
|
||||
Ref<DynamicPrintConfig> get_config();
|
||||
Ref<PresetCollection> get_presets();
|
||||
std::vector<std::string> get_dependent_tabs();
|
||||
};
|
@ -54,6 +54,12 @@ set_var_dir(dir)
|
||||
CODE:
|
||||
Slic3r::set_var_dir(dir);
|
||||
|
||||
void
|
||||
set_local_dir(dir)
|
||||
char *dir;
|
||||
CODE:
|
||||
Slic3r::set_local_dir(dir);
|
||||
|
||||
char*
|
||||
var_dir()
|
||||
CODE:
|
||||
|
@ -233,6 +233,8 @@ PresetBundle* O_OBJECT_SLIC3R
|
||||
Ref<PresetBundle> O_OBJECT_SLIC3R_T
|
||||
PresetHints* O_OBJECT_SLIC3R
|
||||
Ref<PresetHints> O_OBJECT_SLIC3R_T
|
||||
TabIface* O_OBJECT_SLIC3R
|
||||
Ref<TabIface> O_OBJECT_SLIC3R_T
|
||||
|
||||
Axis T_UV
|
||||
ExtrusionLoopRole T_UV
|
||||
|
@ -210,6 +210,8 @@
|
||||
%typemap{Ref<PresetBundle>}{simple};
|
||||
%typemap{PresetHints*};
|
||||
%typemap{Ref<PresetHints>}{simple};
|
||||
%typemap{TabIface*};
|
||||
%typemap{Ref<TabIface>}{simple};
|
||||
|
||||
%typemap{PrintRegionPtrs*};
|
||||
%typemap{PrintObjectPtrs*};
|
||||
|
Loading…
Reference in New Issue
Block a user