Merge branch 'ys_dbl_sl_pure'
17
resources/icons/cog_f.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="machine_x2B_cog">
|
||||
<path fill="#ED6B21" d="M13.77,6.39c-0.13-0.47-0.32-0.92-0.55-1.33l0.43-1.3l-1.41-1.41l-1.3,0.43c-0.42-0.23-0.86-0.42-1.33-0.55
|
||||
L9,1H7L6.39,2.23C5.92,2.36,5.47,2.54,5.06,2.78l-1.3-0.43L2.34,3.76l0.43,1.3C2.54,5.47,2.36,5.92,2.23,6.39L1,7v2l1.23,0.61
|
||||
c0.13,0.47,0.32,0.92,0.55,1.33l-0.43,1.3l1.41,1.41l1.3-0.43c0.42,0.23,0.86,0.42,1.33,0.55L7,15h2l0.61-1.23
|
||||
c0.47-0.13,0.92-0.32,1.33-0.55l1.3,0.43l1.41-1.41l-0.43-1.3c0.23-0.42,0.42-0.86,0.55-1.33L15,9V7L13.77,6.39z M8,13
|
||||
c-2.76,0-5-2.24-5-5s2.24-5,5-5s5,2.24,5,5S10.76,13,8,13z"/>
|
||||
<path fill="#808080" d="M11.3,7.08c-0.07-0.27-0.18-0.52-0.31-0.76l0.25-0.74l-0.81-0.81L9.68,5.01C9.45,4.88,9.19,4.78,8.92,4.7
|
||||
L8.57,4H7.43L7.08,4.7C6.81,4.78,6.55,4.88,6.32,5.01L5.58,4.77L4.77,5.58l0.25,0.74C4.88,6.55,4.78,6.81,4.7,7.08L4,7.43v1.14
|
||||
l0.7,0.35c0.07,0.27,0.18,0.52,0.31,0.76l-0.25,0.74l0.81,0.81l0.74-0.25c0.24,0.13,0.49,0.24,0.76,0.31L7.43,12h1.14l0.35-0.7
|
||||
c0.27-0.07,0.52-0.18,0.76-0.31l0.74,0.25l0.81-0.81l-0.25-0.74c0.13-0.24,0.24-0.49,0.31-0.76L12,8.57V7.43L11.3,7.08z M8,10.86
|
||||
c-1.58,0-2.86-1.28-2.86-2.86S6.42,5.14,8,5.14S10.86,6.42,10.86,8S9.58,10.86,8,10.86z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="edit_x5F_Gcode">
|
||||
<g>
|
||||
<path fill="#808080" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
<path fill="#646464" stroke="#808080" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M7.97,7.47h2.65v2.05c0,1.73-0.82,2.48-2.69,2.48S5.3,11.25,5.3,9.55V6.39c0-1.61,0.73-2.36,2.63-2.36
|
||||
|
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 797 B |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="edit_x5F_Gcode">
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
<path fill="#646464" stroke="#ED6B21" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M7.97,7.47h2.65v2.05c0,1.73-0.82,2.48-2.69,2.48S5.3,11.25,5.3,9.55V6.39c0-1.61,0.73-2.36,2.63-2.36
|
||||
|
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 797 B |
@ -3,7 +3,7 @@
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="error_tick">
|
||||
<path fill="#808080" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
<path fill="#646464" stroke="#808080" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
|
||||
|
||||
<path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-width="2" d="M8 4 L8 9" />
|
||||
|
||||
|
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 629 B |
@ -3,7 +3,7 @@
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="error_tick">
|
||||
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
<path fill="#646464" stroke="#ED6B21" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
|
||||
|
||||
<path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-width="2" d="M8 4 L8 9" />
|
||||
|
||||
|
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 629 B |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="pause_x5F_print">
|
||||
<g>
|
||||
<path fill="#808080" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
<path fill="#646464" stroke="#808080" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M6,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S6.71,4.61,6.71,5v6
|
||||
|
Before Width: | Height: | Size: 833 B After Width: | Height: | Size: 822 B |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="pause_x5F_print">
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
<path fill="#646464" stroke="#ED6B21" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M6,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S6.71,4.61,6.71,5v6
|
||||
|
Before Width: | Height: | Size: 833 B After Width: | Height: | Size: 822 B |
12
resources/icons/undo_f.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="undo">
|
||||
<path fill="none" stroke="#ED6B21" stroke-width="2.5" stroke-linecap="round" stroke-miterlimit="10" d="M3,11
|
||||
c0.91,1.78,2.76,3,4.89,3c3.04,0,5.5-2.46,5.5-5.5c0-3.04-2.46-5.5-5.5-5.5c-0.17,0-0.34,0.01-0.5,0.03"/>
|
||||
|
||||
<polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
7.39,1 7.39,5 4.39,3 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 750 B |
@ -30,7 +30,8 @@ src/slic3r/GUI/ConfigManipulation.cpp
|
||||
src/slic3r/GUI/ConfigSnapshotDialog.cpp
|
||||
src/slic3r/GUI/ConfigWizard.cpp
|
||||
src/slic3r/GUI/DesktopIntegrationDialog.cpp
|
||||
src/slic3r/GUI/DoubleSlider.cpp
|
||||
src/slic3r/GUI/TickCodesManager.cpp
|
||||
src/slic3r/GUI/DoubleSliderForLayers.cpp
|
||||
src/slic3r/GUI/Downloader.cpp
|
||||
src/slic3r/GUI/DownloaderFileGet.cpp
|
||||
src/slic3r/GUI/EditGCodeDialog.cpp
|
||||
|
@ -189,6 +189,25 @@ namespace ImGui
|
||||
const wchar_t InfoMarkerSmall = 0x2716;
|
||||
const wchar_t CollapseBtn = 0x2715;
|
||||
|
||||
// icons for double slider (middle size icons)
|
||||
const wchar_t Lock = 0x2801;
|
||||
const wchar_t LockHovered = 0x2802;
|
||||
const wchar_t Unlock = 0x2803;
|
||||
const wchar_t UnlockHovered = 0x2804;
|
||||
const wchar_t DSRevert = 0x2805;
|
||||
const wchar_t DSRevertHovered = 0x2806;
|
||||
const wchar_t DSSettings = 0x2807;
|
||||
const wchar_t DSSettingsHovered = 0x2808;
|
||||
// icons for double slider (small size icons)
|
||||
const wchar_t ErrorTick = 0x2809;
|
||||
const wchar_t ErrorTickHovered = 0x280A;
|
||||
const wchar_t PausePrint = 0x280B;
|
||||
const wchar_t PausePrintHovered = 0x280C;
|
||||
const wchar_t EditGCode = 0x280D;
|
||||
const wchar_t EditGCodeHovered = 0x280E;
|
||||
const wchar_t RemoveTick = 0x280F;
|
||||
const wchar_t RemoveTickHovered = 0x2810;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,11 @@ class DynamicPrintConfig;
|
||||
|
||||
namespace CustomGCode {
|
||||
|
||||
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
|
||||
* So, let use same value as a permissible error for layer height.
|
||||
*/
|
||||
constexpr double epsilon() { return 0.0011; }
|
||||
|
||||
enum Type
|
||||
{
|
||||
ColorChange,
|
||||
|
@ -252,8 +252,14 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/ProgressStatusBar.cpp
|
||||
GUI/Mouse3DController.cpp
|
||||
GUI/Mouse3DController.hpp
|
||||
GUI/DoubleSlider.cpp
|
||||
GUI/DoubleSlider.hpp
|
||||
GUI/ImGuiDoubleSlider.cpp
|
||||
GUI/ImGuiDoubleSlider.hpp
|
||||
GUI/TickCodesManager.cpp
|
||||
GUI/TickCodesManager.hpp
|
||||
GUI/DoubleSliderForLayers.cpp
|
||||
GUI/DoubleSliderForLayers.hpp
|
||||
GUI/DoubleSliderForGcode.cpp
|
||||
GUI/DoubleSliderForGcode.hpp
|
||||
GUI/Notebook.cpp
|
||||
GUI/Notebook.hpp
|
||||
GUI/TopBar.cpp
|
||||
|
@ -1,484 +0,0 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_DoubleSlider_hpp_
|
||||
#define slic3r_GUI_DoubleSlider_hpp_
|
||||
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <wx/window.h>
|
||||
#include <wx/control.h>
|
||||
#include <wx/dc.h>
|
||||
#include <wx/slider.h>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
class wxMenu;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using namespace CustomGCode;
|
||||
class PrintObject;
|
||||
class Layer;
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
|
||||
* So, let use same value as a permissible error for layer height.
|
||||
*/
|
||||
constexpr double epsilon() { return 0.0011; }
|
||||
|
||||
// return true when areas are mostly equivalent
|
||||
bool equivalent_areas(const double& bottom_area, const double& top_area);
|
||||
|
||||
// return true if color change was detected
|
||||
bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs,
|
||||
// what to do with detected color change
|
||||
// and return true when detection have to be desturbed
|
||||
std::function<bool(const Layer*)> break_condition);
|
||||
|
||||
// custom message the slider sends to its parent to notify a tick-change:
|
||||
wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
|
||||
|
||||
enum SelectedSlider {
|
||||
ssUndef,
|
||||
ssLower,
|
||||
ssHigher
|
||||
};
|
||||
|
||||
enum FocusedItem {
|
||||
fiNone,
|
||||
fiRevertIcon,
|
||||
fiOneLayerIcon,
|
||||
fiCogIcon,
|
||||
fiColorBand,
|
||||
fiActionIcon,
|
||||
fiLowerThumb,
|
||||
fiHigherThumb,
|
||||
fiSmartWipeTower,
|
||||
fiTick
|
||||
};
|
||||
|
||||
enum ConflictType
|
||||
{
|
||||
ctNone,
|
||||
ctModeConflict,
|
||||
ctMeaninglessColorChange,
|
||||
ctMeaninglessToolChange,
|
||||
ctRedundant
|
||||
};
|
||||
|
||||
enum MouseAction
|
||||
{
|
||||
maNone,
|
||||
maAddMenu, // show "Add" context menu for NOTexist active tick
|
||||
maEditMenu, // show "Edit" context menu for exist active tick
|
||||
maCogIconMenu, // show context for "cog" icon
|
||||
maForceColorEdit, // force color editing from colored band
|
||||
maAddTick, // force tick adding
|
||||
maDeleteTick, // force tick deleting
|
||||
maCogIconClick, // LeftMouseClick on "cog" icon
|
||||
maOneLayerIconClick, // LeftMouseClick on "one_layer" icon
|
||||
maRevertIconClick, // LeftMouseClick on "revert" icon
|
||||
};
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
dmRegular,
|
||||
dmSlaPrint,
|
||||
dmSequentialFffPrint,
|
||||
dmSequentialGCodeView,
|
||||
};
|
||||
|
||||
enum LabelType
|
||||
{
|
||||
ltHeightWithLayer,
|
||||
ltHeight,
|
||||
ltEstimatedTime,
|
||||
};
|
||||
|
||||
struct TickCode
|
||||
{
|
||||
bool operator<(const TickCode& other) const { return other.tick > this->tick; }
|
||||
bool operator>(const TickCode& other) const { return other.tick < this->tick; }
|
||||
|
||||
int tick = 0;
|
||||
Type type = ColorChange;
|
||||
int extruder = 0;
|
||||
std::string color;
|
||||
std::string extra;
|
||||
};
|
||||
|
||||
class TickCodeInfo
|
||||
{
|
||||
std::string custom_gcode;
|
||||
std::string pause_print_msg;
|
||||
bool m_suppress_plus = false;
|
||||
bool m_suppress_minus = false;
|
||||
bool m_use_default_colors= false;
|
||||
// int m_default_color_idx = 0;
|
||||
|
||||
std::vector<std::string>* m_colors {nullptr};
|
||||
|
||||
std::string get_color_for_tick(TickCode tick, Type type, const int extruder);
|
||||
|
||||
public:
|
||||
std::set<TickCode> ticks {};
|
||||
Mode mode = Undef;
|
||||
|
||||
bool empty() const { return ticks.empty(); }
|
||||
void set_pause_print_msg(const std::string& message) { pause_print_msg = message; }
|
||||
|
||||
bool add_tick(const int tick, Type type, int extruder, double print_z);
|
||||
bool edit_tick(std::set<TickCode>::iterator it, double print_z);
|
||||
void switch_code(Type type_from, Type type_to);
|
||||
bool switch_code_for_tick(std::set<TickCode>::iterator it, Type type_to, const int extruder);
|
||||
void erase_all_ticks_with_code(Type type);
|
||||
|
||||
bool has_tick_with_code(Type type);
|
||||
bool has_tick(int tick);
|
||||
ConflictType is_conflict_tick(const TickCode& tick, Mode out_mode, int only_extruder, double print_z);
|
||||
|
||||
// Get used extruders for tick.
|
||||
// Means all extruders(tools) which will be used during printing from current tick to the end
|
||||
std::set<int> get_used_extruders_for_tick(int tick, int only_extruder, double print_z, Mode force_mode = Undef) const;
|
||||
|
||||
void suppress_plus (bool suppress) { m_suppress_plus = suppress; }
|
||||
void suppress_minus(bool suppress) { m_suppress_minus = suppress; }
|
||||
bool suppressed_plus () { return m_suppress_plus; }
|
||||
bool suppressed_minus() { return m_suppress_minus; }
|
||||
void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; }
|
||||
|
||||
void set_extruder_colors(std::vector<std::string>* extruder_colors) { m_colors = extruder_colors; }
|
||||
};
|
||||
|
||||
|
||||
struct ExtrudersSequence
|
||||
{
|
||||
bool is_mm_intervals = true;
|
||||
double interval_by_mm = 3.0;
|
||||
int interval_by_layers = 10;
|
||||
bool random_sequence { false };
|
||||
bool color_repetition { false };
|
||||
std::vector<size_t> extruders = { 0 };
|
||||
|
||||
bool operator==(const ExtrudersSequence& other) const
|
||||
{
|
||||
return (other.is_mm_intervals == this->is_mm_intervals ) &&
|
||||
(other.interval_by_mm == this->interval_by_mm ) &&
|
||||
(other.interval_by_layers == this->interval_by_layers ) &&
|
||||
(other.random_sequence == this->random_sequence ) &&
|
||||
(other.color_repetition == this->color_repetition ) &&
|
||||
(other.extruders == this->extruders ) ;
|
||||
}
|
||||
bool operator!=(const ExtrudersSequence& other) const
|
||||
{
|
||||
return (other.is_mm_intervals != this->is_mm_intervals ) ||
|
||||
(other.interval_by_mm != this->interval_by_mm ) ||
|
||||
(other.interval_by_layers != this->interval_by_layers ) ||
|
||||
(other.random_sequence != this->random_sequence ) ||
|
||||
(other.color_repetition != this->color_repetition ) ||
|
||||
(other.extruders != this->extruders ) ;
|
||||
}
|
||||
|
||||
void add_extruder(size_t pos, size_t extruder_id = size_t(0))
|
||||
{
|
||||
extruders.insert(extruders.begin() + pos+1, extruder_id);
|
||||
}
|
||||
|
||||
void delete_extruder(size_t pos)
|
||||
{
|
||||
if (extruders.size() == 1)
|
||||
return;// last item can't be deleted
|
||||
extruders.erase(extruders.begin() + pos);
|
||||
}
|
||||
|
||||
void init(size_t extruders_count)
|
||||
{
|
||||
extruders.clear();
|
||||
for (size_t extruder = 0; extruder < extruders_count; extruder++)
|
||||
extruders.push_back(extruder);
|
||||
}
|
||||
};
|
||||
|
||||
class Control : public wxControl
|
||||
{
|
||||
public:
|
||||
Control(
|
||||
wxWindow *parent,
|
||||
wxWindowID id,
|
||||
int lowerValue,
|
||||
int higherValue,
|
||||
int minValue,
|
||||
int maxValue,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxSL_VERTICAL,
|
||||
const wxValidator& val = wxDefaultValidator,
|
||||
const wxString& name = wxEmptyString);
|
||||
~Control() {}
|
||||
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
|
||||
int GetMinValue() const { return m_min_value; }
|
||||
int GetMaxValue() const { return m_max_value; }
|
||||
double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; }
|
||||
double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; }
|
||||
int GetLowerValue() const { return m_lower_value; }
|
||||
int GetHigherValue() const { return m_higher_value; }
|
||||
int GetActiveValue() const;
|
||||
double GetLowerValueD() { return get_double_value(ssLower); }
|
||||
double GetHigherValueD() { return get_double_value(ssHigher); }
|
||||
wxSize DoGetBestSize() const override;
|
||||
wxSize get_min_size() const ;
|
||||
|
||||
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
|
||||
void SetLowerValue (const int lower_val);
|
||||
void SetHigherValue(const int higher_val);
|
||||
void SetSelectionSpan(const int lower_val, const int higher_val);
|
||||
|
||||
void SetMaxValue(const int max_value);
|
||||
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
|
||||
void SetSliderValues(const std::vector<double>& values);
|
||||
void ChangeOneLayerLock();
|
||||
void SetSliderAlternateValues(const std::vector<double>& values) { m_alternate_values = values; }
|
||||
|
||||
Info GetTicksValues() const;
|
||||
void SetTicksValues(const Info &custom_gcode_per_print_z);
|
||||
void SetLayersTimes(const std::vector<float>& layers_times, float total_time);
|
||||
void SetLayersTimes(const std::vector<double>& layers_times);
|
||||
|
||||
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
|
||||
void SetDrawMode(DrawMode mode) { m_draw_mode = mode; }
|
||||
|
||||
void SetManipulationMode(Mode mode) { m_mode = mode; }
|
||||
Mode GetManipulationMode() const { return m_mode; }
|
||||
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
|
||||
void SetExtruderColors(const std::vector<std::string>& extruder_colors);
|
||||
|
||||
bool IsNewPrint();
|
||||
|
||||
void set_render_as_disabled(bool value) { m_render_as_disabled = value; }
|
||||
bool is_rendering_as_disabled() const { return m_render_as_disabled; }
|
||||
|
||||
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
|
||||
bool is_one_layer() const { return m_is_one_layer; }
|
||||
bool is_lower_at_min() const { return m_lower_value == m_min_value; }
|
||||
bool is_higher_at_max() const { return m_higher_value == m_max_value; }
|
||||
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
|
||||
|
||||
void OnPaint(wxPaintEvent& ) { render(); }
|
||||
void OnLeftDown(wxMouseEvent& event);
|
||||
void OnMotion(wxMouseEvent& event);
|
||||
void OnLeftUp(wxMouseEvent& event);
|
||||
void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); }
|
||||
void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); }
|
||||
void UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); }
|
||||
void OnWheel(wxMouseEvent& event);
|
||||
void OnKeyDown(wxKeyEvent &event);
|
||||
void OnKeyUp(wxKeyEvent &event);
|
||||
void OnChar(wxKeyEvent &event);
|
||||
void OnRightDown(wxMouseEvent& event);
|
||||
void OnRightUp(wxMouseEvent& event);
|
||||
|
||||
void add_code_as_tick(Type type, int selected_extruder = -1);
|
||||
// add default action for tick, when press "+"
|
||||
void add_current_tick(bool call_from_keyboard = false);
|
||||
// delete current tick, when press "-"
|
||||
void delete_current_tick();
|
||||
void edit_tick(int tick = -1);
|
||||
void switch_one_layer_mode();
|
||||
void discard_all_thicks();
|
||||
void move_current_thumb_to_pos(wxPoint pos);
|
||||
void edit_extruder_sequence();
|
||||
void jump_to_value();
|
||||
void enable_action_icon(bool enable) { m_enable_action_icon = enable; }
|
||||
void show_add_context_menu();
|
||||
void show_edit_context_menu();
|
||||
void show_cog_icon_context_menu();
|
||||
void auto_color_change();
|
||||
|
||||
ExtrudersSequence m_extruders_sequence;
|
||||
|
||||
protected:
|
||||
|
||||
void render();
|
||||
void draw_focus_rect(wxDC& dc);
|
||||
void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);
|
||||
void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
|
||||
void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
|
||||
void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
|
||||
void draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len);
|
||||
void draw_ticks(wxDC& dc);
|
||||
void draw_colored_band(wxDC& dc);
|
||||
void draw_ruler(wxDC& dc);
|
||||
void draw_one_layer_icon(wxDC& dc);
|
||||
void draw_revert_icon(wxDC& dc);
|
||||
void draw_cog_icon(wxDC &dc);
|
||||
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
|
||||
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
|
||||
void draw_tick_on_mouse_position(wxDC &dc);
|
||||
void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type = ltHeight, bool right_side = true) const;
|
||||
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
|
||||
|
||||
void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection);
|
||||
bool is_lower_thumb_editable();
|
||||
bool detect_selected_slider(const wxPoint& pt);
|
||||
void correct_lower_value();
|
||||
void correct_higher_value();
|
||||
void move_current_thumb(const bool condition);
|
||||
void enter_window(wxMouseEvent& event, const bool enter);
|
||||
bool is_wipe_tower_layer(int tick) const;
|
||||
|
||||
private:
|
||||
|
||||
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
|
||||
int get_tick_near_point(const wxPoint& pt);
|
||||
|
||||
double get_scroll_step();
|
||||
wxString get_label(int tick, LabelType label_type = ltHeightWithLayer) const;
|
||||
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
|
||||
int get_value_from_position(const wxCoord x, const wxCoord y);
|
||||
int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); }
|
||||
wxCoord get_position_from_value(const int value);
|
||||
wxSize get_size() const;
|
||||
void get_size(int* w, int* h) const;
|
||||
double get_double_value(const SelectedSlider& selection);
|
||||
int get_tick_from_value(double value, bool force_lower_bound = false);
|
||||
wxString get_tooltip(int tick = -1);
|
||||
int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange);
|
||||
|
||||
std::string get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const;
|
||||
std::string get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const;
|
||||
wxRect get_colored_band_rect();
|
||||
|
||||
// Get active extruders for tick.
|
||||
// Means one current extruder for not existing tick OR
|
||||
// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick)
|
||||
// Use those values to disable selection of active extruders
|
||||
std::array<int, 2> get_active_extruders_for_tick(int tick) const;
|
||||
|
||||
void post_ticks_changed_event(Type type = Custom);
|
||||
bool check_ticks_changed_event(Type type);
|
||||
|
||||
void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false);
|
||||
void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false);
|
||||
|
||||
bool is_osx { false };
|
||||
wxFont m_font;
|
||||
int m_min_value;
|
||||
int m_max_value;
|
||||
int m_lower_value;
|
||||
int m_higher_value;
|
||||
|
||||
bool m_render_as_disabled{ false };
|
||||
|
||||
ScalableBitmap m_bmp_thumb_higher;
|
||||
ScalableBitmap m_bmp_thumb_lower;
|
||||
ScalableBitmap m_bmp_add_tick_on;
|
||||
ScalableBitmap m_bmp_add_tick_off;
|
||||
ScalableBitmap m_bmp_del_tick_on;
|
||||
ScalableBitmap m_bmp_del_tick_off;
|
||||
ScalableBitmap m_bmp_one_layer_lock_on;
|
||||
ScalableBitmap m_bmp_one_layer_lock_off;
|
||||
ScalableBitmap m_bmp_one_layer_unlock_on;
|
||||
ScalableBitmap m_bmp_one_layer_unlock_off;
|
||||
ScalableBitmap m_bmp_revert;
|
||||
ScalableBitmap m_bmp_cog;
|
||||
SelectedSlider m_selection;
|
||||
bool m_is_left_down = false;
|
||||
bool m_is_right_down = false;
|
||||
bool m_is_one_layer = false;
|
||||
bool m_is_focused = false;
|
||||
bool m_force_mode_apply = true;
|
||||
bool m_enable_action_icon = true;
|
||||
bool m_is_wipe_tower = false; //This flag indicates that there is multiple extruder print with wipe tower
|
||||
|
||||
DrawMode m_draw_mode = dmRegular;
|
||||
|
||||
Mode m_mode = SingleExtruder;
|
||||
int m_only_extruder = -1;
|
||||
|
||||
MouseAction m_mouse = maNone;
|
||||
FocusedItem m_focus = fiNone;
|
||||
wxPoint m_moving_pos = wxDefaultPosition;
|
||||
|
||||
wxRect m_rect_lower_thumb;
|
||||
wxRect m_rect_higher_thumb;
|
||||
wxRect m_rect_tick_action;
|
||||
wxRect m_rect_one_layer_icon;
|
||||
wxRect m_rect_revert_icon;
|
||||
wxRect m_rect_cog_icon;
|
||||
wxSize m_thumb_size;
|
||||
int m_tick_icon_dim;
|
||||
int m_lock_icon_dim;
|
||||
int m_revert_icon_dim;
|
||||
int m_cog_icon_dim;
|
||||
long m_style;
|
||||
long m_extra_style;
|
||||
float m_label_koef{ 1.0 };
|
||||
|
||||
std::vector<double> m_values;
|
||||
TickCodeInfo m_ticks;
|
||||
std::vector<double> m_layers_times;
|
||||
std::vector<double> m_layers_values;
|
||||
std::vector<std::string> m_extruder_colors;
|
||||
std::string m_print_obj_idxs;
|
||||
|
||||
std::vector<double> m_alternate_values;
|
||||
|
||||
// control's view variables
|
||||
wxCoord SLIDER_MARGIN; // margin around slider
|
||||
|
||||
wxPen DARK_ORANGE_PEN;
|
||||
wxPen ORANGE_PEN;
|
||||
wxPen LIGHT_ORANGE_PEN;
|
||||
|
||||
wxPen DARK_GREY_PEN;
|
||||
wxPen GREY_PEN;
|
||||
wxPen LIGHT_GREY_PEN;
|
||||
|
||||
wxPen FOCUS_RECT_PEN;
|
||||
wxBrush FOCUS_RECT_BRUSH;
|
||||
|
||||
std::vector<wxPen*> m_line_pens;
|
||||
std::vector<wxPen*> m_segm_pens;
|
||||
|
||||
class Ruler {
|
||||
wxWindow* m_parent{nullptr}; // m_parent is nullptr for Unused ruler
|
||||
// in this case we will not init/update/render it
|
||||
// values to check if ruler has to be updated
|
||||
double m_min_val;
|
||||
double m_max_val;
|
||||
double m_scroll_step;
|
||||
size_t m_max_values_cnt;
|
||||
int m_DPI;
|
||||
|
||||
public:
|
||||
|
||||
double long_step;
|
||||
double short_step;
|
||||
std::vector<double> max_values;// max value for each object/instance in sequence print
|
||||
// > 1 for sequential print
|
||||
|
||||
void set_parent(wxWindow* parent);
|
||||
void update_dpi();
|
||||
void init(const std::vector<double>& values, double scroll_step);
|
||||
void update(const std::vector<double>& values, double scroll_step);
|
||||
bool is_ok() { return long_step > 0 && short_step > 0; }
|
||||
size_t count() { return max_values.size(); }
|
||||
bool can_draw() { return m_parent != nullptr; }
|
||||
} m_ruler;
|
||||
};
|
||||
|
||||
} // DoubleSlider;
|
||||
|
||||
} // Slic3r
|
||||
|
||||
|
||||
|
||||
#endif // slic3r_GUI_DoubleSlider_hpp_
|
29
src/slic3r/GUI/DoubleSliderForGcode.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
|
||||
#include "DoubleSliderForGcode.hpp"
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar
|
||||
static const float HORIZONTAL_SLIDER_HEIGHT = 45.0f;
|
||||
|
||||
void DSForGcode::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/, float offset/* = 0.f*/)
|
||||
{
|
||||
if (!m_ctrl.IsShown())
|
||||
return;
|
||||
m_scale = extra_scale * 0.1f * m_em;
|
||||
|
||||
ImVec2 pos = ImVec2{std::max(LEFT_MARGIN, 0.2f * canvas_width), canvas_height - 1.5f * HORIZONTAL_SLIDER_HEIGHT * m_scale};
|
||||
ImVec2 size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_HEIGHT * m_scale);
|
||||
|
||||
m_ctrl.Init(pos, size, m_scale);
|
||||
if (m_ctrl.render())
|
||||
process_thumb_move();
|
||||
}
|
||||
|
||||
} // DoubleSlider
|
||||
|
||||
|
38
src/slic3r/GUI/DoubleSliderForGcode.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_DoubleSliderForGcode_hpp_
|
||||
#define slic3r_GUI_DoubleSliderForGcode_hpp_
|
||||
|
||||
#include "ImGuiDoubleSlider.hpp"
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
class DSForGcode : public Manager<unsigned int>
|
||||
{
|
||||
public:
|
||||
DSForGcode() : Manager<unsigned int>() {}
|
||||
DSForGcode( int lowerPos,
|
||||
int higherPos,
|
||||
int minPos,
|
||||
int maxPos)
|
||||
{
|
||||
Init(lowerPos, higherPos, minPos, maxPos, "moves_slider", true);
|
||||
}
|
||||
~DSForGcode() {}
|
||||
|
||||
void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override;
|
||||
|
||||
void set_render_as_disabled(bool value) { m_render_as_disabled = value; }
|
||||
bool is_rendering_as_disabled() const { return m_render_as_disabled; }
|
||||
|
||||
private:
|
||||
|
||||
bool m_render_as_disabled{ false };
|
||||
};
|
||||
|
||||
} // DoubleSlider;
|
||||
|
||||
|
||||
#endif // slic3r_GUI_DoubleSliderForGcode_hpp_
|
1046
src/slic3r/GUI/DoubleSliderForLayers.cpp
Normal file
203
src/slic3r/GUI/DoubleSliderForLayers.hpp
Normal file
@ -0,0 +1,203 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_DoubleSliderForLayers_hpp_
|
||||
#define slic3r_GUI_DoubleSliderForLayers_hpp_
|
||||
|
||||
#include "ImGuiDoubleSlider.hpp"
|
||||
#include "TickCodesManager.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace Slic3r {
|
||||
class Print;
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
class ImGuiWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Slic3r::CustomGCode;
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
enum FocusedItem {
|
||||
fiNone,
|
||||
fiRevertIcon,
|
||||
fiOneLayerIcon,
|
||||
fiCogIcon,
|
||||
fiColorBand,
|
||||
fiActionIcon,
|
||||
fiLowerThumb,
|
||||
fiHigherThumb,
|
||||
fiSmartWipeTower,
|
||||
fiTick
|
||||
};
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
dmRegular,
|
||||
dmSlaPrint,
|
||||
dmSequentialFffPrint,
|
||||
};
|
||||
|
||||
enum LabelType
|
||||
{
|
||||
ltHeightWithLayer,
|
||||
ltHeight,
|
||||
ltEstimatedTime,
|
||||
};
|
||||
|
||||
class DSForLayers : public Manager<double>
|
||||
{
|
||||
public:
|
||||
DSForLayers() : Manager<double>() {}
|
||||
DSForLayers(int lowerValue,
|
||||
int higherValue,
|
||||
int minValue,
|
||||
int maxValue,
|
||||
bool allow_editing);
|
||||
~DSForLayers() {}
|
||||
|
||||
void ChangeOneLayerLock();
|
||||
|
||||
Info GetTicksValues() const;
|
||||
void SetTicksValues(const Info& custom_gcode_per_print_z);
|
||||
void SetLayersTimes(const std::vector<float>& layers_times, float total_time);
|
||||
void SetLayersTimes(const std::vector<double>& layers_times);
|
||||
|
||||
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
|
||||
|
||||
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
|
||||
|
||||
void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override;
|
||||
|
||||
// jump to selected layer
|
||||
void jump_to_value();
|
||||
|
||||
// just for editor
|
||||
|
||||
void SetExtruderColors(const std::vector<std::string>& extruder_colors);
|
||||
void UseDefaultColors(bool def_colors_on);
|
||||
bool is_new_print(const std::string& print_obj_idxs);
|
||||
void set_imgui_wrapper(Slic3r::GUI::ImGuiWrapper* imgui) { m_imgui = imgui; }
|
||||
|
||||
// manipulation with slider from keyboard
|
||||
|
||||
// add default action for tick, when press "+"
|
||||
void add_current_tick();
|
||||
// delete current tick, when press "-"
|
||||
void delete_current_tick();
|
||||
// process adding of auto color change
|
||||
void auto_color_change();
|
||||
|
||||
void set_callback_on_ticks_changed(std::function<void()> cb)
|
||||
{ m_cb_ticks_changed = cb; };
|
||||
|
||||
void set_callback_on_check_gcode(std::function<void(Type)> cb )
|
||||
{ m_ticks.set_callback_on_check_gcode(cb); }
|
||||
|
||||
void set_callback_on_get_extruder_colors(std::function<std::vector<std::string>()> cb)
|
||||
{ m_cb_get_extruder_colors = cb; }
|
||||
|
||||
void set_callback_on_get_print (std::function<const Slic3r::Print& ()> cb)
|
||||
{ m_cb_get_print = cb; }
|
||||
|
||||
void set_callback_on_empty_auto_color_change(std::function<void()> cb)
|
||||
{ m_ticks.set_callback_on_empty_auto_color_change(cb); }
|
||||
|
||||
void set_callback_on_get_custom_code(std::function<std::string(const std::string&, double)> cb)
|
||||
{ m_ticks.set_callback_on_get_custom_code(cb); }
|
||||
|
||||
void set_callback_on_get_pause_print_msg(std::function<std::string(const std::string&, double)> cb)
|
||||
{ m_ticks.set_callback_on_get_pause_print_msg(cb); }
|
||||
|
||||
void set_callback_on_get_new_color(std::function<std::string(const std::string&)> cb)
|
||||
{ m_ticks.set_callback_on_get_new_color(cb); }
|
||||
|
||||
void set_callback_on_show_info_msg(std::function<int(const std::string&, int)> cb)
|
||||
{ m_ticks.set_callback_on_show_info_msg(cb); }
|
||||
|
||||
void set_callback_on_show_warning_msg(std::function<int(const std::string&, int)> cb)
|
||||
{ m_ticks.set_callback_on_show_warning_msg(cb); }
|
||||
|
||||
void set_callback_on_get_extruders_cnt(std::function<int()> cb)
|
||||
{ m_ticks.set_callback_on_get_extruders_cnt(cb); }
|
||||
|
||||
void set_callback_on_get_extruders_sequence(std::function<bool(ExtrudersSequence&)> cb)
|
||||
{ m_ticks.set_callback_on_get_extruders_sequence(cb); }
|
||||
|
||||
std::string gcode(Type type) { return m_ticks.gcode(type); }
|
||||
|
||||
private:
|
||||
|
||||
bool is_osx { false };
|
||||
bool m_allow_editing { true };
|
||||
bool m_show_estimated_times { false };
|
||||
bool m_show_cog_menu { false };
|
||||
|
||||
DrawMode m_draw_mode { dmRegular };
|
||||
Mode m_mode { SingleExtruder };
|
||||
FocusedItem m_focus { fiNone };
|
||||
|
||||
TickCodeManager m_ticks;
|
||||
Slic3r::GUI::ImGuiWrapper* m_imgui { nullptr };
|
||||
|
||||
std::vector<double> m_layers_times;
|
||||
std::vector<double> m_layers_values;
|
||||
|
||||
bool is_wipe_tower_layer(int tick) const;
|
||||
|
||||
std::string get_label(int tick, LabelType label_type) const;
|
||||
|
||||
std::string get_tooltip(int tick = -1);
|
||||
|
||||
void update_draw_scroll_line_cb();
|
||||
|
||||
// functions for extend rendering of m_ctrl
|
||||
|
||||
void draw_colored_band(const ImRect& groove, const ImRect& slideable_region);
|
||||
void draw_ticks(const ImRect& slideable_region);
|
||||
void render_menu();
|
||||
void render_cog_menu();
|
||||
bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1);
|
||||
|
||||
void add_code_as_tick(Type type, int selected_extruder = -1);
|
||||
void edit_tick(int tick = -1);
|
||||
void discard_all_thicks();
|
||||
void process_jump_to_value();
|
||||
|
||||
std::string get_label(int pos) const override { return get_label(pos, ltHeightWithLayer); }
|
||||
|
||||
void process_ticks_changed() {
|
||||
if (m_cb_ticks_changed)
|
||||
m_cb_ticks_changed();
|
||||
}
|
||||
|
||||
bool m_show_just_color_change_menu { false };
|
||||
bool m_show_get_jump_value { false };
|
||||
bool m_show_color_picker { false };
|
||||
|
||||
double m_jump_to_value { 0.0 };
|
||||
|
||||
std::string m_print_obj_idxs;
|
||||
std::string m_selectable_color;
|
||||
|
||||
void render_add_tick_menu();
|
||||
void render_multi_extruders_menu();
|
||||
bool render_jump_to_window(const ImVec2& pos, double* active_value, double min_z, double max_z);
|
||||
void render_color_picker();
|
||||
|
||||
std::function<void()> m_cb_ticks_changed { nullptr };
|
||||
std::function<std::vector<std::string>()> m_cb_get_extruder_colors { nullptr };
|
||||
std::function<const Slic3r::Print&()> m_cb_get_print { nullptr };
|
||||
};
|
||||
|
||||
} // DoubleSlider;
|
||||
|
||||
|
||||
|
||||
#endif // slic3r_GUI_DoubleSliderForLayers_hpp_
|
@ -6,7 +6,8 @@
|
||||
#define slic3r_GUI_ExtruderSequenceDialog_hpp_
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "DoubleSlider.hpp"
|
||||
#include "DoubleSliderForLayers.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
class wxTextCtrl;
|
||||
class wxFlexGridSizer;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
|
||||
@ -24,7 +25,6 @@
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "DoubleSlider.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "GLToolbar.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
@ -2120,12 +2120,12 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
if (extruder_id + 1 != static_cast<unsigned char>(item.extruder))
|
||||
continue;
|
||||
|
||||
if (item.type != ColorChange)
|
||||
if (item.type != CustomGCode::ColorChange)
|
||||
continue;
|
||||
|
||||
const std::vector<float> zs = m_viewer.get_layers_zs();
|
||||
auto lower_b = std::lower_bound(zs.begin(), zs.end(),
|
||||
static_cast<float>(item.print_z - Slic3r::DoubleSlider::epsilon()));
|
||||
static_cast<float>(item.print_z - Slic3r::CustomGCode::epsilon()));
|
||||
if (lower_b == zs.end())
|
||||
continue;
|
||||
|
||||
|
@ -77,7 +77,6 @@
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "DoubleSlider.hpp"
|
||||
|
||||
#include <imgui/imgui_internal.h>
|
||||
#include <slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp>
|
||||
@ -1019,9 +1018,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SLIDERS_MANIPULATION, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
|
||||
@ -1950,6 +1947,8 @@ void GLCanvas3D::render()
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width());
|
||||
|
||||
wxGetApp().plater()->render_sliders(*this);
|
||||
|
||||
wxGetApp().imgui()->render();
|
||||
|
||||
m_canvas->SwapBuffers();
|
||||
@ -2820,14 +2819,14 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
case '6': { select_view("right"); break; }
|
||||
case '+': {
|
||||
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt));
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
|
||||
else
|
||||
post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, +1));
|
||||
break;
|
||||
}
|
||||
case '-': {
|
||||
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt));
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
|
||||
else
|
||||
post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, -1));
|
||||
break;
|
||||
@ -2845,7 +2844,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
case 'g': {
|
||||
if ((evt.GetModifiers() & shiftMask) != 0) {
|
||||
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_JUMP_TO, evt));
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3130,7 +3129,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
keyCode == WXK_UP ||
|
||||
keyCode == WXK_DOWN) {
|
||||
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_SLIDERS, evt));
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,9 +176,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SLIDERS_MANIPULATION, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
|
||||
|
@ -19,9 +19,12 @@
|
||||
#include "OpenGLManager.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "DoubleSlider.hpp"
|
||||
#include "DoubleSliderForGcode.hpp"
|
||||
#include "DoubleSliderForLayers.hpp"
|
||||
#include "ExtruderSequenceDialog.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include <wx/listbook.h>
|
||||
@ -33,6 +36,7 @@
|
||||
#include <wx/combo.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/colordlg.h>
|
||||
|
||||
// this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421
|
||||
#include "libslic3r/Print.hpp"
|
||||
@ -197,8 +201,8 @@ Preview::Preview(
|
||||
|
||||
void Preview::set_layers_slider_values_range(int bottom, int top)
|
||||
{
|
||||
m_layers_slider->SetHigherValue(std::min(top, m_layers_slider->GetMaxValue()));
|
||||
m_layers_slider->SetLowerValue(std::max(bottom, m_layers_slider->GetMinValue()));
|
||||
m_layers_slider->SetHigherPos(std::min(top, m_layers_slider->GetMaxPos()));
|
||||
m_layers_slider->SetLowerPos(std::max(bottom, m_layers_slider->GetMinPos()));
|
||||
}
|
||||
|
||||
bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
|
||||
@ -231,29 +235,13 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
|
||||
m_canvas->show_legend(true);
|
||||
m_canvas->enable_dynamic_background(true);
|
||||
|
||||
m_layers_slider_sizer = create_layers_slider_sizer();
|
||||
|
||||
wxGetApp().UpdateDarkUI(m_bottom_toolbar_panel = new wxPanel(this));
|
||||
create_sliders();
|
||||
|
||||
m_left_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
|
||||
|
||||
wxBoxSizer* right_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
right_sizer->Add(m_layers_slider_sizer, 1, wxEXPAND, 0);
|
||||
|
||||
m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL);
|
||||
m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView);
|
||||
|
||||
wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0);
|
||||
m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer);
|
||||
|
||||
m_left_sizer->Add(m_bottom_toolbar_panel, 0, wxALL | wxEXPAND, 0);
|
||||
m_left_sizer->Hide(m_bottom_toolbar_panel);
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
main_sizer->Add(m_left_sizer, 1, wxALL | wxEXPAND, 0);
|
||||
main_sizer->Add(right_sizer, 0, wxALL | wxEXPAND, 0);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
SetMinSize(GetSize());
|
||||
@ -325,10 +313,8 @@ void Preview::reload_print()
|
||||
|
||||
void Preview::msw_rescale()
|
||||
{
|
||||
// rescale slider
|
||||
if (m_layers_slider != nullptr) m_layers_slider->msw_rescale();
|
||||
if (m_moves_slider != nullptr) m_moves_slider->msw_rescale();
|
||||
|
||||
m_layers_slider->SetEmUnit(wxGetApp().em_unit());
|
||||
m_moves_slider->SetEmUnit(wxGetApp().em_unit());
|
||||
// rescale warning legend on the canvas
|
||||
get_canvas3d()->msw_rescale();
|
||||
|
||||
@ -336,53 +322,49 @@ void Preview::msw_rescale()
|
||||
reload_print();
|
||||
}
|
||||
|
||||
void Preview::sys_color_changed()
|
||||
void Preview::render_sliders(GLCanvas3D& canvas)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wxWindowUpdateLocker noUpdates(this);
|
||||
wxGetApp().UpdateAllStaticTextDarkUI(m_bottom_toolbar_panel);
|
||||
#endif // _WIN32
|
||||
const Size cnv_size = canvas.get_canvas_size();
|
||||
const int canvas_width = cnv_size.get_width();
|
||||
const int canvas_height = cnv_size.get_height();
|
||||
const float extra_scale = cnv_size.get_scale_factor();
|
||||
|
||||
if (m_layers_slider != nullptr)
|
||||
m_layers_slider->sys_color_changed();
|
||||
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
|
||||
const bool is_collapse_btn_shown = collapse_toolbar.is_enabled();
|
||||
|
||||
if (m_layers_slider)
|
||||
m_layers_slider->Render(canvas_width, canvas_height, extra_scale, is_collapse_btn_shown ? collapse_toolbar.get_height() : 0.f);
|
||||
if (m_moves_slider)
|
||||
m_moves_slider->Render(canvas_width, canvas_height, extra_scale);
|
||||
}
|
||||
|
||||
void Preview::jump_layers_slider(wxKeyEvent& evt)
|
||||
float Preview::get_moves_slider_height()
|
||||
{
|
||||
if (m_layers_slider) m_layers_slider->OnChar(evt);
|
||||
if (m_moves_slider && m_moves_slider->IsShown())
|
||||
return m_moves_slider->GetHeight();
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void Preview::move_layers_slider(wxKeyEvent& evt)
|
||||
float Preview::get_layers_slider_width()
|
||||
{
|
||||
if (m_layers_slider != nullptr) m_layers_slider->OnKeyDown(evt);
|
||||
}
|
||||
|
||||
void Preview::edit_layers_slider(wxKeyEvent& evt)
|
||||
{
|
||||
if (m_layers_slider != nullptr) m_layers_slider->OnChar(evt);
|
||||
if (m_layers_slider && m_layers_slider->IsShown())
|
||||
return m_layers_slider->GetWidth();
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void Preview::bind_event_handlers()
|
||||
{
|
||||
Bind(wxEVT_SIZE, &Preview::on_size, this);
|
||||
m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this);
|
||||
}
|
||||
|
||||
void Preview::unbind_event_handlers()
|
||||
{
|
||||
Unbind(wxEVT_SIZE, &Preview::on_size, this);
|
||||
m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this);
|
||||
}
|
||||
|
||||
void Preview::move_moves_slider(wxKeyEvent& evt)
|
||||
{
|
||||
if (m_moves_slider != nullptr) m_moves_slider->OnKeyDown(evt);
|
||||
}
|
||||
|
||||
void Preview::hide_layers_slider()
|
||||
{
|
||||
m_layers_slider_sizer->Hide((size_t)0);
|
||||
Layout();
|
||||
m_layers_slider->Hide();
|
||||
}
|
||||
|
||||
void Preview::on_size(wxSizeEvent& evt)
|
||||
@ -391,37 +373,167 @@ void Preview::on_size(wxSizeEvent& evt)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
wxBoxSizer* Preview::create_layers_slider_sizer()
|
||||
/* To avoid get an empty string from wxTextEntryDialog
|
||||
* Let disable OK button, if TextCtrl is empty
|
||||
* */
|
||||
static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0, double max = -1.0)
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100);
|
||||
GUI::wxGetApp().UpdateDlgDarkUI(dlg);
|
||||
|
||||
// detect TextCtrl and OK button
|
||||
wxWindowList& dlg_items = dlg->GetChildren();
|
||||
for (auto item : dlg_items) {
|
||||
if (wxTextCtrl* textctrl = dynamic_cast<wxTextCtrl*>(item)) {
|
||||
textctrl->SetInsertionPointEnd();
|
||||
|
||||
wxButton* btn_OK = static_cast<wxButton*>(dlg->FindWindowById(wxID_OK));
|
||||
btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl](wxUpdateUIEvent& evt) {
|
||||
evt.Enable(!textctrl->IsEmpty());
|
||||
}, btn_OK->GetId());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::create_sliders()
|
||||
{
|
||||
// Layers Slider
|
||||
|
||||
m_layers_slider = std::make_unique<DoubleSlider::DSForLayers>(0, 0, 0, 100, wxGetApp().is_editor());
|
||||
m_layers_slider->SetEmUnit(wxGetApp().em_unit());
|
||||
m_layers_slider->set_imgui_wrapper(wxGetApp().imgui());
|
||||
|
||||
m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA,
|
||||
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"));
|
||||
m_layers_slider->enable_action_icon(wxGetApp().is_editor());
|
||||
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"));
|
||||
|
||||
sizer->Add(m_layers_slider, 0, wxEXPAND, 0);
|
||||
m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } );
|
||||
|
||||
// sizer, m_canvas_widget
|
||||
m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_layers_slider_from_canvas, this);
|
||||
m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) {
|
||||
if (event.GetKeyCode() == WXK_SHIFT)
|
||||
m_layers_slider->UseDefaultColors(true);
|
||||
event.Skip();
|
||||
if (wxGetApp().is_editor()) {
|
||||
m_layers_slider->set_callback_on_ticks_changed([this]() -> void {
|
||||
Model& model = wxGetApp().plater()->model();
|
||||
model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues();
|
||||
m_schedule_background_process();
|
||||
|
||||
m_keep_current_preview_type = false;
|
||||
reload_print();
|
||||
});
|
||||
|
||||
m_layers_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_layers_slider_scroll_changed, this);
|
||||
m_layers_slider->set_callback_on_check_gcode([this](CustomGCode::Type type) -> void {
|
||||
if (type == ColorChange && m_layers_slider->gcode(ColorChange).empty())
|
||||
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyColorChangeCode);
|
||||
});
|
||||
|
||||
Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
|
||||
Model& model = wxGetApp().plater()->model();
|
||||
model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues();
|
||||
m_schedule_background_process();
|
||||
m_layers_slider->set_callback_on_empty_auto_color_change([]() -> void {
|
||||
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange);
|
||||
});
|
||||
|
||||
m_keep_current_preview_type = false;
|
||||
reload_print();
|
||||
});
|
||||
m_layers_slider->set_callback_on_get_extruder_colors([]() -> std::vector<std::string> {
|
||||
return wxGetApp().plater()->get_extruder_colors_from_plater_config();
|
||||
});
|
||||
|
||||
return sizer;
|
||||
m_layers_slider->set_callback_on_get_print([]() -> const Print& {
|
||||
return GUI::wxGetApp().plater()->fff_print();
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_get_custom_code([](const std::string& code_in, double height) -> std::string
|
||||
{
|
||||
wxString msg_text = _L("Enter custom G-code used on current layer") + ":";
|
||||
wxString msg_header = format_wxstr(_L("Custom G-code on current layer (%1% mm)."), height);
|
||||
|
||||
// get custom gcode
|
||||
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in,
|
||||
wxTextEntryDialogStyle | wxTE_MULTILINE);
|
||||
upgrade_text_entry_dialog(&dlg);
|
||||
|
||||
bool valid = true;
|
||||
std::string value;
|
||||
do {
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return "";
|
||||
|
||||
value = into_u8(dlg.GetValue());
|
||||
valid = true;// GUI::Tab::validate_custom_gcode("Custom G-code", value); // !ysFIXME validate_custom_gcode
|
||||
} while (!valid);
|
||||
return value;
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_get_pause_print_msg([](const std::string& msg_in, double height) -> std::string
|
||||
{
|
||||
wxString msg_text = _L("Enter short message shown on Printer display when a print is paused") + ":";
|
||||
wxString msg_header = format_wxstr(_L("Message for pause print on current layer (%1% mm)."), height);
|
||||
|
||||
// get custom gcode
|
||||
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in),
|
||||
wxTextEntryDialogStyle);
|
||||
upgrade_text_entry_dialog(&dlg);
|
||||
|
||||
if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty())
|
||||
return "";
|
||||
|
||||
return into_u8(dlg.GetValue());
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_get_new_color([](const std::string& color) -> std::string
|
||||
{
|
||||
wxColour clr(color);
|
||||
if (!clr.IsOk())
|
||||
clr = wxColour(0, 0, 0); // Don't set alfa to transparence
|
||||
|
||||
auto data = new wxColourData();
|
||||
data->SetChooseFull(1);
|
||||
data->SetColour(clr);
|
||||
|
||||
wxColourDialog dialog(GUI::wxGetApp().GetTopWindow(), data);
|
||||
dialog.CenterOnParent();
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||
return "";
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_show_info_msg([this](const std::string& message, int btns_flag) -> int
|
||||
{
|
||||
GUI::MessageDialog msg(this, from_u8(message), _L("Notice"), btns_flag);
|
||||
int ret = msg.ShowModal();
|
||||
return ret == wxID_YES ? wxYES :
|
||||
ret == wxID_NO ? wxNO :
|
||||
ret == wxID_CANCEL ? wxCANCEL : -1;
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_show_warning_msg([this](const std::string& message, int btns_flag) -> int
|
||||
{
|
||||
GUI::WarningDialog msg(this, from_u8(message), _L("Warning"), btns_flag);
|
||||
int ret = msg.ShowModal();
|
||||
return ret == wxID_YES ? wxYES :
|
||||
ret == wxID_NO ? wxNO :
|
||||
ret == wxID_CANCEL ? wxCANCEL : -1;
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_get_extruders_cnt([]() -> int
|
||||
{
|
||||
return GUI::wxGetApp().extruders_edited_cnt();
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_get_extruders_sequence([](DoubleSlider::ExtrudersSequence& extruders_sequence) -> bool
|
||||
{
|
||||
GUI::ExtruderSequenceDialog dlg(extruders_sequence);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return false;
|
||||
extruders_sequence = dlg.GetValue();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Move Gcode Slider
|
||||
|
||||
m_moves_slider = std::make_unique<DoubleSlider::DSForGcode>(0, 0, 0, 100);
|
||||
m_moves_slider->SetEmUnit(wxGetApp().em_unit());
|
||||
|
||||
m_moves_slider->set_callback_on_thumb_move([this]() ->void { on_moves_slider_scroll_changed(); });
|
||||
|
||||
// m_canvas_widget
|
||||
m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_sliders_from_canvas, this);
|
||||
m_canvas_widget->Bind(EVT_GLCANVAS_SLIDERS_MANIPULATION, &Preview::update_sliders_from_canvas, this);
|
||||
}
|
||||
|
||||
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
|
||||
@ -460,7 +572,7 @@ void Preview::check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_f
|
||||
ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(),
|
||||
[layers_z](CustomGCode::Item val)
|
||||
{
|
||||
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon());
|
||||
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - CustomGCode::epsilon());
|
||||
return it == layers_z.end();
|
||||
}),
|
||||
ticks_from_model.end());
|
||||
@ -471,17 +583,17 @@ void Preview::check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_f
|
||||
void Preview::update_layers_slider(const std::vector<double>& layers_z, bool keep_z_range)
|
||||
{
|
||||
// Save the initial slider span.
|
||||
double z_low = m_layers_slider->GetLowerValueD();
|
||||
double z_high = m_layers_slider->GetHigherValueD();
|
||||
bool was_empty = m_layers_slider->GetMaxValue() == 0;
|
||||
double z_low = m_layers_slider->GetLowerValue();
|
||||
double z_high = m_layers_slider->GetHigherValue();
|
||||
bool was_empty = m_layers_slider->GetMaxPos() == 0;
|
||||
|
||||
bool force_sliders_full_range = was_empty;
|
||||
if (!keep_z_range) {
|
||||
bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValueD()) > DoubleSlider::epsilon()/*1e-6*/;
|
||||
bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValue()) > CustomGCode::epsilon()/*1e-6*/;
|
||||
force_sliders_full_range |= span_changed;
|
||||
}
|
||||
bool snap_to_min = force_sliders_full_range || m_layers_slider->is_lower_at_min();
|
||||
bool snap_to_max = force_sliders_full_range || m_layers_slider->is_higher_at_max();
|
||||
bool snap_to_min = force_sliders_full_range || m_layers_slider->IsLowerAtMin();
|
||||
bool snap_to_max = force_sliders_full_range || m_layers_slider->IsHigherAtMax();
|
||||
|
||||
// Detect and set manipulation mode for double slider
|
||||
update_layers_slider_mode();
|
||||
@ -499,19 +611,19 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
//first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
|
||||
m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
|
||||
m_layers_slider->SetSliderValues(layers_z);
|
||||
assert(m_layers_slider->GetMinValue() == 0);
|
||||
m_layers_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1);
|
||||
assert(m_layers_slider->GetMinPos() == 0);
|
||||
m_layers_slider->SetMaxPos(layers_z.empty() ? 0 : layers_z.size() - 1);
|
||||
|
||||
int idx_low = 0;
|
||||
int idx_high = m_layers_slider->GetMaxValue();
|
||||
int idx_high = m_layers_slider->GetMaxPos();
|
||||
if (!layers_z.empty()) {
|
||||
if (!snap_to_min) {
|
||||
int idx_new = find_close_layer_idx(layers_z, z_low, DoubleSlider::epsilon()/*1e-6*/);
|
||||
int idx_new = find_close_layer_idx(layers_z, z_low, CustomGCode::epsilon()/*1e-6*/);
|
||||
if (idx_new != -1)
|
||||
idx_low = idx_new;
|
||||
}
|
||||
if (!snap_to_max) {
|
||||
int idx_new = find_close_layer_idx(layers_z, z_high, DoubleSlider::epsilon()/*1e-6*/);
|
||||
int idx_new = find_close_layer_idx(layers_z, z_high, CustomGCode::epsilon()/*1e-6*/);
|
||||
if (idx_new != -1)
|
||||
idx_high = idx_new;
|
||||
}
|
||||
@ -535,10 +647,20 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
break;
|
||||
}
|
||||
|
||||
auto get_print_obj_idxs = [plater]() ->std::string {
|
||||
if (plater->printer_technology() == ptSLA)
|
||||
return "sla";
|
||||
const Print& print = GUI::wxGetApp().plater()->fff_print();
|
||||
std::string idxs;
|
||||
for (auto object : print.objects())
|
||||
idxs += std::to_string(object->id().id) + "_";
|
||||
return idxs;
|
||||
};
|
||||
|
||||
// Suggest the auto color change, if model looks like sign
|
||||
if (!color_change_already_exists &&
|
||||
wxGetApp().app_config->get_bool("allow_auto_color_change") &&
|
||||
m_layers_slider->IsNewPrint())
|
||||
m_layers_slider->is_new_print(get_print_obj_idxs()))
|
||||
{
|
||||
const Print& print = wxGetApp().plater()->fff_print();
|
||||
|
||||
@ -593,9 +715,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_layers_slider_sizer->Show((size_t)0);
|
||||
Layout();
|
||||
m_layers_slider->Show();
|
||||
}
|
||||
|
||||
void Preview::update_layers_slider_mode()
|
||||
@ -654,33 +774,54 @@ void Preview::update_layers_slider_mode()
|
||||
|
||||
void Preview::reset_layers_slider()
|
||||
{
|
||||
m_layers_slider->SetHigherValue(0);
|
||||
m_layers_slider->SetLowerValue(0);
|
||||
m_layers_slider->SetHigherPos(0);
|
||||
m_layers_slider->SetLowerPos(0);
|
||||
}
|
||||
|
||||
void Preview::update_layers_slider_from_canvas(wxKeyEvent& event)
|
||||
void Preview::update_sliders_from_canvas(wxKeyEvent& event)
|
||||
{
|
||||
if (event.HasModifiers()) {
|
||||
const auto key = event.GetKeyCode();
|
||||
|
||||
const bool can_edit = wxGetApp().is_editor();
|
||||
|
||||
if (can_edit && (key == WXK_NUMPAD_ADD || key == '+'))
|
||||
m_layers_slider->add_current_tick();
|
||||
else if (can_edit && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK || key == '-'))
|
||||
m_layers_slider->delete_current_tick();
|
||||
else if (key == 'G' || key == 'g')
|
||||
m_layers_slider->jump_to_value();
|
||||
else if (key == WXK_LEFT || key == WXK_RIGHT || key == WXK_UP || key == WXK_DOWN) {
|
||||
int delta = 1;
|
||||
// accelerators
|
||||
int accelerator = 0;
|
||||
if (wxGetKeyState(WXK_SHIFT))
|
||||
accelerator += 5;
|
||||
if (wxGetKeyState(WXK_CONTROL))
|
||||
accelerator += 5;
|
||||
if (accelerator > 0)
|
||||
delta *= accelerator;
|
||||
|
||||
if (key == WXK_LEFT || key == WXK_RIGHT)
|
||||
m_moves_slider->move_current_thumb(delta * (key == WXK_LEFT ? 1 : -1));
|
||||
else if (key == WXK_UP || key == WXK_DOWN)
|
||||
m_layers_slider->move_current_thumb(delta * (key == WXK_DOWN ? 1 : -1));
|
||||
}
|
||||
|
||||
else if (event.HasModifiers()) {
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto key = event.GetKeyCode();
|
||||
|
||||
if (key == 'S' || key == 'W') {
|
||||
const int new_pos = key == 'W' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1;
|
||||
m_layers_slider->SetHigherValue(new_pos);
|
||||
if (event.ShiftDown() || m_layers_slider->is_one_layer()) m_layers_slider->SetLowerValue(m_layers_slider->GetHigherValue());
|
||||
else if (key == 'S' || key == 'W') {
|
||||
const int new_pos = key == 'W' ? m_layers_slider->GetHigherPos() + 1 : m_layers_slider->GetHigherPos() - 1;
|
||||
m_layers_slider->SetHigherPos(new_pos);
|
||||
}
|
||||
else if (key == 'A' || key == 'D') {
|
||||
const int new_pos = key == 'D' ? m_moves_slider->GetHigherValue() + 1 : m_moves_slider->GetHigherValue() - 1;
|
||||
m_moves_slider->SetHigherValue(new_pos);
|
||||
if (event.ShiftDown() || m_moves_slider->is_one_layer()) m_moves_slider->SetLowerValue(m_moves_slider->GetHigherValue());
|
||||
const int new_pos = key == 'D' ? m_moves_slider->GetHigherPos() + 1 : m_moves_slider->GetHigherPos() - 1;
|
||||
m_moves_slider->SetHigherPos(new_pos);
|
||||
}
|
||||
else if (key == 'X')
|
||||
m_layers_slider->ChangeOneLayerLock();
|
||||
else if (key == WXK_SHIFT)
|
||||
m_layers_slider->UseDefaultColors(false);
|
||||
else
|
||||
event.Skip();
|
||||
}
|
||||
@ -698,9 +839,9 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
|
||||
std::optional<uint32_t>{ m_canvas->get_gcode_vertex_at(*visible_range_max).gcode_id } : std::nullopt;
|
||||
|
||||
const size_t range_size = range[1] - range[0] + 1;
|
||||
std::vector<double> values;
|
||||
std::vector<unsigned int> values;
|
||||
values.reserve(range_size);
|
||||
std::vector<double> alternate_values;
|
||||
std::vector<unsigned int> alternate_values;
|
||||
alternate_values.reserve(range_size);
|
||||
|
||||
std::optional<uint32_t> visible_range_min_id;
|
||||
@ -713,7 +854,7 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
|
||||
if (i > range[0]) {
|
||||
// skip consecutive moves with same gcode id (resulting from processing G2 and G3 lines)
|
||||
if (last_gcode_id == gcode_id) {
|
||||
values.back() = static_cast<double>(i + 1);
|
||||
values.back() = i + 1;
|
||||
skip = true;
|
||||
}
|
||||
else
|
||||
@ -721,11 +862,11 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
values.emplace_back(static_cast<double>(i + 1));
|
||||
alternate_values.emplace_back(static_cast<double>(gcode_id));
|
||||
if (gcode_id_min.has_value() && alternate_values.back() == static_cast<double>(*gcode_id_min))
|
||||
values.emplace_back(i + 1);
|
||||
alternate_values.emplace_back(gcode_id);
|
||||
if (gcode_id_min.has_value() && alternate_values.back() == *gcode_id_min)
|
||||
visible_range_min_id = counter;
|
||||
else if (gcode_id_max.has_value() && alternate_values.back() == static_cast<double>(*gcode_id_max))
|
||||
else if (gcode_id_max.has_value() && alternate_values.back() == *gcode_id_max)
|
||||
visible_range_max_id = counter;
|
||||
++counter;
|
||||
}
|
||||
@ -736,8 +877,10 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
|
||||
|
||||
m_moves_slider->SetSliderValues(values);
|
||||
m_moves_slider->SetSliderAlternateValues(alternate_values);
|
||||
m_moves_slider->SetMaxValue(static_cast<int>(values.size()) - 1);
|
||||
m_moves_slider->SetMaxPos(static_cast<int>(values.size()) - 1);
|
||||
m_moves_slider->SetSelectionSpan(span_min_id, span_max_id);
|
||||
|
||||
m_moves_slider->ShowLowerThumb(get_app_config()->get("seq_top_layer_only") == "0");
|
||||
}
|
||||
|
||||
void Preview::enable_moves_slider(bool enable)
|
||||
@ -745,7 +888,6 @@ void Preview::enable_moves_slider(bool enable)
|
||||
bool render_as_disabled = !enable;
|
||||
if (m_moves_slider != nullptr && m_moves_slider->is_rendering_as_disabled() != render_as_disabled) {
|
||||
m_moves_slider->set_render_as_disabled(render_as_disabled);
|
||||
m_moves_slider->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -781,9 +923,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
||||
if (wxGetApp().is_editor() && !has_layers) {
|
||||
m_canvas->reset_gcode_layers_times_cache();
|
||||
hide_layers_slider();
|
||||
m_left_sizer->Hide(m_bottom_toolbar_panel);
|
||||
m_left_sizer->Layout();
|
||||
Refresh();
|
||||
m_moves_slider->Hide();
|
||||
m_canvas_widget->Refresh();
|
||||
return;
|
||||
}
|
||||
@ -810,11 +950,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
||||
m_canvas->load_gcode_preview(*m_gcode_result, tool_colors, color_print_colors);
|
||||
// the view type may have been changed by the call m_canvas->load_gcode_preview()
|
||||
gcode_view_type = m_canvas->get_gcode_view_type();
|
||||
m_left_sizer->Layout();
|
||||
Refresh();
|
||||
zs = m_canvas->get_gcode_layers_zs();
|
||||
if (!zs.empty())
|
||||
m_left_sizer->Show(m_bottom_toolbar_panel);
|
||||
m_loaded = true;
|
||||
}
|
||||
else if (is_pregcode_preview) {
|
||||
@ -823,16 +959,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
||||
// the view type has been changed by the call m_canvas->load_gcode_preview()
|
||||
if (gcode_view_type == libvgcode::EViewType::ColorPrint && !color_print_values.empty())
|
||||
m_canvas->set_gcode_view_type(gcode_view_type);
|
||||
m_left_sizer->Hide(m_bottom_toolbar_panel);
|
||||
m_left_sizer->Layout();
|
||||
Refresh();
|
||||
zs = m_canvas->get_gcode_layers_zs();
|
||||
}
|
||||
else {
|
||||
m_left_sizer->Hide(m_bottom_toolbar_panel);
|
||||
m_left_sizer->Layout();
|
||||
Refresh();
|
||||
}
|
||||
m_moves_slider->Show(gcode_preview_data_valid && !zs.empty());
|
||||
|
||||
if (!zs.empty() && !m_keep_current_preview_type) {
|
||||
const unsigned int number_extruders = wxGetApp().is_editor() ?
|
||||
@ -893,9 +1022,7 @@ void Preview::load_print_as_sla()
|
||||
|
||||
if (IsShown()) {
|
||||
m_canvas->load_sla_preview();
|
||||
m_left_sizer->Hide(m_bottom_toolbar_panel);
|
||||
m_left_sizer->Layout();
|
||||
Refresh();
|
||||
m_moves_slider->Hide();
|
||||
|
||||
if (n_layers > 0)
|
||||
update_layers_slider(zs);
|
||||
@ -904,26 +1031,26 @@ void Preview::load_print_as_sla()
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::on_layers_slider_scroll_changed(wxCommandEvent& event)
|
||||
void Preview::on_layers_slider_scroll_changed()
|
||||
{
|
||||
if (IsShown()) {
|
||||
PrinterTechnology tech = m_process->current_printer_technology();
|
||||
if (tech == ptFFF) {
|
||||
m_canvas->set_volumes_z_range({ m_layers_slider->GetLowerValueD(), m_layers_slider->GetHigherValueD() });
|
||||
m_canvas->set_toolpaths_z_range({ static_cast<unsigned int>(m_layers_slider->GetLowerValue()), static_cast<unsigned int>(m_layers_slider->GetHigherValue()) });
|
||||
m_canvas->set_volumes_z_range({ m_layers_slider->GetLowerValue(), m_layers_slider->GetHigherValue() });
|
||||
m_canvas->set_toolpaths_z_range({ static_cast<unsigned int>(m_layers_slider->GetLowerPos()), static_cast<unsigned int>(m_layers_slider->GetHigherPos()) });
|
||||
m_canvas->set_as_dirty();
|
||||
}
|
||||
else if (tech == ptSLA) {
|
||||
m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValueD()));
|
||||
m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValueD()));
|
||||
m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValue()));
|
||||
m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValue()));
|
||||
m_canvas->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event)
|
||||
void Preview::on_moves_slider_scroll_changed()
|
||||
{
|
||||
m_canvas->update_gcode_sequential_view_current(static_cast<unsigned int>(m_moves_slider->GetLowerValueD() - 1.0), static_cast<unsigned int>(m_moves_slider->GetHigherValueD() - 1.0));
|
||||
m_canvas->update_gcode_sequential_view_current(static_cast<unsigned int>(m_moves_slider->GetLowerValue() - 1), static_cast<unsigned int>(m_moves_slider->GetHigherValue() - 1));
|
||||
m_canvas->set_as_dirty();
|
||||
m_canvas->request_extra_frame();
|
||||
}
|
||||
|
@ -20,6 +20,11 @@ class wxComboBox;
|
||||
class wxComboCtrl;
|
||||
class wxCheckBox;
|
||||
|
||||
namespace DoubleSlider {
|
||||
class DSForGcode;
|
||||
class DSForLayers;
|
||||
};
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
@ -27,10 +32,6 @@ class Print;
|
||||
class BackgroundSlicingProcess;
|
||||
class Model;
|
||||
|
||||
namespace DoubleSlider {
|
||||
class Control;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
@ -82,8 +83,6 @@ class Preview : public wxPanel
|
||||
wxGLCanvas* m_canvas_widget { nullptr };
|
||||
GLCanvas3D* m_canvas { nullptr };
|
||||
wxBoxSizer* m_left_sizer { nullptr };
|
||||
wxBoxSizer* m_layers_slider_sizer { nullptr };
|
||||
wxPanel* m_bottom_toolbar_panel { nullptr };
|
||||
|
||||
DynamicPrintConfig* m_config;
|
||||
BackgroundSlicingProcess* m_process;
|
||||
@ -97,8 +96,8 @@ class Preview : public wxPanel
|
||||
|
||||
bool m_loaded { false };
|
||||
|
||||
DoubleSlider::Control* m_layers_slider{ nullptr };
|
||||
DoubleSlider::Control* m_moves_slider{ nullptr };
|
||||
std::unique_ptr<DoubleSlider::DSForLayers> m_layers_slider{ nullptr };
|
||||
std::unique_ptr<DoubleSlider::DSForGcode> m_moves_slider { nullptr };
|
||||
|
||||
public:
|
||||
enum class OptionType : unsigned int
|
||||
@ -135,16 +134,15 @@ public:
|
||||
void reload_print();
|
||||
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
void jump_layers_slider(wxKeyEvent& evt);
|
||||
void move_layers_slider(wxKeyEvent& evt);
|
||||
void edit_layers_slider(wxKeyEvent& evt);
|
||||
|
||||
void render_sliders(GLCanvas3D& canvas);
|
||||
float get_layers_slider_width();
|
||||
float get_moves_slider_height();
|
||||
|
||||
bool is_loaded() const { return m_loaded; }
|
||||
|
||||
void update_moves_slider(std::optional<int> visible_range_min = std::nullopt, std::optional<int> visible_range_max = std::nullopt);
|
||||
void enable_moves_slider(bool enable);
|
||||
void move_moves_slider(wxKeyEvent& evt);
|
||||
void hide_layers_slider();
|
||||
|
||||
void set_keep_current_preview_type(bool value) { m_keep_current_preview_type = value; }
|
||||
@ -160,20 +158,20 @@ private:
|
||||
void on_size(wxSizeEvent& evt);
|
||||
|
||||
// Create/Update/Reset double slider on 3dPreview
|
||||
wxBoxSizer* create_layers_slider_sizer();
|
||||
void create_sliders();
|
||||
void check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_from_model,
|
||||
const std::vector<double>& layers_z);
|
||||
void reset_layers_slider();
|
||||
void update_layers_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
|
||||
void update_layers_slider_mode();
|
||||
// update vertical DoubleSlider after keyDown in canvas
|
||||
void update_layers_slider_from_canvas(wxKeyEvent& event);
|
||||
void update_sliders_from_canvas(wxKeyEvent& event);
|
||||
|
||||
void load_print_as_fff(bool keep_z_range = false);
|
||||
void load_print_as_sla();
|
||||
|
||||
void on_layers_slider_scroll_changed(wxCommandEvent& event);
|
||||
void on_moves_slider_scroll_changed(wxCommandEvent& event);
|
||||
void on_layers_slider_scroll_changed();
|
||||
void on_moves_slider_scroll_changed();
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
554
src/slic3r/GUI/ImGuiDoubleSlider.cpp
Normal file
@ -0,0 +1,554 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
|
||||
#include "ImGuiDoubleSlider.hpp"
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
const ImU32 tooltip_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT);
|
||||
const ImU32 thumb_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT);
|
||||
const ImU32 groove_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND);
|
||||
const ImU32 border_clr = IM_COL32(255, 255, 255, 255);
|
||||
|
||||
static bool behavior(ImGuiID id, const ImRect& region,
|
||||
const ImS32 v_min, const ImS32 v_max,
|
||||
ImS32* out_value, ImRect* out_thumb,
|
||||
ImGuiSliderFlags flags = 0,
|
||||
bool change_on_mouse_move = false)
|
||||
{
|
||||
ImGuiContext& context = *GImGui;
|
||||
|
||||
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
|
||||
|
||||
const ImVec2 thumb_sz = out_thumb->GetSize();
|
||||
ImS32 v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
|
||||
const float region_usable_sz = (region.Max[axis] - region.Min[axis]);
|
||||
const float region_usable_pos_min = region.Min[axis];
|
||||
const float region_usable_pos_max = region.Max[axis];
|
||||
|
||||
const float mouse_abs_pos = context.IO.MousePos[axis];
|
||||
float mouse_pos_ratio = (region_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - region_usable_pos_min) / region_usable_sz, 0.0f, 1.0f) : 0.0f;
|
||||
if (axis == ImGuiAxis_Y)
|
||||
mouse_pos_ratio = 1.0f - mouse_pos_ratio;
|
||||
|
||||
// Process interacting with the slider
|
||||
ImS32 v_new = *out_value;
|
||||
bool value_changed = false;
|
||||
// wheel behavior
|
||||
ImRect mouse_wheel_responsive_region;
|
||||
if (axis == ImGuiAxis_X)
|
||||
mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(thumb_sz.x / 2, 0), region.Max + ImVec2(thumb_sz.x / 2, 0));
|
||||
if (axis == ImGuiAxis_Y)
|
||||
mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(0, thumb_sz.y), region.Max + ImVec2(0, thumb_sz.y));
|
||||
if (ImGui::ItemHoverable(mouse_wheel_responsive_region, id)) {
|
||||
if (change_on_mouse_move)
|
||||
v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f);
|
||||
else
|
||||
v_new = ImClamp(*out_value + (ImS32)(context.IO.MouseWheel/* * accer*/), v_min, v_max);
|
||||
}
|
||||
|
||||
// drag behavior
|
||||
if (context.ActiveId == id)
|
||||
{
|
||||
if (context.ActiveIdSource == ImGuiInputSource_Mouse)
|
||||
{
|
||||
if (context.IO.MouseReleased[0])
|
||||
ImGui::ClearActiveID();
|
||||
if (context.IO.MouseDown[0])
|
||||
v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// apply result, output value
|
||||
if (*out_value != v_new)
|
||||
{
|
||||
*out_value = v_new;
|
||||
value_changed = true;
|
||||
}
|
||||
|
||||
// Output thumb position so it can be displayed by the caller
|
||||
const ImS32 v_clamped = (v_min < v_max) ? ImClamp(*out_value, v_min, v_max) : ImClamp(*out_value, v_max, v_min);
|
||||
float thumb_pos_ratio = v_range != 0 ? ((float)(v_clamped - v_min) / (float)v_range) : 0.0f;
|
||||
thumb_pos_ratio = axis == ImGuiAxis_Y ? 1.0f - thumb_pos_ratio : thumb_pos_ratio;
|
||||
const float thumb_pos = region_usable_pos_min + (region_usable_pos_max - region_usable_pos_min) * thumb_pos_ratio;
|
||||
|
||||
ImVec2 new_thumb_center = axis == ImGuiAxis_Y ? ImVec2(out_thumb->GetCenter().x, thumb_pos) : ImVec2(thumb_pos, out_thumb->GetCenter().y);
|
||||
*out_thumb = ImRect(new_thumb_center - thumb_sz * 0.5f, new_thumb_center + thumb_sz * 0.5f);
|
||||
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const
|
||||
{
|
||||
ImVec2 groove_start = is_horizontal ?
|
||||
ImVec2(pos.x + thumb_dummy_sz().x, pos.y + size.y - groove_sz().y - dummy_sz().y) :
|
||||
ImVec2(pos.x + size.x - groove_sz().x - dummy_sz().x, pos.y + text_dummy_sz().y);
|
||||
ImVec2 groove_size = is_horizontal ?
|
||||
ImVec2(size.x - 2 * thumb_dummy_sz().x - text_dummy_sz().x, groove_sz().y) :
|
||||
ImVec2(groove_sz().x, size.y - 1.6 * text_dummy_sz().y);
|
||||
|
||||
return ImRect(groove_start, groove_start + groove_size);
|
||||
}
|
||||
|
||||
ImRect ImGuiControl::DrawOptions::draggable_region(const ImRect& groove, bool is_horizontal) const
|
||||
{
|
||||
ImRect draggable_region = is_horizontal ?
|
||||
ImRect(groove.Min.x, groove.GetCenter().y, groove.Max.x, groove.GetCenter().y) :
|
||||
ImRect(groove.GetCenter().x, groove.Min.y, groove.GetCenter().x, groove.Max.y);
|
||||
draggable_region.Expand(is_horizontal ?
|
||||
ImVec2(/*thumb_radius()*/0, draggable_region_sz().y) :
|
||||
ImVec2(draggable_region_sz().x, 0));
|
||||
|
||||
return draggable_region;
|
||||
}
|
||||
|
||||
ImRect ImGuiControl::DrawOptions::slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) const
|
||||
{
|
||||
ImVec2 mid = draggable_region.GetCenter();
|
||||
|
||||
ImRect scroll_line = is_horizontal ?
|
||||
ImRect(ImVec2(l_thumb_center.x, mid.y - groove_sz().y / 2), ImVec2(h_thumb_center.x, mid.y + groove_sz().y / 2)) :
|
||||
ImRect(ImVec2(mid.x - groove_sz().x / 2, h_thumb_center.y), ImVec2(mid.x + groove_sz().x / 2, l_thumb_center.y));
|
||||
|
||||
return scroll_line;
|
||||
}
|
||||
|
||||
ImGuiControl::ImGuiControl( int lowerValue,
|
||||
int higherValue,
|
||||
int minValue,
|
||||
int maxValue,
|
||||
ImGuiSliderFlags flags,
|
||||
std::string name,
|
||||
bool use_lower_thumb) :
|
||||
m_selection(ssUndef),
|
||||
m_name(name),
|
||||
m_lower_pos(lowerValue),
|
||||
m_higher_pos (higherValue),
|
||||
m_min_pos(minValue),
|
||||
m_max_pos(maxValue),
|
||||
m_flags(flags),
|
||||
m_draw_lower_thumb(use_lower_thumb)
|
||||
{
|
||||
}
|
||||
|
||||
int ImGuiControl::GetActivePos() const
|
||||
{
|
||||
return m_selection == ssLower ? m_lower_pos :
|
||||
m_selection == ssHigher ? m_higher_pos : -1;
|
||||
}
|
||||
|
||||
void ImGuiControl::SetLowerPos(const int lower_pos)
|
||||
{
|
||||
m_selection = ssLower;
|
||||
m_lower_pos = lower_pos;
|
||||
correct_lower_pos();
|
||||
}
|
||||
|
||||
void ImGuiControl::SetHigherPos(const int higher_pos)
|
||||
{
|
||||
m_selection = ssHigher;
|
||||
m_higher_pos = higher_pos;
|
||||
correct_higher_pos();
|
||||
}
|
||||
|
||||
void ImGuiControl::SetSelectionSpan(const int lower_pos, const int higher_pos)
|
||||
{
|
||||
m_lower_pos = std::max(lower_pos, m_min_pos);
|
||||
m_higher_pos = std::max(std::min(higher_pos, m_max_pos), m_lower_pos);
|
||||
if (m_lower_pos < m_higher_pos)
|
||||
m_combine_thumbs = false;
|
||||
}
|
||||
|
||||
void ImGuiControl::SetMaxPos(const int max_pos)
|
||||
{
|
||||
m_max_pos = max_pos;
|
||||
correct_higher_pos();
|
||||
}
|
||||
|
||||
void ImGuiControl::MoveActiveThumb(int delta)
|
||||
{
|
||||
if (m_selection == ssUndef)
|
||||
m_selection = ssHigher;
|
||||
|
||||
if (m_selection == ssLower) {
|
||||
m_lower_pos -= delta;
|
||||
correct_lower_pos();
|
||||
}
|
||||
else if (m_selection == ssHigher) {
|
||||
m_higher_pos -= delta;
|
||||
correct_higher_pos();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiControl::correct_lower_pos()
|
||||
{
|
||||
if (m_lower_pos < m_min_pos)
|
||||
m_lower_pos = m_min_pos;
|
||||
else if (m_lower_pos > m_max_pos)
|
||||
m_lower_pos = m_max_pos;
|
||||
|
||||
if ((m_lower_pos >= m_higher_pos && m_lower_pos <= m_max_pos) || m_combine_thumbs) {
|
||||
m_higher_pos = m_lower_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiControl::correct_higher_pos()
|
||||
{
|
||||
if (m_higher_pos > m_max_pos)
|
||||
m_higher_pos = m_max_pos;
|
||||
else if (m_higher_pos < m_min_pos)
|
||||
m_higher_pos = m_min_pos;
|
||||
|
||||
if ((m_higher_pos <= m_lower_pos && m_higher_pos >= m_min_pos) || m_combine_thumbs) {
|
||||
m_lower_pos = m_higher_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiControl::CombineThumbs(bool combine)
|
||||
{
|
||||
m_combine_thumbs = combine;
|
||||
if (combine) {
|
||||
m_selection = ssHigher;
|
||||
correct_higher_pos();
|
||||
}
|
||||
else
|
||||
ResetPositions();
|
||||
}
|
||||
|
||||
void ImGuiControl::ResetPositions()
|
||||
{
|
||||
SetLowerPos(m_min_pos);
|
||||
SetHigherPos(m_max_pos);
|
||||
m_selection == ssLower ? correct_lower_pos() : correct_higher_pos();
|
||||
}
|
||||
|
||||
std::string ImGuiControl::get_label(int pos) const
|
||||
{
|
||||
if (m_cb_get_label)
|
||||
return m_cb_get_label(pos);
|
||||
|
||||
if (pos >= m_max_pos || pos < m_min_pos)
|
||||
return "ErrVal";
|
||||
|
||||
return std::to_string(pos);
|
||||
}
|
||||
|
||||
float ImGuiControl::GetPositionInRect(int pos, const ImRect& rect) const
|
||||
{
|
||||
int v_min = m_min_pos;
|
||||
int v_max = m_max_pos;
|
||||
|
||||
float pos_ratio = (v_max - v_min) != 0 ? ((float)(pos - v_min) / (float)(v_max - v_min)) : 0.0f;
|
||||
float thumb_pos;
|
||||
if (is_horizontal()) {
|
||||
thumb_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio;
|
||||
}
|
||||
else {
|
||||
pos_ratio = 1.0f - pos_ratio;
|
||||
thumb_pos = rect.Min.y + (rect.Max.y - rect.Min.y) * pos_ratio;
|
||||
}
|
||||
return thumb_pos;
|
||||
}
|
||||
|
||||
ImRect ImGuiControl::GetActiveThumbRect() const
|
||||
{
|
||||
return m_selection == ssLower ? m_regions.lower_thumb : m_regions.higher_thumb;
|
||||
}
|
||||
|
||||
void ImGuiControl::draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region)
|
||||
{
|
||||
if (m_cb_draw_scroll_line)
|
||||
m_cb_draw_scroll_line(scroll_line, slideable_region);
|
||||
else
|
||||
ImGui::RenderFrame(scroll_line.Min, scroll_line.Max, thumb_bg_clr, false, m_draw_opts.rounding());
|
||||
}
|
||||
|
||||
void ImGuiControl::draw_background(const ImRect& slideable_region)
|
||||
{
|
||||
ImVec2 groove_sz = m_draw_opts.groove_sz() * 0.55f;
|
||||
auto groove_center = slideable_region.GetCenter();
|
||||
ImRect groove = is_horizontal() ?
|
||||
ImRect(slideable_region.Min.x, groove_center.y - groove_sz.y, slideable_region.Max.x, groove_center.y + groove_sz.y) :
|
||||
ImRect(groove_center.x - groove_sz.x, slideable_region.Min.y, groove_center.x + groove_sz.x, slideable_region.Max.y);
|
||||
ImVec2 groove_padding = (is_horizontal() ? ImVec2(2.0f, 2.0f) : ImVec2(3.0f, 4.0f)) * m_draw_opts.scale;
|
||||
|
||||
ImRect bg_rect = groove;
|
||||
bg_rect.Expand(groove_padding);
|
||||
|
||||
// draw bg of slider
|
||||
ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, border_clr, false, 0.5 * bg_rect.GetWidth());
|
||||
// draw bg of scroll
|
||||
ImGui::RenderFrame(groove.Min, groove.Max, groove_bg_clr, false, 0.5 * groove.GetWidth());
|
||||
}
|
||||
|
||||
void ImGuiControl::draw_label(std::string label, const ImRect& thumb)
|
||||
{
|
||||
if (label.empty() || label == "ErrVal")
|
||||
return;
|
||||
|
||||
const ImVec2 thumb_center = thumb.GetCenter();
|
||||
ImVec2 text_padding = m_draw_opts.text_padding();
|
||||
float rounding = m_draw_opts.rounding();
|
||||
ImVec2 triangle_offsets[3] = { ImVec2(2.0f, 0.0f) * m_draw_opts.scale, ImVec2(0.0f, 8.0f) * m_draw_opts.scale, ImVec2(9.0f, 0.0f) * m_draw_opts.scale };
|
||||
|
||||
ImVec2 text_content_size = ImGui::CalcTextSize(label.c_str());
|
||||
ImVec2 text_size = text_content_size + text_padding * 2;
|
||||
ImVec2 text_start = is_horizontal() ?
|
||||
ImVec2(thumb.Max.x + triangle_offsets[2].x, thumb_center.y - text_size.y) :
|
||||
ImVec2(thumb.Min.x - text_size.x - triangle_offsets[2].x, thumb_center.y - text_size.y) ;
|
||||
ImRect text_rect(text_start, text_start + text_size);
|
||||
|
||||
ImVec2 pos_1 = is_horizontal() ?
|
||||
ImVec2(text_rect.Min.x + triangle_offsets[0].x, text_rect.Max.y - triangle_offsets[0].y) :
|
||||
text_rect.Max - triangle_offsets[0];
|
||||
ImVec2 pos_2 = is_horizontal() ? pos_1 - triangle_offsets[2] : pos_1 - triangle_offsets[1];
|
||||
ImVec2 pos_3 = is_horizontal() ? pos_1 - triangle_offsets[1] : pos_1 + triangle_offsets[2];
|
||||
|
||||
ImGui::RenderFrame(text_rect.Min, text_rect.Max, tooltip_bg_clr, true, rounding);
|
||||
ImGui::GetCurrentWindow()->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, tooltip_bg_clr);
|
||||
ImGui::RenderText(text_start + text_padding, label.c_str());
|
||||
};
|
||||
|
||||
void ImGuiControl::draw_thumb(const ImVec2& center, bool mark/* = false*/)
|
||||
{
|
||||
const float line_width = 1.5f * m_draw_opts.scale;
|
||||
const float radius = m_draw_opts.thumb_radius();
|
||||
const float line_offset = 0.5f * radius;
|
||||
|
||||
const float hexagon_angle = is_horizontal() ? 0.f : IM_PI * 0.5f;
|
||||
|
||||
ImGuiPureWrap::draw_hexagon(center, radius, border_clr, hexagon_angle);
|
||||
ImGuiPureWrap::draw_hexagon(center, radius - line_width, thumb_bg_clr, hexagon_angle);
|
||||
|
||||
if (mark) {
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
window->DrawList->AddLine(center + ImVec2(-line_offset, 0.0f), center + ImVec2(line_offset, 0.0f), border_clr, line_width);
|
||||
window->DrawList->AddLine(center + ImVec2(0.0f, -line_offset), center + ImVec2(0.0f, line_offset), border_clr, line_width);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiControl::apply_regions(int higher_pos, int lower_pos, const ImRect& draggable_region)
|
||||
{
|
||||
ImVec2 mid = draggable_region.GetCenter();
|
||||
float thumb_radius = m_draw_opts.thumb_radius();
|
||||
|
||||
// set slideable region
|
||||
m_regions.higher_slideable_region = is_horizontal() ?
|
||||
ImRect(draggable_region.Min + ImVec2(m_draw_lower_thumb ? thumb_radius : 0, 0), draggable_region.Max) :
|
||||
ImRect(draggable_region.Min, draggable_region.Max - ImVec2(0, m_combine_thumbs ? 0 : thumb_radius));
|
||||
m_regions.lower_slideable_region = is_horizontal() ?
|
||||
ImRect(draggable_region.Min, draggable_region.Max - ImVec2(thumb_radius, 0)) :
|
||||
ImRect(draggable_region.Min + ImVec2(0, thumb_radius), draggable_region.Max);
|
||||
|
||||
// initialize the thumbs.
|
||||
float higher_thumb_pos = GetPositionInRect(higher_pos, m_regions.higher_slideable_region);
|
||||
m_regions.higher_thumb = is_horizontal() ?
|
||||
ImRect(higher_thumb_pos - thumb_radius, mid.y - thumb_radius, higher_thumb_pos + thumb_radius, mid.y + thumb_radius) :
|
||||
ImRect(mid.x - thumb_radius, higher_thumb_pos - thumb_radius, mid.x + thumb_radius, higher_thumb_pos + thumb_radius);
|
||||
|
||||
float lower_thumb_pos = GetPositionInRect(lower_pos, m_regions.lower_slideable_region);
|
||||
m_regions.lower_thumb = is_horizontal() ?
|
||||
ImRect(lower_thumb_pos - thumb_radius, mid.y - thumb_radius, lower_thumb_pos + thumb_radius, mid.y + thumb_radius) :
|
||||
ImRect(mid.x - thumb_radius, lower_thumb_pos - thumb_radius, mid.x + thumb_radius, lower_thumb_pos + thumb_radius);
|
||||
}
|
||||
|
||||
void ImGuiControl::check_and_correct_thumbs(int* higher_pos, int* lower_pos)
|
||||
{
|
||||
if (!m_draw_lower_thumb || m_combine_thumbs)
|
||||
return;
|
||||
|
||||
const ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter();
|
||||
const ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter();
|
||||
const float thumb_radius = m_draw_opts.thumb_radius();
|
||||
|
||||
const float higher_thumb_center_pos = is_horizontal() ? higher_thumb_center.x : higher_thumb_center.y;
|
||||
const float lower_thumb_center_pos = is_horizontal() ? lower_thumb_center.x : lower_thumb_center.y;
|
||||
|
||||
if (is_horizontal()) {
|
||||
if (lower_thumb_center_pos + thumb_radius > higher_thumb_center_pos) {
|
||||
if (m_selection == ssHigher) {
|
||||
m_regions.lower_thumb = m_regions.higher_thumb;
|
||||
m_regions.higher_thumb.TranslateX(thumb_radius);
|
||||
*lower_pos = *higher_pos;
|
||||
}
|
||||
else {
|
||||
m_regions.higher_thumb = m_regions.lower_thumb;
|
||||
m_regions.lower_thumb.TranslateX(-thumb_radius);
|
||||
*higher_pos = *lower_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (higher_thumb_center_pos + thumb_radius > lower_thumb_center_pos) {
|
||||
if (m_selection == ssHigher) {
|
||||
m_regions.lower_thumb = m_regions.higher_thumb;
|
||||
m_regions.lower_thumb.TranslateY(thumb_radius);
|
||||
*lower_pos = *higher_pos;
|
||||
}
|
||||
else {
|
||||
m_regions.higher_thumb = m_regions.lower_thumb;
|
||||
m_regions.lower_thumb.TranslateY(-thumb_radius);
|
||||
*higher_pos = *lower_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos,
|
||||
std::string& higher_label, std::string& lower_label,
|
||||
const ImVec2& pos, const ImVec2& size, float scale)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& context = *GImGui;
|
||||
const ImGuiID id = window->GetID(m_name.c_str());
|
||||
|
||||
const ImRect item_size(pos, pos + size);
|
||||
ImGui::ItemSize(item_size);
|
||||
|
||||
// get slider groove size
|
||||
ImRect groove = m_draw_opts.groove(pos, size, is_horizontal());
|
||||
|
||||
// get active(draggable) region.
|
||||
ImRect draggable_region = m_draw_opts.draggable_region(groove, is_horizontal());
|
||||
|
||||
if (ImGui::ItemHoverable(draggable_region, id) && context.IO.MouseDown[0]) {
|
||||
ImGui::SetActiveID(id, window);
|
||||
ImGui::SetFocusID(id, window);
|
||||
ImGui::FocusWindow(window);
|
||||
}
|
||||
|
||||
// set slideable regions and thumbs.
|
||||
apply_regions(*higher_pos, *lower_pos, draggable_region);
|
||||
|
||||
// select and mark higher thumb by default
|
||||
if (m_selection == ssUndef)
|
||||
m_selection = ssHigher;
|
||||
|
||||
// Processing interacting
|
||||
|
||||
if (ImGui::ItemHoverable(m_regions.higher_thumb, id) && context.IO.MouseClicked[0])
|
||||
m_selection = ssHigher;
|
||||
|
||||
if (m_draw_lower_thumb && !m_combine_thumbs &&
|
||||
ImGui::ItemHoverable(m_regions.lower_thumb, id) && context.IO.MouseClicked[0])
|
||||
m_selection = ssLower;
|
||||
|
||||
// update thumb position
|
||||
bool pos_changed = false;
|
||||
if (m_selection == ssHigher) {
|
||||
pos_changed = behavior(id, m_regions.higher_slideable_region, m_min_pos, m_max_pos,
|
||||
higher_pos, &m_regions.higher_thumb, m_flags);
|
||||
}
|
||||
else if (m_draw_lower_thumb && !m_combine_thumbs) {
|
||||
pos_changed = behavior(id, m_regions.lower_slideable_region, m_min_pos, m_max_pos,
|
||||
lower_pos, &m_regions.lower_thumb, m_flags);
|
||||
}
|
||||
|
||||
// check thumbs poss and correct them if needed
|
||||
check_and_correct_thumbs(higher_pos, lower_pos);
|
||||
|
||||
const ImRect& slideable_region = m_selection == ssHigher ? m_regions.higher_slideable_region : m_regions.lower_slideable_region;
|
||||
const ImRect& active_thumb = m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb;
|
||||
|
||||
bool show_move_label = false;
|
||||
ImRect mouse_pos_rc = active_thumb;
|
||||
if (!pos_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) {
|
||||
behavior(id, slideable_region, m_min_pos, m_max_pos,
|
||||
&m_mouse_pos, &mouse_pos_rc, m_flags, true);
|
||||
show_move_label = true;
|
||||
}
|
||||
|
||||
// detect right click on selected thumb
|
||||
if (ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[1])
|
||||
m_rclick_on_selected_thumb = true;
|
||||
if ((!ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[1]) ||
|
||||
context.IO.MouseClicked[0])
|
||||
m_rclick_on_selected_thumb = false;
|
||||
|
||||
// render slider
|
||||
|
||||
ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter();
|
||||
ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter();
|
||||
|
||||
ImRect scroll_line = m_draw_opts.slider_line(slideable_region, higher_thumb_center, lower_thumb_center, is_horizontal());
|
||||
|
||||
if (m_cb_extra_draw)
|
||||
m_cb_extra_draw(slideable_region);
|
||||
|
||||
// draw background
|
||||
draw_background(slideable_region);
|
||||
// draw scroll line
|
||||
draw_scroll_line(m_combine_thumbs ? groove : scroll_line, slideable_region);
|
||||
|
||||
// draw thumbs with label
|
||||
draw_thumb(higher_thumb_center, m_selection == ssHigher && m_draw_lower_thumb);
|
||||
draw_label(higher_label, m_regions.higher_thumb);
|
||||
|
||||
if (m_draw_lower_thumb && !m_combine_thumbs) {
|
||||
draw_thumb(lower_thumb_center, m_selection == ssLower);
|
||||
draw_label(lower_label, m_regions.lower_thumb);
|
||||
}
|
||||
|
||||
// draw label on mouse move
|
||||
if (show_move_label)
|
||||
draw_label(get_label_on_move(m_mouse_pos), mouse_pos_rc);
|
||||
|
||||
return pos_changed;
|
||||
}
|
||||
|
||||
bool ImGuiControl::render()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
|
||||
int windows_flag = ImGuiWindowFlags_NoTitleBar
|
||||
| ImGuiWindowFlags_NoCollapse
|
||||
| ImGuiWindowFlags_NoMove
|
||||
| ImGuiWindowFlags_NoResize
|
||||
| ImGuiWindowFlags_NoScrollbar
|
||||
| ImGuiWindowFlags_NoScrollWithMouse;
|
||||
|
||||
ImGuiPureWrap::set_next_window_pos(m_pos.x, m_pos.y, ImGuiCond_Always);
|
||||
ImGuiPureWrap::begin(m_name, windows_flag);
|
||||
|
||||
float scale = 1.f;
|
||||
|
||||
int higher_pos = m_higher_pos;
|
||||
int lower_pos = m_lower_pos;
|
||||
std::string higher_label = get_label(m_higher_pos);
|
||||
std::string lower_label = get_label(m_lower_pos);
|
||||
int temp_higher_pos = m_higher_pos;
|
||||
int temp_lower_pos = m_lower_pos;
|
||||
|
||||
if (draw_slider(&higher_pos, &lower_pos, higher_label, lower_label, m_pos, m_size, scale)) {
|
||||
if (temp_higher_pos != higher_pos) {
|
||||
m_higher_pos = higher_pos;
|
||||
if (m_combine_thumbs)
|
||||
m_lower_pos = m_higher_pos;
|
||||
}
|
||||
if (temp_lower_pos != lower_pos)
|
||||
m_lower_pos = lower_pos;
|
||||
result = true;
|
||||
}
|
||||
|
||||
ImGuiPureWrap::end();
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // DoubleSlider
|
||||
|
287
src/slic3r/GUI/ImGuiDoubleSlider.hpp
Normal file
@ -0,0 +1,287 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_ImGUI_DoubleSlider_hpp_
|
||||
#define slic3r_ImGUI_DoubleSlider_hpp_
|
||||
|
||||
#include "ImGuiPureWrap.hpp"
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
// this code is borrowed from https://stackoverflow.com/questions/16605967/set-precision-of-stdto-string-when-converting-floating-point-values
|
||||
template <typename T>
|
||||
std::string to_string_with_precision(const T a_value, const int n = 2)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out.precision(n);
|
||||
out << std::fixed << a_value;
|
||||
return std::move(out).str();
|
||||
}
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
enum SelectedSlider {
|
||||
ssUndef,
|
||||
ssLower,
|
||||
ssHigher
|
||||
};
|
||||
|
||||
class ImGuiControl
|
||||
{
|
||||
public:
|
||||
ImGuiControl(int lowerValue,
|
||||
int higherValue,
|
||||
int minValue,
|
||||
int maxValue,
|
||||
ImGuiSliderFlags flags = ImGuiSliderFlags_None,
|
||||
std::string name = "d_slider",
|
||||
bool use_lower_thumb = true);
|
||||
ImGuiControl() {}
|
||||
~ImGuiControl() {}
|
||||
|
||||
int GetMinPos() const { return m_min_pos; }
|
||||
int GetMaxPos() const { return m_max_pos; }
|
||||
int GetLowerPos() const { return m_lower_pos; }
|
||||
int GetHigherPos() const { return m_higher_pos; }
|
||||
int GetActivePos() const;
|
||||
|
||||
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
|
||||
void SetLowerPos (const int lower_pos);
|
||||
void SetHigherPos(const int higher_pos);
|
||||
void SetSelectionSpan(const int lower_pos, const int higher_pos);
|
||||
|
||||
void SetMaxPos(const int max_pos);
|
||||
void CombineThumbs(bool combine);
|
||||
void ResetPositions();
|
||||
|
||||
void SetCtrlPos(ImVec2 pos) { m_pos = pos; }
|
||||
void SetCtrlSize(ImVec2 size) { m_size = size; }
|
||||
void SetCtrlScale(float scale) { m_draw_opts.scale = scale; }
|
||||
void Init(const ImVec2& pos, const ImVec2& size, float scale) {
|
||||
m_pos = pos;
|
||||
m_size = size;
|
||||
m_draw_opts.scale = scale;
|
||||
}
|
||||
ImVec2 GetCtrlSize() { return m_size; }
|
||||
ImVec2 GetCtrlPos() { return m_pos; }
|
||||
|
||||
void Show(bool show) { m_is_shown = show; }
|
||||
void Hide() { m_is_shown = false; }
|
||||
bool IsShown() const { return m_is_shown; }
|
||||
bool IsCombineThumbs() const { return m_combine_thumbs; }
|
||||
bool IsActiveHigherThumb() const { return m_selection == ssHigher; }
|
||||
void MoveActiveThumb(int delta);
|
||||
void ShowLowerThumb(bool show) { m_draw_lower_thumb = show; }
|
||||
|
||||
void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; }
|
||||
ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); }
|
||||
float GetPositionInRect(int pos, const ImRect& rect) const;
|
||||
ImRect GetActiveThumbRect() const;
|
||||
|
||||
bool IsRClickOnThumb() const { return m_rclick_on_selected_thumb; }
|
||||
|
||||
bool is_horizontal() const { return !(m_flags & ImGuiSliderFlags_Vertical); }
|
||||
bool render();
|
||||
|
||||
std::string get_label(int pos) const;
|
||||
|
||||
void set_get_label_on_move_cb(std::function<std::string(int)> cb) { m_cb_get_label_on_move = cb; }
|
||||
void set_get_label_cb(std::function<std::string(int)> cb) { m_cb_get_label = cb; }
|
||||
void set_draw_scroll_line_cb(std::function<void(const ImRect&, const ImRect&)> cb) { m_cb_draw_scroll_line = cb; }
|
||||
void set_extra_draw_cb(std::function<void(const ImRect&)> cb) { m_cb_extra_draw = cb; }
|
||||
|
||||
private:
|
||||
|
||||
struct DrawOptions {
|
||||
float scale { 1.f }; // used for Retina on osx
|
||||
|
||||
ImVec2 dummy_sz() const { return ImVec2(24.0f, 22.0f) * scale; }
|
||||
ImVec2 thumb_dummy_sz() const { return ImVec2(17.0f, 17.0f) * scale; }
|
||||
ImVec2 groove_sz() const { return ImVec2(4.0f, 4.0f) * scale; }
|
||||
ImVec2 draggable_region_sz()const { return ImVec2(20.0f, 19.0f) * scale; }
|
||||
ImVec2 text_dummy_sz() const { return ImVec2(50.0f, 34.0f) * scale; }
|
||||
ImVec2 text_padding() const { return ImVec2( 5.0f, 2.0f) * scale; }
|
||||
|
||||
float thumb_radius() const { return 10.0f * scale; }
|
||||
float thumb_border() const { return 2.0f * scale; }
|
||||
float rounding() const { return 2.0f * scale; }
|
||||
|
||||
ImRect groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const;
|
||||
ImRect draggable_region(const ImRect& groove, bool is_horizontal) const;
|
||||
ImRect slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) const;
|
||||
};
|
||||
|
||||
struct Regions {
|
||||
ImRect higher_slideable_region;
|
||||
ImRect lower_slideable_region;
|
||||
ImRect higher_thumb;
|
||||
ImRect lower_thumb;
|
||||
};
|
||||
|
||||
SelectedSlider m_selection;
|
||||
ImVec2 m_pos;
|
||||
ImVec2 m_size;
|
||||
std::string m_name;
|
||||
ImGuiSliderFlags m_flags{ ImGuiSliderFlags_None };
|
||||
bool m_is_shown{ true };
|
||||
|
||||
int m_min_pos;
|
||||
int m_max_pos;
|
||||
int m_lower_pos;
|
||||
int m_higher_pos;
|
||||
// slider's position of the mouse cursor
|
||||
int m_mouse_pos;
|
||||
|
||||
bool m_rclick_on_selected_thumb{ false };
|
||||
|
||||
bool m_draw_lower_thumb{ true };
|
||||
bool m_combine_thumbs { false };
|
||||
bool m_show_move_label { false };
|
||||
|
||||
DrawOptions m_draw_opts;
|
||||
Regions m_regions;
|
||||
|
||||
std::function<std::string(int)> m_cb_get_label { nullptr };
|
||||
std::function<std::string(int)> m_cb_get_label_on_move { nullptr };
|
||||
std::function<void(const ImRect&, const ImRect&)> m_cb_draw_scroll_line { nullptr };
|
||||
std::function<void(const ImRect&)> m_cb_extra_draw { nullptr };
|
||||
|
||||
void correct_lower_pos();
|
||||
void correct_higher_pos();
|
||||
std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); }
|
||||
|
||||
void apply_regions(int higher_pos, int lower_pos, const ImRect& draggable_region);
|
||||
void check_and_correct_thumbs(int* higher_pos, int* lower_pos);
|
||||
|
||||
void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region);
|
||||
void draw_background(const ImRect& slideable_region);
|
||||
void draw_label(std::string label, const ImRect& thumb);
|
||||
void draw_thumb(const ImVec2& center, bool mark = false);
|
||||
bool draw_slider(int* higher_pos, int* lower_pos,
|
||||
std::string& higher_label, std::string& lower_label,
|
||||
const ImVec2& pos, const ImVec2& size, float scale = 1.0f);
|
||||
};
|
||||
|
||||
// VatType = a typ of values, related to the each position in slider
|
||||
template<typename ValType>
|
||||
class Manager
|
||||
{
|
||||
public:
|
||||
|
||||
void Init( int lowerPos,
|
||||
int higherPos,
|
||||
int minPos,
|
||||
int maxPos,
|
||||
const std::string& name,
|
||||
bool is_horizontal)
|
||||
{
|
||||
m_ctrl = ImGuiControl( lowerPos, higherPos,
|
||||
minPos, maxPos,
|
||||
is_horizontal ? 0 : ImGuiSliderFlags_Vertical,
|
||||
name, !is_horizontal);
|
||||
|
||||
m_ctrl.set_get_label_cb([this](int pos) {return get_label(pos); });
|
||||
};
|
||||
|
||||
Manager() {}
|
||||
Manager(int lowerPos,
|
||||
int higherPos,
|
||||
int minPos,
|
||||
int maxPos,
|
||||
const std::string& name,
|
||||
bool is_horizontal)
|
||||
{
|
||||
Init (lowerPos, higherPos, minPos, maxPos, name, is_horizontal);
|
||||
}
|
||||
~Manager() {}
|
||||
|
||||
int GetMinPos() const { return m_ctrl.GetMinPos(); }
|
||||
int GetMaxPos() const { return m_ctrl.GetMaxPos(); }
|
||||
int GetLowerPos() const { return m_ctrl.GetLowerPos(); }
|
||||
int GetHigherPos()const { return m_ctrl.GetHigherPos(); }
|
||||
|
||||
ValType GetMinValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetMinPos()]; }
|
||||
ValType GetMaxValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetMaxPos()]; }
|
||||
ValType GetLowerValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetLowerPos()];}
|
||||
ValType GetHigherValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetHigherPos()]; }
|
||||
|
||||
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
|
||||
void SetLowerPos(const int lower_pos) {
|
||||
m_ctrl.SetLowerPos(lower_pos);
|
||||
process_thumb_move();
|
||||
}
|
||||
void SetHigherPos(const int higher_pos) {
|
||||
m_ctrl.SetHigherPos(higher_pos);
|
||||
process_thumb_move();
|
||||
}
|
||||
void SetSelectionSpan(const int lower_pos, const int higher_pos) {
|
||||
m_ctrl.SetSelectionSpan(lower_pos, higher_pos);
|
||||
process_thumb_move();
|
||||
}
|
||||
void SetMaxPos(const int max_pos) {
|
||||
m_ctrl.SetMaxPos(max_pos);
|
||||
process_thumb_move();
|
||||
}
|
||||
|
||||
void SetSliderValues(const std::vector<ValType>& values) { m_values = values; }
|
||||
// values used to show thumb labels
|
||||
void SetSliderAlternateValues(const std::vector<ValType>& values) { m_alternate_values = values; }
|
||||
|
||||
bool IsLowerAtMin() const { return m_ctrl.GetLowerPos() == m_ctrl.GetMinPos(); }
|
||||
bool IsHigherAtMax() const { return m_ctrl.GetHigherPos() == m_ctrl.GetMaxPos(); }
|
||||
|
||||
void Show(bool show = true) { m_ctrl.Show(show); }
|
||||
void Hide() { m_ctrl.Show(false); }
|
||||
bool IsShown() { return m_ctrl.IsShown(); }
|
||||
void SetEmUnit(int em_unit) { m_em = em_unit; }
|
||||
void ShowLowerThumb(bool show) { m_ctrl.ShowLowerThumb(show); }
|
||||
|
||||
float GetWidth() { return m_ctrl.GetCtrlSize().x; }
|
||||
float GetHeight() { return m_ctrl.GetCtrlSize().y; }
|
||||
virtual void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) = 0;
|
||||
|
||||
void set_callback_on_thumb_move(std::function<void()> cb) { m_cb_thumb_move = cb; };
|
||||
|
||||
void move_current_thumb(const int delta)
|
||||
{
|
||||
m_ctrl.MoveActiveThumb(delta);
|
||||
process_thumb_move();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<ValType> m_values;
|
||||
std::vector<ValType> m_alternate_values;
|
||||
|
||||
ImGuiControl m_ctrl;
|
||||
int m_em{ 10 };
|
||||
float m_scale{ 1.f };
|
||||
|
||||
virtual std::string get_label(int pos) const {
|
||||
if (m_values.empty())
|
||||
return std::to_string(pos);
|
||||
if (pos >= int(m_values.size()))
|
||||
return "ErrVal";
|
||||
return to_string_with_precision(static_cast<ValType>(m_alternate_values.empty() ? m_values[pos] : m_alternate_values[pos]));
|
||||
}
|
||||
|
||||
void process_thumb_move() {
|
||||
if (m_cb_thumb_move)
|
||||
m_cb_thumb_move();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::function<void()> m_cb_thumb_move{ nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // DoubleSlider
|
||||
|
||||
|
||||
#endif // slic3r_ImGUI_DoubleSlider_hpp_
|
@ -296,6 +296,20 @@ bool combo(const std::string& label, const std::vector<std::string>& options, in
|
||||
return res;
|
||||
}
|
||||
|
||||
void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
|
||||
float a_min = start_angle;
|
||||
float a_max = start_angle + 2.f * IM_PI;
|
||||
|
||||
window->DrawList->PathArcTo(center, radius, a_min, a_max, 6);
|
||||
window->DrawList->PathFillConvex(col);
|
||||
}
|
||||
|
||||
// Scroll up for one item
|
||||
void scroll_up()
|
||||
{
|
||||
@ -603,4 +617,232 @@ bool is_chars_in_ranges(const ImWchar *ranges,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool begin_menu(const char* label, bool enabled)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems) return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
bool menu_is_open = ImGui::IsPopupOpen(id, ImGuiPopupFlags_None);
|
||||
|
||||
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
|
||||
if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow;
|
||||
|
||||
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
|
||||
// We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.
|
||||
// If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.
|
||||
if (g.MenusIdSubmittedThisFrame.contains(id)) {
|
||||
if (menu_is_open)
|
||||
menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
else
|
||||
g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values
|
||||
return menu_is_open;
|
||||
}
|
||||
|
||||
// Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
|
||||
g.MenusIdSubmittedThisFrame.push_back(id);
|
||||
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
|
||||
bool pressed;
|
||||
bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) &&
|
||||
(g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back());
|
||||
ImGuiWindow* backed_nav_window = g.NavWindow;
|
||||
if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
|
||||
|
||||
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
|
||||
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
|
||||
// e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
|
||||
ImVec2 popup_pos, pos = window->DC.CursorPos;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) {
|
||||
// Menu inside an horizontal menu bar
|
||||
// Selectable extend their highlight by half ItemSpacing in each direction.
|
||||
// For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
|
||||
popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
float w = label_size.x;
|
||||
pressed = /*selectable*/ImGui::Selectable(label, menu_is_open,
|
||||
ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups |
|
||||
(!enabled ? ImGuiSelectableFlags_Disabled : 0),
|
||||
ImVec2(w, 0.0f));
|
||||
ImGui::PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_FLOOR(
|
||||
style.ItemSpacing.x *
|
||||
(-1.0f +
|
||||
0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else {
|
||||
// Menu inside a menu
|
||||
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
|
||||
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
||||
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
|
||||
float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame
|
||||
float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - min_w);
|
||||
pressed = /*selectable*/ImGui::Selectable(label, menu_is_open,
|
||||
ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups |
|
||||
ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0),
|
||||
ImVec2(min_w, 0.0f));
|
||||
ImU32 text_col = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
|
||||
ImGui::RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right);
|
||||
}
|
||||
|
||||
const bool hovered = enabled && ImGui::ItemHoverable(window->DC.LastItemRect, id);
|
||||
if (menuset_is_open) g.NavWindow = backed_nav_window;
|
||||
|
||||
bool want_open = false;
|
||||
bool want_close = false;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
|
||||
{
|
||||
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
|
||||
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
|
||||
bool moving_toward_other_child_menu = false;
|
||||
|
||||
ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ?
|
||||
g.OpenPopupStack[g.BeginPopupStack.Size].Window :
|
||||
NULL;
|
||||
if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) {
|
||||
// FIXME-DPI: Values should be derived from a master "scale" factor.
|
||||
ImRect next_window_rect = child_menu_window->Rect();
|
||||
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
|
||||
ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
|
||||
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
|
||||
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
|
||||
ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
|
||||
tb.y = ta.y +
|
||||
ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
|
||||
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
|
||||
moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
|
||||
// GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
|
||||
}
|
||||
if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
|
||||
want_close = true;
|
||||
|
||||
if (!menu_is_open && hovered && pressed) // Click to open
|
||||
want_open = true;
|
||||
else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
|
||||
want_open = true;
|
||||
|
||||
if (g.NavActivateId == id) {
|
||||
want_close = menu_is_open;
|
||||
want_open = !menu_is_open;
|
||||
}
|
||||
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
|
||||
{
|
||||
want_open = true;
|
||||
ImGui::NavMoveRequestCancel();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Menu bar
|
||||
if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
|
||||
{
|
||||
want_close = true;
|
||||
want_open = menu_is_open = false;
|
||||
}
|
||||
else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
|
||||
{
|
||||
want_open = true;
|
||||
}
|
||||
else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
|
||||
{
|
||||
want_open = true;
|
||||
ImGui::NavMoveRequestCancel();
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
|
||||
want_close = true;
|
||||
if (want_close && ImGui::IsPopupOpen(id, ImGuiPopupFlags_None)) ImGui::ClosePopupToLevel(g.BeginPopupStack.Size, true);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
|
||||
if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) {
|
||||
// Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
|
||||
ImGui::OpenPopup(label);
|
||||
return false;
|
||||
}
|
||||
|
||||
menu_is_open |= want_open;
|
||||
if (want_open) ImGui::OpenPopup(label);
|
||||
|
||||
if (menu_is_open) {
|
||||
ImGui::SetNextWindowPos(popup_pos,
|
||||
ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
|
||||
menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
}
|
||||
else {
|
||||
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
|
||||
}
|
||||
|
||||
return menu_is_open;
|
||||
}
|
||||
|
||||
void end_menu()
|
||||
{
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems) return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiStyle& style = g.Style;
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
|
||||
|
||||
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
|
||||
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
|
||||
ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
|
||||
bool pressed;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) {
|
||||
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
|
||||
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
|
||||
float w = label_size.x;
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
pressed = ImGui::Selectable(label, selected, flags, ImVec2(w, 0.0f));
|
||||
ImGui::PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_FLOOR(
|
||||
style.ItemSpacing.x *
|
||||
(-1.0f +
|
||||
0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else {
|
||||
// Menu item inside a vertical menu
|
||||
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
|
||||
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
||||
float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f;
|
||||
float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame
|
||||
float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w);
|
||||
pressed = /*selectable*/ImGui::Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
|
||||
|
||||
if (icon_size.x != 0 && icon_size.y != 0) {
|
||||
float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y;
|
||||
float icon_pos_y = selectable_pos_y + (label_size.y + style.ItemSpacing.y - icon_size.y) / 2;
|
||||
float icon_pos_x = pos.x + window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f;
|
||||
ImVec2 icon_pos = ImVec2(icon_pos_x, icon_pos_y);
|
||||
ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color);
|
||||
}
|
||||
|
||||
if (shortcut_w > 0.0f) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
|
||||
ImGui::RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f),
|
||||
ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
|
||||
}
|
||||
}
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
}
|
||||
|
||||
} // ImGuiPureWrap
|
||||
|
@ -53,6 +53,8 @@ namespace ImGuiPureWrap
|
||||
// Use selection = -1 to not mark any option as selected
|
||||
bool combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
|
||||
|
||||
void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle = 0.f);
|
||||
|
||||
void text(const char* label);
|
||||
void text(const std::string& label);
|
||||
void text(const std::wstring& label);
|
||||
@ -146,6 +148,10 @@ namespace ImGuiPureWrap
|
||||
bool is_chars_in_ranges(const ImWchar *ranges, const char *chars_ptr);
|
||||
bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
|
||||
|
||||
bool begin_menu(const char* label, bool enabled = true);
|
||||
void end_menu();
|
||||
bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true);
|
||||
|
||||
const ImVec4 COL_GREY_DARK = { 0.33f, 0.33f, 0.33f, 1.0f };
|
||||
const ImVec4 COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f };
|
||||
const ImVec4 COL_ORANGE_DARK = { 0.67f, 0.36f, 0.19f, 1.0f };
|
||||
|
@ -116,6 +116,26 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||
{ImGui::SlaViewProcessed , "sla_view_processed" },
|
||||
};
|
||||
|
||||
static const std::map<const wchar_t, std::string> font_icons_medium = {
|
||||
{ImGui::Lock , "lock_closed" },
|
||||
{ImGui::LockHovered , "lock_closed_f" },
|
||||
{ImGui::Unlock , "lock_open" },
|
||||
{ImGui::UnlockHovered , "lock_open_f" },
|
||||
{ImGui::DSRevert , "undo" },
|
||||
{ImGui::DSRevertHovered , "undo_f" },
|
||||
{ImGui::DSSettings , "cog" },
|
||||
{ImGui::DSSettingsHovered , "cog_f" },
|
||||
|
||||
{ImGui::ErrorTick , "error_tick" },
|
||||
{ImGui::ErrorTickHovered , "error_tick_f" },
|
||||
{ImGui::PausePrint , "pause_print" },
|
||||
{ImGui::PausePrintHovered , "pause_print_f" },
|
||||
{ImGui::EditGCode , "edit_gcode" },
|
||||
{ImGui::EditGCodeHovered , "edit_gcode_f" },
|
||||
{ImGui::RemoveTick , "colorchange_del" },
|
||||
{ImGui::RemoveTickHovered , "colorchange_del_f" },
|
||||
};
|
||||
|
||||
static const std::map<const wchar_t, std::string> font_icons_extra_large = {
|
||||
{ImGui::ClippyMarker , "notification_clippy" },
|
||||
};
|
||||
@ -470,7 +490,7 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl
|
||||
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip)
|
||||
bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip, bool highlight_on_hover/* = true*/)
|
||||
{
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const ImTextureID tex_id = io.Fonts->TexID;
|
||||
@ -481,9 +501,9 @@ bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip)
|
||||
const ImVec2 size = { float(rect->Width), float(rect->Height) };
|
||||
const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h);
|
||||
const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, highlight_on_hover ? 1.0f : 0.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, highlight_on_hover ? 1.0f : 0.0f });
|
||||
const bool res = ImGuiPureWrap::image_button(tex_id, size, uv0, uv1);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
@ -1125,6 +1145,11 @@ void ImGuiWrapper::init_font(bool compress)
|
||||
m_custom_glyph_rects_ids[icon.first] =
|
||||
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
|
||||
}
|
||||
const int icon_sz_m = int(1.25 * icon_sz); // default size of medium icon is 20 px
|
||||
for (auto& icon : font_icons_medium) {
|
||||
m_custom_glyph_rects_ids[icon.first] =
|
||||
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz_m, icon_sz_m, 3.0 * font_scale + icon_sz_m);
|
||||
}
|
||||
for (auto& icon : font_icons_large) {
|
||||
m_custom_glyph_rects_ids[icon.first] =
|
||||
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
|
||||
@ -1161,6 +1186,10 @@ void ImGuiWrapper::init_font(bool compress)
|
||||
load_icon_from_svg(icon, icon_sz);
|
||||
}
|
||||
|
||||
for (auto icon : font_icons_medium) {
|
||||
load_icon_from_svg(icon, icon_sz_m);
|
||||
}
|
||||
|
||||
icon_sz *= 2; // default size of large icon is 32 px
|
||||
for (auto icon : font_icons_large) {
|
||||
load_icon_from_svg(icon, icon_sz);
|
||||
@ -1524,5 +1553,6 @@ void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text)
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
@ -22,6 +22,7 @@ struct OptionViewParameters;
|
||||
class wxString;
|
||||
class wxMouseEvent;
|
||||
class wxKeyEvent;
|
||||
struct ImRect;
|
||||
|
||||
struct IMGUI_API ImGuiWindow;
|
||||
|
||||
@ -85,8 +86,7 @@ public:
|
||||
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true);
|
||||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true);
|
||||
|
||||
bool image_button(const wchar_t icon, const std::string& tooltip = {});
|
||||
|
||||
bool image_button(const wchar_t icon, const std::string& tooltip = {}, bool highlight_on_hover = true);
|
||||
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
|
||||
Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized);
|
||||
|
||||
|
@ -174,6 +174,14 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
|
||||
m_top_y = initial_y + m_window_height;
|
||||
|
||||
ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - right_gap, 1.0f * (float)cnv_size.get_height() - m_top_y);
|
||||
|
||||
if (wxGetApp().plater()->is_preview_shown()) {
|
||||
if (Preview* preview = dynamic_cast<Preview*>(canvas.get_wxglcanvas()->GetParent())) {
|
||||
win_pos.y -= 1.5f * preview->get_moves_slider_height();
|
||||
win_pos.x -= preview->get_layers_slider_width();
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiPureWrap::set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
|
||||
ImGuiPureWrap::set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
|
||||
|
||||
|
@ -385,6 +385,7 @@ struct Plater::priv
|
||||
|
||||
void set_current_canvas_as_dirty();
|
||||
GLCanvas3D* get_current_canvas3D();
|
||||
void render_sliders(GLCanvas3D& canvas);
|
||||
void unbind_canvas_event_handlers();
|
||||
void reset_canvas_volumes();
|
||||
|
||||
@ -735,12 +736,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); });
|
||||
}
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_JUMP_TO, [this](wxKeyEvent& evt) { preview->jump_layers_slider(evt); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_SLIDERS, [this](wxKeyEvent& evt) {
|
||||
preview->move_layers_slider(evt);
|
||||
preview->move_moves_slider(evt);
|
||||
});
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); });
|
||||
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
preview->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->q->reload_gcode_from_disk(); });
|
||||
|
||||
@ -3189,6 +3185,12 @@ GLCanvas3D* Plater::priv::get_current_canvas3D()
|
||||
return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr);
|
||||
}
|
||||
|
||||
void Plater::priv::render_sliders(GLCanvas3D& canvas)
|
||||
{
|
||||
if (current_panel == preview)
|
||||
preview->render_sliders(canvas);
|
||||
}
|
||||
|
||||
void Plater::priv::unbind_canvas_event_handlers()
|
||||
{
|
||||
if (view3D != nullptr)
|
||||
@ -6402,6 +6404,11 @@ GLCanvas3D* Plater::get_current_canvas3D()
|
||||
return p->get_current_canvas3D();
|
||||
}
|
||||
|
||||
void Plater::render_sliders(GLCanvas3D& canvas)
|
||||
{
|
||||
p->render_sliders(canvas);
|
||||
}
|
||||
|
||||
static std::string concat_strings(const std::set<std::string> &strings,
|
||||
const std::string &delim = "\n")
|
||||
{
|
||||
@ -6686,7 +6693,6 @@ void Plater::msw_rescale()
|
||||
|
||||
void Plater::sys_color_changed()
|
||||
{
|
||||
p->preview->sys_color_changed();
|
||||
p->sidebar->sys_color_changed();
|
||||
|
||||
p->menus.sys_color_changed();
|
||||
|
@ -271,6 +271,8 @@ public:
|
||||
GLCanvas3D* canvas3D();
|
||||
const GLCanvas3D * canvas3D() const;
|
||||
GLCanvas3D* get_current_canvas3D();
|
||||
|
||||
void render_sliders(GLCanvas3D& canvas);
|
||||
|
||||
void arrange();
|
||||
void arrange(Worker &w, bool selected);
|
||||
|
704
src/slic3r/GUI/TickCodesManager.cpp
Normal file
@ -0,0 +1,704 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "TickCodesManager.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Color.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace CustomGCode;
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2
|
||||
constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2
|
||||
|
||||
static const int YES = 0x00000002; // an analogue of wxYES
|
||||
static const int NO = 0x00000008; // an analogue of wxNO
|
||||
static const int CANCEL = 0x00000010; // an analogue of wxCANCEL
|
||||
|
||||
bool equivalent_areas(const double& bottom_area, const double& top_area)
|
||||
{
|
||||
return fabs(bottom_area - top_area) <= miscalculation;
|
||||
}
|
||||
|
||||
TickCodeManager::TickCodeManager()
|
||||
{
|
||||
m_pause_print_msg = _u8L("Place bearings in slots and resume printing");
|
||||
}
|
||||
|
||||
std::string TickCodeManager::gcode(CustomGCode::Type type)
|
||||
{
|
||||
if (m_print) {
|
||||
const Slic3r::PrintConfig& config = m_print->config();
|
||||
switch (type) {
|
||||
case CustomGCode::ColorChange: return config.color_change_gcode;
|
||||
case CustomGCode::PausePrint: return config.pause_print_gcode;
|
||||
case CustomGCode::Template: return config.template_custom_gcode;
|
||||
default: return std::string();
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
int TickCodeManager::get_tick_from_value(double value, bool force_lower_bound/* = false*/)
|
||||
{
|
||||
if (!m_values)
|
||||
return -1;
|
||||
std::vector<double>::const_iterator it;
|
||||
if (is_wipe_tower && !force_lower_bound)
|
||||
it = std::find_if(m_values->begin(), m_values->end(),
|
||||
[value](const double & val) { return fabs(value - val) <= epsilon(); });
|
||||
else
|
||||
it = std::lower_bound(m_values->begin(), m_values->end(), value - epsilon());
|
||||
|
||||
if (it == m_values->end())
|
||||
return -1;
|
||||
return int(it - m_values->begin());
|
||||
}
|
||||
|
||||
void TickCodeManager::set_ticks(const Info& custom_gcode_per_print_z)
|
||||
{
|
||||
ticks.clear();
|
||||
|
||||
const std::vector<CustomGCode::Item>& heights = custom_gcode_per_print_z.gcodes;
|
||||
for (auto h : heights) {
|
||||
int tick = get_tick_from_value(h.print_z);
|
||||
if (tick >=0)
|
||||
ticks.emplace(TickCode{ tick, h.type, h.extruder, h.color, h.extra });
|
||||
}
|
||||
|
||||
if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty())
|
||||
mode = custom_gcode_per_print_z.mode;
|
||||
}
|
||||
|
||||
// Get active extruders for tick.
|
||||
// Means one current extruder for not existing tick OR
|
||||
// 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick)
|
||||
// Use those values to disable selection of active extruders
|
||||
std::array<int, 2> TickCodeManager::get_active_extruders_for_tick(int tick, Mode main_mode) const
|
||||
{
|
||||
int default_initial_extruder = main_mode == MultiAsSingle ? std::max<int>(1, only_extruder_id) : 1;
|
||||
std::array<int, 2> extruders = { default_initial_extruder, -1 };
|
||||
if (empty())
|
||||
return extruders;
|
||||
|
||||
auto it = ticks.lower_bound(TickCode{tick});
|
||||
|
||||
if (it != ticks.end() && it->tick == tick) // current tick exists
|
||||
extruders[1] = it->extruder;
|
||||
|
||||
while (it != ticks.begin()) {
|
||||
--it;
|
||||
if(it->type == ToolChange) {
|
||||
extruders[0] = it->extruder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return extruders;
|
||||
}
|
||||
|
||||
bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs, std::function<bool(const Layer*)> break_condition)
|
||||
{
|
||||
double prev_area = area(object->get_layer(frst_layer_id)->lslices);
|
||||
|
||||
bool detected = false;
|
||||
for (size_t i = frst_layer_id+1; i < layers_cnt; i++) {
|
||||
const Layer* layer = object->get_layer(i);
|
||||
double cur_area = area(layer->lslices);
|
||||
|
||||
// check for overhangs
|
||||
if (check_overhangs && cur_area > prev_area && !equivalent_areas(prev_area, cur_area))
|
||||
break;
|
||||
|
||||
// Check percent of the area decrease.
|
||||
// This value have to be more than min_delta_area and more then 10%
|
||||
if ((prev_area - cur_area > min_delta_area) && (cur_area / prev_area < 0.9)) {
|
||||
detected = true;
|
||||
if (break_condition(layer))
|
||||
break;
|
||||
}
|
||||
|
||||
prev_area = cur_area;
|
||||
}
|
||||
return detected;
|
||||
}
|
||||
|
||||
bool TickCodeManager::auto_color_change(Mode main_mode)
|
||||
{
|
||||
if (!m_print)
|
||||
return false;
|
||||
|
||||
if (!empty()) {
|
||||
if (m_cb_show_warning_msg) {
|
||||
std::string msg_text = _u8L("This action will cause deletion of all ticks on vertical slider.") + "\n\n" +
|
||||
_u8L("This action is not revertible.\nDo you want to proceed?");
|
||||
if (m_cb_show_warning_msg(msg_text, YES | NO) == NO)
|
||||
return false;
|
||||
}
|
||||
ticks.clear();
|
||||
}
|
||||
|
||||
int extruders_cnt = m_cb_get_extruders_cnt ? m_cb_get_extruders_cnt() : 0;
|
||||
|
||||
for (auto object : m_print->objects()) {
|
||||
// An object should to have at least 2 layers to apply an auto color change
|
||||
if (object->layer_count() < 2)
|
||||
continue;
|
||||
|
||||
check_color_change(object, 1, object->layers().size(), false, [this, extruders_cnt, main_mode](const Layer* layer)
|
||||
{
|
||||
int tick = get_tick_from_value(layer->print_z);
|
||||
if (tick >= 0 && !has_tick(tick)) {
|
||||
if (main_mode == SingleExtruder) {
|
||||
set_default_colors(true);
|
||||
add_tick(tick, ColorChange, 1, layer->print_z);
|
||||
}
|
||||
else {
|
||||
int extruder = 2;
|
||||
if (!empty()) {
|
||||
auto it = ticks.end();
|
||||
it--;
|
||||
extruder = it->extruder + 1;
|
||||
if (extruder > extruders_cnt)
|
||||
extruder = 1;
|
||||
}
|
||||
add_tick(tick, ToolChange, extruder, layer->print_z);
|
||||
}
|
||||
}
|
||||
// allow max 3 auto color changes
|
||||
return ticks.size() > 2;
|
||||
});
|
||||
}
|
||||
|
||||
if (empty() && m_cb_notify_empty_color_change)
|
||||
m_cb_notify_empty_color_change();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string TickCodeManager::get_new_color(const std::string& color)
|
||||
{
|
||||
if (m_cb_get_new_color)
|
||||
return m_cb_get_new_color(color);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string TickCodeManager::get_custom_code(const std::string& code_in, double height)
|
||||
{
|
||||
if (m_cb_get_custom_code)
|
||||
return m_cb_get_custom_code(code_in, height);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string TickCodeManager::get_pause_print_msg(const std::string& msg_in, double height)
|
||||
{
|
||||
if (m_cb_get_pause_print_msg)
|
||||
return m_cb_get_pause_print_msg(msg_in, height);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool TickCodeManager::edit_extruder_sequence(const int max_tick, Mode main_mode)
|
||||
{
|
||||
if (!check_ticks_changed_event(ToolChange, main_mode) || !m_cb_get_extruders_sequence)
|
||||
return false;
|
||||
|
||||
// init extruder sequence in respect to the extruders count
|
||||
if (empty())
|
||||
m_extruders_sequence.init(colors.size());
|
||||
|
||||
if(!m_cb_get_extruders_sequence(m_extruders_sequence))
|
||||
return false;
|
||||
|
||||
erase_all_ticks_with_code(ToolChange);
|
||||
|
||||
const int extr_cnt = m_extruders_sequence.extruders.size();
|
||||
if (extr_cnt == 1)
|
||||
return true;
|
||||
|
||||
int tick = 0;
|
||||
double value = 0.0;
|
||||
int extruder = -1;
|
||||
|
||||
std::random_device rd; //Will be used to obtain a seed for the random number engine
|
||||
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
|
||||
std::uniform_int_distribution<> distrib(0, extr_cnt-1);
|
||||
|
||||
while (tick <= max_tick)
|
||||
{
|
||||
bool color_repetition = false;
|
||||
if (m_extruders_sequence.random_sequence) {
|
||||
int rand_extr = distrib(gen);
|
||||
if (m_extruders_sequence.color_repetition)
|
||||
color_repetition = rand_extr == extruder;
|
||||
else
|
||||
while (rand_extr == extruder)
|
||||
rand_extr = distrib(gen);
|
||||
extruder = rand_extr;
|
||||
}
|
||||
else {
|
||||
extruder++;
|
||||
if (extruder == extr_cnt)
|
||||
extruder = 0;
|
||||
}
|
||||
|
||||
const int cur_extruder = m_extruders_sequence.extruders[extruder];
|
||||
|
||||
bool meaningless_tick = tick == 0.0 && cur_extruder == extruder;
|
||||
if (!meaningless_tick && !color_repetition)
|
||||
ticks.emplace(TickCode{tick, ToolChange,cur_extruder + 1, colors[cur_extruder]});
|
||||
|
||||
if (m_extruders_sequence.is_mm_intervals) {
|
||||
value += m_extruders_sequence.interval_by_mm;
|
||||
tick = get_tick_from_value(value, true);
|
||||
if (tick < 0)
|
||||
break;
|
||||
}
|
||||
else
|
||||
tick += m_extruders_sequence.interval_by_layers;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TickCodeManager::check_ticks_changed_event(Type type, Mode main_mode)
|
||||
{
|
||||
if ( mode == main_mode ||
|
||||
(type != ColorChange && type != ToolChange) ||
|
||||
(mode == SingleExtruder && main_mode == MultiAsSingle) || // All ColorChanges will be applied for 1st extruder
|
||||
(mode == MultiExtruder && main_mode == MultiAsSingle) ) // Just mark ColorChanges for all unused extruders
|
||||
return true;
|
||||
|
||||
if ((mode == SingleExtruder && main_mode == MultiExtruder ) ||
|
||||
(mode == MultiExtruder && main_mode == SingleExtruder) )
|
||||
{
|
||||
if (!has_tick_with_code(ColorChange))
|
||||
return true;
|
||||
|
||||
if (m_cb_show_info_msg) {
|
||||
std::string message = (mode == SingleExtruder ?
|
||||
_u8L("The last color change data was saved for a single extruder printing.") :
|
||||
_u8L("The last color change data was saved for a multi extruder printing.")
|
||||
) + "\n" +
|
||||
_u8L("Your current changes will delete all saved color changes.") + "\n\n\t" +
|
||||
_u8L("Are you sure you want to continue?");
|
||||
|
||||
if ( m_cb_show_info_msg(message, YES | NO) == YES)
|
||||
erase_all_ticks_with_code(ColorChange);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// m_ticks_mode == MultiAsSingle
|
||||
if( has_tick_with_code(ToolChange) ) {
|
||||
if (m_cb_show_info_msg) {
|
||||
std::string message = main_mode == SingleExtruder ? (
|
||||
_u8L("The last color change data was saved for a multi extruder printing.") + "\n\n" +
|
||||
_u8L("Select YES if you want to delete all saved tool changes, \n"
|
||||
"NO if you want all tool changes switch to color changes, \n"
|
||||
"or CANCEL to leave it unchanged.") + "\n\n\t" +
|
||||
_u8L("Do you want to delete all saved tool changes?")
|
||||
): ( // MultiExtruder
|
||||
_u8L("The last color change data was saved for a multi extruder printing with tool changes for whole print.") + "\n\n" +
|
||||
_u8L("Your current changes will delete all saved extruder (tool) changes.") + "\n\n\t" +
|
||||
_u8L("Are you sure you want to continue?") ) ;
|
||||
|
||||
const int answer = m_cb_show_info_msg(message, YES | NO | (main_mode == SingleExtruder ? CANCEL : 0));
|
||||
if (answer == YES) {
|
||||
erase_all_ticks_with_code(ToolChange);
|
||||
}
|
||||
else if (main_mode == SingleExtruder && answer == NO) {
|
||||
switch_code(ToolChange, ColorChange);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_cb_check_gcode_and_notify)
|
||||
m_cb_check_gcode_and_notify(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Get used extruders for tick.
|
||||
// Means all extruders(tools) which will be used during printing from current tick to the end
|
||||
std::set<int> TickCodeManager::get_used_extruders_for_tick(int tick, double print_z, Mode force_mode/* = Undef*/) const
|
||||
{
|
||||
if (!m_print)
|
||||
return {};
|
||||
|
||||
Mode e_mode = !force_mode ? mode : force_mode;
|
||||
|
||||
if (e_mode == MultiExtruder) {
|
||||
const ToolOrdering& tool_ordering = m_print->get_tool_ordering();
|
||||
|
||||
if (tool_ordering.empty())
|
||||
return {};
|
||||
|
||||
std::set<int> used_extruders;
|
||||
|
||||
auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), print_z, [](const LayerTools& lhs, double rhs) { return lhs.print_z < rhs; });
|
||||
for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) {
|
||||
const std::vector<unsigned>& extruders = it_layer_tools->extruders;
|
||||
for (const auto& extruder : extruders)
|
||||
used_extruders.emplace(extruder + 1);
|
||||
}
|
||||
|
||||
return used_extruders;
|
||||
}
|
||||
|
||||
const int default_initial_extruder = e_mode == MultiAsSingle ? std::max(only_extruder_id, 1) : 1;
|
||||
if (ticks.empty() || e_mode == SingleExtruder)
|
||||
return { default_initial_extruder };
|
||||
|
||||
std::set<int> used_extruders;
|
||||
|
||||
auto it_start = ticks.lower_bound(TickCode{ tick });
|
||||
auto it = it_start;
|
||||
if (it == ticks.begin() && it->type == ToolChange &&
|
||||
tick != it->tick) // In case of switch of ToolChange to ColorChange, when tick exists,
|
||||
// we shouldn't change color for extruder, which will be deleted
|
||||
{
|
||||
used_extruders.emplace(it->extruder);
|
||||
if (tick < it->tick)
|
||||
used_extruders.emplace(default_initial_extruder);
|
||||
}
|
||||
|
||||
while (it != ticks.begin()) {
|
||||
--it;
|
||||
if (it->type == ToolChange && tick != it->tick) {
|
||||
used_extruders.emplace(it->extruder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == ticks.begin() && used_extruders.empty())
|
||||
used_extruders.emplace(default_initial_extruder);
|
||||
|
||||
for (it = it_start; it != ticks.end(); ++it)
|
||||
if (it->type == ToolChange && tick != it->tick)
|
||||
used_extruders.emplace(it->extruder);
|
||||
|
||||
return used_extruders;
|
||||
}
|
||||
|
||||
std::string TickCodeManager::get_color_for_tick(TickCode tick, Type type, const int extruder)
|
||||
{
|
||||
auto opposite_one_color = [](const std::string& color) {
|
||||
ColorRGB rgb;
|
||||
decode_color(color, rgb);
|
||||
return encode_color(opposite(rgb));
|
||||
};
|
||||
auto opposite_two_colors = [](const std::string& a, const std::string& b) {
|
||||
ColorRGB rgb1; decode_color(a, rgb1);
|
||||
ColorRGB rgb2; decode_color(b, rgb2);
|
||||
return encode_color(opposite(rgb1, rgb2));
|
||||
};
|
||||
|
||||
if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) {
|
||||
|
||||
if (ticks.empty())
|
||||
return opposite_one_color(colors[0]);
|
||||
|
||||
auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick);
|
||||
if (before_tick_it == ticks.end()) {
|
||||
while (before_tick_it != ticks.begin())
|
||||
if (--before_tick_it; before_tick_it->type == ColorChange)
|
||||
break;
|
||||
if (before_tick_it->type == ColorChange)
|
||||
return opposite_one_color(before_tick_it->color);
|
||||
|
||||
return opposite_one_color(colors[0]);
|
||||
}
|
||||
|
||||
if (before_tick_it == ticks.begin()) {
|
||||
const std::string& frst_color = colors[0];
|
||||
if (before_tick_it->type == ColorChange)
|
||||
return opposite_two_colors(frst_color, before_tick_it->color);
|
||||
|
||||
auto next_tick_it = before_tick_it;
|
||||
while (next_tick_it != ticks.end())
|
||||
if (++next_tick_it; next_tick_it != ticks.end() && next_tick_it->type == ColorChange)
|
||||
break;
|
||||
if (next_tick_it != ticks.end() && next_tick_it->type == ColorChange)
|
||||
return opposite_two_colors(frst_color, next_tick_it->color);
|
||||
|
||||
return opposite_one_color(frst_color);
|
||||
}
|
||||
|
||||
std::string frst_color = "";
|
||||
if (before_tick_it->type == ColorChange)
|
||||
frst_color = before_tick_it->color;
|
||||
else {
|
||||
auto next_tick_it = before_tick_it;
|
||||
while (next_tick_it != ticks.end())
|
||||
if (++next_tick_it; next_tick_it != ticks.end() && next_tick_it->type == ColorChange) {
|
||||
frst_color = next_tick_it->color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (before_tick_it != ticks.begin())
|
||||
if (--before_tick_it; before_tick_it->type == ColorChange)
|
||||
break;
|
||||
|
||||
if (before_tick_it->type == ColorChange) {
|
||||
if (frst_color.empty())
|
||||
return opposite_one_color(before_tick_it->color);
|
||||
|
||||
return opposite_two_colors(before_tick_it->color, frst_color);
|
||||
}
|
||||
|
||||
if (frst_color.empty())
|
||||
return opposite_one_color(colors[0]);
|
||||
|
||||
return opposite_two_colors(colors[0], frst_color);
|
||||
}
|
||||
|
||||
std::string color = colors[extruder - 1];
|
||||
|
||||
if (type == ColorChange) {
|
||||
if (!ticks.empty()) {
|
||||
auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick );
|
||||
while (before_tick_it != ticks.begin()) {
|
||||
--before_tick_it;
|
||||
if (before_tick_it->type == ColorChange && before_tick_it->extruder == extruder) {
|
||||
color = before_tick_it->color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color = get_new_color(color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
bool TickCodeManager::add_tick(const int tick, Type type, const int extruder, double print_z)
|
||||
{
|
||||
std::string color;
|
||||
std::string extra;
|
||||
if (type == Custom) // custom Gcode
|
||||
{
|
||||
extra = get_custom_code(m_custom_gcode, print_z);
|
||||
if (extra.empty())
|
||||
return false;
|
||||
m_custom_gcode = extra;
|
||||
}
|
||||
else if (type == PausePrint) {
|
||||
extra = get_pause_print_msg(m_pause_print_msg, print_z);
|
||||
if (extra.empty())
|
||||
return false;
|
||||
m_pause_print_msg = extra;
|
||||
}
|
||||
else {
|
||||
color = get_color_for_tick(TickCode{ tick }, type, extruder);
|
||||
if (color.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
ticks.emplace(TickCode{ tick, type, extruder, color, extra });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TickCodeManager::edit_tick(std::set<TickCode>::iterator it, double print_z)
|
||||
{
|
||||
// Save previously value of the tick before the call a Dialog from get_... functions,
|
||||
// otherwise a background process can change ticks values and current iterator wouldn't be valid for the moment of a Dialog close
|
||||
// and PS will crash (see https://github.com/prusa3d/PrusaSlicer/issues/10941)
|
||||
TickCode changed_tick = *it;
|
||||
|
||||
std::string edited_value;
|
||||
if (it->type == ColorChange)
|
||||
edited_value = get_new_color(it->color);
|
||||
else if (it->type == PausePrint)
|
||||
edited_value = get_pause_print_msg(it->extra, print_z);
|
||||
else
|
||||
edited_value = get_custom_code(it->type == Template ? gcode(Template) : it->extra, print_z);
|
||||
|
||||
if (edited_value.empty())
|
||||
return false;
|
||||
|
||||
// Update iterator. For this moment its value can be invalid
|
||||
if (it = ticks.find(changed_tick); it == ticks.end())
|
||||
return false;
|
||||
|
||||
if (it->type == ColorChange) {
|
||||
if (it->color == edited_value)
|
||||
return false;
|
||||
changed_tick.color = edited_value;
|
||||
}
|
||||
else if (it->type == Template) {
|
||||
if (gcode(Template) == edited_value)
|
||||
return false;
|
||||
changed_tick.extra = edited_value;
|
||||
changed_tick.type = Custom;
|
||||
}
|
||||
else if (it->type == Custom || it->type == PausePrint) {
|
||||
if (it->extra == edited_value)
|
||||
return false;
|
||||
changed_tick.extra = edited_value;
|
||||
if (it->type == Template)
|
||||
changed_tick.type = Custom;
|
||||
}
|
||||
|
||||
ticks.erase(it);
|
||||
ticks.emplace(changed_tick);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TickCodeManager::switch_code(Type type_from, Type type_to)
|
||||
{
|
||||
for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; )
|
||||
if (it->type == type_from) {
|
||||
TickCode tick = *it;
|
||||
tick.type = type_to;
|
||||
tick.extruder = 1;
|
||||
ticks.erase(it);
|
||||
it = ticks.emplace(tick).first;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
bool TickCodeManager::switch_code_for_tick(std::set<TickCode>::iterator it, Type type_to, const int extruder)
|
||||
{
|
||||
const std::string color = get_color_for_tick(*it, type_to, extruder);
|
||||
if (color.empty())
|
||||
return false;
|
||||
|
||||
TickCode changed_tick = *it;
|
||||
changed_tick.type = type_to;
|
||||
changed_tick.extruder = extruder;
|
||||
changed_tick.color = color;
|
||||
|
||||
ticks.erase(it);
|
||||
ticks.emplace(changed_tick);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TickCodeManager::erase_all_ticks_with_code(Type type)
|
||||
{
|
||||
for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) {
|
||||
if (it->type == type)
|
||||
it = ticks.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool TickCodeManager::has_tick_with_code(Type type)
|
||||
{
|
||||
for (const TickCode& tick : ticks)
|
||||
if (tick.type == type)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TickCodeManager::has_tick(int tick)
|
||||
{
|
||||
return ticks.find(TickCode{ tick }) != ticks.end();
|
||||
}
|
||||
|
||||
ConflictType TickCodeManager::is_conflict_tick(const TickCode& tick, Mode main_mode, double print_z)
|
||||
{
|
||||
if ((tick.type == ColorChange && (
|
||||
(mode == SingleExtruder && main_mode == MultiExtruder ) ||
|
||||
(mode == MultiExtruder && main_mode == SingleExtruder) )) ||
|
||||
(tick.type == ToolChange &&
|
||||
(mode == MultiAsSingle && main_mode != MultiAsSingle)) )
|
||||
return ctModeConflict;
|
||||
|
||||
// check ColorChange tick
|
||||
if (tick.type == ColorChange) {
|
||||
// We should mark a tick as a "MeaninglessColorChange",
|
||||
// if it has a ColorChange for unused extruder from current print to end of the print
|
||||
std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, print_z, main_mode);
|
||||
|
||||
if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end())
|
||||
return ctMeaninglessColorChange;
|
||||
|
||||
// We should mark a tick as a "Redundant",
|
||||
// if it has a ColorChange for extruder that has not been used before
|
||||
if (mode == MultiAsSingle && tick.extruder != std::max<int>(only_extruder_id, 1) )
|
||||
{
|
||||
auto it = ticks.lower_bound( tick );
|
||||
if (it == ticks.begin() && it->type == ToolChange && tick.extruder == it->extruder)
|
||||
return ctNone;
|
||||
|
||||
while (it != ticks.begin()) {
|
||||
--it;
|
||||
if (it->type == ToolChange && tick.extruder == it->extruder)
|
||||
return ctNone;
|
||||
}
|
||||
|
||||
return ctRedundant;
|
||||
}
|
||||
}
|
||||
|
||||
// check ToolChange tick
|
||||
if (mode == MultiAsSingle && tick.type == ToolChange) {
|
||||
// We should mark a tick as a "MeaninglessToolChange",
|
||||
// if it has a ToolChange to the same extruder
|
||||
auto it = ticks.find(tick);
|
||||
if (it == ticks.begin())
|
||||
return tick.extruder == std::max<int>(only_extruder_id, 1) ? ctMeaninglessToolChange : ctNone;
|
||||
|
||||
while (it != ticks.begin()) {
|
||||
--it;
|
||||
if (it->type == ToolChange)
|
||||
return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone;
|
||||
}
|
||||
}
|
||||
|
||||
return ctNone;
|
||||
}
|
||||
|
||||
std::string TickCodeManager::get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const
|
||||
{
|
||||
const int current_extruder = it->extruder == 0 ? std::max<int>(only_extruder_id, 1) : it->extruder;
|
||||
|
||||
auto it_n = it;
|
||||
while (it_n != ticks.begin()) {
|
||||
--it_n;
|
||||
if (it_n->type == ColorChange && it_n->extruder == current_extruder)
|
||||
return it_n->color;
|
||||
}
|
||||
|
||||
return colors[current_extruder-1]; // return a color for a specific extruder from the colors list
|
||||
}
|
||||
|
||||
std::string TickCodeManager::get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const
|
||||
{
|
||||
const int def_extruder = std::max<int>(1, only_extruder_id);
|
||||
auto it_n = it;
|
||||
bool is_tool_change = false;
|
||||
while (it_n != ticks.begin()) {
|
||||
--it_n;
|
||||
if (it_n->type == ToolChange) {
|
||||
is_tool_change = true;
|
||||
if (it_n->extruder == it->extruder)
|
||||
return it->color;
|
||||
break;
|
||||
}
|
||||
if (it_n->type == ColorChange && it_n->extruder == it->extruder)
|
||||
return it->color;
|
||||
}
|
||||
if (!is_tool_change && it->extruder == def_extruder)
|
||||
return it->color;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // DoubleSlider
|
||||
|
||||
|
221
src/slic3r/GUI/TickCodesManager.hpp
Normal file
@ -0,0 +1,221 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_TickCodesManager_hpp_
|
||||
#define slic3r_GUI_TickCodesManager_hpp_
|
||||
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
using namespace Slic3r::CustomGCode;
|
||||
namespace Slic3r {
|
||||
class PrintObject;
|
||||
class Print;
|
||||
class Layer;
|
||||
}
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
// return true when areas are mostly equivalent
|
||||
bool equivalent_areas(const double& bottom_area, const double& top_area);
|
||||
|
||||
// return true if color change was detected
|
||||
bool check_color_change(const Slic3r::PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs,
|
||||
// what to do with detected color change
|
||||
// and return true when detection have to be desturbed
|
||||
std::function<bool(const Slic3r::Layer*)> break_condition);
|
||||
enum ConflictType
|
||||
{
|
||||
ctNone,
|
||||
ctModeConflict,
|
||||
ctMeaninglessColorChange,
|
||||
ctMeaninglessToolChange,
|
||||
ctRedundant
|
||||
};
|
||||
|
||||
struct ExtrudersSequence
|
||||
{
|
||||
bool is_mm_intervals = true;
|
||||
double interval_by_mm = 3.0;
|
||||
int interval_by_layers = 10;
|
||||
bool random_sequence { false };
|
||||
bool color_repetition { false };
|
||||
std::vector<size_t> extruders = { 0 };
|
||||
|
||||
bool operator==(const ExtrudersSequence& other) const
|
||||
{
|
||||
return (other.is_mm_intervals == this->is_mm_intervals ) &&
|
||||
(other.interval_by_mm == this->interval_by_mm ) &&
|
||||
(other.interval_by_layers == this->interval_by_layers ) &&
|
||||
(other.random_sequence == this->random_sequence ) &&
|
||||
(other.color_repetition == this->color_repetition ) &&
|
||||
(other.extruders == this->extruders ) ;
|
||||
}
|
||||
bool operator!=(const ExtrudersSequence& other) const
|
||||
{
|
||||
return (other.is_mm_intervals != this->is_mm_intervals ) ||
|
||||
(other.interval_by_mm != this->interval_by_mm ) ||
|
||||
(other.interval_by_layers != this->interval_by_layers ) ||
|
||||
(other.random_sequence != this->random_sequence ) ||
|
||||
(other.color_repetition != this->color_repetition ) ||
|
||||
(other.extruders != this->extruders ) ;
|
||||
}
|
||||
|
||||
void add_extruder(size_t pos, size_t extruder_id = size_t(0))
|
||||
{
|
||||
extruders.insert(extruders.begin() + pos+1, extruder_id);
|
||||
}
|
||||
|
||||
void delete_extruder(size_t pos)
|
||||
{
|
||||
if (extruders.size() == 1)
|
||||
return;// last item can't be deleted
|
||||
extruders.erase(extruders.begin() + pos);
|
||||
}
|
||||
|
||||
void init(size_t extruders_count)
|
||||
{
|
||||
extruders.clear();
|
||||
for (size_t extruder = 0; extruder < extruders_count; extruder++)
|
||||
extruders.push_back(extruder);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct TickCode
|
||||
{
|
||||
bool operator<(const TickCode& other) const { return other.tick > this->tick; }
|
||||
bool operator>(const TickCode& other) const { return other.tick < this->tick; }
|
||||
|
||||
int tick = 0;
|
||||
Type type = ColorChange;
|
||||
int extruder = 0;
|
||||
std::string color;
|
||||
std::string extra;
|
||||
};
|
||||
|
||||
|
||||
class TickCodeManager
|
||||
{
|
||||
std::string m_custom_gcode;
|
||||
std::string m_pause_print_msg;
|
||||
bool m_use_default_colors { true };
|
||||
|
||||
const Slic3r::Print* m_print{ nullptr };
|
||||
// pointer to the m_values from DSForLayers
|
||||
const std::vector<double>* m_values{ nullptr };
|
||||
|
||||
ExtrudersSequence m_extruders_sequence;
|
||||
|
||||
bool has_tick_with_code(Type type);
|
||||
bool has_tick(int tick);
|
||||
|
||||
std::string get_color_for_tick(TickCode tick, Type type, const int extruder);
|
||||
|
||||
std::string get_custom_code(const std::string& code_in, double height);
|
||||
std::string get_pause_print_msg(const std::string& msg_in, double height);
|
||||
std::string get_new_color(const std::string& color);
|
||||
|
||||
std::function<void()> m_cb_notify_empty_color_change { nullptr };
|
||||
std::function<void(Type type)> m_cb_check_gcode_and_notify { nullptr };
|
||||
|
||||
std::function<std::string(const std::string&, double)> m_cb_get_custom_code { nullptr };
|
||||
std::function<std::string(const std::string&, double)> m_cb_get_pause_print_msg { nullptr };
|
||||
std::function<std::string(const std::string&)> m_cb_get_new_color { nullptr };
|
||||
|
||||
std::function<int(const std::string&, int)> m_cb_show_info_msg { nullptr };
|
||||
std::function<int(const std::string&, int)> m_cb_show_warning_msg { nullptr };
|
||||
std::function<int()> m_cb_get_extruders_cnt { nullptr };
|
||||
std::function<bool(ExtrudersSequence&)> m_cb_get_extruders_sequence { nullptr };
|
||||
|
||||
public:
|
||||
|
||||
TickCodeManager();
|
||||
~TickCodeManager() {}
|
||||
std::set<TickCode> ticks {};
|
||||
Mode mode { Undef };
|
||||
bool is_wipe_tower { false }; //This flag indicates that there is multiple extruder print with wipe tower
|
||||
int only_extruder_id{ -1 };
|
||||
|
||||
// colors per extruder
|
||||
std::vector<std::string> colors {};
|
||||
|
||||
bool empty() const { return ticks.empty(); }
|
||||
|
||||
void set_ticks(const Info& custom_gcode_per_print_z);
|
||||
|
||||
bool add_tick(const int tick, Type type, int extruder, double print_z);
|
||||
bool edit_tick(std::set<TickCode>::iterator it, double print_z);
|
||||
void switch_code(Type type_from, Type type_to);
|
||||
bool switch_code_for_tick(std::set<TickCode>::iterator it, Type type_to, const int extruder);
|
||||
void erase_all_ticks_with_code(Type type);
|
||||
|
||||
ConflictType is_conflict_tick(const TickCode& tick, Mode main_mode, double print_z);
|
||||
|
||||
int get_tick_from_value(double value, bool force_lower_bound = false);
|
||||
|
||||
std::string gcode(Slic3r::CustomGCode::Type type);
|
||||
|
||||
// Get used extruders for tick.
|
||||
// Means all extruders(tools) which will be used during printing from current tick to the end
|
||||
std::set<int> get_used_extruders_for_tick(int tick, double print_z, Mode force_mode = Undef) const;
|
||||
|
||||
// Get active extruders for tick.
|
||||
// Means one current extruder for not existing tick OR
|
||||
// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick)
|
||||
// Use those values to disable selection of active extruders
|
||||
std::array<int, 2> get_active_extruders_for_tick(int tick, Mode main_mode) const;
|
||||
|
||||
std::string get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const;
|
||||
std::string get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const;
|
||||
|
||||
// true -> if manipulation with ticks with selected type and in respect to the main_mode (slider mode) is possible
|
||||
// false -> otherwise
|
||||
bool check_ticks_changed_event(Type type, Mode main_mode);
|
||||
|
||||
// return true, if extruder sequence was changed
|
||||
bool edit_extruder_sequence(const int max_tick, Mode main_mode);
|
||||
|
||||
// return true, if auto color change was successfully processed
|
||||
bool auto_color_change(Mode main_mode);
|
||||
|
||||
void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; }
|
||||
bool used_default_colors() const { return m_use_default_colors; }
|
||||
|
||||
void set_print(const Slic3r::Print& print) { if (!m_print) m_print = &print; }
|
||||
void set_values(const std::vector<double>* values) { m_values = values; }
|
||||
|
||||
void set_callback_on_empty_auto_color_change(std::function<void()> cb)
|
||||
{ m_cb_notify_empty_color_change = cb; }
|
||||
|
||||
void set_callback_on_check_gcode(std::function<void(Type)> cb )
|
||||
{ m_cb_check_gcode_and_notify = cb; }
|
||||
|
||||
void set_callback_on_get_custom_code(std::function<std::string(const std::string&, double)> cb)
|
||||
{ m_cb_get_custom_code = cb; }
|
||||
|
||||
void set_callback_on_get_pause_print_msg(std::function<std::string(const std::string&, double)> cb)
|
||||
{ m_cb_get_pause_print_msg = cb; }
|
||||
|
||||
void set_callback_on_get_new_color(std::function<std::string(const std::string&)> cb)
|
||||
{ m_cb_get_new_color = cb; }
|
||||
|
||||
void set_callback_on_show_info_msg(std::function<int(const std::string&, int)> cb)
|
||||
{ m_cb_show_info_msg = cb; }
|
||||
|
||||
void set_callback_on_show_warning_msg(std::function<int(const std::string&, int)> cb)
|
||||
{ m_cb_show_warning_msg = cb; }
|
||||
|
||||
void set_callback_on_get_extruders_cnt(std::function<int()> cb)
|
||||
{ m_cb_get_extruders_cnt = cb; }
|
||||
|
||||
void set_callback_on_get_extruders_sequence(std::function<bool(ExtrudersSequence&)> cb)
|
||||
{ m_cb_get_extruders_sequence = cb; }
|
||||
};
|
||||
|
||||
} // DoubleSlider;
|
||||
|
||||
#endif // slic3r_GUI_TickCodesManager_hpp_
|