mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-22 00:29:53 +03:00
Compare commits
12 Commits
fix/crash_
...
fix/plugin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b121051220 | ||
|
|
8b93cc5df3 | ||
|
|
a24115d996 | ||
|
|
cb35e89f82 | ||
|
|
60d1f9d502 | ||
|
|
0d6022477b | ||
|
|
4e4e867502 | ||
|
|
e28b5dae94 | ||
|
|
242e100feb | ||
|
|
2a46bd1384 | ||
|
|
ddbbc7f7f2 | ||
|
|
3a48086f5d |
@@ -202,6 +202,10 @@ void AppConfig::set_defaults()
|
||||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
||||
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
|
||||
if (get("preview_dim_previous_layers").empty())
|
||||
set_bool("preview_dim_previous_layers", false);
|
||||
|
||||
if (get("filaments_area_preferred_count").empty())
|
||||
set("filaments_area_preferred_count", "10");
|
||||
|
||||
|
||||
@@ -3246,7 +3246,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
BoundingBoxf bbox;
|
||||
auto pts = std::make_unique<ConfigOptionPoints>();
|
||||
if (print.calib_mode() == CalibMode::Calib_PA_Line || print.calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
if (print.calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
//PA_Pattern can have any size or arrangement - not dependent on 3mf model size
|
||||
bbox = bbox_bed;
|
||||
bbox.offset(-25.0);
|
||||
// add 4 corner points of bbox into pts
|
||||
@@ -3256,6 +3257,22 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
pts->values.emplace_back(bbox.max.x(), bbox.max.y());
|
||||
pts->values.emplace_back(bbox.min.x(), bbox.max.y());
|
||||
|
||||
} else if (print.calib_mode() == CalibMode::Calib_PA_Line) {
|
||||
// Derive X bounds from the actual calibration geometry.
|
||||
CalibPressureAdvanceLine temp_pa_line_forsize(this);
|
||||
BoundingBoxf pattern_extents = temp_pa_line_forsize.print_extents(bbox_bed);
|
||||
|
||||
bbox = bbox_bed;
|
||||
bbox.offset(-25.0);
|
||||
bbox.min.x() = std::max(pattern_extents.min.x(), bbox.min.x());
|
||||
bbox.max.x() = std::min(pattern_extents.max.x(), bbox.max.x());
|
||||
|
||||
pts->values.reserve(4);
|
||||
pts->values.emplace_back(bbox.min.x(), bbox.min.y());
|
||||
pts->values.emplace_back(bbox.max.x(), bbox.min.y());
|
||||
pts->values.emplace_back(bbox.max.x(), bbox.max.y());
|
||||
pts->values.emplace_back(bbox.min.x(), bbox.max.y());
|
||||
|
||||
} else {
|
||||
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
|
||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||
@@ -7532,7 +7549,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
// If adaptive PA is enabled, by default evaluate PA on all extrusion moves
|
||||
bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line ||
|
||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern ||
|
||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
|
||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
|
||||
bool evaluate_adaptive_pa = false;
|
||||
bool role_change = (m_last_extrusion_role != path.role());
|
||||
if (!is_pa_calib && FILAMENT_CONFIG(adaptive_pressure_advance) && FILAMENT_CONFIG(enable_pressure_advance)) {
|
||||
@@ -9087,7 +9104,7 @@ std::string GCode::set_object_info(Print *print) {
|
||||
std::ostringstream gcode;
|
||||
size_t object_id = 0;
|
||||
// Orca: check if we are in pa calib mode
|
||||
if (print->calib_mode() == CalibMode::Calib_PA_Line || print->calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
if (print->calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
BoundingBoxf bbox_bed(print->config().printable_area.values);
|
||||
bbox_bed.offset(-25.0);
|
||||
Polygon polygon_bed;
|
||||
@@ -9098,6 +9115,8 @@ std::string GCode::set_object_info(Print *print) {
|
||||
gcode << "EXCLUDE_OBJECT_DEFINE NAME="
|
||||
<< "Orca-PA-Calibration-Test"
|
||||
<< " CENTER=" << 0 << "," << 0 << " POLYGON=" << polygon_to_string(polygon_bed, print, true) << "\n";
|
||||
} else if (print->calib_mode() == CalibMode::Calib_PA_Line) {
|
||||
// PA_Line has only one object, no EXCLUDE_OBJECT_DEFINE needed
|
||||
} else {
|
||||
size_t unique_id = 0;
|
||||
for (PrintObject* object : print->objects()) {
|
||||
|
||||
@@ -3504,7 +3504,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
|
||||
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
|
||||
{
|
||||
//BBS: add bed_exclude_area
|
||||
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" || opt_key == "wrapping_exclude_area") {
|
||||
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" || opt_key == "wrapping_exclude_area" || opt_key == "slicing_pipeline_plugin") {
|
||||
// Scalar variable, or a vector variable, which is independent from number of extruders,
|
||||
// thus the vector is presented to the user as a single input.
|
||||
diff.emplace_back(opt_key);
|
||||
|
||||
@@ -469,6 +469,31 @@ std::string CalibPressureAdvanceLine::generate_test(double start_pa /*= 0*/, dou
|
||||
return print_pa_lines(startx, starty, start_pa, step_pa, count);
|
||||
}
|
||||
|
||||
BoundingBoxf CalibPressureAdvanceLine::print_extents(const BoundingBoxf &bed_ext) const
|
||||
{
|
||||
BoundingBoxf adjusted_bed = bed_ext;
|
||||
if (is_delta()) {
|
||||
CalibPressureAdvanceLine::delta_scale_bed_ext(adjusted_bed);
|
||||
}
|
||||
|
||||
double bed_width = adjusted_bed.size().x();
|
||||
// m_length_long adjusts for narrow beds – exactly as in generate_test()
|
||||
double line_long = 40.0 + std::min(bed_width - 120.0, 0.0);
|
||||
double total_line_len = m_length_short * 2 + line_long;
|
||||
double start_x = adjusted_bed.min.x() + (bed_width - 2 * m_length_short - line_long - 20.0) / 2.0;
|
||||
double box_width = m_draw_numbers ? (number_spacing() * 8) : 0.0; // 3.0 * 8 = 24 mm
|
||||
|
||||
BoundingBoxf extent;
|
||||
extent.min.x() = start_x;
|
||||
extent.max.x() = start_x + total_line_len + m_line_width + box_width;
|
||||
|
||||
// Y bounds are the full bed (the caller will inset them by -25)
|
||||
extent.min.y() = adjusted_bed.min.y();
|
||||
extent.max.y() = adjusted_bed.max.y();
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
bool CalibPressureAdvanceLine::is_delta() const { return mp_gcodegen->config().printable_area.values.size() > 4; }
|
||||
|
||||
std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double start_y, double start_pa, double step_pa, int num)
|
||||
@@ -491,9 +516,10 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
||||
const double slow = CalibPressureAdvance::speed_adjust(m_slow_speed);
|
||||
std::stringstream gcode;
|
||||
gcode << mp_gcodegen->writer().travel_to_z(m_height_layer + z_offset);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << (m_height_layer + z_offset) << "\n";
|
||||
double y_pos = start_y;
|
||||
|
||||
// prime line
|
||||
// Purge/first perimeter - acts as an anchor to the rest of the model
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Custom\n";
|
||||
gcode << writer.set_pressure_advance(0.0);
|
||||
auto prime_x = start_x;
|
||||
gcode << move_to(Vec2d(prime_x, y_pos + (num) * m_space_y), writer);
|
||||
@@ -503,10 +529,13 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
||||
for (int i = 0; i < num; ++i) {
|
||||
gcode << writer.set_pressure_advance(start_pa + i * step_pa);
|
||||
gcode << move_to(Vec2d(start_x, y_pos + i * m_space_y), writer);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Custom\n";
|
||||
gcode << writer.set_speed(slow);
|
||||
gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short, y_pos + i * m_space_y), e_per_mm * m_length_short);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
gcode << writer.set_speed(fast);
|
||||
gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + i * m_space_y), e_per_mm * m_length_long);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Custom\n";
|
||||
gcode << writer.set_speed(slow);
|
||||
gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long + m_length_short, y_pos + i * m_space_y),
|
||||
e_per_mm * m_length_short);
|
||||
@@ -530,9 +559,15 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
||||
|
||||
const auto box_start_x = start_x + m_length_short + m_length_long + m_length_short + m_line_width;
|
||||
DrawBoxOptArgs default_box_opt_args(2, m_height_layer, m_line_width, fast);
|
||||
//Draw box
|
||||
default_box_opt_args.is_filled = true;
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Bottom surface\n";
|
||||
gcode << draw_box(writer, box_start_x, start_y - m_space_y,
|
||||
number_spacing() * 8, (num + 1) * m_space_y, default_box_opt_args);
|
||||
//Ensure numbers are shown on the next layer in gcode processor, as in reality
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) << "\n";
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << (m_height_layer*2 + z_offset) << "\n";
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Top surface\n";
|
||||
gcode << writer.travel_to_z(m_height_layer*2 + z_offset);
|
||||
for (int i = 0; i < num; i += 2) {
|
||||
gcode << draw_number(box_start_x + 3 + m_line_width, y_pos + i * m_space_y + m_space_y / 2, start_pa + i * step_pa, m_draw_digit_mode,
|
||||
@@ -595,12 +630,16 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
speed_adjust(speed_first_layer()));
|
||||
|
||||
// create anchoring frame
|
||||
//pattern uses outer wall speed/width
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y(), print_size_x(), frame_size_y(), default_box_opt_args);
|
||||
|
||||
// create tab for numbers
|
||||
DrawBoxOptArgs draw_box_opt_args = default_box_opt_args;
|
||||
draw_box_opt_args.is_filled = true;
|
||||
draw_box_opt_args.num_perimeters = wall_count();
|
||||
//draw box as bottom surface, so numbers are clearly visible on top
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Bottom surface\n";
|
||||
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
|
||||
print_size_x(),
|
||||
max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, draw_box_opt_args);
|
||||
@@ -611,7 +650,9 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
const double zhop_config_value = m_config.option<ConfigOptionFloats>("z_hop")->get_at(0);
|
||||
const auto accel = accel_perimeter();
|
||||
|
||||
// draw pressure advance pattern
|
||||
// Draw pressure advance pattern
|
||||
// pattern uses outer wall speed, label it as such
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
for (int i = 0; i < m_num_layers; ++i) {
|
||||
const double layer_height = height_first_layer() + height_z_offset() + (i * height_layer());
|
||||
const double zhop_height = layer_height + zhop_config_value;
|
||||
@@ -643,6 +684,7 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
m_config.option<ConfigOptionFloats>("filament_flow_ratio")->get_at(0));
|
||||
|
||||
// glyph on every other line
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
for (int j = 0; j < num_patterns; j += 2) {
|
||||
gcode << draw_number(glyph_start_x(j), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
||||
m_params.start + (j * m_params.step), m_draw_digit_mode, line_width(), number_e_per_mm,
|
||||
@@ -692,7 +734,7 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
for (int j = 0; j < num_patterns; ++j) {
|
||||
// increment pressure advance
|
||||
gcode << m_writer.set_pressure_advance(m_params.start + (j * m_params.step));
|
||||
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
for (int k = 0; k < wall_count(); ++k) {
|
||||
to_x += std::cos(to_radians(m_corner_angle) / 2) * side_length;
|
||||
to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length;
|
||||
|
||||
@@ -254,6 +254,8 @@ class CalibPressureAdvanceLine : public CalibPressureAdvance
|
||||
public:
|
||||
CalibPressureAdvanceLine(GCode* gcodegen);
|
||||
~CalibPressureAdvanceLine(){};
|
||||
// Return the X‑bounds of the pattern on the given bed.
|
||||
BoundingBoxf print_extents(const BoundingBoxf &bed_ext) const;
|
||||
|
||||
std::string generate_test(double start_pa = 0, double step_pa = 0.002, int count = 50);
|
||||
|
||||
@@ -379,4 +381,4 @@ private:
|
||||
const double m_glyph_padding_vertical{1};
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -91,6 +91,13 @@ public:
|
||||
//
|
||||
void toggle_top_layer_only_view_range();
|
||||
//
|
||||
// Dim previous layers (ORCA, ported from preFlight)
|
||||
// Whether the layers below the current top layer are rendered darkened while
|
||||
// scrubbing below the full print, so only the current layer is shown at full brightness.
|
||||
//
|
||||
bool is_dim_previous_layers() const;
|
||||
void set_dim_previous_layers(bool value);
|
||||
//
|
||||
// Returns true if the given option is visible.
|
||||
//
|
||||
bool is_option_visible(EOptionType type) const;
|
||||
|
||||
@@ -19,6 +19,9 @@ struct Settings
|
||||
EViewType view_type{ EViewType::FeatureType };
|
||||
ETimeMode time_mode{ ETimeMode::Normal };
|
||||
bool top_layer_only_view_range{ false };
|
||||
// ORCA: when enabled, all layers below the current top layer are rendered
|
||||
// darkened (keeping their color) while scrubbing below the full print (ported from preFlight)
|
||||
bool dim_previous_layers{ false };
|
||||
bool spiral_vase_mode{ false };
|
||||
//
|
||||
// Required update flags
|
||||
|
||||
@@ -72,6 +72,16 @@ void Viewer::toggle_top_layer_only_view_range()
|
||||
m_impl->toggle_top_layer_only_view_range();
|
||||
}
|
||||
|
||||
bool Viewer::is_dim_previous_layers() const
|
||||
{
|
||||
return m_impl->is_dim_previous_layers();
|
||||
}
|
||||
|
||||
void Viewer::set_dim_previous_layers(bool value)
|
||||
{
|
||||
m_impl->set_dim_previous_layers(value);
|
||||
}
|
||||
|
||||
bool Viewer::is_option_visible(EOptionType type) const
|
||||
{
|
||||
return m_impl->is_option_visible(type);
|
||||
|
||||
@@ -1223,6 +1223,20 @@ static float encode_color(const Color& color) {
|
||||
return static_cast<float>(i_color);
|
||||
}
|
||||
|
||||
// ORCA: how much the layers below the current top layer are darkened when
|
||||
// Settings::dim_previous_layers is enabled (ported from preFlight). 0.0 = no change, 1.0 = black.
|
||||
static constexpr float PREVIOUS_LAYER_DARKEN_FACTOR = 0.60f;
|
||||
|
||||
// ORCA: returns the encoded color scaled towards black by 'factor', preserving its hue
|
||||
static float encode_color_darkened(const Color& color, float factor) {
|
||||
const float keep = 1.0f - factor;
|
||||
const int r = static_cast<int>(color[0] * keep);
|
||||
const int g = static_cast<int>(color[1] * keep);
|
||||
const int b = static_cast<int>(color[2] * keep);
|
||||
const int i_color = r << 16 | g << 8 | b;
|
||||
return static_cast<float>(i_color);
|
||||
}
|
||||
|
||||
|
||||
void ViewerImpl::update_colors_texture()
|
||||
{
|
||||
@@ -1234,14 +1248,30 @@ void ViewerImpl::update_colors_texture()
|
||||
const size_t top_layer_id = m_settings.top_layer_only_view_range ? m_layers.get_view_range()[1] : 0;
|
||||
const bool color_top_layer_only = m_view_range.get_full()[1] != m_view_range.get_visible()[1];
|
||||
|
||||
// ORCA: when dim_previous_layers is enabled, darken every layer below the current top layer
|
||||
// (keeping its color) whenever we are not rendering the whole print, so that only the layer
|
||||
// being scrubbed to is shown at full brightness (ported from preFlight). This shares
|
||||
// top_layer_id with the greying path, so it only applies while in top-layer-only mode - that
|
||||
// way the moves slider still animates normally across all layers when that mode is disabled.
|
||||
const bool dim_previous_layers = m_settings.dim_previous_layers && !m_layers.empty();
|
||||
const bool full_render = (m_layers.get_view_range()[0] == 0) &&
|
||||
(m_layers.get_view_range()[1] >= static_cast<uint32_t>(m_layers.count()) - 1) &&
|
||||
(m_view_range.get_visible()[1] == m_view_range.get_full()[1]);
|
||||
|
||||
// Based on current settings and slider position, we might want to render some
|
||||
// vertices as dark grey. Use either that or the normal color (from the cache).
|
||||
// vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache).
|
||||
std::vector<float> colors(m_vertices_colors.size());
|
||||
assert(colors.size() == m_vertices.size() && m_vertices_colors.size() == m_vertices.size());
|
||||
for (size_t i=0; i<m_vertices.size(); ++i)
|
||||
colors[i] = (color_top_layer_only && m_vertices[i].layer_id < top_layer_id &&
|
||||
(!m_settings.spiral_vase_mode || i != m_view_range.get_enabled()[0])) ?
|
||||
encode_color(DUMMY_COLOR) : m_vertices_colors[i];
|
||||
for (size_t i=0; i<m_vertices.size(); ++i) {
|
||||
const PathVertex& v = m_vertices[i];
|
||||
const bool keep_spiral_seam = m_settings.spiral_vase_mode && i == m_view_range.get_enabled()[0];
|
||||
if (dim_previous_layers && !full_render && v.layer_id < top_layer_id && !keep_spiral_seam)
|
||||
colors[i] = encode_color_darkened(get_vertex_color(v), PREVIOUS_LAYER_DARKEN_FACTOR);
|
||||
else if (color_top_layer_only && v.layer_id < top_layer_id && !keep_spiral_seam)
|
||||
colors[i] = encode_color(DUMMY_COLOR);
|
||||
else
|
||||
colors[i] = m_vertices_colors[i];
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL_ES
|
||||
if (!colors.empty())
|
||||
@@ -1349,6 +1379,17 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
||||
update_colors_texture();
|
||||
}
|
||||
|
||||
// ORCA: enable/disable darkening of the layers below the current top layer (ported from preFlight)
|
||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||
{
|
||||
if (m_settings.dim_previous_layers == value)
|
||||
return;
|
||||
m_settings.dim_previous_layers = value;
|
||||
// defer the actual color/texture rebuild to the next render(), when the GL context is current
|
||||
// (this may be toggled from the Preferences dialog, outside the canvas context)
|
||||
m_settings.update_colors = true;
|
||||
}
|
||||
|
||||
std::vector<ETimeMode> ViewerImpl::get_time_modes() const
|
||||
{
|
||||
std::vector<ETimeMode> ret;
|
||||
|
||||
@@ -85,6 +85,10 @@ public:
|
||||
bool is_top_layer_only_view_range() const { return m_settings.top_layer_only_view_range; }
|
||||
void toggle_top_layer_only_view_range();
|
||||
|
||||
// ORCA: darken layers below the current top layer while scrubbing (ported from preFlight)
|
||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||
void set_dim_previous_layers(bool value);
|
||||
|
||||
bool is_spiral_vase_mode() const { return m_settings.spiral_vase_mode; }
|
||||
|
||||
std::vector<ETimeMode> get_time_modes() const;
|
||||
|
||||
@@ -111,14 +111,25 @@ wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_o
|
||||
{
|
||||
wxString tooltip = _(opt.tooltip);
|
||||
|
||||
std::string opt_id = id;
|
||||
auto hash_pos = opt_id.find("#");
|
||||
std::string parameter_name = id;
|
||||
std::string opt_key = id;
|
||||
int opt_idx = 0;
|
||||
|
||||
const size_t hash_pos = static_cast<std::string>(id).find("#");
|
||||
|
||||
if (hash_pos != std::string::npos) {
|
||||
opt_id.replace(hash_pos, 1,"[");
|
||||
opt_id += "]";
|
||||
parameter_name.replace(hash_pos, 1, "[");
|
||||
parameter_name += "]";
|
||||
|
||||
std::string temp_str = id;
|
||||
boost::erase_head(temp_str, hash_pos + 1);
|
||||
size_t orig_opt_idx = atoi(temp_str.c_str());
|
||||
opt_idx = orig_opt_idx >= 0 ? orig_opt_idx : 0;
|
||||
|
||||
boost::erase_tail(opt_key, opt_key.size() - hash_pos);
|
||||
}
|
||||
|
||||
tooltip += (tooltip.empty() ? "" : "\n\n") + _(L("parameter name")) + ": " + opt_id;
|
||||
tooltip += (tooltip.empty() ? "" : "\n\n") + _(L("parameter name")) + ": " + parameter_name;
|
||||
|
||||
// Orca:
|
||||
// We can't use Orca's default values as-is because they sometimes depend on other values.
|
||||
@@ -126,7 +137,7 @@ wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_o
|
||||
if (const Preset* print_parent_preset = wxGetApp().preset_bundle->prints.get_selected_preset_parent()) {
|
||||
const DynamicPrintConfig& parent_config = print_parent_preset->config;
|
||||
|
||||
if (!parent_config.has(opt_id))
|
||||
if (!parent_config.has(opt_key))
|
||||
return tooltip;
|
||||
|
||||
wxString side_text = from_u8(opt.sidetext);
|
||||
@@ -135,18 +146,71 @@ wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_o
|
||||
if (opt.sidetext == L("layers"))
|
||||
side_text = " " + _(side_text);
|
||||
|
||||
if (opt.type == coFloat || opt.type == coInt || opt.type == coPercent || opt.type == coFloatOrPercent) {
|
||||
double default_value = 0.;
|
||||
if (opt.type == coFloat || opt.type == coInt || opt.type == coPercent || opt.type == coFloatOrPercent ||
|
||||
opt.type == coFloats || opt.type == coInts || opt.type == coPercents || opt.type == coFloatsOrPercents) {
|
||||
double default_value = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
if (opt.type == coFloat)
|
||||
default_value = parent_config.option<ConfigOptionFloat>(opt_id)->value;
|
||||
default_value = parent_config.option<ConfigOptionFloat>(opt_key)->value;
|
||||
else if (opt.type == coFloats) {
|
||||
if (opt.nullable) {
|
||||
auto opt_floats_nullable = parent_config.option<ConfigOptionFloatsNullable>(opt_key);
|
||||
if (!opt_floats_nullable->values.empty())
|
||||
default_value = opt_floats_nullable->get_at(opt_idx);
|
||||
} else {
|
||||
auto opt_floats = parent_config.option<ConfigOptionFloats>(opt_key);
|
||||
if (!opt_floats->values.empty())
|
||||
default_value = opt_floats->get_at(opt_idx);
|
||||
}
|
||||
}
|
||||
else if (opt.type == coInt)
|
||||
default_value = parent_config.option<ConfigOptionInt>(opt_id)->value;
|
||||
default_value = parent_config.option<ConfigOptionInt>(opt_key)->value;
|
||||
else if(opt.type == coInts) {
|
||||
if (opt.nullable) {
|
||||
auto opt_ints_nullable = parent_config.option<ConfigOptionIntsNullable>(opt_key);
|
||||
if (!opt_ints_nullable->values.empty())
|
||||
default_value = opt_ints_nullable->get_at(opt_idx);
|
||||
} else {
|
||||
auto opt_ints = parent_config.option<ConfigOptionInts>(opt_key);
|
||||
if (!opt_ints->values.empty())
|
||||
default_value = opt_ints->get_at(opt_idx);
|
||||
}
|
||||
}
|
||||
else if (opt.type == coPercent)
|
||||
default_value = parent_config.option<ConfigOptionPercent>(opt_id)->value;
|
||||
else if (opt.type == coFloatOrPercent) {
|
||||
default_value = parent_config.option<ConfigOptionFloatOrPercent>(opt_id)->value;
|
||||
if (parent_config.option<ConfigOptionFloatOrPercent>(opt_id)->percent)
|
||||
default_value = parent_config.option<ConfigOptionPercent>(opt_key)->value;
|
||||
else if (opt.type == coPercents) {
|
||||
if (opt.nullable) {
|
||||
auto opt_percents_nullable = parent_config.option<ConfigOptionPercentsNullable>(opt_key);
|
||||
if (!opt_percents_nullable->values.empty())
|
||||
default_value = opt_percents_nullable->get_at(opt_idx);
|
||||
} else {
|
||||
auto opt_percents = parent_config.option<ConfigOptionPercents>(opt_key);
|
||||
if (!opt_percents->values.empty())
|
||||
default_value = opt_percents->get_at(opt_idx);
|
||||
}
|
||||
}
|
||||
else if (opt.type == coFloatOrPercent || opt.type == coFloatsOrPercents) {
|
||||
bool is_percent = false;
|
||||
if (opt.type == coFloatOrPercent) {
|
||||
default_value = parent_config.option<ConfigOptionFloatOrPercent>(opt_key)->value;
|
||||
is_percent = parent_config.option<ConfigOptionFloatOrPercent>(opt_key)->percent;
|
||||
} else if (opt.type == coFloatsOrPercents) {
|
||||
if (opt.nullable) {
|
||||
auto opt_floats_or_percents_nullable = parent_config.option<ConfigOptionFloatsOrPercentsNullable>(opt_key);
|
||||
if (!opt_floats_or_percents_nullable->values.empty()) {
|
||||
default_value = opt_floats_or_percents_nullable->get_at(opt_idx).value;
|
||||
is_percent = opt_floats_or_percents_nullable->get_at(opt_idx).percent;
|
||||
}
|
||||
} else {
|
||||
auto opt_floats_or_percents = parent_config.option<ConfigOptionFloatsOrPercents>(opt_key);
|
||||
if (!opt_floats_or_percents->values.empty()) {
|
||||
default_value = opt_floats_or_percents->get_at(opt_idx).value;
|
||||
is_percent = opt_floats_or_percents->get_at(opt_idx).percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_percent)
|
||||
side_text = "%";
|
||||
else if (!side_text.empty()) {
|
||||
static std::string postfix = " or %";
|
||||
@@ -156,20 +220,38 @@ wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_o
|
||||
}
|
||||
}
|
||||
|
||||
tooltip += "\n\n" + _(L("Default")) + ": " + _(double_to_string(default_value)) + _(side_text);
|
||||
if (!std::isnan(default_value))
|
||||
tooltip += "\n\n" + _(L("Default")) + ": " + _(double_to_string(default_value)) + _(side_text);
|
||||
|
||||
if (opt.min > -FLT_MAX && opt.max < FLT_MAX) {
|
||||
tooltip += "\n" + _(L("Range")) + ": [" +
|
||||
_(double_to_string(opt.min)) + _(side_text) + ", " +
|
||||
_(double_to_string(opt.max)) + _(side_text) + "]";
|
||||
}
|
||||
} else if (opt.type == coBool || opt.type == coString) {
|
||||
} else if (opt.type == coBool || opt.type == coBools || opt.type == coString || opt.type == coStrings) {
|
||||
std::string default_value = "";
|
||||
|
||||
if (opt.type == coString)
|
||||
default_value = parent_config.option<ConfigOptionString>(opt_id)->value;
|
||||
default_value = parent_config.option<ConfigOptionString>(opt_key)->value;
|
||||
else if (opt.type == coStrings) {
|
||||
auto opt_strings = parent_config.option<ConfigOptionStrings>(opt_key);
|
||||
|
||||
if (!opt_strings->values.empty())
|
||||
default_value = opt_strings->get_at(opt_idx);
|
||||
}
|
||||
else if (opt.type == coBool)
|
||||
default_value = parent_config.option<ConfigOptionBool>(opt_id)->value ? "true" : "false";
|
||||
default_value = parent_config.option<ConfigOptionBool>(opt_key)->value != 0 ? "true" : "false";
|
||||
else if (opt.type == coBools) {
|
||||
if (opt.nullable) {
|
||||
auto opt_bools_nullable = parent_config.option<ConfigOptionBoolsNullable>(opt_key);
|
||||
if (!opt_bools_nullable->values.empty())
|
||||
default_value = opt_bools_nullable->get_at(opt_idx) != 0 ? "true" : "false";
|
||||
} else {
|
||||
auto opt_bools = parent_config.option<ConfigOptionBools>(opt_key);
|
||||
if (!opt_bools->values.empty())
|
||||
default_value = opt_bools->get_at(opt_idx) != 0 ? "true" : "false";
|
||||
}
|
||||
}
|
||||
|
||||
tooltip += "\n\n" + _(L("Default")) + ": " +
|
||||
(default_value.empty() ? _(L("Empty string")) : _(default_value) + _(side_text));
|
||||
|
||||
@@ -1134,6 +1134,9 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
||||
if (current_top_layer_only != required_top_layer_only)
|
||||
m_viewer.toggle_top_layer_only_view_range();
|
||||
|
||||
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
|
||||
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
||||
|
||||
// avoid processing if called with the same gcode_result
|
||||
if (m_last_result_id == gcode_result.id && wxGetApp().is_editor()) {
|
||||
//BBS: add logs
|
||||
|
||||
@@ -333,6 +333,10 @@ public:
|
||||
|
||||
libvgcode::EViewType get_view_type() const { return m_viewer.get_view_type(); }
|
||||
|
||||
// ORCA: darken layers below the current top layer while scrubbing the preview (ported from preFlight)
|
||||
void set_dim_previous_layers(bool value) { m_viewer.set_dim_previous_layers(value); }
|
||||
bool is_dim_previous_layers() const { return m_viewer.is_dim_previous_layers(); }
|
||||
|
||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||
|
||||
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/DialogButtons.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "slic3r/Utils/bambu_networking.hpp"
|
||||
@@ -12,6 +13,10 @@
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/collpane.h>
|
||||
|
||||
#define BORDER_W FromDIP(20)
|
||||
#define TEXT_WRAP FromDIP(400)
|
||||
#define DIALOG_WIDTH FromDIP(440)
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@@ -30,10 +35,10 @@ NetworkPluginDownloadDialog::NetworkPluginDownloadDialog(wxWindow* parent, Mode
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1));
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(DIALOG_WIDTH, 1));
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
main_sizer->AddSpacer(BORDER_W);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
|
||||
@@ -52,157 +57,136 @@ void NetworkPluginDownloadDialog::create_missing_plugin_ui()
|
||||
{
|
||||
wxBoxSizer* main_sizer = static_cast<wxBoxSizer*>(GetSizer());
|
||||
|
||||
auto* desc = new wxStaticText(this, wxID_ANY,
|
||||
auto* desc = new Label(this,
|
||||
m_mode == Mode::CorruptedPlugin ?
|
||||
_L("The Bambu Network Plug-in is corrupted or incompatible. Please reinstall it.") :
|
||||
_L("The Bambu Network Plug-in is required for cloud features, printer discovery, and remote printing."));
|
||||
desc->SetFont(::Label::Body_13);
|
||||
desc->Wrap(FromDIP(400));
|
||||
main_sizer->Add(desc, 0, wxLEFT | wxRIGHT, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(15));
|
||||
desc->Wrap(TEXT_WRAP);
|
||||
desc->SetMaxSize(wxSize(TEXT_WRAP, -1));
|
||||
main_sizer->Add(desc, 0, wxLEFT | wxRIGHT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(15));
|
||||
|
||||
if (!m_error_message.empty()) {
|
||||
auto* error_label = new wxStaticText(this, wxID_ANY,
|
||||
wxString::Format(_L("Error: %s"), wxString::FromUTF8(m_error_message)));
|
||||
error_label->SetFont(::Label::Body_13);
|
||||
error_label->SetForegroundColour(wxColour(208, 93, 93));
|
||||
error_label->Wrap(FromDIP(400));
|
||||
main_sizer->Add(error_label, 0, wxLEFT | wxRIGHT, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
error_label->Wrap(TEXT_WRAP);
|
||||
error_label->SetMaxSize(wxSize(TEXT_WRAP, -1));
|
||||
main_sizer->Add(error_label, 0, wxLEFT | wxRIGHT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
if (!m_error_details.empty()) {
|
||||
m_details_pane = new wxCollapsiblePane(this, wxID_ANY, _L("Show details"));
|
||||
auto* pane = m_details_pane->GetPane();
|
||||
auto* pane_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto expand_btn = new Button(this, _L("Show details"));
|
||||
expand_btn->SetStyle(ButtonStyle::Regular, ButtonType::Compact);
|
||||
main_sizer->Add(expand_btn, 0, wxLEFT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
auto details_text = new wxTextCtrl(this, wxID_ANY, wxString::FromUTF8(m_error_details),
|
||||
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxNO_BORDER);
|
||||
|
||||
auto* details_text = new wxStaticText(pane, wxID_ANY, wxString::FromUTF8(m_error_details));
|
||||
details_text->SetFont(wxGetApp().code_font());
|
||||
details_text->Wrap(FromDIP(380));
|
||||
pane_sizer->Add(details_text, 0, wxALL, FromDIP(10));
|
||||
details_text->SetBackgroundColour(wxColour("#F1F1F1"));
|
||||
details_text->SetMaxSize(wxSize(TEXT_WRAP, -1));
|
||||
main_sizer->Add(details_text, 0, wxLEFT | wxRIGHT | wxEXPAND, BORDER_W);
|
||||
|
||||
pane->SetSizer(pane_sizer);
|
||||
main_sizer->Add(m_details_pane, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
details_text->Hide();
|
||||
|
||||
expand_btn->Bind(wxEVT_BUTTON, [this, details_text, expand_btn](wxCommandEvent&){
|
||||
Freeze();
|
||||
details_text->Show(!details_text->IsShown());
|
||||
expand_btn->SetLabel(details_text->IsShown() ? _L("Hide details") : _L("Show details"));
|
||||
Layout();
|
||||
Fit();
|
||||
Refresh();
|
||||
Thaw();
|
||||
});
|
||||
|
||||
main_sizer->AddSpacer(FromDIP(15));
|
||||
}
|
||||
}
|
||||
|
||||
auto* version_label = new wxStaticText(this, wxID_ANY, _L("Version to install:"));
|
||||
version_label->SetFont(::Label::Body_13);
|
||||
main_sizer->Add(version_label, 0, wxLEFT | wxRIGHT, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
||||
auto* version_label = new Label(this, _L("Version to install:"));
|
||||
main_sizer->Add(version_label, 0, wxLEFT | wxRIGHT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(3));
|
||||
|
||||
setup_version_selector();
|
||||
main_sizer->Add(m_version_combo, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
|
||||
auto* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
||||
main_sizer->Add(m_version_combo, 0, wxLEFT | wxRIGHT | wxEXPAND, BORDER_W);
|
||||
main_sizer->AddSpacer(15);
|
||||
|
||||
StateColor btn_bg_green(
|
||||
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
|
||||
auto dlg_btns = new DialogButtons(this,
|
||||
{"Download and Install", "Skip for Now"},
|
||||
_L("Download and Install") // Primary button
|
||||
);
|
||||
|
||||
StateColor btn_bg_white(
|
||||
std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
dlg_btns->GetButtonFromIndex(0)->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_download, this);
|
||||
dlg_btns->GetButtonFromIndex(1)->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_skip, this);
|
||||
|
||||
auto* btn_download = new Button(this, _L("Download and Install"));
|
||||
btn_download->SetBackgroundColor(btn_bg_green);
|
||||
btn_download->SetBorderColor(*wxWHITE);
|
||||
btn_download->SetTextColor(*wxWHITE);
|
||||
btn_download->SetFont(::Label::Body_12);
|
||||
btn_download->SetMinSize(wxSize(FromDIP(120), FromDIP(24)));
|
||||
btn_download->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_download, this);
|
||||
btn_sizer->Add(btn_download, 0, wxRIGHT, FromDIP(10));
|
||||
|
||||
auto* btn_skip = new Button(this, _L("Skip for Now"));
|
||||
btn_skip->SetBackgroundColor(btn_bg_white);
|
||||
btn_skip->SetBorderColor(wxColour(38, 46, 48));
|
||||
btn_skip->SetFont(::Label::Body_12);
|
||||
btn_skip->SetMinSize(wxSize(FromDIP(100), FromDIP(24)));
|
||||
btn_skip->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_skip, this);
|
||||
btn_sizer->Add(btn_skip, 0, wxRIGHT, FromDIP(10));
|
||||
|
||||
main_sizer->Add(btn_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20));
|
||||
main_sizer->Add(0, 0, 0, wxBOTTOM, FromDIP(20));
|
||||
main_sizer->Add(dlg_btns, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, FromDIP(10));
|
||||
}
|
||||
|
||||
void NetworkPluginDownloadDialog::create_update_available_ui(const std::string& current_version)
|
||||
{
|
||||
wxBoxSizer* main_sizer = static_cast<wxBoxSizer*>(GetSizer());
|
||||
|
||||
auto* desc = new wxStaticText(this, wxID_ANY,
|
||||
auto* desc = new Label(this,
|
||||
_L("A new version of the Bambu Network Plug-in is available."));
|
||||
desc->SetFont(::Label::Body_13);
|
||||
desc->Wrap(FromDIP(400));
|
||||
main_sizer->Add(desc, 0, wxLEFT | wxRIGHT, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(15));
|
||||
desc->Wrap(TEXT_WRAP);
|
||||
desc->SetMaxSize(wxSize(TEXT_WRAP, -1));
|
||||
main_sizer->Add(desc, 0, wxLEFT | wxRIGHT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(15));
|
||||
|
||||
auto* version_text = new wxStaticText(this, wxID_ANY,
|
||||
auto* version_text = new Label(this,
|
||||
wxString::Format(_L("Current version: %s"), wxString::FromUTF8(current_version)));
|
||||
version_text->SetFont(::Label::Body_13);
|
||||
main_sizer->Add(version_text, 0, wxLEFT | wxRIGHT, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
main_sizer->Add(version_text, 0, wxLEFT | wxRIGHT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(15));
|
||||
|
||||
auto* update_label = new wxStaticText(this, wxID_ANY, _L("Update to version:"));
|
||||
update_label->SetFont(::Label::Body_13);
|
||||
main_sizer->Add(update_label, 0, wxLEFT | wxRIGHT, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
||||
auto* update_label = new Label(this, _L("Update to version:"));
|
||||
main_sizer->Add(update_label, 0, wxLEFT | wxRIGHT, BORDER_W);
|
||||
main_sizer->AddSpacer(FromDIP(3));
|
||||
|
||||
setup_version_selector();
|
||||
main_sizer->Add(m_version_combo, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(25));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
main_sizer->Add(m_version_combo, 0, wxLEFT | wxRIGHT | wxEXPAND, BORDER_W);
|
||||
main_sizer->AddSpacer(20);
|
||||
|
||||
auto* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
||||
auto daa_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto cfg = wxGetApp().app_config;
|
||||
|
||||
StateColor btn_bg_green(
|
||||
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
|
||||
auto daa_chk = new CheckBox(this);
|
||||
daa_chk->SetValue(cfg->is_network_update_prompt_disabled());
|
||||
daa_chk->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e){
|
||||
auto cfg = wxGetApp().app_config;
|
||||
cfg->set_network_update_prompt_disabled(e.IsChecked());
|
||||
cfg->save();
|
||||
});
|
||||
|
||||
StateColor btn_bg_white(
|
||||
std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
auto daa_str = new Label(this, _L("Don't Ask Again"));
|
||||
auto on_toggle = [this, daa_chk]() {
|
||||
daa_chk->SetValue(!daa_chk->GetValue());
|
||||
wxCommandEvent evt(wxEVT_TOGGLEBUTTON, daa_chk->GetId());
|
||||
evt.SetEventObject(daa_chk);
|
||||
daa_chk->GetEventHandler()->ProcessEvent(evt);
|
||||
};
|
||||
daa_str->Bind(wxEVT_LEFT_DOWN, [on_toggle](wxMouseEvent& e) {if(!e.LeftDClick()) on_toggle();});
|
||||
daa_str->Bind(wxEVT_LEFT_DCLICK, [on_toggle](wxMouseEvent& e) {on_toggle();});
|
||||
|
||||
auto* btn_download = new Button(this, _L("Update Now"));
|
||||
btn_download->SetBackgroundColor(btn_bg_green);
|
||||
btn_download->SetBorderColor(*wxWHITE);
|
||||
btn_download->SetTextColor(*wxWHITE);
|
||||
btn_download->SetFont(::Label::Body_12);
|
||||
btn_download->SetMinSize(wxSize(FromDIP(100), FromDIP(24)));
|
||||
btn_download->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_download, this);
|
||||
btn_sizer->Add(btn_download, 0, wxRIGHT, FromDIP(10));
|
||||
daa_sizer->Add(daa_chk, 0, wxALIGN_CENTER_VERTICAL);
|
||||
daa_sizer->Add(daa_str, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
|
||||
|
||||
auto* btn_remind = new Button(this, _L("Remind Later"));
|
||||
btn_remind->SetBackgroundColor(btn_bg_white);
|
||||
btn_remind->SetBorderColor(wxColour(38, 46, 48));
|
||||
btn_remind->SetFont(::Label::Body_12);
|
||||
btn_remind->SetMinSize(wxSize(FromDIP(100), FromDIP(24)));
|
||||
btn_remind->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_remind_later, this);
|
||||
btn_sizer->Add(btn_remind, 0, wxRIGHT, FromDIP(10));
|
||||
main_sizer->Add(daa_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, BORDER_W);
|
||||
main_sizer->AddSpacer(10);
|
||||
|
||||
auto* btn_skip = new Button(this, _L("Skip Version"));
|
||||
btn_skip->SetBackgroundColor(btn_bg_white);
|
||||
btn_skip->SetBorderColor(wxColour(38, 46, 48));
|
||||
btn_skip->SetFont(::Label::Body_12);
|
||||
btn_skip->SetMinSize(wxSize(FromDIP(100), FromDIP(24)));
|
||||
btn_skip->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_skip_version, this);
|
||||
btn_sizer->Add(btn_skip, 0, wxRIGHT, FromDIP(10));
|
||||
auto dlg_btns = new DialogButtons(this,
|
||||
{"Update Now", "Remind Later", "Skip Version"},
|
||||
_L("Update Now")
|
||||
);
|
||||
|
||||
auto* btn_dont_ask = new Button(this, _L("Don't Ask Again"));
|
||||
btn_dont_ask->SetBackgroundColor(btn_bg_white);
|
||||
btn_dont_ask->SetBorderColor(wxColour(38, 46, 48));
|
||||
btn_dont_ask->SetFont(::Label::Body_12);
|
||||
btn_dont_ask->SetMinSize(wxSize(FromDIP(110), FromDIP(24)));
|
||||
btn_dont_ask->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_dont_ask, this);
|
||||
btn_sizer->Add(btn_dont_ask, 0, wxRIGHT, FromDIP(10));
|
||||
dlg_btns->GetButtonFromIndex(0)->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_download, this);
|
||||
dlg_btns->GetButtonFromIndex(1)->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_remind_later, this);
|
||||
dlg_btns->GetButtonFromIndex(2)->Bind(wxEVT_BUTTON, &NetworkPluginDownloadDialog::on_skip_version, this);
|
||||
|
||||
main_sizer->Add(btn_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20));
|
||||
main_sizer->Add(0, 0, 0, wxBOTTOM, FromDIP(20));
|
||||
main_sizer->Add(dlg_btns, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, FromDIP(10));
|
||||
}
|
||||
|
||||
wxString network_version_label(const NetworkLibraryVersionInfo& ver)
|
||||
@@ -222,7 +206,6 @@ void NetworkPluginDownloadDialog::setup_version_selector()
|
||||
{
|
||||
m_version_combo = new ComboBox(this, wxID_ANY, wxEmptyString,
|
||||
wxDefaultPosition, wxSize(FromDIP(380), FromDIP(28)), 0, nullptr, wxCB_READONLY);
|
||||
m_version_combo->SetFont(::Label::Body_13);
|
||||
|
||||
m_available_versions = get_all_available_versions();
|
||||
for (const auto& ver : m_available_versions)
|
||||
@@ -294,10 +277,10 @@ NetworkPluginRestartDialog::NetworkPluginRestartDialog(wxWindow* parent)
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1));
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(DIALOG_WIDTH, 1));
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
main_sizer->AddSpacer(BORDER_W);
|
||||
|
||||
auto* icon_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* icon_bitmap = new wxStaticBitmap(this, wxID_ANY,
|
||||
@@ -306,61 +289,39 @@ NetworkPluginRestartDialog::NetworkPluginRestartDialog(wxWindow* parent)
|
||||
|
||||
auto* text_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* desc = new wxStaticText(this, wxID_ANY,
|
||||
auto* desc = new Label(this,
|
||||
_L("The Bambu Network Plug-in has been installed successfully."));
|
||||
desc->SetFont(::Label::Body_14);
|
||||
desc->Wrap(FromDIP(350));
|
||||
desc->Wrap(TEXT_WRAP);
|
||||
desc->SetMaxSize(wxSize(TEXT_WRAP, -1));
|
||||
text_sizer->Add(desc, 0, wxTOP, FromDIP(10));
|
||||
text_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
text_sizer->AddSpacer(FromDIP(10));
|
||||
|
||||
auto* restart_msg = new wxStaticText(this, wxID_ANY,
|
||||
auto* restart_msg = new Label(this,
|
||||
_L("A restart is required to load the new plug-in. Would you like to restart now?"));
|
||||
restart_msg->SetFont(::Label::Body_13);
|
||||
restart_msg->Wrap(FromDIP(350));
|
||||
restart_msg->Wrap(TEXT_WRAP);
|
||||
restart_msg->SetMaxSize(wxSize(TEXT_WRAP, -1));
|
||||
text_sizer->Add(restart_msg, 0, wxBOTTOM, FromDIP(10));
|
||||
|
||||
icon_sizer->Add(text_sizer, 1, wxEXPAND | wxRIGHT, FromDIP(20));
|
||||
main_sizer->Add(icon_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(15));
|
||||
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
icon_sizer->Add(text_sizer, 1, wxEXPAND | wxRIGHT, BORDER_W);
|
||||
main_sizer->Add(icon_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, BORDER_W);
|
||||
main_sizer->AddSpacer(15);
|
||||
|
||||
auto* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
||||
auto dlg_btns = new DialogButtons(this,
|
||||
{"Restart Now", "Restart Later"},
|
||||
_L("Restart Now") // Primary button
|
||||
);
|
||||
|
||||
StateColor btn_bg_green(
|
||||
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
|
||||
|
||||
StateColor btn_bg_white(
|
||||
std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
|
||||
auto* btn_restart = new Button(this, _L("Restart Now"));
|
||||
btn_restart->SetBackgroundColor(btn_bg_green);
|
||||
btn_restart->SetBorderColor(*wxWHITE);
|
||||
btn_restart->SetTextColor(*wxWHITE);
|
||||
btn_restart->SetFont(::Label::Body_12);
|
||||
btn_restart->SetMinSize(wxSize(FromDIP(100), FromDIP(24)));
|
||||
btn_restart->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
dlg_btns->GetButtonFromIndex(0)->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
m_restart_now = true;
|
||||
EndModal(wxID_OK);
|
||||
});
|
||||
btn_sizer->Add(btn_restart, 0, wxRIGHT, FromDIP(10));
|
||||
|
||||
auto* btn_later = new Button(this, _L("Restart Later"));
|
||||
btn_later->SetBackgroundColor(btn_bg_white);
|
||||
btn_later->SetBorderColor(wxColour(38, 46, 48));
|
||||
btn_later->SetFont(::Label::Body_12);
|
||||
btn_later->SetMinSize(wxSize(FromDIP(100), FromDIP(24)));
|
||||
btn_later->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
dlg_btns->GetButtonFromIndex(1)->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
m_restart_now = false;
|
||||
EndModal(wxID_CANCEL);
|
||||
});
|
||||
btn_sizer->Add(btn_later, 0, wxRIGHT, FromDIP(10));
|
||||
|
||||
main_sizer->Add(btn_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20));
|
||||
main_sizer->Add(0, 0, 0, wxBOTTOM, FromDIP(20));
|
||||
|
||||
main_sizer->Add(dlg_btns, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, FromDIP(10));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
|
||||
@@ -56,7 +56,6 @@ private:
|
||||
|
||||
Mode m_mode;
|
||||
ComboBox* m_version_combo{nullptr};
|
||||
wxCollapsiblePane* m_details_pane{nullptr};
|
||||
std::string m_error_message;
|
||||
std::string m_error_details;
|
||||
std::vector<NetworkLibraryVersionInfo> m_available_versions;
|
||||
|
||||
@@ -13513,7 +13513,7 @@ bool Plater::up_to_date(bool saved, bool backup)
|
||||
!Slic3r::has_other_changes(backup));
|
||||
}
|
||||
|
||||
void Plater::add_model(bool imperial_units, std::string fname)
|
||||
bool Plater::add_model(bool imperial_units, std::string fname)
|
||||
{
|
||||
wxArrayString input_files;
|
||||
|
||||
@@ -13521,7 +13521,7 @@ void Plater::add_model(bool imperial_units, std::string fname)
|
||||
if (fname.empty()) {
|
||||
wxGetApp().import_model(this, input_files);
|
||||
if (input_files.empty())
|
||||
return;
|
||||
return false;
|
||||
|
||||
for (const auto& file : input_files)
|
||||
paths.emplace_back(into_path(file));
|
||||
@@ -13565,7 +13565,8 @@ void Plater::add_model(bool imperial_units, std::string fname)
|
||||
|
||||
auto strategy = LoadStrategy::LoadModel;
|
||||
if (imperial_units) strategy = strategy | LoadStrategy::ImperialUnits;
|
||||
if (!load_files(paths, strategy, ask_multi).empty()) {
|
||||
const bool loaded = !load_files(paths, strategy, ask_multi).empty();
|
||||
if (loaded) {
|
||||
|
||||
if (get_project_name() == _L("Untitled") && paths.size() > 0) {
|
||||
boost::filesystem::path full_path(paths[0].string());
|
||||
@@ -13574,6 +13575,7 @@ void Plater::add_model(bool imperial_units, std::string fname)
|
||||
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
void Plater::calib_pa(const Calib_Params& params)
|
||||
@@ -13860,7 +13862,8 @@ void Plater::cut_horizontal(size_t obj_idx, size_t instance_idx, double z, Model
|
||||
}
|
||||
|
||||
void Plater::_calib_pa_tower(const Calib_Params& params) {
|
||||
add_model(false, Slic3r::resources_dir() + "/calib/pressure_advance/tower_with_seam.drc");
|
||||
if (!add_model(false, Slic3r::resources_dir() + "/calib/pressure_advance/tower_with_seam.drc"))
|
||||
return;
|
||||
|
||||
auto& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
@@ -14100,7 +14103,8 @@ void Plater::calib_temp(const Calib_Params& params) {
|
||||
wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor));
|
||||
if (params.mode != CalibMode::Calib_Temp_Tower) return;
|
||||
|
||||
add_model(false, Slic3r::resources_dir() + "/calib/temperature_tower/temperature_tower.drc");
|
||||
if (!add_model(false, Slic3r::resources_dir() + "/calib/temperature_tower/temperature_tower.drc"))
|
||||
return;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
auto start_temp = lround(params.start);
|
||||
@@ -14176,7 +14180,8 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
|
||||
wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor));
|
||||
if (params.mode != CalibMode::Calib_Vol_speed_Tower)
|
||||
return;
|
||||
add_model(false, Slic3r::resources_dir() + "/calib/volumetric_speed/SpeedTestStructure.drc");
|
||||
if (!add_model(false, Slic3r::resources_dir() + "/calib/volumetric_speed/SpeedTestStructure.drc"))
|
||||
return;
|
||||
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
@@ -14255,7 +14260,8 @@ void Plater::calib_retraction(const Calib_Params& params)
|
||||
if (params.mode != CalibMode::Calib_Retraction_tower)
|
||||
return;
|
||||
|
||||
add_model(false, Slic3r::resources_dir() + "/calib/retraction/retraction_tower.drc");
|
||||
if (!add_model(false, Slic3r::resources_dir() + "/calib/retraction/retraction_tower.drc"))
|
||||
return;
|
||||
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
@@ -14314,7 +14320,8 @@ void Plater::calib_VFA(const Calib_Params& params)
|
||||
if (params.mode != CalibMode::Calib_VFA_Tower)
|
||||
return;
|
||||
|
||||
add_model(false, Slic3r::resources_dir() + "/calib/vfa/vfa.drc");
|
||||
if (!add_model(false, Slic3r::resources_dir() + "/calib/vfa/vfa.drc"))
|
||||
return;
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
@@ -14359,7 +14366,8 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
|
||||
if (params.mode != CalibMode::Calib_Input_shaping_freq)
|
||||
return;
|
||||
|
||||
add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc"));
|
||||
if (!add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc")))
|
||||
return;
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
@@ -14424,7 +14432,8 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
|
||||
if (params.mode != CalibMode::Calib_Input_shaping_damp)
|
||||
return;
|
||||
|
||||
add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc"));
|
||||
if (!add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc")))
|
||||
return;
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
@@ -14491,7 +14500,8 @@ void Plater::Calib_Cornering(const Calib_Params& params)
|
||||
const std::string cornering_model_path = params.test_model == 0
|
||||
? "/calib/input_shaping/ringing_tower.drc"
|
||||
: (params.test_model == 1 ? "/calib/input_shaping/fast_tower_test.drc" : "/calib/cornering/SCV-V2.drc");
|
||||
add_model(false, Slic3r::resources_dir() + cornering_model_path);
|
||||
if (!add_model(false, Slic3r::resources_dir() + cornering_model_path))
|
||||
return;
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
|
||||
@@ -328,7 +328,8 @@ public:
|
||||
bool open_3mf_file(const fs::path &file_path);
|
||||
int get_3mf_file_count(std::vector<fs::path> paths);
|
||||
void add_file();
|
||||
void add_model(bool imperial_units = false, std::string fname = "");
|
||||
// Returns false when no object was added (e.g. the user cancelled the load dialog).
|
||||
bool add_model(bool imperial_units = false, std::string fname = "");
|
||||
void import_zip_archive();
|
||||
void import_sl1_archive();
|
||||
void extract_config_from_project();
|
||||
|
||||
@@ -48,13 +48,14 @@ std::string plugin_defaults_user_script()
|
||||
return WebViewHostDialog::document_start_injector(css, "orca-plugin-defaults", "beforeend");
|
||||
}
|
||||
|
||||
// Injected into every page at document start (before the plugin's own scripts).
|
||||
// Defines window.orca as the only host surface the page may use. It references
|
||||
// window.wx lazily (at call time) so it never races the backend's deferred
|
||||
// registration of the "wx" message handler. Guarded against double-injection so
|
||||
// it is harmless if also prepended.
|
||||
// Injected into the top-level page at document start (before the plugin's own
|
||||
// scripts). Defines window.orca as the only host surface the page may use. It
|
||||
// references window.wx lazily (at call time) so it never races the backend's
|
||||
// deferred registration of the "wx" message handler. Guarded against
|
||||
// double-injection so it is harmless if also prepended.
|
||||
constexpr char ORCA_BRIDGE_JS[] = R"JS(
|
||||
(function () {
|
||||
if (window.top !== window.self) return;
|
||||
if (window.orca) return;
|
||||
var handlers = [];
|
||||
function send(kind, data) {
|
||||
|
||||
@@ -300,7 +300,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
|
||||
|
||||
add_action("open_folder", "Show in folder", has_local);
|
||||
|
||||
add_action("reinstall_plugin", "Reinstall");
|
||||
if (is_cloud) {
|
||||
add_action("reinstall_plugin", "Reinstall");
|
||||
} else {
|
||||
add_action("reload_plugin", "Reload");
|
||||
add_action("clear_cache_reload_plugin", "Delete cache and reload");
|
||||
}
|
||||
|
||||
return available_actions;
|
||||
}
|
||||
@@ -739,11 +744,13 @@ void PluginsDialog::handle_plugin_menu_action(const std::string& plugin_key, con
|
||||
unsubscribe_cloud_plugin(row_data);
|
||||
} else if (action == "delete_mine_plugin") {
|
||||
delete_mine_local_and_cloud_plugin(plugin_key);
|
||||
} else if (action == "reload_plugin") {
|
||||
reload_local_plugin(plugin_key, /*clear_cache=*/false);
|
||||
} else if (action == "clear_cache_reload_plugin") {
|
||||
reload_local_plugin(plugin_key, /*clear_cache=*/true);
|
||||
} else if (action == "reinstall_plugin") {
|
||||
if (row_data.is_cloud_plugin())
|
||||
reinstall_cloud_plugin(row_data);
|
||||
else
|
||||
reinstall_local_plugin(plugin_key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1123,7 +1130,7 @@ void PluginsDialog::unsubscribe_cloud_plugin(const PluginDescriptor& plugin)
|
||||
_L("Unsubscribing plugin"), _L("Deleting local files and unsubscribing plugin..."));
|
||||
}
|
||||
|
||||
void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key)
|
||||
void PluginsDialog::reload_local_plugin(const std::string& plugin_key, bool clear_cache)
|
||||
{
|
||||
if (plugin_key.empty())
|
||||
return;
|
||||
@@ -1132,11 +1139,34 @@ void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key)
|
||||
std::pair<bool, std::string> reload_result{false, ""};
|
||||
try {
|
||||
reload_result = run_with_dialog_wait(
|
||||
[plugin_key, was_loaded]() -> std::pair<bool, std::string> {
|
||||
[plugin_key, was_loaded, clear_cache]() -> std::pair<bool, std::string> {
|
||||
PluginManager& manager = PluginManager::instance();
|
||||
|
||||
boost::filesystem::path cache_dir;
|
||||
if (clear_cache) {
|
||||
PluginDescriptor descriptor;
|
||||
if (!manager.try_get_plugin_descriptor(plugin_key, descriptor))
|
||||
return {false, "Plugin not found."};
|
||||
|
||||
boost::filesystem::path resolved_root;
|
||||
std::string resolve_error;
|
||||
if (!resolve_allowed_plugin_root(descriptor, {get_orca_plugins_dir()},
|
||||
"Refusing to clear a plugin cache outside the local plugin directory.",
|
||||
resolved_root, resolve_error))
|
||||
return {false, resolve_error};
|
||||
cache_dir = resolved_root / "__whl_extracted__";
|
||||
}
|
||||
|
||||
if (!manager.unload_plugin(plugin_key))
|
||||
return {false, "Failed to unload plugin."};
|
||||
|
||||
if (clear_cache) {
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::remove_all(cache_dir, ec);
|
||||
if (ec)
|
||||
return {false, "Failed to clear plugin cache: " + ec.message()};
|
||||
}
|
||||
|
||||
manager.load_plugin(plugin_key, false);
|
||||
std::string error;
|
||||
if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key))
|
||||
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
void open_plugin_folder(const Slic3r::PluginDescriptor& plugin);
|
||||
void delete_local_plugin(const Slic3r::PluginDescriptor& plugin);
|
||||
void unsubscribe_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
|
||||
void reinstall_local_plugin(const std::string& plugin_key);
|
||||
void reload_local_plugin(const std::string& plugin_key, bool clear_cache);
|
||||
void reinstall_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
|
||||
void delete_mine_local_and_cloud_plugin(const std::string& plugin_key);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "GLCanvas3D.hpp" // ORCA: for live preview refresh when toggling "Dim lower layers"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
@@ -1049,6 +1050,16 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
wxGetApp().mainframe->m_webview->SendCloudProvidersInfo();
|
||||
}
|
||||
}
|
||||
// ORCA: apply the preview dimming change immediately to the currently loaded preview (ported from preFlight)
|
||||
else if (param == "preview_dim_previous_layers") {
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||
canvas->get_gcode_viewer().set_dim_previous_layers(app_config->get_bool(param));
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
if (param == "associate_3mf") {
|
||||
@@ -1893,11 +1904,21 @@ void PreferencesDialog::create_items()
|
||||
);
|
||||
g_sizer->Add(item_fps_overlay);
|
||||
|
||||
//// GRAPHICS > G-code Preview
|
||||
g_sizer->Add(create_item_title(_L("G-code Preview")), 1, wxEXPAND);
|
||||
|
||||
auto item_dim_previous_layers = create_item_checkbox(
|
||||
_L("Dim lower layers"),
|
||||
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
||||
"preview_dim_previous_layers"
|
||||
);
|
||||
g_sizer->Add(item_dim_previous_layers);
|
||||
|
||||
g_sizer->AddSpacer(FromDIP(10));
|
||||
sizer_page->Add(g_sizer, 0, wxEXPAND);
|
||||
|
||||
//////////////////////////
|
||||
//// ONLINE TAB
|
||||
//// ONLINE TAB
|
||||
/////////////////////////////////////
|
||||
m_pref_tabs->AppendItem(_L("Online"));
|
||||
f_sizers.push_back(new wxFlexGridSizer(1, 1, v_gap, 0));
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
#include "slic3r/plugin/PluginConfig.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
@@ -1795,9 +1796,19 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
|
||||
// Keep this preset's "plugins" manifest in sync when a plugin picker changes, so full_config() and
|
||||
// save_to_json() always find resolved "name;uuid;capability" references and rebuild it nowhere else.
|
||||
// Also drop any plugin_config_overrides entries for a capability the change just stopped
|
||||
// referencing (e.g. a plugin removed from slicing_pipeline_plugin), so a saved preset never
|
||||
// carries configuration for a capability it no longer names. The Configure button is a separate
|
||||
// field holding its own cached copy of that value, so it needs to be told explicitly, or it
|
||||
// keeps showing the stale count until something else happens to refresh it.
|
||||
if (const ConfigOptionDef* opt_def = m_config->def()->get(opt_key);
|
||||
opt_def && opt_def->is_plugin_backed())
|
||||
opt_def && opt_def->is_plugin_backed()) {
|
||||
m_config->update_plugin_manifest();
|
||||
if (prune_stale_plugin_overrides(*m_config)) {
|
||||
if (Field* overrides_field = get_field(PLUGIN_OVERRIDES_OPTION_KEY))
|
||||
overrides_field->set_value(boost::any(m_config->opt_string(PLUGIN_OVERRIDES_OPTION_KEY)), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_key == "gcode_flavor" && m_type == Preset::TYPE_PRINTER) {
|
||||
if (auto printer_tab = dynamic_cast<TabPrinter*>(this))
|
||||
|
||||
@@ -807,7 +807,11 @@ wxString TroubleshootDialog::GetRAMinfo()
|
||||
wxString TroubleshootDialog::GetGPUinfo()
|
||||
{
|
||||
auto gl_info = OpenGLManager::get_gl_info();
|
||||
return gl_info.get_renderer()+ " GLSL:" + gl_info.get_glsl_version();
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
return gl_info.get_renderer() + " GLSL:" + gl_info.get_glsl_version() + (gl_info.is_core_profile() ? " Core" : " Compatibility");
|
||||
#else
|
||||
return gl_info.get_renderer() + " GLSL:" + gl_info.get_glsl_version() + " ES";
|
||||
#endif
|
||||
}
|
||||
|
||||
wxString TroubleshootDialog::GetMONinfo()
|
||||
|
||||
@@ -96,6 +96,9 @@ std::string WebViewHostDialog::document_start_injector(const std::string& markup
|
||||
const std::string literal = nlohmann::json(markup).dump();
|
||||
std::string s;
|
||||
s += "(function(){";
|
||||
// wxWebView's AddUserScript runs in child frames too (including cross-origin
|
||||
// frames on WebView2). Host theme state belongs only to the top-level page.
|
||||
s += "if(window.top!==window.self)return;";
|
||||
s += prelude;
|
||||
s += "var css=" + literal + ";";
|
||||
s += "function inject(){";
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
|
||||
#include <libslic3r/Config.hpp>
|
||||
#include <libslic3r/PresetBundle.hpp>
|
||||
#include <libslic3r/PrintConfig.hpp>
|
||||
#include <slic3r/GUI/GUI.hpp>
|
||||
@@ -164,6 +165,20 @@ bool CapabilityConfigDocument::erase(const PluginCapabilityId& id)
|
||||
return erased;
|
||||
}
|
||||
|
||||
bool CapabilityConfigDocument::prune_unreferenced(const std::set<std::pair<PluginCapabilityType, std::string>>& referenced)
|
||||
{
|
||||
bool changed = false;
|
||||
for (auto it = m_entries.begin(); it != m_entries.end();) {
|
||||
if (referenced.count({it->first.type, it->first.name}) != 0) {
|
||||
++it;
|
||||
} else {
|
||||
it = m_entries.erase(it);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CapabilityConfigDocument::empty() const
|
||||
{
|
||||
return m_entries.empty() && m_opaque_entries.empty();
|
||||
@@ -342,6 +357,50 @@ std::string serialize_plugin_overrides(const CapabilityConfigDocument& document)
|
||||
return document.empty() ? std::string() : document.serialize_entries().dump();
|
||||
}
|
||||
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config)
|
||||
{
|
||||
const auto* overrides_opt = dynamic_cast<const ConfigOptionString*>(config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
||||
if (overrides_opt == nullptr || overrides_opt->value.empty())
|
||||
return false;
|
||||
|
||||
CapabilityConfigDocument overrides;
|
||||
std::string error;
|
||||
if (!parse_plugin_overrides(overrides_opt->value, overrides, error)) {
|
||||
// Malformed text is not ours to fix up here: leave it untouched rather than risk
|
||||
// discarding data the user might still be able to recover.
|
||||
BOOST_LOG_TRIVIAL(error) << "prune_stale_plugin_overrides: " << error;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Capability names currently referenced by a plugin-backed option's value(s) — e.g.
|
||||
// slicing_pipeline_plugin's ConfigOptionStrings entries name SlicingPipeline capabilities
|
||||
// directly, the same raw values save_plugin_collection() resolves into the "plugins" manifest.
|
||||
std::set<std::pair<PluginCapabilityType, std::string>> referenced;
|
||||
const ConfigDef* def = config.def();
|
||||
for (const std::string& opt_key : config.keys()) {
|
||||
const ConfigOptionDef* opt_def = def != nullptr ? def->get(opt_key) : nullptr;
|
||||
if (opt_def == nullptr || !opt_def->is_plugin_backed())
|
||||
continue;
|
||||
|
||||
const ConfigOption* opt = config.option(opt_key);
|
||||
const PluginCapabilityType type = plugin_capability_type_from_string(opt_def->plugin_type);
|
||||
if (const auto* string_opt = dynamic_cast<const ConfigOptionString*>(opt)) {
|
||||
if (!string_opt->value.empty())
|
||||
referenced.emplace(type, string_opt->value);
|
||||
} else if (const auto* vector_opt = dynamic_cast<const ConfigOptionVectorBase*>(opt)) {
|
||||
for (const std::string& value : vector_opt->vserialize())
|
||||
if (!value.empty())
|
||||
referenced.emplace(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!overrides.prune_unreferenced(referenced))
|
||||
return false;
|
||||
|
||||
config.set_key_value(PLUGIN_OVERRIDES_OPTION_KEY, new ConfigOptionString(serialize_plugin_overrides(overrides)));
|
||||
return true;
|
||||
}
|
||||
|
||||
EffectiveCapabilityConfig PresetPluginConfigService::get_effective_config(const CapabilityConfigDocument& overrides,
|
||||
const PluginCapabilityId& id) const
|
||||
{
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define PLUGIN_CONFIG_DIR "config.json"
|
||||
@@ -16,6 +18,7 @@
|
||||
namespace Slic3r {
|
||||
|
||||
class Preset;
|
||||
class DynamicConfig;
|
||||
struct CapabilityConfigEntry
|
||||
{
|
||||
PluginCapabilityId id;
|
||||
@@ -35,6 +38,9 @@ public:
|
||||
bool contains(const PluginCapabilityId& id) const;
|
||||
bool upsert(CapabilityConfigEntry entry);
|
||||
bool erase(const PluginCapabilityId& id);
|
||||
// Drops every entry whose (type, name) is not in `referenced`, e.g. capabilities a preset's
|
||||
// plugin-backed options no longer name. Returns true if anything was removed.
|
||||
bool prune_unreferenced(const std::set<std::pair<PluginCapabilityType, std::string>>& referenced);
|
||||
bool empty() const;
|
||||
nlohmann::json serialize_entries() const;
|
||||
nlohmann::json root_json() const;
|
||||
@@ -50,6 +56,14 @@ std::string plugin_overrides_of(const Preset& preset);
|
||||
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error);
|
||||
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document);
|
||||
|
||||
// Drops plugin_config_overrides entries for capabilities no longer named by any plugin-backed
|
||||
// option's current value in `config` (e.g. slicing_pipeline_plugin cleared or switched to a
|
||||
// different capability), and writes the result back if anything changed. Called wherever a
|
||||
// plugin-backed option's value changes, so a saved preset never carries configuration for a
|
||||
// capability it no longer references. Returns true if `config` was modified, so a caller holding a
|
||||
// GUI field over PLUGIN_OVERRIDES_OPTION_KEY knows it must refresh that field's displayed value.
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config);
|
||||
|
||||
struct EffectiveCapabilityConfig
|
||||
{
|
||||
PluginCapabilityId id;
|
||||
|
||||
@@ -54,7 +54,7 @@ struct PluginDescriptor
|
||||
std::string description; // Plugin description
|
||||
std::string author; // Plugin author from manifest, if available
|
||||
std::string version; // Selected plugin version
|
||||
std::string latest_version; // Latest available cloud version fallback when changelog is unavailable.
|
||||
std::string latest_version; // Authoritative latest available cloud version.
|
||||
std::string installed_version; // Locally installed package version. Preserved across cloud merges, which overwrite `version` with the latest cloud version. Empty when not installed.
|
||||
std::vector<std::string> display_types; // Display-only "compatibility" labels (cloud: raw service labels; local: from real capabilities). Never used for dispatch.
|
||||
std::string plugin_root; // Installed plugin directory, even when entry_path is invalid or ambiguous.
|
||||
@@ -113,10 +113,6 @@ struct PluginDescriptor
|
||||
bool is_unauthorized() const { return get_update_status() == PluginUpdateStatus::Unauthorized; }
|
||||
std::string latest_available_version() const
|
||||
{
|
||||
for (const PluginChangelog& entry : changelog) {
|
||||
if (!entry.version.empty())
|
||||
return entry.version;
|
||||
}
|
||||
if (!latest_version.empty())
|
||||
return latest_version;
|
||||
return version;
|
||||
|
||||
@@ -52,6 +52,29 @@ print('ok')
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("plugin latest version uses the authoritative catalog field", "[PluginDescriptor]")
|
||||
{
|
||||
PluginDescriptor descriptor;
|
||||
descriptor.version = "1.3.0";
|
||||
descriptor.latest_version = "1.3.0";
|
||||
PluginChangelog changelog;
|
||||
changelog.version = "1.2.0";
|
||||
descriptor.changelog.push_back(changelog);
|
||||
|
||||
CHECK(descriptor.latest_available_version() == "1.3.0");
|
||||
}
|
||||
|
||||
TEST_CASE("plugin latest version falls back to the descriptor version", "[PluginDescriptor]")
|
||||
{
|
||||
PluginDescriptor descriptor;
|
||||
descriptor.version = "1.1.0";
|
||||
PluginChangelog changelog;
|
||||
changelog.version = "1.0.0";
|
||||
descriptor.changelog.push_back(changelog);
|
||||
|
||||
CHECK(descriptor.latest_available_version() == "1.1.0");
|
||||
}
|
||||
|
||||
// Regression: update_cloud_metadata() replaces a matched entry's descriptor wholesale with the
|
||||
// cloud catalog record (`entry = cloud_entry`). Configuration used to ride on the descriptor, so
|
||||
// that overwrite silently wiped it and plugins fell back to their built-in defaults (found via
|
||||
|
||||
Reference in New Issue
Block a user