OrcaSlicer/src/slic3r/GUI/OG_CustomCtrl.cpp
lane.wei e9e4d75877 Update the codes to 01.01.00.10 for the formal release
1. first formal version of macos
2. add the bambu networking plugin install logic
3. auto compute the wipe volume when filament change
4. add the logic of wiping into support
5. refine the GUI layout and icons, improve the gui apperance in lots of
   small places
6. serveral improve to support
7. support AMS auto-mapping
8. disable lots of unstable features: such as params table, media file download, HMS
9. fix serveral kinds of bugs
10. update the document of building
11. ...
2022-07-22 20:35:34 +08:00

990 lines
34 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
// BBS: new font
m_font = Label::Body_14;
SetFont(m_font);
m_em_unit = em_unit(m_parent);
m_v_gap = lround(1.0 * 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_gap) * option_set.size();
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, this] (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 += 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")) ? _CTX(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 / 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;
wxCoord v_pos = 0;
// BBS: new layout
if (!GetLabel().IsEmpty()) {
dc.SetFont(Label::Head_16);
wxColour color("#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;
wxString language = wxGetApp().app_config->get("language");
// 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;
}
for (size_t opt_idx = 0; opt_idx < line.rects_undo_icon.size(); opt_idx++)
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)
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)
tooltip = *field->undo_to_sys_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;
for (size_t opt_idx = 0; opt_idx < line.rects_undo_icon.size(); opt_idx++)
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)
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)
field->on_back_to_sys_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;
size_t invisible_lines = 0;
for (CtrlLine& line : ctrl_lines) {
line.update_visibility(mode);
if (line.is_visible)
v_pos += (wxCoord)line.height;
else
invisible_lines++;
}
// 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 invisible_lines != ctrl_lines.size();
}
// BBS: call by Tab/Page
void OG_CustomCtrl::fixup_items_positions()
{
if (GetParent() == nullptr || GetPosition().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.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 1;
}
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.5 * 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_gap) * option_set.size();
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 pen, old_pen = pen = dc.GetPen();
pen.SetColour(*wxLIGHT_GREY);
dc.SetPen(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
draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(), field->undo_bitmap()->bmp(), field->blink());
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("#00AE42");
bool is_url_string = false;
if (ctrl->opt_group->label_width != 0 && !label.IsEmpty()) {
const wxColour* text_clr = (option_set.size() == 1 && field ? field->label_color() : og_line.full_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;
}
}
//is_url_string = !suppress_hyperlinks && !og_line.label_path.empty();
// BBS
h_pos = draw_text(dc, wxPoint(h_pos, v_pos), label /* + ":" */, text_clr, ctrl->opt_group->label_width * ctrl->m_em_unit, 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);
}
#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, -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")) ?
_CTX(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();
h_pos = draw_text(dc, wxPoint(h_pos, v_pos), label, field ? (field->blink() ? &blink_color : field->label_color()) : nullptr, ctrl->opt_group->sublabel_width * ctrl->m_em_unit);
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 / 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_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 / option_set.size() - size.y) / 2);
} else {
pos.y = pos.y + lround((height - size.y) / 2);
}
if (width > 0)
rect_label = wxRect(pos, wxSize(size.x, size.y));
wxColour old_clr = dc.GetTextForeground();
wxFont old_font = dc.GetFont();
// 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
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);
}
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)
{
#ifndef DISABLE_BLINKING
pos = draw_blinking_bmp(dc, pos, is_blinking);
#else
if (ctrl->opt_group->split_multi_line) { // BBS
const std::vector<Option> &option_set = og_line.get_options();
if (option_set.size() > 1)
pos.y += lround((height / 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;
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 h_pos;
}
bool OG_CustomCtrl::CtrlLine::launch_browser() const
{
if (!is_focused || og_line.label_path.empty())
return false;
return true;
}
} // GUI
} // Slic3r