mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 16:51:21 -06:00
Merge remote-tracking branch 'origin/master' into dev
This commit is contained in:
commit
af6ef2cb8a
83 changed files with 5953 additions and 3249 deletions
|
|
@ -255,3 +255,12 @@ endif ()
|
|||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
||||
# We need to implement some hacks for wxWidgets and touch the underlying GTK
|
||||
# layer and sub-libraries. This forces us to use the include locations and
|
||||
# link these libraries.
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
||||
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
|
||||
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES})
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -611,7 +611,7 @@ struct _3DScene
|
|||
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
|
||||
};
|
||||
|
||||
static constexpr float BedEpsilon = float(EPSILON);
|
||||
static constexpr float BedEpsilon = 3.f * float(EPSILON);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,9 +147,9 @@ void GCodeViewer::TBuffer::reset()
|
|||
}
|
||||
|
||||
// release cpu memory
|
||||
indices = std::vector<IBuffer>();
|
||||
paths = std::vector<Path>();
|
||||
render_paths = std::vector<RenderPath>();
|
||||
indices.clear();
|
||||
paths.clear();
|
||||
render_paths.clear();
|
||||
}
|
||||
|
||||
void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id)
|
||||
|
|
@ -781,9 +781,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||
unsigned int start_vertex_offset = buffer.start_segment_vertex_offset();
|
||||
unsigned int end_vertex_offset = buffer.end_segment_vertex_offset();
|
||||
|
||||
for (size_t i = 0; i < buffer.render_paths.size(); ++i) {
|
||||
size_t i = 0;
|
||||
for (const RenderPath& render_path : buffer.render_paths) {
|
||||
// get paths segments from buffer paths
|
||||
const RenderPath& render_path = buffer.render_paths[i];
|
||||
const IndexBuffer& ibuffer = indices[render_path.index_buffer_id];
|
||||
const Path& path = buffer.paths[render_path.path_id];
|
||||
float half_width = 0.5f * path.width;
|
||||
|
|
@ -948,6 +948,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||
for (const Triangle& t : out_triangles) {
|
||||
fprintf(fp, "f %zu//%zu %zu//%zu %zu//%zu\n", t[0], t[0], t[1], t[1], t[2], t[2]);
|
||||
}
|
||||
|
||||
++ i;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
|
@ -1900,6 +1902,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
}
|
||||
|
||||
// second pass: filter paths by sequential data and collect them by color
|
||||
RenderPath *render_path = nullptr;
|
||||
for (const auto& [buffer, index_buffer_id, path_id] : paths) {
|
||||
const Path& path = buffer->paths[path_id];
|
||||
if (m_sequential_view.current.last <= path.first.s_id || path.last.s_id <= m_sequential_view.current.first)
|
||||
|
|
@ -1930,16 +1933,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
default: { color = { 0.0f, 0.0f, 0.0f }; break; }
|
||||
}
|
||||
|
||||
unsigned int ibuffer_id = index_buffer_id;
|
||||
auto it = std::find_if(buffer->render_paths.begin(), buffer->render_paths.end(),
|
||||
[color, ibuffer_id](const RenderPath& path) { return path.index_buffer_id == ibuffer_id && path.color == color; });
|
||||
if (it == buffer->render_paths.end()) {
|
||||
it = buffer->render_paths.insert(buffer->render_paths.end(), RenderPath());
|
||||
it->color = color;
|
||||
it->path_id = path_id;
|
||||
it->index_buffer_id = index_buffer_id;
|
||||
}
|
||||
|
||||
RenderPath key{ color, static_cast<unsigned int>(index_buffer_id), path_id };
|
||||
if (render_path == nullptr || ! RenderPathPropertyEqual()(*render_path, key))
|
||||
render_path = const_cast<RenderPath*>(&(*buffer->render_paths.emplace(key).first));
|
||||
unsigned int segments_count = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1;
|
||||
unsigned int size_in_indices = 0;
|
||||
switch (buffer->render_primitive_type)
|
||||
|
|
@ -1948,7 +1944,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
case TBuffer::ERenderPrimitiveType::Line:
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: { size_in_indices = buffer->indices_per_segment() * (segments_count - 1); break; }
|
||||
}
|
||||
it->sizes.push_back(size_in_indices);
|
||||
render_path->sizes.push_back(size_in_indices);
|
||||
|
||||
unsigned int delta_1st = 0;
|
||||
if (path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= path.last.s_id)
|
||||
|
|
@ -1957,7 +1953,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle)
|
||||
delta_1st *= buffer->indices_per_segment();
|
||||
|
||||
it->offsets.push_back(static_cast<size_t>((path.first.i_id + delta_1st) * sizeof(unsigned int)));
|
||||
render_path->offsets.push_back(static_cast<size_t>((path.first.i_id + delta_1st) * sizeof(unsigned int)));
|
||||
}
|
||||
|
||||
// set sequential data to their final value
|
||||
|
|
@ -2943,7 +2939,7 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional)
|
|||
int64_t render_paths_size = 0;
|
||||
for (const TBuffer& buffer : m_buffers) {
|
||||
paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path);
|
||||
render_paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.render_paths, RenderPath);
|
||||
render_paths_size += SLIC3R_STDUNORDEREDSET_MEMSIZE(buffer.render_paths, RenderPath);
|
||||
for (const RenderPath& path : buffer.render_paths) {
|
||||
render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int);
|
||||
render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <float.h>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -146,11 +148,35 @@ class GCodeViewer
|
|||
// Used to batch the indices needed to render paths
|
||||
struct RenderPath
|
||||
{
|
||||
Color color;
|
||||
unsigned int path_id;
|
||||
unsigned int index_buffer_id;
|
||||
std::vector<unsigned int> sizes;
|
||||
std::vector<size_t> offsets; // use size_t because we need an unsigned int whose size matches pointer's size (used in the call glMultiDrawElements())
|
||||
// Render path property
|
||||
Color color;
|
||||
unsigned int index_buffer_id;
|
||||
// Render path content
|
||||
unsigned int path_id;
|
||||
std::vector<unsigned int> sizes;
|
||||
std::vector<size_t> offsets; // use size_t because we need an unsigned int whose size matches pointer's size (used in the call glMultiDrawElements())
|
||||
};
|
||||
struct RenderPathPropertyHash {
|
||||
size_t operator() (const RenderPath &p) const {
|
||||
// Conver the RGB value to an integer hash.
|
||||
// return (size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) * 7919) ^ size_t(p.index_buffer_id);
|
||||
return size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) ^ size_t(p.index_buffer_id);
|
||||
}
|
||||
};
|
||||
struct RenderPathPropertyLower {
|
||||
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
if (l.color[i] < r.color[i])
|
||||
return true;
|
||||
else if (l.color[i] > r.color[i])
|
||||
return false;
|
||||
return l.index_buffer_id < r.index_buffer_id;
|
||||
}
|
||||
};
|
||||
struct RenderPathPropertyEqual {
|
||||
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
||||
return l.color == r.color && l.index_buffer_id == r.index_buffer_id;
|
||||
}
|
||||
};
|
||||
|
||||
// buffer containing data for rendering a specific toolpath type
|
||||
|
|
@ -169,7 +195,9 @@ class GCodeViewer
|
|||
|
||||
std::string shader;
|
||||
std::vector<Path> paths;
|
||||
std::vector<RenderPath> render_paths;
|
||||
// std::set seems to perform singificantly better, at least on Windows.
|
||||
// std::unordered_set<RenderPath, RenderPathPropertyHash, RenderPathPropertyEqual> render_paths;
|
||||
std::set<RenderPath, RenderPathPropertyLower> render_paths;
|
||||
bool visible{ false };
|
||||
|
||||
void reset();
|
||||
|
|
|
|||
|
|
@ -1331,7 +1331,7 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
|
|||
{
|
||||
for (GLVolume* vol : m_volumes.volumes) {
|
||||
if (vol->composite_id.object_id == 1000) { // wipe tower
|
||||
vol->is_active = (visible && mo == nullptr);
|
||||
vol->is_active = (visible && mo == nullptr);
|
||||
}
|
||||
else {
|
||||
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo)
|
||||
|
|
@ -2996,6 +2996,7 @@ void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
|
|||
}
|
||||
//render();
|
||||
m_dirty = true;
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
void GLCanvas3D::request_extra_frame_delayed(int miliseconds)
|
||||
|
|
@ -4151,9 +4152,13 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
|
|||
shader->start_using();
|
||||
shader->set_uniform("print_box.volume_detection", 0);
|
||||
|
||||
for (const GLVolume* vol : visible_volumes) {
|
||||
for (GLVolume* vol : visible_volumes) {
|
||||
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray);
|
||||
// the volume may have been deactivated by an active gizmo
|
||||
bool is_active = vol->is_active;
|
||||
vol->is_active = true;
|
||||
vol->render();
|
||||
vol->is_active = is_active;
|
||||
}
|
||||
|
||||
shader->stop_using();
|
||||
|
|
|
|||
|
|
@ -652,7 +652,6 @@ public:
|
|||
void set_toolpath_view_type(GCodeViewer::EViewType type);
|
||||
void set_volumes_z_range(const std::array<double, 2>& range);
|
||||
void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
|
||||
void set_toolpaths_range(double low, double high);
|
||||
|
||||
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
|
||||
std::vector<int> load_object(const Model& model, int obj_idx);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ public:
|
|||
|
||||
memDC.SetFont(m_action_font);
|
||||
memDC.SetTextForeground(wxColour(237, 107, 33));
|
||||
memDC.DrawText(text, int(m_scale * 60), int(m_scale * 275));
|
||||
memDC.DrawText(text, int(m_scale * 60), m_action_line_y_position);
|
||||
|
||||
memDC.SelectObject(wxNullBitmap);
|
||||
set_bitmap(bitmap);
|
||||
|
|
@ -206,14 +206,22 @@ public:
|
|||
|
||||
memDc.SetFont(m_constant_text.version_font);
|
||||
memDc.DrawLabel(m_constant_text.version, banner_rect, wxALIGN_TOP | wxALIGN_LEFT);
|
||||
int version_height = memDc.GetTextExtent(m_constant_text.version).GetY();
|
||||
|
||||
memDc.SetFont(m_constant_text.credits_font);
|
||||
memDc.DrawLabel(m_constant_text.credits, banner_rect, wxALIGN_BOTTOM | wxALIGN_LEFT);
|
||||
int credits_height = memDc.GetMultiLineTextExtent(m_constant_text.credits).GetY();
|
||||
int text_height = memDc.GetTextExtent("text").GetY();
|
||||
|
||||
// calculate position for the dynamic text
|
||||
int logo_and_header_height = margin + logo_size + title_height + version_height;
|
||||
m_action_line_y_position = logo_and_header_height + 0.5 * (bmp.GetHeight() - margin - credits_height - logo_and_header_height - text_height);
|
||||
}
|
||||
|
||||
private:
|
||||
wxBitmap m_main_bitmap;
|
||||
wxFont m_action_font;
|
||||
int m_action_line_y_position;
|
||||
float m_scale {1.0};
|
||||
|
||||
struct ConstantText
|
||||
|
|
@ -258,7 +266,8 @@ private:
|
|||
float title_font_scale = (float)text_banner_width / GetTextExtent(m_constant_text.title).GetX();
|
||||
scale_font(m_constant_text.title_font, title_font_scale > 3.5f ? 3.5f : title_font_scale);
|
||||
|
||||
scale_font(m_constant_text.version_font, 2.f);
|
||||
float version_font_scale = (float)text_banner_width / GetTextExtent(m_constant_text.version).GetX();
|
||||
scale_font(m_constant_text.version_font, version_font_scale > 2.f ? 2.f : version_font_scale);
|
||||
|
||||
// The width of the credits information string doesn't respect to the banner width some times.
|
||||
// So, scale credits_font in the respect to the longest string width
|
||||
|
|
@ -753,7 +762,7 @@ bool GUI_App::on_init_inner()
|
|||
|
||||
#ifdef __linux__
|
||||
if (! check_old_linux_datadir(GetAppName())) {
|
||||
std::cerr << "Quitting, user chose to move his data to new location." << std::endl;
|
||||
std::cerr << "Quitting, user chose to move their data to new location." << std::endl;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1879,11 +1888,9 @@ bool GUI_App::OnExceptionInMainLoop()
|
|||
void GUI_App::OSXStoreOpenFiles(const wxArrayString &fileNames)
|
||||
{
|
||||
size_t num_gcodes = 0;
|
||||
for (const wxString &filename : fileNames) {
|
||||
wxString fn = filename.Upper();
|
||||
if (fn.EndsWith(".G") || fn.EndsWith(".GCODE"))
|
||||
for (const wxString &filename : fileNames)
|
||||
if (is_gcode_file(into_u8(filename)))
|
||||
++ num_gcodes;
|
||||
}
|
||||
if (fileNames.size() == num_gcodes) {
|
||||
// Opening PrusaSlicer by drag & dropping a G-Code onto PrusaSlicer icon in Finder,
|
||||
// just G-codes were passed. Switch to G-code viewer mode.
|
||||
|
|
@ -1903,8 +1910,7 @@ void GUI_App::MacOpenFiles(const wxArrayString &fileNames)
|
|||
std::vector<wxString> gcode_files;
|
||||
std::vector<wxString> non_gcode_files;
|
||||
for (const auto& filename : fileNames) {
|
||||
wxString fn = filename.Upper();
|
||||
if (fn.EndsWith(".G") || fn.EndsWith(".GCODE"))
|
||||
if (is_gcode_file(into_u8(filename)))
|
||||
gcode_files.emplace_back(filename);
|
||||
else {
|
||||
files.emplace_back(into_u8(filename));
|
||||
|
|
|
|||
|
|
@ -16,11 +16,26 @@
|
|||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#if __APPLE__
|
||||
#include <signal.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
int GUI_Run(GUI_InitParams ¶ms)
|
||||
{
|
||||
#if __APPLE__
|
||||
// On OSX, we use boost::process::spawn() to launch new instances of PrusaSlicer from another PrusaSlicer.
|
||||
// boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another
|
||||
// subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess
|
||||
// (posix waitpid() call will always fail).
|
||||
// https://jmmv.dev/2008/10/boostprocess-and-sigchld.html
|
||||
// The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work.
|
||||
// See GH issue #5507
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
#endif // __APPLE__
|
||||
|
||||
try {
|
||||
GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
|
||||
if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {
|
||||
|
|
|
|||
|
|
@ -1211,7 +1211,7 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
|
|||
**/
|
||||
m_prevent_list_events = true;//it's needed for GTK
|
||||
|
||||
/* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid value,
|
||||
/* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid vaSome textlue,
|
||||
* so set some nonempty string
|
||||
*/
|
||||
wxTextDataObject* obj = new wxTextDataObject;
|
||||
|
|
@ -1243,8 +1243,10 @@ void ObjectList::OnDropPossible(wxDataViewEvent &event)
|
|||
{
|
||||
const wxDataViewItem& item = event.GetItem();
|
||||
|
||||
if (!can_drop(item))
|
||||
if (!can_drop(item)) {
|
||||
event.Veto();
|
||||
m_prevent_list_events = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::OnDrop(wxDataViewEvent &event)
|
||||
|
|
@ -1269,7 +1271,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
|
|||
// It looks like a fixed in current version of the wxWidgets
|
||||
// #ifdef __WXGTK__
|
||||
// /* Under GTK, DnD moves an item between another two items.
|
||||
// * And event.GetItem() return item, which is under "insertion line"
|
||||
// * And event.GetItem() return item, which is under "insertion line"Some text
|
||||
// * So, if we move item down we should to decrease the to_volume_id value
|
||||
// **/
|
||||
// if (to_volume_id > from_volume_id) to_volume_id--;
|
||||
|
|
@ -2407,6 +2409,8 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
|
|||
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
|
||||
if (is_layer_settings)
|
||||
m_config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
|
||||
|
||||
changed_object();
|
||||
}
|
||||
|
||||
void ObjectList::del_instances_from_object(const int obj_idx)
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ void Preview::reload_print(bool keep_volumes)
|
|||
m_volumes_cleanup_required = !keep_volumes;
|
||||
return;
|
||||
}
|
||||
#endif /* __linux __ */
|
||||
#endif /* __linux__ */
|
||||
if (
|
||||
#ifdef __linux__
|
||||
m_volumes_cleanup_required ||
|
||||
|
|
|
|||
|
|
@ -252,14 +252,31 @@ namespace instance_check_internal
|
|||
|
||||
bool instance_check(int argc, char** argv, bool app_config_single_instance)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
boost::system::error_code ec;
|
||||
#endif
|
||||
std::size_t hashed_path =
|
||||
std::size_t hashed_path;
|
||||
#ifdef _WIN32
|
||||
std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
|
||||
hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
|
||||
#else
|
||||
std::hash<std::string>{}(boost::filesystem::canonical(boost::filesystem::system_complete(argv[0]), ec).string());
|
||||
boost::system::error_code ec;
|
||||
#ifdef __linux__
|
||||
// If executed by an AppImage, start the AppImage, not the main process.
|
||||
// see https://docs.appimage.org/packaging-guide/environment-variables.html#id2
|
||||
const char *appimage_env = std::getenv("APPIMAGE");
|
||||
bool appimage_env_valid = false;
|
||||
if (appimage_env) {
|
||||
try {
|
||||
auto appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env));
|
||||
if (boost::filesystem::exists(appimage_path)) {
|
||||
hashed_path = std::hash<std::string>{}(appimage_path.string());
|
||||
appimage_env_valid = true;
|
||||
}
|
||||
} catch (std::exception &) {
|
||||
}
|
||||
if (! appimage_env_valid)
|
||||
BOOST_LOG_TRIVIAL(error) << "APPIMAGE environment variable was set, but it does not point to a valid file: " << appimage_env;
|
||||
}
|
||||
if (! appimage_env_valid)
|
||||
#endif // __linux__
|
||||
hashed_path = std::hash<std::string>{}(boost::filesystem::canonical(boost::filesystem::system_complete(argv[0]), ec).string());
|
||||
if (ec.value() > 0) { // canonical was not able to find the executable (can happen with appimage on some systems. Does it fail on Fuse file systems?)
|
||||
ec.clear();
|
||||
// Compose path with boost canonical of folder and filename
|
||||
|
|
@ -269,7 +286,7 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance)
|
|||
hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
|
||||
}
|
||||
}
|
||||
#endif // win32
|
||||
#endif // _WIN32
|
||||
|
||||
std::string lock_name = std::to_string(hashed_path);
|
||||
GUI::wxGetApp().set_instance_hash(hashed_path);
|
||||
|
|
|
|||
|
|
@ -73,9 +73,14 @@ void FillBedJob::prepare()
|
|||
// This is the maximum number of items, the real number will always be close but less.
|
||||
int needed_items = (bed_area - fixed_area) / poly_area;
|
||||
|
||||
ModelInstance *mi = model_object->instances[0];
|
||||
int sel_id = m_plater->get_selection().get_instance_idx();
|
||||
// if the selection is not a single instance, choose the first as template
|
||||
sel_id = std::max(sel_id, 0);
|
||||
ModelInstance *mi = model_object->instances[sel_id];
|
||||
ArrangePolygon template_ap = get_arrange_poly(PtrWrapper{mi}, m_plater);
|
||||
|
||||
for (int i = 0; i < needed_items; ++i) {
|
||||
ArrangePolygon ap;
|
||||
ArrangePolygon ap = template_ap;
|
||||
ap.poly = m_selected.front().poly;
|
||||
ap.bed_idx = arrangement::UNARRANGED;
|
||||
ap.setter = [this, mi](const ArrangePolygon &p) {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_
|
|||
{
|
||||
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), monospaced_font ? 30 * wxGetApp().em_unit() : -1));
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
wxFont monospace = wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT);
|
||||
wxFont monospace = wxGetApp().code_font();
|
||||
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
|
|
|
|||
|
|
@ -1233,11 +1233,11 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
|
|||
|
||||
if (this->activate_existing(notification.get())) {
|
||||
m_pop_notifications.back()->update(notification->get_data());
|
||||
canvas.request_extra_frame();
|
||||
canvas.request_extra_frame_delayed(33);
|
||||
return false;
|
||||
} else {
|
||||
m_pop_notifications.emplace_back(std::move(notification));
|
||||
canvas.request_extra_frame();
|
||||
canvas.request_extra_frame_delayed(33);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1387,15 +1387,19 @@ void NotificationManager::update_notifications()
|
|||
if (!top_level_wnd->IsActive())
|
||||
return;
|
||||
|
||||
static size_t last_size = m_pop_notifications.size();
|
||||
//static size_t last_size = m_pop_notifications.size();
|
||||
|
||||
//request frames
|
||||
int64_t next_render = std::numeric_limits<int64_t>::max();
|
||||
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
|
||||
std::unique_ptr<PopNotification>& notification = *it;
|
||||
notification->set_paused(m_hovered);
|
||||
notification->update_state();
|
||||
next_render = std::min<int64_t>(next_render, notification->next_render());
|
||||
if (notification->get_state() == PopNotification::EState::Finished)
|
||||
it = m_pop_notifications.erase(it);
|
||||
else {
|
||||
notification->set_paused(m_hovered);
|
||||
notification->update_state();
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
|
@ -1436,16 +1440,11 @@ void NotificationManager::update_notifications()
|
|||
if (m_requires_render)
|
||||
m_requires_update = true;
|
||||
*/
|
||||
//request frames
|
||||
int64_t next_render = std::numeric_limits<int64_t>::max();
|
||||
const int64_t max = std::numeric_limits<int64_t>::max();
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
next_render = std::min<int64_t>(next_render, notification->next_render());
|
||||
}
|
||||
|
||||
|
||||
if (next_render == 0)
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
else if (next_render < max)
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render
|
||||
else if (next_render < std::numeric_limits<int64_t>::max())
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render));
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -544,7 +544,7 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event)
|
|||
return;
|
||||
}
|
||||
if (printer_name == m_default_name) {
|
||||
warning_catcher(this, _L("You should to change a name of your printer device. It can't be saved."));
|
||||
warning_catcher(this, _L("You should change the name of your printer device."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3566,6 +3566,14 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
|||
*/
|
||||
wxGetApp().obj_list()->update_object_list_by_printer_technology();
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// From the Win 2004 preset combobox lose a focus after change the preset selection
|
||||
// and that is why the up/down arrow doesn't work properly
|
||||
// (see https://github.com/prusa3d/PrusaSlicer/issues/5531 ).
|
||||
// So, set the focus to the combobox explicitly
|
||||
combo->SetFocus();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||
|
|
@ -4847,9 +4855,7 @@ void Plater::load_gcode()
|
|||
|
||||
void Plater::load_gcode(const wxString& filename)
|
||||
{
|
||||
if (filename.empty() ||
|
||||
(!filename.Lower().EndsWith(".gcode") && !filename.Lower().EndsWith(".g")) ||
|
||||
m_last_loaded_gcode == filename)
|
||||
if (! is_gcode_file(into_u8(filename)) || m_last_loaded_gcode == filename)
|
||||
return;
|
||||
|
||||
m_last_loaded_gcode = filename;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@
|
|||
#include "PhysicalPrinterDialog.hpp"
|
||||
#include "SavePresetDialog.hpp"
|
||||
|
||||
// A workaround for a set of issues related to text fitting into gtk widgets:
|
||||
// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584
|
||||
#if defined(__WXGTK20__) || defined(__WXGTK3__)
|
||||
#include <glib-2.0/glib-object.h>
|
||||
#include <pango-1.0/pango/pango-layout.h>
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
using Slic3r::GUI::format_wxstr;
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -179,6 +187,25 @@ void PresetComboBox::update_selection()
|
|||
|
||||
SetSelection(m_last_selected);
|
||||
SetToolTip(GetString(m_last_selected));
|
||||
|
||||
// A workaround for a set of issues related to text fitting into gtk widgets:
|
||||
// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584
|
||||
#if defined(__WXGTK20__) || defined(__WXGTK3__)
|
||||
GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_widget));
|
||||
|
||||
// 'cells' contains the GtkCellRendererPixBuf for the icon,
|
||||
// 'cells->next' contains GtkCellRendererText for the text we need to ellipsize
|
||||
if (!cells || !cells->next) return;
|
||||
|
||||
auto cell = static_cast<GtkCellRendererText *>(cells->next->data);
|
||||
|
||||
if (!cell) return;
|
||||
|
||||
g_object_set(G_OBJECT(cell), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
||||
|
||||
// Only the list of cells must be freed, the renderer isn't ours to free
|
||||
g_list_free(cells);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PresetComboBox::update(std::string select_preset_name)
|
||||
|
|
@ -860,9 +887,13 @@ void PlaterPresetComboBox::update()
|
|||
if (!tooltip.IsEmpty())
|
||||
SetToolTip(tooltip);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Use this part of code just on Windows to avoid of some layout issues on Linux
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/5163 and https://github.com/prusa3d/PrusaSlicer/issues/5505
|
||||
// Update control min size after rescale (changed Display DPI under MSW)
|
||||
if (GetMinWidth() != 20 * m_em_unit)
|
||||
SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight()));
|
||||
#endif //__WXMSW__
|
||||
}
|
||||
|
||||
void PlaterPresetComboBox::msw_rescale()
|
||||
|
|
@ -905,6 +936,13 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type)
|
|||
}
|
||||
|
||||
evt.StopPropagation();
|
||||
#ifdef __WXMSW__
|
||||
// From the Win 2004 preset combobox lose a focus after change the preset selection
|
||||
// and that is why the up/down arrow doesn't work properly
|
||||
// (see https://github.com/prusa3d/PrusaSlicer/issues/5531 ).
|
||||
// So, set the focus to the combobox explicitly
|
||||
this->SetFocus();
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,17 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
|||
Fit();
|
||||
CenterOnParent();
|
||||
|
||||
#ifdef __linux__
|
||||
// On Linux with GTK2 when text control lose the focus then selection (colored background) disappears but text color stay white
|
||||
// and as a result the text is invisible with light mode
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/4532
|
||||
// Workaround: Unselect text selection explicitly on kill focus
|
||||
txt_filename->Bind(wxEVT_KILL_FOCUS, [this](wxEvent& e) {
|
||||
e.Skip();
|
||||
txt_filename->SetInsertionPoint(txt_filename->GetLastPosition());
|
||||
}, txt_filename->GetId());
|
||||
#endif /* __linux__ */
|
||||
|
||||
Bind(wxEVT_SHOW, [=](const wxShowEvent &) {
|
||||
// Another similar case where the function only works with EVT_SHOW + CallAfter,
|
||||
// this time on Mac.
|
||||
|
|
|
|||
|
|
@ -261,19 +261,31 @@ void RemovableDriveManager::eject_drive()
|
|||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
#if __APPLE__
|
||||
// If eject is still pending on the eject thread, wait until it finishes.
|
||||
//FIXME while waiting for the eject thread to finish, the main thread is not pumping Cocoa messages, which may lead
|
||||
// to blocking by the diskutil tool for a couple (up to 10) seconds. This is likely not critical, as the eject normally
|
||||
// finishes quickly.
|
||||
this->eject_thread_finish();
|
||||
#endif
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
std::string correct_path(m_last_save_path);
|
||||
#ifndef __APPLE__
|
||||
for (size_t i = 0; i < correct_path.size(); ++i)
|
||||
if (correct_path[i]==' ') {
|
||||
correct_path = correct_path.insert(i,1,'\\');
|
||||
++ i;
|
||||
}
|
||||
DriveData drive_data;
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
if (it_drive_data == m_current_drives.end())
|
||||
return;
|
||||
drive_data = *it_drive_data;
|
||||
}
|
||||
|
||||
std::string correct_path(m_last_save_path);
|
||||
#if __APPLE__
|
||||
// On Apple, run the eject asynchronously on a worker thread, see the discussion at GH issue #4844.
|
||||
m_eject_thread = new boost::thread([this, correct_path, drive_data]()
|
||||
#endif
|
||||
{
|
||||
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
||||
// there is no usable command in c++ so terminal command is used instead
|
||||
// but neither triggers "succesful safe removal messege"
|
||||
|
|
@ -296,31 +308,36 @@ void RemovableDriveManager::eject_drive()
|
|||
// wait for command to finnish (blocks ui thread)
|
||||
std::error_code ec;
|
||||
child.wait(ec);
|
||||
bool success = false;
|
||||
if (ec) {
|
||||
// The wait call can fail, as it did in https://github.com/prusa3d/PrusaSlicer/issues/5507
|
||||
// It can happen even in cases where the eject is sucessful, but better report it as failed.
|
||||
// We did not find a way to reliably retrieve the exit code of the process.
|
||||
BOOST_LOG_TRIVIAL(error) << "boost::process::child::wait() failed during Ejection. State of Ejection is unknown. Error code: " << ec.value();
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
|
||||
return;
|
||||
} else {
|
||||
int err = child.exit_code();
|
||||
if (err) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting failed. Exit code: " << err;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
int err = child.exit_code();
|
||||
if (err) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting failed. Exit code: " << err;
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
|
||||
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(*it_drive_data), true)));
|
||||
m_current_drives.erase(it_drive_data);
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(drive_data, success)));
|
||||
if (success) {
|
||||
// Remove the drive_data from m_current drives, searching by value, not by pointer, as m_current_drives may get modified during
|
||||
// asynchronous execution on m_eject_thread.
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it = std::find(m_current_drives.begin(), m_current_drives.end(), drive_data);
|
||||
if (it != m_current_drives.end())
|
||||
m_current_drives.erase(it);
|
||||
}
|
||||
}
|
||||
#if __APPLE__
|
||||
);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||
|
|
@ -382,7 +399,11 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
|
|||
void RemovableDriveManager::shutdown()
|
||||
{
|
||||
#if __APPLE__
|
||||
this->unregister_window_osx();
|
||||
// If eject is still pending on the eject thread, wait until it finishes.
|
||||
//FIXME while waiting for the eject thread to finish, the main thread is not pumping Cocoa messages, which may lead
|
||||
// to blocking by the diskutil tool for a couple (up to 10) seconds. This is likely not critical, as the eject normally
|
||||
// finishes quickly.
|
||||
this->eject_thread_finish();
|
||||
#endif
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
|
@ -493,4 +514,15 @@ std::vector<DriveData>::const_iterator RemovableDriveManager::find_last_save_pat
|
|||
[this](const DriveData &data){ return data.path == m_last_save_path; });
|
||||
}
|
||||
|
||||
#if __APPLE__
|
||||
void RemovableDriveManager::eject_thread_finish()
|
||||
{
|
||||
if (m_eject_thread) {
|
||||
m_eject_thread->join();
|
||||
delete m_eject_thread;
|
||||
m_eject_thread = nullptr;
|
||||
}
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ private:
|
|||
void eject_device(const std::string &path);
|
||||
// Opaque pointer to RemovableDriveManagerMM
|
||||
void *m_impl_osx;
|
||||
boost::thread *m_eject_thread { nullptr };
|
||||
void eject_thread_finish();
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -302,6 +302,13 @@ void Tab::create_preset_tab()
|
|||
// This helps to process all the cursor key events on Windows in the tree control,
|
||||
// so that the cursor jumps to the last item.
|
||||
m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, [this](wxTreeEvent&) {
|
||||
#ifdef __linux__
|
||||
// Events queue is opposite On Linux. wxEVT_SET_FOCUS invokes after wxEVT_TREE_SEL_CHANGED,
|
||||
// and a result wxEVT_KILL_FOCUS doesn't invoke for the TextCtrls.
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/5720
|
||||
// So, call SetFocus explicitly for this control before changing of the selection
|
||||
m_treectrl->SetFocus();
|
||||
#endif
|
||||
if (!m_disable_tree_sel_changed_event && !m_pages.empty()) {
|
||||
if (m_page_switch_running)
|
||||
m_page_switch_planned = true;
|
||||
|
|
@ -499,7 +506,7 @@ void Tab::update_label_colours()
|
|||
if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" ||
|
||||
opt.first == "compatible_prints" || opt.first == "compatible_printers" ) {
|
||||
if (m_colored_Label_colors.find(opt.first) != m_colored_Label_colors.end())
|
||||
*m_colored_Label_colors.at(opt.first) = *color;
|
||||
m_colored_Label_colors.at(opt.first) = *color;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -540,7 +547,7 @@ void Tab::decorate()
|
|||
|
||||
if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" ||
|
||||
opt.first == "compatible_prints" || opt.first == "compatible_printers")
|
||||
colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : m_colored_Label_colors.at(opt.first);
|
||||
colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first);
|
||||
|
||||
if (!colored_label_clr) {
|
||||
field = get_field(opt.first);
|
||||
|
|
@ -3551,8 +3558,8 @@ void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::strin
|
|||
line.widget = widget;
|
||||
line.label_path = path;
|
||||
|
||||
m_colored_Label_colors[opt_key] = &m_default_text_clr;
|
||||
line.full_Label_color = m_colored_Label_colors[opt_key];
|
||||
m_colored_Label_colors[opt_key] = m_default_text_clr;
|
||||
line.full_Label_color = &m_colored_Label_colors[opt_key];
|
||||
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ public:
|
|||
|
||||
// map of option name -> wxColour (color of the colored label, associated with option)
|
||||
// Used for options which don't have corresponded field
|
||||
std::map<std::string, wxColour*> m_colored_Label_colors;
|
||||
std::map<std::string, wxColour> m_colored_Label_colors;
|
||||
|
||||
// Counter for the updating (because of an update() function can have a recursive behavior):
|
||||
// 1. increase value from the very beginning of an update() function
|
||||
|
|
|
|||
|
|
@ -78,6 +78,12 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
if (instance_type == NewSlicerInstanceType::Slicer && single_instance)
|
||||
args.emplace_back("--single-instance");
|
||||
boost::process::spawn(bin_path, args);
|
||||
// boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another
|
||||
// subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess
|
||||
// (posix waitpid() call will always fail).
|
||||
// https://jmmv.dev/2008/10/boostprocess-and-sigchld.html
|
||||
// The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work.
|
||||
// See GH issue #5507
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what();
|
||||
|
|
@ -87,7 +93,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
{
|
||||
std::vector<const char*> args;
|
||||
args.reserve(3);
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
static const char* gcodeviewer_param = "--gcodeviewer";
|
||||
{
|
||||
// If executed by an AppImage, start the AppImage, not the main process.
|
||||
|
|
@ -99,7 +105,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
args.emplace_back(gcodeviewer_param);
|
||||
}
|
||||
}
|
||||
#endif // __linux
|
||||
#endif // __linux__
|
||||
std::string my_path;
|
||||
if (args.empty()) {
|
||||
// Binary path was not set to the AppImage in the Linux specific block above, call the application directly.
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ void Serial::set_baud_rate(unsigned baud_rate)
|
|||
speed_t newSpeed = baud_rate;
|
||||
handle_errno(::ioctl(handle, IOSSIOSPEED, &newSpeed));
|
||||
handle_errno(::tcsetattr(handle, TCSANOW, &ios));
|
||||
#elif __linux
|
||||
#elif __linux__
|
||||
|
||||
/* The following definitions are kindly borrowed from:
|
||||
/usr/include/asm-generic/termbits.h
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue