mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 21:42:43 +00:00
* build: enable /Zc:lambda for MSVC MSVC keeps its legacy lambda processor under /std:c++17, which rejects reading a constexpr constant inside a lambda that does not capture it (C3493). No other compiler requires that capture, and clang reports it as an unused one, so the two cannot both be satisfied without the flag. /Zc:lambda selects the conforming lambda parser that clang and GCC already use. It is implied by /std:c++20 and /permissive-, so it is only needed while we are on C++17. clang-cl is conforming already and does not take the flag. It requires VS2019 16.8, so build_release_vs.bat now says 16.8+. * build: clear 237 unused lambda capture warnings 236 captures across 81 files, 142 of them `this`. Removing an unused capture changes no behavior; clang does not report a capture whose type has a non-trivial destructor, so nothing held only to extend an object's lifetime is in this set. Nine of them are the second half of the warning, "is not required to be captured for this use", where the capture is a const or constexpr value the body does read. Those depend on the /Zc:lambda change in the previous commit. One of them, in FillRectilinear.cpp, had been worked around with an #ifndef __APPLE__ guard around the capture list, which is now gone. GUI_ObjectTableSettings.cpp captured its reset button only to read it inside #ifdef __WXOSX_MAC__. That branch now takes the button from the event it is already handling. * build: fail configure on MSVC older than 19.28 instead of dropping /Zc:lambda cl.exe answers an unrecognized /Zc: sub-option with warning D9002 and keeps going, so on VS2019 before 16.8 the flag is silently ignored and the build instead dies with C3493 in FillRectilinear.cpp, nowhere near the cause. * fix: delete three locals that are now unused Their only remaining use was the lambda capture this branch removed. The Clang builds set -Wno-unused-variable, so the build never flagged them. --------- Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
1048 lines
38 KiB
C++
1048 lines
38 KiB
C++
#include "OG_CustomCtrl.hpp"
|
|
#include "OptionsGroup.hpp"
|
|
#include "MarkdownTip.hpp"
|
|
#include "Plater.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "MsgDialog.hpp"
|
|
#include "libslic3r/AppConfig.hpp"
|
|
|
|
#include <wx/utils.h>
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include "libslic3r/Utils.hpp"
|
|
#include "I18N.hpp"
|
|
#include "format.hpp"
|
|
#include <slic3r/GUI/Widgets/Label.hpp>
|
|
|
|
namespace Slic3r { namespace GUI {
|
|
|
|
// BBS: modify param ui style
|
|
constexpr int titleWidth = 20;
|
|
constexpr int ctrlWidth = 50;
|
|
|
|
#define DISABLE_BLINKING
|
|
#define DISABLE_UNDO_SYS
|
|
|
|
static bool is_point_in_rect(const wxPoint& pt, const wxRect& rect)
|
|
{
|
|
return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() &&
|
|
rect.GetTop() <= pt.y && pt.y <= rect.GetBottom();
|
|
}
|
|
|
|
static wxSize get_bitmap_size(const wxBitmap& bmp)
|
|
{
|
|
#ifdef __APPLE__
|
|
return bmp.GetScaledSize();
|
|
#else
|
|
return bmp.GetSize();
|
|
#endif
|
|
}
|
|
|
|
OG_CustomCtrl::OG_CustomCtrl( wxWindow* parent,
|
|
OptionsGroup* og,
|
|
const wxPoint& pos /* = wxDefaultPosition*/,
|
|
const wxSize& size/* = wxDefaultSize*/,
|
|
const wxValidator& val /* = wxDefaultValidator*/,
|
|
const wxString& name/* = wxEmptyString*/) :
|
|
wxPanel(parent, wxID_ANY, pos, size, /*wxWANTS_CHARS |*/ wxBORDER_NONE | wxTAB_TRAVERSAL),
|
|
opt_group(og)
|
|
{
|
|
if (!wxOSX)
|
|
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
|
|
SetBackgroundColour(parent->GetBackgroundColour());
|
|
|
|
// BBS: new font
|
|
m_font = Label::Body_14;
|
|
SetFont(m_font);
|
|
m_em_unit = em_unit(m_parent);
|
|
m_v_gap = lround(1.2 * m_em_unit);
|
|
m_v_gap2 = lround(0.8 * m_em_unit);
|
|
m_h_gap = lround(0.2 * m_em_unit);
|
|
|
|
//m_bmp_mode_sz = get_bitmap_size(create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12));
|
|
m_bmp_blinking_sz = get_bitmap_size(create_scaled_bitmap("blank_16", this));
|
|
|
|
init_ctrl_lines();// from og.lines()
|
|
|
|
this->Bind(wxEVT_PAINT, &OG_CustomCtrl::OnPaint, this);
|
|
this->Bind(wxEVT_MOTION, &OG_CustomCtrl::OnMotion, this);
|
|
this->Bind(wxEVT_LEFT_DOWN, &OG_CustomCtrl::OnLeftDown, this);
|
|
this->Bind(wxEVT_LEAVE_WINDOW, &OG_CustomCtrl::OnLeaveWin, this);
|
|
}
|
|
|
|
void OG_CustomCtrl::init_ctrl_lines()
|
|
{
|
|
const std::vector<Line>& og_lines = opt_group->get_lines();
|
|
for (const Line& line : og_lines)
|
|
{
|
|
if (line.is_separator()) {
|
|
ctrl_lines.emplace_back(CtrlLine(0, this, line));
|
|
continue;
|
|
}
|
|
|
|
if (line.full_width && (
|
|
// description line
|
|
line.widget != nullptr ||
|
|
// description line with widget (button)
|
|
!line.get_extra_widgets().empty())
|
|
)
|
|
continue;
|
|
|
|
const std::vector<Option>& option_set = line.get_options();
|
|
wxCoord height;
|
|
|
|
// if we have a single option with no label, no sidetext just add it directly to sizer
|
|
if (option_set.size() == 1 && opt_group->label_width == 0 && option_set.front().opt.full_width &&
|
|
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
|
|
line.get_extra_widgets().size() == 0)
|
|
{
|
|
height = m_bmp_blinking_sz.GetHeight() + m_v_gap;
|
|
ctrl_lines.emplace_back(CtrlLine(height, this, line, true));
|
|
}
|
|
else if (opt_group->label_width != 0 && (!line.label.IsEmpty() || option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend) )
|
|
{
|
|
wxSize label_sz = GetTextExtent(line.label);
|
|
if (opt_group->split_multi_line) {
|
|
if (option_set.size() > 1) // BBS
|
|
height = (label_sz.y + m_v_gap2) * option_set.size() + m_v_gap - m_v_gap2;
|
|
else
|
|
height = label_sz.y * (label_sz.GetWidth() > int(opt_group->label_width * m_em_unit) ? 2 : 1) + m_v_gap;
|
|
} else {
|
|
height = label_sz.y * (label_sz.GetWidth() > int(opt_group->label_width * m_em_unit) ? 2 : 1) + m_v_gap;
|
|
}
|
|
ctrl_lines.emplace_back(CtrlLine(height, this, line, false, opt_group->staticbox));
|
|
}
|
|
else
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
int OG_CustomCtrl::get_height(const Line& line)
|
|
{
|
|
for (auto ctrl_line : ctrl_lines)
|
|
if (&ctrl_line.og_line == &line)
|
|
return ctrl_line.height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/)
|
|
{
|
|
// BBS: new layout
|
|
wxCoord v_pos = 0;
|
|
wxCoord h_pos = get_title_width() * m_em_unit;
|
|
|
|
auto correct_line_height = [](int& line_height, wxWindow* win)
|
|
{
|
|
int win_height = win->GetSize().GetHeight();
|
|
if (line_height < win_height)
|
|
line_height = win_height;
|
|
};
|
|
|
|
auto correct_horiz_pos = [this](int& h_pos, Field* field) {
|
|
if (m_max_win_width > 0 && field->getWindow()) {
|
|
int win_width = field->getWindow()->GetSize().GetWidth();
|
|
if (dynamic_cast<CheckBox*>(field))
|
|
win_width *= 0.5;
|
|
h_pos += m_max_win_width - win_width;
|
|
}
|
|
};
|
|
|
|
auto add_label_width = [&h_pos, this](CtrlLine &ctrl_line, wxString const & label, int label_width) {
|
|
wxClientDC dc(this);
|
|
dc.SetFont(m_font);
|
|
wxString multiline_text;
|
|
auto size = Label::split_lines(dc, label_width, label, multiline_text);
|
|
if (label_width > 0) size.x = label_width;
|
|
h_pos += size.x + m_h_gap;
|
|
if (ctrl_line.height < size.y)
|
|
ctrl_line.height = size.y;
|
|
};
|
|
|
|
auto add_buttons_width = [&h_pos] (int blinking_button_width) {
|
|
#ifndef DISABLE_BLINKING
|
|
# ifndef DISABLE_UNDO_SYS
|
|
h_pos += 3 * blinking_button_width;
|
|
# else
|
|
h_pos += 2 * blinking_button_width;
|
|
# endif
|
|
#else
|
|
# ifndef DISABLE_UNDO_SYS
|
|
h_pos += 2 * blinking_button_width;
|
|
# else
|
|
h_pos += 1 * blinking_button_width;
|
|
# endif
|
|
#endif
|
|
};
|
|
|
|
for (CtrlLine& ctrl_line : ctrl_lines) {
|
|
if (&ctrl_line.og_line == &line)
|
|
{
|
|
// BBS: new layout
|
|
// h_pos = m_bmp_mode_sz.GetWidth() + m_h_gap;
|
|
if (line.near_label_widget_win) {
|
|
wxSize near_label_widget_sz = line.near_label_widget_win->GetSize();
|
|
if (field_in)
|
|
h_pos += near_label_widget_sz.GetWidth() + m_h_gap;
|
|
else
|
|
break;
|
|
}
|
|
|
|
wxString label = line.label;
|
|
if (opt_group->label_width != 0)
|
|
add_label_width(ctrl_line, label, opt_group->label_width * m_em_unit);
|
|
|
|
int blinking_button_width = m_bmp_blinking_sz.GetWidth() + m_h_gap;
|
|
|
|
if (line.widget) {
|
|
#ifndef DISABLE_BLINKING
|
|
h_pos += (line.has_undo_ui() ? 3 : 1) * blinking_button_width;
|
|
#endif
|
|
|
|
for (auto child : line.widget_sizer->GetChildren())
|
|
if (child->IsWindow())
|
|
correct_line_height(ctrl_line.height, child->GetWindow());
|
|
break;
|
|
}
|
|
|
|
if (opt_group->option_label_at_right) // BBS: position buttons at right
|
|
add_buttons_width(blinking_button_width);
|
|
|
|
// If we have a single option with no sidetext
|
|
const std::vector<Option>& option_set = line.get_options();
|
|
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
|
|
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0)
|
|
{
|
|
// BBS: new layout
|
|
// h_pos += 3 * blinking_button_width;
|
|
Field* field = opt_group->get_field(option_set.front().opt_id);
|
|
correct_line_height(ctrl_line.height, field->getWindow());
|
|
correct_horiz_pos(h_pos, field);
|
|
break;
|
|
}
|
|
|
|
bool is_multioption_line = option_set.size() > 1;
|
|
for (auto opt : option_set) {
|
|
Field* field = opt_group->get_field(opt.opt_id);
|
|
correct_line_height(ctrl_line.height, field->getWindow());
|
|
|
|
if (!opt_group->option_label_at_right) { // BBS: position option label at right
|
|
ConfigOptionDef option = opt.opt;
|
|
// add label if any
|
|
if (is_multioption_line && !option.label.empty()) {
|
|
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
|
|
auto label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? _L_CONTEXT(option.label, "Layers") :
|
|
_(option.label);
|
|
// BBS
|
|
// label += ":";
|
|
add_label_width(ctrl_line, label, opt_group->sublabel_width * m_em_unit);
|
|
h_pos += 8;
|
|
}
|
|
|
|
}
|
|
|
|
if (field == field_in) {
|
|
correct_horiz_pos(h_pos, field);
|
|
break;
|
|
}
|
|
if (opt_group->split_multi_line) {// BBS
|
|
v_pos += (ctrl_line.height - m_v_gap + m_v_gap2) / option_set.size();
|
|
} else {
|
|
// BBS: new layout
|
|
h_pos += field->getWindow()->GetSize().x;
|
|
add_buttons_width(blinking_button_width);
|
|
if (option_set.size() == 1 && option_set.front().opt.full_width)
|
|
break;
|
|
|
|
// add sidetext if any
|
|
if (!field->combine_side_text() && (!opt.opt.sidetext.empty() || opt_group->sidetext_width > 0))
|
|
h_pos += opt_group->sidetext_width * m_em_unit + m_h_gap;
|
|
|
|
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
|
|
h_pos += lround(0.6 * m_em_unit);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (ctrl_line.is_visible)
|
|
v_pos += ctrl_line.height;
|
|
}
|
|
|
|
return wxPoint(h_pos, v_pos);
|
|
}
|
|
|
|
// BBS: draw multi-line title
|
|
static void draw_title(wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width)
|
|
{
|
|
wxString multiline_text;
|
|
if (width > 0 && dc.GetTextExtent(text).x > width) {
|
|
multiline_text = text;
|
|
|
|
size_t idx = size_t(-1);
|
|
size_t start = 0;
|
|
for (size_t i = 0; i < multiline_text.Len(); i++)
|
|
{
|
|
if (multiline_text[i] == ' ')
|
|
{
|
|
if (dc.GetTextExtent(multiline_text.SubString(start, i)).x < width)
|
|
idx = i;
|
|
else {
|
|
if (idx == size_t(-1))
|
|
idx = i;
|
|
multiline_text[idx] = '\n';
|
|
start = idx + 1;
|
|
idx = size_t(-1);
|
|
}
|
|
}
|
|
}
|
|
if (idx != size_t(-1))
|
|
multiline_text[idx] = '\n';
|
|
}
|
|
|
|
if (!text.IsEmpty()) {
|
|
const wxString& out_text = multiline_text.IsEmpty() ? text : multiline_text;
|
|
wxCoord text_width, text_height;
|
|
dc.GetMultiLineTextExtent(out_text, &text_width, &text_height);
|
|
|
|
wxColour old_clr = dc.GetTextForeground();
|
|
wxFont old_font = dc.GetFont();
|
|
dc.SetTextForeground(color ? *color :
|
|
#ifdef _WIN32
|
|
wxGetApp().get_label_clr_default());
|
|
#else
|
|
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
|
#endif /* _WIN32 */
|
|
dc.DrawText(out_text, pos);
|
|
dc.SetTextForeground(old_clr);
|
|
dc.SetFont(old_font);
|
|
|
|
if (width < 1)
|
|
width = text_width;
|
|
}
|
|
}
|
|
|
|
void OG_CustomCtrl::OnPaint(wxPaintEvent&)
|
|
{
|
|
// case, when custom controll is destroyed but doesn't deleted from the evet loop
|
|
if(!this->opt_group->custom_ctrl)
|
|
return;
|
|
|
|
wxPaintDC dc(this);
|
|
|
|
wxCoord h_pos = get_title_width() * m_em_unit + 4; // ORCA Align label with group title. StaticLine.cpp uses 18px for icon 5px for spacing. Spacing doesnt scales on messureSize()
|
|
wxCoord v_pos = 0;
|
|
// BBS: new layout
|
|
if (!GetLabel().IsEmpty()) {
|
|
dc.SetFont(Label::Head_16);
|
|
wxColour color = StateColor::darkModeColorFor("#283436");
|
|
draw_title(dc, {0, v_pos}, GetLabel(), &color, h_pos);
|
|
dc.SetFont(m_font);
|
|
}
|
|
for (CtrlLine& line : ctrl_lines) {
|
|
if (!line.is_visible)
|
|
continue;
|
|
line.render(dc, h_pos, v_pos);
|
|
v_pos += line.height;
|
|
}
|
|
}
|
|
|
|
void OG_CustomCtrl::OnMotion(wxMouseEvent& event)
|
|
{
|
|
const wxPoint pos = event.GetLogicalPosition(wxClientDC(this));
|
|
wxString tooltip;
|
|
std::string markdowntip;
|
|
|
|
// BBS: markdown tip
|
|
CtrlLine* focusedLine = nullptr;
|
|
// BBS
|
|
|
|
bool suppress_hyperlinks = false;
|
|
|
|
for (CtrlLine& line : ctrl_lines) {
|
|
if (!line.is_visible) continue;
|
|
line.is_focused = is_point_in_rect(pos, line.rect_label);
|
|
if (line.is_focused) {
|
|
if (!suppress_hyperlinks && !line.og_line.label_path.empty())
|
|
tooltip = OptionsGroup::get_url(line.og_line.label_path) + "\n\n";
|
|
tooltip += line.og_line.label_tooltip;
|
|
// BBS: markdown tip
|
|
focusedLine = &line;
|
|
markdowntip = line.og_line.label.empty()
|
|
? line.og_line.get_options().front().opt_id : into_u8(line.og_line.label);
|
|
markdowntip.erase(0, markdowntip.find_last_of('#') + 1);
|
|
// BBS
|
|
break;
|
|
}
|
|
|
|
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])) {
|
|
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;
|
|
}
|
|
if (is_point_in_rect(pos, line.rects_undo_to_sys_icon[opt_idx])) {
|
|
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;
|
|
}
|
|
|
|
// Set tooltips with information for each icon
|
|
// BBS: markdown tip
|
|
if (!markdowntip.empty()) {
|
|
wxWindow* window = GetGrandParent();
|
|
assert(focusedLine);
|
|
wxPoint pos2 = { 250, focusedLine->rect_label.y };
|
|
pos2 = ClientToScreen(pos2);
|
|
if (MarkdownTip::ShowTip(markdowntip, into_u8(tooltip), pos2)) {
|
|
tooltip.clear();
|
|
}
|
|
}
|
|
else {
|
|
MarkdownTip::ShowTip(markdowntip, "", {tooltip.empty() ? 0 : 1, 0});
|
|
}
|
|
if (GetToolTipText() != tooltip)
|
|
this->SetToolTip(tooltip);
|
|
// BBS
|
|
|
|
Refresh();
|
|
Update();
|
|
event.Skip();
|
|
}
|
|
|
|
void OG_CustomCtrl::OnLeftDown(wxMouseEvent& event)
|
|
{
|
|
const wxPoint pos = event.GetLogicalPosition(wxClientDC(this));
|
|
|
|
for (const CtrlLine& line : ctrl_lines) {
|
|
if (!line.is_visible) continue;
|
|
if (line.launch_browser())
|
|
return;
|
|
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])) {
|
|
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;
|
|
}
|
|
if (is_point_in_rect(pos, line.rects_undo_to_sys_icon[opt_idx])) {
|
|
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();
|
|
}
|
|
|
|
void OG_CustomCtrl::OnLeaveWin(wxMouseEvent& event)
|
|
{
|
|
for (CtrlLine& line : ctrl_lines)
|
|
line.is_focused = false;
|
|
|
|
// BBS: markdown tip
|
|
MarkdownTip::ShowTip("", "", {});
|
|
|
|
Refresh();
|
|
Update();
|
|
event.Skip();
|
|
}
|
|
|
|
bool OG_CustomCtrl::update_visibility(ConfigOptionMode mode)
|
|
{
|
|
// BBS: new layout
|
|
wxCoord h_pos = (ctrlWidth + get_title_width() - titleWidth) * m_em_unit;
|
|
wxCoord h_pos2 = get_title_width() * m_em_unit;
|
|
wxCoord v_pos = 0;
|
|
|
|
bool has_visible_lines = false;
|
|
for (CtrlLine& line : ctrl_lines) {
|
|
line.update_visibility(mode);
|
|
if (line.is_visible) {
|
|
v_pos += (wxCoord)line.height;
|
|
|
|
if (!line.is_separator()) { // Ignore separators
|
|
has_visible_lines = true;
|
|
}
|
|
}
|
|
}
|
|
// BBS: multi-line title
|
|
SetFont(Label::Head_16);
|
|
wxSize label_sz = GetTextExtent(GetLabel());
|
|
SetFont(m_font);
|
|
auto lineHeight = label_sz.y;
|
|
while (label_sz.x > h_pos2) {
|
|
label_sz.x -= h_pos2;
|
|
label_sz.y += lineHeight;
|
|
}
|
|
if (v_pos < label_sz.y) v_pos = label_sz.y;
|
|
|
|
this->SetMinSize(wxSize(h_pos, v_pos));
|
|
|
|
return has_visible_lines;
|
|
}
|
|
|
|
// BBS: call by Tab/Page
|
|
void OG_CustomCtrl::fixup_items_positions()
|
|
{
|
|
if (GetParent() == nullptr || GetPosition().y + GetSize().y < GetParent()->GetSize().y)
|
|
return;
|
|
for (CtrlLine& line : ctrl_lines) {
|
|
line.correct_items_positions();
|
|
}
|
|
}
|
|
|
|
void OG_CustomCtrl::correct_window_position(wxWindow* win, const Line& line, Field* field/* = nullptr*/)
|
|
{
|
|
wxPoint pos = get_pos(line, field);
|
|
int line_height = get_height(line);
|
|
if (opt_group->split_multi_line) { // BBS
|
|
if (line.get_options().size() > 1)
|
|
line_height = (line_height - m_v_gap + m_v_gap2) / line.get_options().size();
|
|
}
|
|
pos.y += std::max(0, int(0.5 * (line_height - win->GetSize().y)));
|
|
win->SetPosition(pos);
|
|
};
|
|
|
|
void OG_CustomCtrl::correct_widgets_position(wxSizer* widget, const Line& line, Field* field/* = nullptr*/) {
|
|
auto children = widget->GetChildren();
|
|
wxPoint line_pos = get_pos(line, field);
|
|
int line_height = get_height(line);
|
|
for (auto child : children)
|
|
if (child->IsWindow()) {
|
|
wxPoint pos = line_pos;
|
|
wxSize sz = child->GetWindow()->GetSize();
|
|
pos.y += std::max(0, int(0.5 * (line_height - sz.y)));
|
|
if (line.extra_widget_sizer && widget == line.extra_widget_sizer)
|
|
pos.x += m_h_gap;
|
|
child->GetWindow()->SetPosition(pos);
|
|
line_pos.x += sz.x + m_h_gap;
|
|
}
|
|
};
|
|
|
|
void OG_CustomCtrl::init_max_win_width()
|
|
{
|
|
if (opt_group->ctrl_horiz_alignment == wxALIGN_RIGHT && m_max_win_width == 0)
|
|
for (CtrlLine& line : ctrl_lines) {
|
|
if (int max_win_width = line.get_max_win_width();
|
|
m_max_win_width < max_win_width)
|
|
m_max_win_width = max_win_width;
|
|
}
|
|
}
|
|
|
|
int OG_CustomCtrl::get_title_width()
|
|
{
|
|
if (!GetLabel().IsEmpty())
|
|
return titleWidth;
|
|
else
|
|
return 2;
|
|
}
|
|
|
|
void OG_CustomCtrl::set_max_win_width(int max_win_width)
|
|
{
|
|
if (m_max_win_width == max_win_width)
|
|
return;
|
|
m_max_win_width = max_win_width;
|
|
for (CtrlLine& line : ctrl_lines)
|
|
line.correct_items_positions();
|
|
|
|
GetParent()->Layout();
|
|
}
|
|
|
|
|
|
void OG_CustomCtrl::msw_rescale()
|
|
{
|
|
#ifdef __WXOSX__
|
|
return;
|
|
#endif
|
|
// BBS: new font
|
|
m_font = Label::Body_14;
|
|
SetFont(m_font);
|
|
m_em_unit = em_unit(m_parent);
|
|
m_v_gap = lround(1.2 * m_em_unit);
|
|
m_v_gap2 = lround(0.8 * m_em_unit);
|
|
m_h_gap = lround(0.2 * m_em_unit);
|
|
|
|
//m_bmp_mode_sz = create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12).GetSize();
|
|
m_bmp_blinking_sz = create_scaled_bitmap("blank_16", this).GetSize();
|
|
|
|
m_max_win_width = 0;
|
|
|
|
wxCoord h_pos = (ctrlWidth + get_title_width() - titleWidth) * m_em_unit;
|
|
wxCoord h_pos2 = get_title_width() * m_em_unit;
|
|
wxCoord v_pos = 0;
|
|
for (CtrlLine& line : ctrl_lines) {
|
|
line.msw_rescale();
|
|
if (line.is_visible)
|
|
v_pos += (wxCoord)line.height;
|
|
}
|
|
// BBS: multi-line title
|
|
SetFont(Label::Head_16);
|
|
wxSize label_sz = GetTextExtent(GetLabel());
|
|
SetFont(m_font);
|
|
auto lineHeight = label_sz.y;
|
|
while (label_sz.x > h_pos2) {
|
|
label_sz.x -= h_pos2;
|
|
label_sz.y += lineHeight;
|
|
}
|
|
if (v_pos < label_sz.y) v_pos = label_sz.y;
|
|
// BBS: new layout
|
|
this->SetMinSize(wxSize(h_pos, v_pos));
|
|
|
|
GetParent()->Layout();
|
|
}
|
|
|
|
void OG_CustomCtrl::sys_color_changed()
|
|
{
|
|
}
|
|
|
|
OG_CustomCtrl::CtrlLine::CtrlLine( wxCoord height,
|
|
OG_CustomCtrl* ctrl,
|
|
const Line& og_line,
|
|
bool draw_just_act_buttons /* = false*/,
|
|
bool draw_mode_bitmap/* = true*/):
|
|
height(height),
|
|
ctrl(ctrl),
|
|
og_line(og_line),
|
|
draw_just_act_buttons(draw_just_act_buttons),
|
|
draw_mode_bitmap(draw_mode_bitmap)
|
|
{
|
|
|
|
for (size_t i = 0; i < og_line.get_options().size(); i++) {
|
|
rects_undo_icon.emplace_back(wxRect());
|
|
rects_undo_to_sys_icon.emplace_back(wxRect());
|
|
}
|
|
}
|
|
|
|
int OG_CustomCtrl::CtrlLine::get_max_win_width()
|
|
{
|
|
int max_win_width = 0;
|
|
if (!draw_just_act_buttons) {
|
|
const std::vector<Option>& option_set = og_line.get_options();
|
|
for (auto opt : option_set) {
|
|
Field* field = ctrl->opt_group->get_field(opt.opt_id);
|
|
if (field && field->getWindow())
|
|
max_win_width = field->getWindow()->GetSize().GetWidth();
|
|
}
|
|
}
|
|
|
|
return max_win_width;
|
|
}
|
|
|
|
void OG_CustomCtrl::CtrlLine::correct_items_positions()
|
|
{
|
|
if (draw_just_act_buttons || !is_visible)
|
|
return;
|
|
|
|
if (og_line.near_label_widget_win)
|
|
ctrl->correct_window_position(og_line.near_label_widget_win, og_line);
|
|
if (og_line.widget_sizer)
|
|
ctrl->correct_widgets_position(og_line.widget_sizer, og_line);
|
|
if (og_line.extra_widget_sizer)
|
|
ctrl->correct_widgets_position(og_line.extra_widget_sizer, og_line);
|
|
|
|
const std::vector<Option>& option_set = og_line.get_options();
|
|
for (auto opt : option_set) {
|
|
Field* field = ctrl->opt_group->get_field(opt.opt_id);
|
|
if (!field)
|
|
continue;
|
|
if (field->getSizer())
|
|
ctrl->correct_widgets_position(field->getSizer(), og_line, field);
|
|
else if (field->getWindow())
|
|
ctrl->correct_window_position(field->getWindow(), og_line, field);
|
|
}
|
|
}
|
|
|
|
void OG_CustomCtrl::CtrlLine::msw_rescale()
|
|
{
|
|
// if we have a single option with no label, no sidetext
|
|
if (draw_just_act_buttons)
|
|
height = get_bitmap_size(create_scaled_bitmap("empty")).GetHeight();
|
|
|
|
if (ctrl->opt_group->label_width != 0 && !og_line.label.IsEmpty()) {
|
|
wxSize label_sz = ctrl->GetTextExtent(og_line.label);
|
|
if (ctrl->opt_group->split_multi_line) { // BBS
|
|
const std::vector<Option> &option_set = og_line.get_options();
|
|
if (option_set.size() > 1)
|
|
height = (label_sz.y + ctrl->m_v_gap2) * option_set.size() + ctrl->m_v_gap - ctrl->m_v_gap2;
|
|
else
|
|
height = label_sz.y * (label_sz.GetWidth() > int(ctrl->opt_group->label_width * ctrl->m_em_unit) ? 2 : 1) + ctrl->m_v_gap;
|
|
} else {
|
|
height = label_sz.y * (label_sz.GetWidth() > int(ctrl->opt_group->label_width * ctrl->m_em_unit) ? 2 : 1) + ctrl->m_v_gap;
|
|
}
|
|
}
|
|
|
|
correct_items_positions();
|
|
}
|
|
|
|
void OG_CustomCtrl::CtrlLine::update_visibility(ConfigOptionMode mode)
|
|
{
|
|
if (og_line.is_separator())
|
|
return;
|
|
const std::vector<Option>& option_set = og_line.get_options();
|
|
|
|
const ConfigOptionMode& line_mode = option_set.front().opt.mode;
|
|
is_visible = og_line.toggle_visible && line_mode <= mode;
|
|
|
|
if (draw_just_act_buttons)
|
|
return;
|
|
|
|
if (og_line.near_label_widget_win)
|
|
og_line.near_label_widget_win->Show(is_visible);
|
|
if (og_line.widget_sizer)
|
|
og_line.widget_sizer->ShowItems(is_visible);
|
|
if (og_line.extra_widget_sizer)
|
|
og_line.extra_widget_sizer->ShowItems(is_visible);
|
|
|
|
for (auto opt : option_set) {
|
|
Field* field = ctrl->opt_group->get_field(opt.opt_id);
|
|
if (!field)
|
|
continue;
|
|
|
|
if (field->getSizer()) {
|
|
auto children = field->getSizer()->GetChildren();
|
|
for (auto child : children)
|
|
if (child->IsWindow())
|
|
child->GetWindow()->Show(is_visible);
|
|
}
|
|
else if (field->getWindow())
|
|
field->getWindow()->Show(is_visible);
|
|
}
|
|
|
|
correct_items_positions();
|
|
}
|
|
|
|
void OG_CustomCtrl::CtrlLine::render_separator(wxDC& dc, wxCoord v_pos)
|
|
{
|
|
wxPoint begin(ctrl->m_h_gap, v_pos);
|
|
wxPoint end(ctrl->GetSize().GetWidth() - ctrl->m_h_gap, v_pos);
|
|
|
|
wxPen old_pen = dc.GetPen();
|
|
// pen.SetColour(*wxLIGHT_GREY);
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
dc.DrawLine(begin, end);
|
|
dc.SetPen(old_pen);
|
|
}
|
|
|
|
void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos)
|
|
{
|
|
if (is_separator()) {
|
|
render_separator(dc, v_pos);
|
|
return;
|
|
}
|
|
|
|
Field* field = ctrl->opt_group->get_field(og_line.get_options().front().opt_id);
|
|
|
|
bool suppress_hyperlinks = false;
|
|
if (draw_just_act_buttons) {
|
|
//BBS: GUI refactor
|
|
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;
|
|
}
|
|
|
|
if (og_line.near_label_widget_win)
|
|
h_pos += og_line.near_label_widget_win->GetSize().x + ctrl->m_h_gap;
|
|
|
|
const std::vector<Option>& option_set = og_line.get_options();
|
|
|
|
wxString label = og_line.label;
|
|
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.label_color();
|
|
for (const Option& opt : option_set) {
|
|
Field* field = ctrl->opt_group->get_field(opt.opt_id);
|
|
if (field && field->blink()) {
|
|
text_clr = &blink_color;
|
|
break;
|
|
}
|
|
}
|
|
bool is_multi_extruder = false;
|
|
if (ctrl->opt_group->draw_multi_extruder)
|
|
for (const Option& opt : option_set)
|
|
is_multi_extruder |= opt.opt_id.find_last_of('#') != std::string::npos;
|
|
wxCoord icon_pos = h_pos;
|
|
if (is_multi_extruder) {
|
|
static ScalableBitmap multi_extruder(ctrl, "multi_extruder");
|
|
h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), multi_extruder.bmp(), multi_extruder.bmp(), false, 0, true).x;
|
|
}
|
|
is_url_string = !suppress_hyperlinks && !og_line.label_path.empty();
|
|
// BBS
|
|
h_pos = draw_text(dc, wxPoint(h_pos, v_pos), label /* + ":" */, text_clr, icon_pos + ctrl->opt_group->label_width * ctrl->m_em_unit - h_pos, is_url_string, true);
|
|
}
|
|
|
|
// If there's a widget, build it and set result to the correct position.
|
|
#ifndef DISABLE_BLINKING
|
|
if (og_line.widget != nullptr) {
|
|
draw_blinking_bmp(dc, wxPoint(h_pos, v_pos), og_line.blink);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// If we're here, we have more than one option or a single option with sidetext
|
|
// so we need a horizontal sizer to arrange these things
|
|
|
|
auto add_field_width = [&h_pos, this] (Field* field) {
|
|
if (field) {
|
|
if (field->getSizer())
|
|
{
|
|
auto children = field->getSizer()->GetChildren();
|
|
for (auto child : children)
|
|
if (child->IsWindow())
|
|
h_pos += child->GetWindow()->GetSize().x + ctrl->m_h_gap;
|
|
}
|
|
else if (field->getWindow()) {
|
|
h_pos += field->getWindow()->GetSize().x + ctrl->m_h_gap;
|
|
}
|
|
}
|
|
};
|
|
|
|
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).x;
|
|
}
|
|
#ifndef DISABLE_BLINKING
|
|
else if (field && !field->undo_to_sys_bitmap() && field->blink())
|
|
draw_blinking_bmp(dc, wxPoint(h_pos, v_pos), field->blink());
|
|
#endif
|
|
};
|
|
|
|
wxCoord h_pos2 = h_pos;
|
|
// If we have a single option with no sidetext just add it directly to the grid sizer
|
|
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
|
|
option_set.front().side_widget == nullptr && og_line.get_extra_widgets().size() == 0)
|
|
{
|
|
// BBS: new layout
|
|
if (ctrl->opt_group->option_label_at_right)
|
|
draw_buttons(field);
|
|
add_field_width(field);
|
|
wxCoord h_pos3 = h_pos;
|
|
if (!ctrl->opt_group->option_label_at_right)
|
|
draw_buttons(field);
|
|
// update width for full_width fields
|
|
if (option_set.front().opt.full_width && field && field->getWindow())
|
|
field->getWindow()->SetSize(ctrl->GetSize().x - h_pos2 + h_pos3 - h_pos - ctrl->m_em_unit * 3, -1);
|
|
return;
|
|
}
|
|
|
|
size_t bmp_rect_id = 0;
|
|
bool is_multioption_line = option_set.size() > 1;
|
|
for (const Option& opt : option_set) {
|
|
field = ctrl->opt_group->get_field(opt.opt_id);
|
|
ConfigOptionDef option = opt.opt;
|
|
if (ctrl->opt_group->option_label_at_right)
|
|
draw_buttons(field, bmp_rect_id++);
|
|
if (ctrl->opt_group->option_label_at_right) // BBS
|
|
add_field_width(field);
|
|
// add label if any
|
|
if (is_multioption_line && !option.label.empty()) {
|
|
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
|
|
label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ?
|
|
_L_CONTEXT(option.label, "Layers") : _(option.label);
|
|
//if (!ctrl->opt_group->option_label_at_right) // BBS
|
|
//label += ":";
|
|
|
|
if (is_url_string)
|
|
is_url_string = false;
|
|
else if(opt == option_set.front())
|
|
is_url_string = !suppress_hyperlinks && !og_line.label_path.empty();
|
|
wxColor c = StateColor::darkModeColorFor("#6B6B6B");
|
|
h_pos = draw_text(dc, wxPoint(h_pos, v_pos), label, field ? (field->blink() ? &blink_color : &c) : nullptr, ctrl->opt_group->sublabel_width * ctrl->m_em_unit, is_url_string);
|
|
h_pos += 8;
|
|
}
|
|
|
|
// BBS: new layout
|
|
// add field
|
|
if (option_set.size() == 1 && option_set.front().opt.full_width)
|
|
break;
|
|
if (!ctrl->opt_group->option_label_at_right) // BBS
|
|
add_field_width(field);
|
|
// add sidetext if any
|
|
// BBS: new layout
|
|
wxCoord offset = 0;
|
|
if (!field->combine_side_text() && (!option.sidetext.empty() || ctrl->opt_group->sidetext_width > 0)) {
|
|
wxCoord h_pos2 = h_pos + dc.GetTextExtent(_(option.sidetext)).x;
|
|
h_pos = draw_text(dc, wxPoint(h_pos, v_pos), _(option.sidetext), nullptr, ctrl->opt_group->sidetext_width * ctrl->m_em_unit);
|
|
offset = h_pos - h_pos2;
|
|
}
|
|
// BBS: new layout
|
|
if (!ctrl->opt_group->option_label_at_right) {
|
|
offset -= ctrl->m_h_gap; h_pos -= offset;
|
|
draw_buttons(field, bmp_rect_id++);
|
|
h_pos += offset;
|
|
}
|
|
|
|
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
|
|
h_pos += lround(0.6 * ctrl->m_em_unit);
|
|
|
|
if (ctrl->opt_group->split_multi_line) { // BBS
|
|
v_pos += (height - ctrl->m_v_gap + ctrl->m_v_gap2) / option_set.size();
|
|
h_pos = h_pos2;
|
|
}
|
|
}
|
|
}
|
|
|
|
wxCoord OG_CustomCtrl::CtrlLine::draw_text(wxDC &dc, wxPoint pos, const wxString &text, const wxColour *color, int width, bool is_url/* = false*/, bool is_main/* = false*/)
|
|
{
|
|
wxString multiline_text;
|
|
auto size = Label::split_lines(dc, width, text, multiline_text);
|
|
|
|
if (!text.IsEmpty()) {
|
|
const wxString& out_text = multiline_text.IsEmpty() ? text : multiline_text;
|
|
|
|
if (ctrl->opt_group->split_multi_line && !is_main) { // BBS
|
|
const std::vector<Option> &option_set = og_line.get_options();
|
|
pos.y = pos.y + lround(((height - ctrl->m_v_gap + ctrl->m_v_gap2) / option_set.size() - size.y) / 2);
|
|
} else {
|
|
pos.y = pos.y + lround((height - size.y) / 2);
|
|
}
|
|
if (width > 0 && is_main)
|
|
rect_label = wxRect(pos, wxSize(size.x, size.y));
|
|
|
|
wxColour old_clr = dc.GetTextForeground();
|
|
wxFont old_font = dc.GetFont();
|
|
wxColor clr_url = StateColor::darkModeColorFor("#009688");
|
|
if (is_focused && is_url) {
|
|
// temporary workaround for the OSX because of strange Bold font behavior on BigSerf
|
|
#ifdef __APPLE__
|
|
dc.SetFont(old_font.Underlined());
|
|
#else
|
|
dc.SetFont(old_font.Bold().Underlined());
|
|
#endif
|
|
color = &clr_url;
|
|
}
|
|
dc.SetTextForeground(color ? *color :
|
|
#ifdef _WIN32
|
|
wxGetApp().get_label_clr_default());
|
|
#else
|
|
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
|
#endif /* _WIN32 */
|
|
dc.DrawText(out_text, pos);
|
|
dc.SetTextForeground(old_clr);
|
|
dc.SetFont(old_font);
|
|
|
|
if (width < 1)
|
|
width = size.x;
|
|
}
|
|
|
|
return pos.x + width + ctrl->m_h_gap;
|
|
}
|
|
|
|
wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking)
|
|
{
|
|
wxBitmap bmp_blinking = create_scaled_bitmap(is_blinking ? "blank_16" : "empty", ctrl);
|
|
wxCoord h_pos = pos.x;
|
|
wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking).GetHeight()) / 2);
|
|
|
|
dc.DrawBitmap(bmp_blinking, h_pos, v_pos);
|
|
|
|
int bmp_dim = get_bitmap_size(bmp_blinking).GetWidth();
|
|
|
|
h_pos += bmp_dim + ctrl->m_h_gap;
|
|
return wxPoint(h_pos, v_pos);
|
|
}
|
|
|
|
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, bool is_main)
|
|
{
|
|
#ifndef DISABLE_BLINKING
|
|
pos = draw_blinking_bmp(dc, pos, is_blinking);
|
|
#else
|
|
if (ctrl->opt_group->split_multi_line && !is_main) { // BBS
|
|
const std::vector<Option> &option_set = og_line.get_options();
|
|
if (option_set.size() > 1)
|
|
pos.y += lround(((height - ctrl->m_v_gap + ctrl->m_v_gap2) / option_set.size() - get_bitmap_size(bmp_undo).GetHeight()) / 2);
|
|
else
|
|
pos.y += lround((height - get_bitmap_size(bmp_undo).GetHeight()) / 2);
|
|
} else {
|
|
pos.y += lround((height - get_bitmap_size(bmp_undo).GetHeight()) / 2);
|
|
}
|
|
#endif
|
|
wxCoord h_pos = pos.x - ctrl->m_h_gap; // Orca: adjust position to the left
|
|
wxCoord v_pos = pos.y;
|
|
|
|
#ifndef DISABLE_UNDO_SYS
|
|
//BBS: GUI refactor
|
|
dc.DrawBitmap(bmp_undo_to_sys, h_pos, v_pos);
|
|
|
|
int bmp_dim = get_bitmap_size(bmp_undo_to_sys).GetWidth();
|
|
rects_undo_to_sys_icon[rect_id] = wxRect(h_pos, v_pos, bmp_dim, bmp_dim);
|
|
|
|
h_pos += bmp_dim + ctrl->m_h_gap;
|
|
#endif
|
|
dc.DrawBitmap(og_line.undo_to_sys ? bmp_undo_to_sys : bmp_undo, h_pos, v_pos);
|
|
|
|
int bmp_dim2 = get_bitmap_size(bmp_undo).GetWidth();
|
|
(og_line.undo_to_sys ? rects_undo_to_sys_icon[rect_id] : rects_undo_icon[rect_id]) = wxRect(h_pos, v_pos, bmp_dim2, bmp_dim2);
|
|
|
|
h_pos += bmp_dim2 + ctrl->m_h_gap;
|
|
|
|
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
|
|
{
|
|
if (!is_focused || og_line.label_path.empty())
|
|
return false;
|
|
|
|
return OptionsGroup::launch_browser(og_line.label_path);
|
|
}
|
|
|
|
} // GUI
|
|
} // Slic3r
|