Port EditGCodeDialog from PrusaSlicer (#3417)

This is a port of the EditGCodeDialog from PrusaSlicer 2.7.x. There were
a few changes made to make it a bit more functional. Also, it isn't
quite fully complete, but it should be in a very usable state.

General Changes:
- Implement UndoValueUIManager and EditValueUI in Field
- Implement EditGCodeDialog and add buttons to the tabs
- Other minor changes to accommodate the new classes

Differences from PrusaSlicer's Implementation:
- backported to wxWidgets 3.1.5 (reverse commit 8770c4b7 after updating
to 3.2.x)
- icons have been updated to use Orca colors
- improve the report that tells you if certain placeholders have not
been defined properly for the dialog. It now shows all issues at once
rather than having to fix then recompile to see the next issue.
- allow the use of the cmake option `ORCA_CHECK_GCODE_PLACEHOLDERS` to
toggle the above report since our workflow rarely uses debug mode.
- if a custom gcode value is not set when checking gcode placeholders, a
testing value is set. Custom gcode is not parsed if it is empty, and the
only way to check if the placeholders are all defined is by running the
placeholder operation on the custom gcode.
- some calls to `print.config()` in Gcode.cpp were changed to `m_config`
to support the above testing values feature (only m_config is modified
with the placeholders and `print.config()` would return an empty string)
- a macro has been added to quickly add a definition to
SlicingStatesConfigDefs with less boiler plate (it could technically be
used for any ConfigOptionDef, but that would hurt interoperability with
PS. I tried to not use the macro for too many PS defined definitions.)
- the presets are now also categorized by the page they are on in their
tab
<table>
<tr>
 <td>Prusa
 <td>Orca
<tr>
 <td>
<img
src="27cb4f48-d225-4563-9aeb-b2b461f8bff5"
/>
 <td>
<img
src="4fcd8cde-2427-4d1a-a0ed-1738b570b919"
/>
</table>

TODO:
- [x] Make sure all linux fixes have been applied
- [x] Finish adding "universal" gcode options
- [x] add search function to dataview (maybe?)
- [x] determine if any options are being left out of the preset
categories by getting options from Tab rather than Presets. If so,
consider adding outside of the groupings
This commit is contained in:
SoftFever 2024-01-24 19:56:18 +08:00 committed by GitHub
commit 5ff00fb48c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 2155 additions and 122 deletions

View file

@ -212,6 +212,8 @@ set(SLIC3R_GUI_SOURCES
GUI/PresetComboBoxes.cpp
GUI/BitmapComboBox.hpp
GUI/BitmapComboBox.cpp
GUI/EditGCodeDialog.hpp
GUI/EditGCodeDialog.cpp
GUI/SavePresetDialog.hpp
GUI/SavePresetDialog.cpp
GUI/GUI_Colors.hpp

View file

@ -0,0 +1,976 @@
#include "EditGCodeDialog.hpp"
#include <vector>
#include <string>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/button.h>
#include <wx/wupdlock.h>
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
#include "format.hpp"
#include "Tab.hpp"
#include "wxExtensions.hpp"
#include "BitmapCache.hpp"
#include "ExtraRenderers.hpp"
#include "MsgDialog.hpp"
#include "Plater.hpp"
#include "libslic3r/PlaceholderParser.hpp"
#include "libslic3r/Preset.hpp"
#include "libslic3r/Print.hpp"
#define BTN_GAP FromDIP(20)
#define BTN_SIZE wxSize(FromDIP(58), FromDIP(24))
namespace Slic3r {
namespace GUI {
//------------------------------------------
// EditGCodeDialog
//------------------------------------------
EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const std::string& value) :
DPIDialog(parent, wxID_ANY, format_wxstr(_L("Edit Custom G-code (%1%)"), key), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
{
SetFont(wxGetApp().normal_font());
SetBackgroundColour(*wxWHITE);
wxGetApp().UpdateDarkUI(this);
wxGetApp().UpdateDlgDarkUI(this);
int border = 10;
int em = em_unit();
wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Built-in placeholders (Double click item to add to G-code)") + ":");
auto* grid_sizer = new wxFlexGridSizer(1, 3, 5, 15);
grid_sizer->SetFlexibleDirection(wxBOTH);
auto* param_sizer = new wxBoxSizer(wxVERTICAL);
m_search_bar = new wxSearchCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_search_bar->ShowSearchButton(true);
m_search_bar->ShowCancelButton(true);
m_search_bar->SetDescriptiveText(_L("Search gcode placeholders"));
m_search_bar->SetForegroundColour(*wxBLACK);
wxGetApp().UpdateDarkUI(m_search_bar);
m_search_bar->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent&) {
// this->on_search_update();
});
m_search_bar->Bind(wxEVT_COMMAND_TEXT_UPDATED, [this](wxCommandEvent&) {
this->on_search_update();
});
param_sizer->Add(m_search_bar, 0, wxEXPAND | wxALL, border);
m_params_list = new ParamsViewCtrl(this, wxSize(em * 45, em * 70));
m_params_list->SetFont(wxGetApp().code_font());
wxGetApp().UpdateDarkUI(m_params_list);
param_sizer->Add(m_params_list, 0, wxEXPAND | wxALL, border);
m_add_btn = new ScalableButton(this, wxID_ANY, "add_copies");
m_add_btn->SetToolTip(_L("Add selected placeholder to G-code"));
m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 75, em * 70), wxTE_MULTILINE
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
);
m_gcode_editor->SetFont(wxGetApp().code_font());
m_gcode_editor->SetInsertionPointEnd();
wxGetApp().UpdateDarkUI(m_gcode_editor);
grid_sizer->Add(param_sizer, 1, wxEXPAND);
grid_sizer->Add(m_add_btn, 0, wxTOP, m_params_list->GetSize().y/2);
grid_sizer->Add(m_gcode_editor, 2, wxEXPAND);
grid_sizer->AddGrowableRow(0, 1);
grid_sizer->AddGrowableCol(0, 1);
grid_sizer->AddGrowableCol(2, 1);
m_param_label = new wxStaticText(this, wxID_ANY, _L("Select placeholder"));
m_param_label->SetFont(wxGetApp().bold_font());
m_param_description = new wxStaticText(this, wxID_ANY, wxEmptyString);
//Orca: use custom buttons
auto btn_sizer = create_btn_sizer(wxOK | wxCANCEL);
for(auto btn : m_button_list)
wxGetApp().UpdateDarkUI(btn.second);
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(grid_sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(m_param_label , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(m_param_description , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(btn_sizer , 0, wxEXPAND | wxALL, border);
SetSizer(topSizer);
topSizer->SetSizeHints(this);
this->Fit();
this->Layout();
this->CenterOnScreen();
init_params_list(key);
bind_list_and_button();
}
EditGCodeDialog::~EditGCodeDialog()
{
// To avoid redundant process of wxEVT_DATAVIEW_SELECTION_CHANGED after dialog distroing (on Linux)
// unbind this event from params_list
m_params_list->Unbind(wxEVT_DATAVIEW_SELECTION_CHANGED, &EditGCodeDialog::selection_changed, this);
}
std::string EditGCodeDialog::get_edited_gcode() const
{
return into_u8(m_gcode_editor->GetValue());
}
void EditGCodeDialog::on_search_update()
{
wxString search_text = m_search_bar->GetValue().Lower();
if (search_text.empty())
m_params_list->model->FinishSearch();
else
m_params_list->model->RefreshSearch(search_text);
}
static ParamType get_type(const std::string& opt_key, const ConfigOptionDef& opt_def)
{
return opt_def.is_scalar() ? ParamType::Scalar : ParamType::Vector;
}
void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name)
{
const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders();
const auto& specific_params = custom_gcode_placeholders.count(custom_gcode_name) > 0 ?
custom_gcode_placeholders.at(custom_gcode_name) : t_config_option_keys({});
// Add slicing states placeholders
wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing State"), "custom-gcode_slicing-state_global");
if (!cgp_ro_slicing_states_config_def.empty()) {
wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed");
for (const auto& [opt_key, def]: cgp_ro_slicing_states_config_def.options)
m_params_list->AppendParam(read_only, get_type(opt_key, def), opt_key);
}
if (!cgp_rw_slicing_states_config_def.empty()) {
wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read Write"), "lock_open");
for (const auto& [opt_key, def] : cgp_rw_slicing_states_config_def.options)
m_params_list->AppendParam(read_write, get_type(opt_key, def), opt_key);
}
// add other universal params, which are related to slicing state
if (!cgp_other_slicing_states_config_def.empty()) {
slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "custom-gcode_slicing-state");
for (const auto& [opt_key, def] : cgp_other_slicing_states_config_def.options)
m_params_list->AppendParam(slicing_state, get_type(opt_key, def), opt_key);
}
// Add universal placeholders
{
// Add print statistics subgroup
if (!cgp_print_statistics_config_def.empty()) {
wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print Statistics"), "custom-gcode_stats");
for (const auto& [opt_key, def] : cgp_print_statistics_config_def.options)
m_params_list->AppendParam(statistics, get_type(opt_key, def), opt_key);
}
// Add objects info subgroup
if (!cgp_objects_info_config_def.empty()) {
wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects Info"), "custom-gcode_object-info");
for (const auto& [opt_key, def] : cgp_objects_info_config_def.options)
m_params_list->AppendParam(objects_info, get_type(opt_key, def), opt_key);
}
// Add dimensions subgroup
if (!cgp_dimensions_config_def.empty()) {
wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Dimensions"), "custom-gcode_measure");
for (const auto& [opt_key, def] : cgp_dimensions_config_def.options)
m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key);
}
// Add temperature subgroup
if (!cgp_temperatures_config_def.empty()) {
wxDataViewItem temperatures = m_params_list->AppendGroup(_L("Temperatures"), "custom-gcode_temperature");
for (const auto& [opt_key, def] : cgp_temperatures_config_def.options)
m_params_list->AppendParam(temperatures, get_type(opt_key, def), opt_key);
}
// Add timestamp subgroup
if (!cgp_timestamps_config_def.empty()) {
wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Timestamps"), "print-time");
for (const auto& [opt_key, def] : cgp_timestamps_config_def.options)
m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key);
}
}
// Add specific placeholders
if (!specific_params.empty()) {
wxDataViewItem group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "custom-gcode_gcode");
for (const auto& opt_key : specific_params)
if (auto def = custom_gcode_specific_config_def.get(opt_key); def && def->type != coNone) {
m_params_list->AppendParam(group, get_type(opt_key, *def), opt_key);
}
m_params_list->Expand(group);
}
// Add placeholders from presets
wxDataViewItem presets = add_presets_placeholders();
// add other params which are related to presets
if (!cgp_other_presets_config_def.empty())
for (const auto& [opt_key, def] : cgp_other_presets_config_def.options)
m_params_list->AppendParam(presets, get_type(opt_key, def), opt_key);
}
wxDataViewItem EditGCodeDialog::add_presets_placeholders()
{
auto get_set_from_vec = [](const std::vector<std::string>&vec) {
return std::set(vec.begin(), vec.end());
};
const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF;
const std::set<std::string> print_options = get_set_from_vec(is_fff ? Preset::print_options() : Preset::sla_print_options());
const std::set<std::string> material_options = get_set_from_vec(is_fff ? Preset::filament_options() : Preset::sla_material_options());
const std::set<std::string> printer_options = get_set_from_vec(is_fff ? Preset::printer_options() : Preset::sla_printer_options());
const auto& full_config = wxGetApp().preset_bundle->full_config();
const auto& tab_list = wxGetApp().tabs_list;
Tab* tab_print;
Tab* tab_filament;
Tab* tab_printer;
for (const auto tab : tab_list) {
if (tab->m_type == Preset::TYPE_PRINT)
tab_print = tab;
else if (tab->m_type == Preset::TYPE_FILAMENT)
tab_filament = tab;
else if (tab->m_type == Preset::TYPE_PRINTER)
tab_printer = tab;
}
// Orca: create subgroups from the pages of the tabs
auto init_from_tab = [this, full_config](wxDataViewItem parent, Tab* tab, const set<string>& preset_keys){
set extra_keys(preset_keys);
for (const auto& page : tab->m_pages) {
wxDataViewItem subgroup = m_params_list->AppendSubGroup(parent, page->title(), "empty");
std::set<std::string> opt_keys;
for (const auto& optgroup : page->m_optgroups)
for (const auto& opt : optgroup->opt_map())
opt_keys.emplace(opt.first);
for (const auto& opt_key : opt_keys)
if (const ConfigOption* optptr = full_config.optptr(opt_key)) {
extra_keys.erase(opt_key);
m_params_list->AppendParam(subgroup, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt_key);
}
}
for (auto opt_key : extra_keys)
if (const ConfigOption* optptr = full_config.optptr(opt_key))
m_params_list->AppendParam(parent, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt_key);
};
wxDataViewItem group = m_params_list->AppendGroup(_L("Presets"), "cog");
wxDataViewItem print = m_params_list->AppendSubGroup(group, _L("Print settings"), "cog");
init_from_tab(print, tab_print, print_options);
wxDataViewItem material = m_params_list->AppendSubGroup(group, _(is_fff ? L("Filament settings") : L("SLA Materials settings")), is_fff ? "filament" : "resin");
init_from_tab(material, tab_filament, material_options);
wxDataViewItem printer = m_params_list->AppendSubGroup(group, _L("Printer settings"), is_fff ? "printer" : "sla_printer");
init_from_tab(printer, tab_printer, printer_options);
return group;
}
void EditGCodeDialog::add_selected_value_to_gcode()
{
const wxString val = m_params_list->GetSelectedValue();
if (val.IsEmpty())
return;
m_gcode_editor->WriteText(m_gcode_editor->GetInsertionPoint() == m_gcode_editor->GetLastPosition() ? "\n" + val : val);
if (val.Last() == ']') {
const long new_pos = m_gcode_editor->GetInsertionPoint();
if (val[val.Len() - 2] == '[')
m_gcode_editor->SetInsertionPoint(new_pos - 1); // set cursor into brackets
else
m_gcode_editor->SetSelection(new_pos - 17, new_pos - 1); // select "current_extruder"
}
m_gcode_editor->SetFocus();
}
void EditGCodeDialog::selection_changed(wxDataViewEvent& evt)
{
wxString label;
wxString description;
const std::string opt_key = m_params_list->GetSelectedParamKey();
if (!opt_key.empty()) {
const ConfigOptionDef* def { nullptr };
for (const ConfigDef* config: std::initializer_list<const ConfigDef*> {
&custom_gcode_specific_config_def,
&cgp_ro_slicing_states_config_def,
&cgp_rw_slicing_states_config_def,
&cgp_other_slicing_states_config_def,
&cgp_print_statistics_config_def,
&cgp_objects_info_config_def,
&cgp_dimensions_config_def,
&cgp_temperatures_config_def,
&cgp_timestamps_config_def,
&cgp_other_presets_config_def
}) {
if (config->has(opt_key)) {
def = config->get(opt_key);
break;
}
}
// Orca: move below checking for def in custom defined gcode placeholders
// This allows custom placeholders to override the default ones for this dialog
// Override custom def if selection is within the preset category
if (!def || m_params_list->GetSelectedTopLevelCategory() == "Presets") {
const auto& full_config = wxGetApp().preset_bundle->full_config();
if (const ConfigDef* config_def = full_config.def(); config_def && config_def->has(opt_key)) {
def = config_def->get(opt_key);
}
}
if (def) {
const ConfigOptionType scalar_type = def->is_scalar() ? def->type : static_cast<ConfigOptionType>(def->type - coVectorType);
wxString type_str = scalar_type == coNone ? "none" :
scalar_type == coFloat ? "float" :
scalar_type == coInt ? "integer" :
scalar_type == coString ? "string" :
scalar_type == coPercent ? "percent" :
scalar_type == coFloatOrPercent ? "float or percent" :
scalar_type == coPoint ? "point" :
scalar_type == coBool ? "bool" :
scalar_type == coEnum ? "enum" : "undef";
if (!def->is_scalar())
type_str += "[]";
label = (!def || (def->full_label.empty() && def->label.empty()) ) ? format_wxstr("%1%\n(%2%)", opt_key, type_str) :
(!def->full_label.empty() && !def->label.empty() ) ?
format_wxstr("%1% > %2%\n(%3%)", _(def->full_label), _(def->label), type_str) :
format_wxstr("%1%\n(%2%)", def->label.empty() ? _(def->full_label) : _(def->label), type_str);
if (def)
description = get_wraped_wxString(_(def->tooltip), 120);
}
else
label = "Undef optptr";
}
m_param_label->SetLabel(label);
m_param_description->SetLabel(description);
Layout();
}
void EditGCodeDialog::bind_list_and_button()
{
m_params_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &EditGCodeDialog::selection_changed, this);
m_params_list->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& ) {
add_selected_value_to_gcode();
});
m_add_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
add_selected_value_to_gcode();
});
}
void EditGCodeDialog::on_dpi_changed(const wxRect&suggested_rect)
{
const int& em = em_unit();
//Orca: use custom buttons
for (auto button_item : m_button_list)
{
if (button_item.first == wxOK) {
button_item.second->SetMinSize(BTN_SIZE);
button_item.second->SetCornerRadius(FromDIP(12));
}
if (button_item.first == wxCANCEL) {
button_item.second->SetMinSize(BTN_SIZE);
button_item.second->SetCornerRadius(FromDIP(12));
}
}
const wxSize& size = wxSize(45 * em, 35 * em);
SetMinSize(size);
Fit();
Refresh();
}
void EditGCodeDialog::on_sys_color_changed()
{
m_add_btn->msw_rescale();
}
//Orca
wxBoxSizer* EditGCodeDialog::create_btn_sizer(long flags)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
btn_sizer->AddStretchSpacer();
StateColor ok_btn_bg(
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal)
);
StateColor ok_btn_bd(
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal)
);
StateColor ok_btn_text(
std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal)
);
StateColor cancel_btn_bg(
std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal)
);
StateColor cancel_btn_bd_(
std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Normal)
);
StateColor cancel_btn_text(
std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Normal)
);
StateColor calc_btn_bg(
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal)
);
StateColor calc_btn_bd(
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal)
);
StateColor calc_btn_text(
std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal)
);
if (flags & wxOK) {
Button* ok_btn = new Button(this, _L("OK"));
ok_btn->SetMinSize(BTN_SIZE);
ok_btn->SetCornerRadius(FromDIP(12));
ok_btn->SetBackgroundColor(ok_btn_bg);
ok_btn->SetBorderColor(ok_btn_bd);
ok_btn->SetTextColor(ok_btn_text);
ok_btn->SetFocus();
ok_btn->SetId(wxID_OK);
btn_sizer->Add(ok_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, BTN_GAP);
m_button_list[wxOK] = ok_btn;
}
if (flags & wxCANCEL) {
Button* cancel_btn = new Button(this, _L("Cancel"));
cancel_btn->SetMinSize(BTN_SIZE);
cancel_btn->SetCornerRadius(FromDIP(12));
cancel_btn->SetBackgroundColor(cancel_btn_bg);
cancel_btn->SetBorderColor(cancel_btn_bd_);
cancel_btn->SetTextColor(cancel_btn_text);
cancel_btn->SetId(wxID_CANCEL);
btn_sizer->Add(cancel_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, BTN_GAP / 2);
m_button_list[wxCANCEL] = cancel_btn;
}
return btn_sizer;
}
const std::map<ParamType, std::string> ParamsInfo {
// Type BitmapName
{ ParamType::Scalar, "custom-gcode_single" },
{ ParamType::Vector, "custom-gcode_vector" },
{ ParamType::FilamentVector,"custom-gcode_vector-index" },
};
static void make_bold(wxString& str)
{
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
str = format_wxstr("<b>%1%</b>", str);
#endif
}
static void highlight(wxString& str)
{
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
str = format_wxstr("<span bgcolor=\"#009688\">%1%</span>", str);
#endif
}
// ----------------------------------------------------------------------------
// ParamsModelNode: a node inside ParamsModel
// ----------------------------------------------------------------------------
ParamsNode::ParamsNode(const wxString& group_name, const std::string& icon_name, wxDataViewCtrl* ctrl)
: icon_name(icon_name)
, text(group_name)
, m_ctrl(ctrl)
, m_bold(true)
{
}
ParamsNode::ParamsNode( ParamsNode * parent,
const wxString& sub_group_name,
const std::string& icon_name,
wxDataViewCtrl* ctrl)
: m_parent(parent)
, icon_name(icon_name)
, text(sub_group_name)
, m_ctrl(ctrl)
, m_bold(true)
{
}
ParamsNode::ParamsNode( ParamsNode* parent,
ParamType param_type,
const std::string& param_key,
wxDataViewCtrl* ctrl)
: m_parent(parent)
, m_param_type(param_type)
, m_container(false)
, param_key(param_key)
, m_ctrl(ctrl)
{
text = from_u8(param_key);
if (param_type == ParamType::Vector)
text += "[]";
else if (param_type == ParamType::FilamentVector)
text += "[current_extruder]";
icon_name = ParamsInfo.at(param_type);
}
wxString ParamsNode::GetFormattedText()
{
wxString formatted_text(text);
if (m_highlight_index) {
wxString substr = formatted_text.substr(m_highlight_index->first, m_highlight_index->second);
formatted_text = formatted_text.Remove(m_highlight_index->first, m_highlight_index->second);
highlight(substr);
formatted_text.insert(m_highlight_index->first, substr);
}
if (m_bold)
make_bold(formatted_text);
return formatted_text;
}
void ParamsNode::StartSearch()
{
const wxDataViewItem item(this);
m_expanded_before_search = m_ctrl->IsExpanded(item);
if (!GetChildren().empty())
for (const auto& child : GetChildren())
child->StartSearch();
}
void ParamsNode::RefreshSearch(const wxString& search_text)
{
if (!GetChildren().empty())
for (auto& child : GetChildren())
child->RefreshSearch(search_text);
if (GetEnabledChildren().empty())
if (auto pos = text.find(search_text); IsParamNode() && pos != wxString::npos) {
m_highlight_index = make_unique<pair<int, int>>(pos, search_text.Len());
Enable();
} else {
Disable();
}
else
Enable();
}
void ParamsNode::FinishSearch()
{
Enable();
m_highlight_index.reset();
const wxDataViewItem item(this);
if (!GetChildren().empty())
for (const auto& child : GetChildren())
child->FinishSearch();
m_expanded_before_search ? m_ctrl->Expand(item) : m_ctrl->Collapse(item);
}
wxDataViewItemArray ParamsNode::GetEnabledChildren() {
wxDataViewItemArray array;
for (const std::unique_ptr<ParamsNode>& child : m_children)
if (child->IsEnabled())
array.Add(wxDataViewItem(child.get()));
return array;
}
// ----------------------------------------------------------------------------
// ParamsModel
// ----------------------------------------------------------------------------
ParamsModel::ParamsModel()
{
}
wxDataViewItem ParamsModel::AppendGroup(const wxString& group_name,
const std::string& icon_name)
{
m_group_nodes.emplace_back(std::make_unique<ParamsNode>(group_name, icon_name, m_ctrl));
wxDataViewItem parent(nullptr);
wxDataViewItem child((void*)m_group_nodes.back().get());
ItemAdded(parent, child);
m_ctrl->Expand(parent);
return child;
}
wxDataViewItem ParamsModel::AppendSubGroup(wxDataViewItem parent,
const wxString& sub_group_name,
const std::string& icon_name)
{
ParamsNode* parent_node = static_cast<ParamsNode*>(parent.GetID());
if (!parent_node)
return wxDataViewItem(0);
parent_node->Append(std::make_unique<ParamsNode>(parent_node, sub_group_name, icon_name, m_ctrl));
const wxDataViewItem sub_group_item((void*)parent_node->GetChildren().back().get());
ItemAdded(parent, sub_group_item);
return sub_group_item;
}
wxDataViewItem ParamsModel::AppendParam(wxDataViewItem parent,
ParamType param_type,
const std::string& param_key)
{
ParamsNode* parent_node = static_cast<ParamsNode*>(parent.GetID());
if (!parent_node)
return wxDataViewItem(0);
parent_node->Append(std::make_unique<ParamsNode>(parent_node, param_type, param_key, m_ctrl));
const wxDataViewItem child_item((void*)parent_node->GetChildren().back().get());
ItemAdded(parent, child_item);
return child_item;
}
wxString ParamsModel::GetParamName(wxDataViewItem item)
{
if (item.IsOk()) {
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
if (node->IsParamNode())
return node->text;
}
return wxEmptyString;
}
std::string ParamsModel::GetParamKey(wxDataViewItem item)
{
if (item.IsOk()) {
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
return node->param_key;
}
return std::string();
}
std::string ParamsModel::GetTopLevelCategory(wxDataViewItem item)
{
if (item.IsOk()) {
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
while (!node->IsGroupNode())
node = node->GetParent();
return node->text.ToStdString();
}
return std::string();
}
void ParamsModel::RefreshSearch(const wxString& search_text)
{
if (!m_currently_searching) { // if not currently searching, save expansion state for all items
for (const auto& node : m_group_nodes)
node->StartSearch();
m_currently_searching = true;
}
for (const auto& node : m_group_nodes)
node->RefreshSearch(search_text); //Enable/Disable node based on search
Cleared(); //Reload the model into the control
for (const auto& node : m_group_nodes) // (re)expand all
m_ctrl->ExpandChildren(wxDataViewItem(node.get()));
}
void ParamsModel::FinishSearch()
{
RefreshSearch("");
Cleared();
if (m_currently_searching) {
for (const auto& node : m_group_nodes)
node->FinishSearch();
m_currently_searching = false;
}
}
wxDataViewItem ParamsModel::Delete(const wxDataViewItem& item)
{
auto ret_item = wxDataViewItem(nullptr);
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
if (!node) // happens if item.IsOk()==false
return ret_item;
// first remove the node from the parent's array of children;
// NOTE: m_group_nodes is only a vector of _pointers_
// thus removing the node from it doesn't result in freeing it
ParamsNodePtrArray& children = node->GetChildren();
// Delete all children
while (!children.empty())
Delete(wxDataViewItem(children.back().get()));
auto node_parent = node->GetParent();
ParamsNodePtrArray& parents_children = node_parent ? node_parent->GetChildren() : m_group_nodes;
auto it = find_if(parents_children.begin(), parents_children.end(),
[node](std::unique_ptr<ParamsNode>& child) { return child.get() == node; });
assert(it != parents_children.end());
it = parents_children.erase(it);
if (it != parents_children.end())
ret_item = wxDataViewItem(it->get());
wxDataViewItem parent(node_parent);
// set m_container to FALSE if parent has no child
if (node_parent) {
#ifndef __WXGTK__
if (node_parent->GetChildren().empty())
node_parent->SetContainer(false);
#endif //__WXGTK__
ret_item = parent;
}
// notify control
ItemDeleted(parent, item);
return ret_item;
}
void ParamsModel::Clear()
{
while (!m_group_nodes.empty())
Delete(wxDataViewItem(m_group_nodes.back().get()));
}
void ParamsModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
{
assert(item.IsOk());
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
if (col == (unsigned int)0)
#ifdef __linux__
// variant << wxDataViewIconText(node->GetFormattedText(), get_bmp_bundle(node->icon_name)->GetIconFor(m_ctrl->GetParent())); //TODO: update to bundle with wx update
{
wxIcon icon;
icon.CopyFromBitmap(create_scaled_bitmap(node->icon_name, m_ctrl->GetParent()));
variant << wxDataViewIconText(node->GetFormattedText(), icon);
}
#else
// variant << DataViewBitmapText(node->GetFormattedText(), get_bmp_bundle(node->icon_name)->GetBitmapFor(m_ctrl->GetParent())); //TODO: update to bundle with wx update
variant << DataViewBitmapText(node->GetFormattedText(), create_scaled_bitmap(node->icon_name, m_ctrl->GetParent()));
#endif //__linux__
else
wxLogError("DiffModel::GetValue: wrong column %d", col);
}
bool ParamsModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
{
assert(item.IsOk());
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
if (col == (unsigned int)0) {
#ifdef __linux__
wxDataViewIconText data;
data << variant;
node->icon = data.GetIcon();
#else
DataViewBitmapText data;
data << variant;
node->icon = data.GetBitmap();
#endif
node->text = data.GetText();
return true;
}
wxLogError("DiffModel::SetValue: wrong column");
return false;
}
wxDataViewItem ParamsModel::GetParent(const wxDataViewItem&item) const
{
// the invisible root node has no parent
if (!item.IsOk())
return wxDataViewItem(nullptr);
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
if (node->IsGroupNode())
return wxDataViewItem(nullptr);
return wxDataViewItem((void*)node->GetParent());
}
bool ParamsModel::IsContainer(const wxDataViewItem& item) const
{
// the invisble root node can have children
if (!item.IsOk())
return true;
ParamsNode* node = static_cast<ParamsNode*>(item.GetID());
return node->IsContainer();
}
unsigned int ParamsModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
{
ParamsNode* parent_node = (ParamsNode*)parent.GetID();
if (parent_node == nullptr) {
for (const auto& group : m_group_nodes)
if (group->IsEnabled())
array.Add(wxDataViewItem((void*)group.get()));
}
else {
const ParamsNodePtrArray& children = parent_node->GetChildren();
for (const std::unique_ptr<ParamsNode>& child : children)
if (child->IsEnabled())
array.Add(wxDataViewItem((void*)child.get()));
}
return array.Count();
}
unsigned int ParamsModel::GetColumnCount() const { return 1; }
wxString ParamsModel::GetColumnType(unsigned int col) const {
#ifdef __linux__
return wxT("wxDataViewIconText");
#else
return wxT("DataViewBitmapText");
#endif
}
// ----------------------------------------------------------------------------
// ParamsViewCtrl
// ----------------------------------------------------------------------------
ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSize size)
: wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_SINGLE | wxDV_NO_HEADER// | wxDV_ROW_LINES
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
),
m_em_unit(em_unit(parent))
{
wxGetApp().UpdateDVCDarkUI(this);
model = new ParamsModel();
this->AssociateModel(model);
model->SetAssociatedControl(this);
#ifdef __linux__
wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer();
#ifdef SUPPORTS_MARKUP
rd->EnableMarkup(true);
#endif
wxDataViewColumn* column = new wxDataViewColumn("", rd, 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT);
#else
wxDataViewColumn* column = new wxDataViewColumn("", new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
#endif //__linux__
this->AppendColumn(column);
this->SetExpanderColumn(column);
}
wxDataViewItem ParamsViewCtrl::AppendGroup(const wxString& group_name, const std::string& icon_name)
{
return model->AppendGroup(group_name, icon_name);
}
wxDataViewItem ParamsViewCtrl::AppendSubGroup( wxDataViewItem parent,
const wxString& sub_group_name,
const std::string& icon_name)
{
return model->AppendSubGroup(parent, sub_group_name, icon_name);
}
wxDataViewItem ParamsViewCtrl::AppendParam( wxDataViewItem parent,
ParamType param_type,
const std::string& param_key)
{
return model->AppendParam(parent, param_type, param_key);
}
wxString ParamsViewCtrl::GetValue(wxDataViewItem item)
{
return model->GetParamName(item);
}
wxString ParamsViewCtrl::GetSelectedValue()
{
return model->GetParamName(this->GetSelection());
}
std::string ParamsViewCtrl::GetSelectedParamKey()
{
return model->GetParamKey(this->GetSelection());
}
std::string ParamsViewCtrl::GetSelectedTopLevelCategory()
{
return model->GetTopLevelCategory(this->GetSelection());
}
void ParamsViewCtrl::CheckAndDeleteIfEmpty(wxDataViewItem item)
{
wxDataViewItemArray children;
model->GetChildren(item, children);
if (children.IsEmpty())
model->Delete(item);
}
void ParamsViewCtrl::Clear()
{
model->Clear();
}
void ParamsViewCtrl::Rescale(int em/* = 0*/)
{
// model->Rescale();
Refresh();
}
}} // namespace Slic3r::GUI

View file

@ -0,0 +1,269 @@
#ifndef slic3r_EditGCodeDialog_hpp_
#define slic3r_EditGCodeDialog_hpp_
#include <vector>
#include <wx/gdicmn.h>
#include <slic3r/GUI/Widgets/Button.hpp>
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
#include "libslic3r/Preset.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <wx/srchctrl.h>
class wxListBox;
class wxTextCtrl;
class ScalableButton;
namespace Slic3r {
namespace GUI {
class ParamsViewCtrl;
//------------------------------------------
// EditGCodeDialog
//------------------------------------------
class EditGCodeDialog : public DPIDialog
{
ParamsViewCtrl* m_params_list {nullptr};
ScalableButton* m_add_btn {nullptr};
wxTextCtrl* m_gcode_editor {nullptr};
wxStaticText* m_param_label {nullptr};
wxStaticText* m_param_description {nullptr};
wxSearchCtrl* m_search_bar {nullptr};
ReadOnlySlicingStatesConfigDef cgp_ro_slicing_states_config_def;
ReadWriteSlicingStatesConfigDef cgp_rw_slicing_states_config_def;
OtherSlicingStatesConfigDef cgp_other_slicing_states_config_def;
PrintStatisticsConfigDef cgp_print_statistics_config_def;
ObjectsInfoConfigDef cgp_objects_info_config_def;
DimensionsConfigDef cgp_dimensions_config_def;
TemperaturesConfigDef cgp_temperatures_config_def;
TimestampsConfigDef cgp_timestamps_config_def;
OtherPresetsConfigDef cgp_other_presets_config_def;
public:
EditGCodeDialog(wxWindow*parent, const std::string&key, const std::string&value);
~EditGCodeDialog();
std::string get_edited_gcode() const;
void on_search_update();
void init_params_list(const std::string& custom_gcode_name);
wxDataViewItem add_presets_placeholders();
void add_selected_value_to_gcode();
void bind_list_and_button();
protected:
std::unordered_map<int, Button *> m_button_list;
void on_dpi_changed(const wxRect& suggested_rect) override;
void on_sys_color_changed() override;
void selection_changed(wxDataViewEvent& evt);
wxBoxSizer* create_btn_sizer(long flags);
};
// ----------------------------------------------------------------------------
// ParamsModelNode: a node inside ParamsModel
// ----------------------------------------------------------------------------
class ParamsNode;
using ParamsNodePtrArray = std::vector<std::unique_ptr<ParamsNode>>;
enum class ParamType {
Undef,
Scalar,
Vector,
FilamentVector,
};
// On all of 3 different platforms Bitmap+Text icon column looks different
// because of Markup text is missed or not implemented.
// As a temporary workaround, we will use:
// MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text)
// OSX - -//-, but Markup text is not implemented right now
// GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text)
class ParamsNode
{
ParamsNode* m_parent{ nullptr };
ParamsNodePtrArray m_children;
wxDataViewCtrl* m_ctrl;
ParamType m_param_type{ ParamType::Undef };
// TODO/FIXME:
// the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded)
// needs to know in advance if a node is or _will be_ a container.
// Thus implementing:
// bool IsContainer() const
// { return m_children.size()>0; }
// doesn't work with wxGTK when DiffModel::AddToClassical is called
// AND the classical node was removed (a new node temporary without children
// would be added to the control)
bool m_container{ true };
bool m_expanded_before_search{false};
bool m_enabled{true};
bool m_bold{false};
// first is pos, second is length
std::unique_ptr<std::pair<int, int>> m_highlight_index{nullptr};
public:
#ifdef __linux__
wxIcon icon;
#else
wxBitmap icon;
#endif //__linux__
std::string icon_name;
std::string param_key;
wxString text;
// Group params(root) node
ParamsNode(const wxString& group_name, const std::string& icon_name, wxDataViewCtrl* ctrl);
// sub SlicingState node
ParamsNode(ParamsNode* parent,
const wxString& sub_group_name,
const std::string& icon_name,
wxDataViewCtrl* ctrl);
// parametre node
ParamsNode( ParamsNode* parent,
ParamType param_type,
const std::string& param_key,
wxDataViewCtrl* ctrl);
wxString GetFormattedText();
bool IsContainer() const { return m_container; }
bool IsGroupNode() const { return m_parent == nullptr; }
bool IsParamNode() const { return m_param_type != ParamType::Undef; }
void SetContainer(bool is_container) { m_container = is_container; }
bool IsEnabled() { return m_enabled; }
void Enable(bool enable = true) { m_enabled = enable; }
void Disable() { Enable(false); }
void StartSearch();
void RefreshSearch(const wxString& search_text);
void FinishSearch();
ParamsNode* GetParent() { return m_parent; }
ParamsNodePtrArray& GetChildren() { return m_children; }
wxDataViewItemArray GetEnabledChildren();
void Append(std::unique_ptr<ParamsNode> child) { m_children.emplace_back(std::move(child)); }
};
// ----------------------------------------------------------------------------
// ParamsModel
// ----------------------------------------------------------------------------
class ParamsModel : public wxDataViewModel
{
ParamsNodePtrArray m_group_nodes;
wxDataViewCtrl* m_ctrl{ nullptr };
bool m_currently_searching{false};
public:
ParamsModel();
~ParamsModel() override = default;
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
wxDataViewItem AppendGroup(const wxString& group_name,
const std::string& icon_name);
wxDataViewItem AppendSubGroup(wxDataViewItem parent,
const wxString& sub_group_name,
const std::string&icon_name);
wxDataViewItem AppendParam( wxDataViewItem parent,
ParamType param_type,
const std::string& param_key);
wxDataViewItem Delete(const wxDataViewItem& item);
wxString GetParamName(wxDataViewItem item);
std::string GetParamKey(wxDataViewItem item);
std::string GetTopLevelCategory(wxDataViewItem item);
void RefreshSearch(const wxString& search_text);
void FinishSearch();
void Clear();
wxDataViewItem GetParent(const wxDataViewItem& item) const override;
unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override;
unsigned int GetColumnCount() const override;
wxString GetColumnType(unsigned int col) const override;
void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override;
bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override;
bool IsContainer(const wxDataViewItem& item) const override;
// Is the container just a header or an item with all columns
// In our case it is an item with all columns
bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
};
// ----------------------------------------------------------------------------
// ParamsViewCtrl
// ----------------------------------------------------------------------------
class ParamsViewCtrl : public wxDataViewCtrl
{
int m_em_unit;
public:
ParamsViewCtrl(wxWindow* parent, wxSize size);
~ParamsViewCtrl() override {
if (model) {
Clear();
model->DecRef();
}
}
ParamsModel* model{ nullptr };
wxDataViewItem AppendGroup(const wxString& group_name,
const std::string& icon_name);
wxDataViewItem AppendSubGroup(wxDataViewItem parent,
const wxString& sub_group_name,
const std::string&icon_name);
wxDataViewItem AppendParam( wxDataViewItem parent,
ParamType param_type,
const std::string& param_key);
wxString GetValue(wxDataViewItem item);
wxString GetSelectedValue();
std::string GetSelectedParamKey();
std::string GetSelectedTopLevelCategory();
void CheckAndDeleteIfEmpty(wxDataViewItem item);
void Clear();
void Rescale(int em = 0);
void set_em_unit(int em) { m_em_unit = em; }
};
} // namespace GUI
} // namespace Slic3r
#endif

