mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-20 13:17:54 -06:00
Modifiers Gallery (#6703)
* Added GalleryDialog * GalleryDialog improvements: * Added DnD functionality * Added "Delete custom shapes" function
This commit is contained in:
parent
569b7d7dab
commit
d6fdf2d5c2
23 changed files with 528 additions and 7 deletions
BIN
resources/gallery/system/OTHER_recycling_symbol.png
Normal file
BIN
resources/gallery/system/OTHER_recycling_symbol.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
resources/gallery/system/OTHER_recycling_symbol.stl
Normal file
BIN
resources/gallery/system/OTHER_recycling_symbol.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/PETG_recycling_symbol.png
Normal file
BIN
resources/gallery/system/PETG_recycling_symbol.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
resources/gallery/system/PETG_recycling_symbol.stl
Normal file
BIN
resources/gallery/system/PETG_recycling_symbol.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/box.png
Normal file
BIN
resources/gallery/system/box.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
BIN
resources/gallery/system/box.stl
Normal file
BIN
resources/gallery/system/box.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/bunny.png
Normal file
BIN
resources/gallery/system/bunny.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
BIN
resources/gallery/system/bunny.stl
Normal file
BIN
resources/gallery/system/bunny.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/cylinder.stl
Normal file
BIN
resources/gallery/system/cylinder.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/pepa_razitko4_scaled.stl
Normal file
BIN
resources/gallery/system/pepa_razitko4_scaled.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/pyramid.png
Normal file
BIN
resources/gallery/system/pyramid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
resources/gallery/system/pyramid.stl
Normal file
BIN
resources/gallery/system/pyramid.stl
Normal file
Binary file not shown.
BIN
resources/gallery/system/sphere.png
Normal file
BIN
resources/gallery/system/sphere.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
BIN
resources/gallery/system/sphere.stl
Normal file
BIN
resources/gallery/system/sphere.stl
Normal file
Binary file not shown.
|
@ -661,6 +661,7 @@ bool CLI::setup(int argc, char **argv)
|
||||||
set_resources_dir(path_resources.string());
|
set_resources_dir(path_resources.string());
|
||||||
set_var_dir((path_resources / "icons").string());
|
set_var_dir((path_resources / "icons").string());
|
||||||
set_local_dir((path_resources / "localization").string());
|
set_local_dir((path_resources / "localization").string());
|
||||||
|
set_gallery_dir((path_resources / "gallery").string());
|
||||||
|
|
||||||
// Parse all command line options into a DynamicConfig.
|
// Parse all command line options into a DynamicConfig.
|
||||||
// If any option is unsupported, print usage and abort immediately.
|
// If any option is unsupported, print usage and abort immediately.
|
||||||
|
|
|
@ -45,6 +45,11 @@ void set_local_dir(const std::string &path);
|
||||||
// Return a full path to the localization directory.
|
// Return a full path to the localization directory.
|
||||||
const std::string& localization_dir();
|
const std::string& localization_dir();
|
||||||
|
|
||||||
|
// Set a path with shapes gallery files.
|
||||||
|
void set_gallery_dir(const std::string &path);
|
||||||
|
// Return a full path to the gallery directory.
|
||||||
|
const std::string& gallery_dir();
|
||||||
|
|
||||||
// Set a path with preset files.
|
// Set a path with preset files.
|
||||||
void set_data_dir(const std::string &path);
|
void set_data_dir(const std::string &path);
|
||||||
// Return a full path to the GUI resource files.
|
// Return a full path to the GUI resource files.
|
||||||
|
@ -91,6 +96,9 @@ extern bool is_plain_file(const boost::filesystem::directory_entry &path);
|
||||||
extern bool is_ini_file(const boost::filesystem::directory_entry &path);
|
extern bool is_ini_file(const boost::filesystem::directory_entry &path);
|
||||||
extern bool is_idx_file(const boost::filesystem::directory_entry &path);
|
extern bool is_idx_file(const boost::filesystem::directory_entry &path);
|
||||||
extern bool is_gcode_file(const std::string &path);
|
extern bool is_gcode_file(const std::string &path);
|
||||||
|
extern bool is_img_file(const std::string& path);
|
||||||
|
extern bool is_stl_file(const boost::filesystem::directory_entry& path);
|
||||||
|
extern bool is_stl_file(const std::string& path);
|
||||||
|
|
||||||
// File path / name / extension splitting utilities, working with UTF-8,
|
// File path / name / extension splitting utilities, working with UTF-8,
|
||||||
// to be published to Perl.
|
// to be published to Perl.
|
||||||
|
|
|
@ -175,6 +175,18 @@ const std::string& localization_dir()
|
||||||
return g_local_dir;
|
return g_local_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string g_gallery_dir;
|
||||||
|
|
||||||
|
void set_gallery_dir(const std::string &dir)
|
||||||
|
{
|
||||||
|
g_gallery_dir = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& gallery_dir()
|
||||||
|
{
|
||||||
|
return g_gallery_dir;
|
||||||
|
}
|
||||||
|
|
||||||
// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
|
// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
|
||||||
Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
|
Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
|
||||||
|
|
||||||
|
@ -744,6 +756,21 @@ bool is_gcode_file(const std::string &path)
|
||||||
boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc");
|
boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_img_file(const std::string &path)
|
||||||
|
{
|
||||||
|
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_stl_file(const boost::filesystem::directory_entry& dir_entry)
|
||||||
|
{
|
||||||
|
return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".stl") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_stl_file(const std::string &path)
|
||||||
|
{
|
||||||
|
return boost::iends_with(path, ".stl");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
|
@ -101,6 +101,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/GUI_Factories.hpp
|
GUI/GUI_Factories.hpp
|
||||||
GUI/GUI_ObjectList.cpp
|
GUI/GUI_ObjectList.cpp
|
||||||
GUI/GUI_ObjectList.hpp
|
GUI/GUI_ObjectList.hpp
|
||||||
|
GUI/GalleryDialog.cpp
|
||||||
|
GUI/GalleryDialog.hpp
|
||||||
GUI/GUI_ObjectManipulation.cpp
|
GUI/GUI_ObjectManipulation.cpp
|
||||||
GUI/GUI_ObjectManipulation.hpp
|
GUI/GUI_ObjectManipulation.hpp
|
||||||
GUI/GUI_ObjectSettings.cpp
|
GUI/GUI_ObjectSettings.cpp
|
||||||
|
|
|
@ -433,6 +433,12 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty
|
||||||
[type, item](wxCommandEvent&) { obj_list()->load_generic_subobject(item, type); }, "", menu);
|
[type, item](wxCommandEvent&) { obj_list()->load_generic_subobject(item, type); }, "", menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) {
|
||||||
|
sub_menu->AppendSeparator();
|
||||||
|
append_menu_item(sub_menu, wxID_ANY, _L("Gallery"), "",
|
||||||
|
[type](wxCommandEvent&) { obj_list()->load_subobject(type, true); }, "", menu);
|
||||||
|
}
|
||||||
|
|
||||||
return sub_menu;
|
return sub_menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
#include "BitmapComboBox.hpp"
|
#include "BitmapComboBox.hpp"
|
||||||
|
#include "GalleryDialog.hpp"
|
||||||
#if ENABLE_PROJECT_DIRTY_STATE
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
@ -1337,7 +1338,7 @@ bool ObjectList::is_instance_or_object_selected()
|
||||||
return selection.is_single_full_instance() || selection.is_single_full_object();
|
return selection.is_single_full_instance() || selection.is_single_full_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::load_subobject(ModelVolumeType type)
|
void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false*/)
|
||||||
{
|
{
|
||||||
wxDataViewItem item = GetSelection();
|
wxDataViewItem item = GetSelection();
|
||||||
// we can add volumes for Object or Instance
|
// we can add volumes for Object or Instance
|
||||||
|
@ -1351,10 +1352,14 @@ void ObjectList::load_subobject(ModelVolumeType type)
|
||||||
if (m_objects_model->GetItemType(item)&itInstance)
|
if (m_objects_model->GetItemType(item)&itInstance)
|
||||||
item = m_objects_model->GetItemById(obj_idx);
|
item = m_objects_model->GetItemById(obj_idx);
|
||||||
|
|
||||||
|
std::vector<ModelVolume*> volumes;
|
||||||
|
load_part((*m_objects)[obj_idx], volumes, type, from_galery);
|
||||||
|
|
||||||
|
if (volumes.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
take_snapshot(_L("Load Part"));
|
take_snapshot(_L("Load Part"));
|
||||||
|
|
||||||
std::vector<ModelVolume*> volumes;
|
|
||||||
load_part((*m_objects)[obj_idx], volumes, type);
|
|
||||||
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) {
|
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) {
|
||||||
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); });
|
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); });
|
||||||
|
|
||||||
|
@ -1371,11 +1376,21 @@ void ObjectList::load_subobject(ModelVolumeType type)
|
||||||
selection_changed();
|
selection_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::load_part(ModelObject* model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type)
|
void ObjectList::load_part(ModelObject* model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery/* = false*/)
|
||||||
{
|
{
|
||||||
wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
|
wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
|
||||||
|
|
||||||
wxArrayString input_files;
|
wxArrayString input_files;
|
||||||
|
|
||||||
|
if (from_galery) {
|
||||||
|
GalleryDialog dlg(this);
|
||||||
|
if (dlg.ShowModal() == wxID_CANCEL)
|
||||||
|
return;
|
||||||
|
dlg.get_input_files(input_files);
|
||||||
|
if (input_files.IsEmpty())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
wxGetApp().import_model(parent, input_files);
|
wxGetApp().import_model(parent, input_files);
|
||||||
|
|
||||||
wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE);
|
wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE);
|
||||||
|
|
|
@ -238,8 +238,8 @@ public:
|
||||||
void show_settings(const wxDataViewItem settings_item);
|
void show_settings(const wxDataViewItem settings_item);
|
||||||
bool is_instance_or_object_selected();
|
bool is_instance_or_object_selected();
|
||||||
|
|
||||||
void load_subobject(ModelVolumeType type);
|
void load_subobject(ModelVolumeType type, bool from_galery = false);
|
||||||
void load_part(ModelObject* model_object, std::vector<ModelVolume*> &added_volumes, ModelVolumeType type);
|
void load_part(ModelObject* model_object, std::vector<ModelVolume*> &added_volumes, ModelVolumeType type, bool from_galery = false);
|
||||||
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
||||||
void load_shape_object(const std::string &type_name);
|
void load_shape_object(const std::string &type_name);
|
||||||
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
|
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
|
||||||
|
|
404
src/slic3r/GUI/GalleryDialog.cpp
Normal file
404
src/slic3r/GUI/GalleryDialog.cpp
Normal file
|
@ -0,0 +1,404 @@
|
||||||
|
#include "GalleryDialog.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/statbox.h>
|
||||||
|
#include <wx/wupdlock.h>
|
||||||
|
|
||||||
|
#include "GUI.hpp"
|
||||||
|
#include "GUI_App.hpp"
|
||||||
|
#include "format.hpp"
|
||||||
|
#include "wxExtensions.hpp"
|
||||||
|
#include "I18N.hpp"
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
#include <wx/notebook.h>
|
||||||
|
#include "Notebook.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
#define BORDER_W 10
|
||||||
|
#define IMG_PX_CNT 64
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
// Gallery::DropTarget
|
||||||
|
class GalleryDropTarget : public wxFileDropTarget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GalleryDropTarget(GalleryDialog* gallery_dlg) : gallery_dlg(gallery_dlg) { this->SetDefaultAction(wxDragCopy); }
|
||||||
|
|
||||||
|
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GalleryDialog* gallery_dlg {nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GalleryDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
// hides the system icon
|
||||||
|
this->MSWUpdateDragImageOnLeave();
|
||||||
|
#endif // WIN32
|
||||||
|
return gallery_dlg ? gallery_dlg->load_files(filenames) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GalleryDialog::GalleryDialog(wxWindow* parent) :
|
||||||
|
DPIDialog(parent, wxID_ANY, _L("Shapes Gallery"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
|
#endif
|
||||||
|
SetFont(wxGetApp().normal_font());
|
||||||
|
|
||||||
|
wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Select shape from the gallery") + ":");
|
||||||
|
|
||||||
|
m_list_ctrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(55 * wxGetApp().em_unit(), 35 * wxGetApp().em_unit()),
|
||||||
|
wxLC_ICON | wxLC_NO_HEADER | wxLC_ALIGN_TOP | wxSIMPLE_BORDER);
|
||||||
|
m_list_ctrl->Bind(wxEVT_LIST_ITEM_SELECTED, &GalleryDialog::select, this);
|
||||||
|
m_list_ctrl->Bind(wxEVT_LIST_ITEM_DESELECTED, &GalleryDialog::deselect, this);
|
||||||
|
m_list_ctrl->Bind(wxEVT_LIST_ITEM_ACTIVATED, [this](wxListEvent& event) {
|
||||||
|
m_selected_items.clear();
|
||||||
|
select(event);
|
||||||
|
this->EndModal(wxID_OK);
|
||||||
|
});
|
||||||
|
this->Bind(wxEVT_SIZE, [this](wxSizeEvent& event) {
|
||||||
|
event.Skip();
|
||||||
|
m_list_ctrl->Arrange();
|
||||||
|
});
|
||||||
|
|
||||||
|
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||||
|
|
||||||
|
auto add_btn = [this, buttons]( size_t pos, int& ID, wxString title, wxString tooltip,
|
||||||
|
void (GalleryDialog::* method)(wxEvent&),
|
||||||
|
std::function<bool()> enable_fn = []() {return true; }) {
|
||||||
|
ID = NewControlId();
|
||||||
|
wxButton* btn = new wxButton(this, ID, title);
|
||||||
|
btn->SetToolTip(tooltip);
|
||||||
|
btn->Bind(wxEVT_UPDATE_UI, [enable_fn](wxUpdateUIEvent& evt) { evt.Enable(enable_fn()); });
|
||||||
|
buttons->Insert(pos, btn, 0, wxRIGHT, BORDER_W);
|
||||||
|
this->Bind(wxEVT_BUTTON, method, this, ID);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto enable_del_fn = [this]() {
|
||||||
|
if (m_selected_items.empty())
|
||||||
|
return false;
|
||||||
|
for (const Item& item : m_selected_items)
|
||||||
|
if (item.is_system)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
add_btn(0, ID_BTN_ADD_CUSTOM_SHAPE, _L("Add"), _L("Add one or more custom shapes"), &GalleryDialog::add_custom_shapes);
|
||||||
|
add_btn(1, ID_BTN_DEL_CUSTOM_SHAPE, _L("Delete"), _L("Delete one or more custom shape. You can't delete system shapes"), &GalleryDialog::del_custom_shapes, enable_del_fn);
|
||||||
|
add_btn(2, ID_BTN_REPLACE_CUSTOM_PNG, _L("Replace PNG"), _L("Replace PNG for custom shape. You can't raplace PNG for system shape"),&GalleryDialog::replace_custom_png, [this]() { return (m_selected_items.size() == 1 && !m_selected_items[0].is_system); });
|
||||||
|
buttons->InsertStretchSpacer(3, 2* BORDER_W);
|
||||||
|
|
||||||
|
load_label_icon_list();
|
||||||
|
|
||||||
|
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W);
|
||||||
|
topSizer->Add(m_list_ctrl, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W);
|
||||||
|
topSizer->Add(buttons , 0, wxEXPAND | wxALL, BORDER_W);
|
||||||
|
|
||||||
|
SetSizer(topSizer);
|
||||||
|
topSizer->SetSizeHints(this);
|
||||||
|
|
||||||
|
wxGetApp().UpdateDlgDarkUI(this);
|
||||||
|
this->CenterOnScreen();
|
||||||
|
|
||||||
|
this->SetDropTarget(new GalleryDropTarget(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
GalleryDialog::~GalleryDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||||
|
{
|
||||||
|
const int& em = em_unit();
|
||||||
|
|
||||||
|
msw_buttons_rescale(this, em, { ID_BTN_ADD_CUSTOM_SHAPE, ID_BTN_DEL_CUSTOM_SHAPE, ID_BTN_REPLACE_CUSTOM_PNG, wxID_OK, wxID_CANCEL });
|
||||||
|
|
||||||
|
wxSize size = wxSize(55 * em, 35 * em);
|
||||||
|
m_list_ctrl->SetMinSize(size);
|
||||||
|
m_list_ctrl->SetSize(size);
|
||||||
|
|
||||||
|
Fit();
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_border(wxImage& image)
|
||||||
|
{
|
||||||
|
const wxColour& clr = wxGetApp().get_color_hovered_btn_label();
|
||||||
|
|
||||||
|
auto px_data = (uint8_t*)image.GetData();
|
||||||
|
auto a_data = (uint8_t*)image.GetAlpha();
|
||||||
|
|
||||||
|
int width = image.GetWidth();
|
||||||
|
int height = image.GetHeight();
|
||||||
|
int border_width = 2;
|
||||||
|
|
||||||
|
for (size_t x = 0; x < width; ++x) {
|
||||||
|
for (size_t y = 0; y < height; ++y) {
|
||||||
|
if (x < border_width || y < border_width ||
|
||||||
|
x >= (width - border_width) || y >= (height - border_width)) {
|
||||||
|
const size_t idx = (x + y * width);
|
||||||
|
const size_t idx_rgb = (x + y * width) * 3;
|
||||||
|
px_data[idx_rgb] = clr.Red();
|
||||||
|
px_data[idx_rgb + 1] = clr.Green();
|
||||||
|
px_data[idx_rgb + 2] = clr.Blue();
|
||||||
|
if (a_data)
|
||||||
|
a_data[idx] = 255u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_def_img(wxImageList* img_list, bool is_system, std::string stl_path)
|
||||||
|
{
|
||||||
|
wxBitmap bmp = create_scaled_bitmap("cog", nullptr, IMG_PX_CNT, true);
|
||||||
|
|
||||||
|
if (is_system) {
|
||||||
|
wxImage image = bmp.ConvertToImage();
|
||||||
|
if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) {
|
||||||
|
add_border(image);
|
||||||
|
bmp = wxBitmap(std::move(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img_list->Add(bmp);
|
||||||
|
};
|
||||||
|
|
||||||
|
static fs::path get_dir(bool sys_dir)
|
||||||
|
{
|
||||||
|
return fs::absolute(fs::path(gallery_dir()) / (sys_dir ? "system" : "custom")).make_preferred();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool custom_exists()
|
||||||
|
{
|
||||||
|
return fs::exists(fs::absolute(fs::path(gallery_dir()) / "custom").make_preferred());
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string get_dir_path(bool sys_dir)
|
||||||
|
{
|
||||||
|
fs::path dir = get_dir(sys_dir);
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
return dir.string() + "\\";
|
||||||
|
#else
|
||||||
|
return dir.string() + "/";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string name_from_path(fs::path path)
|
||||||
|
{
|
||||||
|
std::string filename = path.filename().string();
|
||||||
|
filename.erase(filename.size() - 4); // Remove the extention suffix.
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::load_label_icon_list()
|
||||||
|
{
|
||||||
|
// load names from files
|
||||||
|
auto add_files_from_gallery = [](std::vector<Item>& items, bool sys_dir, std::string& dir_path)
|
||||||
|
{
|
||||||
|
fs::path dir = get_dir(sys_dir);
|
||||||
|
dir_path = get_dir_path(sys_dir);
|
||||||
|
|
||||||
|
for (auto& dir_entry : fs::directory_iterator(dir))
|
||||||
|
if (is_stl_file(dir_entry)) {
|
||||||
|
std::string name = name_from_path(dir_entry.path());
|
||||||
|
Item item = Item{ name, sys_dir };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string m_sys_dir_path, m_cust_dir_path;
|
||||||
|
std::vector<Item> list_items;
|
||||||
|
add_files_from_gallery(list_items, true, m_sys_dir_path);
|
||||||
|
if (custom_exists())
|
||||||
|
add_files_from_gallery(list_items, false, m_cust_dir_path);
|
||||||
|
|
||||||
|
// Make an image list containing large icons
|
||||||
|
|
||||||
|
int px_cnt = (int)(em_unit() * IMG_PX_CNT * 0.1f + 0.5f);
|
||||||
|
m_image_list = new wxImageList(px_cnt, px_cnt);
|
||||||
|
|
||||||
|
std::string ext = ".png";
|
||||||
|
|
||||||
|
for (const auto& item : list_items) {
|
||||||
|
std::string img_name = (item.is_system ? m_sys_dir_path : m_cust_dir_path) + item.name + ext;
|
||||||
|
std::string stl_name = (item.is_system ? m_sys_dir_path : m_cust_dir_path) + item.name + ".stl";
|
||||||
|
if (!fs::exists(img_name)) {
|
||||||
|
add_def_img(m_image_list, item.is_system, stl_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxImage image;
|
||||||
|
if (!image.LoadFile(from_u8(img_name), wxBITMAP_TYPE_PNG) ||
|
||||||
|
image.GetWidth() == 0 || image.GetHeight() == 0) {
|
||||||
|
add_def_img(m_image_list, item.is_system, stl_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
image.Rescale(px_cnt, px_cnt, wxIMAGE_QUALITY_BILINEAR);
|
||||||
|
|
||||||
|
if (item.is_system)
|
||||||
|
add_border(image);
|
||||||
|
wxBitmap bmp = wxBitmap(std::move(image));
|
||||||
|
m_image_list->Add(bmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_list_ctrl->SetImageList(m_image_list, wxIMAGE_LIST_NORMAL);
|
||||||
|
|
||||||
|
int img_cnt = m_image_list->GetImageCount();
|
||||||
|
for (int i = 0; i < img_cnt; i++) {
|
||||||
|
m_list_ctrl->InsertItem(i, from_u8(list_items[i].name), i);
|
||||||
|
if (list_items[i].is_system)
|
||||||
|
m_list_ctrl->SetItemFont(i, m_list_ctrl->GetItemFont(i).Bold());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::get_input_files(wxArrayString& input_files)
|
||||||
|
{
|
||||||
|
for (const Item& item : m_selected_items)
|
||||||
|
input_files.Add(from_u8(get_dir_path(item.is_system) + item.name + ".stl"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::add_custom_shapes(wxEvent& event)
|
||||||
|
{
|
||||||
|
wxArrayString input_files;
|
||||||
|
wxFileDialog dialog(this, _L("Choose one or more files (STL):"),
|
||||||
|
from_u8(wxGetApp().app_config->get_last_dir()), "",
|
||||||
|
file_wildcards(FT_STL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||||
|
|
||||||
|
if (dialog.ShowModal() == wxID_OK)
|
||||||
|
dialog.GetPaths(input_files);
|
||||||
|
|
||||||
|
if (input_files.IsEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
load_files(input_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::del_custom_shapes(wxEvent& event)
|
||||||
|
{
|
||||||
|
auto dest_dir = get_dir(false);
|
||||||
|
|
||||||
|
for (const Item& item : m_selected_items) {
|
||||||
|
std::string filename = item.name + ".stl";
|
||||||
|
|
||||||
|
if (!fs::exists(dest_dir / filename))
|
||||||
|
continue;
|
||||||
|
try {
|
||||||
|
fs::remove(dest_dir / filename);
|
||||||
|
}
|
||||||
|
catch (fs::filesystem_error const& e) {
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::replace_custom_png(wxEvent& event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::select(wxListEvent& event)
|
||||||
|
{
|
||||||
|
int idx = event.GetIndex();
|
||||||
|
Item item { into_u8(m_list_ctrl->GetItemText(idx)), m_list_ctrl->GetItemFont(idx).GetWeight() == wxFONTWEIGHT_BOLD };
|
||||||
|
|
||||||
|
m_selected_items.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::deselect(wxListEvent& event)
|
||||||
|
{
|
||||||
|
if (m_list_ctrl->GetSelectedItemCount() == 0) {
|
||||||
|
m_selected_items.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name = into_u8(m_list_ctrl->GetItemText(event.GetIndex()));
|
||||||
|
m_selected_items.erase(std::remove_if(m_selected_items.begin(), m_selected_items.end(), [name](Item item) { return item.name == name; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GalleryDialog::update()
|
||||||
|
{
|
||||||
|
m_selected_items.clear();
|
||||||
|
m_image_list->RemoveAll();
|
||||||
|
m_list_ctrl->ClearAll();
|
||||||
|
load_label_icon_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GalleryDialog::load_files(const wxArrayString& input_files)
|
||||||
|
{
|
||||||
|
auto dest_dir = get_dir(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!fs::exists(dest_dir))
|
||||||
|
if (!fs::create_directory(dest_dir)) {
|
||||||
|
std::cerr << "Unable to create destination directory" << dest_dir.string() << '\n' ;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (fs::filesystem_error const& e) {
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the source directory
|
||||||
|
for (size_t i = 0; i < input_files.size(); ++i) {
|
||||||
|
std::string input_file = into_u8(input_files.Item(i));
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs::path current = fs::path(input_file);
|
||||||
|
if (!fs::exists(dest_dir / current.filename()))
|
||||||
|
fs::copy_file(current, dest_dir / current.filename());
|
||||||
|
else {
|
||||||
|
std::string filename = name_from_path(current);
|
||||||
|
|
||||||
|
int file_idx = 0;
|
||||||
|
for (auto& dir_entry : fs::directory_iterator(dest_dir))
|
||||||
|
if (is_stl_file(dir_entry)) {
|
||||||
|
std::string name = name_from_path(dir_entry.path());
|
||||||
|
if (filename == name) {
|
||||||
|
if (file_idx == 0)
|
||||||
|
file_idx++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.find(filename) != 0 ||
|
||||||
|
name[filename.size()] != ' ' || name[filename.size()+1] != '(' || name[name.size()-1] != ')')
|
||||||
|
continue;
|
||||||
|
std::string idx_str = name.substr(filename.size() + 2, name.size() - filename.size() - 3);
|
||||||
|
if (int cur_idx = atoi(idx_str.c_str()); file_idx <= cur_idx)
|
||||||
|
file_idx = cur_idx+1;
|
||||||
|
}
|
||||||
|
if (file_idx > 0) {
|
||||||
|
filename += " (" + std::to_string(file_idx) + ").stl";
|
||||||
|
fs::copy_file(current, dest_dir / filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (fs::filesystem_error const& e) {
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}} // namespace Slic3r::GUI
|
58
src/slic3r/GUI/GalleryDialog.hpp
Normal file
58
src/slic3r/GUI/GalleryDialog.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef slic3r_GalleryDialog_hpp_
|
||||||
|
#define slic3r_GalleryDialog_hpp_
|
||||||
|
|
||||||
|
#include "GUI_Utils.hpp"
|
||||||
|
|
||||||
|
class wxListCtrl;
|
||||||
|
class wxImageList;
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
// GalleryDialog
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
class GalleryDialog : public DPIDialog
|
||||||
|
{
|
||||||
|
wxListCtrl* m_list_ctrl { nullptr };
|
||||||
|
wxImageList* m_image_list { nullptr };
|
||||||
|
|
||||||
|
struct Item {
|
||||||
|
std::string name;
|
||||||
|
bool is_system;
|
||||||
|
};
|
||||||
|
std::vector<Item> m_selected_items;
|
||||||
|
|
||||||
|
int ID_BTN_ADD_CUSTOM_SHAPE;
|
||||||
|
int ID_BTN_DEL_CUSTOM_SHAPE;
|
||||||
|
int ID_BTN_REPLACE_CUSTOM_PNG;
|
||||||
|
|
||||||
|
void load_label_icon_list();
|
||||||
|
void add_custom_shapes(wxEvent& event);
|
||||||
|
void del_custom_shapes(wxEvent& event);
|
||||||
|
void replace_custom_png(wxEvent& event);
|
||||||
|
void select(wxListEvent& event);
|
||||||
|
void deselect(wxListEvent& event);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
public:
|
||||||
|
GalleryDialog(wxWindow* parent);
|
||||||
|
~GalleryDialog();
|
||||||
|
|
||||||
|
void get_input_files(wxArrayString& input_files);
|
||||||
|
bool load_files(const wxArrayString& input_files);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||||
|
void on_sys_color_changed() override {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif //slic3r_GalleryDialog_hpp_
|
Loading…
Add table
Add a link
Reference in a new issue