diff --git a/resources/calib/PressureAdvance/pressure_advance_test.stl b/resources/calib/PressureAdvance/pressure_advance_test.stl
new file mode 100644
index 0000000..0df26de
Binary files /dev/null and b/resources/calib/PressureAdvance/pressure_advance_test.stl differ
diff --git a/resources/calib/PressureAdvance/tower_with_seam.stl b/resources/calib/PressureAdvance/tower_with_seam.stl
new file mode 100644
index 0000000..325ffb1
Binary files /dev/null and b/resources/calib/PressureAdvance/tower_with_seam.stl differ
diff --git a/resources/calib/filament_flow/flowrate-test-pass1.3mf b/resources/calib/filament_flow/flowrate-test-pass1.3mf
new file mode 100644
index 0000000..8f1a1b5
Binary files /dev/null and b/resources/calib/filament_flow/flowrate-test-pass1.3mf differ
diff --git a/resources/calib/filament_flow/flowrate-test-pass2.3mf b/resources/calib/filament_flow/flowrate-test-pass2.3mf
new file mode 100644
index 0000000..4d1d0c3
Binary files /dev/null and b/resources/calib/filament_flow/flowrate-test-pass2.3mf differ
diff --git a/resources/icons/check_half.svg b/resources/icons/check_half.svg
new file mode 100644
index 0000000..bc99d2d
--- /dev/null
+++ b/resources/icons/check_half.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/icons/check_half_disabled.svg b/resources/icons/check_half_disabled.svg
new file mode 100644
index 0000000..1e6fb24
--- /dev/null
+++ b/resources/icons/check_half_disabled.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/icons/check_half_focused.svg b/resources/icons/check_half_focused.svg
new file mode 100644
index 0000000..517bb7a
--- /dev/null
+++ b/resources/icons/check_half_focused.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/icons/check_off.svg b/resources/icons/check_off.svg
new file mode 100644
index 0000000..cf58fbc
--- /dev/null
+++ b/resources/icons/check_off.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/icons/check_off_disabled.svg b/resources/icons/check_off_disabled.svg
new file mode 100644
index 0000000..8653c1f
--- /dev/null
+++ b/resources/icons/check_off_disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/icons/check_off_focused.svg b/resources/icons/check_off_focused.svg
new file mode 100644
index 0000000..474375d
--- /dev/null
+++ b/resources/icons/check_off_focused.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/icons/check_on.svg b/resources/icons/check_on.svg
new file mode 100644
index 0000000..029abf8
--- /dev/null
+++ b/resources/icons/check_on.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/icons/check_on_disabled.svg b/resources/icons/check_on_disabled.svg
new file mode 100644
index 0000000..dfd917f
--- /dev/null
+++ b/resources/icons/check_on_disabled.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/icons/check_on_focused.svg b/resources/icons/check_on_focused.svg
new file mode 100644
index 0000000..3c1c56c
--- /dev/null
+++ b/resources/icons/check_on_focused.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/icons/checked.svg b/resources/icons/checked.svg
new file mode 100644
index 0000000..88747cb
--- /dev/null
+++ b/resources/icons/checked.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 0de0b4e..3f432ae 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -35,6 +35,8 @@ set(SLIC3R_SOURCES
BuildVolume.cpp
BuildVolume.hpp
BoostAdapter.hpp
+ calib.cpp
+ calib.hpp
clipper.cpp
clipper.hpp
ClipperUtils.cpp
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 5f80a02..9ed53c0 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -162,6 +162,25 @@ std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait
return gcode.str();
}
+
+
+//B34
+std::string GCodeWriter::set_pressure_advance(double pa) const
+{
+ std::ostringstream gcode;
+ if (pa < 0)
+ return gcode.str();
+ else{
+ if (FLAVOR_IS(gcfKlipper))
+ gcode << "SET_PRESSURE_ADVANCE ADVANCE=" << std::setprecision(4) << pa << "; Override pressure advance value\n";
+ else if(FLAVOR_IS(gcfRepRapFirmware))
+ gcode << ("M572 D0 S") << std::setprecision(4) << pa << "; Override pressure advance value\n";
+ else
+ gcode << "M900 K" <
+
+namespace Slic3r {
+
+// Calculate the optimal Pressure Advance speed
+float CalibPressureAdvance::find_optimal_PA_speed(const DynamicPrintConfig &config, double line_width, double layer_height,
+ int filament_idx) {
+ const double general_suggested_min_speed = 100.0;
+ double filament_max_volumetric_speed = config.option("filament_max_volumetric_speed")->get_at(0);
+ Flow pattern_line = Flow(line_width, layer_height, config.option("nozzle_diameter")->get_at(0));
+ auto pa_speed = std::min(std::max(general_suggested_min_speed,config.option("outer_wall_speed")->value), filament_max_volumetric_speed / pattern_line.mm3_per_mm());
+
+ return std::floor(pa_speed);
+}
+
+std::string CalibPressureAdvance::move_to(Vec2d pt, GCodeWriter& writer, std::string comment)
+{
+ std::stringstream gcode;
+
+ gcode << writer.retract();
+ gcode << writer.travel_to_xy(pt, comment);
+ gcode << writer.unretract();
+
+ m_last_pos = Vec3d(pt.x(), pt.y(), 0);
+
+ return gcode.str();
+}
+
+double CalibPressureAdvance::e_per_mm(
+ double line_width,
+ double layer_height,
+ float nozzle_diameter,
+ float filament_diameter,
+ float print_flow_ratio
+) const
+{
+ const Flow line_flow = Flow(line_width, layer_height, nozzle_diameter);
+ const double filament_area = M_PI * std::pow(filament_diameter / 2, 2);
+
+ return line_flow.mm3_per_mm() / filament_area * print_flow_ratio;
+}
+
+std::string CalibPressureAdvance::convert_number_to_string(double num) const
+{
+ auto sNumber = std::to_string(num);
+ sNumber.erase(sNumber.find_last_not_of('0') + 1, std::string::npos);
+ sNumber.erase(sNumber.find_last_not_of('.') + 1, std::string::npos);
+
+ return sNumber;
+}
+
+std::string CalibPressureAdvance::draw_digit(
+ double startx,
+ double starty,
+ char c,
+ CalibPressureAdvance::DrawDigitMode mode,
+ double line_width,
+ double e_per_mm,
+ GCodeWriter& writer
+)
+{
+ const double len = m_digit_segment_len;
+ const double gap = line_width / 2.0;
+
+ const auto dE = e_per_mm * len;
+ const auto two_dE = dE * 2;
+
+ Vec2d p0, p1, p2, p3, p4, p5;
+ Vec2d p0_5, p4_5;
+ Vec2d gap_p0_toward_p3, gap_p2_toward_p3;
+ Vec2d dot_direction;
+
+ if (mode == CalibPressureAdvance::DrawDigitMode::Bottom_To_Top) {
+ // 1-------2-------5
+ // | | |
+ // | | |
+ // 0-------3-------4
+ p0 = Vec2d(startx, starty);
+ p0_5 = Vec2d(startx, starty + len / 2);
+ p1 = Vec2d(startx, starty + len);
+ p2 = Vec2d(startx + len, starty + len);
+ p3 = Vec2d(startx + len, starty);
+ p4 = Vec2d(startx + len * 2, starty);
+ p4_5 = Vec2d(startx + len * 2, starty + len / 2);
+ p5 = Vec2d(startx + len * 2, starty + len);
+
+ gap_p0_toward_p3 = p0 + Vec2d(gap, 0);
+ gap_p2_toward_p3 = p2 + Vec2d(0, gap);
+
+ dot_direction = Vec2d(-len / 2, 0);
+ } else {
+ // 0-------1
+ // | |
+ // 3-------2
+ // | |
+ // 4-------5
+ p0 = Vec2d(startx, starty);
+ p0_5 = Vec2d(startx + len / 2, starty);
+ p1 = Vec2d(startx + len, starty);
+ p2 = Vec2d(startx + len, starty - len);
+ p3 = Vec2d(startx, starty - len);
+ p4 = Vec2d(startx, starty - len * 2);
+ p4_5 = Vec2d(startx + len / 2, starty - len * 2);
+ p5 = Vec2d(startx + len, starty - len * 2);
+
+ gap_p0_toward_p3 = p0 - Vec2d(0, gap);
+ gap_p2_toward_p3 = p2 - Vec2d(gap, 0);
+
+ dot_direction = Vec2d(0, len / 2);
+ }
+
+ std::stringstream gcode;
+
+ switch (c) {
+ case '0':
+ gcode << move_to(p0, writer, "Glyph: 0");
+ gcode << writer.extrude_to_xy(p1, dE);
+ gcode << writer.extrude_to_xy(p5, two_dE);
+ gcode << writer.extrude_to_xy(p4, dE);
+ gcode << writer.extrude_to_xy(gap_p0_toward_p3, two_dE);
+ break;
+ case '1':
+ gcode << move_to(p0_5, writer, "Glyph: 1");
+ gcode << writer.extrude_to_xy(p4_5, two_dE);
+ break;
+ case '2':
+ gcode << move_to(p0, writer, "Glyph: 2");
+ gcode << writer.extrude_to_xy(p1, dE);
+ gcode << writer.extrude_to_xy(p2, dE);
+ gcode << writer.extrude_to_xy(p3, dE);
+ gcode << writer.extrude_to_xy(p4, dE);
+ gcode << writer.extrude_to_xy(p5, dE);
+ break;
+ case '3':
+ gcode << move_to(p0, writer, "Glyph: 3");
+ gcode << writer.extrude_to_xy(p1, dE);
+ gcode << writer.extrude_to_xy(p5, two_dE);
+ gcode << writer.extrude_to_xy(p4, dE);
+ gcode << move_to(gap_p2_toward_p3, writer);
+ gcode << writer.extrude_to_xy(p3, dE);
+ break;
+ case '4':
+ gcode << move_to(p0, writer, "Glyph: 4");
+ gcode << writer.extrude_to_xy(p3, dE);
+ gcode << writer.extrude_to_xy(p2, dE);
+ gcode << move_to(p1, writer);
+ gcode << writer.extrude_to_xy(p5, two_dE);
+ break;
+ case '5':
+ gcode << move_to(p1, writer, "Glyph: 5");
+ gcode << writer.extrude_to_xy(p0, dE);
+ gcode << writer.extrude_to_xy(p3, dE);
+ gcode << writer.extrude_to_xy(p2, dE);
+ gcode << writer.extrude_to_xy(p5, dE);
+ gcode << writer.extrude_to_xy(p4, dE);
+ break;
+ case '6':
+ gcode << move_to(p1, writer, "Glyph: 6");
+ gcode << writer.extrude_to_xy(p0, dE);
+ gcode << writer.extrude_to_xy(p4, two_dE);
+ gcode << writer.extrude_to_xy(p5, dE);
+ gcode << writer.extrude_to_xy(p2, dE);
+ gcode << writer.extrude_to_xy(p3, dE);
+ break;
+ case '7':
+ gcode << move_to(p0, writer, "Glyph: 7");
+ gcode << writer.extrude_to_xy(p1, dE);
+ gcode << writer.extrude_to_xy(p5, two_dE);
+ break;
+ case '8':
+ gcode << move_to(p2, writer, "Glyph: 8");
+ gcode << writer.extrude_to_xy(p3, dE);
+ gcode << writer.extrude_to_xy(p4, dE);
+ gcode << writer.extrude_to_xy(p5, dE);
+ gcode << writer.extrude_to_xy(p1, two_dE);
+ gcode << writer.extrude_to_xy(p0, dE);
+ gcode << writer.extrude_to_xy(p3, dE);
+ break;
+ case '9':
+ gcode << move_to(p5, writer, "Glyph: 9");
+ gcode << writer.extrude_to_xy(p1, two_dE);
+ gcode << writer.extrude_to_xy(p0, dE);
+ gcode << writer.extrude_to_xy(p3, dE);
+ gcode << writer.extrude_to_xy(p2, dE);
+ break;
+ case '.':
+ gcode << move_to(p4_5, writer, "Glyph: .");
+ gcode << writer.extrude_to_xy(p4_5 + dot_direction, dE);
+ break;
+ default:
+ break;
+ }
+
+ return gcode.str();
+}
+
+std::string CalibPressureAdvance::draw_number(
+ double startx,
+ double starty,
+ double value,
+ CalibPressureAdvance::DrawDigitMode mode,
+ double line_width,
+ double e_per_mm,
+ double speed,
+ GCodeWriter& writer
+)
+{
+ auto sNumber = convert_number_to_string(value);
+ std::stringstream gcode;
+ gcode << writer.set_speed(speed);
+
+ for (std::string::size_type i = 0; i < sNumber.length(); ++i) {
+ if (i > m_max_number_len) {
+ break;
+ }
+ switch (mode) {
+ case DrawDigitMode::Bottom_To_Top:
+ gcode << draw_digit(
+ startx,
+ starty + i * number_spacing(),
+ sNumber[i],
+ mode,
+ line_width,
+ e_per_mm,
+ writer
+ );
+ break;
+ default:
+ gcode << draw_digit(
+ startx + i * number_spacing(),
+ starty,
+ sNumber[i],
+ mode,
+ line_width,
+ e_per_mm,
+ writer
+ );
+ }
+ }
+
+ return gcode.str();
+}
+
+std::string CalibPressureAdvanceLine::generate_test(double start_pa /*= 0*/, double step_pa /*= 0.002*/, int count /*= 10*/)
+{
+ BoundingBoxf bed_ext = get_extents(mp_gcodegen->config().bed_shape.values);
+ if (is_delta()) {
+ CalibPressureAdvanceLine::delta_scale_bed_ext(bed_ext);
+ }
+
+ auto bed_sizes = mp_gcodegen->config().bed_shape.values;
+ const auto &w = bed_ext.size().x();
+ const auto &h = bed_ext.size().y();
+ count = std::min(count, int((h - 10) / m_space_y));
+
+ m_length_long = 40 + std::min(w - 120.0, 0.0);
+
+ auto startx = (w - m_length_short * 2 - m_length_long - 20) / 2;
+ auto starty = (h - count * m_space_y) / 2;
+ if (is_delta()) {
+ CalibPressureAdvanceLine::delta_modify_start(startx, starty, count);
+ }
+
+ return print_pa_lines(startx, starty, start_pa, step_pa, count);
+}
+
+bool CalibPressureAdvanceLine::is_delta() const
+{
+ return mp_gcodegen->config().bed_shape.values.size() > 4;
+}
+
+std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double start_y, double start_pa, double step_pa, int num)
+{
+ auto& writer = mp_gcodegen->writer();
+ const auto& config = mp_gcodegen->config();
+
+ const auto filament_diameter = config.filament_diameter.get_at(0);
+ const auto print_flow_ratio = 1;
+
+ const double e_per_mm = CalibPressureAdvance::e_per_mm(
+ m_line_width,
+ m_height_layer,
+ m_nozzle_diameter,
+ filament_diameter,
+ print_flow_ratio
+ );
+ const double thin_e_per_mm = CalibPressureAdvance::e_per_mm(
+ m_thin_line_width,
+ m_height_layer,
+ m_nozzle_diameter,
+ filament_diameter,
+ print_flow_ratio
+ );
+ const double number_e_per_mm = CalibPressureAdvance::e_per_mm(
+ m_number_line_width,
+ m_height_layer,
+ m_nozzle_diameter,
+ filament_diameter,
+ print_flow_ratio
+ );
+
+ const double fast = CalibPressureAdvance::speed_adjust(m_fast_speed);
+ const double slow = CalibPressureAdvance::speed_adjust(m_slow_speed);
+ std::stringstream gcode;
+ gcode << mp_gcodegen->writer().travel_to_z(m_height_layer);
+ double y_pos = start_y;
+
+ // prime line
+ auto prime_x = start_x - 2;
+ gcode << move_to(Vec2d(prime_x, y_pos + (num - 4) * m_space_y), writer);
+ gcode << writer.set_speed(slow);
+ gcode << writer.extrude_to_xy(Vec2d(prime_x, y_pos + 3 * m_space_y), e_per_mm * m_space_y * num * 1.1);
+
+ 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 << 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 << 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 << 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);
+ }
+ gcode << writer.set_pressure_advance(0.0);
+
+ if (m_draw_numbers) {
+ // draw indicator lines
+ gcode << writer.set_speed(fast);
+ gcode << move_to(Vec2d(start_x + m_length_short, y_pos + (num - 1) * m_space_y + 2), writer);
+ gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short, y_pos + (num - 1) * m_space_y + 7), thin_e_per_mm * 7);
+ gcode << move_to(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 7), writer);
+ gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 2), thin_e_per_mm * 7);
+
+ for (int i = 0; i < num; i += 2) {
+ gcode << draw_number(
+ start_x + m_length_short + m_length_long + m_length_short + 3,
+ y_pos + i * m_space_y + m_space_y / 2,
+ start_pa + i * step_pa,
+ m_draw_digit_mode,
+ m_number_line_width,
+ number_e_per_mm,
+ 3600,
+ writer
+ );
+ }
+ }
+ return gcode.str();
+}
+
+void CalibPressureAdvanceLine::delta_modify_start(double& startx, double& starty, int count)
+{
+ startx = -startx;
+ starty = -(count * m_space_y) / 2;
+}
+
+CalibPressureAdvancePattern::CalibPressureAdvancePattern(
+ const Calib_Params& params,
+ const DynamicPrintConfig& config,
+ bool is_bbl_machine,
+ Model& model,
+ const Vec3d& origin
+) :
+ m_params(params)
+{
+ this->m_draw_digit_mode = DrawDigitMode::Bottom_To_Top;
+
+ refresh_setup(config, is_bbl_machine, model, origin);
+};
+
+void CalibPressureAdvancePattern::generate_custom_gcodes(
+ const DynamicPrintConfig& config,
+ bool is_bbl_machine,
+ Model& model,
+ const Vec3d& origin
+)
+{
+ std::stringstream gcode;
+ gcode << "; start pressure advance pattern for layer\n";
+
+ refresh_setup(config, is_bbl_machine, model, origin);
+
+ gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), m_writer, "Move to start XY position");
+ gcode << m_writer.travel_to_z(height_first_layer(), "Move to start Z position");
+ gcode << m_writer.set_pressure_advance(m_params.start);
+
+ const DrawBoxOptArgs default_box_opt_args(*this);
+
+ // create anchoring frame
+ gcode << draw_box(
+ 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();
+ gcode << draw_box(
+ m_starting_point.x(),
+ m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
+ glyph_tab_max_x() - m_starting_point.x(),
+ max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2,
+ draw_box_opt_args
+ );
+
+ std::vector gcode_items;
+ const DrawLineOptArgs default_line_opt_args(*this);
+ const int num_patterns = get_num_patterns(); // "cache" for use in loops
+
+ // draw pressure advance pattern
+ for (int i = 0; i < m_num_layers; ++i) {
+ if (i > 0) {
+ gcode << "; end pressure advance pattern for layer\n";
+ CustomGCode::Item item;
+ item.print_z = height_first_layer() + (i - 1) * height_layer();
+ item.type = CustomGCode::Type::Custom;
+ item.extra = gcode.str();
+ gcode_items.push_back(item);
+
+ gcode = std::stringstream(); // reset for next layer contents
+ gcode << "; start pressure advance pattern for layer\n";
+
+ const double layer_height = height_first_layer() + (i * height_layer());
+ gcode << m_writer.travel_to_z(layer_height, "Move to layer height");
+ }
+
+ // line numbering
+ if (i == 1) {
+ gcode << m_writer.set_pressure_advance(m_params.start);
+
+ double number_e_per_mm = e_per_mm(
+ line_width(),
+ height_layer(),
+ m_config.option("nozzle_diameter")->get_at(0),
+ m_config.option("filament_diameter")->get_at(0),
+ m_config.option("filament_flow_ratio")->get_at(0)
+ );
+
+ // glyph on every other line
+ 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,
+ speed_first_layer(),
+ m_writer
+ );
+ }
+ }
+
+ DrawLineOptArgs draw_line_opt_args = default_line_opt_args;
+
+ double to_x = m_starting_point.x() + pattern_shift();
+ double to_y = m_starting_point.y();
+ double side_length = m_wall_side_length;
+
+ // shrink first layer to fit inside frame
+ if (i == 0) {
+ double shrink =
+ (
+ line_spacing_first_layer() * (wall_count() - 1) +
+ (line_width_first_layer() * (1 - m_encroachment))
+ ) / std::sin(to_radians(m_corner_angle) / 2)
+ ;
+ side_length = m_wall_side_length - shrink;
+ to_x += shrink * std::sin(to_radians(90) - to_radians(m_corner_angle) / 2);
+ to_y +=
+ line_spacing_first_layer() * (wall_count() - 1) +
+ (line_width_first_layer() * (1 - m_encroachment))
+ ;
+ }
+
+ double initial_x = to_x;
+ double initial_y = to_y;
+
+ gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to pattern start");
+
+ for (int j = 0; j < num_patterns; ++j) {
+ // increment pressure advance
+ gcode << m_writer.set_pressure_advance(m_params.start + (j * m_params.step));
+
+ 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;
+
+ draw_line_opt_args = default_line_opt_args;
+ draw_line_opt_args.height = i == 0 ? height_first_layer() : height_layer();
+ draw_line_opt_args.line_width = line_width(); // don't use line_width_first_layer so results are consistent across all layers
+ draw_line_opt_args.speed = i == 0 ? speed_adjust(speed_first_layer()) : speed_adjust(speed_perimeter());
+ draw_line_opt_args.comment = "Print pattern wall";
+ gcode << draw_line(Vec2d(to_x, to_y), draw_line_opt_args);
+
+ to_x -= std::cos(to_radians(m_corner_angle) / 2) * side_length;
+ to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length;
+
+ gcode << draw_line(Vec2d(to_x, to_y), draw_line_opt_args);
+
+ to_y = initial_y;
+ if (k != wall_count() - 1) {
+ // perimeters not done yet. move to next perimeter
+ to_x += line_spacing_angle();
+ gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to start next pattern wall");
+ } else if (j != num_patterns - 1) {
+ // patterns not done yet. move to next pattern
+ to_x += m_pattern_spacing + line_width();
+ gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to next pattern");
+ } else if (i != m_num_layers - 1) {
+ // layers not done yet. move back to start
+ to_x = initial_x;
+ gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move back to start position");
+ } else {
+ // everything done
+ }
+ }
+ }
+ }
+
+ gcode << m_writer.set_pressure_advance(m_params.start);
+ gcode << "; end pressure advance pattern for layer\n";
+
+ CustomGCode::Item item;
+ item.print_z = max_layer_z();
+ item.type = CustomGCode::Type::Custom;
+ item.extra = gcode.str();
+ gcode_items.push_back(item);
+
+ CustomGCode::Info info;
+ info.mode = CustomGCode::Mode::SingleExtruder;
+ info.gcodes = gcode_items;
+
+ //model.plates_custom_gcodes[model.curr_plate_index] = info;
+}
+
+void CalibPressureAdvancePattern::refresh_setup(
+ const DynamicPrintConfig& config,
+ bool is_bbl_machine,
+ const Model& model,
+ const Vec3d& origin
+)
+{
+ m_config = config;
+ m_config.apply(model.objects.front()->config.get(), true);
+ m_config.apply(model.objects.front()->volumes.front()->config.get(), true);
+
+ m_is_delta = (m_config.option("bed_shape")->values.size() > 4);
+
+ _refresh_starting_point(model);
+ _refresh_writer(is_bbl_machine, model, origin);
+}
+
+void CalibPressureAdvancePattern::_refresh_starting_point(const Model& model)
+{
+ ModelObject* obj = model.objects.front();
+ //BoundingBoxf3 bbox =
+ // obj->instance_bounding_box(
+ // *obj->instances.front(),
+ // false
+ // )
+ //;
+
+ m_starting_point = Vec3d(0, 0, 0);
+ m_starting_point.y() += m_handle_spacing;
+
+ if (m_is_delta) {
+ m_starting_point.x() *= -1;
+ m_starting_point.y() -= (frame_size_y() / 2);
+ }
+}
+
+void CalibPressureAdvancePattern::_refresh_writer(
+ bool is_bbl_machine,
+ const Model& model,
+ const Vec3d& origin
+)
+{
+ PrintConfig print_config;
+ print_config.apply(m_config, true);
+
+ m_writer.apply_print_config(print_config);
+ //m_writer.set_xy_offset(origin(0), origin(1));
+
+ const unsigned int extruder_id = model.objects.front()->volumes.front()->extruder_id();
+ m_writer.set_extruders({ extruder_id });
+ m_writer.set_extruder(extruder_id);
+}
+
+std::string CalibPressureAdvancePattern::draw_line(
+ Vec2d to_pt,
+ DrawLineOptArgs opt_args
+)
+{
+ const double e_per_mm = CalibPressureAdvance::e_per_mm(
+ opt_args.line_width,
+ opt_args.height,
+ m_config.option("nozzle_diameter")->get_at(0),
+ m_config.option("filament_diameter")->get_at(0),
+ m_config.option("filament_flow_ratio")->get_at(0)
+ );
+
+ const double length = get_distance(Vec2d(m_last_pos.x(), m_last_pos.y()), to_pt);
+ auto dE = e_per_mm * length;
+
+ std::stringstream gcode;
+
+ gcode << m_writer.set_speed(opt_args.speed);
+ gcode << m_writer.extrude_to_xy(to_pt, dE, opt_args.comment);
+
+ m_last_pos = Vec3d(to_pt.x(), to_pt.y(), 0);
+
+ return gcode.str();
+}
+
+std::string CalibPressureAdvancePattern::draw_box(
+ double min_x,
+ double min_y,
+ double size_x,
+ double size_y,
+ DrawBoxOptArgs opt_args
+)
+{
+ std::stringstream gcode;
+
+ double x = min_x;
+ double y = min_y;
+ const double max_x = min_x + size_x;
+ const double max_y = min_y + size_y;
+
+ const double spacing = opt_args.line_width - opt_args.height * (1 - M_PI / 4);
+
+ // if number of perims exceeds size of box, reduce it to max
+ const int max_perimeters =
+ std::min(
+ // this is the equivalent of number of perims for concentric fill
+ std::floor(size_x * std::sin(to_radians(45))) / (spacing / std::sin(to_radians(45))),
+ std::floor(size_y * std::sin(to_radians(45))) / (spacing / std::sin(to_radians(45)))
+ )
+ ;
+
+ opt_args.num_perimeters = std::min(opt_args.num_perimeters, max_perimeters);
+
+ gcode << move_to(Vec2d(min_x, min_y), m_writer, "Move to box start");
+
+ DrawLineOptArgs line_opt_args(*this);
+ line_opt_args.height = opt_args.height;
+ line_opt_args.line_width = opt_args.line_width;
+ line_opt_args.speed = opt_args.speed;
+
+ for (int i = 0; i < opt_args.num_perimeters; ++i) {
+ if (i != 0) { // after first perimeter, step inwards to start next perimeter
+ x += spacing;
+ y += spacing;
+ gcode << move_to(Vec2d(x, y), m_writer, "Step inwards to print next perimeter");
+ }
+
+ y += size_y - i * spacing * 2;
+ line_opt_args.comment = "Draw perimeter (up)";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+
+ x += size_x - i * spacing * 2;
+ line_opt_args.comment = "Draw perimeter (right)";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+
+ y -= size_y - i * spacing * 2;
+ line_opt_args.comment = "Draw perimeter (down)";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+
+ x -= size_x - i * spacing * 2;
+ line_opt_args.comment = "Draw perimeter (left)";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ }
+
+ if (!opt_args.is_filled) {
+ return gcode.str();
+ }
+
+ // create box infill
+ const double spacing_45 = spacing / std::sin(to_radians(45));
+
+ const double bound_modifier =
+ (spacing * (opt_args.num_perimeters - 1)) +
+ (opt_args.line_width * (1 - m_encroachment))
+ ;
+ const double x_min_bound = min_x + bound_modifier;
+ const double x_max_bound = max_x - bound_modifier;
+ const double y_min_bound = min_y + bound_modifier;
+ const double y_max_bound = max_y - bound_modifier;
+ const int x_count = std::floor((x_max_bound - x_min_bound) / spacing_45);
+ const int y_count = std::floor((y_max_bound - y_min_bound) / spacing_45);
+
+ double x_remainder = std::fmod((x_max_bound - x_min_bound), spacing_45);
+ double y_remainder = std::fmod((y_max_bound - y_min_bound), spacing_45);
+
+ x = x_min_bound;
+ y = y_min_bound;
+
+ gcode << move_to(Vec2d(x, y), m_writer, "Move to fill start");
+
+ for (int i = 0; i < x_count + y_count + (x_remainder + y_remainder >= spacing_45 ? 1 : 0); ++i) { // this isn't the most robust way, but less expensive than finding line intersections
+ if (i < std::min(x_count, y_count)) {
+ if (i % 2 == 0) {
+ x += spacing_45;
+ y = y_min_bound;
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right");
+
+ y += x - x_min_bound;
+ x = x_min_bound;
+ line_opt_args.comment = "Fill: Print up/left";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ } else {
+ y += spacing_45;
+ x = x_min_bound;
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up");
+
+ x += y - y_min_bound;
+ y = y_min_bound;
+ line_opt_args.comment = "Fill: Print down/right";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ }
+ } else if (i < std::max(x_count, y_count)) {
+ if (x_count > y_count) {
+ // box is wider than tall
+ if (i % 2 == 0) {
+ x += spacing_45;
+ y = y_min_bound;
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right");
+
+ x -= y_max_bound - y_min_bound;
+ y = y_max_bound;
+ line_opt_args.comment = "Fill: Print up/left";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ } else {
+ if (i == y_count) {
+ x += spacing_45 - y_remainder;
+ y_remainder = 0;
+ } else {
+ x += spacing_45;
+ }
+ y = y_max_bound;
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right");
+
+ x += y_max_bound - y_min_bound;
+ y = y_min_bound;
+ line_opt_args.comment = "Fill: Print down/right";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ }
+ } else {
+ // box is taller than wide
+ if (i % 2 == 0) {
+ x = x_max_bound;
+ if (i == x_count) {
+ y += spacing_45 - x_remainder;
+ x_remainder = 0;
+ } else {
+ y += spacing_45;
+ }
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up");
+
+ x = x_min_bound;
+ y += x_max_bound - x_min_bound;
+ line_opt_args.comment = "Fill: Print up/left";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ } else {
+ x = x_min_bound;
+ y += spacing_45;
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up");
+
+ x = x_max_bound;
+ y -= x_max_bound - x_min_bound;
+ line_opt_args.comment = "Fill: Print down/right";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ }
+ }
+ } else {
+ if (i % 2 == 0) {
+ x = x_max_bound;
+ if (i == x_count) {
+ y += spacing_45 - x_remainder;
+ } else {
+ y += spacing_45;
+ }
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up");
+
+ x -= y_max_bound - y;
+ y = y_max_bound;
+ line_opt_args.comment = "Fill: Print up/left";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ } else {
+ if (i == y_count) {
+ x += spacing_45 - y_remainder;
+ } else {
+ x += spacing_45;
+ }
+ y = y_max_bound;
+ gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right");
+
+ y -= x_max_bound - x;
+ x = x_max_bound;
+ line_opt_args.comment = "Fill: Print down/right";
+ gcode << draw_line(Vec2d(x, y), line_opt_args);
+ }
+ }
+ }
+
+ return gcode.str();
+}
+
+double CalibPressureAdvancePattern::get_distance(Vec2d from, Vec2d to) const
+{
+ return std::hypot((to.x() - from.x()), (to.y() - from.y()));
+}
+
+double CalibPressureAdvancePattern::object_size_x() const
+{
+ return get_num_patterns() * ((wall_count() - 1) * line_spacing_angle()) +
+ (get_num_patterns() - 1) * (m_pattern_spacing + line_width()) +
+ std::cos(to_radians(m_corner_angle) / 2) * m_wall_side_length +
+ line_spacing_first_layer() * wall_count()
+ ;
+}
+
+double CalibPressureAdvancePattern::object_size_y() const
+{
+ return 2 * (std::sin(to_radians(m_corner_angle) / 2) * m_wall_side_length) +
+ max_numbering_height() +
+ m_glyph_padding_vertical * 2 +
+ line_width_first_layer();
+}
+
+double CalibPressureAdvancePattern::glyph_start_x(int pattern_i) const
+{
+ // note that pattern_i is zero-based!
+ // align glyph's start with first perimeter of specified pattern
+ double x =
+ // starting offset
+ m_starting_point.x() +
+ pattern_shift() +
+
+ // width of pattern extrusions
+ pattern_i * (wall_count() - 1) * line_spacing_angle() + // center to center distance of extrusions
+ pattern_i * line_width() + // endcaps. center to end on either side = 1 line width
+
+ // space between each pattern
+ pattern_i * m_pattern_spacing
+ ;
+
+ // align to middle of pattern walls
+ x += wall_count() * line_spacing_angle() / 2;
+
+ // shift so glyph is centered on pattern
+ // m_digit_segment_len = half of X length of glyph
+ x -= (glyph_length_x() / 2);
+
+ return x;
+}
+
+double CalibPressureAdvancePattern::glyph_length_x() const
+{
+ // half of line_width sticks out on each side
+ return line_width() + (2 * m_digit_segment_len);
+}
+
+double CalibPressureAdvancePattern::glyph_tab_max_x() const
+{
+ // only every other glyph is shown, starting with 1
+ int num = get_num_patterns();
+ int max_num =
+ (num % 2 == 0)
+ ? num - 1
+ : num
+ ;
+
+ // padding at end should be same as padding at start
+ double padding = glyph_start_x(0) - m_starting_point.x();
+
+ return
+ glyph_start_x(max_num - 1) + // glyph_start_x is zero-based
+ (glyph_length_x() - line_width() / 2) +
+ padding
+ ;
+}
+
+double CalibPressureAdvancePattern::max_numbering_height() const
+{
+ std::string::size_type most_characters = 0;
+ const int num_patterns = get_num_patterns();
+
+ // note: only every other number is printed
+ for (std::string::size_type i = 0; i < num_patterns; i += 2) {
+ std::string sNumber = convert_number_to_string(m_params.start + (i * m_params.step));
+
+ if (sNumber.length() > most_characters) {
+ most_characters = sNumber.length();
+ }
+ }
+
+ most_characters = std::min(most_characters, m_max_number_len);
+
+ return (most_characters * m_digit_segment_len) + ((most_characters - 1) * m_digit_gap_len);
+}
+
+double CalibPressureAdvancePattern::pattern_shift() const
+{
+ return
+ (wall_count() - 1) * line_spacing_first_layer() +
+ line_width_first_layer() +
+ m_glyph_padding_horizontal
+ ;
+}
+} // namespace Slic3r
diff --git a/src/libslic3r/calib.hpp b/src/libslic3r/calib.hpp
new file mode 100644
index 0000000..59d671d
--- /dev/null
+++ b/src/libslic3r/calib.hpp
@@ -0,0 +1,280 @@
+#pragma once
+#define calib_pressure_advance_dd
+
+#include "GCode.hpp"
+#include "GCodeWriter.hpp"
+#include "PrintConfig.hpp"
+
+namespace Slic3r {
+
+enum class CalibMode : int {
+ Calib_None = 0,
+ Calib_PA_Line,
+ Calib_PA_Pattern,
+ Calib_PA_Tower,
+ Calib_Temp_Tower,
+ Calib_Vol_speed_Tower,
+ Calib_VFA_Tower,
+ Calib_Retraction_tower
+};
+
+struct Calib_Params {
+ Calib_Params() : mode(CalibMode::Calib_None) { };
+ double start, end, step;
+ bool print_numbers;
+ CalibMode mode;
+};
+
+class CalibPressureAdvance {
+ public:
+ static float find_optimal_PA_speed(const DynamicPrintConfig &config, double line_width, double layer_height,
+ int filament_idx = 0);
+
+ protected:
+ CalibPressureAdvance() =default;
+ ~CalibPressureAdvance() =default;
+
+ enum class DrawDigitMode {
+ Left_To_Right,
+ Bottom_To_Top
+ };
+
+ void delta_scale_bed_ext(BoundingBoxf& bed_ext) const { bed_ext.scale(1.0f / 1.41421f); }
+
+ std::string move_to(Vec2d pt, GCodeWriter& writer, std::string comment = std::string());
+ double e_per_mm(
+ double line_width,
+ double layer_height,
+ float nozzle_diameter,
+ float filament_diameter,
+ float print_flow_ratio
+ ) const;
+ double speed_adjust(int speed) const { return speed * 60; };
+
+ std::string convert_number_to_string(double num) const;
+ double number_spacing() const { return m_digit_segment_len + m_digit_gap_len; };
+ std::string draw_digit(
+ double startx,
+ double starty,
+ char c,
+ CalibPressureAdvance::DrawDigitMode mode,
+ double line_width,
+ double e_per_mm,
+ GCodeWriter& writer
+ );
+ std::string draw_number(
+ double startx,
+ double starty,
+ double value,
+ CalibPressureAdvance::DrawDigitMode mode,
+ double line_width,
+ double e_per_mm,
+ double speed,
+ GCodeWriter& writer
+ );
+
+ Vec3d m_last_pos;
+
+ DrawDigitMode m_draw_digit_mode {DrawDigitMode::Left_To_Right};
+ const double m_digit_segment_len {2};
+ const double m_digit_gap_len {1};
+ const std::string::size_type m_max_number_len {5};
+};
+
+class CalibPressureAdvanceLine : public CalibPressureAdvance {
+public:
+ CalibPressureAdvanceLine(GCode* gcodegen) :
+ mp_gcodegen(gcodegen),
+ m_nozzle_diameter(gcodegen->config().nozzle_diameter.get_at(0))
+ { };
+ ~CalibPressureAdvanceLine() { };
+
+ std::string generate_test(double start_pa = 0, double step_pa = 0.002, int count = 50);
+
+ void set_speed(double fast = 100.0, double slow = 20.0) {
+ m_slow_speed = slow;
+ m_fast_speed = fast;
+ }
+
+ const double& line_width() { return m_line_width; };
+ bool is_delta() const;
+ bool& draw_numbers() { return m_draw_numbers; }
+
+private:
+ std::string print_pa_lines(double start_x, double start_y, double start_pa, double step_pa, int num);
+
+ void delta_modify_start(double& startx, double& starty, int count);
+
+ GCode* mp_gcodegen;
+
+ double m_nozzle_diameter;
+ double m_slow_speed, m_fast_speed;
+
+ const double m_height_layer {0.2};
+ const double m_line_width {0.6};
+ const double m_thin_line_width {0.44};
+ const double m_number_line_width {0.48};
+ const double m_space_y {3.5};
+
+ double m_length_short {20.0}, m_length_long {40.0};
+ bool m_draw_numbers {true};
+};
+
+struct SuggestedConfigCalibPAPattern {
+ const std::vector> float_pairs {
+ {"initial_layer_print_height", 0.25},
+ {"layer_height", 0.2},
+ {"initial_layer_speed", 30}
+ };
+
+ const std::vector> nozzle_ratio_pairs {
+ {"line_width", 112.5},
+ {"initial_layer_line_width", 140}
+ };
+
+ const std::vector> int_pairs {
+ {"skirt_loops", 0},
+ {"wall_loops", 3}
+ };
+
+ const std::pair brim_pair {"brim_type", BrimType::btNoBrim};
+};
+
+class CalibPressureAdvancePattern : public CalibPressureAdvance {
+friend struct DrawLineOptArgs;
+friend struct DrawBoxOptArgs;
+
+public:
+ CalibPressureAdvancePattern(
+ const Calib_Params& params,
+ const DynamicPrintConfig& config,
+ bool is_bbl_machine,
+ Model& model,
+ const Vec3d& origin
+ );
+
+ double handle_xy_size() const { return m_handle_xy_size; };
+ double handle_spacing() const { return m_handle_spacing; };
+ double print_size_x() const { return object_size_x() + pattern_shift(); };
+ double print_size_y() const { return object_size_y(); };
+ double max_layer_z() const { return height_first_layer() + ((m_num_layers - 1) * height_layer()); };
+
+ void generate_custom_gcodes(
+ const DynamicPrintConfig& config,
+ bool is_bbl_machine,
+ Model& model,
+ const Vec3d& origin
+ );
+
+protected:
+ double speed_first_layer() const { return m_config.option("initial_layer_speed")->value; };
+ double speed_perimeter() const { return m_config.option("outer_wall_speed")->value; };
+ double line_width_first_layer() const { return m_config.get_abs_value("initial_layer_line_width"); };
+ double line_width() const { return m_config.get_abs_value("line_width"); };
+ int wall_count() const { return m_config.option("wall_loops")->value; };
+
+private:
+ struct DrawLineOptArgs {
+ DrawLineOptArgs(const CalibPressureAdvancePattern& p) :
+ height {p.height_layer()},
+ line_width {p.line_width()},
+ speed {p.speed_adjust(p.speed_perimeter())}
+ { };
+
+ double height;
+ double line_width;
+ double speed;
+ std::string comment {"Print line"};
+ };
+
+ struct DrawBoxOptArgs {
+ DrawBoxOptArgs(const CalibPressureAdvancePattern& p) :
+ num_perimeters {p.wall_count()},
+ height {p.height_first_layer()},
+ line_width {p.line_width_first_layer()},
+ speed {p.speed_adjust(p.speed_first_layer())}
+ { };
+
+ bool is_filled {false};
+ int num_perimeters;
+ double height;
+ double line_width;
+ double speed;
+ };
+
+ void refresh_setup(
+ const DynamicPrintConfig& config,
+ bool is_bbl_machine,
+ const Model& model,
+ const Vec3d& origin
+ );
+ void _refresh_starting_point(const Model& model);
+ void _refresh_writer(
+ bool is_bbl_machine,
+ const Model& model,
+ const Vec3d& origin
+ );
+
+ double height_first_layer() const { return m_config.option("initial_layer_print_height")->value; };
+ double height_layer() const { return m_config.option("layer_height")->value; };
+ const int get_num_patterns() const
+ {
+ return std::ceil((m_params.end - m_params.start) / m_params.step + 1);
+ }
+
+ std::string draw_line(
+ Vec2d to_pt,
+ DrawLineOptArgs opt_args
+ );
+ std::string draw_box(
+ double min_x,
+ double min_y,
+ double size_x,
+ double size_y,
+ DrawBoxOptArgs opt_args
+ );
+
+ double to_radians(double degrees) const { return degrees * M_PI / 180; };
+ double get_distance(Vec2d from, Vec2d to) const;
+
+ /*
+ from slic3r documentation: spacing = extrusion_width - layer_height * (1 - PI/4)
+ "spacing" = center-to-center distance of adjacent extrusions, which partially overlap
+ https://manual.slic3r.org/advanced/flow-math
+ https://ellis3dp.com/Print-Tuning-Guide/articles/misconceptions.html#two-04mm-perimeters--08mm
+ */
+ double line_spacing() const { return line_width() - height_layer() * (1 - M_PI / 4); };
+ double line_spacing_first_layer() const { return line_width_first_layer() - height_first_layer() * (1 - M_PI / 4); };
+ double line_spacing_angle() const { return line_spacing() / std::sin(to_radians(m_corner_angle) / 2); };
+
+ double object_size_x() const;
+ double object_size_y() const;
+ double frame_size_y() const { return std::sin(to_radians(double(m_corner_angle) / 2)) * m_wall_side_length * 2; };
+
+ double glyph_start_x(int pattern_i = 0) const;
+ double glyph_length_x() const;
+ double glyph_tab_max_x() const;
+ double max_numbering_height() const;
+
+ double pattern_shift() const;
+
+ const Calib_Params& m_params;
+
+ DynamicPrintConfig m_config;
+ GCodeWriter m_writer;
+ bool m_is_delta;
+ Vec3d m_starting_point;
+
+ const double m_handle_xy_size {5};
+ const double m_handle_spacing {2};
+ const int m_num_layers {4};
+
+ const double m_wall_side_length {30.0};
+ const int m_corner_angle {90};
+ const int m_pattern_spacing {2};
+ const double m_encroachment {1. / 3.};
+
+ const double m_glyph_padding_horizontal {1};
+ const double m_glyph_padding_vertical {1};
+};
+} // namespace Slic3r
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index aca55aa..e76efeb 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -26,8 +26,27 @@ set(SLIC3R_GUI_SOURCES
GUI/Widgets/StateHandler.hpp
GUI/Widgets/StaticBox.cpp
GUI/Widgets/StaticBox.hpp
+ GUI/Widgets/Button.cpp
+ GUI/Widgets/Button.hpp
+ GUI/Widgets/CheckBoxInWT.cpp
+ GUI/Widgets/CheckBoxInWT.hpp
+ GUI/Widgets/ComboBox.cpp
+ GUI/Widgets/ComboBox.hpp
+ GUI/Widgets/TextInput.cpp
+ GUI/Widgets/TextInput.hpp
+ GUI/Widgets/TextCtrl.h
+ GUI/Widgets/RoundedRectangle.cpp
+ GUI/Widgets/RoundedRectangle.hpp
+ GUI/Widgets/RadioBox.cpp
+ GUI/Widgets/RadioBox.hpp
+ GUI/Widgets/DropDown.cpp
+ GUI/Widgets/DropDown.hpp
+ GUI/Widgets/PopupWindow.cpp
+ GUI/Widgets/PopupWindow.hpp
GUI/ConfigSnapshotDialog.cpp
GUI/ConfigSnapshotDialog.hpp
+ GUI/calib_dlg.cpp
+ GUI/calib_dlg.hpp
GUI/3DScene.cpp
GUI/3DScene.hpp
GUI/format.hpp
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 74a8719..7c2adb0 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -831,7 +831,8 @@ void Preview::load_print_as_fff(bool keep_z_range)
unsigned int number_extruders = wxGetApp().is_editor() ?
(unsigned int)print->extruders().size() :
m_canvas->get_gcode_extruders_count();
- std::vector- gcodes = wxGetApp().is_editor() ?
+ //B34
+ std::vector gcodes = wxGetApp().is_editor() ?
wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes :
m_canvas->get_custom_gcode_per_print_z();
const bool contains_color_gcodes = std::any_of(std::begin(gcodes), std::end(gcodes),
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 6f71b1b..a8189d9 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -880,6 +880,7 @@ void MainFrame::create_preset_tabs()
else
#endif
m_tabpanel->AddPage(m_guide_view, _L("Guide"));
+
}
void MainFrame::add_created_tab(Tab* panel, const std::string& bmp_name /*= ""*/)
@@ -1566,6 +1567,45 @@ void MainFrame::init_menubar_as_editor()
// Help menu
auto helpMenu = generate_help_menu();
+
+ //B34
+ //auto calibrationMenu = generate_calibration_menu();
+ auto calibrationMenu = new wxMenu();
+ auto flowrate_menu = new wxMenu();
+ append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow rate test - Pass 1"), [this](wxCommandEvent &) {
+ if (m_plater)
+ m_plater->calib_flowrate(1);
+ },
+ "", nullptr,
+ [this]() {
+ return m_plater->is_view3D_shown();
+ ;
+ },
+ this);
+ append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow rate test - Pass 2"), [this](wxCommandEvent &) {
+ if (m_plater)
+ m_plater->calib_flowrate(2);
+ },
+ "", nullptr,
+ [this]() {
+ return m_plater->is_view3D_shown();
+ ;
+ },
+ this);
+ calibrationMenu->AppendSubMenu(flowrate_menu, _L("Flow rate"));
+ append_menu_item(
+ calibrationMenu, wxID_ANY, _L("Pressure advance"), _L("Pressure advance"),
+ [this](wxCommandEvent &) {
+ if (!m_pa_calib_dlg)
+ m_pa_calib_dlg = new PA_Calibration_Dlg((wxWindow *) this, wxID_ANY, m_plater);
+ m_pa_calib_dlg->ShowModal();
+ },
+ "", nullptr,
+ [this]() {
+ return m_plater->is_view3D_shown();
+ ;
+ },
+ this);
// menubar
// assign menubar to frame after appending items, otherwise special items
@@ -1579,6 +1619,8 @@ void MainFrame::init_menubar_as_editor()
// Add additional menus from C++
wxGetApp().add_config_menu(m_menubar);
m_menubar->Append(helpMenu, _L("&Help"));
+ //B34
+ m_menubar->Append(calibrationMenu, _L("&Calibration"));
#ifdef _MSW_DARK_MODE
if (wxGetApp().tabs_as_menu()) {
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 64c04f3..ccda7c8 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -21,6 +21,8 @@
#include "PrinterWebView.hpp"
// B28
#include "GuideWebView.hpp"
+//B34
+#include "calib_dlg.hpp"
class wxBookCtrlBase;
class wxProgressDialog;
@@ -210,7 +212,8 @@ public:
Plater *m_plater{nullptr};
-
+ //B34
+ PA_Calibration_Dlg *m_pa_calib_dlg{nullptr};
//B4
wxString tem_host;
PrinterWebView * m_printer_view{nullptr};
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index eab71b7..969eec0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -5363,6 +5363,81 @@ void Plater::add_model(bool imperial_units/* = false*/)
if (! load_files(paths, true, false, imperial_units).empty())
wxGetApp().mainframe->update_title();
}
+//B34
+void Plater::add_model_calibration(bool imperial_units /* = false*/, std::string fname /* = ""*/)
+{
+ std::vector paths;
+
+ if (fname.empty()) {
+ wxArrayString input_files;
+ wxGetApp().import_model(this, input_files);
+ if (input_files.empty())
+ return;
+
+ for (const auto &file : input_files)
+ paths.emplace_back(into_path(file));
+ } else {
+ paths.emplace_back(fname);
+ }
+
+ wxString snapshot_label;
+ assert(!paths.empty());
+ if (paths.size() == 1) {
+ snapshot_label = "Import Object";
+ snapshot_label += ": ";
+ snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
+ } else {
+ snapshot_label = "Import Objects";
+ snapshot_label += ": ";
+ snapshot_label += paths.front().filename().string().c_str();
+ for (size_t i = 1; i < paths.size(); ++i) {
+ snapshot_label += ", ";
+ snapshot_label += wxString::FromUTF8(paths[i].filename().string().c_str());
+ }
+ }
+
+ Plater::TakeSnapshot snapshot(this, snapshot_label);
+ if (!load_files(paths, true, false, imperial_units).empty())
+ wxGetApp().mainframe->update_title();
+}
+
+//B34
+void Plater::calib_flowrate(int pass)
+{
+ if (pass != 1 && pass != 2)
+ return;
+ const auto calib_name = wxString::Format(L"Flowrate Test - Pass%d", pass);
+ new_project();
+
+ wxGetApp().mainframe->select_tab(size_t(0));
+
+ if (pass == 1)
+ add_model_calibration(false,
+ (boost::filesystem::path(Slic3r::resources_dir()) / "calib" / "filament_flow" / "flowrate-test-pass1.3mf").string());
+ else
+ add_model_calibration(false,
+ (boost::filesystem::path(Slic3r::resources_dir()) / "calib" / "filament_flow" / "flowrate-test-pass2.3mf").string());
+
+}
+//B34
+void Plater::calib_pa(const Calib_Params ¶ms)
+{
+ const auto calib_pa_name = wxString::Format(L"Pressure Advance Test");
+ new_project();
+ wxGetApp().mainframe->select_tab(size_t(0));
+
+ switch (params.mode) {
+ case CalibMode::Calib_PA_Line:
+ add_model_calibration(false, Slic3r::resources_dir() + "/calib/PressureAdvance/pressure_advance_test.stl");
+ break;
+ //case CalibMode::Calib_PA_Pattern: _calib_pa_pattern(params); break;
+ //case CalibMode::Calib_PA_Tower: _calib_pa_tower(params); break;
+ default: break;
+ }
+
+ //p->background_process.fff_print()->set_calib_params(params);
+}
+
void Plater::import_zip_archive()
{
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index fd37720..54f7ff9 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -16,6 +16,8 @@
#include "Jobs/Job.hpp"
#include "Jobs/Worker.hpp"
#include "Search.hpp"
+//B34
+ #include "libslic3r/Calib.hpp"
class wxButton;
class ScalableButton;
@@ -164,10 +166,17 @@ public:
const SLAPrint& sla_print() const;
SLAPrint& sla_print();
+ //B34
+ void calib_pa(const Calib_Params ¶ms);
+ void calib_flowrate(int pass);
+
+
void new_project();
void load_project();
void load_project(const wxString& filename);
void add_model(bool imperial_units = false);
+ //B34
+ void add_model_calibration(bool imperial_units = false, std::string fname = "");
void import_zip_archive();
void import_sl1_archive();
void extract_config_from_project();
diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp
new file mode 100644
index 0000000..585e8e2
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/Button.cpp
@@ -0,0 +1,327 @@
+#include "Button.hpp"
+#include "Label.hpp"
+
+#include
+
+BEGIN_EVENT_TABLE(Button, StaticBox)
+
+EVT_LEFT_DOWN(Button::mouseDown)
+EVT_LEFT_UP(Button::mouseReleased)
+EVT_MOUSE_CAPTURE_LOST(Button::mouseCaptureLost)
+EVT_KEY_DOWN(Button::keyDownUp)
+EVT_KEY_UP(Button::keyDownUp)
+
+// catch paint events
+EVT_PAINT(Button::paintEvent)
+
+END_EVENT_TABLE()
+
+/*
+ * Called by the system of by wxWidgets when the panel needs
+ * to be redrawn. You can also trigger this call by
+ * calling Refresh()/Update().
+ */
+
+Button::Button()
+ : paddingSize(10, 8)
+{
+ background_color = StateColor(
+ std::make_pair(0xF0F0F1, (int) StateColor::Disabled),
+ std::make_pair(0x52c7b8, (int) StateColor::Hovered | StateColor::Checked),
+ std::make_pair(0x009688, (int) StateColor::Checked),
+ std::make_pair(*wxLIGHT_GREY, (int) StateColor::Hovered),
+ std::make_pair(*wxWHITE, (int) StateColor::Normal));
+ text_color = StateColor(
+ std::make_pair(*wxLIGHT_GREY, (int) StateColor::Disabled),
+ std::make_pair(*wxBLACK, (int) StateColor::Normal));
+}
+
+Button::Button(wxWindow* parent, wxString text, wxString icon, long style, int iconSize, wxWindowID btn_id)
+ : Button()
+{
+ Create(parent, text, icon, style, iconSize, btn_id);
+}
+
+bool Button::Create(wxWindow* parent, wxString text, wxString icon, long style, int iconSize, wxWindowID btn_id)
+{
+ StaticBox::Create(parent, btn_id, wxDefaultPosition, wxDefaultSize, style);
+ state_handler.attach({&text_color});
+ state_handler.update_binds();
+ //BBS set default font
+ SetFont(Label::Body_14);
+ wxWindow::SetLabel(text);
+ if (!icon.IsEmpty()) {
+ //BBS set button icon default size to 20
+ this->active_icon = ScalableBitmap(this, icon.ToStdString(), iconSize > 0 ? iconSize : 20);
+ }
+ messureSize();
+ return true;
+}
+
+void Button::SetLabel(const wxString& label)
+{
+ wxWindow::SetLabel(label);
+ messureSize();
+ Refresh();
+}
+
+bool Button::SetFont(const wxFont& font)
+{
+ wxWindow::SetFont(font);
+ messureSize();
+ Refresh();
+ return true;
+}
+
+void Button::SetIcon(const wxString& icon)
+{
+ if (!icon.IsEmpty()) {
+ //BBS set button icon default size to 20
+ this->active_icon = ScalableBitmap(this, icon.ToStdString(), this->active_icon.px_cnt());
+ }
+ else
+ {
+ this->active_icon = ScalableBitmap();
+ }
+ Refresh();
+}
+
+void Button::SetInactiveIcon(const wxString &icon)
+{
+ if (!icon.IsEmpty()) {
+ // BBS set button icon default size to 20
+ this->inactive_icon = ScalableBitmap(this, icon.ToStdString(), this->active_icon.px_cnt());
+ } else {
+ this->inactive_icon = ScalableBitmap();
+ }
+ Refresh();
+}
+
+void Button::SetMinSize(const wxSize& size)
+{
+ minSize = size;
+ messureSize();
+}
+
+void Button::SetPaddingSize(const wxSize& size)
+{
+ paddingSize = size;
+ messureSize();
+}
+
+void Button::SetTextColor(StateColor const& color)
+{
+ text_color = color;
+ state_handler.update_binds();
+ Refresh();
+}
+
+void Button::SetTextColorNormal(wxColor const &color)
+{
+ text_color.setColorForStates(color, 0);
+ Refresh();
+}
+
+bool Button::Enable(bool enable)
+{
+ bool result = wxWindow::Enable(enable);
+ if (result) {
+ wxCommandEvent e(EVT_ENABLE_CHANGED);
+ e.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(e);
+ }
+ return result;
+}
+
+void Button::SetCanFocus(bool canFocus) { this->canFocus = canFocus; }
+
+void Button::SetValue(bool state)
+{
+ if (GetValue() == state) return;
+ state_handler.set_state(state ? StateHandler::Checked : 0, StateHandler::Checked);
+}
+
+bool Button::GetValue() const { return state_handler.states() & StateHandler::Checked; }
+
+void Button::Rescale()
+{
+ if (this->active_icon.get_bitmap().IsOk())
+ this->active_icon.msw_rescale();
+
+ if (this->inactive_icon.get_bitmap().IsOk())
+ this->inactive_icon.msw_rescale();
+
+ messureSize();
+}
+
+void Button::paintEvent(wxPaintEvent& evt)
+{
+ // depending on your system you may need to look at double-buffered dcs
+ wxPaintDC dc(this);
+ render(dc);
+}
+
+/*
+ * Here we do the actual rendering. I put it in a separate
+ * method so that it can work no matter what type of DC
+ * (e.g. wxPaintDC or wxClientDC) is used.
+ */
+void Button::render(wxDC& dc)
+{
+ StaticBox::render(dc);
+ int states = state_handler.states();
+ wxSize size = GetSize();
+ dc.SetBrush(*wxTRANSPARENT_BRUSH);
+ // calc content size
+ wxSize szIcon;
+ wxSize szContent = textSize.GetSize();
+
+ ScalableBitmap icon;
+ if (m_selected || ((states & (int)StateColor::State::Hovered) != 0))
+ icon = active_icon;
+ else
+ icon = inactive_icon;
+ int padding = 5;
+ if (icon.get_bitmap().IsOk()) {
+ if (szContent.y > 0) {
+ //BBS norrow size between text and icon
+ szContent.x += padding;
+ }
+ szIcon = icon.GetSize();
+ szContent.x += szIcon.x;
+ if (szIcon.y > szContent.y)
+ szContent.y = szIcon.y;
+ if (szContent.x > size.x) {
+ int d = std::min(padding, szContent.x - size.x);
+ padding -= d;
+ szContent.x -= d;
+ }
+ }
+ // move to center
+ wxRect rcContent = { {0, 0}, size };
+ wxSize offset = (size - szContent) / 2;
+ if (offset.x < 0) offset.x = 0;
+ rcContent.Deflate(offset.x, offset.y);
+ // start draw
+ wxPoint pt = rcContent.GetLeftTop();
+ if (icon.get_bitmap().IsOk()) {
+ pt.y += (rcContent.height - szIcon.y) / 2;
+ dc.DrawBitmap(icon.get_bitmap(), pt);
+ //BBS norrow size between text and icon
+ pt.x += szIcon.x + padding;
+ pt.y = rcContent.y;
+ }
+ auto text = GetLabel();
+ if (!text.IsEmpty()) {
+ if (pt.x + textSize.width > size.x)
+ text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x);
+ pt.y += (rcContent.height - textSize.height) / 2;
+ dc.SetTextForeground(text_color.colorForStates(states));
+#if 0
+ dc.SetBrush(*wxLIGHT_GREY);
+ dc.SetPen(wxPen(*wxLIGHT_GREY));
+ dc.DrawRectangle(pt, textSize.GetSize());
+#endif
+#ifdef __WXOSX__
+ pt.y -= textSize.x / 2;
+#endif
+ dc.DrawText(text, pt);
+ }
+}
+
+void Button::messureSize()
+{
+ wxClientDC dc(this);
+ dc.GetTextExtent(GetLabel(), &textSize.width, &textSize.height, &textSize.x, &textSize.y);
+ wxSize szContent = textSize.GetSize();
+ if (this->active_icon.get_bitmap().IsOk()) {
+ if (szContent.y > 0) {
+ //BBS norrow size between text and icon
+ szContent.x += 5;
+ }
+ wxSize szIcon = this->active_icon.GetSize();
+ szContent.x += szIcon.x;
+ if (szIcon.y > szContent.y)
+ szContent.y = szIcon.y;
+ }
+ wxSize size = szContent + paddingSize * 2;
+ if (minSize.GetHeight() > 0)
+ size.SetHeight(minSize.GetHeight());
+
+ if (minSize.GetWidth() > size.GetWidth())
+ wxWindow::SetMinSize(minSize);
+ else
+ wxWindow::SetMinSize(size);
+}
+
+void Button::mouseDown(wxMouseEvent& event)
+{
+ event.Skip();
+ pressedDown = true;
+ if (canFocus)
+ SetFocus();
+ if (!HasCapture())
+ CaptureMouse();
+}
+
+void Button::mouseReleased(wxMouseEvent& event)
+{
+ event.Skip();
+ if (pressedDown) {
+ pressedDown = false;
+ if (HasCapture())
+ ReleaseMouse();
+ if (wxRect({0, 0}, GetSize()).Contains(event.GetPosition()))
+ sendButtonEvent();
+ }
+}
+
+void Button::mouseCaptureLost(wxMouseCaptureLostEvent &event)
+{
+ wxMouseEvent evt;
+ mouseReleased(evt);
+}
+
+void Button::keyDownUp(wxKeyEvent &event)
+{
+ if (event.GetKeyCode() == WXK_SPACE || event.GetKeyCode() == WXK_RETURN) {
+ wxMouseEvent evt(event.GetEventType() == wxEVT_KEY_UP ? wxEVT_LEFT_UP : wxEVT_LEFT_DOWN);
+ event.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(evt);
+ return;
+ }
+ if (event.GetEventType() == wxEVT_KEY_DOWN &&
+ (event.GetKeyCode() == WXK_TAB || event.GetKeyCode() == WXK_LEFT || event.GetKeyCode() == WXK_RIGHT
+ || event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_DOWN))
+ HandleAsNavigationKey(event);
+ else
+ event.Skip();
+}
+
+void Button::sendButtonEvent()
+{
+ wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
+ event.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(event);
+}
+
+#ifdef __WIN32__
+
+WXLRESULT Button::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ if (nMsg == WM_GETDLGCODE) { return DLGC_WANTMESSAGE; }
+ if (nMsg == WM_KEYDOWN) {
+ wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam));
+ switch (wParam) {
+ case WXK_RETURN: { // WXK_RETURN key is handled by default button
+ GetEventHandler()->ProcessEvent(event);
+ return 0;
+ }
+ }
+ }
+ return wxWindow::MSWWindowProc(nMsg, wParam, lParam);
+}
+
+#endif
+
+bool Button::AcceptsFocus() const { return canFocus; }
diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp
new file mode 100644
index 0000000..2f5c8ea
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/Button.hpp
@@ -0,0 +1,84 @@
+#ifndef slic3r_GUI_Button_hpp_
+#define slic3r_GUI_Button_hpp_
+
+#include "../wxExtensions.hpp"
+#include "StaticBox.hpp"
+
+class Button : public StaticBox
+{
+ wxRect textSize;
+ wxSize minSize; // set by outer
+ wxSize paddingSize;
+ ScalableBitmap active_icon;
+ ScalableBitmap inactive_icon;
+
+ StateColor text_color;
+
+ bool pressedDown = false;
+ bool m_selected = true;
+ bool canFocus = true;
+
+ static const int buttonWidth = 200;
+ static const int buttonHeight = 50;
+
+public:
+ Button();
+
+ Button(wxWindow* parent, wxString text, wxString icon = "", long style = 0, int iconSize = 0, wxWindowID btn_id = wxID_ANY);
+
+ bool Create(wxWindow* parent, wxString text, wxString icon = "", long style = 0, int iconSize = 0, wxWindowID btn_id = wxID_ANY);
+
+ void SetLabel(const wxString& label) override;
+
+ bool SetFont(const wxFont& font) override;
+
+ void SetIcon(const wxString& icon);
+
+ void SetInactiveIcon(const wxString& icon);
+
+ void SetMinSize(const wxSize& size) override;
+
+ void SetPaddingSize(const wxSize& size);
+
+ void SetTextColor(StateColor const &color);
+
+ void SetTextColorNormal(wxColor const &color);
+
+ void SetSelected(bool selected = true) { m_selected = selected; }
+
+ bool Enable(bool enable = true) override;
+
+ void SetCanFocus(bool canFocus) override;
+
+ void SetValue(bool state);
+
+ bool GetValue() const;
+
+ void Rescale();
+
+protected:
+#ifdef __WIN32__
+ WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override;
+#endif
+
+ bool AcceptsFocus() const override;
+
+private:
+ void paintEvent(wxPaintEvent& evt);
+
+ void render(wxDC& dc);
+
+ void messureSize();
+
+ // some useful events
+ void mouseDown(wxMouseEvent& event);
+ void mouseReleased(wxMouseEvent& event);
+ void mouseCaptureLost(wxMouseCaptureLostEvent &event);
+ void keyDownUp(wxKeyEvent &event);
+
+ void sendButtonEvent();
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif // !slic3r_GUI_Button_hpp_
diff --git a/src/slic3r/GUI/Widgets/CheckBoxInWT.cpp b/src/slic3r/GUI/Widgets/CheckBoxInWT.cpp
new file mode 100644
index 0000000..efbea05
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/CheckBoxInWT.cpp
@@ -0,0 +1,125 @@
+#include "CheckBoxInWT.hpp"
+
+#include "../wxExtensions.hpp"
+
+CheckBoxInWT::CheckBoxInWT(wxWindow *parent, int id)
+ : wxBitmapToggleButton(parent, id, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)
+ , m_on(this, "check_on", 18)
+ , m_half(this, "check_half", 18)
+ , m_off(this, "check_off", 18)
+ , m_on_disabled(this, "check_on_disabled", 18)
+ , m_half_disabled(this, "check_half_disabled", 18)
+ , m_off_disabled(this, "check_off_disabled", 18)
+ , m_on_focused(this, "check_on_focused", 18)
+ , m_half_focused(this, "check_half_focused", 18)
+ , m_off_focused(this, "check_off_focused", 18)
+{
+ //SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
+ if (parent)
+ SetBackgroundColour(parent->GetBackgroundColour());
+ Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { m_half_checked = false; update(); e.Skip(); });
+#ifdef __WXOSX__ // State not fully implement on MacOS
+ Bind(wxEVT_SET_FOCUS, &CheckBoxInWT::updateBitmap, this);
+ Bind(wxEVT_KILL_FOCUS, &CheckBoxInWT::updateBitmap, this);
+ Bind(wxEVT_ENTER_WINDOW, &CheckBoxInWT::updateBitmap, this);
+ Bind(wxEVT_LEAVE_WINDOW, &CheckBoxInWT::updateBitmap, this);
+#endif
+ SetSize(m_on.GetSize());
+ SetMinSize(m_on.GetSize());
+ update();
+}
+
+void CheckBoxInWT::SetValue(bool value)
+{
+ wxBitmapToggleButton::SetValue(value);
+ update();
+}
+
+void CheckBoxInWT::SetHalfChecked(bool value)
+{
+ m_half_checked = value;
+ update();
+}
+
+void CheckBoxInWT::Rescale()
+{
+ m_on.msw_rescale();
+ m_half.msw_rescale();
+ m_off.msw_rescale();
+ m_on_disabled.msw_rescale();
+ m_half_disabled.msw_rescale();
+ m_off_disabled.msw_rescale();
+ m_on_focused.msw_rescale();
+ m_half_focused.msw_rescale();
+ m_off_focused.msw_rescale();
+ SetSize(m_on.GetSize());
+ update();
+}
+
+void CheckBoxInWT::update()
+{
+ SetBitmapLabel((m_half_checked ? m_half : GetValue() ? m_on : m_off).bmp());
+ SetBitmapDisabled((m_half_checked ? m_half_disabled : GetValue() ? m_on_disabled : m_off_disabled).bmp());
+#ifdef __WXMSW__
+ SetBitmapFocus((m_half_checked ? m_half_focused : GetValue() ? m_on_focused : m_off_focused).bmp());
+#endif
+ SetBitmapCurrent((m_half_checked ? m_half_focused : GetValue() ? m_on_focused : m_off_focused).bmp());
+#ifdef __WXOSX__
+ wxCommandEvent e(wxEVT_UPDATE_UI);
+ updateBitmap(e);
+#endif
+}
+
+#ifdef __WXMSW__
+
+CheckBoxInWT::State CheckBoxInWT::GetNormalState() const { return State_Normal; }
+
+#endif
+
+
+#ifdef __WXOSX__
+
+bool CheckBoxInWT::Enable(bool enable)
+{
+ bool result = wxBitmapToggleButton::Enable(enable);
+ if (result) {
+ m_disable = !enable;
+ wxCommandEvent e(wxEVT_ACTIVATE);
+ updateBitmap(e);
+ }
+ return result;
+}
+
+wxBitmap CheckBoxInWT::DoGetBitmap(State which) const
+{
+ if (m_disable) {
+ return wxBitmapToggleButton::DoGetBitmap(State_Disabled);
+ }
+ if (m_focus) {
+ return wxBitmapToggleButton::DoGetBitmap(State_Current);
+ }
+ return wxBitmapToggleButton::DoGetBitmap(which);
+}
+
+void CheckBoxInWT::updateBitmap(wxEvent &evt)
+{
+ evt.Skip();
+ if (evt.GetEventType() == wxEVT_ENTER_WINDOW) {
+ m_hover = true;
+ } else if (evt.GetEventType() == wxEVT_LEAVE_WINDOW) {
+ m_hover = false;
+ } else {
+ if (evt.GetEventType() == wxEVT_SET_FOCUS) {
+ m_focus = true;
+ } else if (evt.GetEventType() == wxEVT_KILL_FOCUS) {
+ m_focus = false;
+ }
+ wxMouseEvent e;
+ if (m_hover)
+ OnEnterWindow(e);
+ else
+ OnLeaveWindow(e);
+ }
+}
+
+#endif
diff --git a/src/slic3r/GUI/Widgets/CheckBoxInWT.hpp b/src/slic3r/GUI/Widgets/CheckBoxInWT.hpp
new file mode 100644
index 0000000..5e88c2d
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/CheckBoxInWT.hpp
@@ -0,0 +1,55 @@
+#ifndef slic3r_GUI_CheckBoxInWT_hpp_
+#define slic3r_GUI_CheckBoxInWT_hpp_
+
+#include "../wxExtensions.hpp"
+
+#include
+
+class CheckBoxInWT : public wxBitmapToggleButton
+{
+public:
+ CheckBoxInWT(wxWindow *parent, int id = wxID_ANY);
+
+public:
+ void SetValue(bool value) override;
+
+ void SetHalfChecked(bool value = true);
+
+ void Rescale();
+
+#ifdef __WXOSX__
+ virtual bool Enable(bool enable = true) wxOVERRIDE;
+#endif
+
+protected:
+#ifdef __WXMSW__
+ virtual State GetNormalState() const wxOVERRIDE;
+#endif
+
+#ifdef __WXOSX__
+ virtual wxBitmap DoGetBitmap(State which) const wxOVERRIDE;
+
+ void updateBitmap(wxEvent & evt);
+
+ bool m_disable = false;
+ bool m_hover = false;
+ bool m_focus = false;
+#endif
+
+private:
+ void update();
+
+private:
+ ScalableBitmap m_on;
+ ScalableBitmap m_half;
+ ScalableBitmap m_off;
+ ScalableBitmap m_on_disabled;
+ ScalableBitmap m_half_disabled;
+ ScalableBitmap m_off_disabled;
+ ScalableBitmap m_on_focused;
+ ScalableBitmap m_half_focused;
+ ScalableBitmap m_off_focused;
+ bool m_half_checked = false;
+};
+
+#endif // !slic3r_GUI_CheckBoxInWT_hpp_
diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp
new file mode 100644
index 0000000..5600477
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/ComboBox.cpp
@@ -0,0 +1,309 @@
+#include "ComboBox.hpp"
+#include "Label.hpp"
+
+#include
+
+BEGIN_EVENT_TABLE(ComboBox, TextInput)
+
+EVT_LEFT_DOWN(ComboBox::mouseDown)
+EVT_LEFT_DCLICK(ComboBox::mouseDown)
+//EVT_MOUSEWHEEL(ComboBox::mouseWheelMoved)
+EVT_KEY_DOWN(ComboBox::keyDown)
+
+// catch paint events
+END_EVENT_TABLE()
+
+/*
+ * Called by the system of by wxWidgets when the panel needs
+ * to be redrawn. You can also trigger this call by
+ * calling Refresh()/Update().
+ */
+
+ComboBox::ComboBox(wxWindow * parent,
+ wxWindowID id,
+ const wxString &value,
+ const wxPoint & pos,
+ const wxSize & size,
+ int n,
+ const wxString choices[],
+ long style)
+ : drop(texts, icons)
+{
+ if (style & wxCB_READONLY)
+ style |= wxRIGHT;
+ text_off = style & CB_NO_TEXT;
+ TextInput::Create(parent, "", value, (style & CB_NO_DROP_ICON) ? "" : "drop_down", pos, size,
+ style | wxTE_PROCESS_ENTER);
+ drop.Create(this, style & DD_STYLE_MASK);
+
+ if (style & wxCB_READONLY) {
+ GetTextCtrl()->Hide();
+ TextInput::SetFont(Label::Body_14);
+ TextInput::SetBorderColor(StateColor(std::make_pair(0xDBDBDB, (int) StateColor::Disabled),
+ std::make_pair(0x009688, (int) StateColor::Hovered),
+ std::make_pair(0xDBDBDB, (int) StateColor::Normal)));
+ TextInput::SetBackgroundColor(StateColor(std::make_pair(0xF0F0F1, (int) StateColor::Disabled),
+ std::make_pair(0xEDFAF2, (int) StateColor::Focused),
+ std::make_pair(*wxWHITE, (int) StateColor::Normal)));
+ TextInput::SetLabelColor(StateColor(std::make_pair(0x909090, (int) StateColor::Disabled),
+ std::make_pair(0x262E30, (int) StateColor::Normal)));
+ } else {
+ GetTextCtrl()->Bind(wxEVT_KEY_DOWN, &ComboBox::keyDown, this);
+ }
+ drop.Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) {
+ SetSelection(e.GetInt());
+ e.SetEventObject(this);
+ e.SetId(GetId());
+ GetEventHandler()->ProcessEvent(e);
+ });
+ drop.Bind(EVT_DISMISS, [this](auto &) {
+ drop_down = false;
+ wxCommandEvent e(wxEVT_COMBOBOX_CLOSEUP);
+ GetEventHandler()->ProcessEvent(e);
+ });
+ for (int i = 0; i < n; ++i) Append(choices[i]);
+}
+
+int ComboBox::GetSelection() const { return drop.GetSelection(); }
+
+void ComboBox::SetSelection(int n)
+{
+ drop.SetSelection(n);
+ SetLabel(drop.GetValue());
+ if (drop.selection >= 0)
+ SetIcon(icons[drop.selection]);
+}
+
+void ComboBox::SelectAndNotify(int n) {
+ SetSelection(n);
+ sendComboBoxEvent();
+}
+
+
+void ComboBox::Rescale()
+{
+ TextInput::Rescale();
+ drop.Rescale();
+}
+
+wxString ComboBox::GetValue() const
+{
+ return drop.GetSelection() >= 0 ? drop.GetValue() : GetLabel();
+}
+
+void ComboBox::SetValue(const wxString &value)
+{
+ drop.SetValue(value);
+ SetLabel(value);
+ if (drop.selection >= 0)
+ SetIcon(icons[drop.selection]);
+}
+
+void ComboBox::SetLabel(const wxString &value)
+{
+ if (GetTextCtrl()->IsShown() || text_off)
+ GetTextCtrl()->SetValue(value);
+ else
+ TextInput::SetLabel(value);
+}
+
+wxString ComboBox::GetLabel() const
+{
+ if (GetTextCtrl()->IsShown() || text_off)
+ return GetTextCtrl()->GetValue();
+ else
+ return TextInput::GetLabel();
+}
+
+void ComboBox::SetTextLabel(const wxString& label)
+{
+ TextInput::SetLabel(label);
+}
+
+wxString ComboBox::GetTextLabel() const
+{
+ return TextInput::GetLabel();
+}
+
+bool ComboBox::SetFont(wxFont const& font)
+{
+ if (GetTextCtrl() && GetTextCtrl()->IsShown())
+ return GetTextCtrl()->SetFont(font);
+ else
+ return TextInput::SetFont(font);
+}
+
+int ComboBox::Append(const wxString &item, const wxBitmap &bitmap)
+{
+ return Append(item, bitmap, nullptr);
+}
+
+int ComboBox::Append(const wxString &item,
+ const wxBitmap &bitmap,
+ void * clientData)
+{
+ texts.push_back(item);
+ icons.push_back(bitmap);
+ datas.push_back(clientData);
+ types.push_back(wxClientData_None);
+ drop.Invalidate();
+ return texts.size() - 1;
+}
+
+void ComboBox::DoClear()
+{
+ texts.clear();
+ icons.clear();
+ datas.clear();
+ types.clear();
+ drop.Invalidate(true);
+}
+
+void ComboBox::DoDeleteOneItem(unsigned int pos)
+{
+ if (pos >= texts.size()) return;
+ texts.erase(texts.begin() + pos);
+ icons.erase(icons.begin() + pos);
+ datas.erase(datas.begin() + pos);
+ types.erase(types.begin() + pos);
+ drop.Invalidate(true);
+}
+
+unsigned int ComboBox::GetCount() const { return texts.size(); }
+
+wxString ComboBox::GetString(unsigned int n) const
+{
+ return n < texts.size() ? texts[n] : wxString{};
+}
+
+void ComboBox::SetString(unsigned int n, wxString const &value)
+{
+ if (n >= texts.size()) return;
+ texts[n] = value;
+ drop.Invalidate();
+ if (n == drop.GetSelection()) SetLabel(value);
+}
+
+wxBitmap ComboBox::GetItemBitmap(unsigned int n) { return icons[n]; }
+
+void ComboBox::SetItemBitmap(unsigned int n, wxBitmap const &bitmap)
+{
+ if (n >= texts.size()) return;
+ icons[n] = bitmap;
+ drop.Invalidate();
+}
+
+int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items,
+ unsigned int pos,
+ void ** clientData,
+ wxClientDataType type)
+{
+ if (pos > texts.size()) return -1;
+ for (int i = 0; i < items.GetCount(); ++i) {
+ texts.insert(texts.begin() + pos, items[i]);
+ icons.insert(icons.begin() + pos, wxNullBitmap);
+ datas.insert(datas.begin() + pos, clientData ? clientData[i] : NULL);
+ types.insert(types.begin() + pos, type);
+ ++pos;
+ }
+ drop.Invalidate(true);
+ return pos - 1;
+}
+
+void *ComboBox::DoGetItemClientData(unsigned int n) const { return n < texts.size() ? datas[n] : NULL; }
+
+void ComboBox::DoSetItemClientData(unsigned int n, void *data)
+{
+ if (n < texts.size())
+ datas[n] = data;
+}
+
+void ComboBox::mouseDown(wxMouseEvent &event)
+{
+ SetFocus();
+ if (drop_down) {
+ drop.Hide();
+ } else if (drop.HasDismissLongTime()) {
+ drop.autoPosition();
+ drop_down = true;
+ drop.Popup(&drop);
+ wxCommandEvent e(wxEVT_COMBOBOX_DROPDOWN);
+ GetEventHandler()->ProcessEvent(e);
+ }
+}
+
+void ComboBox::mouseWheelMoved(wxMouseEvent &event)
+{
+ event.Skip();
+ if (drop_down) return;
+ auto delta = event.GetWheelRotation() < 0 ? 1 : -1;
+ unsigned int n = GetSelection() + delta;
+ if (n < GetCount()) {
+ SetSelection((int) n);
+ sendComboBoxEvent();
+ }
+}
+
+void ComboBox::keyDown(wxKeyEvent& event)
+{
+ switch (event.GetKeyCode()) {
+ case WXK_RETURN:
+ case WXK_SPACE:
+ if (drop_down) {
+ drop.DismissAndNotify();
+ } else if (drop.HasDismissLongTime()) {
+ drop.autoPosition();
+ drop_down = true;
+ drop.Popup();
+ wxCommandEvent e(wxEVT_COMBOBOX_DROPDOWN);
+ GetEventHandler()->ProcessEvent(e);
+ }
+ break;
+ case WXK_UP:
+ case WXK_DOWN:
+ case WXK_LEFT:
+ case WXK_RIGHT:
+ if ((event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) && GetSelection() > 0) {
+ SetSelection(GetSelection() - 1);
+ } else if ((event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_RIGHT) && GetSelection() + 1 < texts.size()) {
+ SetSelection(GetSelection() + 1);
+ } else {
+ break;
+ }
+ sendComboBoxEvent();
+ break;
+ case WXK_TAB:
+ HandleAsNavigationKey(event);
+ break;
+ default:
+ event.Skip();
+ break;
+ }
+}
+
+void ComboBox::OnEdit()
+{
+ auto value = GetTextCtrl()->GetValue();
+ SetValue(value);
+}
+
+#ifdef __WIN32__
+
+WXLRESULT ComboBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ if (nMsg == WM_GETDLGCODE) {
+ return DLGC_WANTALLKEYS;
+ }
+ return TextInput::MSWWindowProc(nMsg, wParam, lParam);
+}
+
+#endif
+
+void ComboBox::sendComboBoxEvent()
+{
+ wxCommandEvent event(wxEVT_COMBOBOX, GetId());
+ event.SetEventObject(this);
+ event.SetInt(drop.GetSelection());
+ event.SetString(drop.GetValue());
+ GetEventHandler()->ProcessEvent(event);
+}
diff --git a/src/slic3r/GUI/Widgets/ComboBox.hpp b/src/slic3r/GUI/Widgets/ComboBox.hpp
new file mode 100644
index 0000000..bac3523
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/ComboBox.hpp
@@ -0,0 +1,95 @@
+#ifndef slic3r_GUI_ComboBox_hpp_
+#define slic3r_GUI_ComboBox_hpp_
+
+#include "TextInput.hpp"
+#include "DropDown.hpp"
+
+#define CB_NO_DROP_ICON DD_NO_CHECK_ICON
+#define CB_NO_TEXT DD_NO_TEXT
+
+class ComboBox : public wxWindowWithItems
+{
+ std::vector texts;
+ std::vector icons;
+ std::vector datas;
+ std::vector types;
+
+ DropDown drop;
+ bool drop_down = false;
+ bool text_off = false;
+
+public:
+ ComboBox(wxWindow * parent,
+ wxWindowID id,
+ const wxString &value = wxEmptyString,
+ const wxPoint & pos = wxDefaultPosition,
+ const wxSize & size = wxDefaultSize,
+ int n = 0,
+ const wxString choices[] = NULL,
+ long style = 0);
+
+ DropDown & GetDropDown() { return drop; }
+
+ virtual bool SetFont(wxFont const & font) override;
+
+public:
+ int Append(const wxString &item, const wxBitmap &bitmap = wxNullBitmap);
+
+ int Append(const wxString &item, const wxBitmap &bitmap, void *clientData);
+
+ unsigned int GetCount() const override;
+
+ int GetSelection() const override;
+
+ void SetSelection(int n) override;
+
+ void SelectAndNotify(int n);
+
+ virtual void Rescale() override;
+
+ wxString GetValue() const;
+ void SetValue(const wxString &value);
+
+ void SetLabel(const wxString &label) override;
+ wxString GetLabel() const override;
+
+ void SetTextLabel(const wxString &label);
+ wxString GetTextLabel() const;
+
+ wxString GetString(unsigned int n) const override;
+ void SetString(unsigned int n, wxString const &value) override;
+
+ wxBitmap GetItemBitmap(unsigned int n);
+ void SetItemBitmap(unsigned int n, wxBitmap const &bitmap);
+
+protected:
+ virtual int DoInsertItems(const wxArrayStringsAdapter &items,
+ unsigned int pos,
+ void ** clientData,
+ wxClientDataType type) override;
+ virtual void DoClear() override;
+
+ void DoDeleteOneItem(unsigned int pos) override;
+
+ void *DoGetItemClientData(unsigned int n) const override;
+ void DoSetItemClientData(unsigned int n, void *data) override;
+
+ void OnEdit() override;
+
+ void sendComboBoxEvent();
+
+#ifdef __WIN32__
+ WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override;
+#endif
+
+private:
+
+ // some useful events
+ void mouseDown(wxMouseEvent &event);
+ void mouseWheelMoved(wxMouseEvent &event);
+ void keyDown(wxKeyEvent &event);
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif // !slic3r_GUI_ComboBox_hpp_
diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp
new file mode 100644
index 0000000..0fd1e20
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/DropDown.cpp
@@ -0,0 +1,492 @@
+#include "DropDown.hpp"
+#include "Label.hpp"
+
+#include
+
+#ifdef __WXGTK__
+#include
+#endif
+
+wxDEFINE_EVENT(EVT_DISMISS, wxCommandEvent);
+
+BEGIN_EVENT_TABLE(DropDown, PopupWindow)
+
+EVT_LEFT_DOWN(DropDown::mouseDown)
+EVT_LEFT_UP(DropDown::mouseReleased)
+EVT_MOUSE_CAPTURE_LOST(DropDown::mouseCaptureLost)
+EVT_MOTION(DropDown::mouseMove)
+EVT_MOUSEWHEEL(DropDown::mouseWheelMoved)
+
+// catch paint events
+EVT_PAINT(DropDown::paintEvent)
+
+END_EVENT_TABLE()
+
+/*
+ * Called by the system of by wxWidgets when the panel needs
+ * to be redrawn. You can also trigger this call by
+ * calling Refresh()/Update().
+ */
+
+DropDown::DropDown(std::vector &texts,
+ std::vector &icons)
+ : texts(texts)
+ , icons(icons)
+ , state_handler(this)
+ , border_color(0xDBDBDB)
+ , text_color(0x363636)
+ , selector_border_color(std::make_pair(0x009688, (int) StateColor::Hovered),
+ std::make_pair(*wxWHITE, (int) StateColor::Normal))
+ , selector_background_color(std::make_pair(0xEDFAF2, (int) StateColor::Checked),
+ std::make_pair(*wxWHITE, (int) StateColor::Normal))
+{
+}
+
+DropDown::DropDown(wxWindow * parent,
+ std::vector &texts,
+ std::vector &icons,
+ long style)
+ : DropDown(texts, icons)
+{
+ Create(parent, style);
+}
+
+void DropDown::Create(wxWindow * parent,
+ long style)
+{
+ PopupWindow::Create(parent, wxPU_CONTAINS_CONTROLS);
+ SetBackgroundStyle(wxBG_STYLE_PAINT);
+ SetBackgroundColour(*wxWHITE);
+ state_handler.attach({&border_color, &text_color, &selector_border_color, &selector_background_color});
+ state_handler.update_binds();
+ if ((style & DD_NO_CHECK_ICON) == 0)
+ check_bitmap = ScalableBitmap(this, "checked", 16);
+ text_off = style & DD_NO_TEXT;
+
+ // BBS set default font
+ SetFont(Label::Body_14);
+#ifdef __WXOSX__
+ // PopupWindow releases mouse on idle, which may cause various problems,
+ // such as losting mouse move, and dismissing soon on first LEFT_DOWN event.
+ Bind(wxEVT_IDLE, [] (wxIdleEvent & evt) {});
+#endif
+}
+
+void DropDown::Invalidate(bool clear)
+{
+ if (clear) {
+ selection = hover_item = -1;
+ offset = wxPoint();
+ }
+ assert(selection < (int) texts.size());
+ need_sync = true;
+}
+
+void DropDown::SetSelection(int n)
+{
+ assert(n < (int) texts.size());
+ if (n >= (int) texts.size())
+ n = -1;
+ if (selection == n) return;
+ selection = n;
+ paintNow();
+}
+
+wxString DropDown::GetValue() const
+{
+ return selection >= 0 ? texts[selection] : wxString();
+}
+
+void DropDown::SetValue(const wxString &value)
+{
+ auto i = std::find(texts.begin(), texts.end(), value);
+ selection = i == texts.end() ? -1 : std::distance(texts.begin(), i);
+}
+
+void DropDown::SetCornerRadius(double radius)
+{
+ this->radius = radius;
+ paintNow();
+}
+
+void DropDown::SetBorderColor(StateColor const &color)
+{
+ border_color = color;
+ state_handler.update_binds();
+ paintNow();
+}
+
+void DropDown::SetSelectorBorderColor(StateColor const &color)
+{
+ selector_border_color = color;
+ state_handler.update_binds();
+ paintNow();
+}
+
+void DropDown::SetTextColor(StateColor const &color)
+{
+ text_color = color;
+ state_handler.update_binds();
+ paintNow();
+}
+
+void DropDown::SetSelectorBackgroundColor(StateColor const &color)
+{
+ selector_background_color = color;
+ state_handler.update_binds();
+ paintNow();
+}
+
+void DropDown::SetUseContentWidth(bool use)
+{
+ if (use_content_width == use)
+ return;
+ use_content_width = use;
+ need_sync = true;
+ messureSize();
+}
+
+void DropDown::SetAlignIcon(bool align) { align_icon = align; }
+
+void DropDown::Rescale()
+{
+ need_sync = true;
+}
+
+bool DropDown::HasDismissLongTime()
+{
+ auto now = boost::posix_time::microsec_clock::universal_time();
+ return !IsShown() &&
+ (now - dismissTime).total_milliseconds() >= 20;
+}
+
+void DropDown::paintEvent(wxPaintEvent& evt)
+{
+ // depending on your system you may need to look at double-buffered dcs
+ wxBufferedPaintDC dc(this);
+ render(dc);
+}
+
+/*
+ * Alternatively, you can use a clientDC to paint on the panel
+ * at any time. Using this generally does not free you from
+ * catching paint events, since it is possible that e.g. the window
+ * manager throws away your drawing when the window comes to the
+ * background, and expects you will redraw it when the window comes
+ * back (by sending a paint event).
+ */
+void DropDown::paintNow()
+{
+ // depending on your system you may need to look at double-buffered dcs
+ //wxClientDC dc(this);
+ //render(dc);
+ Refresh();
+}
+
+static wxSize GetBmpSize(wxBitmap & bmp)
+{
+#ifdef __APPLE__
+ return bmp.GetScaledSize();
+#else
+ return bmp.GetSize();
+#endif
+}
+
+/*
+ * Here we do the actual rendering. I put it in a separate
+ * method so that it can work no matter what type of DC
+ * (e.g. wxPaintDC or wxClientDC) is used.
+ */
+void DropDown::render(wxDC &dc)
+{
+ if (texts.size() == 0) return;
+ int states = state_handler.states();
+ dc.SetPen(wxPen(border_color.colorForStates(states)));
+ dc.SetBrush(wxBrush(StateColor::darkModeColorFor(GetBackgroundColour())));
+ // if (GetWindowStyle() & wxBORDER_NONE)
+ // dc.SetPen(wxNullPen);
+
+ // draw background
+ wxSize size = GetSize();
+ if (radius == 0)
+ dc.DrawRectangle(0, 0, size.x, size.y);
+ else
+ dc.DrawRoundedRectangle(0, 0, size.x, size.y, radius);
+
+ // draw hover rectangle
+ wxRect rcContent = {{0, offset.y}, rowSize};
+ if (hover_item >= 0 && (states & StateColor::Hovered)) {
+ rcContent.y += rowSize.y * hover_item;
+ if (rcContent.GetBottom() > 0 && rcContent.y < size.y) {
+ if (selection == hover_item)
+ dc.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked)));
+ dc.SetPen(wxPen(selector_border_color.colorForStates(states)));
+ rcContent.Deflate(4, 1);
+ dc.DrawRectangle(rcContent);
+ rcContent.Inflate(4, 1);
+ }
+ rcContent.y = offset.y;
+ }
+ // draw checked rectangle
+ if (selection >= 0 && (selection != hover_item || (states & StateColor::Hovered) == 0)) {
+ rcContent.y += rowSize.y * selection;
+ if (rcContent.GetBottom() > 0 && rcContent.y < size.y) {
+ dc.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked)));
+ dc.SetPen(wxPen(selector_background_color.colorForStates(states)));
+ rcContent.Deflate(4, 1);
+ dc.DrawRectangle(rcContent);
+ rcContent.Inflate(4, 1);
+ }
+ rcContent.y = offset.y;
+ }
+ dc.SetBrush(*wxTRANSPARENT_BRUSH);
+ {
+ wxSize offset = (rowSize - textSize) / 2;
+ rcContent.Deflate(0, offset.y);
+ }
+
+ // draw position bar
+ if (rowSize.y * texts.size() > size.y) {
+ int height = rowSize.y * texts.size();
+ wxRect rect = {size.x - 6, -offset.y * size.y / height, 4,
+ size.y * size.y / height};
+ dc.SetPen(wxPen(border_color.defaultColor()));
+ dc.SetBrush(wxBrush(*wxLIGHT_GREY));
+ dc.DrawRoundedRectangle(rect, 2);
+ rcContent.width -= 6;
+ }
+
+ // draw check icon
+ rcContent.x += 5;
+ rcContent.width -= 5;
+ if (check_bitmap.get_bitmap().IsOk()) {
+ auto szBmp = check_bitmap.GetSize();
+ if (selection >= 0) {
+ wxPoint pt = rcContent.GetLeftTop();
+ pt.y += (rcContent.height - szBmp.y) / 2;
+ pt.y += rowSize.y * selection;
+ if (pt.y + szBmp.y > 0 && pt.y < size.y)
+ dc.DrawBitmap(check_bitmap.get_bitmap(), pt);
+ }
+ rcContent.x += szBmp.x + 5;
+ rcContent.width -= szBmp.x + 5;
+ }
+ // draw texts & icons
+ dc.SetTextForeground(text_color.colorForStates(states));
+ for (int i = 0; i < texts.size(); ++i) {
+ if (rcContent.GetBottom() < 0) {
+ rcContent.y += rowSize.y;
+ continue;
+ }
+ if (rcContent.y > size.y) break;
+ wxPoint pt = rcContent.GetLeftTop();
+ auto & icon = icons[i];
+ auto size2 = GetBmpSize(icon);
+ if (iconSize.x > 0) {
+ if (icon.IsOk()) {
+ pt.y += (rcContent.height - size2.y) / 2;
+ dc.DrawBitmap(icon, pt);
+ }
+ pt.x += iconSize.x + 5;
+ pt.y = rcContent.y;
+ } else if (icon.IsOk()) {
+ pt.y += (rcContent.height - size2.y) / 2;
+ dc.DrawBitmap(icon, pt);
+ pt.x += size2.x + 5;
+ pt.y = rcContent.y;
+ }
+ auto text = texts[i];
+ if (!text_off && !text.IsEmpty()) {
+ wxSize tSize = dc.GetMultiLineTextExtent(text);
+ if (pt.x + tSize.x > rcContent.GetRight()) {
+ text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END,
+ rcContent.GetRight() - pt.x);
+ }
+ pt.y += (rcContent.height - textSize.y) / 2;
+ dc.SetFont(GetFont());
+ dc.DrawText(text, pt);
+ }
+ rcContent.y += rowSize.y;
+ }
+}
+
+void DropDown::messureSize()
+{
+ if (!need_sync) return;
+ textSize = wxSize();
+ iconSize = wxSize();
+ wxClientDC dc(GetParent() ? GetParent() : this);
+ for (size_t i = 0; i < texts.size(); ++i) {
+ wxSize size1 = text_off ? wxSize() : dc.GetMultiLineTextExtent(texts[i]);
+ if (icons[i].IsOk()) {
+ wxSize size2 = GetBmpSize(icons[i]);
+ if (size2.x > iconSize.x) iconSize = size2;
+ if (!align_icon) {
+ size1.x += size2.x + (text_off ? 0 : 5);
+ }
+ }
+ if (size1.x > textSize.x) textSize = size1;
+ }
+ if (!align_icon) iconSize.x = 0;
+ wxSize szContent = textSize;
+ szContent.x += 10;
+ if (check_bitmap.get_bitmap().IsOk()) {
+ auto szBmp = check_bitmap.GetSize();
+ szContent.x += szBmp.x + 5;
+ }
+ if (iconSize.x > 0) szContent.x += iconSize.x + (text_off ? 0 : 5);
+ if (iconSize.y > szContent.y) szContent.y = iconSize.y;
+ szContent.y += 10;
+ if (texts.size() > 15) szContent.x += 6;
+ if (GetParent()) {
+ auto x = GetParent()->GetSize().x;
+ if (!use_content_width || x > szContent.x)
+ szContent.x = x;
+ }
+ rowSize = szContent;
+ szContent.y *= std::min((size_t)15, texts.size());
+ szContent.y += texts.size() > 15 ? rowSize.y / 2 : 0;
+ wxWindow::SetSize(szContent);
+#ifdef __WXGTK__
+ // Gtk has a wrapper window for popup widget
+ gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
+#endif
+ need_sync = false;
+}
+
+void DropDown::autoPosition()
+{
+ messureSize();
+ wxPoint pos = GetParent()->ClientToScreen(wxPoint(0, -6));
+ wxPoint old = GetPosition();
+ wxSize size = GetSize();
+ Position(pos, {0, GetParent()->GetSize().y + 12});
+ if (old != GetPosition()) {
+ size = rowSize;
+ size.y *= std::min((size_t)15, texts.size());
+ size.y += texts.size() > 15 ? rowSize.y / 2 : 0;
+ if (size != GetSize()) {
+ wxWindow::SetSize(size);
+ offset = wxPoint();
+ Position(pos, {0, GetParent()->GetSize().y + 12});
+ }
+ }
+ if (GetPosition().y > pos.y) {
+ // may exceed
+ auto drect = wxDisplay(GetParent()).GetGeometry();
+ if (GetPosition().y + size.y + 10 > drect.GetBottom()) {
+ if (use_content_width && texts.size() <= 15) size.x += 6;
+ size.y = drect.GetBottom() - GetPosition().y - 10;
+ wxWindow::SetSize(size);
+ if (selection >= 0) {
+ if (offset.y + rowSize.y * (selection + 1) > size.y)
+ offset.y = size.y - rowSize.y * (selection + 1);
+ else if (offset.y + rowSize.y * selection < 0)
+ offset.y = -rowSize.y * selection;
+ }
+ }
+ }
+}
+
+void DropDown::mouseDown(wxMouseEvent& event)
+{
+ // Receivce unexcepted LEFT_DOWN on Mac after OnDismiss
+ if (!IsShown())
+ return;
+ // force calc hover item again
+ mouseMove(event);
+ pressedDown = true;
+ CaptureMouse();
+ dragStart = event.GetPosition();
+}
+
+void DropDown::mouseReleased(wxMouseEvent& event)
+{
+ if (pressedDown) {
+ dragStart = wxPoint();
+ pressedDown = false;
+ if (HasCapture())
+ ReleaseMouse();
+ if (hover_item >= 0) { // not moved
+ sendDropDownEvent();
+ DismissAndNotify();
+ }
+ }
+}
+
+void DropDown::mouseCaptureLost(wxMouseCaptureLostEvent &event)
+{
+ wxMouseEvent evt;
+ mouseReleased(evt);
+}
+
+void DropDown::mouseMove(wxMouseEvent &event)
+{
+ wxPoint pt = event.GetPosition();
+ if (pressedDown) {
+ wxPoint pt2 = offset + pt - dragStart;
+ wxSize size = GetSize();
+ dragStart = pt;
+ if (pt2.y > 0)
+ pt2.y = 0;
+ else if (pt2.y + rowSize.y * int(texts.size()) < size.y)
+ pt2.y = size.y - rowSize.y * int(texts.size());
+ if (pt2.y != offset.y) {
+ offset = pt2;
+ hover_item = -1; // moved
+ } else {
+ return;
+ }
+ }
+ if (!pressedDown || hover_item >= 0) {
+ int hover = (pt.y - offset.y) / rowSize.y;
+ if (hover >= (int) texts.size()) hover = -1;
+ if (hover == hover_item) return;
+ hover_item = hover;
+ if (hover >= 0)
+ SetToolTip(texts[hover]);
+ }
+ paintNow();
+}
+
+void DropDown::mouseWheelMoved(wxMouseEvent &event)
+{
+ auto delta = event.GetWheelRotation();
+ wxSize size = GetSize();
+ wxPoint pt2 = offset + wxPoint{0, delta};
+ if (pt2.y > 0)
+ pt2.y = 0;
+ else if (pt2.y + rowSize.y * int(texts.size()) < size.y)
+ pt2.y = size.y - rowSize.y * int(texts.size());
+ if (pt2.y != offset.y) {
+ offset = pt2;
+ } else {
+ return;
+ }
+ int hover = (event.GetPosition().y - offset.y) / rowSize.y;
+ if (hover >= (int) texts.size()) hover = -1;
+ if (hover != hover_item) {
+ hover_item = hover;
+ if (hover >= 0) SetToolTip(texts[hover]);
+ }
+ paintNow();
+}
+
+// currently unused events
+void DropDown::sendDropDownEvent()
+{
+ selection = hover_item;
+ wxCommandEvent event(wxEVT_COMBOBOX, GetId());
+ event.SetEventObject(this);
+ event.SetInt(selection);
+ event.SetString(GetValue());
+ GetEventHandler()->ProcessEvent(event);
+}
+
+void DropDown::OnDismiss()
+{
+ dismissTime = boost::posix_time::microsec_clock::universal_time();
+ hover_item = -1;
+ wxCommandEvent e(EVT_DISMISS);
+ GetEventHandler()->ProcessEvent(e);
+}
diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp
new file mode 100644
index 0000000..e2a6cb4
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/DropDown.hpp
@@ -0,0 +1,112 @@
+#ifndef slic3r_GUI_DropDown_hpp_
+#define slic3r_GUI_DropDown_hpp_
+
+#include
+#include "../wxExtensions.hpp"
+#include "StateHandler.hpp"
+#include "PopupWindow.hpp"
+
+#define DD_NO_CHECK_ICON 0x0001
+#define DD_NO_TEXT 0x0002
+#define DD_STYLE_MASK 0x0003
+
+wxDECLARE_EVENT(EVT_DISMISS, wxCommandEvent);
+
+class DropDown : public PopupWindow
+{
+ std::vector & texts;
+ std::vector & icons;
+ bool need_sync = false;
+ int selection = -1;
+ int hover_item = -1;
+
+ double radius = 0;
+ bool use_content_width = false;
+ bool align_icon = false;
+ bool text_off = false;
+
+ wxSize textSize;
+ wxSize iconSize;
+ wxSize rowSize;
+
+ StateHandler state_handler;
+ StateColor text_color;
+ StateColor border_color;
+ StateColor selector_border_color;
+ StateColor selector_background_color;
+ ScalableBitmap check_bitmap;
+
+ bool pressedDown = false;
+ boost::posix_time::ptime dismissTime;
+ wxPoint offset; // x not used
+ wxPoint dragStart;
+
+public:
+ DropDown(std::vector &texts,
+ std::vector &icons);
+
+ DropDown(wxWindow * parent,
+ std::vector &texts,
+ std::vector &icons,
+ long style = 0);
+
+ void Create(wxWindow * parent,
+ long style = 0);
+
+public:
+ void Invalidate(bool clear = false);
+
+ int GetSelection() const { return selection; }
+
+ void SetSelection(int n);
+
+ wxString GetValue() const;
+ void SetValue(const wxString &value);
+
+public:
+ void SetCornerRadius(double radius);
+
+ void SetBorderColor(StateColor const & color);
+
+ void SetSelectorBorderColor(StateColor const & color);
+
+ void SetTextColor(StateColor const &color);
+
+ void SetSelectorBackgroundColor(StateColor const &color);
+
+ void SetUseContentWidth(bool use);
+
+ void SetAlignIcon(bool align);
+
+public:
+ void Rescale();
+
+ bool HasDismissLongTime();
+
+protected:
+ void OnDismiss() override;
+
+private:
+ void paintEvent(wxPaintEvent& evt);
+ void paintNow();
+
+ void render(wxDC& dc);
+
+ friend class ComboBox;
+ void messureSize();
+ void autoPosition();
+
+ // some useful events
+ void mouseDown(wxMouseEvent& event);
+ void mouseReleased(wxMouseEvent &event);
+ void mouseCaptureLost(wxMouseCaptureLostEvent &event);
+ void mouseMove(wxMouseEvent &event);
+ void mouseWheelMoved(wxMouseEvent &event);
+
+ void sendDropDownEvent();
+
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif // !slic3r_GUI_DropDown_hpp_
diff --git a/src/slic3r/GUI/Widgets/PopupWindow.cpp b/src/slic3r/GUI/Widgets/PopupWindow.cpp
new file mode 100644
index 0000000..135802c
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/PopupWindow.cpp
@@ -0,0 +1,37 @@
+#include "PopupWindow.hpp"
+
+static wxWindow *GetTopParent(wxWindow *pWindow)
+{
+ wxWindow *pWin = pWindow;
+ while (pWin->GetParent()) {
+ pWin = pWin->GetParent();
+ if (auto top = dynamic_cast(pWin))
+ return top;
+ }
+ return pWin;
+}
+
+bool PopupWindow::Create(wxWindow *parent, int style)
+{
+ if (!wxPopupTransientWindow::Create(parent, style))
+ return false;
+#ifdef __WXGTK__
+ GetTopParent(parent)->Bind(wxEVT_ACTIVATE, &PopupWindow::topWindowActiavate, this);
+#endif
+ return true;
+}
+
+PopupWindow::~PopupWindow()
+{
+#ifdef __WXGTK__
+ GetTopParent(this)->Unbind(wxEVT_ACTIVATE, &PopupWindow::topWindowActiavate, this);
+#endif
+}
+
+#ifdef __WXGTK__
+void PopupWindow::topWindowActiavate(wxActivateEvent &event)
+{
+ event.Skip();
+ if (!event.GetActive() && IsShown()) DismissAndNotify();
+}
+#endif
diff --git a/src/slic3r/GUI/Widgets/PopupWindow.hpp b/src/slic3r/GUI/Widgets/PopupWindow.hpp
new file mode 100644
index 0000000..88993de
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/PopupWindow.hpp
@@ -0,0 +1,24 @@
+#ifndef slic3r_GUI_PopupWindow_hpp_
+#define slic3r_GUI_PopupWindow_hpp_
+
+#include
+
+class PopupWindow : public wxPopupTransientWindow
+{
+public:
+ PopupWindow() {}
+
+ ~PopupWindow();
+
+ PopupWindow(wxWindow *parent, int style = wxBORDER_NONE)
+ { Create(parent, style); }
+
+ bool Create(wxWindow *parent, int flags = wxBORDER_NONE);
+
+private:
+#ifdef __WXGTK__
+ void topWindowActiavate(wxActivateEvent &event);
+#endif
+};
+
+#endif // !slic3r_GUI_PopupWindow_hpp_
diff --git a/src/slic3r/GUI/Widgets/RadioBox.cpp b/src/slic3r/GUI/Widgets/RadioBox.cpp
new file mode 100644
index 0000000..17712a8
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/RadioBox.cpp
@@ -0,0 +1,42 @@
+#include "RadioBox.hpp"
+
+#include "../wxExtensions.hpp"
+
+namespace Slic3r {
+namespace GUI {
+RadioBox::RadioBox(wxWindow *parent)
+ : wxBitmapToggleButton(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE), m_on(this, "radio_on", 18), m_off(this, "radio_off", 18)
+{
+ // SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
+ if (parent) SetBackgroundColour(parent->GetBackgroundColour());
+ // Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { update(); e.Skip(); });
+ SetSize(m_on.GetSize());
+ SetMinSize(m_on.GetSize());
+ update();
+}
+
+void RadioBox::SetValue(bool value)
+{
+ wxBitmapToggleButton::SetValue(value);
+ update();
+}
+
+bool RadioBox::GetValue()
+{
+ return wxBitmapToggleButton::GetValue();
+}
+
+
+void RadioBox::Rescale()
+{
+ m_on.msw_rescale();
+ m_off.msw_rescale();
+ SetSize(m_on.GetSize());
+ update();
+}
+
+void RadioBox::update() { SetBitmap((GetValue() ? m_on : m_off).bmp()); }
+
+}
+}
+
diff --git a/src/slic3r/GUI/Widgets/RadioBox.hpp b/src/slic3r/GUI/Widgets/RadioBox.hpp
new file mode 100644
index 0000000..8f30994
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/RadioBox.hpp
@@ -0,0 +1,39 @@
+#ifndef slic3r_GUI_RADIOBOX_hpp_
+#define slic3r_GUI_RADIOBOX_hpp_
+
+#include "../wxExtensions.hpp"
+
+#include
+
+namespace Slic3r {
+namespace GUI {
+
+class RadioBox : public wxBitmapToggleButton
+{
+public:
+ RadioBox(wxWindow *parent);
+
+public:
+ void SetValue(bool value) override;
+ bool GetValue();
+ void Rescale();
+ bool Disable() {
+ return wxBitmapToggleButton::Disable();
+ }
+ bool Enable() {
+ return wxBitmapToggleButton::Enable();
+ }
+
+private:
+ void update();
+
+private:
+ ScalableBitmap m_on;
+ ScalableBitmap m_off;
+};
+
+}}
+
+
+
+#endif // !slic3r_GUI_CheckBox_hpp_
diff --git a/src/slic3r/GUI/Widgets/RoundedRectangle.cpp b/src/slic3r/GUI/Widgets/RoundedRectangle.cpp
new file mode 100644
index 0000000..a3c7c13
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/RoundedRectangle.cpp
@@ -0,0 +1,35 @@
+#include "RoundedRectangle.hpp"
+#include "../wxExtensions.hpp"
+#include
+
+BEGIN_EVENT_TABLE(RoundedRectangle, wxPanel)
+EVT_PAINT(RoundedRectangle::OnPaint)
+END_EVENT_TABLE()
+
+ RoundedRectangle::RoundedRectangle(wxWindow *parent, wxColour col, wxPoint pos, wxSize size, double radius, int type)
+ : wxWindow(parent, wxID_ANY, pos, size, wxBORDER_NONE)
+{
+ SetBackgroundColour(wxColour(255,255,255));
+ m_type = type;
+ m_color = col;
+ m_radius = radius;
+}
+
+void RoundedRectangle::OnPaint(wxPaintEvent &evt)
+{
+ //draw RoundedRectangle
+ if (m_type == 0) {
+ wxPaintDC dc(this);
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ dc.SetBrush(wxBrush(m_color));
+ dc.DrawRoundedRectangle(0, 0, GetSize().GetWidth(), GetSize().GetHeight(), m_radius);
+ }
+
+ //draw RoundedRectangle only board
+ if (m_type == 1) {
+ wxPaintDC dc(this);
+ dc.SetPen(m_color);
+ dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH));
+ dc.DrawRoundedRectangle(0, 0, GetSize().GetWidth(), GetSize().GetHeight(), m_radius);
+ }
+}
\ No newline at end of file
diff --git a/src/slic3r/GUI/Widgets/RoundedRectangle.hpp b/src/slic3r/GUI/Widgets/RoundedRectangle.hpp
new file mode 100644
index 0000000..9709010
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/RoundedRectangle.hpp
@@ -0,0 +1,21 @@
+#ifndef slic3r_GUI_ROUNDEDRECTANGLE_hpp_
+#define slic3r_GUI_ROUNDEDRECTANGLE_hpp_
+
+#include "../wxExtensions.hpp"
+
+class RoundedRectangle : public wxWindow
+{
+public:
+ RoundedRectangle(wxWindow *parent, wxColour col, wxPoint pos, wxSize size, double radius, int type = 0);
+ ~RoundedRectangle(){};
+
+private:
+ double m_radius;
+ int m_type;
+ wxColour m_color;
+
+public:
+ void OnPaint(wxPaintEvent &evt);
+ DECLARE_EVENT_TABLE()
+};
+#endif // !slic3r_GUI_RoundedRectangle_hpp_
diff --git a/src/slic3r/GUI/Widgets/TextCtrl.h b/src/slic3r/GUI/Widgets/TextCtrl.h
new file mode 100644
index 0000000..3f3ab2b
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/TextCtrl.h
@@ -0,0 +1,10 @@
+#ifdef __WXMSW__
+class TextCtrl : public wxTextCtrl
+{
+public:
+ using wxTextCtrl::wxTextCtrl;
+ WXHBRUSH DoMSWControlColor(WXHDC pDC, wxColour colBg, WXHWND hWnd) { return wxTextCtrl::DoMSWControlColor(pDC, wxColour(), hWnd); }
+};
+#else
+typedef wxTextCtrl TextCtrl;
+#endif
diff --git a/src/slic3r/GUI/Widgets/TextInput.cpp b/src/slic3r/GUI/Widgets/TextInput.cpp
new file mode 100644
index 0000000..08bfb4f
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/TextInput.cpp
@@ -0,0 +1,240 @@
+#include "TextInput.hpp"
+#include "Label.hpp"
+#include "TextCtrl.h"
+#include "slic3r/GUI/Widgets/Label.hpp"
+
+#include
+
+BEGIN_EVENT_TABLE(TextInput, wxPanel)
+
+EVT_PAINT(TextInput::paintEvent)
+
+END_EVENT_TABLE()
+
+/*
+ * Called by the system of by wxWidgets when the panel needs
+ * to be redrawn. You can also trigger this call by
+ * calling Refresh()/Update().
+ */
+
+TextInput::TextInput()
+ : label_color(std::make_pair(0x909090, (int) StateColor::Disabled),
+ std::make_pair(0x6B6B6B, (int) StateColor::Normal))
+ , text_color(std::make_pair(0x909090, (int) StateColor::Disabled),
+ std::make_pair(0x262E30, (int) StateColor::Normal))
+{
+ radius = 0;
+ border_width = 1;
+ border_color = StateColor(std::make_pair(0xDBDBDB, (int) StateColor::Disabled), std::make_pair(0x009688, (int) StateColor::Hovered),
+ std::make_pair(0xDBDBDB, (int) StateColor::Normal));
+ background_color = StateColor(std::make_pair(0xF0F0F1, (int) StateColor::Disabled), std::make_pair(*wxWHITE, (int) StateColor::Normal));
+ SetFont(Label::Body_12);
+}
+
+TextInput::TextInput(wxWindow * parent,
+ wxString text,
+ wxString label,
+ wxString icon,
+ const wxPoint &pos,
+ const wxSize & size,
+ long style)
+ : TextInput()
+{
+ Create(parent, text, label, icon, pos, size, style);
+}
+
+void TextInput::Create(wxWindow * parent,
+ wxString text,
+ wxString label,
+ wxString icon,
+ const wxPoint &pos,
+ const wxSize & size,
+ long style)
+{
+ text_ctrl = nullptr;
+ StaticBox::Create(parent, wxID_ANY, pos, size, style);
+ wxWindow::SetLabel(label);
+ style &= ~wxRIGHT;
+ state_handler.attach({&label_color, & text_color});
+ state_handler.update_binds();
+ text_ctrl = new TextCtrl(this, wxID_ANY, text, {4, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER);
+ text_ctrl->SetFont(Label::Body_14);
+ text_ctrl->SetInitialSize(text_ctrl->GetBestSize());
+ text_ctrl->SetBackgroundColour(background_color.colorForStates(state_handler.states()));
+ text_ctrl->SetForegroundColour(text_color.colorForStates(state_handler.states()));
+ state_handler.attach_child(text_ctrl);
+ text_ctrl->Bind(wxEVT_KILL_FOCUS, [this](auto &e) {
+ OnEdit();
+ e.SetId(GetId());
+ ProcessEventLocally(e);
+ e.Skip();
+ });
+ text_ctrl->Bind(wxEVT_TEXT_ENTER, [this](auto &e) {
+ OnEdit();
+ e.SetId(GetId());
+ ProcessEventLocally(e);
+ });
+ text_ctrl->Bind(wxEVT_RIGHT_DOWN, [this](auto &e) {}); // disable context menu
+ if (!icon.IsEmpty()) {
+ this->icon = ScalableBitmap(this, icon.ToStdString(), 16);
+ }
+ messureSize();
+}
+
+void TextInput::SetCornerRadius(double radius)
+{
+ this->radius = radius;
+ Refresh();
+}
+
+void TextInput::SetLabel(const wxString& label)
+{
+ wxWindow::SetLabel(label);
+ messureSize();
+ Refresh();
+}
+
+void TextInput::SetIcon(const wxBitmap &icon)
+{
+ this->icon.get_bitmap() = icon;
+ Rescale();
+}
+
+void TextInput::SetLabelColor(StateColor const &color)
+{
+ label_color = color;
+ state_handler.update_binds();
+}
+
+void TextInput::SetTextColor(StateColor const& color)
+{
+ text_color= color;
+ state_handler.update_binds();
+}
+
+void TextInput::Rescale()
+{
+ if (!this->icon.name().empty())
+ this->icon.msw_rescale();
+ messureSize();
+ Refresh();
+}
+
+bool TextInput::Enable(bool enable)
+{
+ bool result = text_ctrl->Enable(enable) && wxWindow::Enable(enable);
+ if (result) {
+ wxCommandEvent e(EVT_ENABLE_CHANGED);
+ e.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(e);
+ text_ctrl->SetBackgroundColour(background_color.colorForStates(state_handler.states()));
+ text_ctrl->SetForegroundColour(text_color.colorForStates(state_handler.states()));
+ }
+ return result;
+}
+
+void TextInput::SetMinSize(const wxSize& size)
+{
+ wxSize size2 = size;
+ if (size2.y < 0) {
+#ifdef __WXMAC__
+ if (GetPeer()) // peer is not ready in Create on mac
+#endif
+ size2.y = GetSize().y;
+ }
+ wxWindow::SetMinSize(size2);
+}
+
+void TextInput::DoSetSize(int x, int y, int width, int height, int sizeFlags)
+{
+ wxWindow::DoSetSize(x, y, width, height, sizeFlags);
+ if (sizeFlags & wxSIZE_USE_EXISTING) return;
+ wxSize size = GetSize();
+ wxPoint textPos = {5, 0};
+ if (this->icon.get_bitmap().IsOk()) {
+ wxSize szIcon = this->icon.GetSize();
+ textPos.x += szIcon.x;
+ }
+ bool align_right = GetWindowStyle() & wxRIGHT;
+ if (align_right)
+ textPos.x += labelSize.x;
+ if (text_ctrl) {
+ wxSize textSize = text_ctrl->GetSize();
+ textSize.x = size.x - textPos.x - labelSize.x - 10;
+ text_ctrl->SetSize(textSize);
+ text_ctrl->SetPosition({textPos.x, (size.y - textSize.y) / 2});
+ }
+}
+
+void TextInput::DoSetToolTipText(wxString const &tip)
+{
+ wxWindow::DoSetToolTipText(tip);
+ text_ctrl->SetToolTip(tip);
+}
+
+void TextInput::paintEvent(wxPaintEvent &evt)
+{
+ // depending on your system you may need to look at double-buffered dcs
+ wxPaintDC dc(this);
+ render(dc);
+}
+
+/*
+ * Here we do the actual rendering. I put it in a separate
+ * method so that it can work no matter what type of DC
+ * (e.g. wxPaintDC or wxClientDC) is used.
+ */
+void TextInput::render(wxDC& dc)
+{
+ StaticBox::render(dc);
+ int states = state_handler.states();
+ wxSize size = GetSize();
+ bool align_right = GetWindowStyle() & wxRIGHT;
+ // start draw
+ wxPoint pt = {5, 0};
+ if (icon.get_bitmap().IsOk()) {
+ wxSize szIcon = icon.GetSize();
+ pt.y = (size.y - szIcon.y) / 2;
+ dc.DrawBitmap(icon.get_bitmap(), pt);
+ pt.x += szIcon.x + 0;
+ }
+ auto text = wxWindow::GetLabel();
+ if (!text.IsEmpty()) {
+ wxSize textSize = text_ctrl->GetSize();
+ if (align_right) {
+ if (pt.x + labelSize.x > size.x)
+ text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x);
+ pt.y = (size.y - labelSize.y) / 2;
+ } else {
+ pt.x += textSize.x;
+ pt.y = (size.y + textSize.y) / 2 - labelSize.y;
+ }
+ dc.SetTextForeground(label_color.colorForStates(states));
+ if(align_right)
+ dc.SetFont(GetFont());
+ else
+ dc.SetFont(Label::Body_12);
+ dc.DrawText(text, pt);
+ }
+}
+
+void TextInput::messureSize()
+{
+ wxSize size = GetSize();
+ wxClientDC dc(this);
+ bool align_right = GetWindowStyle() & wxRIGHT;
+ if (align_right)
+ dc.SetFont(GetFont());
+ else
+ dc.SetFont(Label::Body_12);
+ labelSize = dc.GetTextExtent(wxWindow::GetLabel());
+ wxSize textSize = text_ctrl->GetSize();
+ int h = textSize.y + 8;
+ if (size.y < h) {
+ size.y = h;
+ }
+ wxSize minSize = size;
+ minSize.x = GetMinWidth();
+ SetMinSize(minSize);
+ SetSize(size);
+}
diff --git a/src/slic3r/GUI/Widgets/TextInput.hpp b/src/slic3r/GUI/Widgets/TextInput.hpp
new file mode 100644
index 0000000..1013e7c
--- /dev/null
+++ b/src/slic3r/GUI/Widgets/TextInput.hpp
@@ -0,0 +1,77 @@
+#ifndef slic3r_GUI_TextInput_hpp_
+#define slic3r_GUI_TextInput_hpp_
+
+#include
+#include "StaticBox.hpp"
+
+class TextInput : public wxNavigationEnabled
+{
+
+ wxSize labelSize;
+ ScalableBitmap icon;
+ StateColor label_color;
+ StateColor text_color;
+ wxTextCtrl * text_ctrl;
+
+ static const int TextInputWidth = 200;
+ static const int TextInputHeight = 50;
+
+public:
+ TextInput();
+
+ TextInput(wxWindow * parent,
+ wxString text,
+ wxString label = "",
+ wxString icon = "",
+ const wxPoint &pos = wxDefaultPosition,
+ const wxSize & size = wxDefaultSize,
+ long style = 0);
+
+public:
+ void Create(wxWindow * parent,
+ wxString text,
+ wxString label = "",
+ wxString icon = "",
+ const wxPoint &pos = wxDefaultPosition,
+ const wxSize & size = wxDefaultSize,
+ long style = 0);
+
+ void SetCornerRadius(double radius);
+
+ void SetLabel(const wxString& label);
+
+ void SetIcon(const wxBitmap &icon);
+
+ void SetLabelColor(StateColor const &color);
+
+ void SetTextColor(StateColor const &color);
+
+ virtual void Rescale();
+
+ virtual bool Enable(bool enable = true) override;
+
+ virtual void SetMinSize(const wxSize& size) override;
+
+ wxTextCtrl *GetTextCtrl() { return text_ctrl; }
+
+ wxTextCtrl const *GetTextCtrl() const { return text_ctrl; }
+
+protected:
+ virtual void OnEdit() {}
+
+ virtual void DoSetSize(
+ int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO);
+
+ void DoSetToolTipText(wxString const &tip) override;
+
+private:
+ void paintEvent(wxPaintEvent& evt);
+
+ void render(wxDC& dc);
+
+ void messureSize();
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif // !slic3r_GUI_TextInput_hpp_
diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp
new file mode 100644
index 0000000..bf46f8b
--- /dev/null
+++ b/src/slic3r/GUI/calib_dlg.cpp
@@ -0,0 +1,242 @@
+#include "calib_dlg.hpp"
+#include "GUI_App.hpp"
+#include "MsgDialog.hpp"
+#include "I18N.hpp"
+#include
+#include "MainFrame.hpp"
+#include
+namespace Slic3r {
+namespace GUI {
+
+wxBoxSizer *create_item_checkbox(wxString title, wxWindow *parent, bool *value, CheckBoxInWT *&checkbox)
+{
+ wxBoxSizer* m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL);
+
+ m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 5);
+
+ checkbox = new ::CheckBoxInWT(parent);
+ m_sizer_checkbox->Add(checkbox, 0, wxALIGN_CENTER, 0);
+ m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 8);
+
+ auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxSize(-1, -1), 0);
+ checkbox_title->SetForegroundColour(wxColour(144, 144, 144));
+ checkbox_title->SetFont(::Label::Body_13);
+ checkbox_title->Wrap(-1);
+ m_sizer_checkbox->Add(checkbox_title, 0, wxALIGN_CENTER | wxALL, 3);
+
+ checkbox->SetValue(true);
+
+ checkbox->Bind(wxEVT_TOGGLEBUTTON, [parent, checkbox, value](wxCommandEvent& e) {
+ (*value) = (*value) ? false : true;
+ e.Skip();
+ });
+
+ return m_sizer_checkbox;
+}
+
+PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
+ : DPIDialog(parent, id, _L("PA Calibration"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater)
+{
+ wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
+ SetSizer(v_sizer);
+ wxBoxSizer* choice_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ wxString m_rbExtruderTypeChoices[] = { _L("DDE"), _L("Bowden") };
+ int m_rbExtruderTypeNChoices = sizeof(m_rbExtruderTypeChoices) / sizeof(wxString);
+ m_rbExtruderType = new wxRadioBox(this, wxID_ANY, _L("Extruder type"), wxDefaultPosition, wxDefaultSize, m_rbExtruderTypeNChoices, m_rbExtruderTypeChoices, 2, wxRA_SPECIFY_COLS);
+ m_rbExtruderType->SetSelection(0);
+ choice_sizer->Add(m_rbExtruderType, 0, wxALL, 5);
+ choice_sizer->Add(FromDIP(5), 0, 0, wxEXPAND, 5);
+ wxString m_rbMethodChoices[] = { _L("PA Tower"), _L("PA Line"), _L("PA Pattern") };
+ int m_rbMethodNChoices = sizeof(m_rbMethodChoices) / sizeof(wxString);
+ m_rbMethod = new wxRadioBox(this, wxID_ANY, _L("Method"), wxDefaultPosition, wxDefaultSize, m_rbMethodNChoices, m_rbMethodChoices, 2, wxRA_SPECIFY_COLS);
+ m_rbMethod->SetSelection(0);
+ choice_sizer->Add(m_rbMethod, 0, wxALL, 5);
+
+ v_sizer->Add(choice_sizer);
+
+ // Settings
+ //
+ wxString start_pa_str = _L("Start PA: ");
+ wxString end_pa_str = _L("End PA: ");
+ wxString PA_step_str = _L("PA step: ");
+ auto text_size = wxWindow::GetTextExtent(start_pa_str);
+ text_size.IncTo(wxWindow::GetTextExtent(end_pa_str));
+ text_size.IncTo(wxWindow::GetTextExtent(PA_step_str));
+ text_size.x = text_size.x * 1.5;
+ wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Settings"));
+
+ auto st_size = FromDIP(wxSize(text_size.x, -1));
+ auto ti_size = FromDIP(wxSize(90, -1));
+ // start PA
+ auto start_PA_sizer = new wxBoxSizer(wxHORIZONTAL);
+ auto start_pa_text = new wxStaticText(this, wxID_ANY, start_pa_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
+ m_tiStartPA = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_CENTRE | wxTE_PROCESS_ENTER);
+ m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+
+ start_PA_sizer->Add(start_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
+ start_PA_sizer->Add(m_tiStartPA, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
+ settings_sizer->Add(start_PA_sizer);
+
+ // end PA
+ auto end_PA_sizer = new wxBoxSizer(wxHORIZONTAL);
+ auto end_pa_text = new wxStaticText(this, wxID_ANY, end_pa_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
+ m_tiEndPA = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_CENTRE | wxTE_PROCESS_ENTER);
+ m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+ end_PA_sizer->Add(end_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
+ end_PA_sizer->Add(m_tiEndPA, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
+ settings_sizer->Add(end_PA_sizer);
+
+ // PA step
+ auto PA_step_sizer = new wxBoxSizer(wxHORIZONTAL);
+ auto PA_step_text = new wxStaticText(this, wxID_ANY, PA_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
+ m_tiPAStep = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_CENTRE | wxTE_PROCESS_ENTER);
+ m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+ PA_step_sizer->Add(PA_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
+ PA_step_sizer->Add(m_tiPAStep, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
+ settings_sizer->Add(PA_step_sizer);
+
+ settings_sizer->Add(create_item_checkbox(_L("Print numbers"), this, &m_params.print_numbers, m_cbPrintNum));
+ m_cbPrintNum->SetValue(false);
+
+ v_sizer->Add(settings_sizer);
+ v_sizer->Add(0, FromDIP(10), 0, wxEXPAND, 5);
+ m_btnStart = new Button(this, _L("OK"));
+ StateColor btn_bg_green(std::pair(wxColour(0, 137, 123), StateColor::Pressed),
+ std::pair(wxColour(38, 166, 154), StateColor::Hovered),
+ std::pair(wxColour(0, 150, 136), StateColor::Normal));
+
+ m_btnStart->SetBackgroundColor(btn_bg_green);
+ m_btnStart->SetBorderColor(wxColour(0, 150, 136));
+ m_btnStart->SetTextColor(wxColour("#FFFFFE"));
+ m_btnStart->SetSize(wxSize(FromDIP(48), FromDIP(24)));
+ m_btnStart->SetMinSize(wxSize(FromDIP(48), FromDIP(24)));
+ m_btnStart->SetCornerRadius(FromDIP(3));
+ m_btnStart->Bind(wxEVT_BUTTON, &PA_Calibration_Dlg::on_start, this);
+ v_sizer->Add(m_btnStart, 0, wxALL | wxALIGN_RIGHT, FromDIP(5));
+
+ PA_Calibration_Dlg::reset_params();
+
+ // Connect Events
+ m_rbExtruderType->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_extruder_type_changed), NULL, this);
+ m_rbMethod->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_method_changed), NULL, this);
+ this->Connect(wxEVT_SHOW, wxShowEventHandler(PA_Calibration_Dlg::on_show));
+ //wxGetApp().UpdateDlgDarkUI(this);
+
+ Layout();
+ Fit();
+}
+
+PA_Calibration_Dlg::~PA_Calibration_Dlg() {
+ // Disconnect Events
+ m_rbExtruderType->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_extruder_type_changed), NULL, this);
+ m_rbMethod->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_method_changed), NULL, this);
+ m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PA_Calibration_Dlg::on_start), NULL, this);
+}
+
+void PA_Calibration_Dlg::reset_params() {
+ bool isDDE = m_rbExtruderType->GetSelection() == 0 ? true : false;
+ int method = m_rbMethod->GetSelection();
+
+ m_tiStartPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.0));
+
+ switch (method) {
+ case 1:
+ m_params.mode = CalibMode::Calib_PA_Line;
+ m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.1));
+ m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.002));
+ m_cbPrintNum->SetValue(true);
+ m_cbPrintNum->Enable(true);
+ break;
+ case 2:
+ m_params.mode = CalibMode::Calib_PA_Pattern;
+ m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.08));
+ m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.005));
+ m_cbPrintNum->SetValue(true);
+ m_cbPrintNum->Enable(false);
+ break;
+ default:
+ m_params.mode = CalibMode::Calib_PA_Tower;
+ m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.1));
+ m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.002));
+ m_cbPrintNum->SetValue(false);
+ m_cbPrintNum->Enable(false);
+ break;
+ }
+
+ if (!isDDE) {
+ m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(1.0));
+
+ if (m_params.mode == CalibMode::Calib_PA_Pattern) {
+ m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.05));
+ } else {
+ m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.02));
+ }
+ }
+}
+
+void PA_Calibration_Dlg::on_start(wxCommandEvent& event) {
+ bool read_double = false;
+ read_double = m_tiStartPA->GetTextCtrl()->GetValue().ToDouble(&m_params.start);
+ read_double = read_double && m_tiEndPA->GetTextCtrl()->GetValue().ToDouble(&m_params.end);
+ read_double = read_double && m_tiPAStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step);
+ if (!read_double || m_params.start < 0 || m_params.step < EPSILON || m_params.end < m_params.start + m_params.step) {
+ MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart PA: >= 0.0\nEnd PA: > Start PA\nPA step: >= 0.001)"), wxEmptyString, wxICON_WARNING | wxOK);
+ msg_dlg.ShowModal();
+ return;
+ }
+
+ switch (m_rbMethod->GetSelection()) {
+ case 1:
+ m_params.mode = CalibMode::Calib_PA_Line;
+ break;
+ case 2:
+ m_params.mode = CalibMode::Calib_PA_Pattern;
+ break;
+ default:
+ m_params.mode = CalibMode::Calib_PA_Tower;
+ }
+
+ m_params.print_numbers = m_cbPrintNum->GetValue();
+
+ m_plater->calib_pa(m_params);
+ EndModal(wxID_OK);
+
+}
+void PA_Calibration_Dlg::on_extruder_type_changed(wxCommandEvent& event) {
+ PA_Calibration_Dlg::reset_params();
+ event.Skip();
+}
+void PA_Calibration_Dlg::on_method_changed(wxCommandEvent& event) {
+ PA_Calibration_Dlg::reset_params();
+ event.Skip();
+}
+
+void PA_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
+ this->Refresh();
+ Fit();
+}
+
+void PA_Calibration_Dlg::on_show(wxShowEvent& event) {
+ PA_Calibration_Dlg::reset_params();
+}
+
+// Temp Calib dlg
+//
+enum FILAMENT_TYPE : int
+{
+ tPLA = 0,
+ tABS_ASA,
+ tPETG,
+ tTPU,
+ tPA_CF,
+ tPET_CF,
+ tCustom
+};
+
+
+
+
+
+
+}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/calib_dlg.hpp b/src/slic3r/GUI/calib_dlg.hpp
new file mode 100644
index 0000000..4889c4d
--- /dev/null
+++ b/src/slic3r/GUI/calib_dlg.hpp
@@ -0,0 +1,52 @@
+#ifndef slic3r_calib_dlg_hpp_
+#define slic3r_calib_dlg_hpp_
+
+#include "wxExtensions.hpp"
+#include "GUI_Utils.hpp"
+#include "Widgets/Button.hpp"
+#include "Widgets/Label.hpp"
+#include "Widgets/RadioBox.hpp"
+#include "Widgets/RoundedRectangle.hpp"
+#include "Widgets/CheckBoxInWT.hpp"
+#include "Widgets/ComboBox.hpp"
+#include "Widgets/TextInput.hpp"
+#include "GUI_App.hpp"
+#include "wx/hyperlink.h"
+#include
+#include "libslic3r/calib.hpp"
+#include "Plater.hpp"
+
+namespace Slic3r { namespace GUI {
+
+class PA_Calibration_Dlg : public DPIDialog
+{
+public:
+ PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater);
+ ~PA_Calibration_Dlg();
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+ void on_show(wxShowEvent& event);
+protected:
+ void reset_params();
+ virtual void on_start(wxCommandEvent& event);
+ virtual void on_extruder_type_changed(wxCommandEvent& event);
+ virtual void on_method_changed(wxCommandEvent& event);
+
+protected:
+ bool m_bDDE;
+ Calib_Params m_params;
+
+
+ wxRadioBox* m_rbExtruderType;
+ wxRadioBox* m_rbMethod;
+ TextInput* m_tiStartPA;
+ TextInput* m_tiEndPA;
+ TextInput* m_tiPAStep;
+ CheckBoxInWT *m_cbPrintNum;
+ Button* m_btnStart;
+
+ Plater* m_plater;
+};
+
+}} // namespace Slic3r::GUI
+
+#endif
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index d8eaa44..e48d323 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -795,6 +795,56 @@ void ScalableBitmap::sys_color_changed()
m_bmp = *get_bmp_bundle(m_icon_name, m_px_cnt);
}
+//B34
+// win is used to get a correct em_unit value
+// It's important for bitmaps of dialogs.
+// if win == nullptr, em_unit value of MainFrame will be used
+wxBitmap create_scaled_bitmap(const std::string &bmp_name_in,
+ wxWindow * win /* = nullptr*/,
+ const int px_cnt /* = 16*/,
+ const bool grayscale /* = false*/,
+ const std::string &new_color /* = std::string()*/, // color witch will used instead of orange
+ const bool menu_bitmap /* = false*/,
+ const bool resize /* = false*/)
+{
+ static Slic3r::GUI::BitmapCache cache;
+
+ unsigned int width = 0;
+ unsigned int height = (unsigned int) (win->FromDIP(px_cnt) + 0.5f);
+
+ std::string bmp_name = bmp_name_in;
+ boost::replace_last(bmp_name, ".png", "");
+
+ bool dark_mode =
+#ifdef _WIN32
+ menu_bitmap ? Slic3r::GUI::check_dark_mode() :
+#endif
+ Slic3r::GUI::wxGetApp().dark_mode();
+
+ // Try loading an SVG first, then PNG if SVG is not found:
+ wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, dark_mode, new_color);
+ if (bmp == nullptr) {
+ bmp = cache.load_png(bmp_name, width, height, grayscale);
+ }
+
+ if (bmp == nullptr) {
+ // Neither SVG nor PNG has been found, raise error
+ throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name);
+ }
+
+ return *bmp;
+}
+
+
+
+//B34
+void ScalableBitmap::msw_rescale()
+{
+
+ m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt, m_grayscale, std::string(), false, m_resize);
+}
+
+
// ----------------------------------------------------------------------------
// QIDIButton
// ----------------------------------------------------------------------------
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 00f117e..aad5aea 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -158,6 +158,9 @@ public:
return m_bmp.GetDefaultSize();
#endif
}
+ //B34
+ void msw_rescale();
+
int GetWidth() const { return GetSize().GetWidth(); }
int GetHeight() const { return GetSize().GetHeight(); }
@@ -167,6 +170,9 @@ private:
wxBitmap m_bitmap = wxBitmap();
std::string m_icon_name = "";
int m_px_cnt {16};
+ //B34
+ bool m_grayscale{false};
+ bool m_resize{false};
};