View file

@ -196,6 +196,11 @@ void Field::on_back_to_sys_value()
m_back_to_sys_value(m_opt_id);
}
void Field::on_edit_value()
{
if (m_fn_edit_value)
m_fn_edit_value(m_opt_id);
}
/// Fires the enable or disable function, based on the input.
@ -206,7 +211,7 @@ wxString Field::get_tooltip_text(const wxString &default_string)
wxString tooltip_text("");
#ifdef NDEBUG
wxString tooltip = _(m_opt.tooltip);
edit_tooltip(tooltip);
::edit_tooltip(tooltip);
std::string opt_id = m_opt_id;
auto hash_pos = opt_id.find("#");

View file

@ -41,7 +41,120 @@ wxString double_to_string(double const value, const int max_precision = 4);
wxString get_thumbnail_string(const Vec2d& value);
wxString get_thumbnails_string(const std::vector<Vec2d>& values);
class Field {
class UndoValueUIManager
{
struct UndoValueUI {
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* undo_bitmap{ nullptr };
const wxString* undo_tooltip{ nullptr };
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* undo_to_sys_bitmap{ nullptr };
const wxString* undo_to_sys_tooltip{ nullptr };
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
const wxColour* label_color{ nullptr };
// State of the blinker icon
bool blink{ false };
bool set_undo_bitmap(const ScalableBitmap* bmp) {
if (undo_bitmap != bmp) {
undo_bitmap = bmp;
return true;
}
return false;
}
bool set_undo_to_sys_bitmap(const ScalableBitmap* bmp) {
if (undo_to_sys_bitmap != bmp) {
undo_to_sys_bitmap = bmp;
return true;
}
return false;
}
bool set_label_colour(const wxColour* clr) {
if (label_color != clr) {
label_color = clr;
}
return false;
}
bool set_undo_tooltip(const wxString* tip) {
if (undo_tooltip != tip) {
undo_tooltip = tip;
return true;
}
return false;
}
bool set_undo_to_sys_tooltip(const wxString* tip) {
if (undo_to_sys_tooltip != tip) {
undo_to_sys_tooltip = tip;
return true;
}
return false;
}
};
UndoValueUI m_undo_ui;
struct EditValueUI {
// Bitmap and Tooltip text for m_Edit_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* bitmap{ nullptr };
wxString tooltip { wxEmptyString };
bool set_bitmap(const ScalableBitmap* bmp) {
if (bitmap != bmp) {
bitmap = bmp;
return true;
}
return false;
}
bool set_tooltip(const wxString& tip) {
if (tooltip != tip) {
tooltip = tip;
return true;
}
return false;
}
};
EditValueUI m_edit_ui;
public:
UndoValueUIManager() {}
~UndoValueUIManager() {}
bool set_undo_bitmap(const ScalableBitmap* bmp) { return m_undo_ui.set_undo_bitmap(bmp); }
bool set_undo_to_sys_bitmap(const ScalableBitmap* bmp) { return m_undo_ui.set_undo_to_sys_bitmap(bmp); }
bool set_label_colour(const wxColour* clr) { return m_undo_ui.set_label_colour(clr); }
bool set_undo_tooltip(const wxString* tip) { return m_undo_ui.set_undo_tooltip(tip); }
bool set_undo_to_sys_tooltip(const wxString* tip) { return m_undo_ui.set_undo_to_sys_tooltip(tip); }
bool set_edit_bitmap(const ScalableBitmap* bmp) { return m_edit_ui.set_bitmap(bmp); }
bool set_edit_tooltip(const wxString& tip) { return m_edit_ui.set_tooltip(tip); }
// ui items used for revert line value
bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; }
const ScalableBitmap* undo_bitmap() const { return m_undo_ui.undo_bitmap; }
const wxString* undo_tooltip() const { return m_undo_ui.undo_tooltip; }
const ScalableBitmap* undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap; }
const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; }
const wxColour* label_color() const { return m_undo_ui.label_color; }
// Extentions
// Search blinker
const bool blink() const { return m_undo_ui.blink; }
bool* get_blink_ptr() { return &m_undo_ui.blink; }
// Edit field button
bool has_edit_ui() const { return !m_edit_ui.tooltip.IsEmpty(); }
const wxBitmap* edit_bitmap() const { return &m_edit_ui.bitmap->bmp(); }
const wxString* edit_tooltip() const { return &m_edit_ui.tooltip; }
};
class Field : public UndoValueUIManager {
protected:
// factory function to defer and enforce creation of derived type.
virtual void PostInitialize();
@ -70,6 +183,8 @@ public:
void on_back_to_initial_value();
/// Call the attached m_back_to_sys_value method.
void on_back_to_sys_value();
/// Call the attached m_fn_edit_value method.
void on_edit_value();
public:
/// parent wx item, opportunity to refactor (probably not necessary - data duplication)
@ -85,6 +200,9 @@ public:
t_back_to_init m_back_to_initial_value{ nullptr };
t_back_to_init m_back_to_sys_value{ nullptr };
/// Callback function to edit field value
t_back_to_init m_fn_edit_value{ nullptr };
// This is used to avoid recursive invocation of the field change/update by wxWidgets.
bool m_disable_change_event {false};
bool m_is_modified_value {false};
@ -139,49 +257,6 @@ public:
return std::move(p); //!p;
}
bool set_undo_bitmap(const ScalableBitmap *bmp) {
if (m_undo_bitmap != bmp) {
m_undo_bitmap = bmp;
return true;
}
return false;
}
bool set_undo_to_sys_bitmap(const ScalableBitmap *bmp) {
if (m_undo_to_sys_bitmap != bmp) {
m_undo_to_sys_bitmap = bmp;
return true;
}
return false;
}
bool set_label_colour(const wxColour *clr) {
if (m_label_color != clr) {
m_label_color = clr;
}
return false;
}
bool set_undo_tooltip(const wxString *tip) {
if (m_undo_tooltip != tip) {
m_undo_tooltip = tip;
return true;
}
return false;
}
bool set_undo_to_sys_tooltip(const wxString *tip) {
if (m_undo_to_sys_tooltip != tip) {
m_undo_to_sys_tooltip = tip;
return true;
}
return false;
}
bool* get_blink_ptr() {
return &m_blink;
}
virtual void msw_rescale();
virtual void sys_color_changed();
@ -193,27 +268,9 @@ public:
static int def_width_wider() ;
static int def_width_thinner() ;
const ScalableBitmap* undo_bitmap() { return m_undo_bitmap; }
const wxString* undo_tooltip() { return m_undo_tooltip; }
const ScalableBitmap* undo_to_sys_bitmap() { return m_undo_to_sys_bitmap; }
const wxString* undo_to_sys_tooltip() { return m_undo_to_sys_tooltip; }
const wxColour* label_color() { return m_label_color; }
const bool blink() { return m_blink; }
const bool combine_side_text() { return m_combine_side_text; } // BBS: new param style
protected:
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* m_undo_bitmap = nullptr;
const wxString* m_undo_tooltip = nullptr;
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const ScalableBitmap* m_undo_to_sys_bitmap = nullptr;
const wxString* m_undo_to_sys_tooltip = nullptr;
bool m_blink{ false };
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
const wxColour* m_label_color = nullptr;
// current value
boost::any m_value;
// last maeningful value

View file

@ -3185,14 +3185,17 @@ void GUI_App::UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited/* = false*/)
UpdateDarkUI(dvc, highlited ? dark_mode() : false);
#ifdef _MSW_DARK_MODE
//dvc->RefreshHeaderDarkMode(&m_normal_font);
HWND hwnd = (HWND)dvc->GenericGetHeader()->GetHandle();
hwnd = GetWindow(hwnd, GW_CHILD);
if (hwnd != NULL)
NppDarkMode::SetDarkListViewHeader(hwnd);
wxItemAttr attr;
attr.SetTextColour(NppDarkMode::GetTextColor());
attr.SetFont(m_normal_font);
dvc->SetHeaderAttr(attr);
HWND hwnd;
if (!dvc->HasFlag(wxDV_NO_HEADER)) {
hwnd = (HWND) dvc->GenericGetHeader()->GetHandle();
hwnd = GetWindow(hwnd, GW_CHILD);
if (hwnd != NULL)
NppDarkMode::SetDarkListViewHeader(hwnd);
wxItemAttr attr;
attr.SetTextColour(NppDarkMode::GetTextColor());
attr.SetFont(m_normal_font);
dvc->SetHeaderAttr(attr);
}
#endif //_MSW_DARK_MODE
if (dvc->HasFlag(wxDV_ROW_LINES))
dvc->SetAlternateRowColour(m_color_highlight_default);

View file

@ -195,7 +195,7 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/)
if (line.widget) {
#ifndef DISABLE_BLINKING
h_pos += blinking_button_width;
h_pos += (line.has_undo_ui() ? 3 : 1) * blinking_button_width;
#endif
for (auto child : line.widget_sizer->GetChildren())
@ -373,22 +373,31 @@ void OG_CustomCtrl::OnMotion(wxMouseEvent& event)
break;
}
for (size_t opt_idx = 0; opt_idx < line.rects_undo_icon.size(); opt_idx++)
size_t undo_icons_cnt = line.rects_undo_icon.size();
assert(line.rects_undo_icon.size() == line.rects_undo_to_sys_icon.size());
const std::vector<Option>& option_set = line.og_line.get_options();
for (size_t opt_idx = 0; opt_idx < undo_icons_cnt; opt_idx++) {
const std::string& opt_key = option_set[opt_idx].opt_id;
if (is_point_in_rect(pos, line.rects_undo_icon[opt_idx])) {
const std::vector<Option>& option_set = line.og_line.get_options();
Field* field = opt_group->get_field(option_set[opt_idx].opt_id);
if (field)
if (line.og_line.has_undo_ui())
tooltip = *line.og_line.undo_tooltip();
else if (Field* field = opt_group->get_field(opt_key))
tooltip = *field->undo_tooltip();
break;
}
for (size_t opt_idx = 0; opt_idx < line.rects_undo_to_sys_icon.size(); opt_idx++)
if (is_point_in_rect(pos, line.rects_undo_to_sys_icon[opt_idx])) {
const std::vector<Option>& option_set = line.og_line.get_options();
Field* field = opt_group->get_field(option_set[opt_idx].opt_id);
if (field)
if (line.og_line.has_undo_ui())
tooltip = *line.og_line.undo_to_sys_tooltip();
else if (Field* field = opt_group->get_field(opt_key))
tooltip = *field->undo_to_sys_tooltip();
break;
}
if (opt_idx < line.rects_edit_icon.size() && is_point_in_rect(pos, line.rects_edit_icon[opt_idx])) {
if (Field* field = opt_group->get_field(opt_key); field && field->has_edit_ui())
tooltip = *field->edit_tooltip();
break;
}
}
if (!tooltip.IsEmpty())
break;
}
@ -424,24 +433,40 @@ void OG_CustomCtrl::OnLeftDown(wxMouseEvent& event)
if (!line.is_visible) continue;
if (line.launch_browser())
return;
for (size_t opt_idx = 0; opt_idx < line.rects_undo_icon.size(); opt_idx++)
size_t undo_icons_cnt = line.rects_undo_icon.size();
assert(line.rects_undo_icon.size() == line.rects_undo_to_sys_icon.size());
const std::vector<Option>& option_set = line.og_line.get_options();
for (size_t opt_idx = 0; opt_idx < undo_icons_cnt; opt_idx++) {
const std::string& opt_key = option_set[opt_idx].opt_id;
if (is_point_in_rect(pos, line.rects_undo_icon[opt_idx])) {
const std::vector<Option>& option_set = line.og_line.get_options();
Field* field = opt_group->get_field(option_set[opt_idx].opt_id);
if (field)
if (line.og_line.has_undo_ui()) {
if (ConfigOptionsGroup* conf_OG = dynamic_cast<ConfigOptionsGroup*>(line.ctrl->opt_group))
conf_OG->back_to_initial_value(opt_key);
}
else if (Field* field = opt_group->get_field(opt_key))
field->on_back_to_initial_value();
event.Skip();
return;
}
for (size_t opt_idx = 0; opt_idx < line.rects_undo_to_sys_icon.size(); opt_idx++)
if (is_point_in_rect(pos, line.rects_undo_to_sys_icon[opt_idx])) {
const std::vector<Option>& option_set = line.og_line.get_options();
Field* field = opt_group->get_field(option_set[opt_idx].opt_id);
if (field)
if (line.og_line.has_undo_ui()) {
if (ConfigOptionsGroup* conf_OG = dynamic_cast<ConfigOptionsGroup*>(line.ctrl->opt_group))
conf_OG->back_to_sys_value(opt_key);
}
else if (Field* field = opt_group->get_field(opt_key))
field->on_back_to_sys_value();
event.Skip();
return;
}
if (opt_idx < line.rects_edit_icon.size() && is_point_in_rect(pos, line.rects_edit_icon[opt_idx])) {
if (Field* field = opt_group->get_field(opt_key))
field->on_edit_value();
event.Skip();
return;
}
}
}
SetFocusIgnoringChildren();
@ -745,10 +770,14 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos)
bool suppress_hyperlinks = false;
if (draw_just_act_buttons) {
//BBS: GUI refactor
if (field && field->undo_bitmap())
//if (field)
// BBS: new layout
draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(), field->undo_bitmap()->bmp(), field->blink());
if (field && field->undo_bitmap()) {
// if (field)
// BBS: new layout
const wxPoint pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(),
field->undo_bitmap()->bmp(), field->blink());
if (field->has_edit_ui())
draw_edit_bmp(dc, pos, *field->edit_bitmap());
}
return;
}
@ -761,7 +790,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos)
wxColour blink_color = StateColor::darkModeColorFor("#009688");
bool is_url_string = false;
if (ctrl->opt_group->label_width != 0 && !label.IsEmpty()) {
const wxColour* text_clr = field ? field->label_color() : og_line.full_Label_color;
const wxColour* text_clr = field ? field->label_color() : og_line.label_color();
for (const Option& opt : option_set) {
Field* field = ctrl->opt_group->get_field(opt.opt_id);
if (field && field->blink()) {
@ -802,7 +831,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos)
auto draw_buttons = [&h_pos, &dc, &v_pos, this](Field* field, size_t bmp_rect_id = 0) {
if (field && field->undo_to_sys_bitmap()) {
h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(), field->undo_bitmap()->bmp(), field->blink(), bmp_rect_id);
h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(), field->undo_bitmap()->bmp(), field->blink(), bmp_rect_id).x;
}
#ifndef DISABLE_BLINKING
else if (field && !field->undo_to_sys_bitmap() && field->blink())
@ -945,7 +974,7 @@ wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool i
return wxPoint(h_pos, v_pos);
}
wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id)
wxPoint OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id)
{
#ifndef DISABLE_BLINKING
pos = draw_blinking_bmp(dc, pos, is_blinking);
@ -979,7 +1008,19 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBi
h_pos += bmp_dim2 + ctrl->m_h_gap;
return h_pos;
return wxPoint(h_pos, v_pos);
}
wxCoord OG_CustomCtrl::CtrlLine::draw_edit_bmp(wxDC &dc, wxPoint pos, const wxBitmap& bmp_edit)
{
const wxCoord h_pos = pos.x + ctrl->m_h_gap;
const wxCoord v_pos = pos.y;
const int bmp_w = bmp_edit.GetWidth();
rects_edit_icon.emplace_back(wxRect(h_pos, v_pos, bmp_w, bmp_w));
dc.DrawBitmap(bmp_edit, h_pos, v_pos);
return h_pos + bmp_w + ctrl->m_h_gap;
}
bool OG_CustomCtrl::CtrlLine::launch_browser() const

View file

@ -64,12 +64,14 @@ class OG_CustomCtrl :public wxPanel
void render(wxDC& dc, wxCoord h_pos, wxCoord v_pos);
wxCoord draw_text (wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url = false, bool is_main = false);
wxPoint draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking);
wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id = 0);
wxPoint draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id = 0);
wxCoord draw_edit_bmp(wxDC& dc, wxPoint pos, const wxBitmap& bmp_edit);
bool launch_browser() const;
bool is_separator() const { return og_line.is_separator(); }
std::vector<wxRect> rects_undo_icon;
std::vector<wxRect> rects_undo_to_sys_icon;
std::vector<wxRect> rects_edit_icon;
wxRect rect_label;
};

View file

@ -102,6 +102,14 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
};
field->m_parent = parent();
if (edit_custom_gcode && opt.is_code) {
field->m_fn_edit_value = [this](std::string opt_id) {
if (!m_disabled)
this->edit_custom_gcode(opt_id);
};
field->set_edit_tooltip(_L("Edit Custom G-code"));
}
field->m_back_to_initial_value = [this](std::string opt_id) {
if (!m_disabled)
this->back_to_initial_value(opt_id);
@ -678,6 +686,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
opt_key == "thumbnails" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") {
value = get_config_value(config, opt_key);
this->change_opt_value(opt_key, value);
OptionsGroup::on_change_OG(opt_key, value);
return;
} else {
auto opt_id = m_opt_map.find(opt_key)->first;

View file

@ -48,7 +48,8 @@ struct Option {
using t_option = std::unique_ptr<Option>; //!
/// Represents option lines
class Line {
class Line : public UndoValueUIManager
{
bool m_is_separator{ false };
public:
wxString label;
@ -58,8 +59,6 @@ public:
bool toggle_visible{true}; // BBS: hide some line
size_t full_width {0};
wxColour* full_Label_color {nullptr};
bool blink {false};
widget_t widget {nullptr};
std::function<wxWindow*(wxWindow*)> near_label_widget{ nullptr };
wxWindow* near_label_widget_win {nullptr};
@ -83,10 +82,10 @@ public:
Line() : m_is_separator(true) {}
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; }
const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;}
const std::vector<Option>& get_options() const { return m_options; }
bool* get_blink_ptr() { return &blink; }
private:
std::vector<Option> m_options;//! {std::vector<Option>()};
@ -122,6 +121,8 @@ public:
std::function<void(wxWindow* win)> rescale_extra_column_item { nullptr };
std::function<void(wxWindow* win)> rescale_near_label_widget { nullptr };
std::function<void(const t_config_option_key& opt_key)> edit_custom_gcode { nullptr };
wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
@ -155,6 +156,7 @@ public:
if (m_fields.find(id) == m_fields.end()) return nullptr;
return m_fields.at(id).get();
}
bool set_value(const t_config_option_key& id, const boost::any& value, bool change_event = false) {
if (m_fields.find(id) == m_fields.end()) return false;
m_fields.at(id)->set_value(value, change_event);

View file

@ -38,6 +38,7 @@
#include "format.hpp"
#include "UnsavedChangesDialog.hpp"
#include "SavePresetDialog.hpp"
#include "EditGCodeDialog.hpp"
#include "MsgDialog.hpp"
#include "Notebook.hpp"
@ -255,6 +256,8 @@ void Tab::create_preset_tab()
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
add_scaled_bitmap(this, m_bmp_value_revert, "undo");
add_scaled_bitmap(this, m_bmp_white_bullet, "dot");
// Bitmap to be shown on the "edit" button before to each editable input field.
add_scaled_bitmap(this, m_bmp_edit_value, "edit");
set_tooltips_text();
@ -708,8 +711,8 @@ void Tab::update_label_colours()
}
if (opt.first == "printable_area" ||
opt.first == "compatible_prints" || opt.first == "compatible_printers" ) {
if (m_colored_Label_colors.find(opt.first) != m_colored_Label_colors.end())
m_colored_Label_colors.at(opt.first) = *color;
if (Line* line = get_line(opt.first))
line->set_label_colour(color);
continue;
}
@ -748,13 +751,13 @@ void Tab::decorate()
for (const auto& opt : m_options_list)
{
Field* field = nullptr;
wxColour* colored_label_clr = nullptr;
bool option_without_field = false;
if (opt.first == "printable_area" ||
opt.first == "compatible_prints" || opt.first == "compatible_printers")
colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first);
option_without_field = true;
if (!colored_label_clr) {
if (!option_without_field) {
field = get_field(opt.first);
if (!field)
continue;
@ -789,8 +792,14 @@ void Tab::decorate()
tt = &m_tt_white_bullet;
}
if (colored_label_clr) {
*colored_label_clr = *color;
if (option_without_field) {
if (Line* line = get_line(opt.first)) {
line->set_undo_bitmap(icon);
line->set_undo_to_sys_bitmap(sys_icon);
line->set_undo_tooltip(tt);
line->set_undo_to_sys_tooltip(sys_tt);
line->set_label_colour(color);
}
continue;
}
@ -802,6 +811,10 @@ void Tab::decorate()
field->set_undo_tooltip(tt);
field->set_undo_to_sys_tooltip(sys_tt);
field->set_label_colour(color);
if (field->has_edit_ui())
field->set_edit_bitmap(&m_bmp_edit_value);
}
if (m_active_page)
@ -1082,7 +1095,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
m_postpone_update_ui = false;
// When all values are rolled, then we hane to update whole tab in respect to the reverted values
// When all values are rolled, then we have to update whole tab in respect to the reverted values
update();
// BBS: restore all pages in preset, update_dirty also update combobox
@ -1265,6 +1278,11 @@ Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/
return m_active_page ? m_active_page->get_field(opt_key, opt_index) : nullptr;
}
Line* Tab::get_line(const t_config_option_key& opt_key)
{
return m_active_page ? m_active_page->get_line(opt_key) : nullptr;
}
std::pair<OG_CustomCtrl*, bool*> Tab::get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key, int opt_index/* = -1*/)
{
if (!m_active_page)
@ -2880,6 +2898,43 @@ static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group,
tab->on_value_change(opt_key, value);
}
void Tab::edit_custom_gcode(const t_config_option_key& opt_key)
{
EditGCodeDialog dlg = EditGCodeDialog(this, opt_key, get_custom_gcode(opt_key));
if (dlg.ShowModal() == wxID_OK) {
set_custom_gcode(opt_key, dlg.get_edited_gcode());
update_dirty();
update();
}
}
const std::string& Tab::get_custom_gcode(const t_config_option_key& opt_key)
{
return m_config->opt_string(opt_key);
}
void Tab::set_custom_gcode(const t_config_option_key& opt_key, const std::string& value)
{
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value(opt_key, new ConfigOptionString(value));
load_config(new_conf);
}
const std::string& TabFilament::get_custom_gcode(const t_config_option_key& opt_key)
{
return m_config->opt_string(opt_key, unsigned(0));
}
void TabFilament::set_custom_gcode(const t_config_option_key& opt_key, const std::string& value)
{
std::vector<std::string> gcodes = static_cast<const ConfigOptionStrings*>(m_config->option(opt_key))->values;
gcodes[0] = value;
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value(opt_key, new ConfigOptionStrings(gcodes));
load_config(new_conf);
}
void TabFilament::add_filament_overrides_page()
{
//BBS
@ -3155,11 +3210,14 @@ void TabFilament::build()
const int gcode_field_height = 15; // 150
const int notes_field_height = 25; // 250
auto edit_custom_gcode_fn = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); };
page = add_options_page(L("Advanced"), "advanced");
optgroup = page->new_optgroup(L("Filament start G-code"), L"param_gcode", 0);
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("filament_start_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3170,6 +3228,7 @@ void TabFilament::build()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("filament_end_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3471,6 +3530,8 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line("support_chamber_temp_control", "chamber-temperature");
optgroup->append_single_option_line("support_air_filtration", "air-filtration");
auto edit_custom_gcode_fn = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); };
const int gcode_field_height = 15; // 150
const int notes_field_height = 25; // 250
page = add_options_page(L("Machine gcode"), "cog");
@ -3478,6 +3539,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("machine_start_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3488,6 +3550,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("machine_end_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3498,6 +3561,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, optgroup](const t_config_option_key &opt_key, const boost::any &value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("printing_by_object_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3509,6 +3573,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("before_layer_change_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3519,6 +3584,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("layer_change_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3529,6 +3595,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("time_lapse_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3539,6 +3606,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("change_filament_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3549,7 +3617,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key &opt_key, const boost::any &value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("change_extrusion_role_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -3560,6 +3628,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("machine_pause_gcode");
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
@ -3569,6 +3638,7 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("template_custom_gcode");
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
@ -5312,8 +5382,12 @@ void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::strin
line.widget = widget;
line.label_path = path;
m_colored_Label_colors[opt_key] = m_default_text_clr;
line.full_Label_color = &m_colored_Label_colors[opt_key];
// set default undo ui
line.set_undo_bitmap(&m_bmp_white_bullet);
line.set_undo_to_sys_bitmap(&m_bmp_white_bullet);
line.set_undo_tooltip(&m_tt_white_bullet);
line.set_undo_to_sys_tooltip(&m_tt_white_bullet);
line.set_label_colour(&m_default_text_clr);
optgroup->append_line(line);
}
@ -5671,12 +5745,10 @@ Field *Page::get_field(const t_config_option_key &opt_key, int opt_index /*= -1*
Line *Page::get_line(const t_config_option_key &opt_key)
{
Line *line = nullptr;
for (auto opt : m_optgroups) {
line = opt->get_line(opt_key);
if (line != nullptr) return line;
}
return line;
for (auto opt : m_optgroups)
if (Line* line = opt->get_line(opt_key))
return line;
return nullptr;
}
bool Page::set_value(const t_config_option_key &opt_key, const boost::any &value)

View file

@ -201,6 +201,8 @@ protected:
ScalableBitmap *m_bmp_non_system;
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
ScalableBitmap m_bmp_value_revert;
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
ScalableBitmap m_bmp_edit_value;
std::vector<ScalableButton*> m_scaled_buttons = {};
std::vector<ScalableBitmap*> m_scaled_bitmaps = {};
@ -374,6 +376,7 @@ public:
virtual void msw_rescale();
virtual void sys_color_changed();
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
Line* get_line(const t_config_option_key& opt_key);
std::pair<OG_CustomCtrl*, bool*> get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key, int opt_index = -1);
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
@ -410,6 +413,10 @@ public:
bool validate_custom_gcodes_was_shown{ false };
void set_just_edit(bool just_edit);
void edit_custom_gcode(const t_config_option_key& opt_key);
virtual const std::string& get_custom_gcode(const t_config_option_key& opt_key);
virtual void set_custom_gcode(const t_config_option_key& opt_key, const std::string& value);
protected:
void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const std::string& path, widget_t widget);
wxSizer* compatible_widget_create(wxWindow* parent, PresetDependencies &deps);
@ -427,6 +434,7 @@ protected:
ConfigManipulation m_config_manipulation;
ConfigManipulation get_config_manipulation();
friend class EditGCodeDialog;
};
class TabPrint : public Tab
@ -562,6 +570,9 @@ public:
void update() override;
void clear_pages() override;
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
const std::string& get_custom_gcode(const t_config_option_key& opt_key) override;
void set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) override;
};
class TabPrinter : public Tab