2018-02-02 12:38:35 +01:00
#include "wxExtensions.hpp"
2019-04-05 18:53:02 +02:00
#include <stdexcept>
2019-05-17 19:12:52 +02:00
#include <cmath>
2019-04-05 18:53:02 +02:00
2018-05-10 16:36:12 +02:00
#include <wx/sizer.h>
2018-11-26 14:41:58 +01:00
2019-04-04 09:20:11 +02:00
#include <boost/algorithm/string/replace.hpp>
2018-11-26 14:41:58 +01:00
#include "BitmapCache.hpp"
2019-01-07 19:17:10 +01:00
#include "GUI.hpp"
2018-10-05 23:29:15 +02:00
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
2019-02-11 14:14:35 +01:00
#include "I18N.hpp"
2019-04-17 21:35:53 +02:00
#include "GUI_Utils.hpp"
2020-05-26 17:42:57 +02:00
#include "Plater.hpp"
2019-05-17 19:12:52 +02:00
#include "../Utils/MacDarkMode.hpp"
2018-05-10 16:36:12 +02:00
2019-05-13 14:27:51 +02:00
#ifndef __WXGTK__ // msw_menuitem_bitmaps is used for MSW and OSX
2019-05-07 13:35:37 +02:00
static std :: map < int , std :: string > msw_menuitem_bitmaps ;
2019-05-13 14:27:51 +02:00
#ifdef __WXMSW__
2019-05-03 09:44:19 +02:00
void msw_rescale_menu ( wxMenu * menu )
{
2019-05-07 13:35:37 +02:00
struct update_icons {
static void run ( wxMenuItem * item ) {
const auto it = msw_menuitem_bitmaps . find ( item -> GetId ());
if ( it != msw_menuitem_bitmaps . end ()) {
2020-01-31 16:50:11 +01:00
const wxBitmap & item_icon = create_scaled_bitmap ( it -> second );
2019-05-07 13:35:37 +02:00
if ( item_icon . IsOk ())
item -> SetBitmap ( item_icon );
}
if ( item -> IsSubMenu ())
for ( wxMenuItem * sub_item : item -> GetSubMenu () -> GetMenuItems ())
update_icons :: run ( sub_item );
}
};
2019-05-03 09:44:19 +02:00
2019-05-07 13:35:37 +02:00
for ( wxMenuItem * item : menu -> GetMenuItems ())
update_icons :: run ( item );
2019-05-03 09:44:19 +02:00
}
2019-05-07 13:35:37 +02:00
#endif /* __WXMSW__ */
2019-05-13 14:27:51 +02:00
#endif /* no __WXGTK__ */
2019-12-18 15:54:01 +01:00
void enable_menu_item ( wxUpdateUIEvent & evt , std :: function < bool () > const cb_condition , wxMenuItem * item , wxWindow * win )
2019-05-13 14:27:51 +02:00
{
const bool enable = cb_condition ();
evt . Enable ( enable );
#ifdef __WXOSX__
const auto it = msw_menuitem_bitmaps . find ( item -> GetId ());
if ( it != msw_menuitem_bitmaps . end ())
{
2020-02-02 19:40:00 +01:00
const wxBitmap & item_icon = create_scaled_bitmap ( it -> second , win , 16 , ! enable );
2019-05-13 17:57:31 +02:00
if ( item_icon . IsOk ())
item -> SetBitmap ( item_icon );
2019-05-13 14:27:51 +02:00
}
#endif // __WXOSX__
}
2019-05-03 09:44:19 +02:00
2018-10-17 12:17:25 +02:00
wxMenuItem * append_menu_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
2019-05-13 14:27:51 +02:00
std :: function < void ( wxCommandEvent & event ) > cb , const wxBitmap & icon , wxEvtHandler * event_handler ,
std :: function < bool () > const cb_condition , wxWindow * parent )
2018-10-17 12:17:25 +02:00
{
if ( id == wxID_ANY )
id = wxNewId ();
2019-05-07 13:35:37 +02:00
auto * item = new wxMenuItem ( menu , id , string , description );
if ( icon . IsOk ()) {
2019-05-03 09:44:19 +02:00
item -> SetBitmap ( icon );
}
2019-05-07 13:35:37 +02:00
menu -> Append ( item );
2018-10-17 12:17:25 +02:00
2018-12-06 14:49:14 +01:00
#ifdef __WXMSW__
2018-12-07 17:50:48 +01:00
if ( event_handler != nullptr && event_handler != menu )
2018-10-18 14:42:21 +02:00
event_handler -> Bind ( wxEVT_MENU , cb , id );
else
2018-12-07 17:50:48 +01:00
#endif // __WXMSW__
2018-10-18 14:42:21 +02:00
menu -> Bind ( wxEVT_MENU , cb , id );
2019-05-13 14:27:51 +02:00
if ( parent ) {
2019-12-18 15:54:01 +01:00
parent -> Bind ( wxEVT_UPDATE_UI , [ cb_condition , item , parent ]( wxUpdateUIEvent & evt ) {
enable_menu_item ( evt , cb_condition , item , parent ); }, id );
2019-05-13 14:27:51 +02:00
}
2018-10-18 14:42:21 +02:00
return item ;
}
2018-12-06 14:49:14 +01:00
wxMenuItem * append_menu_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
2019-05-13 14:27:51 +02:00
std :: function < void ( wxCommandEvent & event ) > cb , const std :: string & icon , wxEvtHandler * event_handler ,
std :: function < bool () > const cb_condition , wxWindow * parent )
2018-12-06 14:49:14 +01:00
{
2019-05-03 09:44:19 +02:00
if ( id == wxID_ANY )
id = wxNewId ();
2020-01-31 16:50:11 +01:00
const wxBitmap & bmp = ! icon . empty () ? create_scaled_bitmap ( icon ) : wxNullBitmap ; // FIXME: pass window ptr
2019-05-13 14:27:51 +02:00
//#ifdef __WXMSW__
#ifndef __WXGTK__
2019-05-03 09:44:19 +02:00
if ( bmp . IsOk ())
2019-05-07 13:35:37 +02:00
msw_menuitem_bitmaps [ id ] = icon ;
#endif /* __WXMSW__ */
2019-05-03 09:44:19 +02:00
2019-05-13 14:27:51 +02:00
return append_menu_item ( menu , id , string , description , cb , bmp , event_handler , cb_condition , parent );
2018-12-06 14:49:14 +01:00
}
2019-05-13 14:27:51 +02:00
wxMenuItem * append_submenu ( wxMenu * menu , wxMenu * sub_menu , int id , const wxString & string , const wxString & description , const std :: string & icon ,
std :: function < bool () > const cb_condition , wxWindow * parent )
2018-10-18 14:42:21 +02:00
{
if ( id == wxID_ANY )
id = wxNewId ();
wxMenuItem * item = new wxMenuItem ( menu , id , string , description );
2019-05-07 13:35:37 +02:00
if ( ! icon . empty ()) {
2020-01-31 16:50:11 +01:00
item -> SetBitmap ( create_scaled_bitmap ( icon )); // FIXME: pass window ptr
2019-05-13 14:27:51 +02:00
//#ifdef __WXMSW__
#ifndef __WXGTK__
2019-05-07 13:35:37 +02:00
msw_menuitem_bitmaps [ id ] = icon ;
#endif /* __WXMSW__ */
}
2018-10-18 14:42:21 +02:00
item -> SetSubMenu ( sub_menu );
menu -> Append ( item );
2019-05-13 14:27:51 +02:00
if ( parent ) {
2019-12-18 15:54:01 +01:00
parent -> Bind ( wxEVT_UPDATE_UI , [ cb_condition , item , parent ]( wxUpdateUIEvent & evt ) {
enable_menu_item ( evt , cb_condition , item , parent ); }, id );
2019-05-13 14:27:51 +02:00
}
2018-10-17 12:17:25 +02:00
return item ;
}
2019-03-28 16:28:34 +01:00
wxMenuItem * append_menu_radio_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
std :: function < void ( wxCommandEvent & event ) > cb , wxEvtHandler * event_handler )
{
if ( id == wxID_ANY )
id = wxNewId ();
wxMenuItem * item = menu -> AppendRadioItem ( id , string , description );
#ifdef __WXMSW__
if ( event_handler != nullptr && event_handler != menu )
event_handler -> Bind ( wxEVT_MENU , cb , id );
else
#endif // __WXMSW__
menu -> Bind ( wxEVT_MENU , cb , id );
return item ;
}
2019-07-31 10:12:13 +02:00
wxMenuItem * append_menu_check_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
2020-02-04 14:42:26 +01:00
std :: function < void ( wxCommandEvent & event ) > cb , wxEvtHandler * event_handler ,
std :: function < bool () > const enable_condition , std :: function < bool () > const check_condition , wxWindow * parent )
2019-07-31 10:12:13 +02:00
{
if ( id == wxID_ANY )
id = wxNewId ();
wxMenuItem * item = menu -> AppendCheckItem ( id , string , description );
#ifdef __WXMSW__
if ( event_handler != nullptr && event_handler != menu )
event_handler -> Bind ( wxEVT_MENU , cb , id );
else
#endif // __WXMSW__
menu -> Bind ( wxEVT_MENU , cb , id );
2020-02-04 14:42:26 +01:00
if ( parent )
parent -> Bind ( wxEVT_UPDATE_UI , [ enable_condition , check_condition ]( wxUpdateUIEvent & evt )
{
evt . Enable ( enable_condition ());
evt . Check ( check_condition ());
}, id );
2019-07-31 10:12:13 +02:00
return item ;
}
2018-02-20 14:25:40 +01:00
const unsigned int wxCheckListBoxComboPopup :: DefaultWidth = 200 ;
const unsigned int wxCheckListBoxComboPopup :: DefaultHeight = 200 ;
2018-02-02 12:38:35 +01:00
bool wxCheckListBoxComboPopup :: Create ( wxWindow * parent )
{
2018-02-06 12:43:25 +01:00
return wxCheckListBox :: Create ( parent , wxID_HIGHEST + 1 , wxPoint ( 0 , 0 ));
2018-02-02 12:38:35 +01:00
}
wxWindow * wxCheckListBoxComboPopup :: GetControl ()
{
return this ;
}
void wxCheckListBoxComboPopup :: SetStringValue ( const wxString & value )
{
m_text = value ;
}
wxString wxCheckListBoxComboPopup :: GetStringValue () const
{
return m_text ;
}
wxSize wxCheckListBoxComboPopup :: GetAdjustedSize ( int minWidth , int prefHeight , int maxHeight )
{
2020-04-25 10:36:51 +02:00
// set width dinamically in dependence of items text
// and set height dinamically in dependence of items count
2018-02-02 12:38:35 +01:00
wxComboCtrl * cmb = GetComboCtrl ();
2020-06-30 12:53:42 +02:00
if ( cmb != nullptr ) {
2018-02-02 12:38:35 +01:00
wxSize size = GetComboCtrl () -> GetSize ();
2018-02-20 14:25:40 +01:00
unsigned int count = GetCount ();
2020-06-30 12:53:42 +02:00
if ( count > 0 ) {
2020-04-25 10:36:51 +02:00
int max_width = size . x ;
2020-06-30 12:53:42 +02:00
for ( unsigned int i = 0 ; i < count ; ++ i ) {
2020-04-25 10:36:51 +02:00
max_width = std :: max ( max_width , 60 + GetTextExtent ( GetString ( i )). x );
}
size . SetWidth ( max_width );
2020-06-30 12:53:42 +02:00
size . SetHeight ( count * cmb -> GetCharHeight ());
2020-04-25 10:36:51 +02:00
}
2018-02-20 14:25:40 +01:00
else
size . SetHeight ( DefaultHeight );
2018-02-02 12:38:35 +01:00
return size ;
}
else
2018-02-20 14:25:40 +01:00
return wxSize ( DefaultWidth , DefaultHeight );
}
void wxCheckListBoxComboPopup :: OnKeyEvent ( wxKeyEvent & evt )
{
2018-02-22 08:59:47 +01:00
// filters out all the keys which are not working properly
switch ( evt . GetKeyCode ())
{
case WXK_LEFT :
case WXK_UP :
case WXK_RIGHT :
case WXK_DOWN :
case WXK_PAGEUP :
case WXK_PAGEDOWN :
case WXK_END :
case WXK_HOME :
case WXK_NUMPAD_LEFT :
case WXK_NUMPAD_UP :
case WXK_NUMPAD_RIGHT :
case WXK_NUMPAD_DOWN :
case WXK_NUMPAD_PAGEUP :
case WXK_NUMPAD_PAGEDOWN :
case WXK_NUMPAD_END :
case WXK_NUMPAD_HOME :
{
break ;
}
default :
{
evt . Skip ();
break ;
}
}
2018-02-02 12:38:35 +01:00
}
void wxCheckListBoxComboPopup :: OnCheckListBox ( wxCommandEvent & evt )
{
// forwards the checklistbox event to the owner wxComboCtrl
2018-05-17 10:23:02 +02:00
if ( m_check_box_events_status == OnCheckListBoxFunction :: FreeToProceed )
2018-02-02 12:38:35 +01:00
{
2018-05-17 10:23:02 +02:00
wxComboCtrl * cmb = GetComboCtrl ();
if ( cmb != nullptr ) {
wxCommandEvent event ( wxEVT_CHECKLISTBOX , cmb -> GetId ());
event . SetEventObject ( cmb );
cmb -> ProcessWindowEvent ( event );
}
2018-02-02 12:38:35 +01:00
}
2018-02-20 15:22:30 +01:00
evt . Skip ();
2018-05-17 10:23:02 +02:00
#ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file)
if ( m_check_box_events_status == OnCheckListBoxFunction :: RefuseToProceed )
// this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should
// explicitly change the state on the checkbox
m_check_box_events_status = OnCheckListBoxFunction :: WasRefusedLastTime ;
else
// if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it
m_check_box_events_status = OnCheckListBoxFunction :: RefuseToProceed ;
#endif
2018-02-02 12:38:35 +01:00
}
void wxCheckListBoxComboPopup :: OnListBoxSelection ( wxCommandEvent & evt )
{
// transforms list box item selection event into checklistbox item toggle event
int selId = GetSelection ();
if ( selId != wxNOT_FOUND )
{
2018-05-17 10:23:02 +02:00
#ifndef _WIN32
if ( m_check_box_events_status == OnCheckListBoxFunction :: RefuseToProceed )
#endif
Check (( unsigned int ) selId , ! IsChecked (( unsigned int ) selId ));
2018-02-02 12:38:35 +01:00
2018-05-17 10:23:02 +02:00
m_check_box_events_status = OnCheckListBoxFunction :: FreeToProceed ; // so the checkbox reacts to square-click the next time
SetSelection ( wxNOT_FOUND );
2018-02-02 12:38:35 +01:00
wxCommandEvent event ( wxEVT_CHECKLISTBOX , GetId ());
event . SetInt ( selId );
event . SetEventObject ( this );
ProcessEvent ( event );
}
}
2018-04-06 13:37:00 +02:00
// *** wxDataViewTreeCtrlComboPopup ***
const unsigned int wxDataViewTreeCtrlComboPopup :: DefaultWidth = 270 ;
const unsigned int wxDataViewTreeCtrlComboPopup :: DefaultHeight = 200 ;
const unsigned int wxDataViewTreeCtrlComboPopup :: DefaultItemHeight = 22 ;
bool wxDataViewTreeCtrlComboPopup :: Create ( wxWindow * parent )
{
2018-04-09 16:50:17 +02:00
return wxDataViewTreeCtrl :: Create ( parent , wxID_ANY /*HIGHEST + 1*/ , wxPoint ( 0 , 0 ), wxDefaultSize /*wxSize(270, -1)*/ , wxDV_NO_HEADER );
2018-04-06 13:37:00 +02:00
}
2018-04-09 12:41:25 +02:00
/*
2018-04-06 13:37:00 +02:00
wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
{
// matches owner wxComboCtrl's width
// and sets height dinamically in dependence of contained items count
2018-04-06 15:42:52 +02:00
wxComboCtrl* cmb = GetComboCtrl();
if (cmb != nullptr)
{
wxSize size = GetComboCtrl()->GetSize();
if (m_cnt_open_items > 0)
size.SetHeight(m_cnt_open_items * DefaultItemHeight);
else
size.SetHeight(DefaultHeight);
return size;
}
else
return wxSize(DefaultWidth, DefaultHeight);
2018-04-06 13:37:00 +02:00
}
2018-04-09 12:41:25 +02:00
*/
2018-04-06 13:37:00 +02:00
void wxDataViewTreeCtrlComboPopup :: OnKeyEvent ( wxKeyEvent & evt )
{
// filters out all the keys which are not working properly
if ( evt . GetKeyCode () == WXK_UP )
{
return ;
}
else if ( evt . GetKeyCode () == WXK_DOWN )
{
return ;
}
else
{
evt . Skip ();
return ;
}
}
void wxDataViewTreeCtrlComboPopup :: OnDataViewTreeCtrlSelection ( wxCommandEvent & evt )
{
wxComboCtrl * cmb = GetComboCtrl ();
auto selected = GetItemText ( GetSelection ());
cmb -> SetText ( selected );
}
2018-05-04 18:32:20 +02:00
2019-04-26 10:52:38 +02:00
// edit tooltip : change Slic3r to SLIC3R_APP_KEY
// Temporary workaround for localization
void edit_tooltip ( wxString & tooltip )
{
tooltip . Replace ( "Slic3r" , SLIC3R_APP_KEY , true );
}
2019-04-25 15:06:44 +02:00
/* Function for rescale of buttons in Dialog under MSW if dpi is changed.
* btn_ids - vector of buttons identifiers
*/
void msw_buttons_rescale ( wxDialog * dlg , const int em_unit , const std :: vector < int >& btn_ids )
{
const wxSize & btn_size = wxSize ( - 1 , int ( 2.5f * em_unit + 0.5f ));
for ( int btn_id : btn_ids ) {
// There is a case [FirmwareDialog], when we have wxControl instead of wxButton
// so let casting everything to the wxControl
wxControl * btn = static_cast < wxControl *> ( dlg -> FindWindowById ( btn_id , dlg ));
if ( btn )
btn -> SetMinSize ( btn_size );
}
}
2019-04-17 21:35:53 +02:00
/* Function for getting of em_unit value from correct parent.
* In most of cases it is m_em_unit value from GUI_App,
* but for DPIDialogs it's its own value.
* This value will be used to correct rescale after moving between
* Displays with different HDPI */
int em_unit ( wxWindow * win )
{
2019-04-18 15:05:17 +02:00
if ( win )
{
2019-05-06 18:28:23 +02:00
wxTopLevelWindow * toplevel = Slic3r :: GUI :: find_toplevel_parent ( win );
Slic3r :: GUI :: DPIDialog * dlg = dynamic_cast < Slic3r :: GUI :: DPIDialog *> ( toplevel );
2019-04-17 21:35:53 +02:00
if ( dlg )
2019-04-18 15:05:17 +02:00
return dlg -> em_unit ();
2019-05-06 18:28:23 +02:00
Slic3r :: GUI :: DPIFrame * frame = dynamic_cast < Slic3r :: GUI :: DPIFrame *> ( toplevel );
if ( frame )
return frame -> em_unit ();
2019-04-17 21:35:53 +02:00
}
return Slic3r :: GUI :: wxGetApp (). em_unit ();
}
2019-04-08 09:37:23 +02:00
2020-02-10 20:05:20 +01:00
int mode_icon_px_size ()
{
#ifdef __APPLE__
return 10 ;
#else
return 12 ;
#endif
}
2020-01-31 16:50:11 +01:00
// win is used to get a correct em_unit value
// It's important for bitmaps of dialogs.
// if win == nullptr, em_unit value of MainFrame will be used
wxBitmap create_scaled_bitmap ( const std :: string & bmp_name_in ,
wxWindow * win /* = nullptr*/ ,
const int px_cnt /* = 16*/ ,
const bool grayscale /* = false*/ )
2019-04-08 09:37:23 +02:00
{
static Slic3r :: GUI :: BitmapCache cache ;
2020-01-28 16:15:43 +01:00
unsigned int width = 0 ;
unsigned int height = ( unsigned int )( em_unit ( win ) * px_cnt * 0.1f + 0.5f );
2019-04-08 09:37:23 +02:00
2020-01-02 13:41:49 +01:00
std :: string bmp_name = bmp_name_in ;
boost :: replace_last ( bmp_name , ".png" , "" );
2019-12-18 15:54:01 +01:00
2019-04-05 18:53:02 +02:00
// Try loading an SVG first, then PNG if SVG is not found:
2020-01-31 16:50:11 +01:00
wxBitmap * bmp = cache . load_svg ( bmp_name , width , height , grayscale , Slic3r :: GUI :: wxGetApp (). dark_mode ());
2019-04-09 14:18:03 +02:00
if ( bmp == nullptr ) {
2019-05-13 17:57:31 +02:00
bmp = cache . load_png ( bmp_name , width , height , grayscale );
2019-04-05 18:53:02 +02:00
}
2019-04-09 14:18:03 +02:00
if ( bmp == nullptr ) {
2019-04-05 18:53:02 +02:00
// Neither SVG nor PNG has been found, raise error
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "Could not load bitmap: " + bmp_name );
2019-04-05 18:53:02 +02:00
}
2019-04-08 09:37:23 +02:00
return * bmp ;
}
2019-10-24 10:38:36 +02:00
std :: vector < wxBitmap *> get_extruder_color_icons ( bool thin_icon /* = false*/ )
2019-10-01 18:19:28 +02:00
{
2020-02-02 22:22:40 +01:00
static Slic3r :: GUI :: BitmapCache bmp_cache ;
2019-10-01 18:19:28 +02:00
// Create the bitmap with color bars.
std :: vector < wxBitmap *> bmps ;
std :: vector < std :: string > colors = Slic3r :: GUI :: wxGetApp (). plater () -> get_extruder_colors_from_plater_config ();
2019-10-08 11:05:59 +02:00
if ( colors . empty ())
2019-10-07 12:36:16 +02:00
return bmps ;
2019-10-01 18:19:28 +02:00
unsigned char rgb [ 3 ];
/* It's supposed that standard size of an icon is 36px*16px for 100% scaled display.
* So set sizes for solid_colored icons used for filament preset
* and scale them in respect to em_unit value
*/
const double em = Slic3r :: GUI :: wxGetApp (). em_unit ();
2020-02-17 11:26:40 +01:00
const int icon_width = lround (( thin_icon ? 1.6 : 3.2 ) * em );
2019-10-01 18:19:28 +02:00
const int icon_height = lround ( 1.6 * em );
for ( const std :: string & color : colors )
{
2019-10-24 10:38:36 +02:00
std :: string bitmap_key = color + "-h" + std :: to_string ( icon_height ) + "-w" + std :: to_string ( icon_width );
2020-02-02 22:22:40 +01:00
wxBitmap * bitmap = bmp_cache . find ( bitmap_key );
2019-10-01 18:19:28 +02:00
if ( bitmap == nullptr ) {
// Paint the color icon.
2020-02-02 22:22:40 +01:00
Slic3r :: GUI :: BitmapCache :: parse_color ( color , rgb );
2020-01-31 16:50:11 +01:00
// there is no neede to scale created solid bitmap
2020-02-02 22:22:40 +01:00
bitmap = bmp_cache . insert ( bitmap_key , bmp_cache . mksolid ( icon_width , icon_height , rgb , true ));
2019-10-01 18:19:28 +02:00
}
bmps . emplace_back ( bitmap );
}
return bmps ;
}
2019-10-18 12:35:35 +02:00
void apply_extruder_selector ( wxBitmapComboBox ** ctrl ,
wxWindow * parent ,
const std :: string & first_item /* = ""*/ ,
wxPoint pos /* = wxDefaultPosition*/ ,
2019-10-24 10:38:36 +02:00
wxSize size /* = wxDefaultSize*/ ,
bool use_thin_icon /* = false*/ )
2019-10-18 12:35:35 +02:00
{
2019-10-24 10:38:36 +02:00
std :: vector < wxBitmap *> icons = get_extruder_color_icons ( use_thin_icon );
2019-10-18 12:35:35 +02:00
if ( !* ctrl )
* ctrl = new wxBitmapComboBox ( parent , wxID_ANY , wxEmptyString , pos , size ,
0 , nullptr , wxCB_READONLY );
else
{
( * ctrl ) -> SetPosition ( pos );
( * ctrl ) -> SetMinSize ( size );
( * ctrl ) -> SetSize ( size );
( * ctrl ) -> Clear ();
}
2019-11-15 16:44:17 +01:00
if ( first_item . empty ())
( * ctrl ) -> Hide (); // to avoid unwanted rendering before layout (ExtruderSequenceDialog)
2019-10-18 12:35:35 +02:00
2019-11-08 11:24:57 +01:00
if ( icons . empty () && ! first_item . empty ()) {
( * ctrl ) -> Append ( _ ( first_item ), wxNullBitmap );
return ;
}
// For ObjectList we use short extruder name (just a number)
const bool use_full_item_name = dynamic_cast < Slic3r :: GUI :: ObjectList *> ( parent ) == nullptr ;
2019-10-18 12:35:35 +02:00
int i = 0 ;
2019-10-24 10:38:36 +02:00
wxString str = _ ( L ( "Extruder" ));
2019-10-18 12:35:35 +02:00
for ( wxBitmap * bmp : icons ) {
if ( i == 0 ) {
if ( ! first_item . empty ())
( * ctrl ) -> Append ( _ ( first_item ), * bmp );
++ i ;
}
2020-03-02 12:46:40 +01:00
( * ctrl ) -> Append ( use_full_item_name
2020-03-04 09:24:13 +01:00
? Slic3r :: GUI :: from_u8 (( boost :: format ( "%1% %2%" ) % str % i ). str ())
2020-03-02 12:46:40 +01:00
: wxString :: Format ( "%d" , i ), * bmp );
2019-10-18 12:35:35 +02:00
++ i ;
}
( * ctrl ) -> SetSelection ( 0 );
}
2018-08-28 15:51:53 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// LockButton
2018-08-28 15:51:53 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
LockButton :: LockButton ( wxWindow * parent ,
wxWindowID id ,
const wxPoint & pos /*= wxDefaultPosition*/ ,
const wxSize & size /*= wxDefaultSize*/ ) :
wxButton ( parent , id , wxEmptyString , pos , size , wxBU_EXACTFIT | wxNO_BORDER )
2018-08-28 15:51:53 +02:00
{
2019-07-31 17:14:32 +02:00
m_bmp_lock_closed = ScalableBitmap ( this , "lock_closed" );
m_bmp_lock_closed_f = ScalableBitmap ( this , "lock_closed_f" );
m_bmp_lock_open = ScalableBitmap ( this , "lock_open" );
m_bmp_lock_open_f = ScalableBitmap ( this , "lock_open_f" );
2018-08-28 15:51:53 +02:00
#ifdef __WXMSW__
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
#endif // __WXMSW__
2019-07-31 17:14:32 +02:00
SetBitmap ( m_bmp_lock_open . bmp ());
SetBitmapDisabled ( m_bmp_lock_open . bmp ());
SetBitmapHover ( m_bmp_lock_closed_f . bmp ());
2018-08-28 15:51:53 +02:00
//button events
2019-07-31 17:14:32 +02:00
Bind ( wxEVT_BUTTON , & LockButton :: OnButton , this );
2018-08-28 15:51:53 +02:00
}
2019-04-25 01:45:00 +02:00
void LockButton :: OnButton ( wxCommandEvent & event )
2018-08-28 15:51:53 +02:00
{
2019-06-20 12:56:23 +02:00
if ( m_disabled )
return ;
2018-08-28 15:51:53 +02:00
m_is_pushed = ! m_is_pushed ;
2019-07-31 17:14:32 +02:00
update_button_bitmaps ();
2018-08-28 15:51:53 +02:00
event . Skip ();
}
2019-04-25 01:45:00 +02:00
void LockButton :: SetLock ( bool lock )
2019-01-09 08:48:25 +01:00
{
m_is_pushed = lock ;
2019-07-31 17:14:32 +02:00
update_button_bitmaps ();
2019-01-09 08:48:25 +01:00
}
2019-04-25 01:45:00 +02:00
void LockButton :: msw_rescale ()
2019-04-16 10:05:45 +02:00
{
2019-07-31 17:14:32 +02:00
m_bmp_lock_closed . msw_rescale ();
m_bmp_lock_closed_f . msw_rescale ();
m_bmp_lock_open . msw_rescale ();
m_bmp_lock_open_f . msw_rescale ();
2019-09-18 15:10:36 +02:00
update_button_bitmaps ();
2019-04-16 10:05:45 +02:00
}
2019-07-31 17:14:32 +02:00
void LockButton :: update_button_bitmaps ()
2018-08-28 15:51:53 +02:00
{
2019-07-31 17:14:32 +02:00
SetBitmap ( m_is_pushed ? m_bmp_lock_closed . bmp () : m_bmp_lock_open . bmp ());
SetBitmapHover ( m_is_pushed ? m_bmp_lock_closed_f . bmp () : m_bmp_lock_open_f . bmp ());
2018-08-28 15:51:53 +02:00
Refresh ();
Update ();
}
2019-01-10 11:05:58 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// ModeButton
2019-01-10 11:05:58 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
ModeButton :: ModeButton ( wxWindow * parent ,
wxWindowID id ,
const std :: string & icon_name /* = ""*/ ,
const wxString & mode /* = wxEmptyString*/ ,
const wxSize & size /* = wxDefaultSize*/ ,
const wxPoint & pos /* = wxDefaultPosition*/ ) :
2019-07-25 16:23:32 +02:00
ScalableButton ( parent , id , icon_name , mode , size , pos , wxBU_EXACTFIT )
2020-02-10 12:46:07 +01:00
{
Init ( mode );
}
ModeButton :: ModeButton ( wxWindow * parent ,
const wxString & mode /* = wxEmptyString*/ ,
const std :: string & icon_name /* = ""*/ ,
int px_cnt /* = 16*/ ) :
ScalableButton ( parent , wxID_ANY , ScalableBitmap ( parent , icon_name , px_cnt ), mode , wxBU_EXACTFIT )
{
Init ( mode );
}
void ModeButton :: Init ( const wxString & mode )
2019-01-10 11:05:58 +01:00
{
2020-03-02 12:46:40 +01:00
std :: string mode_str = std :: string ( mode . ToUTF8 ());
2020-03-04 09:24:13 +01:00
m_tt_focused = Slic3r :: GUI :: from_u8 (( boost :: format ( _utf8 ( L ( "Switch to the %s mode" ))) % mode_str ). str ());
m_tt_selected = Slic3r :: GUI :: from_u8 (( boost :: format ( _utf8 ( L ( "Current mode is %s" ))) % mode_str ). str ());
2019-02-26 10:50:00 +01:00
2019-07-25 16:23:32 +02:00
SetBitmapMargins ( 3 , 0 );
2019-01-10 11:05:58 +01:00
//button events
2019-04-25 01:45:00 +02:00
Bind ( wxEVT_BUTTON , & ModeButton :: OnButton , this );
Bind ( wxEVT_ENTER_WINDOW , & ModeButton :: OnEnterBtn , this );
Bind ( wxEVT_LEAVE_WINDOW , & ModeButton :: OnLeaveBtn , this );
2019-01-10 11:05:58 +01:00
}
2019-04-25 01:45:00 +02:00
void ModeButton :: OnButton ( wxCommandEvent & event )
2019-01-10 11:05:58 +01:00
{
m_is_selected = true ;
focus_button ( m_is_selected );
event . Skip ();
}
2019-04-25 01:45:00 +02:00
void ModeButton :: SetState ( const bool state )
2019-01-10 11:05:58 +01:00
{
m_is_selected = state ;
focus_button ( m_is_selected );
2019-02-26 10:50:00 +01:00
SetToolTip ( state ? m_tt_selected : m_tt_focused );
2019-01-10 11:05:58 +01:00
}
2019-04-25 01:45:00 +02:00
void ModeButton :: focus_button ( const bool focus )
2019-01-10 11:05:58 +01:00
{
2019-04-29 09:43:42 +02:00
const wxFont & new_font = focus ?
Slic3r :: GUI :: wxGetApp (). bold_font () :
Slic3r :: GUI :: wxGetApp (). normal_font ();
2019-04-10 09:56:32 +02:00
2019-01-10 11:05:58 +01:00
SetFont ( new_font );
2019-07-25 16:23:32 +02:00
SetForegroundColour ( wxSystemSettings :: GetColour ( focus ? wxSYS_COLOUR_BTNTEXT : wxSYS_COLOUR_BTNSHADOW ));
2019-01-10 11:05:58 +01:00
Refresh ();
Update ();
}
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// ModeSizer
2019-01-10 11:05:58 +01:00
// ----------------------------------------------------------------------------
2019-07-25 16:23:32 +02:00
ModeSizer :: ModeSizer ( wxWindow * parent , int hgap /* = 0*/ ) :
2019-02-11 15:36:05 +01:00
wxFlexGridSizer ( 3 , 0 , hgap )
2019-01-10 11:05:58 +01:00
{
SetFlexibleDirection ( wxHORIZONTAL );
2019-04-13 23:46:52 +02:00
std :: vector < std :: pair < wxString , std :: string >> buttons = {
2020-02-07 18:49:39 +01:00
{ _ ( L ( "Simple" )), "mode_simple" },
2020-04-16 11:36:41 +02:00
// {_(L("Advanced")), "mode_advanced"},
{ _CTX ( L_CONTEXT ( "Advanced" , "Mode" ), "Mode" ), "mode_advanced" },
2020-02-07 18:49:39 +01:00
{ _ ( L ( "Expert" )), "mode_expert" },
2019-02-11 14:14:35 +01:00
};
2019-02-06 15:29:13 +01:00
2019-05-21 14:06:43 +02:00
auto modebtnfn = []( wxCommandEvent & event , int mode_id ) {
Slic3r :: GUI :: wxGetApp (). save_mode ( mode_id );
event . Skip ();
};
2019-04-25 01:45:00 +02:00
m_mode_btns . reserve ( 3 );
2019-02-11 14:14:35 +01:00
for ( const auto & button : buttons ) {
2020-02-10 20:05:20 +01:00
m_mode_btns . push_back ( new ModeButton ( parent , button . first , button . second , mode_icon_px_size ()));
2019-07-25 16:23:32 +02:00
2019-05-27 13:07:37 +02:00
m_mode_btns . back () -> Bind ( wxEVT_BUTTON , std :: bind ( modebtnfn , std :: placeholders :: _1 , int ( m_mode_btns . size () - 1 )));
2019-05-21 14:06:43 +02:00
Add ( m_mode_btns . back ());
2019-02-11 14:14:35 +01:00
}
2019-01-10 11:05:58 +01:00
}
2019-04-25 01:45:00 +02:00
void ModeSizer :: SetMode ( const int mode )
2019-01-10 11:05:58 +01:00
{
2019-05-21 14:06:43 +02:00
for ( size_t m = 0 ; m < m_mode_btns . size (); m ++ )
m_mode_btns [ m ] -> SetState ( int ( m ) == mode );
2019-01-10 11:05:58 +01:00
}
2019-01-25 13:16:32 +01:00
2019-04-25 01:45:00 +02:00
void ModeSizer :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2019-05-21 14:06:43 +02:00
for ( size_t m = 0 ; m < m_mode_btns . size (); m ++ )
2019-04-25 01:45:00 +02:00
m_mode_btns [ m ] -> msw_rescale ();
2019-04-13 23:46:52 +02:00
}
2019-01-25 13:16:32 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// MenuWithSeparators
2019-01-25 13:16:32 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
void MenuWithSeparators :: DestroySeparators ()
2019-01-25 13:16:32 +01:00
{
if ( m_separator_frst ) {
Destroy ( m_separator_frst );
m_separator_frst = nullptr ;
}
if ( m_separator_scnd ) {
Destroy ( m_separator_scnd );
m_separator_scnd = nullptr ;
}
}
2019-04-25 01:45:00 +02:00
void MenuWithSeparators :: SetFirstSeparator ()
{
m_separator_frst = this -> AppendSeparator ();
}
void MenuWithSeparators :: SetSecondSeparator ()
{
m_separator_scnd = this -> AppendSeparator ();
}
2019-01-25 13:16:32 +01:00
2019-04-13 23:46:52 +02:00
// ----------------------------------------------------------------------------
// PrusaBitmap
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
ScalableBitmap :: ScalableBitmap ( wxWindow * parent ,
const std :: string & icon_name /* = ""*/ ,
2020-06-26 09:58:39 +02:00
const int px_cnt /* = 16*/ ,
const bool grayscale /* = false*/ ) :
2019-04-25 01:45:00 +02:00
m_parent ( parent ), m_icon_name ( icon_name ),
2020-01-28 16:15:43 +01:00
m_px_cnt ( px_cnt )
2019-04-13 23:46:52 +02:00
{
2020-06-26 09:58:39 +02:00
m_bmp = create_scaled_bitmap ( icon_name , parent , px_cnt , grayscale );
2020-01-31 16:50:11 +01:00
}
wxSize ScalableBitmap :: GetBmpSize () const
{
#ifdef __APPLE__
return m_bmp . GetScaledSize ();
#else
return m_bmp . GetSize ();
#endif
}
int ScalableBitmap :: GetBmpWidth () const
{
#ifdef __APPLE__
return m_bmp . GetScaledWidth ();
#else
return m_bmp . GetWidth ();
#endif
}
int ScalableBitmap :: GetBmpHeight () const
{
#ifdef __APPLE__
return m_bmp . GetScaledHeight ();
#else
return m_bmp . GetHeight ();
#endif
2019-04-13 23:46:52 +02:00
}
2019-04-25 01:45:00 +02:00
void ScalableBitmap :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2020-06-26 09:58:39 +02:00
m_bmp = create_scaled_bitmap ( m_icon_name , m_parent , m_px_cnt , m_grayscale );
2019-04-13 23:46:52 +02:00
}
// ----------------------------------------------------------------------------
// PrusaButton
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
ScalableButton :: ScalableButton ( wxWindow * parent ,
wxWindowID id ,
const std :: string & icon_name /*= ""*/ ,
const wxString & label /* = wxEmptyString*/ ,
const wxSize & size /* = wxDefaultSize*/ ,
const wxPoint & pos /* = wxDefaultPosition*/ ,
2020-08-10 19:07:45 +02:00
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/ ,
2020-10-15 16:48:48 +02:00
bool use_default_disabled_bitmap /* = false*/ ,
int bmp_px_cnt /* = 16*/ ) :
2020-08-10 19:07:45 +02:00
m_parent ( parent ),
2019-04-13 23:46:52 +02:00
m_current_icon_name ( icon_name ),
2020-10-15 16:48:48 +02:00
m_use_default_disabled_bitmap ( use_default_disabled_bitmap ),
m_px_cnt ( bmp_px_cnt )
2019-04-13 23:46:52 +02:00
{
Create ( parent , id , label , pos , size , style );
#ifdef __WXMSW__
if ( style & wxNO_BORDER )
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
#endif // __WXMSW__
2019-05-17 19:11:11 +02:00
2020-10-15 16:48:48 +02:00
SetBitmap ( create_scaled_bitmap ( icon_name , parent , m_px_cnt ));
2020-08-10 19:07:45 +02:00
if ( m_use_default_disabled_bitmap )
SetBitmapDisabled ( create_scaled_bitmap ( m_current_icon_name , m_parent , m_px_cnt , true ));
2019-08-01 11:35:43 +02:00
if ( size != wxDefaultSize )
{
const int em = em_unit ( parent );
m_width = size . x / em ;
m_height = size . y / em ;
}
2019-04-13 23:46:52 +02:00
}
2019-04-25 01:45:00 +02:00
ScalableButton :: ScalableButton ( wxWindow * parent ,
wxWindowID id ,
const ScalableBitmap & bitmap ,
const wxString & label /*= wxEmptyString*/ ,
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/ ) :
2019-12-13 13:23:55 +01:00
m_parent ( parent ),
2019-04-13 23:46:52 +02:00
m_current_icon_name ( bitmap . name ()),
2020-01-28 16:15:43 +01:00
m_px_cnt ( bitmap . px_cnt ())
2019-04-13 23:46:52 +02:00
{
Create ( parent , id , label , wxDefaultPosition , wxDefaultSize , style );
#ifdef __WXMSW__
if ( style & wxNO_BORDER )
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
#endif // __WXMSW__
2019-04-16 10:05:45 +02:00
SetBitmap ( bitmap . bmp ());
2019-04-13 23:46:52 +02:00
}
2019-04-25 01:45:00 +02:00
void ScalableButton :: SetBitmap_ ( const ScalableBitmap & bmp )
2019-04-13 23:46:52 +02:00
{
SetBitmap ( bmp . bmp ());
m_current_icon_name = bmp . name ();
}
2019-08-01 11:35:43 +02:00
void ScalableButton :: SetBitmapDisabled_ ( const ScalableBitmap & bmp )
{
SetBitmapDisabled ( bmp . bmp ());
m_disabled_icon_name = bmp . name ();
}
2019-12-13 17:56:04 +01:00
int ScalableButton :: GetBitmapHeight ()
{
2020-01-31 16:50:11 +01:00
#ifdef __APPLE__
return GetBitmap (). GetScaledHeight ();
#else
return GetBitmap (). GetHeight ();
#endif
2019-12-13 17:56:04 +01:00
}
2020-08-10 19:07:45 +02:00
void ScalableButton :: UseDefaultBitmapDisabled ()
{
m_use_default_disabled_bitmap = true ;
SetBitmapDisabled ( create_scaled_bitmap ( m_current_icon_name , m_parent , m_px_cnt , true ));
}
2019-04-25 01:45:00 +02:00
void ScalableButton :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2020-01-31 16:50:11 +01:00
SetBitmap ( create_scaled_bitmap ( m_current_icon_name , m_parent , m_px_cnt ));
2019-08-01 11:35:43 +02:00
if ( ! m_disabled_icon_name . empty ())
2020-01-31 16:50:11 +01:00
SetBitmapDisabled ( create_scaled_bitmap ( m_disabled_icon_name , m_parent , m_px_cnt ));
2020-08-10 19:07:45 +02:00
else if ( m_use_default_disabled_bitmap )
SetBitmapDisabled ( create_scaled_bitmap ( m_current_icon_name , m_parent , m_px_cnt , true ));
2019-04-13 23:46:52 +02:00
2019-08-01 11:35:43 +02:00
if ( m_width > 0 || m_height > 0 )
{
const int em = em_unit ( m_parent );
wxSize size ( m_width * em , m_height * em );
SetMinSize ( size );
}
2019-04-13 23:46:52 +02:00
}
2018-08-29 11:21:22 +02:00
2018-08-28 15:51:53 +02:00
2020-04-16 09:59:12 +02:00
// ----------------------------------------------------------------------------
// BlinkingBitmap
// ----------------------------------------------------------------------------
BlinkingBitmap :: BlinkingBitmap ( wxWindow * parent , const std :: string & icon_name ) :
wxStaticBitmap ( parent , wxID_ANY , wxNullBitmap , wxDefaultPosition , wxSize ( int ( 1.6 * Slic3r :: GUI :: wxGetApp (). em_unit ()), - 1 ))
{
bmp = ScalableBitmap ( parent , icon_name );
}
void BlinkingBitmap :: msw_rescale ()
{
bmp . msw_rescale ();
this -> SetSize ( bmp . GetBmpSize ());
this -> SetMinSize ( bmp . GetBmpSize ());
}
void BlinkingBitmap :: invalidate ()
{
this -> SetBitmap ( wxNullBitmap );
}
void BlinkingBitmap :: activate ()
{
this -> SetBitmap ( bmp . bmp ());
show = true ;
}
void BlinkingBitmap :: blink ()
{
show = ! show ;
this -> SetBitmap ( show ? bmp . bmp () : wxNullBitmap );
}
2018-08-29 11:21:22 +02:00