Merge branch 'master' of https://github.com/prusa3d/Slic3r into objects_centering
This commit is contained in:
commit
6107b7fd2f
9 changed files with 103 additions and 149 deletions
|
@ -38,6 +38,7 @@ struct FillParams
|
||||||
// in this case we don't try to make more continuous paths
|
// in this case we don't try to make more continuous paths
|
||||||
bool complete;
|
bool complete;
|
||||||
};
|
};
|
||||||
|
static_assert(std::is_trivially_copyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||||
|
|
||||||
class Fill
|
class Fill
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
|
@ -91,6 +92,8 @@ struct SlicingParameters
|
||||||
coordf_t object_print_z_min;
|
coordf_t object_print_z_min;
|
||||||
coordf_t object_print_z_max;
|
coordf_t object_print_z_max;
|
||||||
};
|
};
|
||||||
|
static_assert(std::is_trivially_copyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
|
||||||
|
|
||||||
|
|
||||||
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
|
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
|
||||||
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
|
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
|
||||||
|
|
|
@ -348,50 +348,6 @@ bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void save_window_size(wxTopLevelWindow *window, const std::string &name)
|
|
||||||
{
|
|
||||||
const wxSize size = window->GetSize();
|
|
||||||
const wxPoint pos = window->GetPosition();
|
|
||||||
const auto maximized = window->IsMaximized() ? "1" : "0";
|
|
||||||
|
|
||||||
get_app_config()->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
|
|
||||||
get_app_config()->set((boost::format("window_%1%_maximized") % name).str(), maximized);
|
|
||||||
}
|
|
||||||
|
|
||||||
void restore_window_size(wxTopLevelWindow *window, const std::string &name)
|
|
||||||
{
|
|
||||||
// XXX: This still doesn't behave nicely in some situations (mostly on Linux).
|
|
||||||
// The problem is that it's hard to obtain window position with respect to screen geometry reliably
|
|
||||||
// from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
|
|
||||||
// it's actually visible. I suspect this has something to do with window initialization (maybe we
|
|
||||||
// restore window geometry too early), but haven't yet found a workaround.
|
|
||||||
|
|
||||||
const auto display_idx = wxDisplay::GetFromWindow(window);
|
|
||||||
if (display_idx == wxNOT_FOUND) { return; }
|
|
||||||
|
|
||||||
const auto display = wxDisplay(display_idx).GetClientArea();
|
|
||||||
std::vector<std::string> pair;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const auto key_size = (boost::format("window_%1%_size") % name).str();
|
|
||||||
if (get_app_config()->has(key_size)) {
|
|
||||||
if (unescape_strings_cstyle(get_app_config()->get(key_size), pair) && pair.size() == 2) {
|
|
||||||
auto width = boost::lexical_cast<int>(pair[0]);
|
|
||||||
auto height = boost::lexical_cast<int>(pair[1]);
|
|
||||||
|
|
||||||
window->SetSize(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(const boost::bad_lexical_cast &) {}
|
|
||||||
|
|
||||||
// Maximizing should be the last thing to do.
|
|
||||||
// This ensure the size and position are sane when the user un-maximizes the window.
|
|
||||||
const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
|
|
||||||
if (get_app_config()->get(key_maximized) == "1") {
|
|
||||||
window->Maximize(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void about()
|
void about()
|
||||||
{
|
{
|
||||||
AboutDialog dlg;
|
AboutDialog dlg;
|
||||||
|
|
|
@ -70,11 +70,6 @@ boost::filesystem::path into_path(const wxString &str);
|
||||||
// Returns the dimensions of the screen on which the main frame is displayed
|
// Returns the dimensions of the screen on which the main frame is displayed
|
||||||
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
|
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
|
||||||
|
|
||||||
// Save window size and maximized status into AppConfig
|
|
||||||
void save_window_size(wxTopLevelWindow *window, const std::string &name);
|
|
||||||
// Restore the above
|
|
||||||
void restore_window_size(wxTopLevelWindow *window, const std::string &name);
|
|
||||||
|
|
||||||
// Display an About dialog
|
// Display an About dialog
|
||||||
extern void about();
|
extern void about();
|
||||||
// Ask the destop to open the datadir using the default file explorer.
|
// Ask the destop to open the datadir using the default file explorer.
|
||||||
|
|
|
@ -143,7 +143,6 @@ bool GUI_App::OnInit()
|
||||||
init_fonts();
|
init_fonts();
|
||||||
|
|
||||||
// application frame
|
// application frame
|
||||||
std::cerr << "Creating main frame..." << std::endl;
|
|
||||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||||
wxImage::AddHandler(new wxPNGHandler());
|
wxImage::AddHandler(new wxPNGHandler());
|
||||||
mainframe = new MainFrame();
|
mainframe = new MainFrame();
|
||||||
|
@ -165,27 +164,8 @@ bool GUI_App::OnInit()
|
||||||
// to correct later layouts
|
// to correct later layouts
|
||||||
});
|
});
|
||||||
|
|
||||||
// This makes CallAfter() work
|
|
||||||
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
||||||
{
|
{
|
||||||
std::function<void()> cur_cb{ nullptr };
|
|
||||||
// try to get the mutex. If we can't, just skip this idle event and get the next one.
|
|
||||||
if (!callback_register.try_lock()) return;
|
|
||||||
// pop callback
|
|
||||||
if (m_cb.size() != 0) {
|
|
||||||
cur_cb = m_cb.top();
|
|
||||||
m_cb.pop();
|
|
||||||
}
|
|
||||||
// unlock mutex
|
|
||||||
this->callback_register.unlock();
|
|
||||||
|
|
||||||
try { // call the function if it's not nullptr;
|
|
||||||
if (cur_cb != nullptr) cur_cb();
|
|
||||||
}
|
|
||||||
catch (std::exception& e) {
|
|
||||||
std::cerr << "Exception thrown: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app_config->dirty())
|
if (app_config->dirty())
|
||||||
app_config->save();
|
app_config->save();
|
||||||
|
|
||||||
|
@ -370,6 +350,33 @@ void GUI_App::update_ui_from_settings()
|
||||||
mainframe->update_ui_from_settings();
|
mainframe->update_ui_from_settings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI_App::persist_window_geometry(wxTopLevelWindow *window)
|
||||||
|
{
|
||||||
|
const std::string name = into_u8(window->GetName());
|
||||||
|
|
||||||
|
window->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent &event) {
|
||||||
|
window_pos_save(window, name);
|
||||||
|
event.Skip();
|
||||||
|
});
|
||||||
|
|
||||||
|
window_pos_restore(window, name);
|
||||||
|
#ifdef _WIN32
|
||||||
|
// On windows, the wxEVT_SHOW is not received if the window is created maximized
|
||||||
|
// cf. https://groups.google.com/forum/#!topic/wx-users/c7ntMt6piRI
|
||||||
|
// so we sanitize the position right away
|
||||||
|
window_pos_sanitize(window);
|
||||||
|
#else
|
||||||
|
// On other platforms on the other hand it's needed to wait before the window is actually on screen
|
||||||
|
// and some initial round of events is complete otherwise position / display index is not reported correctly.
|
||||||
|
window->Bind(wxEVT_SHOW, [=](wxShowEvent &event) {
|
||||||
|
CallAfter([=]() {
|
||||||
|
window_pos_sanitize(window);
|
||||||
|
});
|
||||||
|
event.Skip();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void GUI_App::load_project(wxWindow *parent, wxString& input_file)
|
void GUI_App::load_project(wxWindow *parent, wxString& input_file)
|
||||||
{
|
{
|
||||||
input_file.Clear();
|
input_file.Clear();
|
||||||
|
@ -394,55 +401,6 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files)
|
||||||
dialog.GetPaths(input_files);
|
dialog.GetPaths(input_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUI_App::CallAfter(std::function<void()> cb)
|
|
||||||
{
|
|
||||||
// set mutex
|
|
||||||
callback_register.lock();
|
|
||||||
// push function onto stack
|
|
||||||
m_cb.emplace(cb);
|
|
||||||
// unset mutex
|
|
||||||
callback_register.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name)
|
|
||||||
{
|
|
||||||
if (name.empty()) { return; }
|
|
||||||
const auto config_key = (boost::format("window_%1%") % name).str();
|
|
||||||
|
|
||||||
WindowMetrics metrics = WindowMetrics::from_window(window);
|
|
||||||
app_config->set(config_key, metrics.serialize());
|
|
||||||
app_config->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &name)
|
|
||||||
{
|
|
||||||
if (name.empty()) { return; }
|
|
||||||
const auto config_key = (boost::format("window_%1%") % name).str();
|
|
||||||
|
|
||||||
if (! app_config->has(config_key)) { return; }
|
|
||||||
|
|
||||||
auto metrics = WindowMetrics::deserialize(app_config->get(config_key));
|
|
||||||
if (! metrics) { return; }
|
|
||||||
|
|
||||||
window->SetSize(metrics->get_rect());
|
|
||||||
window->Maximize(metrics->get_maximized());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUI_App::window_pos_sanitize(wxTopLevelWindow* window)
|
|
||||||
{
|
|
||||||
const auto display_idx = wxDisplay::GetFromWindow(window);
|
|
||||||
if (display_idx == wxNOT_FOUND) { return; }
|
|
||||||
|
|
||||||
const auto display = wxDisplay(display_idx).GetClientArea();
|
|
||||||
|
|
||||||
auto metrics = WindowMetrics::from_window(window);
|
|
||||||
|
|
||||||
metrics.sanitize_for_display(display);
|
|
||||||
if (window->GetScreenRect() != metrics.get_rect()) {
|
|
||||||
window->SetSize(metrics.get_rect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// select language from the list of installed languages
|
// select language from the list of installed languages
|
||||||
bool GUI_App::select_language( wxArrayString & names,
|
bool GUI_App::select_language( wxArrayString & names,
|
||||||
wxArrayLong & identifiers)
|
wxArrayLong & identifiers)
|
||||||
|
@ -789,6 +747,48 @@ wxNotebook* GUI_App::tab_panel() const
|
||||||
return mainframe->m_tabpanel;
|
return mainframe->m_tabpanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name)
|
||||||
|
{
|
||||||
|
if (name.empty()) { return; }
|
||||||
|
const auto config_key = (boost::format("window_%1%") % name).str();
|
||||||
|
|
||||||
|
WindowMetrics metrics = WindowMetrics::from_window(window);
|
||||||
|
app_config->set(config_key, metrics.serialize());
|
||||||
|
app_config->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &name)
|
||||||
|
{
|
||||||
|
if (name.empty()) { return; }
|
||||||
|
const auto config_key = (boost::format("window_%1%") % name).str();
|
||||||
|
|
||||||
|
if (! app_config->has(config_key)) { return; }
|
||||||
|
|
||||||
|
auto metrics = WindowMetrics::deserialize(app_config->get(config_key));
|
||||||
|
if (! metrics) { return; }
|
||||||
|
|
||||||
|
window->SetSize(metrics->get_rect());
|
||||||
|
window->Maximize(metrics->get_maximized());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI_App::window_pos_sanitize(wxTopLevelWindow* window)
|
||||||
|
{
|
||||||
|
unsigned display_idx = wxDisplay::GetFromWindow(window);
|
||||||
|
wxRect display;
|
||||||
|
if (display_idx == wxNOT_FOUND) {
|
||||||
|
display = wxDisplay(0u).GetClientArea();
|
||||||
|
window->Move(display.GetTopLeft());
|
||||||
|
} else {
|
||||||
|
display = wxDisplay(display_idx).GetClientArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto metrics = WindowMetrics::from_window(window);
|
||||||
|
metrics.sanitize_for_display(display);
|
||||||
|
if (window->GetScreenRect() != metrics.get_rect()) {
|
||||||
|
window->SetSize(metrics.get_rect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static method accepting a wxWindow object as first parameter
|
// static method accepting a wxWindow object as first parameter
|
||||||
// void warning_catcher{
|
// void warning_catcher{
|
||||||
// my($self, $message_dialog) = @_;
|
// my($self, $message_dialog) = @_;
|
||||||
|
|
|
@ -73,11 +73,6 @@ class GUI_App : public wxApp
|
||||||
{
|
{
|
||||||
bool app_conf_exists{ false };
|
bool app_conf_exists{ false };
|
||||||
|
|
||||||
// Lock to guard the callback stack
|
|
||||||
std::mutex callback_register;
|
|
||||||
// callbacks registered to run during idle event.
|
|
||||||
std::stack<std::function<void()>> m_cb{};
|
|
||||||
|
|
||||||
wxColour m_color_label_modified;
|
wxColour m_color_label_modified;
|
||||||
wxColour m_color_label_sys;
|
wxColour m_color_label_sys;
|
||||||
wxColour m_color_label_default;
|
wxColour m_color_label_default;
|
||||||
|
@ -121,12 +116,9 @@ public:
|
||||||
// wxMessageDialog* message_dialog,
|
// wxMessageDialog* message_dialog,
|
||||||
const std::string& err);
|
const std::string& err);
|
||||||
// void notify(/*message*/);
|
// void notify(/*message*/);
|
||||||
void update_ui_from_settings();
|
|
||||||
void CallAfter(std::function<void()> cb);
|
|
||||||
|
|
||||||
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
|
void persist_window_geometry(wxTopLevelWindow *window);
|
||||||
void window_pos_restore(wxTopLevelWindow* window, const std::string &name);
|
void update_ui_from_settings();
|
||||||
void window_pos_sanitize(wxTopLevelWindow* window);
|
|
||||||
|
|
||||||
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
||||||
bool load_language();
|
bool load_language();
|
||||||
|
@ -172,6 +164,10 @@ public:
|
||||||
|
|
||||||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
|
||||||
|
void window_pos_restore(wxTopLevelWindow* window, const std::string &name);
|
||||||
|
void window_pos_sanitize(wxTopLevelWindow* window);
|
||||||
};
|
};
|
||||||
DECLARE_APP(GUI_App)
|
DECLARE_APP(GUI_App)
|
||||||
|
|
||||||
|
|
|
@ -120,19 +120,28 @@ boost::optional<WindowMetrics> WindowMetrics::deserialize(const std::string &str
|
||||||
void WindowMetrics::sanitize_for_display(const wxRect &screen_rect)
|
void WindowMetrics::sanitize_for_display(const wxRect &screen_rect)
|
||||||
{
|
{
|
||||||
rect = rect.Intersect(screen_rect);
|
rect = rect.Intersect(screen_rect);
|
||||||
|
|
||||||
|
// Prevent the window from going too far towards the right and/or bottom edge
|
||||||
|
// It's hardcoded here that the threshold is 80% of the screen size
|
||||||
|
rect.x = std::min(rect.x, screen_rect.x + 4*screen_rect.width/5);
|
||||||
|
rect.y = std::min(rect.y, screen_rect.y + 4*screen_rect.height/5);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WindowMetrics::serialize()
|
std::string WindowMetrics::serialize() const
|
||||||
{
|
{
|
||||||
return (boost::format("%1%; %2%; %3%; %4%; %5%")
|
return (boost::format("%1%; %2%; %3%; %4%; %5%")
|
||||||
% rect.GetX()
|
% rect.x
|
||||||
% rect.GetY()
|
% rect.y
|
||||||
% rect.GetWidth()
|
% rect.width
|
||||||
% rect.GetHeight()
|
% rect.height
|
||||||
% static_cast<int>(maximized)
|
% static_cast<int>(maximized)
|
||||||
).str();
|
).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics)
|
||||||
|
{
|
||||||
|
return os << '(' << metrics.serialize() << ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
@ -149,9 +150,11 @@ public:
|
||||||
bool get_maximized() const { return maximized; }
|
bool get_maximized() const { return maximized; }
|
||||||
|
|
||||||
void sanitize_for_display(const wxRect &screen_rect);
|
void sanitize_for_display(const wxRect &screen_rect);
|
||||||
std::string serialize();
|
std::string serialize() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics);
|
||||||
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
MainFrame::MainFrame() :
|
MainFrame::MainFrame() :
|
||||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
|
||||||
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||||
{
|
{
|
||||||
// Load the icon either from the exe, or from the ico file.
|
// Load the icon either from the exe, or from the ico file.
|
||||||
|
@ -80,8 +80,7 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
||||||
event.Veto();
|
event.Veto();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// save window size
|
|
||||||
wxGetApp().window_pos_save(this, "mainframe");
|
|
||||||
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
|
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
|
||||||
// but in rare cases it may not have been called yet.
|
// but in rare cases it may not have been called yet.
|
||||||
wxGetApp().app_config->save();
|
wxGetApp().app_config->save();
|
||||||
|
@ -93,18 +92,9 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
||||||
event.Skip();
|
event.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
// NB: Restoring the window position is done in a two-phase manner here,
|
wxGetApp().persist_window_geometry(this);
|
||||||
// first the saved position is restored as-is and validation is done after the window is shown
|
|
||||||
// and initial round of events is complete, because on some platforms that is the only way
|
|
||||||
// to get an accurate window position & size.
|
|
||||||
wxGetApp().window_pos_restore(this, "mainframe");
|
|
||||||
Bind(wxEVT_SHOW, [this](wxShowEvent&) {
|
|
||||||
CallAfter([this]() {
|
|
||||||
wxGetApp().window_pos_sanitize(this);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
update_ui_from_settings();
|
update_ui_from_settings(); // FIXME (?)
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrame::init_tabpanel()
|
void MainFrame::init_tabpanel()
|
||||||
|
@ -447,7 +437,8 @@ void MainFrame::init_menubar()
|
||||||
SetMenuBar(menubar);
|
SetMenuBar(menubar);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// This fixes a bug (?) on Mac OS where the quit command doesn't emit window close events
|
// This fixes a bug on Mac OS where the quit command doesn't emit window close events
|
||||||
|
// wx bug: https://trac.wxwidgets.org/ticket/18328
|
||||||
wxMenu *apple_menu = menubar->OSXGetAppleMenu();
|
wxMenu *apple_menu = menubar->OSXGetAppleMenu();
|
||||||
if (apple_menu != nullptr) {
|
if (apple_menu != nullptr) {
|
||||||
apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) {
|
apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue