Merge branch 'main' into feat/plugin-pages

This commit is contained in:
peachismomo
2026-08-06 06:16:28 +08:00
408 changed files with 19559 additions and 4589 deletions
+64 -14
View File
@@ -70,6 +70,9 @@ float FullTransparentModdifiedToFixAlpha = 0.3f;
// value like 0.18f could not because in C++ (int)(0.18f * 255) == 45 however in OpenGL it renders this as 46
// which breaks the `SelectMachineDialog::record_edge_pixels_data()` function!
float FULL_BLACK_THRESHOLD = 0.2f;
// Keep depth_tex away from texture unit 0 to avoid sampler-type aliasing with
// shadow/environment samplers when realistic view is disabled.
static constexpr int OUTLINE_DEPTH_TEX_UNIT = 5;
Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors)
{
@@ -518,6 +521,37 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
glsafe(::glStencilMask(0xFF));
glsafe(::glDisable(GL_STENCIL_TEST));
// render the outline using depth buffer and discard the pixels that are not on the outline
// The silhouette is resolved per sample in the shader (see DetectSilho in gouraud.fs/phong.fs).
// That needs the GL 3.2 entry points and a shader that declares depth_tex as sampler2DMS, which
// only the 140 ones do and only under GL_ARB_texture_multisample - so ask the compiled program
// rather than the GL version, or a sampler2D ends up bound to a multisample texture.
// Only the Arb branch below allocates a multisample texture, so keep the target consistent with it.
const bool use_msaa_outline = framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb &&
GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 2) &&
shader->get_uniform_location("msaa_samples") >= 0;
const GLenum depth_tex_target = use_msaa_outline ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
// Keep the depth texture off image unit 0. The object shaders leave shadow_map (and
// environment_tex) at the default sampler value 0 whenever the shadow pass is skipped - which is
// the case with realistic view off - and GL forbids two sampler types referring to the same image
// unit. A sampler2DMS on unit 0 then makes every draw fail with INVALID_OPERATION on drivers that
// enforce it (Mesa), i.e. the model disappears entirely. Unit 5 is unused (shadow_map takes 4).
const int depth_tex_unit = OUTLINE_DEPTH_TEX_UNIT;
int aa_samples = 1;
if (use_msaa_outline) {
if (const AppConfig* app_config = GUI::wxGetApp().app_config; app_config != nullptr) {
const std::string value = app_config->get(SETTING_OPENGL_AA_SAMPLES);
if (value == "2" || value == "4" || value == "8" || value == "16")
aa_samples = ::atoi(value.c_str());
}
// Never request more samples than the driver supports for depth textures (a 1-sample texture
// is used when MSAA is disabled, keeping a single code path for the sampler2DMS shader).
GLint max_samples = 1;
glsafe(::glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &max_samples));
if (aa_samples > max_samples)
aa_samples = max_samples < 1 ? 1 : max_samples;
if (aa_samples < 1)
aa_samples = 1;
}
// 1st. render pass, render the model into a separate render target that has only depth buffer
GLuint depth_fbo = 0;
GLuint depth_tex = 0;
@@ -525,21 +559,26 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
glsafe(::glGenFramebuffers(1, &depth_fbo));
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo));
glActiveTexture(GL_TEXTURE0);
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
glsafe(::glBindTexture(depth_tex_target, depth_tex));
if (use_msaa_outline) {
// Multisample textures do not take filter/wrap parameters.
glsafe(::glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, aa_samples, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), GL_TRUE));
} else {
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
}
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0));
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_tex_target, depth_tex, 0));
} else {
glsafe(::glGenFramebuffersEXT(1, &depth_fbo));
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo));
glActiveTexture(GL_TEXTURE0);
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
@@ -550,12 +589,15 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
}
// Unbind before drawing: the texture is this framebuffer's depth attachment, so leaving it bound
// to a sampled unit would be a feedback loop.
glsafe(::glBindTexture(depth_tex_target, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
model.render(shader);
else
model.render(this->tverts_range, shader);
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
// 2nd. render pass, just a normal render with the depth buffer passed as a texture
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
@@ -565,13 +607,17 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
}
shader->set_uniform("is_outline", true);
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
glActiveTexture(GL_TEXTURE0);
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
shader->set_uniform("depth_tex", 0);
shader->set_uniform("msaa_samples", aa_samples);
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glBindTexture(depth_tex_target, depth_tex));
glsafe(::glActiveTexture(GL_TEXTURE0));
shader->set_uniform("depth_tex", depth_tex_unit);
simple_render(shader, model_objects, colors);
// Some clean up to do
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glBindTexture(depth_tex_target, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
shader->set_uniform("is_outline", false);
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
@@ -1075,6 +1121,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
const float support_normal_z = get_selection_support_normal_z();
// Prime depth_tex on every frame so non-outline draws do not keep the
// default sampler unit 0, which can conflict with other sampler types.
shader->set_uniform("depth_tex", OUTLINE_DEPTH_TEX_UNIT);
for (GLVolumeWithIdAndZ& volume : to_render) {
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
if (type == ERenderType::Transparent) {
+27 -10
View File
@@ -70,6 +70,12 @@ void ConfigManipulation::toggle_line(const std::string& opt_key, const bool togg
cb_toggle_line(opt_key, toggle, opt_index);
}
void ConfigManipulation::set_option_label(const std::string& opt_key, const wxString& label, int opt_index)
{
if (cb_set_option_label)
cb_set_option_label(opt_key, label, opt_index);
}
void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config) {
if (is_msg_dlg_already_exist)
return;
@@ -703,12 +709,14 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
toggle_line("spiral_starting_flow_ratio", has_spiral_vase);
toggle_line("spiral_finishing_flow_ratio", has_spiral_vase);
bool has_top_shell = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1);
bool has_top_shell_layers = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1);
bool has_top_shell = has_top_shell_layers && config->option<ConfigOptionPercent>("top_surface_density")->value > 0;
bool has_bottom_shell = config->opt_int("bottom_shell_layers") > 0;
bool has_solid_infill = has_top_shell || has_bottom_shell;
bool has_solid_infill = has_top_shell_layers || has_bottom_shell;
toggle_line("sparse_infill_smooth_factor", pattern == ipHilbertCurve);
toggle_field("top_surface_pattern", has_top_shell);
toggle_field("bottom_surface_pattern", has_bottom_shell);
toggle_field("top_surface_density", has_top_shell);
toggle_field("top_surface_density", has_top_shell_layers);
toggle_field("bottom_surface_density", has_bottom_shell);
toggle_field("top_layer_direction", has_top_shell);
toggle_field("bottom_layer_direction", has_bottom_shell);
@@ -751,7 +759,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
for (auto el : { "sparse_infill_speed", "bridge_speed", "internal_bridge_speed"})
toggle_field(el, have_infill || has_solid_infill, variant_index);
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell);
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell_layers);
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_shell);
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
@@ -806,14 +814,19 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_field("outer_wall_filament_id", have_perimeters || have_brim);
toggle_field("inner_wall_filament_id", have_perimeters || have_brim);
bool have_brim_ear = (config->opt_enum<BrimType>("brim_type") == btEar);
const BrimType brim_type = config->opt_enum<BrimType>("brim_type");
const bool have_auto_brim_ear = brim_type == btEar;
const bool have_painted_brim_ear = brim_type == btPainted;
set_option_label("brim_width", have_auto_brim_ear ? _L("Brim ear radius") : _L("Brim width"));
const auto brim_width = config->opt_float("brim_width");
// disable brim_ears_max_angle and brim_ears_detection_length if brim_width is 0
// Automatic brim ear settings require a non-zero brim width.
toggle_field("brim_ears_max_angle", brim_width > 0.0f);
toggle_field("brim_ears_detection_length", brim_width > 0.0f);
// hide brim_ears_max_angle and brim_ears_detection_length if brim_ear is not selected
toggle_line("brim_ears_max_angle", have_brim_ear);
toggle_line("brim_ears_detection_length", have_brim_ear);
// Painted ears carry their own radius and do not depend on brim_width.
toggle_field("brim_ears_outer_only", have_painted_brim_ear || brim_width > 0.0f);
toggle_line("brim_ears_max_angle", have_auto_brim_ear);
toggle_line("brim_ears_detection_length", have_auto_brim_ear);
toggle_line("brim_ears_outer_only", have_auto_brim_ear || have_painted_brim_ear);
// Hide Elephant foot compensation layers if elefant_foot_compensation is not enabled
toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0 || config->option<ConfigOptionPercent>("elefant_foot_layers_density")->get_abs_value(1.0f) < 1.0f);
@@ -1008,7 +1021,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
toggle_line("min_width_top_surface", config->opt_bool("only_one_wall_top") || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
// Orca: the one-wall options act on top/bottom surfaces, which exist only with a shell. An unfilled surface
// (0% surface density) is still a surface, so these are gated on the layer counts alone.
toggle_line("only_one_wall_first_layer", has_bottom_shell);
toggle_line("only_one_wall_top", has_top_shell_layers);
toggle_line("min_width_top_surface", (has_top_shell_layers && config->opt_bool("only_one_wall_top")) || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "hole_to_polyhole_max_edges" })
toggle_line(el, config->opt_bool("hole_to_polyhole"));
+6 -1
View File
@@ -29,6 +29,7 @@ class ConfigManipulation
std::function<void()> load_config = nullptr;
std::function<void (const std::string&, bool toggle, int opt_index)> cb_toggle_field = nullptr;
std::function<void(const std::string &, bool toggle, int opt_index)> cb_toggle_line = nullptr;
std::function<void(const std::string &, const wxString &, int opt_index)> cb_set_option_label = nullptr;
// callback to propagation of changed value, if needed
std::function<void(const std::string&, const boost::any&)> cb_value_change = nullptr;
//BBS: change local config to const DynamicPrintConfig
@@ -45,10 +46,12 @@ public:
std::function<void(const std::string&, const boost::any&)> cb_value_change,
//BBS: change local config to DynamicPrintConfig
const DynamicPrintConfig* local_config = nullptr,
wxWindow* msg_dlg_parent = nullptr) :
wxWindow* msg_dlg_parent = nullptr,
std::function<void(const std::string &, const wxString &, int opt_index)> cb_set_option_label = nullptr) :
load_config(load_config),
cb_toggle_field(cb_toggle_field),
cb_toggle_line(cb_toggle_line),
cb_set_option_label(cb_set_option_label),
cb_value_change(cb_value_change),
m_msg_dlg_parent(msg_dlg_parent),
local_config(local_config) {}
@@ -58,6 +61,7 @@ public:
load_config = nullptr;
cb_toggle_field = nullptr;
cb_toggle_line = nullptr;
cb_set_option_label = nullptr;
cb_value_change = nullptr;
}
@@ -67,6 +71,7 @@ public:
t_config_option_keys const &applying_keys() const;
void toggle_field(const std::string& field_key, const bool toggle, int opt_index = -1);
void toggle_line(const std::string& field_key, const bool toggle, int opt_index = -1);
void set_option_label(const std::string& field_key, const wxString& label, int opt_index = -1);
// FFF print
void update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config = false, const bool is_plate_config = false);
+32 -13
View File
@@ -496,6 +496,26 @@ namespace Slic3r
OnSelectedMachineChanged(previous_selected_machine, selected_machine);
}
void DeviceManager::clear_other_devices()
{
// why: on agent swap, keep "My Devices" but drop the transient "Other Devices"
// Those belong to the previous agent's network scan; the new agent's start_discovery re-populates its own.
const auto my = get_my_machine_list();
for (auto it = localMachineList.begin(); it != localMachineList.end();)
{
if (my.find(it->first) == my.end())
{
// not a "My Device" -> an "Other Device"
delete it->second;
it = localMachineList.erase(it);
}
else
{
++it;
}
}
}
bool DeviceManager::set_selected_machine(std::string dev_id)
{
BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id
@@ -558,7 +578,6 @@ namespace Slic3r
}
else
{
Slic3r::GUI::wxGetApp().reset_unsigned_plugin_warning();
if (m_agent)
{
if (it->second->connection_type() != "lan" || it->second->connection_type().empty())
@@ -851,7 +870,9 @@ namespace Slic3r
int result = m_agent->get_user_print_info(&http_code, &body, provider);
if (result == 0)
{
parse_user_print_info(body);
// parse_user_print_info and on_machine_alive (SSDP for discovery) both mutate the same userMachineList map.
// on_machine_alive mutates the map on the UI thread, do the same for parse_user_print_info.
Slic3r::GUI::wxGetApp().CallAfter([this, body]() { parse_user_print_info(body); });
}
}
@@ -878,17 +899,15 @@ namespace Slic3r
void DeviceManager::load_last_machine()
{
if (userMachineList.empty()) return;
else if (userMachineList.size() == 1) {
this->set_selected_machine(userMachineList.begin()->second->get_dev_id());
} else {
const auto& last_monitor_machine = get_user_last_machine();
if (userMachineList.find(last_monitor_machine) != userMachineList.end()) {
set_selected_machine(last_monitor_machine);
} else {
this->set_selected_machine(userMachineList.begin()->second->get_dev_id());
}
}
// Only reconnect the remembered cloud machine. Do not select an arbitrary
// first machine: agent swaps intentionally leave the selection empty until
// the new agent explicitly selects its configured printer.
if (userMachineList.empty())
return;
const auto& last_monitor_machine = get_user_last_machine();
if (userMachineList.find(last_monitor_machine) != userMachineList.end())
set_selected_machine(last_monitor_machine);
}
void DeviceManager::OnMachineBindStateChanged(MachineObject* obj, const std::string& new_state)
+6 -1
View File
@@ -48,6 +48,10 @@ public:
MachineObject* get_selected_machine();
bool set_selected_machine(std::string dev_id);
// why: clears stale sidebar sync-status / AMS visuals. Public so the printer-agent
// swap path can reuse it instead of duplicating the two sidebar calls.
void OnSelectedMachineLost();
void record_user_last_machine(const std::string& dev_id);
std::string get_user_last_machine() const;
@@ -70,6 +74,8 @@ public:
void erase_user_machine(std::string dev_id) { userMachineList.erase(dev_id); }
void clean_user_info(bool keep_local_selection = false);
void clear_other_devices();
void load_last_machine();
void update_user_machine_list_info(const std::string& provider);
void parse_user_print_info(std::string body);
@@ -110,7 +116,6 @@ private:
void check_pushing();
void OnMachineBindStateChanged(MachineObject* obj, const std::string& new_state);
void OnSelectedMachineLost();
void OnSelectedMachineChanged(const std::string& pre_dev_id, const std::string& new_dev_id);
+34
View File
@@ -4647,6 +4647,40 @@ void MachineObject::set_ctt_dlg( wxString text){
}
}
void MachineObject::show_unsupported_dlg(int code)
{
// why: a dead control invites repeat clicks, and the frame is modeless - without the guard
// every click stacks another one. Same shape as set_ctt_dlg above, including the reset on
// both hide and close so a dismissed dialog can reappear on the next attempt.
if (m_unsupported_dlg_shown) {
return;
}
m_unsupported_dlg_shown = true;
// why: two codes so the user learns which kind of dead end this is - the slicer having no
// translation for the command, or the printer's own config lacking the hardware to run it.
const wxString text = (code == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) ?
_L("This printer is not configured with the hardware this control needs.") :
_L("This control is not supported on this printer.");
// note: constructed directly rather than through CallAfter because every publish_json caller
// is on the UI thread - clicks come from wx handlers, and the agent marshals its own push
// callbacks back to main before parse_json runs. set_ctt_dlg relies on the same property.
auto unsupported_dlg = new GUI::SecondaryCheckDialog(nullptr, wxID_ANY, _L("Warning"),
GUI::SecondaryCheckDialog::VisibleButtons::ONLY_CONFIRM);
unsupported_dlg->update_text(text);
unsupported_dlg->Bind(wxEVT_SHOW, [this](auto& e) {
if (!e.IsShown()) {
m_unsupported_dlg_shown = false;
}
});
unsupported_dlg->Bind(wxEVT_CLOSE_WINDOW, [this](auto& e) {
e.Skip();
m_unsupported_dlg_shown = false;
});
unsupported_dlg->on_show();
}
int MachineObject::publish_gcode(std::string gcode_str)
{
json j;
+2
View File
@@ -272,9 +272,11 @@ public:
bool m_is_online;
bool m_lan_mode_connection_state{false};
bool m_set_ctt_dlg{ false };
bool m_unsupported_dlg_shown{ false };
void set_lan_mode_connection_state(bool state) {m_lan_mode_connection_state = state;};
bool get_lan_mode_connection_state() {return m_lan_mode_connection_state;};
void set_ctt_dlg( wxString text);
void show_unsupported_dlg(int code);
int parse_msg_count = 0;
int keep_alive_count = 0;
std::chrono::system_clock::time_point last_update_time; /* last received print data from machine */
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiAmsHumidityPopup.cpp
/**********************************************************
* File: uiAmsHumidityPopup.cpp
* Description: The popup with DevAms Humidity
*
* \n class uiAmsHumidityPopup
//**********************************************************/
**********************************************************/
#include "uiAmsHumidityPopup.h"
@@ -191,4 +191,4 @@ void uiAmsPercentHumidityDryPopup::msw_rescale()
} // namespace GUI
} // namespace Slic3r
} // namespace Slic3r
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiAmsHumidityPopup.h
/**********************************************************
* File: uiAmsHumidityPopup.h
* Description: The popup with DevAms Humidity
*
* \n class uiAmsHumidityPopup
//**********************************************************/
**********************************************************/
#pragma once
#include "slic3r/GUI/Widgets/AMSItem.hpp"
@@ -68,7 +68,7 @@ private:
wxStaticBitmap* m_dry_state_img;
Label* m_dry_state;
Label* m_humidity_header;
Label* m_humidity_label;
@@ -81,4 +81,4 @@ private:
wxSizer* m_sizer;
};
}} // namespace Slic3r::GUI
}} // namespace Slic3r::GUI
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiDeviceUpdateVersion.cpp
/**********************************************************
* File: uiDeviceUpdateVersion.cpp
* Description: The panel with firmware info
*
* \n class uiDeviceUpdateVersion
//**********************************************************/
**********************************************************/
#include "uiDeviceUpdateVersion.h"
@@ -114,4 +114,4 @@ void uiDeviceUpdateVersion::CreateWidgets()
Layout();
wxGetApp().UpdateDarkUIWin(this);
}
}
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiDeviceUpdateVersion.h
/**********************************************************
* File: uiDeviceUpdateVersion.h
* Description: The panel with firmware info
*
* \n class uiDeviceUpdateVersion
//**********************************************************/
**********************************************************/
#pragma once
#include <wx/panel.h>
@@ -44,4 +44,4 @@ private:
wxStaticText* m_dev_version;
wxStaticBitmap* m_dev_upgrade_indicator;
};
};// end of namespace Slic3r::GUI
};// end of namespace Slic3r::GUI
+176 -118
View File
@@ -35,6 +35,7 @@
#include "Widgets/TextCtrl.h"
#include "../Utils/ColorSpaceConvert.hpp"
#include "../Utils/NetworkAgentFactory.hpp"
#ifdef __WXOSX__
#define wxOSX true
#else
@@ -1336,27 +1337,7 @@ void SpinCtrl::BUILD() {
if (!parsed || value < INT_MIN || value > INT_MAX)
tmp_value = UNDEF_VALUE;
else {
tmp_value = std::min(std::max((int)value, temp->GetMin()), temp->GetMax());
#ifdef __WXOSX__
#ifdef UNDEFINED__WXOSX__ // BBS
// Forcibly set the input value for SpinControl, since the value
// inserted from the keyboard or clipboard is not updated under OSX
SpinInput* spin = static_cast<SpinInput*>(window);
spin->SetValue(tmp_value);
// But in SetValue() is executed m_text_ctrl->SelectAll(), so
// discard this selection and set insertion point to the end of string
// temp->GetText()->SetInsertionPointEnd();
#endif
#else
// update value for the control only if it was changed in respect to the Min/max values
if (tmp_value != (int)value) {
temp->SetValue(tmp_value);
// But after SetValue() cursor ison the first position
// so put it to the end of string
// int pos = std::to_string(tmp_value).length();
// temp->SetSelection(pos, pos);
}
#endif
tmp_value = (int)value;
}
}), temp->GetTextCtrl()->GetId());
@@ -1377,6 +1358,10 @@ void SpinCtrl::propagate_value()
on_kill_focus();
} else {
auto ctrl = dynamic_cast<SpinInput *>(window);
tmp_value = std::min(std::max(tmp_value, ctrl->GetMin()), ctrl->GetMax());
if (ctrl->GetValue() != tmp_value)
ctrl->SetValue(tmp_value); // Clamp now when the user is done typing (kill focus / Enter / spin arrows)
if (m_value.empty()
? !ctrl->GetTextCtrl()->GetLabel().IsEmpty()
: ctrl->GetValue() != boost::any_cast<int>(m_value))
@@ -1421,39 +1406,6 @@ using choice_ctrl = ::ComboBox; // BBS
static std::map<std::string, DynamicList*> dynamic_lists;
static bool is_plugin_printer_agent_key(const std::string& value)
{
return value.rfind("plugin:", 0) == 0;
}
static int printer_agent_item_for_enum_index(const choice_ctrl* field, int enum_index)
{
if (!field)
return -1;
const unsigned int count = field->GetCount();
for (unsigned int idx = 0; idx < count; ++idx) {
if (void* data = field->GetClientData(idx)) {
const int stored = static_cast<int>(reinterpret_cast<uintptr_t>(data)) - 1;
if (stored == enum_index)
return static_cast<int>(idx);
}
}
return -1;
}
static int printer_agent_enum_index_for_item(const choice_ctrl* field, int item_index, int fallback)
{
if (!field || item_index < 0)
return fallback;
if (void* data = field->GetClientData(item_index))
return static_cast<int>(reinterpret_cast<uintptr_t>(data)) - 1;
return fallback;
}
void Choice::register_dynamic_list(std::string const &optname, DynamicList *list) { dynamic_lists.emplace(optname, list); }
void DynamicList::update()
@@ -1536,33 +1488,7 @@ void Choice::BUILD()
window = dynamic_cast<wxWindow*>(temp);
if (! m_opt.enum_labels.empty() || ! m_opt.enum_values.empty()) {
if (m_opt_id == "printer_agent") {
const bool has_builtin_agents = std::any_of(m_opt.enum_values.begin(), m_opt.enum_values.end(),
[](const std::string& value) { return !is_plugin_printer_agent_key(value); });
const bool has_plugin_agents = std::any_of(m_opt.enum_values.begin(), m_opt.enum_values.end(),
[](const std::string& value) { return is_plugin_printer_agent_key(value); });
auto append_agent_rows = [this, temp](bool plugins) {
for (size_t i = 0; i < m_opt.enum_values.size(); ++i) {
const bool is_plugin = is_plugin_printer_agent_key(m_opt.enum_values[i]);
if (is_plugin != plugins)
continue;
const wxString label = i < m_opt.enum_labels.size() ? _(m_opt.enum_labels[i]) : wxString(m_opt.enum_values[i]);
const int item = temp->Append(label);
temp->SetClientData(item, reinterpret_cast<void*>(static_cast<uintptr_t>(i + 1)));
}
};
if (has_builtin_agents) {
temp->Append(_L("System agents"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM | DD_ITEM_STYLE_DISABLED);
append_agent_rows(false);
}
if (has_plugin_agents) {
temp->Append(_L("Plugins"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM | DD_ITEM_STYLE_DISABLED);
append_agent_rows(true);
}
} else if (m_opt.enum_labels.empty()) {
if (m_opt.enum_labels.empty()) {
// Append non-localized enum_values
for (auto el : m_opt.enum_values)
temp->Append(el);
@@ -1669,7 +1595,7 @@ void Choice::set_selection()
switch (m_opt.type) {
case coEnum:{
const int val = m_opt.default_value->getInt();
field->SetSelection(m_opt_id == "printer_agent" ? printer_agent_item_for_enum_index(field, val) : val);
field->SetSelection(val);
break;
}
case coFloat:
@@ -1719,12 +1645,7 @@ void Choice::set_value(const std::string& value, bool change_event) //! Redunda
}
choice_ctrl* field = dynamic_cast<choice_ctrl*>(window);
if (m_opt_id == "printer_agent") {
const int enum_index = idx == m_opt.enum_values.size() ?
(m_opt.default_value ? m_opt.default_value->getInt() : 0) :
static_cast<int>(idx);
field->SetSelection(printer_agent_item_for_enum_index(field, enum_index));
} else if (idx == m_opt.enum_values.size())
if (idx == m_opt.enum_values.size())
field->SetValue(value);
else
field->SetSelection(idx);
@@ -1790,33 +1711,11 @@ void Choice::set_value(const boost::any& value, bool change_event)
case coEnum:
// BBS
case coEnums: {
auto printer_agent_index_from_key = [this](const std::string& key) {
auto it = std::find(m_opt.enum_values.begin(), m_opt.enum_values.end(), key);
if (it != m_opt.enum_values.end())
return static_cast<int>(it - m_opt.enum_values.begin());
return m_opt.default_value ? m_opt.default_value->getInt() : 0;
};
int val = 0;
if (m_opt_id == "printer_agent") {
if (const int* int_value = boost::any_cast<int>(&value))
val = *int_value;
else if (const wxString* wx_value = boost::any_cast<wxString>(&value))
val = printer_agent_index_from_key(into_u8(*wx_value));
else if (const std::string* string_value = boost::any_cast<std::string>(&value))
val = printer_agent_index_from_key(*string_value);
else {
m_disable_change_event = false;
return;
}
} else
val = boost::any_cast<int>(value);
int val = boost::any_cast<int>(value);
int selection = val;
if (m_opt_id == "printer_agent") {
selection = printer_agent_item_for_enum_index(field, val);
} else if (m_opt_id == "input_shaping_type") {
if (m_opt_id == "input_shaping_type") {
if (field != nullptr) {
const unsigned int count = field->GetCount();
int match_index = -1;
@@ -1938,12 +1837,6 @@ boost::any& Choice::get_value()
{
if (m_opt.nullable && field->GetSelection() == -1)
m_value = ConfigOptionEnumsGenericNullable::nil_value();
else if (m_opt_id == "printer_agent")
{
const int selection = field->GetSelection();
const int fallback = m_opt.default_value ? m_opt.default_value->getInt() : 0;
m_value = printer_agent_enum_index_for_item(field, selection, fallback);
}
else if (m_opt_id == "input_shaping_type")
{
int selection = field->GetSelection();
@@ -2085,6 +1978,171 @@ void Choice::msw_rescale()
}
// PrinterAgentChoice
void PrinterAgentChoice::reload_rows()
{
auto* combo = dynamic_cast<choice_ctrl*>(window); // wxWidgets ComboBox
if (!combo)
return;
// clear ComboBox
combo->Clear();
// helpers
const auto agents = NetworkAgentFactory::get_registered_printer_agents();
const bool has_builtin_agents = std::any_of(agents.begin(), agents.end(),
[](const PrinterAgentInfo& a) { return !a.is_plugin(); });
const bool has_plugin_agents = std::any_of(agents.begin(), agents.end(),
[](const PrinterAgentInfo& a) { return a.is_plugin(); });
auto append_agent_rows = [combo](bool is_plugin)
{
const auto agents = NetworkAgentFactory::get_registered_printer_agents();
for (size_t i = 0; i < agents.size(); ++i)
{
if (agents[i].is_plugin() != is_plugin)
continue;
const int item = combo->Append(_(agents[i].display_name));
// why: carry the agent-id string on the row. alias is an owned wxString (auto-freed, never rendered)
combo->SetItemAlias(item, from_u8(agents[i].id));
}
};
// append rows
if (has_builtin_agents)
{
combo->Append(_L("System agents"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM | DD_ITEM_STYLE_DISABLED);
append_agent_rows(false); // append rows for agents that are not plugins
}
if (has_plugin_agents)
{
combo->Append(_L("Plugins"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM | DD_ITEM_STYLE_DISABLED);
append_agent_rows(true); // append rows for agents that are plugins
}
}
void PrinterAgentChoice::BUILD()
{
wxSize size(def_width_wider() * m_em_unit, wxDefaultCoord);
if (m_opt.height >= 0) size.SetHeight(m_opt.height * m_em_unit);
if (m_opt.width >= 0) size.SetWidth(m_opt.width * m_em_unit);
static Builder<choice_ctrl> builder;
choice_ctrl* temp = builder.build(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size, 0, nullptr,
wxCB_READONLY);
temp->Clear();
temp->GetDropDown().SetUseContentWidth(true);
if (parent_is_custom_ctrl && m_opt.height < 0)
opt_height = (double)temp->GetTextCtrl()->GetSize().GetHeight() / m_em_unit;
temp->SetTextLabel(_L(m_opt.sidetext));
m_combine_side_text = true;
#ifdef __WXGTK3__
wxSize best_sz = temp->GetBestSize();
if (best_sz.x > size.x) temp->SetSize(best_sz);
#endif
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
window = dynamic_cast<wxWindow*>(temp);
reload_rows();
temp->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { on_change_field(); }, temp->GetId());
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
}
// Resolve CONFIG id string to a matching row in the live REGISTRY. "" uses the vendor default.
// An unregistered id clears selection and shows "<id> (missing)" as free text.
void PrinterAgentChoice::set_value(const std::string& value, bool change_event)
{
m_disable_change_event = !change_event;
auto* field = dynamic_cast<choice_ctrl*>(window);
// check if any row's corresponding id matches the agent id we are attempting to set
const std::string effective_agent_id = wxGetApp().resolve_printer_agent_id(value);
const unsigned int count = field->GetCount();
int match = wxNOT_FOUND;
for (unsigned int i = 0; i < count; ++i)
{
if (into_u8(field->GetItemAlias(i)) == effective_agent_id) // if alias == id
{
match = static_cast<int>(i);
break;
}
}
// based on match or not, set selection and value
// - SetSelection and SetValue are UI to manipulate the display of the ComboBox
// - SetSelection automatically calls SetValue for the same value
// - we can also SetValue separately from SetSelection
if (match == wxNOT_FOUND)
{
field->SetSelection(wxNOT_FOUND); // nothing shows as selected in the dropdown
field->SetValue(from_u8(value + " (missing)")); // set a value not in the selection (upper display field)
}
else
{
// display name of agent shows both in upper display field and appears selected in dropdown
field->SetSelection(match);
}
m_disable_change_event = false;
}
// Accept boost::any values from callers (usually to OptionsGroup/Field parent classes) and normalize them to an agent id.
// Then use PrinterAgentChoice::set_value(std::string& value, ...)
void PrinterAgentChoice::set_value(const boost::any& value, bool change_event)
{
m_disable_change_event = !change_event;
auto* field = dynamic_cast<choice_ctrl*>(window);
if (value.empty())
{
field->SetValue("");
m_value = value;
m_disable_change_event = false;
return;
}
std::string id;
if (const std::string* s = boost::any_cast<std::string>(&value))
id = *s;
else if (const wxString* w = boost::any_cast<wxString>(&value))
id = into_u8(*w);
set_value(id, change_event);
}
// A real row returns its alias, which is the agent id. Header rows, missing rows,
// and no selection return empty boost::any so the custom writer leaves config unchanged.
boost::any& PrinterAgentChoice::get_value()
{
auto* field = dynamic_cast<choice_ctrl*>(window);
const int sel = field->GetSelection();
const std::string id = sel < 0 ? std::string{} : into_u8(field->GetItemAlias(sel));
if (id.empty())
m_value = boost::any{};
else
m_value = id;
return m_value;
}
void PrinterAgentChoice::enable() { dynamic_cast<choice_ctrl*>(window)->Enable(); }
void PrinterAgentChoice::disable() { dynamic_cast<choice_ctrl*>(window)->Disable(); }
void PrinterAgentChoice::msw_rescale()
{
Field::msw_rescale();
auto* field = dynamic_cast<choice_ctrl*>(window)->GetTextCtrl();
wxSize size(wxDefaultSize);
size.SetWidth((m_opt.width > 0 ? m_opt.width : def_width_wider()) * m_em_unit);
field->SetMinSize(wxSize(-1, int(1.5f * field->GetFont().GetPixelSize().y + 0.5f)));
field->SetSize(size);
dynamic_cast<choice_ctrl*>(window)->Rescale();
}
void PluginField::BUILD()
{
auto* panel = new wxPanel(m_parent, wxID_ANY);
+38
View File
@@ -469,6 +469,44 @@ public:
void suppress_scroll();
};
// printer_agent is a coString whose choices come from the live agent registry.
// PrinterAgentChoice uses a ComboBox directly because Choice expects static config enums.
// Real rows carry the stored agent id in the row alias (SetItemAlias/GetItemAlias).
class PrinterAgentChoice : public Field
{
using Field::Field;
public:
PrinterAgentChoice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id)
{
}
PrinterAgentChoice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(
parent, opt, id)
{
}
~PrinterAgentChoice()
{
}
wxWindow* window{nullptr};
void BUILD() override;
// Clear and repopulate rows from the live registry (grouped System agents / Plugins).
// Does not change selection; the caller follows with set_value(stored id).
void reload_rows();
void set_value(const std::string& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false) override;
boost::any& get_value() override;
void enable() override;
void disable() override;
void msw_rescale() override;
wxWindow* getWindow() override { return window; }
};
class PluginField : public Field {
using Field::Field;
public:
+2 -1
View File
@@ -1134,8 +1134,9 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
if (current_top_layer_only != required_top_layer_only)
m_viewer.toggle_top_layer_only_view_range();
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
// ORCA: darken the layers the preview layer slider is not scrubbed to
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
// avoid processing if called with the same gcode_result
if (m_last_result_id == gcode_result.id && wxGetApp().is_editor()) {
+4 -1
View File
@@ -333,9 +333,12 @@ public:
libvgcode::EViewType get_view_type() const { return m_viewer.get_view_type(); }
// ORCA: darken layers below the current top layer while scrubbing the preview (ported from preFlight)
// ORCA: darken the layers not scrubbed to while using the preview layer slider
void set_dim_previous_layers(bool value) { m_viewer.set_dim_previous_layers(value); }
bool is_dim_previous_layers() const { return m_viewer.is_dim_previous_layers(); }
// ORCA: brightness of those darkened layers, 1.0 = unchanged, 0.0 = black
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
+96 -6
View File
@@ -1842,6 +1842,10 @@ void GLCanvas3D::enable_separator_toolbar(bool enable)
m_separator_toolbar.set_enabled(enable);
}
bool GLCanvas3D::has_mouse_capture() const {
return m_canvas != nullptr && m_canvas->HasCapture();
}
void GLCanvas3D::zoom_to_bed()
{
BoundingBoxf3 box = m_bed.build_volume().bounding_volume();
@@ -2182,7 +2186,7 @@ void GLCanvas3D::render(bool only_init)
// Negative coordinate means out of the window, likely because the window was deactivated.
// In that case the tooltip should be hidden.
if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0.) {
if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0. || has_mouse_capture()) { // ORCA continue to capture mouse pos mid drag
if (tooltip.empty())
tooltip = m_layers_editing.get_tooltip(*this);
@@ -4170,6 +4174,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// BBS: single snapshot
Plater::SingleSnapshot single(wxGetApp().plater());
#ifdef __WXMAC__
// On macOS, the mouse key state is only present for mouse btn related events such as wxEVT_LEFT_DOWN.
// For other events, all buttons are reported as non-pressed, such as window leaving event. This causes
// imgui stopped responding if cursor moved out of window, such as
// https://github.com/OrcaSlicer/OrcaSlicer/pull/14999#issuecomment-5151344759
// We solve this by correcting the state of the event from the actual mouse state querying with `wxGetMouseState()`
// so it works like on other platforms.
{
const auto state = wxGetMouseState();
evt.SetLeftDown(state.LeftIsDown());
evt.SetMiddleDown(state.MiddleIsDown());
evt.SetRightDown(state.RightIsDown());
evt.SetAux1Down(state.Aux1IsDown());
evt.SetAux2Down(state.Aux2IsDown());
}
#endif
#if ENABLE_RETINA_GL
const float scale = m_retina_helper->get_scale_factor();
evt.SetX(evt.GetX() * scale);
@@ -4183,11 +4204,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// ignore left up events coming from imgui windows and not processed by them
m_mouse.ignore_left_up = true;
m_tooltip.set_in_imgui(false);
if (imgui->update_mouse_data(evt)) {
// while a non-ImGui drag is already in progress (gizmo grabber, object move, rectangle selection, layer editing),
// don't let ImGui/ImGuizmo claim the event just because the cursor is hovering something like the navigator cube
// that incorrectly suppresses the active drag's tooltip and can interrupt its processing. The active drag always takes priority.
const bool other_drag_active = m_gizmos.is_dragging() || m_mouse.dragging || m_rectangle_selection.is_dragging() || m_layers_editing.state == LayersEditing::Editing;
if (imgui->update_mouse_data(evt) && !other_drag_active) {
if ((evt.LeftDown() || (evt.Moving() && (evt.AltDown() || evt.ShiftDown()))) && m_canvas != nullptr)
m_canvas->SetFocus();
m_mouse.position = evt.Leaving() ? Vec2d(-1.0, -1.0) : pos.cast<double>();
m_tooltip.set_in_imgui(true);
// ORCA keep tracking mouse position while drag active and cursor not in window bounds
const bool imgui_dragging_active = (GImGui != nullptr && ImGui::GetIO().MouseDown[0] && GImGui->ActiveId != 0) || m_navigator_dragging;
if (!has_mouse_capture() && imgui_dragging_active)
m_canvas->CaptureMouse();
// release capture as soon as the button goes up
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
render();
#ifdef SLIC3R_DEBUG_MOUSE_EVENTS
printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str());
@@ -4284,6 +4321,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_main_toolbar.on_mouse(evt2, *this);
}
// ORCA keep tracking mouse position while drag active and cursor not in window bounds
if (!has_mouse_capture() && evt.LeftIsDown() && m_gizmos.is_dragging())
m_canvas->CaptureMouse();
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
@@ -4389,6 +4430,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Start editing the layer height.
m_layers_editing.state = LayersEditing::Editing;
_perform_layer_editing_action(&evt);
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
}
else {
@@ -4402,6 +4446,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
&& m_gizmos.get_current_type() != GLGizmosManager::MmSegmentation
&& m_gizmos.get_current_type() != GLGizmosManager::FuzzySkin) {
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
m_dirty = true;
}
}
@@ -4469,6 +4517,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.drag.start_position_3D = m_mouse.scene_position;
m_sequential_print_clearance_first_displacement = true;
m_moving = true;
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
}
}
}
@@ -4477,6 +4528,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if (evt.Dragging() && evt.LeftIsDown() && m_mouse.drag.move_volume_idx != -1 && m_layers_editing.state == LayersEditing::Unknown) {
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
if (!m_mouse.drag.move_requires_threshold) {
m_mouse.dragging = true;
Vec3d cur_pos = m_mouse.drag.start_position_3D;
@@ -4528,6 +4583,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) {
//BBS not in assemble view
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
m_rectangle_selection.dragging(pos.cast<double>());
m_dirty = true;
}
@@ -4537,12 +4596,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) {
if (m_layers_editing.state == LayersEditing::Editing) {
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
_perform_layer_editing_action(&evt);
m_mouse.position = pos.cast<double>();
}
}
// do not process the dragging if the left mouse was set down in another canvas
else if (is_camera_rotate(evt, button_mappings)) {
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
// Orca: Sphere rotation for painting view
// if dragging over blank area with left button or other button mapped to rotate, then rotate
bool middle_or_right_button_used_as_rotate = (evt.MiddleIsDown() && button_mappings[MouseButton::Middle] == MouseAction::Rotation) ||
@@ -4622,6 +4688,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0);
}
else if (is_camera_pan(evt, button_mappings)) {
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
m_canvas->CaptureMouse();
// if dragging with right button or if button functions swapped and dragging with left button over blank area then pan
if (m_mouse.is_start_position_2D_defined()) {
// get point in model space at Z = 0
@@ -4686,7 +4756,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
deselect_all();
}
//BBS Select plate in this 3D canvas.
else if (evt.LeftUp() && !m_mouse.dragging && m_picking_enabled && !m_hover_plate_idxs.empty() && (m_canvas_type == CanvasView3D) && !is_layers_editing_enabled())
// The left up may come from an ImGui window (e.g. a drag started on the gizmo floating window and released over the bed),
// in which case it must not be treated as a click on the plate, otherwise the gizmo would be closed (see deselect_all below).
else if (evt.LeftUp() && !m_mouse.ignore_left_up && !m_mouse.dragging && m_picking_enabled && !m_hover_plate_idxs.empty() && (m_canvas_type == CanvasView3D) && !is_layers_editing_enabled())
{
int hover_idx = m_hover_plate_idxs.front();
wxGetApp().plater()->select_plate_by_hover_id(hover_idx);
@@ -6033,9 +6105,18 @@ void GLCanvas3D::_render_3d_navigator()
{
if (!wxGetApp().show_3d_navigator()) {
m_canvas_toolbar_pos[0] = 0;
m_navigator_dragging = false;
return;
}
// Fix stealing capture event from other drag events
const bool other_drag_active = !m_navigator_dragging && (m_moving || m_rectangle_selection.is_dragging() || m_gizmos.is_dragging() || m_layers_editing.state == LayersEditing::Editing);
ImGuiIO& io = ImGui::GetIO();
const bool saved_mouse_down0 = io.MouseDown[0];
if (other_drag_active)
io.MouseDown[0] = false;
ImGuizmo::BeginFrame();
auto& style = ImGuizmo::GetStyle();
@@ -6060,7 +6141,6 @@ void GLCanvas3D::_render_3d_navigator()
sc *= (float) dpi / (float) DPI_DEFAULT;
#endif // WIN32
const ImGuiIO& io = ImGui::GetIO();
const float viewManipulateLeft = 0;
const float viewManipulateTop = io.DisplaySize.y;
const float camDistance = 8.f;
@@ -6084,6 +6164,10 @@ void GLCanvas3D::_render_3d_navigator()
camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size), ImVec2(size, size),
0x00101010);
// Restore the real mouse-down state
if (other_drag_active)
io.MouseDown[0] = saved_mouse_down0;
if (result.changed) {
for (unsigned int c = 0; c < 4; ++c) {
for (unsigned int r = 0; r < 4; ++r) {
@@ -6115,6 +6199,8 @@ void GLCanvas3D::_render_3d_navigator()
request_extra_frame();
}
m_navigator_dragging = result.dragging;
}
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0
@@ -9226,8 +9312,12 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
//ORCA ImGui::IsWindowHovered() returns false when left_down events on buttons that causes scrollbar disappears for a short time
auto win_pos = ImGui::GetWindowPos();
bool is_win_hovered = ImGui::IsMouseHoveringRect(win_pos, win_pos + ImVec2(window_width + (show_scroll ? scrollbar_size : 0), window_height), !show_scroll); // use non clipped rectangle to reserve clickable area for scrollbar track
m_sel_plate_toolbar.is_display_scrollbar = is_win_hovered;
bool is_win_hovered = ImGui::IsMouseHoveringRect(win_pos, win_pos + ImVec2(window_width + (show_scroll ? scrollbar_size : 0), window_height), !show_scroll);
// Also show scrollbar visible and continue to capture mouse position
const bool is_scrollbar_active_drag = GImGui != nullptr && ImGui::GetIO().MouseDown[0] && GImGui->ActiveId != 0 && GImGui->ActiveIdWindow == ImGui::GetCurrentWindow();
m_sel_plate_toolbar.is_display_scrollbar = is_win_hovered || is_scrollbar_active_drag;
imgui.end();
}
+6
View File
@@ -589,6 +589,7 @@ private:
bool m_toolpath_outside{ false };
ECursorType m_cursor_type;
GLSelectionRectangle m_rectangle_selection;
bool m_navigator_dragging{ false };
//BBS:add plate related logic
mutable std::vector<int> m_hover_volume_idxs;
@@ -916,6 +917,7 @@ public:
void update_volumes_colors_by_extruder();
bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; }
bool has_mouse_capture() const;
void render(bool only_init = false);
bool is_rendering_enabled()
@@ -1117,6 +1119,10 @@ public:
void set_mouse_as_dragging() { m_mouse.dragging = true; }
bool is_mouse_dragging() const { return m_mouse.dragging; }
// True when the current left up event comes from an ImGui window and was not processed by it
// (e.g. a drag that started on a gizmo floating window and was released over the 3D scene).
// Such a release is the end of an ImGui interaction, not a click on the scene.
bool is_mouse_left_up_ignored() const { return m_mouse.ignore_left_up; }
double get_size_proportional_to_max_bed_size(double factor) const;
+4 -4
View File
@@ -256,18 +256,18 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
}
}
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font)
void show_error(wxWindow* parent, const wxString& message, bool has_code_excerpts)
{
wxGetApp().CallAfter([=] {
ErrorDialog msg(parent, message, monospaced_font);
ErrorDialog msg(parent, message, has_code_excerpts);
msg.ShowModal();
});
}
void show_error(wxWindow* parent, const char* message, bool monospaced_font)
void show_error(wxWindow* parent, const char* message, bool has_code_excerpts)
{
assert(message);
show_error(parent, wxString::FromUTF8(message), monospaced_font);
show_error(parent, wxString::FromUTF8(message), has_code_excerpts);
}
void show_error_id(int id, const std::string& message)
+5 -5
View File
@@ -40,11 +40,11 @@ extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_
// Change option value in config
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
// If monospaced_font is true, the error message is displayed using html <code><pre></pre></code> tags,
// so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font = false);
void show_error(wxWindow* parent, const char* message, bool monospaced_font = false);
inline void show_error(wxWindow* parent, const std::string& message, bool monospaced_font = false) { show_error(parent, message.c_str(), monospaced_font); }
// If has_code_excerpts is true, code excerpts (a source line and the caret line below it) render
// monospaced so the caret aligns. Used for placeholder-parser errors.
void show_error(wxWindow* parent, const wxString& message, bool has_code_excerpts = false);
void show_error(wxWindow* parent, const char* message, bool has_code_excerpts = false);
inline void show_error(wxWindow* parent, const std::string& message, bool has_code_excerpts = false) { show_error(parent, message.c_str(), has_code_excerpts); }
void show_error_id(int id, const std::string& message); // For Perl
void show_info(wxWindow* parent, const wxString& message, const wxString& title = wxString());
void show_info(wxWindow* parent, const char* message, const char* title = nullptr);
+113 -19
View File
@@ -307,6 +307,20 @@ public:
#endif // !__APPLE__
)
{
// Some desktop environments ignore splash screen typed window properties
// when running the app through Wayland,resulting in the titlebar being shown
// on the splash screen. The code below creates a client-side window decoration
// when running on Wayland and then removes that decoration. This ensures every
// environment correctly targets and removes the titlebar for this screen.
#if defined(__WXGTK__)
if (Slic3r::GUI::is_running_on_wayland()) {
GtkWidget *empty = gtk_fixed_new();
gtk_widget_set_size_request(empty, 0, 0);
gtk_window_set_titlebar(GTK_WINDOW(GetHandle()), empty);
gtk_window_set_decorated(GTK_WINDOW(GetHandle()), false);
}
#endif
this->SetPosition(pos);
this->CenterOnScreen();
@@ -2795,6 +2809,45 @@ void GUI_App::init_plugin_gui_wiring()
});
};
// why: a newly loaded plugin only adds a selectable agent
// refresh the dropdown and leave the live agent alone
auto refresh_printer_agent_dropdown_after_load = [](const std::string&)
{
if (!wxTheApp)
return;
GUI_App* app = &GUI::wxGetApp();
if (app->is_closing())
return;
app->CallAfter([app]
{
if (!app->is_closing())
app->refresh_printer_agent_dropdown();
});
};
// why: the unloaded plugin may have been the provider of the live agent
// re-run selection, where a now-missing agent will be cleared
// refresh dropdown after
auto switch_printer_agent_after_unload = [](const std::string&)
{
if (!wxTheApp)
return;
GUI_App* app = &GUI::wxGetApp();
if (app->is_closing())
return;
app->CallAfter([app] {
if (app->is_closing())
return;
app->switch_printer_agent();
app->refresh_printer_agent_dropdown();
});
};
plugin_mgr.subscribe_on_unload_callback(PluginHostUi::close_windows_for_plugin);
plugin_mgr.subscribe_on_load_callback([refresh_plugins_dialog](const std::string&) { refresh_plugins_dialog(); });
plugin_mgr.subscribe_on_unload_callback([refresh_plugins_dialog](const std::string&) { refresh_plugins_dialog(); });
@@ -2810,11 +2863,14 @@ void GUI_App::init_plugin_gui_wiring()
return;
wxGetApp().mainframe->plugin_pages().on_plugin_deregister(plugin_key);
});
plugin_mgr.subscribe_on_load_callback(refresh_printer_agent_dropdown_after_load);
plugin_mgr.subscribe_on_unload_callback(switch_printer_agent_after_unload);
plugin_mgr.subscribe_on_capability_load_callback(
[refresh_plugins_dialog](const PluginCapabilityId& capability) {
[refresh_plugins_dialog, refresh_printer_agent_dropdown_after_load](const PluginCapabilityId& capability) {
if (capability.type == PluginCapabilityType::PrinterConnection)
NetworkAgentFactory::register_python_printer_agent(capability.plugin_key, capability.name);
refresh_plugins_dialog();
refresh_printer_agent_dropdown_after_load(capability.plugin_key);
// A newly loaded capability may satisfy a missing-plugin notification; re-validate the
// current plate (on the UI thread) so the notification clears once its plugin is available.
if (wxTheApp && !wxGetApp().is_closing())
@@ -2826,12 +2882,13 @@ void GUI_App::init_plugin_gui_wiring()
wxGetApp().mainframe->plugin_pages().on_cap_register(capability);
});
plugin_mgr.subscribe_on_capability_unload_callback(
[refresh_plugins_dialog](const PluginCapabilityId& capability) {
[refresh_plugins_dialog, switch_printer_agent_after_unload](const PluginCapabilityId& capability) {
if (capability.type == PluginCapabilityType::PrinterConnection)
NetworkAgentFactory::deregister_python_printer_agent(capability.plugin_key, capability.name);
if (capability.type == PluginCapabilityType::Pages && wxTheApp && !wxGetApp().is_closing() && wxGetApp().mainframe)
wxGetApp().mainframe->plugin_pages().on_cap_deregister(capability);
refresh_plugins_dialog();
switch_printer_agent_after_unload(capability.plugin_key);
});
}
@@ -3873,6 +3930,48 @@ unsigned GUI_App::get_colour_approx_luma(const wxColour &colour)
));
}
void GUI_App::refresh_printer_agent_dropdown()
{
if (Tab* tab = get_tab(Preset::TYPE_PRINTER))
{
if (auto* printer_tab = dynamic_cast<TabPrinter*>(tab))
printer_tab->refresh_printer_agent_dropdown();
}
}
void GUI_App::set_live_printer_agent(std::shared_ptr<IPrinterAgent> agent)
{
if (!m_agent)
return;
// why: tearing down the old machine selection is only ever the prefix of setting the live
// agent (to a new one, or to null when the selection is missing) - so it lives here, not as
// a standalone helper. Pass nullptr to clear the selection.
if (DeviceManager* dev = getDeviceManager())
{
dev->set_selected_machine(""); // why: empty id disconnects and deselects the current machine
m_agent->set_user_selected_machine("");
// note: belt-and-suspenders (precedent: DeviceManagerRefresher::on_timer)
dev->OnSelectedMachineLost(); // why: clear stale sidebar sync-status / AMS
dev->clear_other_devices(); // why: drop stale LAN discoveries; keep My Devices
}
m_agent->set_printer_agent(agent);
sidebar().update_all_preset_comboboxes();
}
std::string GUI_App::resolve_printer_agent_id(const std::string& stored_id)
{
if (!stored_id.empty())
return stored_id;
return (preset_bundle && preset_bundle->is_bbl_vendor()) ? BBL_PRINTER_AGENT_ID : ORCA_PRINTER_AGENT_ID;
}
std::string GUI_App::canonical_printer_agent_id(const std::string& picked_id)
{
return picked_id == resolve_printer_agent_id("") ? std::string() : picked_id;
}
void GUI_App::switch_printer_agent()
{
if (!m_agent) {
@@ -3880,24 +3979,17 @@ void GUI_App::switch_printer_agent()
return;
}
// Read printer_agent from config, falling back to default
std::string effective_agent_id = ORCA_PRINTER_AGENT_ID;
if (preset_bundle->is_bbl_vendor())
effective_agent_id = BBL_PRINTER_AGENT_ID;
const DynamicPrintConfig& config = preset_bundle->printers.get_edited_preset().config;
if (config.has("printer_agent")) {
const std::string& value = config.option<ConfigOptionString>("printer_agent")->value;
if (!value.empty())
effective_agent_id = value;
}
const std::string effective_agent_id = resolve_printer_agent_id(config.opt_string("printer_agent"));
// Check if agent is registered
const PrinterAgentInfo* agent_info_ptr = NetworkAgentFactory::get_printer_agent_info(effective_agent_id);
if (!agent_info_ptr) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": unregistered agent ID '" << effective_agent_id
<< "', keeping current agent";
// Keep current agent, don't switch
// why: the selected agent's provider is gone (e.g. plugin unloaded); leaving the old
// live agent up would keep talking to a machine the user can no longer select.
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": agent ID '" << effective_agent_id
<< "' is unregistered; clearing live printer agent";
set_live_printer_agent(nullptr);
return;
}
const PrinterAgentInfo agent_info = *agent_info_ptr;
@@ -3911,7 +4003,9 @@ void GUI_App::switch_printer_agent()
NetworkAgentFactory::create_printer_agent_by_id(effective_agent_id, cloud_agent, log_dir);
if (!new_printer_agent) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": failed to create agent '" << effective_agent_id << "', keeping current agent";
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": failed to create agent '" << effective_agent_id
<< "'; clearing live printer agent";
set_live_printer_agent(nullptr);
return;
}
@@ -3934,9 +4028,9 @@ void GUI_App::switch_printer_agent()
return;
}
// Swap the agent
m_agent->set_printer_agent(new_printer_agent);
sidebar().update_all_preset_comboboxes();
// Swap the agent; set_live_printer_agent resets the device selection so the new
// agent starts clean (#124).
set_live_printer_agent(new_printer_agent);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": printer agent switched to " << effective_agent_id;
+11 -1
View File
@@ -365,9 +365,14 @@ public:
HMSQuery* get_hms_query() { return hms_query; }
NetworkAgent* getAgent() { return m_agent; }
// Dynamic printer agent switching
// Reconcile the live printer agent with the stored preset selection.
void switch_printer_agent();
std::string resolve_printer_agent_id(const std::string& stored_id);
// ORCA TODO: in the future, bbl presets should specify "bbl" printer agent id
// then, all resolve and canonical would just be ORCA<->""
std::string canonical_printer_agent_id(const std::string& picked_id);
FilamentColorCodeQuery* get_filament_color_code_query();
bool is_editor() const { return m_app_mode == EAppMode::Editor; }
bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; }
@@ -798,6 +803,11 @@ private:
void window_pos_center(wxTopLevelWindow *window);
bool select_language();
// Dynamic printer agent selection - internal helpers for switch_printer_agent
// and the plugin load/unload callbacks (init_plugin_gui_wiring).
void refresh_printer_agent_dropdown();
void set_live_printer_agent(std::shared_ptr<IPrinterAgent> agent); // null clears the selection
bool config_wizard_startup();
void check_updates(const bool verbose);
+1
View File
@@ -123,6 +123,7 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CATE
{"sparse_infill_density", "", 1},
{"fill_multiline", "", 1},
{"sparse_infill_pattern", "", 1},
{"sparse_infill_smooth_factor", "", 1},
{"lateral_lattice_angle_1", "", 1},
{"lateral_lattice_angle_2", "", 1},
{"infill_overhang_angle", "", 1},
+1 -1
View File
@@ -3213,7 +3213,7 @@ void ObjectList::merge(bool to_multipart_object)
//changed_object(obj_idx);
//remove();
}
/* wxGetApp().plater()->load_model_objects(objects);
// wxGetApp().plater()->load_model_objects(objects);
Selection& selection = p->view3D->get_canvas3d()->get_selection();
size_t last_obj_idx = p->model.objects.size() - 1;
+53 -1
View File
@@ -548,7 +548,7 @@ void RemoveButtonBorder(wxWindow* win)
GtkCssProvider* provider = gtk_css_provider_new();
const char* css =
"button {"
"button, button:hover, button:active, button:focus {"
" border: none;"
" outline: none;"
" box-shadow: none;"
@@ -589,6 +589,58 @@ void RemoveButtonBorder(wxWindow* win)
);
#endif
}
void RemoveInputBorder(wxWindow* win)
{
GtkWidget* widget = win->GetHandle();
if (!widget) return;
#if GTK_CHECK_VERSION(3, 0, 0)
// GTK3+: use CSS provider
GtkCssProvider* provider = gtk_css_provider_new();
// Target 'entry' and its inner subnodes (like text selection areas)
const char* css =
"entry, entry text, entry undershoot {"
" border: none;"
" outline: none;"
" box-shadow: none;"
" padding: 0px;"
" margin: 0px;"
" min-height: 0px;"
" min-width: 0px;"
" background: none;"
"}";
#if GTK_CHECK_VERSION(4, 0, 0)
// GTK4
gtk_css_provider_load_from_data(provider, css, -1);
#else
// GTK3
gtk_css_provider_load_from_data(provider, css, -1, nullptr);
#endif
GtkStyleContext* ctx = gtk_widget_get_style_context(widget);
gtk_style_context_add_provider(
ctx,
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER
);
g_object_unref(provider);
#else
// GTK2: Target the x/y thickness of the entry widget
gtk_rc_parse_string(
"style \"no-padding-entry\" {"
" xthickness = 0"
" ythickness = 0"
" GtkEntry::inner-border = { 0, 0, 0, 0 }"
" GtkEntry::focus-line-width = 0"
"}"
"class \"GtkEntry\" style \"no-padding-entry\""
);
#endif
}
#endif // __WXGTK__
#ifdef __linux__
+18 -12
View File
@@ -113,14 +113,7 @@ public:
update_dark_ui(this);
#endif
// Linux specific issue : get_dpi_for_window(this) still doesn't responce to the Display's scale in new wxWidgets(3.1.3).
// So, calculate the m_em_unit value from the font size, as before
#if !defined(__WXGTK__)
m_em_unit = std::max<size_t>(10, 10.0f * m_scale_factor);
#else
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
#endif // __WXGTK__
update_em_unit();
// recalc_font();
@@ -235,6 +228,19 @@ private:
// m_em_unit = metrics.averageWidth;
// }
// update em_unit value for new window font
void update_em_unit()
{
// Linux specific issue : get_dpi_for_window(this) still doesn't responce to the Display's scale in new wxWidgets(3.1.3).
// So, calculate the m_em_unit value from the font size, as before
#if !defined(__WXGTK__)
m_em_unit = std::max<size_t>(10, 10.0f * m_scale_factor);
#else
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
#endif // __WXGTK__
}
// check if new scale is differ from previous
bool is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }
@@ -247,8 +253,7 @@ private:
// set normal application font as a current window font
m_normal_font = this->GetFont();
// update em_unit value for new window font
m_em_unit = std::max<int>(10, 10.0f * m_scale_factor);
update_em_unit();
// rescale missed controls sizes and images
on_dpi_changed(suggested_rect);
@@ -472,8 +477,9 @@ void dataview_remove_insets(wxDataViewCtrl* dv);
void staticbox_remove_margin(wxStaticBox* sb);
#endif
#ifdef __WXGTK3__
void RemoveButtonBorder(wxWindow* win);
#ifdef __WXGTK__
void RemoveButtonBorder(wxWindow* win); // for wxButton/wxBitmapToggleButton based controls (SwitchButton, CheckBox)
void RemoveInputBorder(wxWindow* win); // for TextCtrl based controls (TextInput, ComboBox, SpinInput..)
#endif
#if defined(__WXOSX__) || defined(__linux__)
+1 -1
View File
@@ -442,7 +442,7 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
}
} else if (m_dragging) {
// when mouse cursor leave window than finish actual dragging operation
bool is_leaving = mouse_event.Leaving();
bool is_leaving = mouse_event.Leaving() && !m_parent.has_mouse_capture(); // ORCA keep tracking mouse position while drag active and cursor not in window bounds
if (mouse_event.Dragging()) {
Point mouse_coord(mouse_event.GetX(), mouse_event.GetY());
auto ray = m_parent.mouse_ray(mouse_coord);
+37 -33
View File
@@ -15,6 +15,8 @@ static const ColorRGBA DEF_COLOR = {0.7f, 0.7f, 0.7f, 1.f};
static const ColorRGBA SELECTED_COLOR = {0.0f, 0.5f, 0.5f, 1.0f};
static const ColorRGBA ERR_COLOR = {1.0f, 0.3f, 0.3f, 0.5f};
static const ColorRGBA HOVER_COLOR = {0.7f, 0.7f, 0.7f, 0.5f};
static constexpr float BRIM_EAR_RADIUS_MIN = 0.1f;
static constexpr float BRIM_EAR_RADIUS_MAX = 100.f;
static ModelVolume *get_model_volume(const Selection &selection, Model &model)
{
@@ -41,14 +43,14 @@ GLGizmoBrimEars::GLGizmoBrimEars(GLCanvas3D &parent, const std::string &icon_fil
bool GLGizmoBrimEars::on_init()
{
m_new_point_head_diameter = get_brim_default_radius();
m_new_point_head_radius = get_brim_default_radius();
m_shortcut_key = WXK_CONTROL_E;
const wxString ctrl = GUI::shortkey_ctrl_prefix();
const wxString alt = GUI::shortkey_alt_prefix();
m_desc["head_diameter"] = _L("Head diameter");
m_desc["brim_ear_radius"] = _L("Brim ear radius");
m_desc["max_angle"] = _L("Max angle");
m_desc["detection_radius"] = _L("Detection radius");
m_desc["remove"] = _L("Remove");
@@ -62,7 +64,7 @@ bool GLGizmoBrimEars::on_init()
m_shortcuts = {
{_L("Left mouse button"), _L("Add or Select")},
{_L("Right mouse button"), _L("Remove")},
{ctrl + _L("Mouse wheel"), m_desc["head_diameter"]},
{ctrl + _L("Mouse wheel"), m_desc["brim_ear_radius"]},
{alt + _L("Mouse wheel"), m_desc["section_view"]},
};
@@ -358,7 +360,7 @@ bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_p
Transform3d inverse_trsf = volume->get_instance_transformation().get_matrix_no_offset().inverse();
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh2(mouse_position, pos_and_normal)) {
render_hover_point = CacheEntry(BrimPoint(pos_and_normal.first, m_new_point_head_diameter / 2.f), false, (inverse_trsf * m_world_normal).cast<float>(), true);
render_hover_point = CacheEntry(BrimPoint(pos_and_normal.first, m_new_point_head_radius), false, (inverse_trsf * m_world_normal).cast<float>(), true);
} else {
render_hover_point.reset();
}
@@ -397,7 +399,7 @@ bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_p
Vec3d object_pos = trsf.inverse() * world_pos;
// brim ear always face up
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Add brim ear");
add_point_to_cache(object_pos.cast<float>(), m_new_point_head_diameter / 2.f, false, (inverse_trsf * m_world_normal).cast<float>());
add_point_to_cache(object_pos.cast<float>(), m_new_point_head_radius, false, (inverse_trsf * m_world_normal).cast<float>());
m_parent.set_as_dirty();
m_wait_for_up_event = true;
find_single();
@@ -490,9 +492,9 @@ bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_p
// mouse wheel up
if (action == SLAGizmoEventType::MouseWheelUp) {
if (control_down) {
float initial_value = m_new_point_head_diameter;
float initial_value = m_new_point_head_radius;
begin_radius_change(initial_value);
m_new_point_head_diameter = std::min(20., initial_value + 0.1);
m_new_point_head_radius = std::min(BRIM_EAR_RADIUS_MAX, initial_value + 0.1f);
update_cache_radius();
return true;
}
@@ -502,9 +504,9 @@ bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_p
if (action == SLAGizmoEventType::MouseWheelDown) {
if (control_down) {
float initial_value = m_new_point_head_diameter;
float initial_value = m_new_point_head_radius;
begin_radius_change(initial_value);
m_new_point_head_diameter = std::max(5., initial_value - 0.1);
m_new_point_head_radius = std::max(BRIM_EAR_RADIUS_MIN, initial_value - 0.1f);
update_cache_radius();
return true;
}
@@ -597,18 +599,18 @@ std::vector<const ConfigOption *> GLGizmoBrimEars::get_config_options(const std:
void GLGizmoBrimEars::begin_radius_change(float initial_value)
{
if (m_old_point_head_diameter == 0.f)
m_old_point_head_diameter = initial_value;
if (m_old_point_head_radius == 0.f)
m_old_point_head_radius = initial_value;
}
void GLGizmoBrimEars::update_cache_radius()
{
if (render_hover_point)
render_hover_point->brim_point.head_front_radius = m_new_point_head_diameter / 2.f;
render_hover_point->brim_point.head_front_radius = m_new_point_head_radius;
for (auto &cache_entry : m_editing_cache)
if (cache_entry.selected) {
cache_entry.brim_point.head_front_radius = m_new_point_head_diameter / 2.f;
cache_entry.brim_point.head_front_radius = m_new_point_head_radius;
find_single();
update_model_object();
}
@@ -617,18 +619,18 @@ void GLGizmoBrimEars::update_cache_radius()
void GLGizmoBrimEars::apply_radius_change()
{
if (m_old_point_head_diameter == 0.f) return;
if (m_old_point_head_radius == 0.f) return;
// momentarily restore the old value to take snapshot
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.brim_point.head_front_radius = m_old_point_head_diameter / 2.f;
float backup = m_new_point_head_diameter;
m_new_point_head_diameter = m_old_point_head_diameter;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Change point head diameter");
m_new_point_head_diameter = backup;
cache_entry.brim_point.head_front_radius = m_old_point_head_radius;
float backup = m_new_point_head_radius;
m_new_point_head_radius = m_old_point_head_radius;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Change brim ear radius");
m_new_point_head_radius = backup;
update_cache_radius();
m_old_point_head_diameter = 0.f;
m_old_point_head_radius = 0.f;
}
void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limit)
@@ -653,7 +655,7 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
float space_size = m_imgui->get_style_scaling() * 8;
std::vector<wxString> text_list = {m_desc["head_diameter"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"],
std::vector<wxString> text_list = {m_desc["brim_ear_radius"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"],
m_desc["create"], m_desc["remove"]};
float widest_text = m_imgui->find_widest_text(text_list);
float caption_size = widest_text + space_size + ImGui::GetStyle().WindowPadding.x;
@@ -680,11 +682,11 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider
ImGui::AlignTextToFramePadding();
float initial_value = m_new_point_head_diameter;
m_imgui->text(m_desc["head_diameter"]);
float initial_value = m_new_point_head_radius;
m_imgui->text(m_desc["brim_ear_radius"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
m_imgui->bbl_slider_float_style("##head_diameter", &m_new_point_head_diameter, 5, 20, "%.1f", 1.0f, true);
m_imgui->bbl_slider_float_style("##brim_ear_radius", &m_new_point_head_radius, BRIM_EAR_RADIUS_MIN, BRIM_EAR_RADIUS_MAX, "%.1f", 1.0f, true);
if (m_imgui->get_last_slider_status().clicked) {
begin_radius_change(initial_value);
}
@@ -695,7 +697,7 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
}
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##head_diameter_input", &m_new_point_head_diameter, 0.05f, 0.0f, 0.0f, "%.1f");
ImGui::BBLDragFloat("##brim_ear_radius_input", &m_new_point_head_radius, 0.05f, BRIM_EAR_RADIUS_MIN, BRIM_EAR_RADIUS_MAX, "%.1f");
ImGui::Separator();
@@ -910,9 +912,9 @@ void GLGizmoBrimEars::on_stop_dragging()
m_point_before_drag = CacheEntry();
}
void GLGizmoBrimEars::on_load(cereal::BinaryInputArchive &ar) { ar(m_new_point_head_diameter, m_editing_cache, m_selection_empty); }
void GLGizmoBrimEars::on_load(cereal::BinaryInputArchive &ar) { ar(m_new_point_head_radius, m_editing_cache, m_selection_empty); }
void GLGizmoBrimEars::on_save(cereal::BinaryOutputArchive &ar) const { ar(m_new_point_head_diameter, m_editing_cache, m_selection_empty); }
void GLGizmoBrimEars::on_save(cereal::BinaryOutputArchive &ar) const { ar(m_new_point_head_radius, m_editing_cache, m_selection_empty); }
void GLGizmoBrimEars::select_point(int i)
{
@@ -920,11 +922,11 @@ void GLGizmoBrimEars::select_point(int i)
for (auto &point_and_selection : m_editing_cache) point_and_selection.selected = (i == AllPoints);
m_selection_empty = (i == NoPoints);
if (i == AllPoints) m_new_point_head_diameter = m_editing_cache[0].brim_point.head_front_radius * 2.f;
if (i == AllPoints) m_new_point_head_radius = m_editing_cache[0].brim_point.head_front_radius;
} else {
m_editing_cache[i].selected = true;
m_selection_empty = false;
m_new_point_head_diameter = m_editing_cache[i].brim_point.head_front_radius * 2.f;
m_new_point_head_radius = m_editing_cache[i].brim_point.head_front_radius;
}
}
@@ -1011,8 +1013,7 @@ void GLGizmoBrimEars::auto_generate()
auto add_point = [this, &trsf, &normal](const Point &p) {
Vec3d world_pos = {float(p.x() * SCALING_FACTOR), float(p.y() * SCALING_FACTOR), -0.0001};
Vec3d object_pos = trsf.inverse() * world_pos;
// m_editing_cache.emplace_back(BrimPoint(object_pos.cast<float>(), m_new_point_head_diameter / 2), false, normal);
add_point_to_cache(object_pos.cast<float>(), m_new_point_head_diameter / 2, false, normal);
add_point_to_cache(object_pos.cast<float>(), m_new_point_head_radius, false, normal);
};
for (const ExPolygon &ex_poly : m_first_layer) {
Polygon out_poly = ex_poly.contour;
@@ -1158,8 +1159,11 @@ void GLGizmoBrimEars::reset_all_pick() { std::map<GLVolume *, std::shared_ptr<Pi
float GLGizmoBrimEars::get_brim_default_radius() const
{
const double nozzle_diameter = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0);
const DynamicPrintConfig &pring_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
return pring_cfg.get_abs_value("initial_layer_line_width", nozzle_diameter) * 16.0f;
const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
return std::clamp(
float(print_cfg.get_abs_value("initial_layer_line_width", nozzle_diameter) * 8.0),
BRIM_EAR_RADIUS_MIN,
BRIM_EAR_RADIUS_MAX);
}
ExPolygon GLGizmoBrimEars::make_polygon(BrimPoint point, const Geometry::Transformation &trsf)
+2 -2
View File
@@ -98,12 +98,12 @@ private:
void render_points(const Selection& selection);
float m_new_point_head_diameter; // Size of a new point.
float m_new_point_head_radius; // Radius of a new point.
float m_max_angle = 125.f;
float m_detection_radius = 1.f;
double m_detection_radius_max = .0f;
CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited
float m_old_point_head_diameter = 0.; // the same
float m_old_point_head_radius = 0.; // the same
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selectedchanges or undo/redo
std::map<int, CacheEntry> m_single_brim;
ObjectID m_old_mo_id;
+5 -2
View File
@@ -566,8 +566,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
void GLGizmoEmboss::on_mouse_change_selection(const wxMouseEvent &mouse_event)
{
static bool was_dragging = true;
if ((mouse_event.LeftUp() || mouse_event.RightUp()) && !was_dragging) {
static bool was_dragging = true;
// The left up may be the end of a drag that started on the gizmo floating window (e.g. selecting
// text in the input field). Such a release is not a click on the scene and must not close the gizmo.
// (The flag is only set for left up events, so right up behavior is unchanged.)
if ((mouse_event.LeftUp() || mouse_event.RightUp()) && !was_dragging && !m_parent.is_mouse_left_up_ignored()) {
// is hovered volume closest hovered?
int hovered_idx = m_parent.get_first_hover_volume_idx();
if (hovered_idx < 0)
+1 -1
View File
@@ -1733,7 +1733,7 @@ std::string IMSlider::get_label(int tick, LabelType label_type)
::sprintf(layer_height, "%.2f", m_values.empty() ? m_label_koef * value : m_values[value]);
if (label_type == ltHeight) return std::string(layer_height);
if (label_type == ltHeightWithLayer) {
char buffer[64];
char buffer[90];
size_t layer_number;
layer_number = m_draw_mode == dmSequentialFffPrint ? (m_values.empty() ? value : value + 1) : m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1);
::sprintf(buffer, "%5s\n%5s", std::to_string(layer_number).c_str(), layer_height);
+1 -1
View File
@@ -548,7 +548,7 @@ void ArrangeJob::process(Ctl &ctl)
params.stopcondition = [&ctl]() { return ctl.was_canceled(); };
params.progressind = [this, &ctl](unsigned num_finished, std::string str = "") {
ctl.update_status(num_finished * 100 / status_range(), _u8L("Arranging") + str);
ctl.update_status(num_finished * 100 / status_range(), _u8L("Arranging ") + str);
};
{
+41 -2
View File
@@ -149,6 +149,46 @@ void OrientJob::prepare()
}
}
/// parameters to minimize support area
static void setMinimalSupportAreaPrams(Slic3r::orientation::OrientParams &out)
{
out.TAR_A = 0.015f;
out.TAR_B = 0.177f;
out.RELATIVE_F = 20;
out.CONTOUR_F = 0.5f;
out.BOTTOM_F = 2.5f;
out.BOTTOM_HULL_F = 0.1f;
out.TAR_C = 0.1f;
out.TAR_D = 1;
out.TAR_E = 0.0115f;
out.FIRST_LAY_H = 0.2f; // 0.0475;
out.VECTOR_TOL = -0.00083f;
out.NEGL_FACE_SIZE = 0.01f;
out.ASCENT = -0.5f;
out.PLAFOND_ADV = 0.0599f;
out.CONTOUR_AMOUNT = 0.0182427f;
out.OV_H = 2.574f;
out.height_offset = 2.3728f;
out.height_log = 0.041375f;
out.height_log_k = 1.9325457f;
out.LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face 0.9997f
out.LAF_MIN = 0.97f; // cos(14\degree) 0.9703f
out.TAR_LAF = 0.001f; // 0.01f
out.TAR_PROJ_AREA = 0.1f;
out.BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the object may be unstable
out.BOTTOM_MAX = 2000; // max bottom area. If get to it the object is stable enough (further increase bottom area won't do more help)
out.height_to_bottom_hull_ratio_MIN = 1,
out.BOTTOM_HULL_MAX = 2000; // max bottom hull area
out.APPERANCE_FACE_SUPP = 3; // penalty of generating supports on appearance face
out.overhang_angle = 60.f;
out.use_low_angle_face = true;
out.min_volume = false;
out.fun_dir = {};
out.parallel = true;
out.progressind = {};
out.stopcondition = {};
}
void OrientJob::process(Ctl &ctl)
{
static const auto arrangestr = _u8L("Orienting...");
@@ -161,9 +201,8 @@ void OrientJob::process(Ctl &ctl)
const GLCanvas3D::OrientSettings& settings = m_plater->canvas3D()->get_orient_settings();
orientation::OrientParams params;
orientation::OrientParamsArea params_area;
if (settings.min_area) {
memcpy(&params, &params_area, sizeof(params));
setMinimalSupportAreaPrams(params);
params.min_volume = false;
}
else {
+86 -6
View File
@@ -707,7 +707,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
if (m_print_enable) {
if (wxGetApp().preset_bundle->use_bbl_network())
if (wxGetApp().preset_bundle->use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents"))
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_PRINT_PLATE));
else
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_GCODE));
@@ -1377,9 +1377,88 @@ void MainFrame::init_tabpanel() {
}
// SoftFever
void MainFrame::show_device(bool bBBLPrinter) {
void MainFrame::show_device(bool should_use_native) {
auto idx = -1;
if (bBBLPrinter) {
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
// The legacy page is appended when printer agents are enabled. Remove that
// extra page before switching back to the normal native/legacy layout.
if (!use_printer_agents) {
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND && idx != tpMonitor) {
m_printer_view->Show(false);
m_tabpanel->RemovePage(idx);
}
}
if (use_printer_agents) {
if (!m_monitor) {
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_monitor->SetBackgroundColour(*wxWHITE);
}
if (m_tabpanel->FindPage(m_monitor) == wxNOT_FOUND) {
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) {
m_printer_view->Show(false);
m_tabpanel->RemovePage(idx);
}
m_monitor->Show(false);
m_tabpanel->InsertPage(tpMonitor, m_monitor, _L("Device"), std::string("tab_monitor_active"),
std::string("tab_monitor_active"));
}
if (m_printer_view == nullptr) {
m_printer_view = new PrinterWebView(m_tabpanel);
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) {
wxString url = evt.GetString();
wxString key = evt.GetAPIkey();
// select_tab(MainFrame::tpMonitor);
m_printer_view->load_url(url, key);
});
}
if (wxGetApp().is_enable_multi_machine()) {
if (!m_multi_machine) {
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_multi_machine->SetBackgroundColour(*wxWHITE);
}
// TODO: change the bitmap
if (m_tabpanel->FindPage(m_multi_machine) == wxNOT_FOUND) {
m_multi_machine->Show(false);
m_tabpanel->InsertPage(tpMultiDevice, m_multi_machine, _L("Multi-device"), std::string("tab_multi_active"),
std::string("tab_multi_active"), false);
}
}
if (!m_calibration) {
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_calibration->SetBackgroundColour(*wxWHITE);
}
// Calibration is always the last page, so don't use InsertPage here. Otherwise, if multi_machine page is not enabled,
// the calibration tab won't be properly added as well, due to the TabPosition::tpCalibration no longer matches the real tab position.
if (m_tabpanel->FindPage(m_calibration) == wxNOT_FOUND) {
m_calibration->Show(false);
m_tabpanel->AddPage(m_calibration, _L("Calibration"), std::string("tab_calibration_active"),
std::string("tab_calibration_active"), false);
}
if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) {
m_printer_view->Show(false);
m_tabpanel->AddPage(m_printer_view, _L("Device (legacy)"), std::string("tab_monitor_active"),
std::string("tab_monitor_active"), false);
} else {
m_tabpanel->SetPageText(idx, _L("Device (legacy)"));
}
#ifdef _MSW_DARK_MODE
wxGetApp().UpdateDarkUIWin(this);
#endif // _MSW_DARK_MODE
fit_tab_labels(); // ORCA on printer change
return;
}
if (should_use_native) {
if (m_tabpanel->FindPage(m_monitor) != wxNOT_FOUND) {
fit_tab_labels(); // ORCA on printer change - same button layout
return;
@@ -2018,7 +2097,8 @@ wxBoxSizer* MainFrame::create_side_tools()
SidePopup* p = new SidePopup(this);
if (wxGetApp().preset_bundle
&& !wxGetApp().preset_bundle->is_bbl_vendor()) {
&& !wxGetApp().preset_bundle->is_bbl_vendor()
&& !wxGetApp().app_config->get_bool("use_printer_agents")) {
// ThirdParty Buttons
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
export_gcode_btn->SetCornerRadius(0);
@@ -2151,7 +2231,7 @@ wxBoxSizer* MainFrame::create_side_tools()
const auto preset_bundle = wxGetApp().preset_bundle;
if (preset_bundle) {
if (preset_bundle->use_bbl_network()) {
if (preset_bundle->use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents")) {
// BBL network support everything
} else {
support_send = false; // All 3rd print hosts do not have the send options
@@ -4281,7 +4361,7 @@ void MainFrame::load_printer_url(wxString url, wxString apikey)
void MainFrame::load_printer_url()
{
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.use_bbl_device_tab() || NetworkAgentFactory::is_current_printer_agent_plugin())
if (preset_bundle.use_bbl_device_tab() || wxGetApp().app_config->get_bool("use_printer_agents"))
return;
auto cfg = preset_bundle.printers.get_edited_preset().config;
+1 -1
View File
@@ -356,7 +356,7 @@ public:
void RunScript(wxString js);
//SoftFever
void show_device(bool bBBLPrinter);
void show_device(bool should_use_native);
void fit_tab_labels(); // ORCA
PluginPages& plugin_pages() { return m_plugin_pages; }
+106 -18
View File
@@ -9,8 +9,13 @@
#include <wx/clipbrd.h>
#include <wx/checkbox.h>
#include <wx/html/htmlwin.h>
#include <wx/html/winpars.h>
#include <algorithm>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp"
@@ -229,12 +234,82 @@ void MsgDialog::finalize()
}
// A placeholder-parser caret line, pointing at the column where parsing failed.
static bool is_caret_line(const std::string &line)
{
return std::count(line.begin(), line.end(), '^') == 1 &&
std::all_of(line.begin(), line.end(), [](char c) { return c == ' ' || c == '^'; });
}
// Tag each line as a code excerpt (a caret line or the source line above one) that must stay
// monospaced for the '^' to align.
static std::vector<std::pair<std::string, bool>> classify_code_lines(const std::string &msg)
{
std::vector<std::string> lines;
boost::split(lines, msg, boost::is_any_of("\n"));
for (std::string &line : lines)
if (!line.empty() && line.back() == '\r')
line.pop_back();
std::vector<std::pair<std::string, bool>> tagged;
tagged.reserve(lines.size());
for (size_t i = 0; i < lines.size(); ++i) {
bool is_code = is_caret_line(lines[i]) || (i + 1 < lines.size() && is_caret_line(lines[i + 1]));
tagged.emplace_back(std::move(lines[i]), is_code);
}
return tagged;
}
// Keeps whitespace literal so the caret's leading spaces survive.
// Used inside <code>, which supplies the fixed face. <pre> does both but adds a blank line above it.
class CodeExcerptTagHandler : public wxHtmlWinTagHandler
{
public:
wxString GetSupportedTags() override { return wxT("EXCERPT"); }
bool HandleTag(const wxHtmlTag &tag) override
{
const wxHtmlWinParser::WhitespaceMode ws = m_WParser->GetWhitespaceMode();
m_WParser->SetWhitespaceMode(wxHtmlWinParser::Whitespace_Pre);
ParseInner(tag);
m_WParser->SetWhitespaceMode(ws);
return true;
}
};
// Render the message as HTML, monospacing only the code excerpts.
static std::string format_parser_error_html(const std::string &msg)
{
std::string out;
for (const auto &[text, is_code] : classify_code_lines(msg)) {
if (!out.empty()) out += "<br>"; // join, not trail; a trailing <br> forces a scrollbar
std::string escaped = xml_escape(text);
if (is_code)
out += "<code><excerpt>" + escaped + "</excerpt></code>";
else
out += escaped;
}
return out;
}
// Measure each line in the font it will render in, so the dialog fits the longest line without slack.
static wxSize measure_mixed_text(wxWindow *parent, const std::string &msg, const wxFont &prose_font, const wxFont &code_font)
{
wxClientDC dc(parent);
int width = 0, height = 0;
for (const auto &[text, is_code] : classify_code_lines(msg)) {
dc.SetFont(is_code ? code_font : prose_font);
width = std::max(width, dc.GetTextExtent(wxString::FromUTF8(text.c_str())).GetWidth());
height += dc.GetCharHeight();
}
return wxSize(width, height);
}
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
static void add_msg_content(wxWindow *parent,
wxBoxSizer *content_sizer,
wxString msg,
bool monospaced_font = false,
bool is_marked_msg = false,
bool has_code_excerpts = false,
bool is_marked_msg = false,
const wxString &link_text = "",
std::function<void(const wxString &)> link_callback = nullptr)
{
@@ -243,7 +318,7 @@ static void add_msg_content(wxWindow *parent,
// count lines in the message
int msg_lines = 0;
if (!monospaced_font) {
if (!has_code_excerpts) {
int line_len = 55;// count of symbols in one line
int start_line = 0;
for (auto i = msg.begin(); i != msg.end(); ++i) {
@@ -300,13 +375,23 @@ static void add_msg_content(wxWindow *parent,
page_size = wxSize(info_width, page_height);
}
else {
wxClientDC dc(parent);
dc.SetFont(font); // ORCA without this it calculates bigger size
wxSize msg_sz = dc.GetMultiLineTextExtent(msg) + parent->FromDIP(wxSize(10,5)); // added extra spacing to prevent wrapping
wxSize msg_sz;
if (has_code_excerpts) {
msg_sz = measure_mixed_text(parent, msg.ToUTF8().data(), font, monospace);
} else {
wxClientDC dc(parent);
dc.SetFont(font); // ORCA without this it calculates bigger size
msg_sz = dc.GetMultiLineTextExtent(msg);
}
msg_sz += parent->FromDIP(wxSize(10,5)); // added extra spacing to prevent wrapping
page_size = wxSize(std::min(msg_sz.GetX(), info_width), std::min(msg_sz.GetY(), info_width));
int page_height = msg_sz.GetY();
// Reserve the horizontal scrollbar's height, or it clips the last line.
if (msg_sz.GetX() > info_width)
page_height += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y, parent);
page_size = wxSize(std::min(msg_sz.GetX(), info_width), std::min(page_height, info_width));
// Extra line breaks in message dialog
if (link_text.IsEmpty() && !link_callback && is_marked_msg == false) {//for common text
if (link_text.IsEmpty() && !link_callback && is_marked_msg == false && !has_code_excerpts) {//for common text
html->Destroy();
if (msg_sz.GetX() < info_width) {//No need for line breaks
info_width = msg_sz.GetX();
@@ -337,12 +422,15 @@ static void add_msg_content(wxWindow *parent,
}
html->SetMinSize(page_size);
std::string msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked_msg);
boost::replace_all(msg_escaped, "\r\n", "<br>");
boost::replace_all(msg_escaped, "\n", "<br>");
if (monospaced_font)
// Code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
msg_escaped = std::string("<pre><code>") + msg_escaped + "</code></pre>";
std::string msg_escaped;
if (has_code_excerpts) {
html->GetParser()->AddTagHandler(new CodeExcerptTagHandler());
msg_escaped = format_parser_error_html(msg.ToUTF8().data());
} else {
msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked_msg);
boost::replace_all(msg_escaped, "\r\n", "<br>");
boost::replace_all(msg_escaped, "\n", "<br>");
}
if (!link_text.IsEmpty() && link_callback) {
msg_escaped += "<span><a href=\"#\" style=\"color:rgb(0, 150, 136); text-decoration:underline;\">" + std::string(link_text.ToUTF8().data()) + "</a></span>";
@@ -360,15 +448,15 @@ static void add_msg_content(wxWindow *parent,
// ErrorDialog
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &temp_msg, bool monospaced_font)
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &temp_msg, bool has_code_excerpts)
: MsgDialog(parent, wxString::Format(_(L("%s error")), SLIC3R_APP_FULL_NAME),
wxString::Format(_(L("%s has encountered an error")), SLIC3R_APP_FULL_NAME), wxOK)
, msg(temp_msg)
{
add_msg_content(this, content_sizer, msg, monospaced_font);
add_msg_content(this, content_sizer, msg, has_code_excerpts);
// Use a small bitmap with monospaced font, as the error text will not be wrapped.
logo->SetBitmap(create_scaled_bitmap("OrcaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/64));
// Use a small bitmap for code excerpts, which cannot wrap and so need the width.
logo->SetBitmap(create_scaled_bitmap("OrcaSlicer_192px_grayscale.png", this, has_code_excerpts ? 48 : /*1*/64));
SetMaxSize(MSG_DLG_MAX_SIZE);
+3 -3
View File
@@ -106,9 +106,9 @@ protected:
class ErrorDialog : public MsgDialog
{
public:
// If monospaced_font is true, the error message is displayed using html <code><pre></pre></code> tags,
// so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser.
ErrorDialog(wxWindow *parent, const wxString &temp_msg, bool courier_font);
// If has_code_excerpts is true, code excerpts (a source line and the caret line below it) render
// monospaced so the caret aligns. Used for placeholder-parser errors.
ErrorDialog(wxWindow *parent, const wxString &temp_msg, bool has_code_excerpts);
ErrorDialog(ErrorDialog &&) = delete;
ErrorDialog(const ErrorDialog &) = delete;
ErrorDialog &operator=(ErrorDialog &&) = delete;
+28
View File
@@ -54,6 +54,9 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
case ConfigOptionDef::GUIType::one_string: m_fields.emplace(id, TextCtrl::Create<TextCtrl>(this->ctrl_parent(), opt, id)); break;
case ConfigOptionDef::GUIType::plugin_picker: m_fields.emplace(id, PluginField::Create<PluginField>(this->ctrl_parent(), opt, id)); break;
case ConfigOptionDef::GUIType::plugin_config: m_fields.emplace(id, PluginConfigField::Create<PluginConfigField>(this->ctrl_parent(), opt, id)); break;
case ConfigOptionDef::GUIType::printer_agent_select: m_fields.emplace(
id, PrinterAgentChoice::Create<PrinterAgentChoice>(this->ctrl_parent(), opt, id));
break;
default:
switch (opt.type) {
case coFloatOrPercent:
@@ -386,6 +389,7 @@ void OptionsGroup::activate_line(Line& line)
}
if (label != nullptr && line.label_tooltip != "")
label->SetToolTip(line.label_tooltip);
line.label_widget = label;
}
}
@@ -574,6 +578,7 @@ void OptionsGroup::clear(bool destroy_custom_ctrl)
for (Line& line : m_lines) {
if (line.near_label_widget_win)
line.near_label_widget_win = nullptr;
line.label_widget = nullptr;
if (line.widget_sizer) {
line.widget_sizer->Clear(true);
@@ -652,6 +657,16 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index
void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value)
{
if (opt_id == "printer_agent") {
// TODO: Replace this option-specific branch with a generic value adapter if
// more fields need custom field-value to config-value conversion.
if (const std::string* id = boost::any_cast<std::string>(&value))
this->change_opt_value("printer_agent", wxGetApp().canonical_printer_agent_id(*id));
OptionsGroup::on_change_OG(opt_id, value);
return;
}
if (!m_opt_map.empty()) {
auto it = m_opt_map.find(opt_id);
if (it == m_opt_map.end()) {
@@ -770,6 +785,19 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
}
}
#endif
else if (opt_key == "printer_agent")
{
// why: printer_agent is a coString kept out of m_opt_map. The generic non-opt_map revert
// below restores the edited config from get_value(), but a deregistered/"(missing)" saved
// id has no selectable row, so the field yields no value and the edited config keeps the
// user's interim pick -> stuck dirty. Restore the SAVED id straight into the edited config
// (displayable or not; config is the saved or system baseline), then repaint and notify.
const std::string saved_id = config.opt_string("printer_agent");
set_value(opt_key, saved_id);
this->change_opt_value(opt_key, saved_id);
OptionsGroup::on_change_OG(opt_key, saved_id);
return;
}
else if (m_opt_map.find(opt_key) == m_opt_map.end() ||
// This option don't have corresponded field
opt_key == "printable_area" || opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "thumbnails" ||
+9
View File
@@ -62,6 +62,7 @@ public:
widget_t widget {nullptr};
std::function<wxWindow*(wxWindow*)> near_label_widget{ nullptr };
wxWindow* near_label_widget_win {nullptr};
wxStaticText* label_widget {nullptr};
wxSizer* widget_sizer {nullptr};
wxSizer* extra_widget_sizer {nullptr};
//BBS: export the extra colume widget
@@ -81,6 +82,14 @@ public:
label(_(label)), label_tooltip(_(tooltip)) {}
Line() : m_is_separator(true) {}
void set_label(const wxString& new_label) {
label = new_label;
if (label_widget != nullptr) {
label_widget->SetLabel(label + (label.IsEmpty() ? "" : ": "));
label_widget->Refresh();
}
}
bool is_separator() const { return m_is_separator; }
bool has_only_option(const std::string& opt_key) const { return m_options.size() == 1 && m_options[0].opt_id == opt_key; }
+1
View File
@@ -2298,6 +2298,7 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic
bool enable_wrapping = (wrapping_opt != nullptr) && wrapping_opt->value;
wt_size = estimate_wipe_tower_size(config, w, v, extruder_count, plate_extruder_size, use_global_objects, enable_wrapping);
int plate_width=m_width, plate_depth=m_depth;
w = wt_size(0); // effective width; differs from prime_tower_width when the rib wall squares the tower
float depth = wt_size(1);
float margin = WIPE_TOWER_MARGIN + tower_brim_width, wp_brim_width = 0.f;
const ConfigOption* wipe_tower_brim_width_opt = config.option("prime_tower_brim_width");
+2 -88
View File
@@ -25,7 +25,6 @@
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "format.hpp"
#include "Tab.hpp"
#include "wxExtensions.hpp"
@@ -128,22 +127,8 @@ PhysicalPrinterDialog::~PhysicalPrinterDialog()
void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup)
{
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
// Special handling for printer_agent: convert fake enum index to string agent ID
if (opt_key == "printer_agent") {
try {
int selected_idx = boost::any_cast<int>(value);
auto agents = NetworkAgentFactory::get_registered_printer_agents();
if (selected_idx >= 0 && selected_idx < static_cast<int>(agents.size())) {
m_config->set_key_value("printer_agent",
new ConfigOptionString(agents[selected_idx].id));
}
} catch (const boost::bad_any_cast&) {
// If value is not an int, ignore
}
if (opt_key == "host_type" || opt_key == "printhost_authorization_type")
this->update();
} else if (opt_key == "host_type" || opt_key == "printhost_authorization_type") {
this->update();
}
if (opt_key == "print_host")
this->update_printhost_buttons();
if (opt_key == "printhost_port")
@@ -154,47 +139,6 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
m_optgroup->append_single_option_line("host_type");
// Build printer agent dropdown from registry (only if network agent is available)
if (wxGetApp().getAgent() != nullptr) {
auto agents = NetworkAgentFactory::get_registered_printer_agents();
if (!agents.empty()) {
// Create a fake enum option to force a Choice widget instead of TextCtrl
// (printer_agent is coString in config, but we need a dropdown)
ConfigOptionDef def;
def.type = coEnum;
def.width = Field::def_width_wider();
def.label = L("Printer Agent");
def.tooltip = L("Select the network agent implementation for printer communication. "
"Available agents are registered at startup.");
def.mode = comAdvanced;
// Populate enum values and labels from registered agents
for (const auto& agent : agents) {
def.enum_values.push_back(agent.id);
def.enum_labels.push_back(agent.display_name);
}
// Resolve selected agent: use config value if valid, otherwise fall back to default
std::string selected_agent = m_config->opt_string("printer_agent");
auto it = std::find_if(agents.begin(), agents.end(), [&selected_agent](const auto& a) { return a.id == selected_agent; });
if (it == agents.end()) {
selected_agent = ORCA_PRINTER_AGENT_ID;
it = std::find_if(agents.begin(), agents.end(), [&selected_agent](const auto& a) { return a.id == selected_agent; });
}
if (it != agents.end()) {
size_t default_idx = std::distance(agents.begin(), it);
def.set_default_value(new ConfigOptionInt(static_cast<int>(default_idx)));
}
// Create and append the option line
auto agent_option = Option(def, "printer_agent");
Line agent_line = m_optgroup->create_single_option_line(agent_option);
m_optgroup->append_line(agent_line);
}
}
auto create_sizer_with_btn = [](wxWindow* parent, Button** btn, const std::string& icon_name, const wxString& label) {
*btn = new Button(parent, label);
(*btn)->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
@@ -725,7 +669,7 @@ void PhysicalPrinterDialog::update(bool printer_change)
}
// For bbl printers, show option to control the device tab
if (wxGetApp().preset_bundle->is_bbl_vendor()) {
if (wxGetApp().preset_bundle->is_bbl_vendor() || wxGetApp().app_config->get_bool("use_printer_agents")) {
m_optgroup->show_field("bbl_use_print_host_webui");
const bool use_print_host_webui = !current_webui.empty();
if (Field* printhost_webui_field = m_optgroup->get_field("bbl_use_print_host_webui"); printhost_webui_field) {
@@ -816,31 +760,6 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
}
}
void PhysicalPrinterDialog::update_printer_agent_type()
{
if (m_config == nullptr)
return;
Field* agent_field = m_optgroup->get_field("printer_agent");
if (!agent_field)
return;
Choice* agent_choice = dynamic_cast<Choice*>(agent_field);
if (!agent_choice)
return;
// Sync selection with current config value
const std::string current_agent = m_config->opt_string("printer_agent");
auto agents = NetworkAgentFactory::get_registered_printer_agents();
for (size_t i = 0; i < agents.size(); ++i) {
if (agents[i].id == current_agent) {
agent_choice->set_value(i);
return;
}
}
}
void PhysicalPrinterDialog::update_printers()
{
wxBusyCursor wait;
@@ -894,11 +813,6 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event)
{
wxGetApp().get_tab(Preset::TYPE_PRINTER)->save_preset("", false, false, true, m_preset_name);
event.Skip();
// Defer printer agent switch to ensure preset save completes first
wxGetApp().CallAfter([] {
wxGetApp().switch_printer_agent();
});
}
}} // namespace Slic3r::GUI
-1
View File
@@ -60,7 +60,6 @@ public:
void update(bool printer_change = false);
void update_host_type(bool printer_change);
void update_printer_agent_type();
void update_preset_input();
void update_printhost_buttons();
void update_printers();
+31 -26
View File
@@ -3246,7 +3246,8 @@ void Sidebar::update_all_preset_comboboxes()
auto p_mainframe = wxGetApp().mainframe;
auto cfg = preset_bundle.printers.get_edited_preset().config;
const bool use_native_device_tab = preset_bundle.use_bbl_device_tab() || NetworkAgentFactory::is_current_printer_agent_plugin();
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
const bool use_native_device_tab = preset_bundle.use_bbl_device_tab() || use_printer_agents;
if (preset_bundle.use_bbl_network()) {
//only show connection button for not-BBL printer
@@ -3258,7 +3259,8 @@ void Sidebar::update_all_preset_comboboxes()
p_mainframe->set_print_button_to_default(MainFrame::PrintSelectType::ePrintPlate);
} else {
//p->btn_connect_printer->Show();
p->m_printer_connect->Show();
// ORCA: hide the physical-printer connection button when printer agents are enabled
p->m_printer_connect->Show(!use_printer_agents);
// ORCA: show/hide sync-ams button based on filament sync mode
auto agent = wxGetApp().getAgent();
@@ -3280,10 +3282,12 @@ void Sidebar::update_all_preset_comboboxes()
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
if (cfg.has("printhost_apikey") && (host_type != htSimplyPrint))
apikey = cfg.opt_string("printhost_apikey");
print_btn_type = preset_bundle.is_bbl_vendor() ? MainFrame::PrintSelectType::ePrintPlate : MainFrame::PrintSelectType::eSendGcode;
print_btn_type = (preset_bundle.is_bbl_vendor() || wxGetApp().app_config->get_bool("use_printer_agents"))
? MainFrame::PrintSelectType::ePrintPlate
: MainFrame::PrintSelectType::eSendGcode;
}
if (!use_native_device_tab)
if (!use_native_device_tab || use_printer_agents)
p_mainframe->load_printer_url(url, apikey);
@@ -3439,7 +3443,10 @@ void Sidebar::update_presets(Preset::Type preset_type)
bool isBBL = preset_bundle.is_bbl_vendor();
bool is_dual_extruder = extruder_variants->size() == 2;
p->layout_printer(preset_bundle.use_bbl_network(), isBBL && is_dual_extruder);
// why: agent mode drives the native device tab, so the sidebar lays out like BBL
// (no physical-printer connect button).
p->layout_printer(preset_bundle.use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents"),
isBBL && is_dual_extruder);
// Update nozzle titles from printer config (e.g. "Main Nozzle" / "Auxiliary Nozzle" for N6)
// UI left = DEPUTY_EXTRUDER_ID(1), UI right = MAIN_EXTRUDER_ID(0)
@@ -5625,6 +5632,7 @@ struct Plater::priv
void on_action_slice_all(SimpleEvent&);
void on_action_publish(wxCommandEvent &evt);
void on_action_print_plate(SimpleEvent&);
void open_machine_select_dialog(int plate_idx, PrintFromType print_type = PrintFromType::FROM_NORMAL);
void on_action_print_all(SimpleEvent&);
void on_action_export_gcode(SimpleEvent&);
void on_action_send_gcode(SimpleEvent&);
@@ -11166,18 +11174,23 @@ void Plater::priv::on_action_print_plate(SimpleEvent&)
}
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.use_bbl_network()) {
// BBS
if (!m_select_machine_dlg)
m_select_machine_dlg = new SelectMachineDialog(q);
m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL);
m_select_machine_dlg->prepare(partplate_list.get_curr_plate_index());
m_select_machine_dlg->ShowModal();
if (preset_bundle.use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents")) {
open_machine_select_dialog(partplate_list.get_curr_plate_index());
} else {
q->send_gcode_legacy(PLATE_CURRENT_IDX, nullptr);
}
}
void Plater::priv::open_machine_select_dialog(int plate_idx, PrintFromType print_type)
{
// BBS
if (!m_select_machine_dlg)
m_select_machine_dlg = new SelectMachineDialog(q);
m_select_machine_dlg->set_print_type(print_type);
m_select_machine_dlg->prepare(plate_idx);
m_select_machine_dlg->ShowModal();
}
void Plater::priv::on_action_send_to_multi_machine(SimpleEvent&)
{
if (!m_send_multi_dlg)
@@ -11193,10 +11206,7 @@ void Plater::priv::on_action_print_plate_from_sdcard(SimpleEvent&)
}
//BBS
if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q);
m_select_machine_dlg->set_print_type(PrintFromType::FROM_SDCARD_VIEW);
m_select_machine_dlg->prepare(0);
m_select_machine_dlg->ShowModal();
open_machine_select_dialog(0, PrintFromType::FROM_SDCARD_VIEW);
}
void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
@@ -11218,13 +11228,13 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
new_sel == main_frame->m_tabpanel->FindPageByName(TAB_ID_PREVIEW);
update_sidebar();
int old_sel = e.GetOldSelection();
const bool is_printer_agent_plugin = NetworkAgentFactory::is_current_printer_agent_plugin();
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
const bool use_native_device_tab = wxGetApp().preset_bundle &&
(wxGetApp().preset_bundle->use_bbl_device_tab() || is_printer_agent_plugin);
(wxGetApp().preset_bundle->use_bbl_device_tab() || use_printer_agents);
if (use_native_device_tab && new_sel == main_frame->m_tabpanel->FindPageByName(TAB_ID_MONITOR)) {
// BBL network module is only required for BBL-vendor printers.
// Non-BBL Python plugins (e.g. moonraker) drive the Device tab without it.
if (!is_printer_agent_plugin && wxGetApp().preset_bundle->is_bbl_vendor() && !Slic3r::NetworkAgent::is_network_module_loaded()) {
if (!use_printer_agents && wxGetApp().preset_bundle->is_bbl_vendor() && !Slic3r::NetworkAgent::is_network_module_loaded()) {
e.Veto();
BOOST_LOG_TRIVIAL(info) << boost::format("skipped tab switch from %1% to %2%, lack of network plugins") % old_sel % new_sel;
if (q) {
@@ -11280,13 +11290,8 @@ void Plater::priv::on_action_print_all(SimpleEvent&)
}
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.use_bbl_network()) {
// BBS
if (!m_select_machine_dlg)
m_select_machine_dlg = new SelectMachineDialog(q);
m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL);
m_select_machine_dlg->prepare(PLATE_ALL_IDX);
m_select_machine_dlg->ShowModal();
if (preset_bundle.use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents")) {
open_machine_select_dialog(PLATE_ALL_IDX);
} else {
q->send_gcode_legacy(PLATE_ALL_IDX, nullptr);
}
+2
View File
@@ -11,6 +11,7 @@ namespace Slic3r
// IMPORTANT: ordinal order is the Plugins dialog Source sort priority.
Mine,
Subscribed,
Orphaned,
Local
};
@@ -20,6 +21,7 @@ namespace Slic3r
{
case PluginSource::Mine: return "mine";
case PluginSource::Subscribed: return "subscribed";
case PluginSource::Orphaned: return "orphaned";
case PluginSource::Local: return "local";
}
+44 -9
View File
@@ -103,6 +103,7 @@ struct PluginDialogItem
bool loading = false;
bool is_cloud_plugin = false;
bool orphaned = false;
bool has_local_package = false;
bool unauthorized = false;
bool has_script_capability = false;
@@ -246,6 +247,7 @@ nlohmann::json build_plugin_payload_item(const PluginDialogItem& dialog_item)
payload_item["sharing_token"] = dialog_item.sharing_token;
payload_item["thumbnail_url"] = dialog_item.thumbnail_url;
payload_item["installed"] = dialog_item.has_local_package;
payload_item["orphaned"] = dialog_item.orphaned;
payload_item["installed_version"] = dialog_item.installed_version;
payload_item["latest_version"] = dialog_item.latest_version;
return payload_item;
@@ -268,7 +270,8 @@ PluginSource derive_plugin_source(const PluginDescriptor& descriptor)
const bool is_cloud = descriptor.is_cloud_plugin();
const bool is_mine = is_cloud && has_cloud_meta && descriptor.cloud->is_mine;
// Source is ownership/locality only; issue states never replace this badge.
if (is_cloud && has_cloud_meta && descriptor.cloud->orphaned)
return PluginSource::Orphaned;
if (is_mine)
return PluginSource::Mine;
if (is_cloud)
@@ -281,11 +284,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
PluginAvailableActions available_actions;
const bool is_loading = item.status == PluginStatus::Loading;
const bool is_cloud = item.is_cloud_plugin;
const bool is_orphaned = item.orphaned;
const bool is_mine = item.source == PluginSource::Mine;
const bool has_local = item.has_local_package;
const bool authorized_for_install = !item.unauthorized;
available_actions.toggle_installs_cloud_plugin = is_cloud && !has_local && authorized_for_install;
available_actions.toggle_installs_cloud_plugin = is_cloud && !is_orphaned && !has_local && authorized_for_install;
available_actions.can_toggle = !is_loading && (has_local || available_actions.toggle_installs_cloud_plugin);
auto add_action = [&available_actions](const char* id, const char* label, bool enabled = true, bool danger = false) {
@@ -294,7 +298,7 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
// Owned cloud plugins fall through to the local delete: it removes the installed package only.
// Deleting a plugin from the cloud is a plugin hub operation and is never offered here.
if (is_cloud && !is_mine) {
if (is_cloud && !is_orphaned && !is_mine) {
add_action("unsubscribe_plugin", "Unsubscribe", true, true);
} else if (has_local) {
add_action("delete_plugin", "Delete", true, true);
@@ -302,11 +306,13 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
add_action("open_folder", "Show in folder", has_local);
if (is_cloud) {
add_action("reinstall_plugin", "Reinstall");
} else {
add_action("reload_plugin", "Reload");
add_action("clear_cache_reload_plugin", "Delete cache and reload");
if (!is_orphaned) {
if (is_cloud) {
add_action("reinstall_plugin", "Reinstall");
} else {
add_action("reload_plugin", "Reload");
add_action("clear_cache_reload_plugin", "Delete cache and reload");
}
}
return available_actions;
@@ -360,6 +366,7 @@ PluginDialogItem build_plugin_dialog_item(const PluginDescriptor& descriptor)
item.error_text = descriptor.normalized_error();
item.has_error = descriptor.has_error();
item.is_cloud_plugin = descriptor.is_cloud_plugin();
item.orphaned = descriptor.cloud.has_value() && descriptor.cloud->orphaned;
item.has_local_package = descriptor.has_local_package();
item.unauthorized = descriptor.is_unauthorized();
item.is_loaded = manager.is_plugin_loaded(descriptor.plugin_key);
@@ -592,7 +599,35 @@ bool PluginsDialog::get_descriptor(const std::string& plugin_key, PluginDescript
void PluginsDialog::refresh_plugin_metadata_async(const wxString& title, const wxString& message, bool fetch_cloud)
{
run_with_dialog([fetch_cloud]() { refresh_plugin_metadata_blocking(fetch_cloud); }, [this]() { send_plugins(); }, title, message);
run_with_dialog([fetch_cloud]() { refresh_plugin_metadata_blocking(fetch_cloud); }, [this]() {
prompt_for_missing_plugins();
send_plugins();
}, title, message);
}
void PluginsDialog::prompt_for_missing_plugins()
{
PluginManager& manager = PluginManager::instance();
const std::vector<PluginDescriptor> missing = manager.get_missing_plugin_descriptors();
if (missing.empty())
return;
wxString names;
std::vector<std::string> keys;
keys.reserve(missing.size());
for (const PluginDescriptor& plugin : missing) {
keys.push_back(plugin.plugin_key);
names += "\n- ";
names += plugin_display_name(plugin.plugin_key);
}
const int result = wxMessageBox(
wxString::Format(_L("The following installed plugins were not found on disk:\n%s\n\nRemove them from OrcaSlicer?"), names),
_L("Missing Plugins"), wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
restore_z_order();
if (result == wxYES)
manager.remove_missing_plugins(keys);
}
void PluginsDialog::refresh_plugins()
+1
View File
@@ -68,6 +68,7 @@ private:
bool get_descriptor(const std::string& plugin_key, Slic3r::PluginDescriptor& descriptor) const;
void refresh_plugin_metadata_async(const wxString& title, const wxString& message, bool fetch_cloud);
void prompt_for_missing_plugins();
void refresh_plugins();
void toggle_plugin(const std::string& plugin_key, bool enabled);
void toggle_plugin_capability(const std::string& plugin_key, PluginCapabilityType type, const std::string& capability_name, bool enabled);
+45 -1
View File
@@ -700,6 +700,12 @@ wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString tit
auto input = new SpinInput(m_parent, wxEmptyString, side_label, wxDefaultPosition, DESIGN_INPUT_SIZE, wxSP_ARROW_KEYS, min, max, stoi(app_config->get(param)));
input->SetToolTip(tip);
// ORCA: this one is only meaningful while the dimming it controls is enabled
if (param == "preview_dim_previous_layers_brightness") {
m_dim_previous_layers_brightness_input = input;
input->Enable(app_config->get_bool("preview_dim_previous_layers"));
}
m_sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL);
if(!title2.empty()){
@@ -1050,8 +1056,10 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
wxGetApp().mainframe->m_webview->SendCloudProvidersInfo();
}
}
// ORCA: apply the preview dimming change immediately to the currently loaded preview (ported from preFlight)
// ORCA: apply the preview dimming change immediately to the currently loaded preview
else if (param == "preview_dim_previous_layers") {
if (m_dim_previous_layers_brightness_input)
m_dim_previous_layers_brightness_input->Enable(app_config->get_bool(param));
if (Plater* plater = wxGetApp().plater()) {
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
canvas->get_gcode_viewer().set_dim_previous_layers(app_config->get_bool(param));
@@ -1127,6 +1135,14 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT);
}
if (param == "use_printer_agents")
{
// Rebuild the Device tab so the native/web-UI choice reflects the new flag
// immediately, instead of only on the next printer-preset change or restart.
if (wxGetApp().plater())
wxGetApp().plater()->sidebar().update_all_preset_comboboxes();
}
if (param == "enable_high_low_temp_mixed_printing") {
if (checkbox->GetValue()) {
const wxString warning_title = _L("Bed Temperature Difference Warning");
@@ -1914,6 +1930,28 @@ void PreferencesDialog::create_items()
);
g_sizer->Add(item_dim_previous_layers);
auto item_dim_previous_layers_brightness = create_item_spinctrl(
_L("Dimmed layer brightness"),
"",
_L("%"),
_L("How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."),
"preview_dim_previous_layers_brightness",
0,
99,
// ORCA: apply the new brightness immediately to the currently loaded preview
[](int value) {
if (Plater* plater = wxGetApp().plater()) {
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
canvas->get_gcode_viewer().set_dim_previous_layers_brightness(0.01f * value);
canvas->set_as_dirty();
canvas->request_extra_frame();
}
}
}
);
g_sizer->Add(item_dim_previous_layers_brightness);
g_sizer->AddSpacer(FromDIP(10));
sizer_page->Add(g_sizer, 0, wxEXPAND);
@@ -2071,6 +2109,12 @@ void PreferencesDialog::create_items()
auto item_show_unsupported = create_item_checkbox(_L("Show unsupported presets"), _L("Show incompatible/unsupported presets in the printer and filament dropdown lists. These presets cannot be selected."), "show_unsupported_presets");
g_sizer->Add(item_show_unsupported);
auto item_plugin_printer_agents = create_item_checkbox(
_L("(Experimental) Use printer agents instead of print hosts"), _L(
"Route print jobs for non-Bambu printers through printer plug-in agents instead of the classic print-host upload flow.\nWhen disabled, OrcaSlicer uses the legacy print-host behavior."),
"use_printer_agents");
g_sizer->Add(item_plugin_printer_agents);
//// DEVELOPER > Experimental Features
g_sizer->Add(create_item_title(_L("Experimental Features")), 1, wxEXPAND);
+2
View File
@@ -13,6 +13,7 @@
#include "Widgets/ComboBox.hpp"
#include "Widgets/CheckBox.hpp"
#include "Widgets/TextInput.hpp"
#include "Widgets/SpinInput.hpp"
#include "Widgets/TabCtrl.hpp"
#include "slic3r/Utils/bambu_networking.hpp"
@@ -71,6 +72,7 @@ public:
::CheckBox * m_sync_user_preset_checkbox = {nullptr};
::CheckBox * m_bambu_cloud_checkbox = {nullptr};
::TextInput *m_backup_interval_textinput = {nullptr};
::SpinInput *m_dim_previous_layers_brightness_input = {nullptr};
::ComboBox * m_network_version_combo = {nullptr};
std::vector<NetworkLibraryVersionInfo> m_available_versions;
+3
View File
@@ -865,6 +865,9 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset
clr_picker = new wxBitmapButton(parent, wxID_ANY, {}, wxDefaultPosition, wxSize(FromDIP(20), FromDIP(20)), wxBU_EXACTFIT | wxBU_AUTODRAW | wxBORDER_NONE);
clr_picker->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
clr_picker->SetToolTip(_L("Click to select filament color"));
#ifdef __WXGTK__
RemoveButtonBorder(clr_picker);
#endif
clr_picker->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
// Check if it's an official filament
auto fila_type = Preset::remove_suffix_modified(GetValue().ToUTF8().data());
+6 -7
View File
@@ -3629,7 +3629,7 @@ void SelectMachineDialog::on_send_print()
BOOST_LOG_TRIVIAL(error) << "build_nozzle_info errors";
}
m_print_job->sdcard_state = obj_->GetStorage()->get_sdcard_state();
m_print_job->sdcard_state = obj_->GetStorage()->get_sdcard_state();
m_print_job->has_sdcard = wxGetApp().app_config->get("allow_abnormal_storage") == "true"
? (m_print_job->sdcard_state == DevStorage::SdcardState::HAS_SDCARD_NORMAL
|| m_print_job->sdcard_state == DevStorage::SdcardState::HAS_SDCARD_ABNORMAL)
@@ -3868,12 +3868,11 @@ _compare_obj_names(MachineObject* obj1, MachineObject* obj2)
}
/*******************************************************************
*@note _collect_machine_list
*@param dev_manager -- the device manager
*@param sorted_machine_objs -- return the sorted machine objects
*@param best_one -- return the best one
*/
/*******************************************************************/
* @note _collect_machine_list
* @param dev_manager -- the device manager
* @param sorted_machine_objs -- return the sorted machine objects
* @param best_one -- return the best one
*******************************************************************/
static void
_collect_sorted_machines(Slic3r::DeviceManager* dev_manager,
std::vector<MachineObject*>& sorted_machine_objs)
+104 -8
View File
@@ -33,6 +33,7 @@
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
#include "slic3r/plugin/PluginConfig.hpp"
#include "Plater.hpp"
@@ -1738,6 +1739,13 @@ void Tab::toggle_line(const std::string &opt_key, bool toggle, int opt_index)
if (line) line->toggle_visible = toggle;
};
void Tab::set_option_label(const std::string &opt_key, const wxString &label, int opt_index)
{
if (!m_active_page) return;
Line *line = m_active_page->get_line(opt_key, opt_index);
if (line) line->set_label(label);
}
// To be called by custom widgets, load a value into a config,
// update the preset selection boxes (the dirty flags)
// If value is saved before calling this function, put saved_value = true,
@@ -2816,6 +2824,7 @@ void TabPrint::build()
optgroup->append_single_option_line("fill_multiline", "strength_settings_infill#fill-multiline");
optgroup->append_single_option_line("sparse_infill_pattern", "strength_settings_infill#sparse-infill-pattern");
optgroup->append_single_option_line("gyroid_optimized", "strength_settings_patterns#gyroid-optimized");
optgroup->append_single_option_line("sparse_infill_smooth_factor", "strength_settings_patterns#sparse-infill-smooth-factor");
optgroup->append_single_option_line("infill_direction", "strength_settings_infill#direction");
optgroup->append_single_option_line("sparse_infill_rotate_template", "strength_settings_infill_rotation_template_metalanguage");
optgroup->append_single_option_line("skin_infill_density", "strength_settings_patterns#locked-zag");
@@ -3069,6 +3078,7 @@ void TabPrint::build()
optgroup->append_single_option_line("combine_brims", "others_settings_brim#combine-brims");
optgroup->append_single_option_line("brim_ears_max_angle", "others_settings_brim#ear-max-angle");
optgroup->append_single_option_line("brim_ears_detection_length", "others_settings_brim#ear-detection-radius");
optgroup->append_single_option_line("brim_ears_outer_only", "others_settings_brim#brim-ears-outer-only");
optgroup = page->new_optgroup(L("Special mode"), L"param_special");
optgroup->append_single_option_line("slicing_mode", "others_settings_special_mode#slicing-mode");
@@ -3997,13 +4007,12 @@ void TabFilament::add_filament_overrides_page()
const int extruder_idx = 0; // #ys_FIXME
ConfigOptionsGroupShp retraction_optgroup = page->new_optgroup(L("Retraction"), L"param_retraction");
auto append_retraction_option = [this, retraction_optgroup](const std::string& opt_key, int opt_index)
auto append_retraction_option = [this](ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index)
{
Line line {"",""};
line = retraction_optgroup->create_single_option_line(retraction_optgroup->get_option(opt_key, opt_index));
line = optgroup->create_single_option_line(optgroup->get_option(opt_key, opt_index));
line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(retraction_optgroup), opt_key, opt_index](wxWindow* parent) {
line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup), opt_key, opt_index](wxWindow* parent) {
auto check_box = new ::CheckBox(parent); // ORCA modernize checkboxes
check_box->Bind(wxEVT_TOGGLEBUTTON, [this, optgroup_wk, opt_key, opt_index](wxCommandEvent& evt) {
const bool is_checked = evt.IsChecked();
@@ -4030,9 +4039,10 @@ void TabFilament::add_filament_overrides_page()
return check_box;
};
retraction_optgroup->append_line(line);
optgroup->append_line(line);
};
ConfigOptionsGroupShp retraction_optgroup = page->new_optgroup(L("Retraction"), L"param_retraction");
for (const std::string opt_key : { "filament_retraction_length",
"filament_z_hop",
"filament_z_hop_types",
@@ -4056,7 +4066,13 @@ void TabFilament::add_filament_overrides_page()
//SoftFever
// "filament_seam_gap"
})
append_retraction_option(opt_key, extruder_idx);
append_retraction_option(retraction_optgroup, opt_key, extruder_idx);
ConfigOptionsGroupShp toolchange_optgroup = page->new_optgroup(L("Retraction when switching material"), L"param_retraction_material_change");
for (const std::string opt_key : { "filament_retract_length_toolchange",
"filament_retract_restart_extra_toolchange"
})
append_retraction_option(toolchange_optgroup, opt_key, extruder_idx);
ConfigOptionsGroupShp ironing_optgroup = page->new_optgroup(L("Ironing"), L"param_ironing");
auto append_ironing_option = [this, ironing_optgroup](const std::string& opt_key, int opt_index)
@@ -4171,6 +4187,8 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print
"filament_retraction_speed",
"filament_deretraction_speed",
"filament_retract_restart_extra",
"filament_retract_length_toolchange",
"filament_retract_restart_extra_toolchange",
"filament_retraction_minimum_travel",
"filament_retract_when_changing_layer",
"filament_wipe",
@@ -4201,7 +4219,8 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print
is_checked &= !dynamic_cast<ConfigOptionVectorBase*>(m_config->option(opt_key))->is_nil(extruder_idx);
m_overrides_options[opt_key]->SetValue(is_checked);
Field* field = optgroup->get_fieldc(opt_key, 0);
// the toolchange overrides live in their own optgroup, so search the whole page
Field* field = page->get_field(opt_key, 0);
if (field == nullptr) continue;
if (opt_key == "filament_long_retractions_when_cut") {
@@ -5007,8 +5026,43 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line("printer_structure", "printer_basic_information_advanced#printer-structure");
optgroup->append_single_option_line("gcode_flavor", "printer_basic_information_advanced#g-code-flavor");
optgroup->append_single_option_line("gcode_skip_config_block", "printer_basic_information_advanced#skip-g-code-config-block");
optgroup->append_single_option_line("pellet_modded_printer", "printer_basic_information_advanced#pellet-modded-printer");
optgroup->append_single_option_line("bbl_use_printhost", "printer_basic_information_advanced#use-3rd-party-print-host");
// "Printer Agent" dropdown - printer_agent is a coString; gui_type routes it to
// PrinterAgentChoice instead of a TextCtrl. Rows and values come from the live agent
// registry, and the value is stored as the agent-id string.
if (wxGetApp().getAgent() != nullptr)
{
auto registered_printer_agents = NetworkAgentFactory::get_registered_printer_agents();
if (!registered_printer_agents.empty())
{
ConfigOptionDef def;
def.type = coString;
def.gui_type = ConfigOptionDef::GUIType::printer_agent_select;
def.width = 3 * Field::def_width_wider() / 2;
def.label = L("Printer Agent");
def.tooltip = L("Select the network agent implementation for printer communication. "
"Available agents are registered at startup.");
def.mode = comAdvanced;
// Create the field without get_option() so it is not registered in m_opt_map.
// ConfigOptionsGroup handles printer_agent before the generic mapped write path.
Line agent_line = optgroup->create_single_option_line(Option(def, "printer_agent"));
optgroup->append_line(agent_line);
if (Field* agent_field = get_field("printer_agent"))
{
if (auto* choice = dynamic_cast<PrinterAgentChoice*>(agent_field); choice && choice->getWindow())
choice->set_value(m_config->opt_string("printer_agent"), false);
}
// Register by hand so the UnsavedChanges dialog can render a row for it.
wxGetApp().sidebar().get_searcher().add_key("printer_agent", m_type, optgroup->title,
optgroup->config_category());
}
}
optgroup->append_single_option_line("use_3mf");
optgroup->append_single_option_line("scan_first_layer" , "printer_basic_information_advanced#scan-first-layer");
optgroup->append_single_option_line("enable_power_loss_recovery", "printer_basic_information_advanced#power-loss-recovery");
@@ -5875,6 +5929,16 @@ void TabPrinter::reload_config()
// so update it implicitly
if (m_active_page && m_active_page->title() == "Multimaterial")
m_active_page->set_value("extruders_count", int(m_extruders_count));
// m_opt_map-driven reload does not cover printer_agent, so sync this custom field explicitly.
if (Field* agent_field = get_field("printer_agent"))
{
if (auto* choice = dynamic_cast<PrinterAgentChoice*>(agent_field); choice && choice->getWindow())
{
const std::string selected_agent = m_config->opt_string("printer_agent");
choice->set_value(selected_agent, false);
}
}
}
void TabPrinter::activate_selected_page(std::function<void()> throw_if_canceled)
@@ -5885,6 +5949,16 @@ void TabPrinter::activate_selected_page(std::function<void()> throw_if_canceled)
// so update it implicitly
if (m_active_page && m_active_page->title() == "Multimaterial")
m_active_page->set_value("extruders_count", int(m_extruders_count));
// m_opt_map-driven reload does not cover printer_agent, so sync this custom field explicitly.
if (Field* agent_field = get_field("printer_agent"))
{
if (auto* choice = dynamic_cast<PrinterAgentChoice*>(agent_field); choice && choice->getWindow())
{
const std::string selected_agent = m_config->opt_string("printer_agent");
choice->set_value(selected_agent, false);
}
}
}
void TabPrinter::clear_pages()
@@ -7843,6 +7917,24 @@ bool TabPrinter::apply_extruder_cnt_from_cache()
return false;
}
void TabPrinter::refresh_printer_agent_dropdown() const
{
auto* choice = dynamic_cast<PrinterAgentChoice*>(get_field("printer_agent"));
if (!choice || !choice->getWindow())
return;
const auto agents = NetworkAgentFactory::get_registered_printer_agents();
if (agents.empty())
return;
// why: rows live on PrinterAgentChoice now; rebuild them from the live registry and re-select the stored id.
const std::string selected_agent = wxGetApp().preset_bundle->printers.get_edited_preset()
.config.opt_string("printer_agent");
choice->reload_rows();
choice->set_value(selected_agent, false);
this->GetParent()->Layout();
}
bool Tab::validate_custom_gcodes()
{
if (m_type != Preset::TYPE_FILAMENT &&
@@ -8938,11 +9030,15 @@ ConfigManipulation Tab::get_config_manipulation()
return toggle_line(opt_key, toggle, opt_index >= 0 ? opt_index + 256 : opt_index);
};
auto cb_set_option_label = [this](const t_config_option_key &opt_key, const wxString &label, int opt_index) {
return set_option_label(opt_key, label, opt_index >= 0 ? opt_index + 256 : opt_index);
};
auto cb_value_change = [this](const std::string& opt_key, const boost::any& value) {
return on_value_change(opt_key, value);
};
return ConfigManipulation(load_config, cb_toggle_field, cb_toggle_line, cb_value_change, nullptr, this);
return ConfigManipulation(load_config, cb_toggle_field, cb_toggle_line, cb_value_change, nullptr, this, cb_set_option_label);
}
+2
View File
@@ -402,6 +402,7 @@ public:
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
void toggle_option(const std::string &opt_key, bool toggle, int opt_index = -1);
void toggle_line(const std::string &opt_key, bool toggle, int opt_index = -1); // BBS: hide some line
void set_option_label(const std::string &opt_key, const wxString &label, int opt_index = -1);
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString);
bool current_preset_is_dirty() const;
bool saved_preset_is_dirty() const;
@@ -674,6 +675,7 @@ public:
wxSizer* create_bed_shape_widget(wxWindow* parent);
void cache_extruder_cnt(const DynamicPrintConfig* config = nullptr);
bool apply_extruder_cnt_from_cache();
void refresh_printer_agent_dropdown() const;
};
class TabSLAMaterial : public Tab
+2 -2
View File
@@ -2,7 +2,7 @@
#include "../wxExtensions.hpp"
#ifdef __WXGTK3__
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
@@ -29,7 +29,7 @@ CheckBox::CheckBox(wxWindow *parent, int id)
Bind(wxEVT_LEAVE_WINDOW, &CheckBox::updateBitmap, this);
#endif
#ifdef __WXGTK3__
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
#endif
+5
View File
@@ -2,6 +2,10 @@
#include "../wxExtensions.hpp"
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
namespace Slic3r {
namespace GUI {
RadioBox::RadioBox(wxWindow *parent)
@@ -15,6 +19,7 @@ RadioBox::RadioBox(wxWindow *parent)
// Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { update(); e.Skip(); });
update();
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
wxSize bestSize = GetBestSize();
bestSize.IncTo(m_on.GetBmpSize());
SetSize(bestSize);
+9
View File
@@ -5,6 +5,10 @@
#include <wx/dcgraph.h>
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
BEGIN_EVENT_TABLE(SpinInput, StaticBox)
EVT_KEY_DOWN(SpinInput::keyPressed)
@@ -58,6 +62,11 @@ void SpinInput::Create(wxWindow *parent,
state_handler.attach({&label_color, &text_color});
state_handler.update_binds();
text_ctrl = new TextCtrl(this, wxID_ANY, text, {20, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER, wxTextValidator(wxFILTER_DIGITS));
#ifdef __WXGTK__
Slic3r::GUI::RemoveInputBorder(text_ctrl);
#endif
text_ctrl->SetFont(Label::Body_14);
text_ctrl->SetBackgroundColour(background_color.colorForStates(state_handler.states()));
text_ctrl->SetForegroundColour(text_color.colorForStates(state_handler.states()));
+2 -2
View File
@@ -12,7 +12,7 @@
#include "libslic3r/MacUtils.hpp"
#endif
#ifdef __WXGTK3__
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
@@ -37,7 +37,7 @@ SwitchButton::SwitchButton(wxWindow* parent, wxWindowID id)
Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { update(); e.Skip(); });
SetFont(Label::Body_12);
#ifdef __WXGTK3__
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
#endif
+9
View File
@@ -6,6 +6,10 @@
#include <wx/dcclient.h>
#include <wx/dcgraph.h>
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
BEGIN_EVENT_TABLE(TextInput, StaticBox)
EVT_PAINT(TextInput::paintEvent)
@@ -60,6 +64,11 @@ void TextInput::Create(wxWindow * parent,
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);
#ifdef __WXGTK__
Slic3r::GUI::RemoveInputBorder(text_ctrl);
#endif
text_ctrl->SetFont(Label::Body_14);
text_ctrl->SetInitialSize(text_ctrl->GetBestSize());
text_ctrl->SetBackgroundColour(background_color.colorForStates(state_handler.states()));
+4
View File
@@ -1022,6 +1022,10 @@ ScalableButton::ScalableButton( wxWindow * parent,
m_width = size.x * 10 / em;
m_height= size.y * 10 / em;
}
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
#endif
}