Merge branch 'master' of https://github.com/prusa3d/Slic3r into objects_centering

This commit is contained in:
Enrico Turri 2019-01-21 15:46:05 +01:00
commit 6107b7fd2f
9 changed files with 103 additions and 149 deletions

View file

@ -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
{ {

View file

@ -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)

View file

@ -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;

View file

@ -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.

View file

@ -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) = @_;

View file

@ -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)

View file

@ -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() << ')';
}
} }

View file

@ -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);
}} }}

View file

@ -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 &) {