mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-23 19:02:10 +00:00
Enhance mixed filament management: Introduce manual pattern support for mixed filaments, allowing users to define custom layer patterns using '1' and '2' for component selection. Update parsing logic to accommodate new pattern definitions and ensure backward compatibility. Improve GUI to facilitate pattern entry and display, enhancing user experience in mixed filament configurations.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
@@ -261,35 +262,100 @@ static bool parse_row_definition(const std::string &row,
|
||||
unsigned int &b,
|
||||
bool &enabled,
|
||||
bool &custom,
|
||||
int &mix_b_percent)
|
||||
int &mix_b_percent,
|
||||
std::string &manual_pattern)
|
||||
{
|
||||
std::vector<int> values;
|
||||
std::stringstream ss(row);
|
||||
std::string token;
|
||||
while (std::getline(ss, token, ',')) {
|
||||
if (token.empty())
|
||||
auto trim_copy = [](const std::string &s) {
|
||||
size_t lo = 0;
|
||||
size_t hi = s.size();
|
||||
while (lo < hi && std::isspace(static_cast<unsigned char>(s[lo])))
|
||||
++lo;
|
||||
while (hi > lo && std::isspace(static_cast<unsigned char>(s[hi - 1])))
|
||||
--hi;
|
||||
return s.substr(lo, hi - lo);
|
||||
};
|
||||
|
||||
auto parse_int_token = [&trim_copy](const std::string &tok, int &out) {
|
||||
const std::string t = trim_copy(tok);
|
||||
if (t.empty())
|
||||
return false;
|
||||
try {
|
||||
values.push_back(std::stoi(token));
|
||||
size_t consumed = 0;
|
||||
int v = std::stoi(t, &consumed);
|
||||
if (consumed != t.size())
|
||||
return false;
|
||||
out = v;
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (values.size() != 4 && values.size() != 5)
|
||||
std::vector<std::string> tokens;
|
||||
std::stringstream ss(row);
|
||||
std::string token;
|
||||
while (std::getline(ss, token, ','))
|
||||
tokens.emplace_back(trim_copy(token));
|
||||
|
||||
if (tokens.size() < 4 || tokens.size() > 6)
|
||||
return false;
|
||||
|
||||
int values[5] = { 0, 0, 1, 1, 50 };
|
||||
if (tokens.size() == 4) {
|
||||
// Legacy: a,b,enabled,mix
|
||||
if (!parse_int_token(tokens[0], values[0]) ||
|
||||
!parse_int_token(tokens[1], values[1]) ||
|
||||
!parse_int_token(tokens[2], values[2]) ||
|
||||
!parse_int_token(tokens[3], values[4]))
|
||||
return false;
|
||||
} else {
|
||||
// Current: a,b,enabled,custom,mix[,pattern]
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
if (!parse_int_token(tokens[i], values[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (values[0] <= 0 || values[1] <= 0)
|
||||
return false;
|
||||
|
||||
a = unsigned(values[0]);
|
||||
b = unsigned(values[1]);
|
||||
enabled = (values[2] != 0);
|
||||
custom = (values.size() == 5) ? (values[3] != 0) : true;
|
||||
mix_b_percent = clamp_int(values.size() == 5 ? values[4] : values[3], 0, 100);
|
||||
custom = (tokens.size() == 4) ? true : (values[3] != 0);
|
||||
mix_b_percent = clamp_int(values[4], 0, 100);
|
||||
manual_pattern = (tokens.size() == 6) ? tokens[5] : std::string();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_pattern_separator(char c)
|
||||
{
|
||||
return std::isspace(static_cast<unsigned char>(c)) || c == '/' || c == '-' || c == '_' || c == '|' || c == ':' || c == ';';
|
||||
}
|
||||
|
||||
static bool decode_pattern_step(char c, char &out)
|
||||
{
|
||||
switch (std::tolower(static_cast<unsigned char>(c))) {
|
||||
case '1':
|
||||
case 'a':
|
||||
out = '1';
|
||||
return true;
|
||||
case '2':
|
||||
case 'b':
|
||||
out = '2';
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int mix_percent_from_normalized_pattern(const std::string &pattern)
|
||||
{
|
||||
if (pattern.empty())
|
||||
return 50;
|
||||
const int count_b = int(std::count(pattern.begin(), pattern.end(), '2'));
|
||||
return clamp_int(int(std::lround(100.0 * double(count_b) / double(pattern.size()))), 0, 100);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MixedFilamentManager
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -388,6 +454,7 @@ void MixedFilamentManager::add_custom_filament(unsigned int component_a,
|
||||
mf.mix_b_percent = clamp_int(mix_b_percent, 0, 100);
|
||||
mf.ratio_a = 1;
|
||||
mf.ratio_b = 1;
|
||||
mf.manual_pattern.clear();
|
||||
mf.enabled = true;
|
||||
mf.custom = true;
|
||||
m_mixed.push_back(std::move(mf));
|
||||
@@ -399,6 +466,24 @@ void MixedFilamentManager::clear_custom_entries()
|
||||
m_mixed.erase(std::remove_if(m_mixed.begin(), m_mixed.end(), [](const MixedFilament &mf) { return mf.custom; }), m_mixed.end());
|
||||
}
|
||||
|
||||
std::string MixedFilamentManager::normalize_manual_pattern(const std::string &pattern)
|
||||
{
|
||||
std::string normalized;
|
||||
normalized.reserve(pattern.size());
|
||||
for (char c : pattern) {
|
||||
char step = '\0';
|
||||
if (decode_pattern_step(c, step)) {
|
||||
normalized.push_back(step);
|
||||
continue;
|
||||
}
|
||||
if (is_pattern_separator(c))
|
||||
continue;
|
||||
// Unknown token => invalid pattern.
|
||||
return std::string();
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
||||
float lower_bound,
|
||||
float upper_bound,
|
||||
@@ -434,6 +519,9 @@ std::string MixedFilamentManager::serialize_custom_entries() const
|
||||
<< (mf.enabled ? 1 : 0) << ','
|
||||
<< (mf.custom ? 1 : 0) << ','
|
||||
<< clamp_int(mf.mix_b_percent, 0, 100);
|
||||
const std::string normalized_pattern = normalize_manual_pattern(mf.manual_pattern);
|
||||
if (!normalized_pattern.empty())
|
||||
ss << ',' << normalized_pattern;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
@@ -464,7 +552,8 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
bool enabled = true;
|
||||
bool custom = true;
|
||||
int mix = 50;
|
||||
if (!parse_row_definition(row, a, b, enabled, custom, mix)) {
|
||||
std::string manual_pattern;
|
||||
if (!parse_row_definition(row, a, b, enabled, custom, mix, manual_pattern)) {
|
||||
++skipped_rows;
|
||||
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries invalid row format: " << row;
|
||||
continue;
|
||||
@@ -485,7 +574,8 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
});
|
||||
if (it_auto != m_mixed.end()) {
|
||||
it_auto->enabled = enabled;
|
||||
it_auto->mix_b_percent = mix;
|
||||
it_auto->manual_pattern = normalize_manual_pattern(manual_pattern);
|
||||
it_auto->mix_b_percent = it_auto->manual_pattern.empty() ? mix : mix_percent_from_normalized_pattern(it_auto->manual_pattern);
|
||||
++updated_auto;
|
||||
continue;
|
||||
}
|
||||
@@ -497,6 +587,9 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
mf.mix_b_percent = mix;
|
||||
mf.ratio_a = 1;
|
||||
mf.ratio_b = 1;
|
||||
mf.manual_pattern = normalize_manual_pattern(manual_pattern);
|
||||
if (!mf.manual_pattern.empty())
|
||||
mf.mix_b_percent = mix_percent_from_normalized_pattern(mf.manual_pattern);
|
||||
mf.enabled = enabled;
|
||||
mf.custom = custom;
|
||||
m_mixed.push_back(std::move(mf));
|
||||
@@ -528,6 +621,13 @@ unsigned int MixedFilamentManager::resolve(unsigned int filament_id,
|
||||
|
||||
const MixedFilament &mf = m_mixed[idx];
|
||||
|
||||
// Manual pattern takes precedence when provided. Pattern uses repeating
|
||||
// steps: '1' => component_a, '2' => component_b.
|
||||
if (!mf.manual_pattern.empty()) {
|
||||
const int pos = safe_mod(layer_index, int(mf.manual_pattern.size()));
|
||||
return mf.manual_pattern[size_t(pos)] == '2' ? mf.component_b : mf.component_a;
|
||||
}
|
||||
|
||||
// Height-weighted cadence can be forced by the local-Z planner. The
|
||||
// regular gradient height mode keeps historical behavior (custom rows).
|
||||
const bool use_height_weighted = force_height_weighted || (m_gradient_mode == 1 && mf.custom);
|
||||
|
||||
@@ -26,6 +26,11 @@ struct MixedFilament
|
||||
// Blend percentage of component B in [0..100].
|
||||
int mix_b_percent = 50;
|
||||
|
||||
// Optional manual layer pattern for this mixed filament, encoded as a
|
||||
// string of '1' and '2'. '1' means component_a, '2' means component_b.
|
||||
// Example: "11112222" => AAAABBBB repeating.
|
||||
std::string manual_pattern;
|
||||
|
||||
// Whether this mixed filament is enabled (available for assignment).
|
||||
bool enabled = true;
|
||||
|
||||
@@ -42,6 +47,7 @@ struct MixedFilament
|
||||
ratio_a == rhs.ratio_a &&
|
||||
ratio_b == rhs.ratio_b &&
|
||||
mix_b_percent == rhs.mix_b_percent &&
|
||||
manual_pattern == rhs.manual_pattern &&
|
||||
enabled == rhs.enabled &&
|
||||
custom == rhs.custom;
|
||||
}
|
||||
@@ -93,6 +99,10 @@ public:
|
||||
std::string serialize_custom_entries() const;
|
||||
void load_custom_entries(const std::string &serialized, const std::vector<std::string> &filament_colours);
|
||||
|
||||
// Normalize a manual mixed-pattern string into compact '1'/'2' form.
|
||||
// Accepts separators and A/B aliases. Returns empty string if invalid.
|
||||
static std::string normalize_manual_pattern(const std::string &pattern);
|
||||
|
||||
// ---- Queries --------------------------------------------------------
|
||||
|
||||
// True when `filament_id` (1-based) refers to a mixed filament.
|
||||
|
||||
@@ -2489,21 +2489,28 @@ void Sidebar::update_mixed_filament_panel()
|
||||
|
||||
p->m_sizer_mixed_filaments->Clear(true);
|
||||
|
||||
// Header with title and add button.
|
||||
// Header with title and add buttons (gradient / pattern).
|
||||
auto *header_row = new wxPanel(p->m_panel_mixed_filaments, wxID_ANY);
|
||||
header_row->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
auto *header_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *mixed_title = new wxStaticText(header_row, wxID_ANY, _L("Mixed Filaments"));
|
||||
mixed_title->SetFont(Label::Head_14);
|
||||
header_sizer->Add(mixed_title, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(8));
|
||||
auto *add_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22)));
|
||||
add_btn->SetToolTip(_L("Add custom mixed filament"));
|
||||
add_btn->Enable(num_physical >= 2);
|
||||
add_btn->Bind(wxEVT_BUTTON, [this, preset_bundle, physical_colors, get_mixed_mode, get_mixed_int, get_mixed_float, get_mixed_bool, set_mixed_string, notify_mixed_change](wxCommandEvent &) {
|
||||
|
||||
auto add_custom_row = [this, preset_bundle, physical_colors, get_mixed_mode, get_mixed_int, get_mixed_float, get_mixed_bool, set_mixed_string, notify_mixed_change](bool pattern_row) {
|
||||
if (physical_colors.size() < 2)
|
||||
return;
|
||||
auto &mgr = preset_bundle->mixed_filaments;
|
||||
mgr.add_custom_filament(1, 2, 50, physical_colors);
|
||||
if (!mgr.mixed_filaments().empty()) {
|
||||
MixedFilament &row = mgr.mixed_filaments().back();
|
||||
if (pattern_row) {
|
||||
row.manual_pattern = "12";
|
||||
row.mix_b_percent = 50;
|
||||
} else {
|
||||
row.manual_pattern.clear();
|
||||
}
|
||||
}
|
||||
|
||||
int mode = get_mixed_mode(false) ? 1 : 0;
|
||||
float lo = get_mixed_float("mixed_filament_height_lower_bound", 0.04f);
|
||||
@@ -2523,8 +2530,24 @@ void Sidebar::update_mixed_filament_panel()
|
||||
update_dynamic_filament_list();
|
||||
update_mixed_filament_panel();
|
||||
});
|
||||
});
|
||||
header_sizer->Add(add_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(8));
|
||||
};
|
||||
|
||||
auto *grad_label = new wxStaticText(header_row, wxID_ANY, _L("Gradient"));
|
||||
header_sizer->Add(grad_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4));
|
||||
auto *add_gradient_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22)));
|
||||
add_gradient_btn->SetToolTip(_L("Add custom gradient mixed filament"));
|
||||
add_gradient_btn->Enable(num_physical >= 2);
|
||||
add_gradient_btn->Bind(wxEVT_BUTTON, [add_custom_row](wxCommandEvent &) { add_custom_row(false); });
|
||||
header_sizer->Add(add_gradient_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(8));
|
||||
|
||||
auto *pattern_label = new wxStaticText(header_row, wxID_ANY, _L("Pattern"));
|
||||
header_sizer->Add(pattern_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4));
|
||||
auto *add_pattern_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22)));
|
||||
add_pattern_btn->SetToolTip(_L("Add custom pattern mixed filament"));
|
||||
add_pattern_btn->Enable(num_physical >= 2);
|
||||
add_pattern_btn->Bind(wxEVT_BUTTON, [add_custom_row](wxCommandEvent &) { add_custom_row(true); });
|
||||
header_sizer->Add(add_pattern_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(8));
|
||||
|
||||
header_row->SetSizer(header_sizer);
|
||||
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(2));
|
||||
p->m_sizer_mixed_filaments->Add(header_row, 0, wxEXPAND);
|
||||
@@ -2648,22 +2671,38 @@ void Sidebar::update_mixed_filament_panel()
|
||||
picker_row->Add(choice_b, 1, wxALIGN_CENTER_VERTICAL);
|
||||
content_sizer->Add(picker_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8));
|
||||
|
||||
wxColour color_a = parse_mixed_color(physical_colors[size_t(component_a - 1)]);
|
||||
wxColour color_b = parse_mixed_color(physical_colors[size_t(component_b - 1)]);
|
||||
auto *blend_selector = new MixedGradientSelector(row, color_a, color_b, std::clamp(mf.mix_b_percent, 0, 100));
|
||||
auto *blend_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
blend_row->Add(blend_selector, 1, wxEXPAND);
|
||||
content_sizer->Add(blend_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8));
|
||||
const std::string normalized_pattern = MixedFilamentManager::normalize_manual_pattern(mf.manual_pattern);
|
||||
const bool pattern_row_mode = !normalized_pattern.empty();
|
||||
MixedGradientSelector *blend_selector = nullptr;
|
||||
wxStaticText *blend_label = nullptr;
|
||||
wxTextCtrl *pattern_ctrl = nullptr;
|
||||
|
||||
auto *blend_label = new wxStaticText(row, wxID_ANY, wxString::Format("%d%%/%d%%",
|
||||
100 - std::clamp(mf.mix_b_percent, 0, 100),
|
||||
std::clamp(mf.mix_b_percent, 0, 100)));
|
||||
auto *ratio_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
ratio_row->AddStretchSpacer(1);
|
||||
ratio_row->Add(blend_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
content_sizer->Add(ratio_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8));
|
||||
if (pattern_row_mode) {
|
||||
auto *pattern_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
pattern_row->Add(new wxStaticText(row, wxID_ANY, _L("Pattern")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(6));
|
||||
pattern_ctrl = new wxTextCtrl(row, wxID_ANY, from_u8(normalized_pattern), wxDefaultPosition,
|
||||
wxSize(FromDIP(170), -1), wxTE_PROCESS_ENTER);
|
||||
pattern_ctrl->SetToolTip(_L("Manual repeating pattern. Use 1/2 or A/B, example: 1/1/1/1/2/2/2/2."));
|
||||
pattern_row->Add(pattern_ctrl, 1, wxALIGN_CENTER_VERTICAL);
|
||||
content_sizer->Add(pattern_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8));
|
||||
} else {
|
||||
wxColour color_a = parse_mixed_color(physical_colors[size_t(component_a - 1)]);
|
||||
wxColour color_b = parse_mixed_color(physical_colors[size_t(component_b - 1)]);
|
||||
blend_selector = new MixedGradientSelector(row, color_a, color_b, std::clamp(mf.mix_b_percent, 0, 100));
|
||||
auto *blend_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
blend_row->Add(blend_selector, 1, wxEXPAND);
|
||||
content_sizer->Add(blend_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8));
|
||||
|
||||
auto apply_custom_row = [this, preset_bundle, mixed_id, choice_a, choice_b, blend_selector, blend_label, swatch, num_physical, get_mixed_mode, get_mixed_int, get_mixed_float, get_mixed_bool, set_mixed_string, notify_mixed_change](bool refresh_panel) {
|
||||
blend_label = new wxStaticText(row, wxID_ANY, wxString::Format("%d%%/%d%%",
|
||||
100 - std::clamp(mf.mix_b_percent, 0, 100),
|
||||
std::clamp(mf.mix_b_percent, 0, 100)));
|
||||
auto *ratio_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
ratio_row->AddStretchSpacer(1);
|
||||
ratio_row->Add(blend_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
content_sizer->Add(ratio_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8));
|
||||
}
|
||||
|
||||
auto apply_custom_row = [this, preset_bundle, mixed_id, choice_a, choice_b, blend_selector, blend_label, pattern_ctrl, swatch, num_physical, pattern_row_mode, get_mixed_mode, get_mixed_int, get_mixed_float, get_mixed_bool, set_mixed_string, notify_mixed_change](bool refresh_panel) {
|
||||
if (num_physical < 1)
|
||||
return;
|
||||
|
||||
@@ -2682,7 +2721,19 @@ void Sidebar::update_mixed_filament_panel()
|
||||
MixedFilament &cur = mfs[mixed_id];
|
||||
cur.component_a = unsigned(a);
|
||||
cur.component_b = unsigned(b);
|
||||
cur.mix_b_percent = std::clamp(blend_selector->value(), 0, 100);
|
||||
if (pattern_row_mode) {
|
||||
std::string normalized = MixedFilamentManager::normalize_manual_pattern(into_u8(pattern_ctrl->GetValue()));
|
||||
if (normalized.empty())
|
||||
normalized = "12";
|
||||
if (into_u8(pattern_ctrl->GetValue()) != normalized)
|
||||
pattern_ctrl->ChangeValue(from_u8(normalized));
|
||||
cur.manual_pattern = normalized;
|
||||
const int count_b = int(std::count(normalized.begin(), normalized.end(), '2'));
|
||||
cur.mix_b_percent = std::clamp((100 * count_b + int(normalized.size()) / 2) / std::max(1, int(normalized.size())), 0, 100);
|
||||
} else {
|
||||
cur.mix_b_percent = std::clamp(blend_selector ? blend_selector->value() : 50, 0, 100);
|
||||
cur.manual_pattern.clear();
|
||||
}
|
||||
cur.custom = true;
|
||||
|
||||
ConfigOptionStrings *co = preset_bundle->project_config.option<ConfigOptionStrings>("filament_colour");
|
||||
@@ -2691,10 +2742,12 @@ void Sidebar::update_mixed_filament_panel()
|
||||
|
||||
wxColour color_a_wx = parse_mixed_color(colors[size_t(a - 1)]);
|
||||
wxColour color_b_wx = parse_mixed_color(colors[size_t(b - 1)]);
|
||||
blend_selector->set_colors(color_a_wx, color_b_wx);
|
||||
if (blend_selector)
|
||||
blend_selector->set_colors(color_a_wx, color_b_wx);
|
||||
|
||||
cur.display_color = MixedFilamentManager::blend_color(colors[size_t(a - 1)], colors[size_t(b - 1)], 100 - cur.mix_b_percent, cur.mix_b_percent);
|
||||
blend_label->SetLabel(wxString::Format("%d%%/%d%%", 100 - cur.mix_b_percent, cur.mix_b_percent));
|
||||
if (blend_label)
|
||||
blend_label->SetLabel(wxString::Format("%d%%/%d%%", 100 - cur.mix_b_percent, cur.mix_b_percent));
|
||||
swatch->SetBackgroundColour(wxColour(cur.display_color));
|
||||
swatch->Refresh();
|
||||
|
||||
@@ -2724,7 +2777,12 @@ void Sidebar::update_mixed_filament_panel()
|
||||
|
||||
choice_a->Bind(wxEVT_CHOICE, [apply_custom_row](wxCommandEvent &) { apply_custom_row(true); });
|
||||
choice_b->Bind(wxEVT_CHOICE, [apply_custom_row](wxCommandEvent &) { apply_custom_row(true); });
|
||||
blend_selector->Bind(wxEVT_SLIDER, [apply_custom_row](wxCommandEvent &) { apply_custom_row(false); });
|
||||
if (blend_selector)
|
||||
blend_selector->Bind(wxEVT_SLIDER, [apply_custom_row](wxCommandEvent &) { apply_custom_row(false); });
|
||||
if (pattern_ctrl) {
|
||||
pattern_ctrl->Bind(wxEVT_TEXT_ENTER, [apply_custom_row](wxCommandEvent &) { apply_custom_row(true); });
|
||||
pattern_ctrl->Bind(wxEVT_KILL_FOCUS, [apply_custom_row](wxFocusEvent &evt) { apply_custom_row(true); evt.Skip(); });
|
||||
}
|
||||
}
|
||||
|
||||
row_sizer->Add(content_sizer, 1, wxEXPAND);
|
||||
|
||||
Reference in New Issue
Block a user