1 #include "libslic3r/Point.hpp"
2 #include "libslic3r/libslic3r.h"
3 
4 #include "GLToolbar.hpp"
5 
6 #include "slic3r/GUI/GLCanvas3D.hpp"
7 #include "slic3r/GUI/GUI_App.hpp"
8 #include "slic3r/GUI/Camera.hpp"
9 #include "slic3r/GUI/Plater.hpp"
10 
11 #include <wx/event.h>
12 #include <wx/bitmap.h>
13 #include <wx/dcmemory.h>
14 #include <wx/settings.h>
15 #include <wx/glcanvas.h>
16 
17 namespace Slic3r {
18 namespace GUI {
19 
20 wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
21 wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
22 wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
23 wxDEFINE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
24 wxDEFINE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
25 wxDEFINE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
26 wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
27 wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
28 wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
29 wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent);
30 wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent);
31 
32 wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_3D, SimpleEvent);
33 wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent);
34 
__anon09d882000102()35 const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){};
__anon09d882000202()36 const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; };
__anon09d882000302()37 const GLToolbarItem::EnablingCallback GLToolbarItem::Default_Enabling_Callback = []()->bool { return true; };
__anon09d882000402(float, float, float, float)38 const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){};
39 
Option()40 GLToolbarItem::Data::Option::Option()
41     : toggable(false)
42     , action_callback(Default_Action_Callback)
43     , render_callback(nullptr)
44 {
45 }
46 
Data()47 GLToolbarItem::Data::Data()
48     : name("")
49     , icon_filename("")
50     , tooltip("")
51     , additional_tooltip("")
52     , sprite_id(-1)
53     , visible(true)
54     , visibility_callback(Default_Visibility_Callback)
55     , enabling_callback(Default_Enabling_Callback)
56 {
57 }
58 
GLToolbarItem(GLToolbarItem::EType type,const GLToolbarItem::Data & data)59 GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Data& data)
60     : m_type(type)
61     , m_state(Normal)
62     , m_data(data)
63     , m_last_action_type(Undefined)
64 {
65 }
66 
update_visibility()67 bool GLToolbarItem::update_visibility()
68 {
69     bool visible = m_data.visibility_callback();
70     bool ret = (m_data.visible != visible);
71     if (ret)
72         m_data.visible = visible;
73     // Return false for separator as it would always return true.
74     return is_separator() ? false : ret;
75 }
76 
update_enabled_state()77 bool GLToolbarItem::update_enabled_state()
78 {
79     bool enabled = m_data.enabling_callback();
80     bool ret = (is_enabled() != enabled);
81     if (ret)
82         m_state = enabled ? GLToolbarItem::Normal : GLToolbarItem::Disabled;
83 
84     return ret;
85 }
86 
render(unsigned int tex_id,float left,float right,float bottom,float top,unsigned int tex_width,unsigned int tex_height,unsigned int icon_size) const87 void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
88 {
89     auto uvs = [this](unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) -> GLTexture::Quad_UVs
90     {
91         assert((tex_width != 0) && (tex_height != 0));
92         GLTexture::Quad_UVs ret;
93         // tiles in the texture are spaced by 1 pixel
94         float icon_size_px = (float)(tex_width - 1) / (float)Num_States;
95         float inv_tex_width = 1.0f / (float)tex_width;
96         float inv_tex_height = 1.0f / (float)tex_height;
97         // tiles in the texture are spaced by 1 pixel
98         float u_offset = 1.0f * inv_tex_width;
99         float v_offset = 1.0f * inv_tex_height;
100         float du = icon_size_px * inv_tex_width;
101         float dv = icon_size_px * inv_tex_height;
102         float left = u_offset + (float)m_state * du;
103         float right = left + du - u_offset;
104         float top = v_offset + (float)m_data.sprite_id * dv;
105         float bottom = top + dv - v_offset;
106         ret.left_top = { left, top };
107         ret.left_bottom = { left, bottom };
108         ret.right_bottom = { right, bottom };
109         ret.right_top = { right, top };
110         return ret;
111     };
112 
113     GLTexture::render_sub_texture(tex_id, left, right, bottom, top, uvs(tex_width, tex_height, icon_size));
114 
115     if (is_pressed())
116     {
117         if ((m_last_action_type == Left) && m_data.left.can_render())
118             m_data.left.render_callback(left, right, bottom, top);
119         else if ((m_last_action_type == Right) && m_data.right.can_render())
120             m_data.right.render_callback(left, right, bottom, top);
121     }
122 }
123 
Metadata()124 BackgroundTexture::Metadata::Metadata()
125     : filename("")
126     , left(0)
127     , right(0)
128     , top(0)
129     , bottom(0)
130 {
131 }
132 
133 const float GLToolbar::Default_Icons_Size = 40.0f;
134 
Layout()135 GLToolbar::Layout::Layout()
136     : type(Horizontal)
137     , horizontal_orientation(HO_Center)
138     , vertical_orientation(VO_Center)
139     , top(0.0f)
140     , left(0.0f)
141     , border(0.0f)
142     , separator_size(0.0f)
143     , gap_size(0.0f)
144     , icons_size(Default_Icons_Size)
145     , scale(1.0f)
146     , width(0.0f)
147     , height(0.0f)
148     , dirty(true)
149 {
150 }
151 
GLToolbar(GLToolbar::EType type,const std::string & name)152 GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name)
153     : m_type(type)
154     , m_name(name)
155     , m_enabled(false)
156     , m_icons_texture_dirty(true)
157     , m_pressed_toggable_id(-1)
158 {
159 }
160 
~GLToolbar()161 GLToolbar::~GLToolbar()
162 {
163     for (GLToolbarItem* item : m_items)
164     {
165         delete item;
166     }
167 }
168 
init(const BackgroundTexture::Metadata & background_texture)169 bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
170 {
171     if (m_background_texture.texture.get_id() != 0)
172         return true;
173 
174     std::string path = resources_dir() + "/icons/";
175     bool res = false;
176 
177     if (!background_texture.filename.empty())
178         res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, GLTexture::SingleThreaded, false);
179 
180     if (res)
181         m_background_texture.metadata = background_texture;
182 
183     return res;
184 }
185 
get_layout_type() const186 GLToolbar::Layout::EType GLToolbar::get_layout_type() const
187 {
188     return m_layout.type;
189 }
190 
set_layout_type(GLToolbar::Layout::EType type)191 void GLToolbar::set_layout_type(GLToolbar::Layout::EType type)
192 {
193     m_layout.type = type;
194     m_layout.dirty = true;
195 }
196 
set_position(float top,float left)197 void GLToolbar::set_position(float top, float left)
198 {
199     m_layout.top = top;
200     m_layout.left = left;
201 }
202 
set_border(float border)203 void GLToolbar::set_border(float border)
204 {
205     m_layout.border = border;
206     m_layout.dirty = true;
207 }
208 
set_separator_size(float size)209 void GLToolbar::set_separator_size(float size)
210 {
211     m_layout.separator_size = size;
212     m_layout.dirty = true;
213 }
214 
set_gap_size(float size)215 void GLToolbar::set_gap_size(float size)
216 {
217     m_layout.gap_size = size;
218     m_layout.dirty = true;
219 }
220 
set_icons_size(float size)221 void GLToolbar::set_icons_size(float size)
222 {
223     if (m_layout.icons_size != size)
224     {
225         m_layout.icons_size = size;
226         m_layout.dirty = true;
227         m_icons_texture_dirty = true;
228     }
229 }
230 
set_scale(float scale)231 void GLToolbar::set_scale(float scale)
232 {
233     if (m_layout.scale != scale) {
234         m_layout.scale = scale;
235         m_layout.dirty = true;
236         m_icons_texture_dirty = true;
237     }
238 }
239 
add_item(const GLToolbarItem::Data & data)240 bool GLToolbar::add_item(const GLToolbarItem::Data& data)
241 {
242     GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data);
243     if (item == nullptr)
244         return false;
245 
246     m_items.push_back(item);
247     m_layout.dirty = true;
248     return true;
249 }
250 
add_separator()251 bool GLToolbar::add_separator()
252 {
253     GLToolbarItem::Data data;
254     GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Separator, data);
255     if (item == nullptr)
256         return false;
257 
258     m_items.push_back(item);
259     m_layout.dirty = true;
260     return true;
261 }
262 
get_width() const263 float GLToolbar::get_width() const
264 {
265     if (m_layout.dirty)
266         calc_layout();
267 
268     return m_layout.width;
269 }
270 
get_height() const271 float GLToolbar::get_height() const
272 {
273     if (m_layout.dirty)
274         calc_layout();
275 
276     return m_layout.height;
277 }
278 
select_item(const std::string & name)279 void GLToolbar::select_item(const std::string& name)
280 {
281     if (is_item_disabled(name))
282         return;
283 
284     for (GLToolbarItem* item : m_items)
285     {
286         if (!item->is_disabled())
287         {
288             bool hover = item->is_hovered();
289             item->set_state((item->get_name() == name) ? (hover ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed) : (hover ? GLToolbarItem::Hover : GLToolbarItem::Normal));
290         }
291     }
292 }
293 
is_item_pressed(const std::string & name) const294 bool GLToolbar::is_item_pressed(const std::string& name) const
295 {
296     for (const GLToolbarItem* item : m_items)
297     {
298         if (item->get_name() == name)
299             return item->is_pressed();
300     }
301 
302     return false;
303 }
304 
is_item_disabled(const std::string & name) const305 bool GLToolbar::is_item_disabled(const std::string& name) const
306 {
307     for (const GLToolbarItem* item : m_items)
308     {
309         if (item->get_name() == name)
310             return item->is_disabled();
311     }
312 
313     return false;
314 }
315 
is_item_visible(const std::string & name) const316 bool GLToolbar::is_item_visible(const std::string& name) const
317 {
318     for (const GLToolbarItem* item : m_items)
319     {
320         if (item->get_name() == name)
321             return item->is_visible();
322     }
323 
324     return false;
325 }
326 
is_any_item_pressed() const327 bool GLToolbar::is_any_item_pressed() const
328 {
329     for (const GLToolbarItem* item : m_items)
330     {
331         if (item->is_pressed())
332             return true;
333     }
334 
335     return false;
336 }
337 
get_item_id(const std::string & name) const338 int GLToolbar::get_item_id(const std::string& name) const
339 {
340     for (int i = 0; i < (int)m_items.size(); ++i)
341     {
342         if (m_items[i]->get_name() == name)
343             return i;
344     }
345 
346     return -1;
347 }
348 
get_tooltip() const349 std::string GLToolbar::get_tooltip() const
350 {
351     std::string tooltip;
352 
353     for (GLToolbarItem* item : m_items)
354     {
355         if (item->is_hovered())
356         {
357             tooltip = item->get_tooltip();
358             if (!item->is_pressed())
359             {
360                 const std::string& additional_tooltip = item->get_additional_tooltip();
361                 if (!additional_tooltip.empty())
362                     tooltip += "\n" + additional_tooltip;
363 
364                 break;
365             }
366         }
367     }
368 
369     return tooltip;
370 }
371 
get_additional_tooltip(int item_id,std::string & text)372 void GLToolbar::get_additional_tooltip(int item_id, std::string& text)
373 {
374     if (0 <= item_id && item_id < (int)m_items.size())
375     {
376         text = m_items[item_id]->get_additional_tooltip();
377         return;
378     }
379 
380     text.clear();
381 }
382 
set_additional_tooltip(int item_id,const std::string & text)383 void GLToolbar::set_additional_tooltip(int item_id, const std::string& text)
384 {
385     if (0 <= item_id && item_id < (int)m_items.size())
386         m_items[item_id]->set_additional_tooltip(text);
387 }
388 
set_tooltip(int item_id,const std::string & text)389 void GLToolbar::set_tooltip(int item_id, const std::string& text)
390 {
391     if (0 <= item_id && item_id < (int)m_items.size())
392         m_items[item_id]->set_tooltip(text);
393 }
394 
update_items_state()395 bool GLToolbar::update_items_state()
396 {
397     bool ret = false;
398     ret |= update_items_visibility();
399     ret |= update_items_enabled_state();
400     if (!is_any_item_pressed())
401         m_pressed_toggable_id = -1;
402 
403     return ret;
404 }
405 
render(const GLCanvas3D & parent) const406 void GLToolbar::render(const GLCanvas3D& parent) const
407 {
408     if (!m_enabled || m_items.empty())
409         return;
410 
411     if (m_icons_texture_dirty)
412         generate_icons_texture();
413 
414     switch (m_layout.type)
415     {
416     default:
417     case Layout::Horizontal: { render_horizontal(parent); break; }
418     case Layout::Vertical: { render_vertical(parent); break; }
419     }
420 }
421 
on_mouse(wxMouseEvent & evt,GLCanvas3D & parent)422 bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
423 {
424     if (!m_enabled)
425         return false;
426 
427     Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY());
428     bool processed = false;
429 
430     // mouse anywhere
431     if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr))
432     {
433         if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) {
434             // prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it,
435             // as when switching between views
436             m_mouse_capture.reset();
437             return true;
438         }
439         m_mouse_capture.reset();
440     }
441 
442     if (evt.Moving())
443         update_hover_state(mouse_pos, parent);
444     else if (evt.LeftUp())
445     {
446         if (m_mouse_capture.left)
447         {
448             processed = true;
449             m_mouse_capture.left = false;
450         }
451         else
452             return false;
453     }
454     else if (evt.MiddleUp())
455     {
456         if (m_mouse_capture.middle)
457         {
458             processed = true;
459             m_mouse_capture.middle = false;
460         }
461         else
462             return false;
463     }
464     else if (evt.RightUp())
465     {
466         if (m_mouse_capture.right)
467         {
468             processed = true;
469             m_mouse_capture.right = false;
470         }
471         else
472             return false;
473     }
474     else if (evt.Dragging())
475     {
476         if (m_mouse_capture.any())
477             // if the button down was done on this toolbar, prevent from dragging into the scene
478             processed = true;
479         else
480             return false;
481     }
482 
483     int item_id = contains_mouse(mouse_pos, parent);
484     if (item_id != -1)
485     {
486         // mouse inside toolbar
487         if (evt.LeftDown() || evt.LeftDClick())
488         {
489             m_mouse_capture.left = true;
490             m_mouse_capture.parent = &parent;
491             processed = true;
492             if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() &&
493                 ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)))
494             {
495                 // mouse is inside an icon
496                 do_action(GLToolbarItem::Left, item_id, parent, true);
497                 parent.set_as_dirty();
498             }
499         }
500         else if (evt.MiddleDown())
501         {
502             m_mouse_capture.middle = true;
503             m_mouse_capture.parent = &parent;
504         }
505         else if (evt.RightDown())
506         {
507             m_mouse_capture.right = true;
508             m_mouse_capture.parent = &parent;
509             processed = true;
510             if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() &&
511                 ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right)))
512             {
513                 // mouse is inside an icon
514                 do_action(GLToolbarItem::Right, item_id, parent, true);
515                 parent.set_as_dirty();
516             }
517         }
518     }
519 
520     return processed;
521 }
522 
calc_layout() const523 void GLToolbar::calc_layout() const
524 {
525     switch (m_layout.type)
526     {
527     default:
528     case Layout::Horizontal:
529     {
530         m_layout.width = get_width_horizontal();
531         m_layout.height = get_height_horizontal();
532         break;
533     }
534     case Layout::Vertical:
535     {
536         m_layout.width = get_width_vertical();
537         m_layout.height = get_height_vertical();
538         break;
539     }
540     }
541 
542     m_layout.dirty = false;
543 }
544 
get_width_horizontal() const545 float GLToolbar::get_width_horizontal() const
546 {
547     return get_main_size();
548 }
549 
get_width_vertical() const550 float GLToolbar::get_width_vertical() const
551 {
552     return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
553 }
554 
get_height_horizontal() const555 float GLToolbar::get_height_horizontal() const
556 {
557     return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
558 }
559 
get_height_vertical() const560 float GLToolbar::get_height_vertical() const
561 {
562     return get_main_size();
563 }
564 
get_main_size() const565 float GLToolbar::get_main_size() const
566 {
567     float size = 2.0f * m_layout.border;
568     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
569     {
570         if (!m_items[i]->is_visible())
571             continue;
572 
573         if (m_items[i]->is_separator())
574             size += m_layout.separator_size;
575         else
576             size += (float)m_layout.icons_size;
577     }
578 
579     if (m_items.size() > 1)
580         size += ((float)m_items.size() - 1.0f) * m_layout.gap_size;
581 
582     return size * m_layout.scale;
583 }
584 
get_visible_items_cnt() const585 int GLToolbar::get_visible_items_cnt() const
586 {
587     int cnt = 0;
588     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
589         if (m_items[i]->is_visible() && !m_items[i]->is_separator())
590             cnt++;
591 
592     return cnt;
593 }
594 
do_action(GLToolbarItem::EActionType type,int item_id,GLCanvas3D & parent,bool check_hover)595 void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover)
596 {
597     if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id))
598     {
599         if ((0 <= item_id) && (item_id < (int)m_items.size()))
600         {
601             GLToolbarItem* item = m_items[item_id];
602             if ((item != nullptr) && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered()))
603             {
604                 if (((type == GLToolbarItem::Right) && item->is_right_toggable()) ||
605                     ((type == GLToolbarItem::Left) && item->is_left_toggable()))
606                 {
607                     GLToolbarItem::EState state = item->get_state();
608                     if (state == GLToolbarItem::Hover)
609                         item->set_state(GLToolbarItem::HoverPressed);
610                     else if (state == GLToolbarItem::HoverPressed)
611                         item->set_state(GLToolbarItem::Hover);
612                     else if (state == GLToolbarItem::Pressed)
613                         item->set_state(GLToolbarItem::Normal);
614                     else if (state == GLToolbarItem::Normal)
615                         item->set_state(GLToolbarItem::Pressed);
616 
617                     m_pressed_toggable_id = item->is_pressed() ? item_id : -1;
618                     item->reset_last_action_type();
619 
620                     parent.render();
621                     switch (type)
622                     {
623                     default:
624                     case GLToolbarItem::Left: { item->do_left_action(); break; }
625                     case GLToolbarItem::Right: { item->do_right_action(); break; }
626                     }
627                 }
628                 else
629                 {
630                     if (m_type == Radio)
631                         select_item(item->get_name());
632                     else
633                         item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed);
634 
635                     item->reset_last_action_type();
636                     parent.render();
637                     switch (type)
638                     {
639                     default:
640                     case GLToolbarItem::Left: { item->do_left_action(); break; }
641                     case GLToolbarItem::Right: { item->do_right_action(); break; }
642                     }
643 
644                     if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled))
645                     {
646                         // the item may get disabled during the action, if not, set it back to hover state
647                         item->set_state(GLToolbarItem::Hover);
648                         parent.render();
649                     }
650                 }
651             }
652         }
653     }
654 }
655 
update_hover_state(const Vec2d & mouse_pos,GLCanvas3D & parent)656 void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent)
657 {
658     if (!m_enabled)
659         return;
660 
661     switch (m_layout.type)
662     {
663     default:
664     case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; }
665     case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; }
666     }
667 }
668 
update_hover_state_horizontal(const Vec2d & mouse_pos,GLCanvas3D & parent)669 void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent)
670 {
671     // NB: mouse_pos is already scaled appropriately
672 
673     float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
674     float factor = m_layout.scale * inv_zoom;
675 
676     Size cnv_size = parent.get_canvas_size();
677     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
678 
679     float scaled_icons_size = m_layout.icons_size * factor;
680     float scaled_separator_size = m_layout.separator_size * factor;
681     float scaled_gap_size = m_layout.gap_size * factor;
682     float scaled_border = m_layout.border * factor;
683 
684     float separator_stride = scaled_separator_size + scaled_gap_size;
685     float icon_stride = scaled_icons_size + scaled_gap_size;
686 
687     float left = m_layout.left + scaled_border;
688     float top = m_layout.top - scaled_border;
689 
690     for (GLToolbarItem* item : m_items)
691     {
692         if (!item->is_visible())
693             continue;
694 
695         if (item->is_separator())
696             left += separator_stride;
697         else
698         {
699             float right = left + scaled_icons_size;
700             float bottom = top - scaled_icons_size;
701 
702             GLToolbarItem::EState state = item->get_state();
703             bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
704 
705             switch (state)
706             {
707             case GLToolbarItem::Normal:
708             {
709                 if (inside)
710                 {
711                     item->set_state(GLToolbarItem::Hover);
712                     parent.set_as_dirty();
713                 }
714 
715                 break;
716             }
717             case GLToolbarItem::Hover:
718             {
719                 if (!inside)
720                 {
721                     item->set_state(GLToolbarItem::Normal);
722                     parent.set_as_dirty();
723                 }
724 
725                 break;
726             }
727             case GLToolbarItem::Pressed:
728             {
729                 if (inside)
730                 {
731                     item->set_state(GLToolbarItem::HoverPressed);
732                     parent.set_as_dirty();
733                 }
734 
735                 break;
736             }
737             case GLToolbarItem::HoverPressed:
738             {
739                 if (!inside)
740                 {
741                     item->set_state(GLToolbarItem::Pressed);
742                     parent.set_as_dirty();
743                 }
744 
745                 break;
746             }
747             case GLToolbarItem::Disabled:
748             {
749                 if (inside)
750                 {
751                     item->set_state(GLToolbarItem::HoverDisabled);
752                     parent.set_as_dirty();
753                 }
754 
755                 break;
756             }
757             case GLToolbarItem::HoverDisabled:
758             {
759                 if (!inside)
760                 {
761                     item->set_state(GLToolbarItem::Disabled);
762                     parent.set_as_dirty();
763                 }
764 
765                 break;
766             }
767             default:
768             {
769                 break;
770             }
771             }
772 
773             left += icon_stride;
774         }
775     }
776 }
777 
update_hover_state_vertical(const Vec2d & mouse_pos,GLCanvas3D & parent)778 void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent)
779 {
780     // NB: mouse_pos is already scaled appropriately
781 
782     float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
783     float factor = m_layout.scale * inv_zoom;
784 
785     Size cnv_size = parent.get_canvas_size();
786     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
787 
788     float scaled_icons_size = m_layout.icons_size * factor;
789     float scaled_separator_size = m_layout.separator_size * factor;
790     float scaled_gap_size = m_layout.gap_size * factor;
791     float scaled_border = m_layout.border * factor;
792     float separator_stride = scaled_separator_size + scaled_gap_size;
793     float icon_stride = scaled_icons_size + scaled_gap_size;
794 
795     float left = m_layout.left + scaled_border;
796     float top = m_layout.top - scaled_border;
797 
798     for (GLToolbarItem* item : m_items)
799     {
800         if (!item->is_visible())
801             continue;
802 
803         if (item->is_separator())
804             top -= separator_stride;
805         else
806         {
807             float right = left + scaled_icons_size;
808             float bottom = top - scaled_icons_size;
809 
810             GLToolbarItem::EState state = item->get_state();
811             bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
812 
813             switch (state)
814             {
815             case GLToolbarItem::Normal:
816             {
817                 if (inside)
818                 {
819                     item->set_state(GLToolbarItem::Hover);
820                     parent.set_as_dirty();
821                 }
822 
823                 break;
824             }
825             case GLToolbarItem::Hover:
826             {
827                 if (!inside)
828                 {
829                     item->set_state(GLToolbarItem::Normal);
830                     parent.set_as_dirty();
831                 }
832 
833                 break;
834             }
835             case GLToolbarItem::Pressed:
836             {
837                 if (inside)
838                 {
839                     item->set_state(GLToolbarItem::HoverPressed);
840                     parent.set_as_dirty();
841                 }
842 
843                 break;
844             }
845             case GLToolbarItem::HoverPressed:
846             {
847                 if (!inside)
848                 {
849                     item->set_state(GLToolbarItem::Pressed);
850                     parent.set_as_dirty();
851                 }
852 
853                 break;
854             }
855             case GLToolbarItem::Disabled:
856             {
857                 if (inside)
858                 {
859                     item->set_state(GLToolbarItem::HoverDisabled);
860                     parent.set_as_dirty();
861                 }
862 
863                 break;
864             }
865             case GLToolbarItem::HoverDisabled:
866             {
867                 if (!inside)
868                 {
869                     item->set_state(GLToolbarItem::Disabled);
870                     parent.set_as_dirty();
871                 }
872 
873                 break;
874             }
875             default:
876             {
877                 break;
878             }
879             }
880 
881             top -= icon_stride;
882         }
883     }
884 }
885 
contains_mouse(const Vec2d & mouse_pos,const GLCanvas3D & parent) const886 int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
887 {
888     if (!m_enabled)
889         return -1;
890 
891     switch (m_layout.type)
892     {
893     default:
894     case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); }
895     case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); }
896     }
897 }
898 
contains_mouse_horizontal(const Vec2d & mouse_pos,const GLCanvas3D & parent) const899 int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
900 {
901     // NB: mouse_pos is already scaled appropriately
902 
903     float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
904     float factor = m_layout.scale * inv_zoom;
905 
906     Size cnv_size = parent.get_canvas_size();
907     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
908 
909     float scaled_icons_size = m_layout.icons_size * factor;
910     float scaled_separator_size = m_layout.separator_size * factor;
911     float scaled_gap_size = m_layout.gap_size * factor;
912     float scaled_border = m_layout.border * factor;
913 
914     float left = m_layout.left + scaled_border;
915     float top = m_layout.top - scaled_border;
916 
917 
918     for (size_t id=0; id<m_items.size(); ++id)
919     {
920         GLToolbarItem* item = m_items[id];
921 
922         if (!item->is_visible())
923             continue;
924 
925         if (item->is_separator())
926         {
927             float right = left + scaled_separator_size;
928             float bottom = top - scaled_icons_size;
929 
930             // mouse inside the separator
931             if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
932                 return id;
933 
934             left = right;
935             right += scaled_gap_size;
936 
937             if (id < m_items.size() - 1)
938             {
939                 // mouse inside the gap
940                 if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
941                     return -2;
942             }
943 
944             left = right;
945         }
946         else
947         {
948             float right = left + scaled_icons_size;
949             float bottom = top - scaled_icons_size;
950 
951             // mouse inside the icon
952             if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
953                 return id;
954 
955             left = right;
956             right += scaled_gap_size;
957 
958             if (id < m_items.size() - 1)
959             {
960                 // mouse inside the gap
961                 if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
962                     return -2;
963             }
964 
965             left = right;
966         }
967     }
968 
969     return -1;
970 }
971 
contains_mouse_vertical(const Vec2d & mouse_pos,const GLCanvas3D & parent) const972 int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
973 {
974     // NB: mouse_pos is already scaled appropriately
975 
976     float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
977     float factor = m_layout.scale * inv_zoom;
978 
979     Size cnv_size = parent.get_canvas_size();
980     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
981 
982     float scaled_icons_size = m_layout.icons_size * factor;
983     float scaled_separator_size = m_layout.separator_size * factor;
984     float scaled_gap_size = m_layout.gap_size * factor;
985     float scaled_border = m_layout.border * factor;
986 
987     float left = m_layout.left + scaled_border;
988     float top = m_layout.top - scaled_border;
989 
990     for (size_t id=0; id<m_items.size(); ++id)
991     {
992         GLToolbarItem* item = m_items[id];
993 
994         if (!item->is_visible())
995             continue;
996 
997         if (item->is_separator())
998         {
999             float right = left + scaled_icons_size;
1000             float bottom = top - scaled_separator_size;
1001 
1002             // mouse inside the separator
1003             if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
1004                 return id;
1005 
1006             top = bottom;
1007             bottom -= scaled_gap_size;
1008 
1009             if (id < m_items.size() - 1)
1010             {
1011                 // mouse inside the gap
1012                 if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
1013                     return -2;
1014             }
1015 
1016             top = bottom;
1017         }
1018         else
1019         {
1020             float right = left + scaled_icons_size;
1021             float bottom = top - scaled_icons_size;
1022 
1023             // mouse inside the icon
1024             if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
1025                 return id;
1026 
1027             top = bottom;
1028             bottom -= scaled_gap_size;
1029 
1030             if (id < m_items.size() - 1)
1031             {
1032                 // mouse inside the gap
1033                 if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
1034                     return -2;
1035             }
1036 
1037             top = bottom;
1038         }
1039     }
1040 
1041     return -1;
1042 }
1043 
render_background(float left,float top,float right,float bottom,float border) const1044 void GLToolbar::render_background(float left, float top, float right, float bottom, float border) const
1045 {
1046     unsigned int tex_id = m_background_texture.texture.get_id();
1047     float tex_width = (float)m_background_texture.texture.get_width();
1048     float tex_height = (float)m_background_texture.texture.get_height();
1049     if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0))
1050     {
1051         float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
1052         float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
1053 
1054         float internal_left = left + border;
1055         float internal_right = right - border;
1056         float internal_top = top - border;
1057         float internal_bottom = bottom + border;
1058 
1059         float left_uv = 0.0f;
1060         float right_uv = 1.0f;
1061         float top_uv = 1.0f;
1062         float bottom_uv = 0.0f;
1063 
1064         float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width;
1065         float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width;
1066         float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height;
1067         float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height;
1068 
1069         // top-left corner
1070         if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Top))
1071             GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1072         else
1073             GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { left_uv, internal_top_uv }, { internal_left_uv, internal_top_uv }, { internal_left_uv, top_uv }, { left_uv, top_uv } });
1074 
1075         // top edge
1076         if (m_layout.vertical_orientation == Layout::VO_Top)
1077             GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1078         else
1079             GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } });
1080 
1081         // top-right corner
1082         if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Top))
1083             GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1084         else
1085             GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } });
1086 
1087         // center-left edge
1088         if (m_layout.horizontal_orientation == Layout::HO_Left)
1089             GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1090         else
1091             GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { left_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { left_uv, internal_top_uv } });
1092 
1093         // center
1094         GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1095 
1096         // center-right edge
1097         if (m_layout.horizontal_orientation == Layout::HO_Right)
1098             GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1099         else
1100             GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } });
1101 
1102         // bottom-left corner
1103         if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Bottom))
1104             GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1105         else
1106             GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { left_uv, bottom_uv }, { internal_left_uv, bottom_uv }, { internal_left_uv, internal_bottom_uv }, { left_uv, internal_bottom_uv } });
1107 
1108         // bottom edge
1109         if (m_layout.vertical_orientation == Layout::VO_Bottom)
1110             GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1111         else
1112             GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } });
1113 
1114         // bottom-right corner
1115         if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Bottom))
1116             GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
1117         else
1118             GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } });
1119     }
1120 }
1121 
render_horizontal(const GLCanvas3D & parent) const1122 void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
1123 {
1124     unsigned int tex_id = m_icons_texture.get_id();
1125     int tex_width = m_icons_texture.get_width();
1126     int tex_height = m_icons_texture.get_height();
1127 
1128     float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
1129     float factor = inv_zoom * m_layout.scale;
1130 
1131     float scaled_icons_size = m_layout.icons_size * factor;
1132     float scaled_separator_size = m_layout.separator_size * factor;
1133     float scaled_gap_size = m_layout.gap_size * factor;
1134     float scaled_border = m_layout.border * factor;
1135     float scaled_width = get_width() * inv_zoom;
1136     float scaled_height = get_height() * inv_zoom;
1137 
1138     float separator_stride = scaled_separator_size + scaled_gap_size;
1139     float icon_stride = scaled_icons_size + scaled_gap_size;
1140 
1141     float left = m_layout.left;
1142     float top = m_layout.top;
1143     float right = left + scaled_width;
1144     float bottom = top - scaled_height;
1145 
1146     render_background(left, top, right, bottom, scaled_border);
1147 
1148     left += scaled_border;
1149     top -= scaled_border;
1150 
1151     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
1152         return;
1153 
1154     // renders icons
1155     for (const GLToolbarItem* item : m_items)
1156     {
1157         if (!item->is_visible())
1158             continue;
1159 
1160         if (item->is_separator())
1161             left += separator_stride;
1162         else
1163         {
1164             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
1165             left += icon_stride;
1166         }
1167     }
1168 }
1169 
render_vertical(const GLCanvas3D & parent) const1170 void GLToolbar::render_vertical(const GLCanvas3D& parent) const
1171 {
1172     unsigned int tex_id = m_icons_texture.get_id();
1173     int tex_width = m_icons_texture.get_width();
1174     int tex_height = m_icons_texture.get_height();
1175 
1176     float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
1177     float factor = inv_zoom * m_layout.scale;
1178 
1179     float scaled_icons_size = m_layout.icons_size * factor;
1180     float scaled_separator_size = m_layout.separator_size * factor;
1181     float scaled_gap_size = m_layout.gap_size * factor;
1182     float scaled_border = m_layout.border * factor;
1183     float scaled_width = get_width() * inv_zoom;
1184     float scaled_height = get_height() * inv_zoom;
1185 
1186     float separator_stride = scaled_separator_size + scaled_gap_size;
1187     float icon_stride = scaled_icons_size + scaled_gap_size;
1188 
1189     float left = m_layout.left;
1190     float top = m_layout.top;
1191     float right = left + scaled_width;
1192     float bottom = top - scaled_height;
1193 
1194     render_background(left, top, right, bottom, scaled_border);
1195 
1196     left += scaled_border;
1197     top -= scaled_border;
1198 
1199     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
1200         return;
1201 
1202     // renders icons
1203     for (const GLToolbarItem* item : m_items)
1204     {
1205         if (!item->is_visible())
1206             continue;
1207 
1208         if (item->is_separator())
1209             top -= separator_stride;
1210         else
1211         {
1212             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
1213             top -= icon_stride;
1214         }
1215     }
1216 }
1217 
generate_icons_texture() const1218 bool GLToolbar::generate_icons_texture() const
1219 {
1220     std::string path = resources_dir() + "/icons/";
1221     std::vector<std::string> filenames;
1222     for (GLToolbarItem* item : m_items)
1223     {
1224         const std::string& icon_filename = item->get_icon_filename();
1225         if (!icon_filename.empty())
1226             filenames.push_back(path + icon_filename);
1227     }
1228 
1229     std::vector<std::pair<int, bool>> states;
1230     if (m_type == Normal)
1231     {
1232         states.push_back({ 1, false }); // Normal
1233         states.push_back({ 0, false }); // Pressed
1234         states.push_back({ 2, false }); // Disabled
1235         states.push_back({ 0, false }); // Hover
1236         states.push_back({ 0, false }); // HoverPressed
1237         states.push_back({ 2, false }); // HoverDisabled
1238     }
1239     else
1240     {
1241         states.push_back({ 1, false }); // Normal
1242         states.push_back({ 1, true });  // Pressed
1243         states.push_back({ 1, false }); // Disabled
1244         states.push_back({ 0, false }); // Hover
1245         states.push_back({ 1, true });  // HoverPressed
1246         states.push_back({ 1, false }); // HoverDisabled
1247     }
1248 
1249     unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale);
1250 //    // force even size
1251 //    if (sprite_size_px % 2 != 0)
1252 //        sprite_size_px += 1;
1253 
1254     bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false);
1255     if (res)
1256         m_icons_texture_dirty = false;
1257 
1258     return res;
1259 }
1260 
update_items_visibility()1261 bool GLToolbar::update_items_visibility()
1262 {
1263     bool ret = false;
1264 
1265     for (GLToolbarItem* item : m_items)
1266     {
1267         ret |= item->update_visibility();
1268     }
1269 
1270     if (ret)
1271         m_layout.dirty = true;
1272 
1273     // updates separators visibility to avoid having two of them consecutive
1274     bool any_item_visible = false;
1275     for (GLToolbarItem* item : m_items)
1276     {
1277         if (!item->is_separator())
1278             any_item_visible |= item->is_visible();
1279         else
1280         {
1281             item->set_visible(any_item_visible);
1282             any_item_visible = false;
1283         }
1284     }
1285 
1286     return ret;
1287 }
1288 
update_items_enabled_state()1289 bool GLToolbar::update_items_enabled_state()
1290 {
1291     bool ret = false;
1292 
1293     for (int i = 0; i < (int)m_items.size(); ++i)
1294     {
1295         GLToolbarItem* item = m_items[i];
1296         ret |= item->update_enabled_state();
1297         if (item->is_enabled() && (m_pressed_toggable_id != -1) && (m_pressed_toggable_id != i))
1298         {
1299             ret = true;
1300             item->set_state(GLToolbarItem::Disabled);
1301         }
1302     }
1303 
1304     if (ret)
1305         m_layout.dirty = true;
1306 
1307     return ret;
1308 }
1309 
1310 } // namespace GUI
1311 } // namespace Slic3r
1312