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.
|
# Let the XS module know where the GUI resources reside.
|
||||||
set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources'));
|
set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources'));
|
||||||
set_var_dir(resources_dir() . "/icons");
|
set_var_dir(resources_dir() . "/icons");
|
||||||
|
set_local_dir(resources_dir() . "/localization/");
|
||||||
|
|
||||||
use Moo 1.003001;
|
use Moo 1.003001;
|
||||||
|
|
||||||
|
@ -68,6 +68,10 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
|||||||
$medium_font->SetPointSize(12);
|
$medium_font->SetPointSize(12);
|
||||||
our $grey = Wx::Colour->new(200,200,200);
|
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 {
|
sub OnInit {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
@ -81,6 +85,7 @@ sub OnInit {
|
|||||||
# Mac: "~/Library/Application Support/Slic3r"
|
# Mac: "~/Library/Application Support/Slic3r"
|
||||||
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
||||||
Slic3r::GUI::set_wxapp($self);
|
Slic3r::GUI::set_wxapp($self);
|
||||||
|
Slic3r::GUI::load_language();
|
||||||
|
|
||||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||||
$self->{app_config} = Slic3r::GUI::AppConfig->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.
|
# 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_controller => $self->{app_config}->get('no_controller'),
|
||||||
no_plater => $no_plater,
|
no_plater => $no_plater,
|
||||||
|
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||||
);
|
);
|
||||||
$self->SetTopWindow($frame);
|
$self->SetTopWindow($frame);
|
||||||
|
|
||||||
EVT_IDLE($frame, sub {
|
#EVT_IDLE($frame, sub {
|
||||||
|
EVT_IDLE($self->{mainframe}, sub {
|
||||||
while (my $cb = shift @cb) {
|
while (my $cb = shift @cb) {
|
||||||
$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;
|
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 {
|
sub about {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $about = Slic3r::GUI::AboutDialog->new(undef);
|
my $about = Slic3r::GUI::AboutDialog->new(undef);
|
||||||
|
@ -11,13 +11,23 @@ use List::Util qw(min first);
|
|||||||
use Slic3r::Geometry qw(X Y);
|
use Slic3r::Geometry qw(X Y);
|
||||||
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
|
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
|
||||||
:font :icon wxTheApp);
|
: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';
|
use base 'Wx::Frame';
|
||||||
|
|
||||||
our $qs_last_input_file;
|
our $qs_last_input_file;
|
||||||
our $qs_last_output_file;
|
our $qs_last_output_file;
|
||||||
our $last_config;
|
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 {
|
sub new {
|
||||||
my ($class, %params) = @_;
|
my ($class, %params) = @_;
|
||||||
|
|
||||||
@ -37,6 +47,7 @@ sub new {
|
|||||||
$self->{no_controller} = $params{no_controller};
|
$self->{no_controller} = $params{no_controller};
|
||||||
$self->{no_plater} = $params{no_plater};
|
$self->{no_plater} = $params{no_plater};
|
||||||
$self->{loaded} = 0;
|
$self->{loaded} = 0;
|
||||||
|
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||||
|
|
||||||
# initialize tabpanel and menubar
|
# initialize tabpanel and menubar
|
||||||
$self->_init_tabpanel;
|
$self->_init_tabpanel;
|
||||||
@ -106,33 +117,41 @@ sub _init_tabpanel {
|
|||||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$self->{options_tabs} = {};
|
|
||||||
|
|
||||||
for my $tab_name (qw(print filament printer)) {
|
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
|
||||||
my $tab;
|
# The following event is emited by the C++ Tab implementation on config value change.
|
||||||
$tab = $self->{options_tabs}{$tab_name} = ("Slic3r::GUI::Tab::" . ucfirst $tab_name)->new(
|
EVT_COMMAND($self, -1, $VALUE_CHANGE_EVENT, sub {
|
||||||
$panel,
|
my ($self, $event) = @_;
|
||||||
no_controller => $self->{no_controller});
|
my $str = $event->GetString;
|
||||||
# Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value.
|
my ($opt_key, $name) = ($str =~ /(.*) (.*)/);
|
||||||
$tab->on_value_change(sub {
|
#print "VALUE_CHANGE_EVENT: ", $opt_key, "\n";
|
||||||
my ($opt_key, $value) = @_;
|
my $tab = Slic3r::GUI::get_preset_tab($name);
|
||||||
my $config = $tab->{presets}->get_current_preset->config;
|
my $config = $tab->get_config;
|
||||||
if ($self->{plater}) {
|
if ($self->{plater}) {
|
||||||
$self->{plater}->on_config_change($config); # propagate config change events to the 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';
|
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};
|
# 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.
|
# The following event is emited by the C++ Tab implementation on preset selection,
|
||||||
$tab->on_presets_changed(sub {
|
# or when the preset's "modified" status changes.
|
||||||
if ($self->{plater}) {
|
EVT_COMMAND($self, -1, $PRESETS_CHANGED_EVENT, sub {
|
||||||
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
|
my ($self, $event) = @_;
|
||||||
$self->{plater}->update_presets($tab_name, @_);
|
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') {
|
if ($tab_name eq 'printer') {
|
||||||
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
|
# 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)) {
|
for my $tab_name_other (qw(print filament)) {
|
||||||
# If the printer tells us that the print or filament preset has been switched or invalidated,
|
# 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.
|
# 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;
|
$self->{options_tabs}{$tab_name_other}->$update_action;
|
||||||
}
|
}
|
||||||
# Update the controller printers.
|
# 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;
|
# The following event is emited by the C++ Tab implementation ,
|
||||||
$panel->AddPage($tab, $tab->title);
|
# 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.
|
# look for devices
|
||||||
# Slic3r::GUI::create_preset_tab("print");
|
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}) {
|
if ($self->{plater}) {
|
||||||
$self->{plater}->on_select_preset(sub {
|
$self->{plater}->on_select_preset(sub {
|
||||||
my ($group, $name) = @_;
|
my ($group, $name) = @_;
|
||||||
$self->{options_tabs}{$group}->select_preset($name);
|
$self->{options_tabs}{$group}->select_preset($name);
|
||||||
});
|
});
|
||||||
# load initial config
|
# load initial config
|
||||||
my $full_config = wxTheApp->{preset_bundle}->full_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($self->{object_menu}, "&Object") if $self->{object_menu};
|
||||||
$menubar->Append($windowMenu, "&Window");
|
$menubar->Append($windowMenu, "&Window");
|
||||||
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
|
$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");
|
$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);
|
$self->SetMenuBar($menubar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,7 +738,7 @@ sub check_unsaved_changes {
|
|||||||
|
|
||||||
my @dirty = ();
|
my @dirty = ();
|
||||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
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) {
|
if (@dirty) {
|
||||||
|
@ -158,7 +158,9 @@ sub _build_field {
|
|||||||
|
|
||||||
my $opt_id = $opt->opt_id;
|
my $opt_id = $opt->opt_id;
|
||||||
my $on_change = sub {
|
my $on_change = sub {
|
||||||
|
#! This function will be called from Field.
|
||||||
my ($opt_id, $value) = @_;
|
my ($opt_id, $value) = @_;
|
||||||
|
#! Call OptionGroup._on_change(...)
|
||||||
$self->_on_change($opt_id, $value)
|
$self->_on_change($opt_id, $value)
|
||||||
unless $self->_disabled;
|
unless $self->_disabled;
|
||||||
};
|
};
|
||||||
@ -213,6 +215,8 @@ sub _build_field {
|
|||||||
}
|
}
|
||||||
return undef if !$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_change($on_change);
|
||||||
$field->on_kill_focus($on_kill_focus);
|
$field->on_kill_focus($on_kill_focus);
|
||||||
$self->_fields->{$opt_id} = $field;
|
$self->_fields->{$opt_id} = $field;
|
||||||
|
@ -93,6 +93,8 @@ sub export_gcode {
|
|||||||
$self->status_cb->(95, "Running post-processing scripts");
|
$self->status_cb->(95, "Running post-processing scripts");
|
||||||
$self->config->setenv;
|
$self->config->setenv;
|
||||||
for my $script (@{$self->config->post_process}) {
|
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;
|
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
|
||||||
# -x doesn't return true on Windows except for .exe files
|
# -x doesn't return true on Windows except for .exe files
|
||||||
if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) {
|
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/PresetHints.hpp
|
||||||
${LIBDIR}/slic3r/GUI/GUI.cpp
|
${LIBDIR}/slic3r/GUI/GUI.cpp
|
||||||
${LIBDIR}/slic3r/GUI/GUI.hpp
|
${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.cpp
|
||||||
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
||||||
)
|
)
|
||||||
@ -307,6 +319,7 @@ set(XS_XSP_FILES
|
|||||||
${XSP_DIR}/GUI_AppConfig.xsp
|
${XSP_DIR}/GUI_AppConfig.xsp
|
||||||
${XSP_DIR}/GUI_3DScene.xsp
|
${XSP_DIR}/GUI_3DScene.xsp
|
||||||
${XSP_DIR}/GUI_Preset.xsp
|
${XSP_DIR}/GUI_Preset.xsp
|
||||||
|
${XSP_DIR}/GUI_Tab.xsp
|
||||||
${XSP_DIR}/Layer.xsp
|
${XSP_DIR}/Layer.xsp
|
||||||
${XSP_DIR}/Line.xsp
|
${XSP_DIR}/Line.xsp
|
||||||
${XSP_DIR}/Model.xsp
|
${XSP_DIR}/Model.xsp
|
||||||
|
@ -283,6 +283,7 @@ for my $class (qw(
|
|||||||
Slic3r::GUI::_3DScene::GLVolume
|
Slic3r::GUI::_3DScene::GLVolume
|
||||||
Slic3r::GUI::Preset
|
Slic3r::GUI::Preset
|
||||||
Slic3r::GUI::PresetCollection
|
Slic3r::GUI::PresetCollection
|
||||||
|
Slic3r::GUI::Tab2
|
||||||
Slic3r::Layer
|
Slic3r::Layer
|
||||||
Slic3r::Layer::Region
|
Slic3r::Layer::Region
|
||||||
Slic3r::Layer::Support
|
Slic3r::Layer::Support
|
||||||
|
@ -24,6 +24,12 @@ public:
|
|||||||
explicit Polygon(const Points &points): MultiPoint(points) {}
|
explicit Polygon(const Points &points): MultiPoint(points) {}
|
||||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||||
Polygon(Polygon &&other) : MultiPoint(std::move(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=(const Polygon &other) { points = other.points; return *this; }
|
||||||
Polygon& operator=(Polygon &&other) { points = std::move(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(Polyline &&other) : MultiPoint(std::move(other.points)) {}
|
||||||
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
|
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
|
||||||
Polyline& operator=(Polyline &&other) { points = std::move(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 Point &point) { this->points.push_back(point); }
|
||||||
void append(const Points &src) { this->append(src.begin(), src.end()); }
|
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.
|
// Return a full path to the resources directory.
|
||||||
const std::string& resources_dir();
|
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.
|
// Set a path with preset files.
|
||||||
void set_data_dir(const std::string &path);
|
void set_data_dir(const std::string &path);
|
||||||
// Return a full path to the GUI resource files.
|
// Return a full path to the GUI resource files.
|
||||||
|
@ -103,6 +103,18 @@ const std::string& resources_dir()
|
|||||||
return g_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;
|
static std::string g_data_dir;
|
||||||
|
|
||||||
void set_data_dir(const std::string &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(PresetCollection, "GUI::PresetCollection");
|
||||||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||||
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
||||||
|
REGISTER_CLASS(TabIface, "GUI::Tab2");
|
||||||
|
|
||||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
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/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
@ -12,21 +13,39 @@
|
|||||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
#include <Windows.h>
|
#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"
|
#include "boost/nowide/convert.hpp"
|
||||||
#pragma comment(lib, "user32.lib")
|
#pragma comment(lib, "user32.lib")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
|
#include <wx/config.h>
|
||||||
|
#include <wx/dir.h>
|
||||||
|
#include <wx/filename.h>
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/menu.h>
|
#include <wx/menu.h>
|
||||||
#include <wx/notebook.h>
|
#include <wx/notebook.h>
|
||||||
#include <wx/panel.h>
|
#include <wx/panel.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/combo.h>
|
#include <wx/combo.h>
|
||||||
|
#include <wx/window.h>
|
||||||
|
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
|
|
||||||
|
#include "Tab.hpp"
|
||||||
|
#include "TabIface.hpp"
|
||||||
|
#include "AppConfig.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
@ -153,6 +172,10 @@ wxApp *g_wxApp = nullptr;
|
|||||||
wxFrame *g_wxMainFrame = nullptr;
|
wxFrame *g_wxMainFrame = nullptr;
|
||||||
wxNotebook *g_wxTabPanel = nullptr;
|
wxNotebook *g_wxTabPanel = nullptr;
|
||||||
|
|
||||||
|
std::vector<Tab *> g_tabs_list;
|
||||||
|
|
||||||
|
wxLocale* g_wxLocale;
|
||||||
|
|
||||||
void set_wxapp(wxApp *app)
|
void set_wxapp(wxApp *app)
|
||||||
{
|
{
|
||||||
g_wxApp = app;
|
g_wxApp = app;
|
||||||
@ -168,25 +191,292 @@ void set_tab_panel(wxNotebook *tab_panel)
|
|||||||
g_wxTabPanel = tab_panel;
|
g_wxTabPanel = tab_panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_debug_menu(wxMenuBar *menu)
|
std::vector<Tab *>& get_tabs_list()
|
||||||
{
|
{
|
||||||
#if 0
|
return g_tabs_list;
|
||||||
auto debug_menu = new wxMenu();
|
|
||||||
debug_menu->Append(wxWindow::NewControlId(1), "Some debug");
|
|
||||||
menu->Append(debug_menu, _T("&Debug"));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
bool ret = true;
|
||||||
// Vertical sizer to hold the choice menu and the rest of the page.
|
if (find(g_tabs_list.begin(), g_tabs_list.end(), tab) == g_tabs_list.end())
|
||||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
ret = false;
|
||||||
sizer->SetSizeHints(panel);
|
return ret;
|
||||||
panel->SetSizer(sizer);
|
}
|
||||||
auto *button = new wxButton(panel, wxID_ANY, "Hello World", wxDefaultPosition, wxDefaultSize, 0);
|
|
||||||
sizer->Add(button, 0, 0, 0);
|
void delete_tab_from_list(Tab* tab)
|
||||||
g_wxTabPanel->AddPage(panel, name);
|
{
|
||||||
|
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)
|
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
|
||||||
|
@ -3,14 +3,50 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "Config.hpp"
|
||||||
|
|
||||||
class wxApp;
|
class wxApp;
|
||||||
class wxFrame;
|
class wxFrame;
|
||||||
|
class wxWindow;
|
||||||
class wxMenuBar;
|
class wxMenuBar;
|
||||||
class wxNotebook;
|
class wxNotebook;
|
||||||
class wxComboCtrl;
|
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 disable_screensaver();
|
||||||
void enable_screensaver();
|
void enable_screensaver();
|
||||||
@ -23,10 +59,34 @@ void set_wxapp(wxApp *app);
|
|||||||
void set_main_frame(wxFrame *main_frame);
|
void set_main_frame(wxFrame *main_frame);
|
||||||
void set_tab_panel(wxNotebook *tab_panel);
|
void set_tab_panel(wxNotebook *tab_panel);
|
||||||
|
|
||||||
void add_debug_menu(wxMenuBar *menu);
|
void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
||||||
// Create a new preset tab (print, filament or printer),
|
// 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.
|
// 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.
|
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
|
||||||
// Items are all initialized to the given value.
|
// 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)
|
void set_tab_panel(SV *ui)
|
||||||
%code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %};
|
%code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %};
|
||||||
|
|
||||||
void add_debug_menu(SV *ui)
|
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")); %};
|
%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)
|
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
||||||
%code%{ Slic3r::GUI::create_preset_tab(name); %};
|
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)
|
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); %};
|
%code%{ Slic3r::GUI::create_combochecklist((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl"), text, items, initial_value); %};
|
||||||
|
|
||||||
int combochecklist_get_flags(SV *ui)
|
int combochecklist_get_flags(SV *ui)
|
||||||
%code%{ RETVAL=Slic3r::GUI::combochecklist_get_flags((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl")); %};
|
%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:
|
CODE:
|
||||||
Slic3r::set_var_dir(dir);
|
Slic3r::set_var_dir(dir);
|
||||||
|
|
||||||
|
void
|
||||||
|
set_local_dir(dir)
|
||||||
|
char *dir;
|
||||||
|
CODE:
|
||||||
|
Slic3r::set_local_dir(dir);
|
||||||
|
|
||||||
char*
|
char*
|
||||||
var_dir()
|
var_dir()
|
||||||
CODE:
|
CODE:
|
||||||
|
@ -233,6 +233,8 @@ PresetBundle* O_OBJECT_SLIC3R
|
|||||||
Ref<PresetBundle> O_OBJECT_SLIC3R_T
|
Ref<PresetBundle> O_OBJECT_SLIC3R_T
|
||||||
PresetHints* O_OBJECT_SLIC3R
|
PresetHints* O_OBJECT_SLIC3R
|
||||||
Ref<PresetHints> O_OBJECT_SLIC3R_T
|
Ref<PresetHints> O_OBJECT_SLIC3R_T
|
||||||
|
TabIface* O_OBJECT_SLIC3R
|
||||||
|
Ref<TabIface> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
Axis T_UV
|
Axis T_UV
|
||||||
ExtrusionLoopRole T_UV
|
ExtrusionLoopRole T_UV
|
||||||
|
@ -210,6 +210,8 @@
|
|||||||
%typemap{Ref<PresetBundle>}{simple};
|
%typemap{Ref<PresetBundle>}{simple};
|
||||||
%typemap{PresetHints*};
|
%typemap{PresetHints*};
|
||||||
%typemap{Ref<PresetHints>}{simple};
|
%typemap{Ref<PresetHints>}{simple};
|
||||||
|
%typemap{TabIface*};
|
||||||
|
%typemap{Ref<TabIface>}{simple};
|
||||||
|
|
||||||
%typemap{PrintRegionPtrs*};
|
%typemap{PrintRegionPtrs*};
|
||||||
%typemap{PrintObjectPtrs*};
|
%typemap{PrintObjectPtrs*};
|
||||||
|
Loading…
Reference in New Issue
Block a user