diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4a56ce632..9fdc320f3 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -178,6 +178,9 @@ sub new { Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate); Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info); Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); +#====================================================================================================================================================== + Slic3r::GUI::_3DScene::enable_toolbar($self->{canvas3D}, 1); +#====================================================================================================================================================== Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); @@ -627,6 +630,9 @@ sub on_layer_editing_toggled { $self->{"btn_layer_editing"}->Disable; $self->{"btn_layer_editing"}->SetValue(0); } +#=================================================================================================================================================== + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0); +#=================================================================================================================================================== } $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; @@ -1907,6 +1913,9 @@ sub on_config_change { $self->{"btn_layer_editing"}->Disable; $self->{"btn_layer_editing"}->SetValue(0); } +#=================================================================================================================================================== + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0); +#=================================================================================================================================================== Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, 0); $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; @@ -1917,6 +1926,9 @@ sub on_config_change { } else { $self->{"btn_layer_editing"}->Enable; } +#=================================================================================================================================================== + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 1); +#=================================================================================================================================================== } } elsif ($opt_key eq 'extruder_colour') { $update_scheduled = 1; @@ -2098,6 +2110,13 @@ sub object_list_changed { $self->{"btn_layer_editing"}->Disable if (! $variable_layer_height_allowed); } +#=================================================================================================================================================== + for my $toolbar_item (qw(deleteall arrange layersediting)) { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, $toolbar_item, $have_objects); + } + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0) if (! $variable_layer_height_allowed); +#=================================================================================================================================================== + my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file}; my $model_fits = $self->{canvas3D} ? Slic3r::GUI::_3DScene::check_volumes_outside_state($self->{canvas3D}, $self->{config}) : 1; my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable'; @@ -2123,6 +2142,12 @@ sub selection_changed { for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw changescale split cut settings); } +#=================================================================================================================================================== + for my $toolbar_item (qw(delete more fewer ccw45 cw45 scale split cut settings)) { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, $toolbar_item, $have_sel); + } +#=================================================================================================================================================== + if ($self->{object_info_size}) { # have we already loaded the info pane? if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 976943d64..972a1b094 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -195,9 +195,11 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.hpp ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.cpp ${LIBDIR}/slic3r/GUI/GLGizmo.hpp - ${LIBDIR}/slic3r/GUI/GLGizmo.cpp + ${LIBDIR}/slic3r/GUI/GLGizmo.cpp ${LIBDIR}/slic3r/GUI/GLTexture.hpp - ${LIBDIR}/slic3r/GUI/GLTexture.cpp + ${LIBDIR}/slic3r/GUI/GLTexture.cpp + ${LIBDIR}/slic3r/GUI/GLToolbar.hpp + ${LIBDIR}/slic3r/GUI/GLToolbar.cpp ${LIBDIR}/slic3r/GUI/Preferences.cpp ${LIBDIR}/slic3r/GUI/Preferences.hpp ${LIBDIR}/slic3r/GUI/Preset.cpp diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 2ffd788eb..29ea5fb9c 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1776,6 +1776,13 @@ void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_gizmos(canvas, enable); } +//################################################################################################################################### +void _3DScene::enable_toolbar(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_toolbar(canvas, enable); +} +//################################################################################################################################### + void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_shader(canvas, enable); @@ -1791,6 +1798,13 @@ void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) s_canvas_mgr.allow_multisample(canvas, allow); } +//################################################################################################################################### +void _3DScene::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) +{ + s_canvas_mgr.enable_toolbar_item(canvas, name, enable); +} +//################################################################################################################################### + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index a2ca1de7c..b7ef9a0c8 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -495,10 +495,17 @@ public: static void enable_picking(wxGLCanvas* canvas, bool enable); static void enable_moving(wxGLCanvas* canvas, bool enable); static void enable_gizmos(wxGLCanvas* canvas, bool enable); +//################################################################################################################################### + static void enable_toolbar(wxGLCanvas* canvas, bool enable); +//################################################################################################################################### static void enable_shader(wxGLCanvas* canvas, bool enable); static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); static void allow_multisample(wxGLCanvas* canvas, bool allow); +//################################################################################################################################### + static void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable); +//################################################################################################################################### + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 064a9adce..eee601520 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1803,6 +1803,11 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (m_gizmos.is_enabled() && !m_gizmos.init()) return false; +//################################################################################################################################### + if (!_init_toolbar()) + return false; +//################################################################################################################################### + m_initialized = true; return true; @@ -2053,6 +2058,13 @@ void GLCanvas3D::enable_gizmos(bool enable) m_gizmos.set_enabled(enable); } +//################################################################################################################################### +void GLCanvas3D::enable_toolbar(bool enable) +{ + m_toolbar.set_enabled(enable); +} +//################################################################################################################################### + void GLCanvas3D::enable_shader(bool enable) { m_shader_enabled = enable; @@ -2068,6 +2080,16 @@ void GLCanvas3D::allow_multisample(bool allow) m_multisample_allowed = allow; } +//################################################################################################################################### +void GLCanvas3D::enable_toolbar_item(const std::string& name, bool enable) +{ + if (enable) + m_toolbar.enable_item(name); + else + m_toolbar.disable_item(name); +} +//################################################################################################################################### + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(m_bed.get_bounding_box()); @@ -2197,6 +2219,9 @@ void GLCanvas3D::render() _render_warning_texture(); _render_legend_texture(); _render_gizmo(); +//################################################################################################################################### + _render_toolbar(); +//################################################################################################################################### _render_layer_editing_overlay(); m_canvas->SwapBuffers(); @@ -3422,6 +3447,146 @@ void GLCanvas3D::_force_zoom_to_bed() m_force_zoom_to_bed_enabled = false; } +//################################################################################################################################### +bool GLCanvas3D::_init_toolbar() +{ + if (!m_toolbar.is_enabled()) + return true; + + GLToolbar::ItemCreationData item; + + item.name = "add"; + item.tooltip = GUI::L_str("Add..."); + item.textures[GLToolbarItem::Normal] = "brick_add_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "brick_add_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "brick_add_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "brick_add_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "delete"; + item.tooltip = GUI::L_str("Delete"); + item.textures[GLToolbarItem::Normal] = "brick_delete_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "brick_delete_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "brick_delete_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "brick_delete_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "deleteall"; + item.tooltip = GUI::L_str("Delete all"); + item.textures[GLToolbarItem::Normal] = "cross_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "cross_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "cross_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "cross_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "arrange"; + item.tooltip = GUI::L_str("Arrange"); + item.textures[GLToolbarItem::Normal] = "bricks_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "bricks_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "bricks_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "bricks_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + item.name = "more"; + item.tooltip = GUI::L_str("Add instance"); + item.textures[GLToolbarItem::Normal] = "add_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "add_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "add_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "add_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "fewer"; + item.tooltip = GUI::L_str("Remove instance"); + item.textures[GLToolbarItem::Normal] = "delete_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "delete_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "delete_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "delete_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + item.name = "ccw45"; + item.tooltip = GUI::L_str("Rotate CCW 45°"); + item.textures[GLToolbarItem::Normal] = "arrow_rotate_anticlockwise_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "arrow_rotate_anticlockwise_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "arrow_rotate_anticlockwise_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "arrow_rotate_anticlockwise_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "cw45"; + item.tooltip = GUI::L_str("Rotate CW 45°"); + item.textures[GLToolbarItem::Normal] = "arrow_rotate_clockwise_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "arrow_rotate_clockwise_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "arrow_rotate_clockwise_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "arrow_rotate_clockwise_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "scale"; + item.tooltip = GUI::L_str("Scale..."); + item.textures[GLToolbarItem::Normal] = "arrow_out_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "arrow_out_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "arrow_out_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "arrow_out_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "split"; + item.tooltip = GUI::L_str("Split"); + item.textures[GLToolbarItem::Normal] = "shape_ungroup_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "shape_ungroup_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "shape_ungroup_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "shape_ungroup_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "cut"; + item.tooltip = GUI::L_str("Cut..."); + item.textures[GLToolbarItem::Normal] = "package_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "package_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "package_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "package_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + item.name = "settings"; + item.tooltip = GUI::L_str("Settings..."); + item.textures[GLToolbarItem::Normal] = "cog_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "cog_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "cog_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "cog_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "layersediting"; + item.tooltip = GUI::L_str("Layers editing"); + item.textures[GLToolbarItem::Normal] = "variable_layer_height_normal_36.png"; + item.textures[GLToolbarItem::Hover] = "variable_layer_height_hover_36.png"; + item.textures[GLToolbarItem::Pressed] = "variable_layer_height_pressed_36.png"; + item.textures[GLToolbarItem::Disabled] = "variable_layer_height_disabled_36.png"; + if (!m_toolbar.add_item(item)) + return false; + + enable_toolbar_item("add", true); + + return true; +} +//################################################################################################################################### + void GLCanvas3D::_resize(unsigned int w, unsigned int h) { if ((m_canvas == nullptr) && (m_context == nullptr)) @@ -3717,6 +3882,10 @@ void GLCanvas3D::_picking_pass() const m_gizmos.update_hover_state(*this, pos); else m_gizmos.reset_all_states(); + +//################################################################################################################################### + m_toolbar.update_hover_state(*this, pos); +//################################################################################################################################### } } @@ -3963,6 +4132,13 @@ void GLCanvas3D::_render_gizmo() const m_gizmos.render(*this, _selected_volumes_bounding_box()); } +//################################################################################################################################### +void GLCanvas3D::_render_toolbar() const +{ + m_toolbar.render(*this, m_mouse.position); +} +//################################################################################################################################### + float GLCanvas3D::_get_layers_editing_cursor_z_relative() const { return m_layers_editing.get_cursor_z_relative(*this); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d18ca0cab..67f6ef32f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -2,7 +2,10 @@ #define slic3r_GLCanvas3D_hpp_ #include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLTexture.hpp" +//################################################################################################################################### +#include "../../slic3r/GUI/GLToolbar.hpp" +//#include "../../slic3r/GUI/GLTexture.hpp" +//################################################################################################################################### class wxTimer; class wxSizeEvent; @@ -432,6 +435,9 @@ private: Shader m_shader; Mouse m_mouse; mutable Gizmos m_gizmos; +//################################################################################################################################### + mutable GLToolbar m_toolbar; +//################################################################################################################################### mutable GLVolumeCollection m_volumes; DynamicPrintConfig* m_config; @@ -537,10 +543,17 @@ public: void enable_picking(bool enable); void enable_moving(bool enable); void enable_gizmos(bool enable); +//################################################################################################################################### + void enable_toolbar(bool enable); +//################################################################################################################################### void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void allow_multisample(bool allow); +//################################################################################################################################### + void enable_toolbar_item(const std::string& name, bool enable); +//################################################################################################################################### + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); @@ -610,6 +623,10 @@ private: bool _is_shown_on_screen() const; void _force_zoom_to_bed(); +//################################################################################################################################### + bool _init_toolbar(); +//################################################################################################################################### + void _resize(unsigned int w, unsigned int h); BoundingBoxf3 _max_bounding_box() const; @@ -635,6 +652,9 @@ private: void _render_layer_editing_overlay() const; void _render_volumes(bool fake_colors) const; void _render_gizmo() const; +//################################################################################################################################### + void _render_toolbar() const; +//################################################################################################################################### float _get_layers_editing_cursor_z_relative() const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 23a8f4c15..ef1d7fd19 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -404,6 +404,15 @@ void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable) it->second->enable_gizmos(enable); } +//################################################################################################################################### +void GLCanvas3DManager::enable_toolbar(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_toolbar(enable); +} +//################################################################################################################################### + void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -425,6 +434,15 @@ void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow) it->second->allow_multisample(allow); } +//################################################################################################################################### +void GLCanvas3DManager::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_toolbar_item(name, enable); +} +//################################################################################################################################### + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 98205982f..724c1e4c6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -110,10 +110,17 @@ public: void enable_picking(wxGLCanvas* canvas, bool enable); void enable_moving(wxGLCanvas* canvas, bool enable); void enable_gizmos(wxGLCanvas* canvas, bool enable); +//################################################################################################################################### + void enable_toolbar(wxGLCanvas* canvas, bool enable); +//################################################################################################################################### void enable_shader(wxGLCanvas* canvas, bool enable); void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); +//################################################################################################################################### + void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable); +//################################################################################################################################### + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLToolbar.cpp b/xs/src/slic3r/GUI/GLToolbar.cpp new file mode 100644 index 000000000..70f7ef79c --- /dev/null +++ b/xs/src/slic3r/GUI/GLToolbar.cpp @@ -0,0 +1,388 @@ +#include "GLToolbar.hpp" + +#include "../../libslic3r/Utils.hpp" +#include "../../slic3r/GUI/GLCanvas3D.hpp" + +#include + +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +const unsigned char GLToolbarItem::TooltipTexture::Border_Color[3] = { 0, 0, 0 }; +const int GLToolbarItem::TooltipTexture::Border_Offset = 5; + +bool GLToolbarItem::TooltipTexture::generate(const std::string& text) +{ + reset(); + + if (text.empty()) + return false; + + wxMemoryDC memDC; + // select default font + memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + + // calculates texture size + wxCoord w, h; + memDC.GetTextExtent(text, &w, &h); + m_width = (int)w + 2 * Border_Offset; + m_height = (int)h + 2 * Border_Offset; + + // generates bitmap + wxBitmap bitmap(m_width, m_height); + + memDC.SelectObject(bitmap); + memDC.SetBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK)); + memDC.Clear(); + + // draw message + memDC.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT)); + memDC.DrawText(text, (wxCoord)Border_Offset, (wxCoord)Border_Offset); + + wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVEBORDER)); + memDC.SetPen(pen); + wxCoord ww = (wxCoord)m_width - 1; + wxCoord hh = (wxCoord)m_height - 1; + memDC.DrawLine(0, 0, ww, 0); + memDC.DrawLine(ww, 0, ww, hh); + memDC.DrawLine(ww, hh, 0, hh); + memDC.DrawLine(0, hh, 0, 0); + + memDC.SelectObject(wxNullBitmap); + + // Convert the bitmap into a linear data ready to be loaded into the GPU. + wxImage image = bitmap.ConvertToImage(); + + // prepare buffer + std::vector data(4 * m_width * m_height, 0); + for (int h = 0; h < m_height; ++h) + { + int hh = h * m_width; + unsigned char* px_ptr = data.data() + 4 * hh; + for (int w = 0; w < m_width; ++w) + { + *px_ptr++ = image.GetRed(w, h); + *px_ptr++ = image.GetGreen(w, h); + *px_ptr++ = image.GetBlue(w, h); + *px_ptr++ = 255; + } + } + + // sends buffer to gpu + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +GLToolbarItem::GLToolbarItem(EType type, const std::string& name, const std::string& tooltip) + : m_type(type) + , m_state(Disabled) + , m_name(name) + , m_tooltip(tooltip) + , m_tooltip_shown(false) +{ +} + +bool GLToolbarItem::load_textures(const std::string* filenames) +{ + if (filenames == nullptr) + return false; + + std::string path = resources_dir() + "/icons/"; + + for (unsigned int i = (unsigned int)Normal; i < (unsigned int)Num_States; ++i) + { + std::string filename = path + filenames[i]; + if (!m_icon_textures[i].load_from_file(filename, false)) + return false; + } + + if ((m_type == Action) && !m_tooltip.empty()) + { + if (!m_tooltip_texture.generate(m_tooltip)) + return false; + } + + return true; +} + +GLToolbarItem::EState GLToolbarItem::get_state() const +{ + return m_state; +} + +void GLToolbarItem::set_state(GLToolbarItem::EState state) +{ + m_state = state; +} + +const std::string& GLToolbarItem::get_name() const +{ + return m_name; +} + +void GLToolbarItem::show_tooltip() +{ + m_tooltip_shown = true; +} + +void GLToolbarItem::hide_tooltip() +{ + m_tooltip_shown = false; +} + +bool GLToolbarItem::is_tooltip_shown() const +{ + return m_tooltip_shown && (m_tooltip_texture.get_id() > 0); +} + +unsigned int GLToolbarItem::get_icon_texture_id() const +{ + return m_icon_textures[m_state].get_id(); +} + +int GLToolbarItem::get_icon_textures_size() const +{ + return m_icon_textures[Normal].get_width(); +} + +unsigned int GLToolbarItem::get_tooltip_texture_id() const +{ + return m_tooltip_texture.get_id(); +} + +int GLToolbarItem::get_tooltip_texture_width() const +{ + return m_tooltip_texture.get_width(); +} + +int GLToolbarItem::get_tooltip_texture_height() const +{ + return m_tooltip_texture.get_height(); +} + +bool GLToolbarItem::is_separator() const +{ + return m_type == Separator; +} + +GLToolbar::GLToolbar() + : m_enabled(false) + , m_textures_scale(1.0f) + , m_offset_y(5.0f) + , m_gap_x(2.0f) + , m_separator_x(5.0f) +{ +} + +bool GLToolbar::is_enabled() const +{ + return m_enabled; +} + +void GLToolbar::set_enabled(bool enable) +{ + m_enabled = true; +} + +void GLToolbar::set_textures_scale(float scale) +{ + m_textures_scale = scale; +} + +void GLToolbar::set_offset_y(float offset) +{ + m_offset_y = offset; +} + +void GLToolbar::set_gap_x(float gap) +{ + m_gap_x = gap; +} + +void GLToolbar::set_separator_x(float separator) +{ + m_separator_x = separator; +} + +bool GLToolbar::add_item(const GLToolbar::ItemCreationData& data) +{ + GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data.name, data.tooltip); + if ((item == nullptr) || !item->load_textures(data.textures)) + return false; + + m_items.push_back(item); + + if (data.name == "add") + item->show_tooltip(); + + return true; +} + +bool GLToolbar::add_separator() +{ + GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Separator, "", ""); + if (item == nullptr) + return false; + + m_items.push_back(item); + return true; +} + +void GLToolbar::enable_item(const std::string& name) +{ + for (GLToolbarItem* item : m_items) + { + if (item->get_name() == name) + { + item->set_state(GLToolbarItem::Normal); + return; + } + } +} + +void GLToolbar::disable_item(const std::string& name) +{ + for (GLToolbarItem* item : m_items) + { + if (item->get_name() == name) + { + item->set_state(GLToolbarItem::Disabled); + return; + } + } +} + +void GLToolbar::update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos) +{ + if (!m_enabled) + return; + + float cnv_w = (float)canvas.get_canvas_size().get_width(); + float width = _get_total_width(); + float left = 0.5f * (cnv_w - width); + float top = m_offset_y; + + for (GLToolbarItem* item : m_items) + { + if (item->is_separator()) + left += (m_separator_x + m_gap_x); + else + { + float tex_size = (float)item->get_icon_textures_size() * m_textures_scale; + float right = left + tex_size; + float bottom = top + tex_size; + + GLToolbarItem::EState state = item->get_state(); + bool inside = (left <= mouse_pos.x) && (mouse_pos.x <= right) && (top <= mouse_pos.y) && (mouse_pos.y <= bottom); + + switch (state) + { + case GLToolbarItem::Normal: + { + if (inside) + item->set_state(GLToolbarItem::Hover); + + break; + } + case GLToolbarItem::Hover: + case GLToolbarItem::Pressed: + { + if (!inside) + item->set_state(GLToolbarItem::Normal); + + break; + } + default: + case GLToolbarItem::Disabled: + { + break; + } + } + left += (tex_size + m_gap_x); + } + } +} + +void GLToolbar::render(const GLCanvas3D& canvas, const Pointf& mouse_pos) const +{ + if (m_items.empty()) + return; + + ::glDisable(GL_DEPTH_TEST); + + ::glPushMatrix(); + ::glLoadIdentity(); + + float cnv_w = (float)canvas.get_canvas_size().get_width(); + float cnv_h = (float)canvas.get_canvas_size().get_height(); + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float width = _get_total_width(); + float top_x = -0.5f * width * inv_zoom; + float top_y = (0.5f * cnv_h - m_offset_y * m_textures_scale) * inv_zoom; + float scaled_gap_x = m_gap_x * inv_zoom; + float scaled_separator_x = m_separator_x * inv_zoom; + + // renders icons + for (const GLToolbarItem* item : m_items) + { + if (item->is_separator()) + top_x += (scaled_separator_x + scaled_gap_x); + else + { + float tex_size = (float)item->get_icon_textures_size() * m_textures_scale * inv_zoom; + GLTexture::render_texture(item->get_icon_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); + top_x += (tex_size + scaled_gap_x); + } + } + + // renders tooltip + for (const GLToolbarItem* item : m_items) + { + if (!item->is_separator() && item->is_tooltip_shown()) + { + float l = (-0.5f * cnv_w + (float)mouse_pos.x) * inv_zoom; + float r = l + (float)item->get_tooltip_texture_width() * inv_zoom; + float t = (0.5f * cnv_h - (float)mouse_pos.y) * inv_zoom; + float b = t - (float)item->get_tooltip_texture_height() * inv_zoom; + GLTexture::render_texture(item->get_tooltip_texture_id(), l, r, b, t); + break; + } + } + + ::glPopMatrix(); +} + +float GLToolbar::_get_total_width() const +{ + float width = 0.0f; + + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { + if (m_items[i]->is_separator()) + width += m_separator_x; + else + width += m_items[i]->get_icon_textures_size(); + + if (i < (unsigned int)m_items.size() - 1) + width += m_gap_x; + } + + return width; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLToolbar.hpp b/xs/src/slic3r/GUI/GLToolbar.hpp new file mode 100644 index 000000000..09429677c --- /dev/null +++ b/xs/src/slic3r/GUI/GLToolbar.hpp @@ -0,0 +1,132 @@ +#ifndef slic3r_GLToolbar_hpp_ +#define slic3r_GLToolbar_hpp_ + +#include "../../slic3r/GUI/GLTexture.hpp" + +#include +#include + +namespace Slic3r { + +class Pointf; + +namespace GUI { + +class GLCanvas3D; + +class GLToolbarItem +{ +public: + enum EType : unsigned char + { + Action, + Separator, + Num_Types + }; + + enum EState : unsigned char + { + Normal, + Hover, + Pressed, + Disabled, + Num_States + }; + + class TooltipTexture : public GLTexture + { + static const unsigned char Border_Color[3]; + static const int Border_Offset; + + public: + bool generate(const std::string& text); + }; + +private: + // icon textures are assumed to be square and all with the same size in pixels, no internal check is done + GLTexture m_icon_textures[Num_States]; + TooltipTexture m_tooltip_texture; + + EType m_type; + EState m_state; + + std::string m_name; + std::string m_tooltip; + + bool m_tooltip_shown; + +public: + GLToolbarItem(EType type, const std::string& name, const std::string& tooltip); + + bool load_textures(const std::string* filenames); + + EState get_state() const; + void set_state(EState state); + + const std::string& get_name() const; + + void show_tooltip(); + void hide_tooltip(); + + bool is_tooltip_shown() const; + + unsigned int get_icon_texture_id() const; + int get_icon_textures_size() const; + + unsigned int get_tooltip_texture_id() const; + int get_tooltip_texture_width() const; + int get_tooltip_texture_height() const; + + bool is_separator() const; +}; + +class GLToolbar +{ +public: + struct ItemCreationData + { + std::string name; + std::string tooltip; + std::string textures[GLToolbarItem::Num_States]; + }; + +private: + typedef std::vector ItemsList; + + bool m_enabled; + ItemsList m_items; + + float m_textures_scale; + float m_offset_y; + float m_gap_x; + float m_separator_x; + +public: + GLToolbar(); + + bool is_enabled() const; + void set_enabled(bool enable); + + void set_textures_scale(float scale); + void set_offset_y(float offset); + void set_gap_x(float gap); + void set_separator_x(float separator); + + bool add_item(const ItemCreationData& data); + bool add_separator(); + + void enable_item(const std::string& name); + void disable_item(const std::string& name); + + void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); + + void render(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; + +private: + float _get_total_width() const; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLToolbar_hpp_ diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 38c85c328..0caaac1d6 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -423,6 +423,13 @@ enable_shader(canvas, enable) CODE: _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_toolbar(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_toolbar((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void enable_force_zoom_to_bed(canvas, enable) SV *canvas; @@ -437,6 +444,14 @@ allow_multisample(canvas, allow) CODE: _3DScene::allow_multisample((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), allow); +void +enable_toolbar_item(canvas, item, enable) + SV *canvas; + std::string item; + bool enable; + CODE: + _3DScene::enable_toolbar_item((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), item, enable); + void zoom_to_bed(canvas) SV *canvas;