#include "BitmapComboBox.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #ifdef _MSW_DARK_MODE #include #endif //_MSW_DARK_MODE #endif #include "libslic3r/libslic3r.h" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "Plater.hpp" #include "MainFrame.hpp" #include "format.hpp" // A workaround for a set of issues related to text fitting into gtk widgets: // See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584 #if defined(__WXGTK20__) || defined(__WXGTK3__) #include #include #include #endif using Slic3r::GUI::format_wxstr; #define BORDER_W 10 // --------------------------------- // *** BitmapComboBox *** // --------------------------------- namespace Slic3r { namespace GUI { /* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean * "please scale this to such and such" but rather * "the wxImage is already sized for backing scale such and such". ) * Unfortunately, the constructor changes the size of wxBitmap too. * Thus We need to use unscaled size value for bitmaps that we use * to avoid scaled size of control items. * For this purpose control drawing methods and * control size calculation methods (virtual) are overridden. **/ BitmapComboBox::BitmapComboBox(wxWindow* parent, wxWindowID id/* = wxID_ANY*/, const wxString& value/* = wxEmptyString*/, const wxPoint& pos/* = wxDefaultPosition*/, const wxSize& size/* = wxDefaultSize*/, int n/* = 0*/, const wxString choices[]/* = NULL*/, long style/* = 0*/) : wxBitmapComboBox(parent, id, value, pos, size, n, choices, style) { SetFont(Slic3r::GUI::wxGetApp().normal_font()); #ifdef _WIN32 // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that // the index of the item inside CBN_EDITCHANGE may no more be valid. EnableTextChangedEvents(false); wxGetApp().UpdateDarkUI(this); if (!HasFlag(wxCB_READONLY)) wxTextEntry::SetMargins(0,0); #endif /* _WIN32 */ } BitmapComboBox::~BitmapComboBox() { } #ifdef __APPLE__ bool BitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) { if (bitmap.IsOk()) { // we should use scaled! size values of bitmap int width = (int)bitmap.GetScaledWidth(); int height = (int)bitmap.GetScaledHeight(); if (m_usedImgSize.x < 0) { // If size not yet determined, get it from this image. m_usedImgSize.x = width; m_usedImgSize.y = height; // Adjust control size to vertically fit the bitmap wxWindow* ctrl = GetControl(); ctrl->InvalidateBestSize(); wxSize newSz = ctrl->GetBestSize(); wxSize sz = ctrl->GetSize(); if (newSz.y > sz.y) ctrl->SetSize(sz.x, newSz.y); else DetermineIndent(); } wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, false, "you can only add images of same size"); return true; } return false; } void BitmapComboBox::OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const { const wxBitmap& bmp = *(static_cast(m_bitmaps[item])); if (bmp.IsOk()) { // we should use scaled! size values of bitmap wxCoord w = bmp.GetScaledWidth(); wxCoord h = bmp.GetScaledHeight(); const int imgSpacingLeft = 4; // Draw the image centered dc.DrawBitmap(bmp, rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, rect.y + (rect.height - h) / 2, true); } wxString text = GetString(item); if (!text.empty()) dc.DrawText(text, rect.x + m_imgAreaWidth + 1, rect.y + (rect.height - dc.GetCharHeight()) / 2); } #endif #ifdef _WIN32 int BitmapComboBox::Append(const wxString& item) { // Workaround for a correct rendering of the control without Bitmap (under MSW): //1. We should create small Bitmap to fill Bitmaps RefData, // ! in this case wxBitmap.IsOK() return true. //2. But then set width to 0 value for no using of bitmap left and right spacing //3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1)); bitmap.SetWidth(0); OnAddBitmap(bitmap); const int n = wxComboBox::Append(item); if (n != wxNOT_FOUND) DoSetItemBitmap(n, bitmap); return n; } enum OwnerDrawnComboBoxPaintingFlags { ODCB_PAINTING_DISABLED = 0x0004, }; bool BitmapComboBox::MSWOnDraw(WXDRAWITEMSTRUCT* item) { LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)item; int pos = lpDrawItem->itemID; // Draw default for item -1, which means 'focus rect only' if (pos == -1) return false; int flags = 0; if (lpDrawItem->itemState & ODS_COMBOBOXEDIT) flags |= wxODCB_PAINTING_CONTROL; if (lpDrawItem->itemState & ODS_SELECTED) flags |= wxODCB_PAINTING_SELECTED; if (lpDrawItem->itemState & ODS_DISABLED) flags |= ODCB_PAINTING_DISABLED; wxPaintDCEx dc(this, lpDrawItem->hDC); wxRect rect = wxRectFromRECT(lpDrawItem->rcItem); DrawBackground_(dc, rect, pos, flags); wxString text; if (flags & wxODCB_PAINTING_CONTROL) { // Don't draw anything in the editable selection field. //if (!HasFlag(wxCB_READONLY)) // return true; pos = GetSelection(); // Skip drawing if there is nothing selected. if (pos < 0) return true; text = GetValue(); } else { text = GetString(pos); } wxBitmapComboBoxBase::DrawItem(dc, rect, pos, text, flags); return true; } void BitmapComboBox::DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(item), int flags) const { if (flags & wxODCB_PAINTING_SELECTED) { const int vSizeDec = 0; // Vertical size reduction of selection rectangle edges dc.SetTextForeground(wxGetApp().get_label_highlight_clr()); wxColour selCol = wxGetApp().get_highlight_default_clr(); dc.SetPen(selCol); dc.SetBrush(selCol); dc.DrawRectangle(rect.x, rect.y + vSizeDec, rect.width, rect.height - (vSizeDec * 2)); } else { dc.SetTextForeground(flags & ODCB_PAINTING_DISABLED ? wxColour(108,108,108) : wxGetApp().get_label_clr_default()); wxColour selCol = flags & ODCB_PAINTING_DISABLED ? #ifdef _MSW_DAEK_MODE wxRGBToColour(NppDarkMode::InvertLightnessSofter(NppDarkMode::GetBackgroundColor())) : #else wxGetApp().get_highlight_default_clr() : #endif wxGetApp().get_window_default_clr(); dc.SetPen(selCol); dc.SetBrush(selCol); dc.DrawRectangle(rect); } } void BitmapComboBox::Rescale() { // Next workaround: To correct scaling of a BitmapCombobox // we need to refill control with new bitmaps const wxString selection = this->GetValue(); std::vector items; for (size_t i = 0; i < GetCount(); i++) items.push_back(GetString(i)); this->Clear(); for (const wxString& item : items) Append(item); this->SetValue(selection); } #endif }}