1 #include "wxExtensions.hpp"
2
3 #include <stdexcept>
4 #include <cmath>
5
6 #include <wx/sizer.h>
7
8 #include <boost/algorithm/string/replace.hpp>
9
10 #include "BitmapCache.hpp"
11 #include "GUI.hpp"
12 #include "GUI_App.hpp"
13 #include "GUI_ObjectList.hpp"
14 #include "I18N.hpp"
15 #include "GUI_Utils.hpp"
16 #include "Plater.hpp"
17 #include "../Utils/MacDarkMode.hpp"
18
19 #ifndef __linux__
20 // msw_menuitem_bitmaps is used for MSW and OSX
21 static std::map<int, std::string> msw_menuitem_bitmaps;
22 #ifdef __WXMSW__
msw_rescale_menu(wxMenu * menu)23 void msw_rescale_menu(wxMenu* menu)
24 {
25 struct update_icons {
26 static void run(wxMenuItem* item) {
27 const auto it = msw_menuitem_bitmaps.find(item->GetId());
28 if (it != msw_menuitem_bitmaps.end()) {
29 const wxBitmap& item_icon = create_scaled_bitmap(it->second);
30 if (item_icon.IsOk())
31 item->SetBitmap(item_icon);
32 }
33 if (item->IsSubMenu())
34 for (wxMenuItem *sub_item : item->GetSubMenu()->GetMenuItems())
35 update_icons::run(sub_item);
36 }
37 };
38
39 for (wxMenuItem *item : menu->GetMenuItems())
40 update_icons::run(item);
41 }
42 #endif /* __WXMSW__ */
43 #endif /* no __WXGTK__ */
44
enable_menu_item(wxUpdateUIEvent & evt,std::function<bool ()> const cb_condition,wxMenuItem * item,wxWindow * win)45 void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> const cb_condition, wxMenuItem* item, wxWindow* win)
46 {
47 const bool enable = cb_condition();
48 evt.Enable(enable);
49
50 #ifdef __WXOSX__
51 const auto it = msw_menuitem_bitmaps.find(item->GetId());
52 if (it != msw_menuitem_bitmaps.end())
53 {
54 const wxBitmap& item_icon = create_scaled_bitmap(it->second, win, 16, !enable);
55 if (item_icon.IsOk())
56 item->SetBitmap(item_icon);
57 }
58 #endif // __WXOSX__
59 }
60
append_menu_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,const wxBitmap & icon,wxEvtHandler * event_handler,std::function<bool ()> const cb_condition,wxWindow * parent,int insert_pos)61 wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
62 std::function<void(wxCommandEvent& event)> cb, const wxBitmap& icon, wxEvtHandler* event_handler,
63 std::function<bool()> const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/)
64 {
65 if (id == wxID_ANY)
66 id = wxNewId();
67
68 auto *item = new wxMenuItem(menu, id, string, description);
69 if (icon.IsOk()) {
70 item->SetBitmap(icon);
71 }
72 if (insert_pos == wxNOT_FOUND)
73 menu->Append(item);
74 else
75 menu->Insert(insert_pos, item);
76
77 #ifdef __WXMSW__
78 if (event_handler != nullptr && event_handler != menu)
79 event_handler->Bind(wxEVT_MENU, cb, id);
80 else
81 #endif // __WXMSW__
82 menu->Bind(wxEVT_MENU, cb, id);
83
84 if (parent) {
85 parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item, parent](wxUpdateUIEvent& evt) {
86 enable_menu_item(evt, cb_condition, item, parent); }, id);
87 }
88
89 return item;
90 }
91
append_menu_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,const std::string & icon,wxEvtHandler * event_handler,std::function<bool ()> const cb_condition,wxWindow * parent,int insert_pos)92 wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
93 std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler,
94 std::function<bool()> const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/)
95 {
96 if (id == wxID_ANY)
97 id = wxNewId();
98
99 const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr
100 //#ifdef __WXMSW__
101 #ifndef __WXGTK__
102 if (bmp.IsOk())
103 msw_menuitem_bitmaps[id] = icon;
104 #endif /* __WXMSW__ */
105
106 return append_menu_item(menu, id, string, description, cb, bmp, event_handler, cb_condition, parent, insert_pos);
107 }
108
append_submenu(wxMenu * menu,wxMenu * sub_menu,int id,const wxString & string,const wxString & description,const std::string & icon,std::function<bool ()> const cb_condition,wxWindow * parent)109 wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon,
110 std::function<bool()> const cb_condition, wxWindow* parent)
111 {
112 if (id == wxID_ANY)
113 id = wxNewId();
114
115 wxMenuItem* item = new wxMenuItem(menu, id, string, description);
116 if (!icon.empty()) {
117 item->SetBitmap(create_scaled_bitmap(icon)); // FIXME: pass window ptr
118 //#ifdef __WXMSW__
119 #ifndef __WXGTK__
120 msw_menuitem_bitmaps[id] = icon;
121 #endif /* __WXMSW__ */
122 }
123
124 item->SetSubMenu(sub_menu);
125 menu->Append(item);
126
127 if (parent) {
128 parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item, parent](wxUpdateUIEvent& evt) {
129 enable_menu_item(evt, cb_condition, item, parent); }, id);
130 }
131
132 return item;
133 }
134
append_menu_radio_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,wxEvtHandler * event_handler)135 wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
136 std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler)
137 {
138 if (id == wxID_ANY)
139 id = wxNewId();
140
141 wxMenuItem* item = menu->AppendRadioItem(id, string, description);
142
143 #ifdef __WXMSW__
144 if (event_handler != nullptr && event_handler != menu)
145 event_handler->Bind(wxEVT_MENU, cb, id);
146 else
147 #endif // __WXMSW__
148 menu->Bind(wxEVT_MENU, cb, id);
149
150 return item;
151 }
152
append_menu_check_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,wxEvtHandler * event_handler,std::function<bool ()> const enable_condition,std::function<bool ()> const check_condition,wxWindow * parent)153 wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
154 std::function<void(wxCommandEvent & event)> cb, wxEvtHandler* event_handler,
155 std::function<bool()> const enable_condition, std::function<bool()> const check_condition, wxWindow* parent)
156 {
157 if (id == wxID_ANY)
158 id = wxNewId();
159
160 wxMenuItem* item = menu->AppendCheckItem(id, string, description);
161
162 #ifdef __WXMSW__
163 if (event_handler != nullptr && event_handler != menu)
164 event_handler->Bind(wxEVT_MENU, cb, id);
165 else
166 #endif // __WXMSW__
167 menu->Bind(wxEVT_MENU, cb, id);
168
169 if (parent)
170 parent->Bind(wxEVT_UPDATE_UI, [enable_condition, check_condition](wxUpdateUIEvent& evt)
171 {
172 evt.Enable(enable_condition());
173 evt.Check(check_condition());
174 }, id);
175
176 return item;
177 }
178
179 const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200;
180 const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200;
181
Create(wxWindow * parent)182 bool wxCheckListBoxComboPopup::Create(wxWindow* parent)
183 {
184 return wxCheckListBox::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0));
185 }
186
GetControl()187 wxWindow* wxCheckListBoxComboPopup::GetControl()
188 {
189 return this;
190 }
191
SetStringValue(const wxString & value)192 void wxCheckListBoxComboPopup::SetStringValue(const wxString& value)
193 {
194 m_text = value;
195 }
196
GetStringValue() const197 wxString wxCheckListBoxComboPopup::GetStringValue() const
198 {
199 return m_text;
200 }
201
GetAdjustedSize(int minWidth,int prefHeight,int maxHeight)202 wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
203 {
204 // set width dinamically in dependence of items text
205 // and set height dinamically in dependence of items count
206
207 wxComboCtrl* cmb = GetComboCtrl();
208 if (cmb != nullptr) {
209 wxSize size = GetComboCtrl()->GetSize();
210
211 unsigned int count = GetCount();
212 if (count > 0) {
213 int max_width = size.x;
214 for (unsigned int i = 0; i < count; ++i) {
215 max_width = std::max(max_width, 60 + GetTextExtent(GetString(i)).x);
216 }
217 size.SetWidth(max_width);
218 size.SetHeight(count * cmb->GetCharHeight());
219 }
220 else
221 size.SetHeight(DefaultHeight);
222
223 return size;
224 }
225 else
226 return wxSize(DefaultWidth, DefaultHeight);
227 }
228
OnKeyEvent(wxKeyEvent & evt)229 void wxCheckListBoxComboPopup::OnKeyEvent(wxKeyEvent& evt)
230 {
231 // filters out all the keys which are not working properly
232 switch (evt.GetKeyCode())
233 {
234 case WXK_LEFT:
235 case WXK_UP:
236 case WXK_RIGHT:
237 case WXK_DOWN:
238 case WXK_PAGEUP:
239 case WXK_PAGEDOWN:
240 case WXK_END:
241 case WXK_HOME:
242 case WXK_NUMPAD_LEFT:
243 case WXK_NUMPAD_UP:
244 case WXK_NUMPAD_RIGHT:
245 case WXK_NUMPAD_DOWN:
246 case WXK_NUMPAD_PAGEUP:
247 case WXK_NUMPAD_PAGEDOWN:
248 case WXK_NUMPAD_END:
249 case WXK_NUMPAD_HOME:
250 {
251 break;
252 }
253 default:
254 {
255 evt.Skip();
256 break;
257 }
258 }
259 }
260
OnCheckListBox(wxCommandEvent & evt)261 void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt)
262 {
263 // forwards the checklistbox event to the owner wxComboCtrl
264
265 if (m_check_box_events_status == OnCheckListBoxFunction::FreeToProceed )
266 {
267 wxComboCtrl* cmb = GetComboCtrl();
268 if (cmb != nullptr) {
269 wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId());
270 event.SetEventObject(cmb);
271 cmb->ProcessWindowEvent(event);
272 }
273 }
274
275 evt.Skip();
276
277 #ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file)
278 if ( m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed )
279 // this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should
280 // explicitly change the state on the checkbox
281 m_check_box_events_status = OnCheckListBoxFunction::WasRefusedLastTime;
282 else
283 // if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it
284 m_check_box_events_status = OnCheckListBoxFunction::RefuseToProceed;
285 #endif
286 }
287
OnListBoxSelection(wxCommandEvent & evt)288 void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
289 {
290 // transforms list box item selection event into checklistbox item toggle event
291
292 int selId = GetSelection();
293 if (selId != wxNOT_FOUND)
294 {
295 #ifndef _WIN32
296 if (m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed)
297 #endif
298 Check((unsigned int)selId, !IsChecked((unsigned int)selId));
299
300 m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; // so the checkbox reacts to square-click the next time
301
302 SetSelection(wxNOT_FOUND);
303 wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId());
304 event.SetInt(selId);
305 event.SetEventObject(this);
306 ProcessEvent(event);
307 }
308 }
309
310
311 // *** wxDataViewTreeCtrlComboPopup ***
312
313 const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270;
314 const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200;
315 const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22;
316
Create(wxWindow * parent)317 bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent)
318 {
319 return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER);
320 }
321 /*
322 wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
323 {
324 // matches owner wxComboCtrl's width
325 // and sets height dinamically in dependence of contained items count
326 wxComboCtrl* cmb = GetComboCtrl();
327 if (cmb != nullptr)
328 {
329 wxSize size = GetComboCtrl()->GetSize();
330 if (m_cnt_open_items > 0)
331 size.SetHeight(m_cnt_open_items * DefaultItemHeight);
332 else
333 size.SetHeight(DefaultHeight);
334
335 return size;
336 }
337 else
338 return wxSize(DefaultWidth, DefaultHeight);
339 }
340 */
OnKeyEvent(wxKeyEvent & evt)341 void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt)
342 {
343 // filters out all the keys which are not working properly
344 if (evt.GetKeyCode() == WXK_UP)
345 {
346 return;
347 }
348 else if (evt.GetKeyCode() == WXK_DOWN)
349 {
350 return;
351 }
352 else
353 {
354 evt.Skip();
355 return;
356 }
357 }
358
OnDataViewTreeCtrlSelection(wxCommandEvent & evt)359 void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt)
360 {
361 wxComboCtrl* cmb = GetComboCtrl();
362 auto selected = GetItemText(GetSelection());
363 cmb->SetText(selected);
364 }
365
366 // edit tooltip : change Slic3r to SLIC3R_APP_KEY
367 // Temporary workaround for localization
edit_tooltip(wxString & tooltip)368 void edit_tooltip(wxString& tooltip)
369 {
370 tooltip.Replace("Slic3r", SLIC3R_APP_KEY, true);
371 }
372
373 /* Function for rescale of buttons in Dialog under MSW if dpi is changed.
374 * btn_ids - vector of buttons identifiers
375 */
msw_buttons_rescale(wxDialog * dlg,const int em_unit,const std::vector<int> & btn_ids)376 void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids)
377 {
378 const wxSize& btn_size = wxSize(-1, int(2.5f * em_unit + 0.5f));
379
380 for (int btn_id : btn_ids) {
381 // There is a case [FirmwareDialog], when we have wxControl instead of wxButton
382 // so let casting everything to the wxControl
383 wxControl* btn = static_cast<wxControl*>(dlg->FindWindowById(btn_id, dlg));
384 if (btn)
385 btn->SetMinSize(btn_size);
386 }
387 }
388
389 /* Function for getting of em_unit value from correct parent.
390 * In most of cases it is m_em_unit value from GUI_App,
391 * but for DPIDialogs it's its own value.
392 * This value will be used to correct rescale after moving between
393 * Displays with different HDPI */
em_unit(wxWindow * win)394 int em_unit(wxWindow* win)
395 {
396 if (win)
397 {
398 wxTopLevelWindow *toplevel = Slic3r::GUI::find_toplevel_parent(win);
399 Slic3r::GUI::DPIDialog* dlg = dynamic_cast<Slic3r::GUI::DPIDialog*>(toplevel);
400 if (dlg)
401 return dlg->em_unit();
402 Slic3r::GUI::DPIFrame* frame = dynamic_cast<Slic3r::GUI::DPIFrame*>(toplevel);
403 if (frame)
404 return frame->em_unit();
405 }
406
407 return Slic3r::GUI::wxGetApp().em_unit();
408 }
409
mode_icon_px_size()410 int mode_icon_px_size()
411 {
412 #ifdef __APPLE__
413 return 10;
414 #else
415 return 12;
416 #endif
417 }
418
419 // win is used to get a correct em_unit value
420 // It's important for bitmaps of dialogs.
421 // if win == nullptr, em_unit value of MainFrame will be used
create_scaled_bitmap(const std::string & bmp_name_in,wxWindow * win,const int px_cnt,const bool grayscale)422 wxBitmap create_scaled_bitmap( const std::string& bmp_name_in,
423 wxWindow *win/* = nullptr*/,
424 const int px_cnt/* = 16*/,
425 const bool grayscale/* = false*/)
426 {
427 static Slic3r::GUI::BitmapCache cache;
428
429 unsigned int width = 0;
430 unsigned int height = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f);
431
432 std::string bmp_name = bmp_name_in;
433 boost::replace_last(bmp_name, ".png", "");
434
435 // Try loading an SVG first, then PNG if SVG is not found:
436 wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, Slic3r::GUI::wxGetApp().dark_mode());
437 if (bmp == nullptr) {
438 bmp = cache.load_png(bmp_name, width, height, grayscale);
439 }
440
441 if (bmp == nullptr) {
442 // Neither SVG nor PNG has been found, raise error
443 throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name);
444 }
445
446 return *bmp;
447 }
448
get_extruder_color_icons(bool thin_icon)449 std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon/* = false*/)
450 {
451 static Slic3r::GUI::BitmapCache bmp_cache;
452
453 // Create the bitmap with color bars.
454 std::vector<wxBitmap*> bmps;
455 std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
456
457 if (colors.empty())
458 return bmps;
459
460 unsigned char rgb[3];
461
462 /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display.
463 * So set sizes for solid_colored icons used for filament preset
464 * and scale them in respect to em_unit value
465 */
466 const double em = Slic3r::GUI::wxGetApp().em_unit();
467 const int icon_width = lround((thin_icon ? 1.6 : 3.2) * em);
468 const int icon_height = lround(1.6 * em);
469
470 for (const std::string& color : colors)
471 {
472 std::string bitmap_key = color + "-h" + std::to_string(icon_height) + "-w" + std::to_string(icon_width);
473
474 wxBitmap* bitmap = bmp_cache.find(bitmap_key);
475 if (bitmap == nullptr) {
476 // Paint the color icon.
477 Slic3r::GUI::BitmapCache::parse_color(color, rgb);
478 // there is no neede to scale created solid bitmap
479 bitmap = bmp_cache.insert(bitmap_key, bmp_cache.mksolid(icon_width, icon_height, rgb, true));
480 }
481 bmps.emplace_back(bitmap);
482 }
483
484 return bmps;
485 }
486
487
apply_extruder_selector(wxBitmapComboBox ** ctrl,wxWindow * parent,const std::string & first_item,wxPoint pos,wxSize size,bool use_thin_icon)488 void apply_extruder_selector(wxBitmapComboBox** ctrl,
489 wxWindow* parent,
490 const std::string& first_item/* = ""*/,
491 wxPoint pos/* = wxDefaultPosition*/,
492 wxSize size/* = wxDefaultSize*/,
493 bool use_thin_icon/* = false*/)
494 {
495 std::vector<wxBitmap*> icons = get_extruder_color_icons(use_thin_icon);
496
497 if (!*ctrl)
498 *ctrl = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size,
499 0, nullptr, wxCB_READONLY);
500 else
501 {
502 (*ctrl)->SetPosition(pos);
503 (*ctrl)->SetMinSize(size);
504 (*ctrl)->SetSize(size);
505 (*ctrl)->Clear();
506 }
507 if (first_item.empty())
508 (*ctrl)->Hide(); // to avoid unwanted rendering before layout (ExtruderSequenceDialog)
509
510 if (icons.empty() && !first_item.empty()) {
511 (*ctrl)->Append(_(first_item), wxNullBitmap);
512 return;
513 }
514
515 // For ObjectList we use short extruder name (just a number)
516 const bool use_full_item_name = dynamic_cast<Slic3r::GUI::ObjectList*>(parent) == nullptr;
517
518 int i = 0;
519 wxString str = _(L("Extruder"));
520 for (wxBitmap* bmp : icons) {
521 if (i == 0) {
522 if (!first_item.empty())
523 (*ctrl)->Append(_(first_item), *bmp);
524 ++i;
525 }
526
527 (*ctrl)->Append(use_full_item_name
528 ? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % i).str())
529 : wxString::Format("%d", i), *bmp);
530 ++i;
531 }
532 (*ctrl)->SetSelection(0);
533 }
534
535
536 // ----------------------------------------------------------------------------
537 // LockButton
538 // ----------------------------------------------------------------------------
539
LockButton(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size)540 LockButton::LockButton( wxWindow *parent,
541 wxWindowID id,
542 const wxPoint& pos /*= wxDefaultPosition*/,
543 const wxSize& size /*= wxDefaultSize*/):
544 wxButton(parent, id, wxEmptyString, pos, size, wxBU_EXACTFIT | wxNO_BORDER)
545 {
546 m_bmp_lock_closed = ScalableBitmap(this, "lock_closed");
547 m_bmp_lock_closed_f = ScalableBitmap(this, "lock_closed_f");
548 m_bmp_lock_open = ScalableBitmap(this, "lock_open");
549 m_bmp_lock_open_f = ScalableBitmap(this, "lock_open_f");
550
551 #ifdef __WXMSW__
552 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
553 #endif // __WXMSW__
554 SetBitmap(m_bmp_lock_open.bmp());
555 SetBitmapDisabled(m_bmp_lock_open.bmp());
556 SetBitmapHover(m_bmp_lock_closed_f.bmp());
557
558 //button events
559 Bind(wxEVT_BUTTON, &LockButton::OnButton, this);
560 }
561
OnButton(wxCommandEvent & event)562 void LockButton::OnButton(wxCommandEvent& event)
563 {
564 if (m_disabled)
565 return;
566
567 m_is_pushed = !m_is_pushed;
568 update_button_bitmaps();
569
570 event.Skip();
571 }
572
SetLock(bool lock)573 void LockButton::SetLock(bool lock)
574 {
575 m_is_pushed = lock;
576 update_button_bitmaps();
577 }
578
msw_rescale()579 void LockButton::msw_rescale()
580 {
581 m_bmp_lock_closed.msw_rescale();
582 m_bmp_lock_closed_f.msw_rescale();
583 m_bmp_lock_open.msw_rescale();
584 m_bmp_lock_open_f.msw_rescale();
585
586 update_button_bitmaps();
587 }
588
update_button_bitmaps()589 void LockButton::update_button_bitmaps()
590 {
591 SetBitmap(m_is_pushed ? m_bmp_lock_closed.bmp() : m_bmp_lock_open.bmp());
592 SetBitmapHover(m_is_pushed ? m_bmp_lock_closed_f.bmp() : m_bmp_lock_open_f.bmp());
593
594 Refresh();
595 Update();
596 }
597
598
599
600 // ----------------------------------------------------------------------------
601 // ModeButton
602 // ----------------------------------------------------------------------------
603
ModeButton(wxWindow * parent,wxWindowID id,const std::string & icon_name,const wxString & mode,const wxSize & size,const wxPoint & pos)604 ModeButton::ModeButton( wxWindow * parent,
605 wxWindowID id,
606 const std::string& icon_name /* = ""*/,
607 const wxString& mode /* = wxEmptyString*/,
608 const wxSize& size /* = wxDefaultSize*/,
609 const wxPoint& pos /* = wxDefaultPosition*/) :
610 ScalableButton(parent, id, icon_name, mode, size, pos, wxBU_EXACTFIT)
611 {
612 Init(mode);
613 }
614
ModeButton(wxWindow * parent,const wxString & mode,const std::string & icon_name,int px_cnt)615 ModeButton::ModeButton( wxWindow* parent,
616 const wxString& mode/* = wxEmptyString*/,
617 const std::string& icon_name/* = ""*/,
618 int px_cnt/* = 16*/) :
619 ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT)
620 {
621 Init(mode);
622 }
623
Init(const wxString & mode)624 void ModeButton::Init(const wxString &mode)
625 {
626 std::string mode_str = std::string(mode.ToUTF8());
627 m_tt_focused = Slic3r::GUI::from_u8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str());
628 m_tt_selected = Slic3r::GUI::from_u8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str());
629
630 SetBitmapMargins(3, 0);
631
632 //button events
633 Bind(wxEVT_BUTTON, &ModeButton::OnButton, this);
634 Bind(wxEVT_ENTER_WINDOW, &ModeButton::OnEnterBtn, this);
635 Bind(wxEVT_LEAVE_WINDOW, &ModeButton::OnLeaveBtn, this);
636 }
637
OnButton(wxCommandEvent & event)638 void ModeButton::OnButton(wxCommandEvent& event)
639 {
640 m_is_selected = true;
641 focus_button(m_is_selected);
642
643 event.Skip();
644 }
645
SetState(const bool state)646 void ModeButton::SetState(const bool state)
647 {
648 m_is_selected = state;
649 focus_button(m_is_selected);
650 SetToolTip(state ? m_tt_selected : m_tt_focused);
651 }
652
focus_button(const bool focus)653 void ModeButton::focus_button(const bool focus)
654 {
655 const wxFont& new_font = focus ?
656 Slic3r::GUI::wxGetApp().bold_font() :
657 Slic3r::GUI::wxGetApp().normal_font();
658
659 SetFont(new_font);
660 SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT :
661 #if defined (__linux__) && defined (__WXGTK3__)
662 wxSYS_COLOUR_GRAYTEXT
663 #elif defined (__linux__) && defined (__WXGTK2__)
664 wxSYS_COLOUR_BTNTEXT
665 #else
666 wxSYS_COLOUR_BTNSHADOW
667 #endif
668 ));
669
670 Refresh();
671 Update();
672 }
673
674
675 // ----------------------------------------------------------------------------
676 // ModeSizer
677 // ----------------------------------------------------------------------------
678
ModeSizer(wxWindow * parent,int hgap)679 ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) :
680 wxFlexGridSizer(3, 0, hgap)
681 {
682 SetFlexibleDirection(wxHORIZONTAL);
683
684 std::vector < std::pair < wxString, std::string >> buttons = {
685 {_(L("Simple")), "mode_simple"},
686 // {_(L("Advanced")), "mode_advanced"},
687 {_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"},
688 {_(L("Expert")), "mode_expert"},
689 };
690
691 auto modebtnfn = [](wxCommandEvent &event, int mode_id) {
692 Slic3r::GUI::wxGetApp().save_mode(mode_id);
693 event.Skip();
694 };
695
696 m_mode_btns.reserve(3);
697 for (const auto& button : buttons) {
698 m_mode_btns.push_back(new ModeButton(parent, button.first, button.second, mode_icon_px_size()));
699
700 m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1)));
701 Add(m_mode_btns.back());
702 }
703 }
704
SetMode(const int mode)705 void ModeSizer::SetMode(const int mode)
706 {
707 for (size_t m = 0; m < m_mode_btns.size(); m++)
708 m_mode_btns[m]->SetState(int(m) == mode);
709 }
710
set_items_flag(int flag)711 void ModeSizer::set_items_flag(int flag)
712 {
713 for (wxSizerItem* item : this->GetChildren())
714 item->SetFlag(flag);
715 }
716
set_items_border(int border)717 void ModeSizer::set_items_border(int border)
718 {
719 for (wxSizerItem* item : this->GetChildren())
720 item->SetBorder(border);
721 }
722
msw_rescale()723 void ModeSizer::msw_rescale()
724 {
725 for (size_t m = 0; m < m_mode_btns.size(); m++)
726 m_mode_btns[m]->msw_rescale();
727 }
728
729 // ----------------------------------------------------------------------------
730 // MenuWithSeparators
731 // ----------------------------------------------------------------------------
732
DestroySeparators()733 void MenuWithSeparators::DestroySeparators()
734 {
735 if (m_separator_frst) {
736 Destroy(m_separator_frst);
737 m_separator_frst = nullptr;
738 }
739
740 if (m_separator_scnd) {
741 Destroy(m_separator_scnd);
742 m_separator_scnd = nullptr;
743 }
744 }
745
SetFirstSeparator()746 void MenuWithSeparators::SetFirstSeparator()
747 {
748 m_separator_frst = this->AppendSeparator();
749 }
750
SetSecondSeparator()751 void MenuWithSeparators::SetSecondSeparator()
752 {
753 m_separator_scnd = this->AppendSeparator();
754 }
755
756 // ----------------------------------------------------------------------------
757 // PrusaBitmap
758 // ----------------------------------------------------------------------------
ScalableBitmap(wxWindow * parent,const std::string & icon_name,const int px_cnt,const bool grayscale)759 ScalableBitmap::ScalableBitmap( wxWindow *parent,
760 const std::string& icon_name/* = ""*/,
761 const int px_cnt/* = 16*/,
762 const bool grayscale/* = false*/):
763 m_parent(parent), m_icon_name(icon_name),
764 m_px_cnt(px_cnt)
765 {
766 m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt, grayscale);
767 }
768
GetBmpSize() const769 wxSize ScalableBitmap::GetBmpSize() const
770 {
771 #ifdef __APPLE__
772 return m_bmp.GetScaledSize();
773 #else
774 return m_bmp.GetSize();
775 #endif
776 }
777
GetBmpWidth() const778 int ScalableBitmap::GetBmpWidth() const
779 {
780 #ifdef __APPLE__
781 return m_bmp.GetScaledWidth();
782 #else
783 return m_bmp.GetWidth();
784 #endif
785 }
786
GetBmpHeight() const787 int ScalableBitmap::GetBmpHeight() const
788 {
789 #ifdef __APPLE__
790 return m_bmp.GetScaledHeight();
791 #else
792 return m_bmp.GetHeight();
793 #endif
794 }
795
796
msw_rescale()797 void ScalableBitmap::msw_rescale()
798 {
799 m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt, m_grayscale);
800 }
801
802 // ----------------------------------------------------------------------------
803 // PrusaButton
804 // ----------------------------------------------------------------------------
805
ScalableButton(wxWindow * parent,wxWindowID id,const std::string & icon_name,const wxString & label,const wxSize & size,const wxPoint & pos,long style,bool use_default_disabled_bitmap,int bmp_px_cnt)806 ScalableButton::ScalableButton( wxWindow * parent,
807 wxWindowID id,
808 const std::string& icon_name /*= ""*/,
809 const wxString& label /* = wxEmptyString*/,
810 const wxSize& size /* = wxDefaultSize*/,
811 const wxPoint& pos /* = wxDefaultPosition*/,
812 long style /*= wxBU_EXACTFIT | wxNO_BORDER*/,
813 bool use_default_disabled_bitmap/* = false*/,
814 int bmp_px_cnt/* = 16*/) :
815 m_parent(parent),
816 m_current_icon_name(icon_name),
817 m_use_default_disabled_bitmap (use_default_disabled_bitmap),
818 m_px_cnt(bmp_px_cnt)
819 {
820 Create(parent, id, label, pos, size, style);
821 #ifdef __WXMSW__
822 if (style & wxNO_BORDER)
823 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
824 #endif // __WXMSW__
825
826 SetBitmap(create_scaled_bitmap(icon_name, parent, m_px_cnt));
827 if (m_use_default_disabled_bitmap)
828 SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true));
829
830 if (size != wxDefaultSize)
831 {
832 const int em = em_unit(parent);
833 m_width = size.x/em;
834 m_height= size.y/em;
835 }
836 }
837
838
ScalableButton(wxWindow * parent,wxWindowID id,const ScalableBitmap & bitmap,const wxString & label,long style)839 ScalableButton::ScalableButton( wxWindow * parent,
840 wxWindowID id,
841 const ScalableBitmap& bitmap,
842 const wxString& label /*= wxEmptyString*/,
843 long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) :
844 m_parent(parent),
845 m_current_icon_name(bitmap.name()),
846 m_px_cnt(bitmap.px_cnt())
847 {
848 Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style);
849 #ifdef __WXMSW__
850 if (style & wxNO_BORDER)
851 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
852 #endif // __WXMSW__
853
854 SetBitmap(bitmap.bmp());
855 }
856
SetBitmap_(const ScalableBitmap & bmp)857 void ScalableButton::SetBitmap_(const ScalableBitmap& bmp)
858 {
859 SetBitmap(bmp.bmp());
860 m_current_icon_name = bmp.name();
861 }
862
SetBitmapDisabled_(const ScalableBitmap & bmp)863 void ScalableButton::SetBitmapDisabled_(const ScalableBitmap& bmp)
864 {
865 SetBitmapDisabled(bmp.bmp());
866 m_disabled_icon_name = bmp.name();
867 }
868
GetBitmapHeight()869 int ScalableButton::GetBitmapHeight()
870 {
871 #ifdef __APPLE__
872 return GetBitmap().GetScaledHeight();
873 #else
874 return GetBitmap().GetHeight();
875 #endif
876 }
877
UseDefaultBitmapDisabled()878 void ScalableButton::UseDefaultBitmapDisabled()
879 {
880 m_use_default_disabled_bitmap = true;
881 SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true));
882 }
883
msw_rescale()884 void ScalableButton::msw_rescale()
885 {
886 SetBitmap(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt));
887 if (!m_disabled_icon_name.empty())
888 SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt));
889 else if (m_use_default_disabled_bitmap)
890 SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true));
891
892 if (m_width > 0 || m_height>0)
893 {
894 const int em = em_unit(m_parent);
895 wxSize size(m_width * em, m_height * em);
896 SetMinSize(size);
897 }
898 }
899
900
901 // ----------------------------------------------------------------------------
902 // BlinkingBitmap
903 // ----------------------------------------------------------------------------
904
BlinkingBitmap(wxWindow * parent,const std::string & icon_name)905 BlinkingBitmap::BlinkingBitmap(wxWindow* parent, const std::string& icon_name) :
906 wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(int(1.6 * Slic3r::GUI::wxGetApp().em_unit()), -1))
907 {
908 bmp = ScalableBitmap(parent, icon_name);
909 }
910
msw_rescale()911 void BlinkingBitmap::msw_rescale()
912 {
913 bmp.msw_rescale();
914 this->SetSize(bmp.GetBmpSize());
915 this->SetMinSize(bmp.GetBmpSize());
916 }
917
invalidate()918 void BlinkingBitmap::invalidate()
919 {
920 this->SetBitmap(wxNullBitmap);
921 }
922
activate()923 void BlinkingBitmap::activate()
924 {
925 this->SetBitmap(bmp.bmp());
926 show = true;
927 }
928
blink()929 void BlinkingBitmap::blink()
930 {
931 show = !show;
932 this->SetBitmap(show ? bmp.bmp() : wxNullBitmap);
933 }
934
935
936
937
938