From 120ac092e38993a91132b6b3d87777ef8e728f0e Mon Sep 17 00:00:00 2001 From: "chunmao.guo" Date: Fri, 22 Nov 2024 16:44:51 +0800 Subject: [PATCH] ENH: ComboBox second drop list & align center Change-Id: I468468a1a86bb8e89468070b0323aace6279fd09 Jira: STUDIO-8857 --- src/slic3r/GUI/PresetComboBoxes.cpp | 2 +- src/slic3r/GUI/Widgets/ComboBox.cpp | 81 ++++---- src/slic3r/GUI/Widgets/ComboBox.hpp | 13 +- src/slic3r/GUI/Widgets/DropDown.cpp | 289 +++++++++++++++++++++------ src/slic3r/GUI/Widgets/DropDown.hpp | 56 ++++-- src/slic3r/GUI/Widgets/TextInput.cpp | 18 +- 6 files changed, 326 insertions(+), 133 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 021028769..387655c3a 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1088,7 +1088,7 @@ void PlaterPresetComboBox::update() } } else { for (std::map::const_iterator it = presets.begin(); it != presets.end(); ++it) { - SetItemTooltip(Append(it->first, *it->second), preset_descriptions[it->first]); + SetItemTooltip(Append(it->first, *it->second, it->first.Left(13)), preset_descriptions[it->first]); validate_selection(it->first == selected); } } diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index a0c651b45..35ea177ca 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -39,10 +39,10 @@ ComboBox::ComboBox(wxWindow *parent, int n, const wxString choices[], long style) - : drop(texts, tips, icons) + : drop(items) { - if (style & wxCB_READONLY) - style |= wxRIGHT; + if ((style & wxALIGN_MASK) == 0 && (style & wxCB_READONLY)) + style |= wxALIGN_CENTER_HORIZONTAL; text_off = style & CB_NO_TEXT; TextInput::Create(parent, "", value, (style & CB_NO_DROP_ICON) ? "" : "drop_down", pos, size, style | wxTE_PROCESS_ENTER); @@ -84,8 +84,8 @@ void ComboBox::SetSelection(int n) return; drop.SetSelection(n); SetLabel(drop.GetValue()); - if (drop.selection >= 0 && drop.iconSize.y > 0 && icons[drop.selection].IsOk()) - SetIcon(icons[drop.selection]); + if (drop.selection >= 0 && drop.iconSize.y > 0 && items[drop.selection].icon.IsOk()) + SetIcon(items[drop.selection].icon); else SetIcon("drop_down"); } @@ -109,8 +109,8 @@ void ComboBox::SetValue(const wxString &value) { drop.SetValue(value); SetLabel(value); - if (drop.selection >= 0 && drop.iconSize.y > 0 && icons[drop.selection].IsOk()) - SetIcon(icons[drop.selection]); + if (drop.selection >= 0 && drop.iconSize.y > 0 && items[drop.selection].icon.IsOk()) + SetIcon(items[drop.selection].icon); else SetIcon("drop_down"); } @@ -154,72 +154,67 @@ int ComboBox::Append(const wxString &item, const wxBitmap &bitmap) return Append(item, bitmap, nullptr); } -int ComboBox::Append(const wxString &item, +int ComboBox::Append(const wxString &text, const wxBitmap &bitmap, void * clientData) { - texts.push_back(item); - tips.push_back(wxString{}); - icons.push_back(bitmap); - datas.push_back(clientData); + return Append(text, bitmap, wxString{}, clientData); +} + +int ComboBox::Append(const wxString &text, const wxBitmap &bitmap, const wxString &group, void *clientData) +{ + Item item{text, bitmap, clientData, group}; + items.push_back(item); SetClientDataType(wxClientData_Void); drop.Invalidate(); - return texts.size() - 1; + return items.size() - 1; } void ComboBox::DoClear() { SetIcon("drop_down"); - texts.clear(); - tips.clear(); - icons.clear(); - datas.clear(); + items.clear(); drop.Invalidate(true); } void ComboBox::DoDeleteOneItem(unsigned int pos) { - if (pos >= texts.size()) return; - texts.erase(texts.begin() + pos); - tips.erase(tips.begin() + pos); - icons.erase(icons.begin() + pos); - datas.erase(datas.begin() + pos); + if (pos >= items.size()) return; + items.erase(items.begin() + pos); drop.Invalidate(true); } -unsigned int ComboBox::GetCount() const { return texts.size(); } +unsigned int ComboBox::GetCount() const { return items.size(); } wxString ComboBox::GetString(unsigned int n) const -{ - return n < texts.size() ? texts[n] : wxString{}; -} +{ return n < items.size() ? items[n].text : wxString{}; } void ComboBox::SetString(unsigned int n, wxString const &value) { - if (n >= texts.size()) return; - texts[n] = value; + if (n >= items.size()) return; + items[n].text = value; drop.Invalidate(); if (n == drop.GetSelection()) SetLabel(value); } wxString ComboBox::GetItemTooltip(unsigned int n) const { - if (n >= texts.size()) return wxString(); - return tips[n]; + if (n >= items.size()) return wxString(); + return items[n].tip; } void ComboBox::SetItemTooltip(unsigned int n, wxString const &value) { - if (n >= texts.size()) return; - tips[n] = value; + if (n >= items.size()) return; + items[n].tip = value; if (n == drop.GetSelection()) drop.SetToolTip(value); } -wxBitmap ComboBox::GetItemBitmap(unsigned int n) { return icons[n]; } +wxBitmap ComboBox::GetItemBitmap(unsigned int n) { return items[n].icon; } void ComboBox::SetItemBitmap(unsigned int n, wxBitmap const &bitmap) { - if (n >= texts.size()) return; - icons[n] = bitmap; + if (n >= items.size()) return; + items[n].icon = bitmap; drop.Invalidate(); } @@ -228,24 +223,22 @@ int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items, void ** clientData, wxClientDataType type) { - if (pos > texts.size()) return -1; + if (pos > this->items.size()) return -1; for (int i = 0; i < items.GetCount(); ++i) { - texts.insert(texts.begin() + pos, items[i]); - tips.insert(tips.begin() + pos, wxString{}); - icons.insert(icons.begin() + pos, wxNullBitmap); - datas.insert(datas.begin() + pos, clientData ? clientData[i] : NULL); + Item item { items[i], wxNullBitmap, clientData ? clientData[i] : NULL }; + this->items.insert(this->items.begin() + pos, item); ++pos; } drop.Invalidate(true); return pos - 1; } -void *ComboBox::DoGetItemClientData(unsigned int n) const { return n < texts.size() ? datas[n] : NULL; } +void *ComboBox::DoGetItemClientData(unsigned int n) const { return n < items.size() ? items[n].data : NULL; } void ComboBox::DoSetItemClientData(unsigned int n, void *data) { - if (n < texts.size()) - datas[n] = data; + if (n < items.size()) + items[n].data = data; } void ComboBox::mouseDown(wxMouseEvent &event) @@ -295,7 +288,7 @@ void ComboBox::keyDown(wxKeyEvent& event) case WXK_RIGHT: if ((event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) && GetSelection() > 0) { SetSelection(GetSelection() - 1); - } else if ((event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_RIGHT) && GetSelection() + 1 < texts.size()) { + } else if ((event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_RIGHT) && GetSelection() + 1 < items.size()) { SetSelection(GetSelection() + 1); } else { break; diff --git a/src/slic3r/GUI/Widgets/ComboBox.hpp b/src/slic3r/GUI/Widgets/ComboBox.hpp index 5eca3d1b7..b5dba8903 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.hpp +++ b/src/slic3r/GUI/Widgets/ComboBox.hpp @@ -9,10 +9,8 @@ class ComboBox : public wxWindowWithItems { - std::vector texts; - std::vector tips; - std::vector icons; - std::vector datas; + typedef DropDown::Item Item; + std::vector items; DropDown drop; bool drop_down = false; @@ -37,6 +35,8 @@ public: int Append(const wxString &item, const wxBitmap &bitmap, void *clientData); + int Append(const wxString &item, const wxBitmap &bitmap, const wxString &group, void *clientData = nullptr); + unsigned int GetCount() const override; int GetSelection() const override; @@ -62,6 +62,9 @@ public: wxString GetItemTooltip(unsigned int n) const; void SetItemTooltip(unsigned int n, wxString const &value); + wxString GetItemAlias(unsigned int n) const; + void SetItemAlias(unsigned int n, wxString const &value); + wxBitmap GetItemBitmap(unsigned int n); void SetItemBitmap(unsigned int n, wxBitmap const &bitmap); bool is_drop_down(){return drop_down;} @@ -77,7 +80,7 @@ protected: void *DoGetItemClientData(unsigned int n) const override; void DoSetItemClientData(unsigned int n, void *data) override; - + void OnEdit() override; void sendComboBoxEvent(); diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index 6db97bd07..c0ba1c8af 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -30,12 +30,8 @@ END_EVENT_TABLE() * calling Refresh()/Update(). */ -DropDown::DropDown(std::vector &texts, - std::vector &tips, - std::vector &icons) - : texts(texts) - , tips(tips) - , icons(icons) +DropDown::DropDown(std::vector &items) + : items(items) , state_handler(this) , border_color(0xDBDBDB) , text_color(0x363636) @@ -46,18 +42,13 @@ DropDown::DropDown(std::vector &texts, { } -DropDown::DropDown(wxWindow * parent, - std::vector &texts, - std::vector &tips, - std::vector &icons, - long style) - : DropDown(texts, tips, icons) +DropDown::DropDown(wxWindow *parent, std::vector &items, long style) + : DropDown(items) { Create(parent, style); } -void DropDown::Create(wxWindow * parent, - long style) +void DropDown::Create(wxWindow *parent, long style) { PopupWindow::Create(parent, wxPU_CONTAINS_CONTROLS); SetBackgroundStyle(wxBG_STYLE_PAINT); @@ -83,14 +74,14 @@ void DropDown::Invalidate(bool clear) selection = hover_item = -1; offset = wxPoint(); } - assert(selection < (int) texts.size()); + assert(selection < (int) items.size()); need_sync = true; } void DropDown::SetSelection(int n) { - assert(n < (int) texts.size()); - if (n >= (int) texts.size()) + assert(n < (int) items.size()); + if (n >= (int) items.size()) n = -1; if (selection == n) return; selection = n; @@ -98,18 +89,20 @@ void DropDown::SetSelection(int n) messureSize(); need_sync = true; } + if (subDropDown) + subDropDown->SetSelection(n); paintNow(); } wxString DropDown::GetValue() const { - return selection >= 0 ? texts[selection] : wxString(); + return selection >= 0 ? items[selection].text : wxString(); } void DropDown::SetValue(const wxString &value) { - auto i = std::find(texts.begin(), texts.end(), value); - selection = i == texts.end() ? -1 : std::distance(texts.begin(), i); + auto i = std::find_if(items.begin(), items.end(), [&value](Item & item) { return item.text == value; }); + selection = i == items.end() ? -1 : std::distance(items.begin(), i); } void DropDown::SetCornerRadius(double radius) @@ -209,8 +202,10 @@ static wxSize GetBmpSize(wxBitmap & bmp) */ void DropDown::render(wxDC &dc) { - if (texts.size() == 0) return; + if (items.size() == 0) return; int states = state_handler.states(); + if (subDropDown) + states |= subDropDown->state_handler.states(); dc.SetPen(wxPen(border_color.colorForStates(states))); dc.SetBrush(wxBrush(StateColor::darkModeColorFor(GetBackgroundColour()))); // if (GetWindowStyle() & wxBORDER_NONE) @@ -223,12 +218,14 @@ void DropDown::render(wxDC &dc) else dc.DrawRoundedRectangle(0, 0, size.x, size.y, radius); + int selected_item = selectedItem(); + // draw hover rectangle wxRect rcContent = {{0, offset.y}, rowSize}; if (hover_item >= 0 && (states & StateColor::Hovered)) { rcContent.y += rowSize.y * hover_item; if (rcContent.GetBottom() > 0 && rcContent.y < size.y) { - if (selection == hover_item) + if (selected_item == hover_item) dc.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked))); dc.SetPen(wxPen(selector_border_color.colorForStates(states))); rcContent.Deflate(4, 1); @@ -238,8 +235,8 @@ void DropDown::render(wxDC &dc) rcContent.y = offset.y; } // draw checked rectangle - if (selection >= 0 && (selection != hover_item || (states & StateColor::Hovered) == 0)) { - rcContent.y += rowSize.y * selection; + if (selected_item >= 0 && (selected_item != hover_item || (states & StateColor::Hovered) == 0)) { + rcContent.y += rowSize.y * selected_item; if (rcContent.GetBottom() > 0 && rcContent.y < size.y) { dc.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked))); dc.SetPen(wxPen(selector_background_color.colorForStates(states))); @@ -256,8 +253,8 @@ void DropDown::render(wxDC &dc) } // draw position bar - if (rowSize.y * texts.size() > size.y) { - int height = rowSize.y * texts.size(); + if (rowSize.y * count > size.y) { + int height = rowSize.y * count; wxRect rect = {size.x - 6, -offset.y * size.y / height, 4, size.y * size.y / height}; dc.SetPen(wxPen(border_color.defaultColor())); @@ -271,26 +268,43 @@ void DropDown::render(wxDC &dc) rcContent.width -= 5; if (check_bitmap.bmp().IsOk()) { auto szBmp = check_bitmap.GetBmpSize(); - if (selection >= 0) { + if (selected_item >= 0) { wxPoint pt = rcContent.GetLeftTop(); pt.y += (rcContent.height - szBmp.y) / 2; - pt.y += rowSize.y * selection; + pt.y += rowSize.y * selected_item; if (pt.y + szBmp.y > 0 && pt.y < size.y) dc.DrawBitmap(check_bitmap.bmp(), pt); } rcContent.x += szBmp.x + 5; rcContent.width -= szBmp.x + 5; } + + std::set groups; // draw texts & icons dc.SetTextForeground(text_color.colorForStates(states)); - for (int i = 0; i < texts.size(); ++i) { + int index = 0; + for (int i = 0; i < items.size(); ++i) { + auto &item = items[i]; + // Skip by group + if (group.IsEmpty()) { + if (!item.group.IsEmpty()) { + if (groups.find(item.group) == groups.end()) + groups.insert(item.group); + else + continue; + } + } else { + if (item.group != group) + continue; + } + ++index; if (rcContent.GetBottom() < 0) { rcContent.y += rowSize.y; continue; } if (rcContent.y > size.y) break; wxPoint pt = rcContent.GetLeftTop(); - auto & icon = icons[i]; + auto & icon = item.icon; auto size2 = GetBmpSize(icon); if (iconSize.x > 0) { if (icon.IsOk()) { @@ -305,11 +319,13 @@ void DropDown::render(wxDC &dc) pt.x += size2.x + 5; pt.y = rcContent.y; } - auto text = texts[i]; + auto text = group.IsEmpty() + ? (item.group.IsEmpty() ? item.text : item.group) + : item.text.substr(group.size()).Trim(false); if (!text_off && !text.IsEmpty()) { wxSize tSize = dc.GetMultiLineTextExtent(text); if (pt.x + tSize.x > rcContent.GetRight()) { - if (i == hover_item && tips[i].IsEmpty()) + if (index == hover_item && item.tip.IsEmpty()) SetToolTip(text); text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, rcContent.GetRight() - pt.x); @@ -322,16 +338,98 @@ void DropDown::render(wxDC &dc) } } +int DropDown::hoverIndex() +{ + if (hover_item < 0) + return -1; + if (count == items.size()) + return hover_item; + int index = -1; + std::set groups; + for (size_t i = 0; i < items.size(); ++i) { + auto &item = items[i]; + // Skip by group + if (group.IsEmpty()) { + if (!item.group.IsEmpty()) { + if (groups.find(item.group) == groups.end()) + groups.insert(item.group); + else + continue; + } + } else { + if (item.group != group) + continue; + } + if (++index == hover_item) + return (item.group.IsEmpty() || !group.IsEmpty()) ? i : -i - 2; + } + return -1; +} + +int DropDown::selectedItem() +{ + if (selection < 0) + return -1; + if (count == items.size()) + return selection; + auto & sel = items[selection]; + if (group.IsEmpty() ? !sel.group.IsEmpty() : sel.group != group) + return -1; + if (selection == 0) + return 0; + int index = 0; + std::set groups; + for (size_t i = 0; i < selection; ++i) { + auto &item = items[i]; + // Skip by group + if (group.IsEmpty()) { + if (!item.group.IsEmpty()) { + if (groups.find(item.group) == groups.end()) + groups.insert(item.group); + else + continue; + } + } else { + if (item.group != group) + continue; + } + ++index; + } + return index; +} + void DropDown::messureSize() { if (!need_sync) return; textSize = wxSize(); iconSize = wxSize(); + count = 0; wxClientDC dc(GetParent() ? GetParent() : this); - for (size_t i = 0; i < texts.size(); ++i) { - wxSize size1 = text_off ? wxSize() : dc.GetMultiLineTextExtent(texts[i]); - if (icons[i].IsOk()) { - wxSize size2 = GetBmpSize(icons[i]); + std::set groups; + for (size_t i = 0; i < items.size(); ++i) { + auto &item = items[i]; + // Skip by group + if (group.IsEmpty()) { + if (!item.group.IsEmpty()) { + if (groups.find(item.group) == groups.end()) + groups.insert(item.group); + else + continue; + } + } else { + if (item.group != group) + continue; + } + ++count; + wxSize size1; + if (!text_off) { + auto text = group.IsEmpty() + ? (item.group.IsEmpty() ? item.text : item.group) + : item.text.substr(group.size()).Trim(false); + size1 = dc.GetMultiLineTextExtent(text); + } + if (item.icon.IsOk()) { + wxSize size2 = GetBmpSize(item.icon); if (size2.x > iconSize.x) iconSize = size2; if (!align_icon) { @@ -350,8 +448,8 @@ void DropDown::messureSize() if (iconSize.x > 0) szContent.x += iconSize.x + (text_off ? 0 : 5); if (iconSize.y > szContent.y) szContent.y = iconSize.y; szContent.y += 10; - if (texts.size() > 15) szContent.x += 6; - if (GetParent()) { + if (count > 15) szContent.x += 6; + if (GetParent() && group.IsEmpty()) { auto x = GetParent()->GetSize().x; if (!use_content_width || x > szContent.x) szContent.x = x; @@ -364,38 +462,66 @@ void DropDown::messureSize() szContent = rowSize; } } - szContent.y *= std::min((size_t)15, texts.size()); - szContent.y += texts.size() > 15 ? rowSize.y / 2 : 0; + szContent.y *= std::min((size_t) 15, count); + szContent.y += items.size() > 15 ? rowSize.y / 2 : 0; wxWindow::SetSize(szContent); #ifdef __WXGTK__ // Gtk has a wrapper window for popup widget gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y); #endif + if (!groups.empty() && subDropDown == nullptr) { + subDropDown = new DropDown(items); + subDropDown->mainDropDown = this; + subDropDown->check_bitmap = check_bitmap; + subDropDown->text_off = text_off; + subDropDown->use_content_width = true; + subDropDown->Create(GetParent()); + subDropDown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) { + e.SetEventObject(this); + e.SetId(GetId()); + GetEventHandler()->ProcessEvent(e); + }); + } need_sync = false; } void DropDown::autoPosition() { messureSize(); - wxPoint pos = GetParent()->ClientToScreen(wxPoint(0, -6)); + wxPoint pos; + wxSize off; + if (mainDropDown) { + pos = mainDropDown->ClientToScreen(wxPoint(0, 0)); + off = mainDropDown->GetSize(); + pos.x += 6; + pos.y += mainDropDown->hover_item * rowSize.y + rowSize.y + mainDropDown->offset.y; + off.x -= 12; + off.y = -rowSize.y; + } else { + pos = GetParent()->ClientToScreen(wxPoint(0, 0)); + off = GetParent()->GetSize(); + pos.y -= 6; + off.x = 0; + off.y += 12; + } wxPoint old = GetPosition(); wxSize size = GetSize(); - Position(pos, {0, GetParent()->GetSize().y + 12}); + Position(pos, off); if (old != GetPosition()) { size = rowSize; - size.y *= std::min((size_t)15, texts.size()); - size.y += texts.size() > 15 ? rowSize.y / 2 : 0; + size.y *= std::min((size_t) 15, count); + size.y += count > 15 ? rowSize.y / 2 : 0; if (size != GetSize()) { wxWindow::SetSize(size); offset = wxPoint(); - Position(pos, {0, GetParent()->GetSize().y + 12}); + Position(pos, off); } } if (GetPosition().y > pos.y) { // may exceed auto drect = wxDisplay(GetParent()).GetGeometry(); if (GetPosition().y + size.y + 10 > drect.GetBottom()) { - if (use_content_width && texts.size() <= 15) size.x += 6; + if (use_content_width && count <= 15) size.x += 6; size.y = drect.GetBottom() - GetPosition().y - 10; wxWindow::SetSize(size); if (selection >= 0) { @@ -443,14 +569,26 @@ void DropDown::mouseCaptureLost(wxMouseCaptureLostEvent &event) void DropDown::mouseMove(wxMouseEvent &event) { wxPoint pt = event.GetPosition(); +#ifdef __WXOSX__ + if (mainDropDown) { + auto size = GetSize(); + if (pt.x < 0 || pt.y < 0 || pt.x >= size.x || pt.y >= size.y) { + auto diff = GetPosition() - mainDropDown->GetPosition(); + event.SetX(pt.x + diff.x); + event.SetY(pt.y + diff.y); + mainDropDown->mouseMove(event); + return; + } + } +#endif if (pressedDown) { wxPoint pt2 = offset + pt - dragStart; wxSize size = GetSize(); dragStart = pt; if (pt2.y > 0) pt2.y = 0; - else if (pt2.y + rowSize.y * int(texts.size()) < size.y) - pt2.y = size.y - rowSize.y * int(texts.size()); + else if (pt2.y + rowSize.y * int(count) < size.y) + pt2.y = size.y - rowSize.y * int(count); if (pt2.y != offset.y) { offset = pt2; hover_item = -1; // moved @@ -460,10 +598,26 @@ void DropDown::mouseMove(wxMouseEvent &event) } if (!pressedDown || hover_item >= 0) { int hover = (pt.y - offset.y) / rowSize.y; - if (hover >= (int) texts.size()) hover = -1; + if (hover >= (int) count) hover = -1; if (hover == hover_item) return; hover_item = hover; - if (hover >= 0) SetToolTip(tips[hover]); + int index = hoverIndex(); + if (index < -1) { + auto & drop = *subDropDown; + drop.group = items[-index - 2].group; + drop.need_sync = true; + drop.messureSize(); + drop.autoPosition(); + drop.paintNow(); + if (!drop.IsShown()) + drop.Popup(&drop); + } else if (index >= 0) { + if (subDropDown) { + if (subDropDown->IsShown()) + subDropDown->Dismiss(); + } + SetToolTip(items[index].tip); + } } paintNow(); } @@ -475,18 +629,18 @@ void DropDown::mouseWheelMoved(wxMouseEvent &event) wxPoint pt2 = offset + wxPoint{0, delta}; if (pt2.y > 0) pt2.y = 0; - else if (pt2.y + rowSize.y * int(texts.size()) < size.y) - pt2.y = size.y - rowSize.y * int(texts.size()); + else if (pt2.y + rowSize.y * int(count) < size.y) + pt2.y = size.y - rowSize.y * int(count); if (pt2.y != offset.y) { offset = pt2; } else { return; } int hover = (event.GetPosition().y - offset.y) / rowSize.y; - if (hover >= (int) texts.size()) hover = -1; + if (hover >= (int) count) hover = -1; if (hover != hover_item) { hover_item = hover; - if (hover >= 0) SetToolTip(tips[hover]); + if (hover >= 0) SetToolTip(items[hover].tip); } paintNow(); } @@ -494,15 +648,38 @@ void DropDown::mouseWheelMoved(wxMouseEvent &event) // currently unused events void DropDown::sendDropDownEvent() { + int index = hoverIndex(); + if (index < 0) + return; wxCommandEvent event(wxEVT_COMBOBOX, GetId()); event.SetEventObject(this); - event.SetInt(hover_item); - event.SetString(texts[hover_item]); + event.SetInt(index); + event.SetString(items[index].text); GetEventHandler()->ProcessEvent(event); } +void DropDown::Dismiss() +{ + if (subDropDown && subDropDown->IsShown()) + return; + PopupWindow::Dismiss(); +} + void DropDown::OnDismiss() { + if (mainDropDown) { + if (mainDropDown->hover_item < 0) + mainDropDown->DismissAndNotify(); + else +#ifdef __WIN32__ + SetActiveWindow(mainDropDown->GetHandle()); +#else + ; +#endif + return; + } + if (subDropDown && subDropDown->IsShown()) + return; dismissTime = boost::posix_time::microsec_clock::universal_time(); hover_item = -1; wxCommandEvent e(EVT_DISMISS); diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp index e3cf9b453..005c0eda4 100644 --- a/src/slic3r/GUI/Widgets/DropDown.hpp +++ b/src/slic3r/GUI/Widgets/DropDown.hpp @@ -15,12 +15,27 @@ wxDECLARE_EVENT(EVT_DISMISS, wxCommandEvent); class DropDown : public PopupWindow { - std::vector & texts; - std::vector & tips; - std::vector & icons; - bool need_sync = false; - int selection = -1; - int hover_item = -1; +public: + struct Item + { + wxString text; + wxBitmap icon; + void * data{nullptr}; + wxString group{}; + wxString alias{}; + wxString tip{}; + }; + +private: + std::vector &items; + size_t count = 0; + wxString group; + bool need_sync = false; + int selection = -1; + int hover_item = -1; + + DropDown * subDropDown { nullptr }; + DropDown * mainDropDown { nullptr }; double radius = 0; bool use_content_width = false; @@ -45,19 +60,12 @@ class DropDown : public PopupWindow wxPoint dragStart; public: - DropDown(std::vector &texts, - std::vector &tips, - std::vector &icons); - - DropDown(wxWindow * parent, - std::vector &texts, - std::vector &tips, - std::vector &icons, - long style = 0); - - void Create(wxWindow * parent, - long style = 0); - + DropDown(std::vector &items); + + DropDown(wxWindow *parent, std::vector &items, long style = 0); + + void Create(wxWindow * parent, long style = 0); + public: void Invalidate(bool clear = false); @@ -82,13 +90,15 @@ public: void SetUseContentWidth(bool use, bool limit_max_content_width = false); void SetAlignIcon(bool align); - + public: void Rescale(); bool HasDismissLongTime(); - + protected: + void Dismiss() override; + void OnDismiss() override; private: @@ -97,6 +107,10 @@ private: void render(wxDC& dc); + int hoverIndex(); + + int selectedItem(); + friend class ComboBox; void messureSize(); void autoPosition(); diff --git a/src/slic3r/GUI/Widgets/TextInput.cpp b/src/slic3r/GUI/Widgets/TextInput.cpp index e8536735f..d09aa2285 100644 --- a/src/slic3r/GUI/Widgets/TextInput.cpp +++ b/src/slic3r/GUI/Widgets/TextInput.cpp @@ -54,7 +54,8 @@ void TextInput::Create(wxWindow * parent, text_ctrl = nullptr; StaticBox::Create(parent, wxID_ANY, pos, size, style); wxWindow::SetLabel(label); - style &= ~wxRIGHT; + assert((style & wxRIGHT) == 0); + style &= ~wxALIGN_MASK; state_handler.attach({&label_color, & text_color}); state_handler.update_binds(); text_ctrl = new TextCtrl(this, wxID_ANY, text, {4, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER); @@ -164,7 +165,7 @@ void TextInput::DoSetSize(int x, int y, int width, int height, int sizeFlags) wxSize szIcon = this->icon.GetBmpSize(); textPos.x += szIcon.x; } - bool align_right = GetWindowStyle() & wxRIGHT; + bool align_right = GetWindowStyle() & wxALIGN_RIGHT; if (align_right) textPos.x += labelSize.x; if (text_ctrl) { @@ -198,21 +199,26 @@ void TextInput::render(wxDC& dc) StaticBox::render(dc); int states = state_handler.states(); wxSize size = GetSize(); - bool align_right = GetWindowStyle() & wxRIGHT; + bool align_center = GetWindowStyle() & wxALIGN_CENTER_HORIZONTAL; + bool align_right = GetWindowStyle() & wxALIGN_RIGHT; // start draw wxPoint pt = {5, 0}; if (icon.bmp().IsOk()) { wxSize szIcon = icon.GetBmpSize(); pt.y = (size.y - szIcon.y) / 2; + if (align_center) { + if (pt.x * 2 + szIcon.x + 0 + labelSize.x < size.x) + pt.x = (size.x - (szIcon.x + 0 + labelSize.x)) / 2; + } dc.DrawBitmap(icon.bmp(), pt); pt.x += szIcon.x + 0; } auto text = wxWindow::GetLabel(); if (!text.IsEmpty()) { wxSize textSize = text_ctrl->GetSize(); - if (align_right) { - if (pt.x + labelSize.x > size.x) - text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x); + if (align_right || align_center) { + if (pt.x + labelSize.x + 5 > size.x) + text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x - 5); pt.y = (size.y - labelSize.y) / 2; } else { pt.x += textSize.x;