ENH: ComboBox second drop list & align center

Change-Id: I468468a1a86bb8e89468070b0323aace6279fd09
Jira: STUDIO-8857
(cherry picked from commit 120ac092e38993a91132b6b3d87777ef8e728f0e)
This commit is contained in:
chunmao.guo
2024-11-22 16:44:51 +08:00
committed by Noisyfox
parent d3a164251c
commit 4b9aaa7341
5 changed files with 325 additions and 132 deletions

View File

@@ -39,10 +39,10 @@ ComboBox::ComboBox(wxWindow *parent,
int n, int n,
const wxString choices[], const wxString choices[],
long style) long style)
: drop(texts, tips, icons) : drop(items)
{ {
if (style & wxCB_READONLY) if ((style & wxALIGN_MASK) == 0 && (style & wxCB_READONLY))
style |= wxRIGHT; style |= wxALIGN_CENTER_HORIZONTAL;
text_off = style & CB_NO_TEXT; text_off = style & CB_NO_TEXT;
TextInput::Create(parent, "", value, (style & CB_NO_DROP_ICON) ? "" : "drop_down", pos, size, TextInput::Create(parent, "", value, (style & CB_NO_DROP_ICON) ? "" : "drop_down", pos, size,
style | wxTE_PROCESS_ENTER); style | wxTE_PROCESS_ENTER);
@@ -85,8 +85,8 @@ void ComboBox::SetSelection(int n)
return; return;
drop.SetSelection(n); drop.SetSelection(n);
SetLabel(drop.GetValue()); SetLabel(drop.GetValue());
if (drop.selection >= 0 && drop.iconSize.y > 0 && icons[drop.selection].IsOk()) if (drop.selection >= 0 && drop.iconSize.y > 0 && items[drop.selection].icon.IsOk())
SetIcon(icons[drop.selection]); SetIcon(items[drop.selection].icon);
else else
SetIcon("drop_down"); SetIcon("drop_down");
} }
@@ -111,8 +111,8 @@ void ComboBox::SetValue(const wxString &value)
{ {
drop.SetValue(value); drop.SetValue(value);
SetLabel(value); SetLabel(value);
if (drop.selection >= 0 && drop.iconSize.y > 0 && icons[drop.selection].IsOk()) if (drop.selection >= 0 && drop.iconSize.y > 0 && items[drop.selection].icon.IsOk())
SetIcon(icons[drop.selection]); SetIcon(items[drop.selection].icon);
else else
SetIcon("drop_down"); SetIcon("drop_down");
} }
@@ -156,72 +156,67 @@ int ComboBox::Append(const wxString &item, const wxBitmap &bitmap)
return Append(item, bitmap, nullptr); return Append(item, bitmap, nullptr);
} }
int ComboBox::Append(const wxString &item, int ComboBox::Append(const wxString &text,
const wxBitmap &bitmap, const wxBitmap &bitmap,
void * clientData) void * clientData)
{ {
texts.push_back(item); return Append(text, bitmap, wxString{}, clientData);
tips.push_back(wxString{}); }
icons.push_back(bitmap);
datas.push_back(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); SetClientDataType(wxClientData_Void);
drop.Invalidate(); drop.Invalidate();
return texts.size() - 1; return items.size() - 1;
} }
void ComboBox::DoClear() void ComboBox::DoClear()
{ {
SetIcon("drop_down"); SetIcon("drop_down");
texts.clear(); items.clear();
tips.clear();
icons.clear();
datas.clear();
drop.Invalidate(true); drop.Invalidate(true);
} }
void ComboBox::DoDeleteOneItem(unsigned int pos) void ComboBox::DoDeleteOneItem(unsigned int pos)
{ {
if (pos >= texts.size()) return; if (pos >= items.size()) return;
texts.erase(texts.begin() + pos); items.erase(items.begin() + pos);
tips.erase(tips.begin() + pos);
icons.erase(icons.begin() + pos);
datas.erase(datas.begin() + pos);
drop.Invalidate(true); 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 wxString ComboBox::GetString(unsigned int n) const
{ { return n < items.size() ? items[n].text : wxString{}; }
return n < texts.size() ? texts[n] : wxString{};
}
void ComboBox::SetString(unsigned int n, wxString const &value) void ComboBox::SetString(unsigned int n, wxString const &value)
{ {
if (n >= texts.size()) return; if (n >= items.size()) return;
texts[n] = value; items[n].text = value;
drop.Invalidate(); drop.Invalidate();
if (n == drop.GetSelection()) SetLabel(value); if (n == drop.GetSelection()) SetLabel(value);
} }
wxString ComboBox::GetItemTooltip(unsigned int n) const wxString ComboBox::GetItemTooltip(unsigned int n) const
{ {
if (n >= texts.size()) return wxString(); if (n >= items.size()) return wxString();
return tips[n]; return items[n].tip;
} }
void ComboBox::SetItemTooltip(unsigned int n, wxString const &value) { void ComboBox::SetItemTooltip(unsigned int n, wxString const &value) {
if (n >= texts.size()) return; if (n >= items.size()) return;
tips[n] = value; items[n].tip = value;
if (n == drop.GetSelection()) drop.SetToolTip(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) void ComboBox::SetItemBitmap(unsigned int n, wxBitmap const &bitmap)
{ {
if (n >= texts.size()) return; if (n >= items.size()) return;
icons[n] = bitmap; items[n].icon = bitmap;
drop.Invalidate(); drop.Invalidate();
} }
@@ -230,24 +225,22 @@ int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items,
void ** clientData, void ** clientData,
wxClientDataType type) wxClientDataType type)
{ {
if (pos > texts.size()) return -1; if (pos > this->items.size()) return -1;
for (int i = 0; i < items.GetCount(); ++i) { for (int i = 0; i < items.GetCount(); ++i) {
texts.insert(texts.begin() + pos, items[i]); Item item { items[i], wxNullBitmap, clientData ? clientData[i] : NULL };
tips.insert(tips.begin() + pos, wxString{}); this->items.insert(this->items.begin() + pos, item);
icons.insert(icons.begin() + pos, wxNullBitmap);
datas.insert(datas.begin() + pos, clientData ? clientData[i] : NULL);
++pos; ++pos;
} }
drop.Invalidate(true); drop.Invalidate(true);
return pos - 1; 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) void ComboBox::DoSetItemClientData(unsigned int n, void *data)
{ {
if (n < texts.size()) if (n < items.size())
datas[n] = data; items[n].data = data;
} }
void ComboBox::mouseDown(wxMouseEvent &event) void ComboBox::mouseDown(wxMouseEvent &event)
@@ -297,7 +290,7 @@ void ComboBox::keyDown(wxKeyEvent& event)
case WXK_RIGHT: case WXK_RIGHT:
if ((event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) && GetSelection() > 0) { if ((event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) && GetSelection() > 0) {
SetSelection(GetSelection() - 1); 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); SetSelection(GetSelection() + 1);
} else { } else {
break; break;

View File

@@ -9,10 +9,8 @@
class ComboBox : public wxWindowWithItems<TextInput, wxItemContainer> class ComboBox : public wxWindowWithItems<TextInput, wxItemContainer>
{ {
std::vector<wxString> texts; typedef DropDown::Item Item;
std::vector<wxString> tips; std::vector<Item> items;
std::vector<wxBitmap> icons;
std::vector<void *> datas;
DropDown drop; DropDown drop;
bool drop_down = false; 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, void *clientData);
int Append(const wxString &item, const wxBitmap &bitmap, const wxString &group, void *clientData = nullptr);
unsigned int GetCount() const override; unsigned int GetCount() const override;
int GetSelection() const override; int GetSelection() const override;
@@ -62,6 +62,9 @@ public:
wxString GetItemTooltip(unsigned int n) const; wxString GetItemTooltip(unsigned int n) const;
void SetItemTooltip(unsigned int n, wxString const &value); 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); wxBitmap GetItemBitmap(unsigned int n);
void SetItemBitmap(unsigned int n, wxBitmap const &bitmap); void SetItemBitmap(unsigned int n, wxBitmap const &bitmap);
bool is_drop_down(){return drop_down;} bool is_drop_down(){return drop_down;}

View File

@@ -30,12 +30,8 @@ END_EVENT_TABLE()
* calling Refresh()/Update(). * calling Refresh()/Update().
*/ */
DropDown::DropDown(std::vector<wxString> &texts, DropDown::DropDown(std::vector<Item> &items)
std::vector<wxString> &tips, : items(items)
std::vector<wxBitmap> &icons)
: texts(texts)
, tips(tips)
, icons(icons)
, state_handler(this) , state_handler(this)
, border_color(0xDBDBDB) , border_color(0xDBDBDB)
, text_color(0x363636) , text_color(0x363636)
@@ -46,18 +42,13 @@ DropDown::DropDown(std::vector<wxString> &texts,
{ {
} }
DropDown::DropDown(wxWindow * parent, DropDown::DropDown(wxWindow *parent, std::vector<Item> &items, long style)
std::vector<wxString> &texts, : DropDown(items)
std::vector<wxString> &tips,
std::vector<wxBitmap> &icons,
long style)
: DropDown(texts, tips, icons)
{ {
Create(parent, style); Create(parent, style);
} }
void DropDown::Create(wxWindow * parent, void DropDown::Create(wxWindow *parent, long style)
long style)
{ {
PopupWindow::Create(parent, wxPU_CONTAINS_CONTROLS); PopupWindow::Create(parent, wxPU_CONTAINS_CONTROLS);
SetBackgroundStyle(wxBG_STYLE_PAINT); SetBackgroundStyle(wxBG_STYLE_PAINT);
@@ -83,14 +74,14 @@ void DropDown::Invalidate(bool clear)
selection = hover_item = -1; selection = hover_item = -1;
offset = wxPoint(); offset = wxPoint();
} }
assert(selection < (int) texts.size()); assert(selection < (int) items.size());
need_sync = true; need_sync = true;
} }
void DropDown::SetSelection(int n) void DropDown::SetSelection(int n)
{ {
assert(n < (int) texts.size()); assert(n < (int) items.size());
if (n >= (int) texts.size()) if (n >= (int) items.size())
n = -1; n = -1;
if (selection == n) return; if (selection == n) return;
selection = n; selection = n;
@@ -98,18 +89,20 @@ void DropDown::SetSelection(int n)
messureSize(); messureSize();
need_sync = true; need_sync = true;
} }
if (subDropDown)
subDropDown->SetSelection(n);
paintNow(); paintNow();
} }
wxString DropDown::GetValue() const wxString DropDown::GetValue() const
{ {
return selection >= 0 ? texts[selection] : wxString(); return selection >= 0 ? items[selection].text : wxString();
} }
void DropDown::SetValue(const wxString &value) void DropDown::SetValue(const wxString &value)
{ {
auto i = std::find(texts.begin(), texts.end(), value); auto i = std::find_if(items.begin(), items.end(), [&value](Item & item) { return item.text == value; });
selection = i == texts.end() ? -1 : std::distance(texts.begin(), i); selection = i == items.end() ? -1 : std::distance(items.begin(), i);
} }
void DropDown::SetCornerRadius(double radius) void DropDown::SetCornerRadius(double radius)
@@ -209,8 +202,10 @@ static wxSize GetBmpSize(wxBitmap & bmp)
*/ */
void DropDown::render(wxDC &dc) void DropDown::render(wxDC &dc)
{ {
if (texts.size() == 0) return; if (items.size() == 0) return;
int states = state_handler.states(); int states = state_handler.states();
if (subDropDown)
states |= subDropDown->state_handler.states();
dc.SetPen(wxPen(border_color.colorForStates(states))); dc.SetPen(wxPen(border_color.colorForStates(states)));
dc.SetBrush(wxBrush(StateColor::darkModeColorFor(GetBackgroundColour()))); dc.SetBrush(wxBrush(StateColor::darkModeColorFor(GetBackgroundColour())));
// if (GetWindowStyle() & wxBORDER_NONE) // if (GetWindowStyle() & wxBORDER_NONE)
@@ -223,12 +218,14 @@ void DropDown::render(wxDC &dc)
else else
dc.DrawRoundedRectangle(0, 0, size.x, size.y, radius); dc.DrawRoundedRectangle(0, 0, size.x, size.y, radius);
int selected_item = selectedItem();
// draw hover rectangle // draw hover rectangle
wxRect rcContent = {{0, offset.y}, rowSize}; wxRect rcContent = {{0, offset.y}, rowSize};
if (hover_item >= 0 && (states & StateColor::Hovered)) { if (hover_item >= 0 && (states & StateColor::Hovered)) {
rcContent.y += rowSize.y * hover_item; rcContent.y += rowSize.y * hover_item;
if (rcContent.GetBottom() > 0 && rcContent.y < size.y) { 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.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked)));
dc.SetPen(wxPen(selector_border_color.colorForStates(states))); dc.SetPen(wxPen(selector_border_color.colorForStates(states)));
rcContent.Deflate(4, 1); rcContent.Deflate(4, 1);
@@ -238,8 +235,8 @@ void DropDown::render(wxDC &dc)
rcContent.y = offset.y; rcContent.y = offset.y;
} }
// draw checked rectangle // draw checked rectangle
if (selection >= 0 && (selection != hover_item || (states & StateColor::Hovered) == 0)) { if (selected_item >= 0 && (selected_item != hover_item || (states & StateColor::Hovered) == 0)) {
rcContent.y += rowSize.y * selection; rcContent.y += rowSize.y * selected_item;
if (rcContent.GetBottom() > 0 && rcContent.y < size.y) { if (rcContent.GetBottom() > 0 && rcContent.y < size.y) {
dc.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked))); dc.SetBrush(wxBrush(selector_background_color.colorForStates(states | StateColor::Checked)));
dc.SetPen(wxPen(selector_background_color.colorForStates(states))); dc.SetPen(wxPen(selector_background_color.colorForStates(states)));
@@ -256,8 +253,8 @@ void DropDown::render(wxDC &dc)
} }
// draw position bar // draw position bar
if (rowSize.y * texts.size() > size.y) { if (rowSize.y * count > size.y) {
int height = rowSize.y * texts.size(); int height = rowSize.y * count;
wxRect rect = {size.x - 6, -offset.y * size.y / height, 4, wxRect rect = {size.x - 6, -offset.y * size.y / height, 4,
size.y * size.y / height}; size.y * size.y / height};
dc.SetPen(wxPen(border_color.defaultColor())); dc.SetPen(wxPen(border_color.defaultColor()));
@@ -271,26 +268,43 @@ void DropDown::render(wxDC &dc)
rcContent.width -= 5; rcContent.width -= 5;
if (check_bitmap.bmp().IsOk()) { if (check_bitmap.bmp().IsOk()) {
auto szBmp = check_bitmap.GetBmpSize(); auto szBmp = check_bitmap.GetBmpSize();
if (selection >= 0) { if (selected_item >= 0) {
wxPoint pt = rcContent.GetLeftTop(); wxPoint pt = rcContent.GetLeftTop();
pt.y += (rcContent.height - szBmp.y) / 2; 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) if (pt.y + szBmp.y > 0 && pt.y < size.y)
dc.DrawBitmap(check_bitmap.bmp(), pt); dc.DrawBitmap(check_bitmap.bmp(), pt);
} }
rcContent.x += szBmp.x + 5; rcContent.x += szBmp.x + 5;
rcContent.width -= szBmp.x + 5; rcContent.width -= szBmp.x + 5;
} }
std::set<wxString> groups;
// draw texts & icons // draw texts & icons
dc.SetTextForeground(text_color.colorForStates(states)); 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) { if (rcContent.GetBottom() < 0) {
rcContent.y += rowSize.y; rcContent.y += rowSize.y;
continue; continue;
} }
if (rcContent.y > size.y) break; if (rcContent.y > size.y) break;
wxPoint pt = rcContent.GetLeftTop(); wxPoint pt = rcContent.GetLeftTop();
auto & icon = icons[i]; auto & icon = item.icon;
auto size2 = GetBmpSize(icon); auto size2 = GetBmpSize(icon);
if (iconSize.x > 0) { if (iconSize.x > 0) {
if (icon.IsOk()) { if (icon.IsOk()) {
@@ -305,11 +319,13 @@ void DropDown::render(wxDC &dc)
pt.x += size2.x + 5; pt.x += size2.x + 5;
pt.y = rcContent.y; 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()) { if (!text_off && !text.IsEmpty()) {
wxSize tSize = dc.GetMultiLineTextExtent(text); wxSize tSize = dc.GetMultiLineTextExtent(text);
if (pt.x + tSize.x > rcContent.GetRight()) { if (pt.x + tSize.x > rcContent.GetRight()) {
if (i == hover_item && tips[i].IsEmpty()) if (index == hover_item && item.tip.IsEmpty())
SetToolTip(text); SetToolTip(text);
text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END,
rcContent.GetRight() - pt.x); 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<wxString> 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<wxString> 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() void DropDown::messureSize()
{ {
if (!need_sync) return; if (!need_sync) return;
textSize = wxSize(); textSize = wxSize();
iconSize = wxSize(); iconSize = wxSize();
count = 0;
wxClientDC dc(GetParent() ? GetParent() : this); wxClientDC dc(GetParent() ? GetParent() : this);
for (size_t i = 0; i < texts.size(); ++i) { std::set<wxString> groups;
wxSize size1 = text_off ? wxSize() : dc.GetMultiLineTextExtent(texts[i]); for (size_t i = 0; i < items.size(); ++i) {
if (icons[i].IsOk()) { auto &item = items[i];
wxSize size2 = GetBmpSize(icons[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) if (size2.x > iconSize.x)
iconSize = size2; iconSize = size2;
if (!align_icon) { if (!align_icon) {
@@ -350,8 +448,8 @@ void DropDown::messureSize()
if (iconSize.x > 0) szContent.x += iconSize.x + (text_off ? 0 : 5); if (iconSize.x > 0) szContent.x += iconSize.x + (text_off ? 0 : 5);
if (iconSize.y > szContent.y) szContent.y = iconSize.y; if (iconSize.y > szContent.y) szContent.y = iconSize.y;
szContent.y += 10; szContent.y += 10;
if (texts.size() > 15) szContent.x += 6; if (count > 15) szContent.x += 6;
if (GetParent()) { if (GetParent() && group.IsEmpty()) {
auto x = GetParent()->GetSize().x; auto x = GetParent()->GetSize().x;
if (!use_content_width || x > szContent.x) if (!use_content_width || x > szContent.x)
szContent.x = x; szContent.x = x;
@@ -364,38 +462,66 @@ void DropDown::messureSize()
szContent = rowSize; szContent = rowSize;
} }
} }
szContent.y *= std::min((size_t)15, texts.size()); szContent.y *= std::min((size_t) 15, count);
szContent.y += texts.size() > 15 ? rowSize.y / 2 : 0; szContent.y += items.size() > 15 ? rowSize.y / 2 : 0;
wxWindow::SetSize(szContent); wxWindow::SetSize(szContent);
#ifdef __WXGTK__ #ifdef __WXGTK__
// Gtk has a wrapper window for popup widget // Gtk has a wrapper window for popup widget
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y); gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
#endif #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; need_sync = false;
} }
void DropDown::autoPosition() void DropDown::autoPosition()
{ {
messureSize(); 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(); wxPoint old = GetPosition();
wxSize size = GetSize(); wxSize size = GetSize();
Position(pos, {0, GetParent()->GetSize().y + 12}); Position(pos, off);
if (old != GetPosition()) { if (old != GetPosition()) {
size = rowSize; size = rowSize;
size.y *= std::min((size_t)15, texts.size()); size.y *= std::min((size_t) 15, count);
size.y += texts.size() > 15 ? rowSize.y / 2 : 0; size.y += count > 15 ? rowSize.y / 2 : 0;
if (size != GetSize()) { if (size != GetSize()) {
wxWindow::SetSize(size); wxWindow::SetSize(size);
offset = wxPoint(); offset = wxPoint();
Position(pos, {0, GetParent()->GetSize().y + 12}); Position(pos, off);
} }
} }
if (GetPosition().y > pos.y) { if (GetPosition().y > pos.y) {
// may exceed // may exceed
auto drect = wxDisplay(GetParent()).GetGeometry(); auto drect = wxDisplay(GetParent()).GetGeometry();
if (GetPosition().y + size.y + 10 > drect.GetBottom()) { 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; size.y = drect.GetBottom() - GetPosition().y - 10;
wxWindow::SetSize(size); wxWindow::SetSize(size);
if (selection >= 0) { if (selection >= 0) {
@@ -443,14 +569,26 @@ void DropDown::mouseCaptureLost(wxMouseCaptureLostEvent &event)
void DropDown::mouseMove(wxMouseEvent &event) void DropDown::mouseMove(wxMouseEvent &event)
{ {
wxPoint pt = event.GetPosition(); 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) { if (pressedDown) {
wxPoint pt2 = offset + pt - dragStart; wxPoint pt2 = offset + pt - dragStart;
wxSize size = GetSize(); wxSize size = GetSize();
dragStart = pt; dragStart = pt;
if (pt2.y > 0) if (pt2.y > 0)
pt2.y = 0; pt2.y = 0;
else if (pt2.y + rowSize.y * int(texts.size()) < size.y) else if (pt2.y + rowSize.y * int(count) < size.y)
pt2.y = size.y - rowSize.y * int(texts.size()); pt2.y = size.y - rowSize.y * int(count);
if (pt2.y != offset.y) { if (pt2.y != offset.y) {
offset = pt2; offset = pt2;
hover_item = -1; // moved hover_item = -1; // moved
@@ -460,10 +598,26 @@ void DropDown::mouseMove(wxMouseEvent &event)
} }
if (!pressedDown || hover_item >= 0) { if (!pressedDown || hover_item >= 0) {
int hover = (pt.y - offset.y) / rowSize.y; 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; if (hover == hover_item) return;
hover_item = hover; 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(); paintNow();
} }
@@ -475,18 +629,18 @@ void DropDown::mouseWheelMoved(wxMouseEvent &event)
wxPoint pt2 = offset + wxPoint{0, delta}; wxPoint pt2 = offset + wxPoint{0, delta};
if (pt2.y > 0) if (pt2.y > 0)
pt2.y = 0; pt2.y = 0;
else if (pt2.y + rowSize.y * int(texts.size()) < size.y) else if (pt2.y + rowSize.y * int(count) < size.y)
pt2.y = size.y - rowSize.y * int(texts.size()); pt2.y = size.y - rowSize.y * int(count);
if (pt2.y != offset.y) { if (pt2.y != offset.y) {
offset = pt2; offset = pt2;
} else { } else {
return; return;
} }
int hover = (event.GetPosition().y - offset.y) / rowSize.y; 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) { if (hover != hover_item) {
hover_item = hover; hover_item = hover;
if (hover >= 0) SetToolTip(tips[hover]); if (hover >= 0) SetToolTip(items[hover].tip);
} }
paintNow(); paintNow();
} }
@@ -494,15 +648,38 @@ void DropDown::mouseWheelMoved(wxMouseEvent &event)
// currently unused events // currently unused events
void DropDown::sendDropDownEvent() void DropDown::sendDropDownEvent()
{ {
int index = hoverIndex();
if (index < 0)
return;
wxCommandEvent event(wxEVT_COMBOBOX, GetId()); wxCommandEvent event(wxEVT_COMBOBOX, GetId());
event.SetEventObject(this); event.SetEventObject(this);
event.SetInt(hover_item); event.SetInt(index);
event.SetString(texts[hover_item]); event.SetString(items[index].text);
GetEventHandler()->ProcessEvent(event); GetEventHandler()->ProcessEvent(event);
} }
void DropDown::Dismiss()
{
if (subDropDown && subDropDown->IsShown())
return;
PopupWindow::Dismiss();
}
void DropDown::OnDismiss() 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(); dismissTime = boost::posix_time::microsec_clock::universal_time();
hover_item = -1; hover_item = -1;
wxCommandEvent e(EVT_DISMISS); wxCommandEvent e(EVT_DISMISS);

View File

@@ -15,13 +15,28 @@ wxDECLARE_EVENT(EVT_DISMISS, wxCommandEvent);
class DropDown : public PopupWindow class DropDown : public PopupWindow
{ {
std::vector<wxString> & texts; public:
std::vector<wxString> & tips; struct Item
std::vector<wxBitmap> & icons; {
wxString text;
wxBitmap icon;
void * data{nullptr};
wxString group{};
wxString alias{};
wxString tip{};
};
private:
std::vector<Item> &items;
size_t count = 0;
wxString group;
bool need_sync = false; bool need_sync = false;
int selection = -1; int selection = -1;
int hover_item = -1; int hover_item = -1;
DropDown * subDropDown { nullptr };
DropDown * mainDropDown { nullptr };
double radius = 0; double radius = 0;
bool use_content_width = false; bool use_content_width = false;
bool limit_max_content_width = false; bool limit_max_content_width = false;
@@ -45,18 +60,11 @@ class DropDown : public PopupWindow
wxPoint dragStart; wxPoint dragStart;
public: public:
DropDown(std::vector<wxString> &texts, DropDown(std::vector<Item> &items);
std::vector<wxString> &tips,
std::vector<wxBitmap> &icons);
DropDown(wxWindow * parent, DropDown(wxWindow *parent, std::vector<Item> &items, long style = 0);
std::vector<wxString> &texts,
std::vector<wxString> &tips,
std::vector<wxBitmap> &icons,
long style = 0);
void Create(wxWindow * parent, void Create(wxWindow * parent, long style = 0);
long style = 0);
public: public:
void Invalidate(bool clear = false); void Invalidate(bool clear = false);
@@ -89,6 +97,8 @@ public:
bool HasDismissLongTime(); bool HasDismissLongTime();
protected: protected:
void Dismiss() override;
void OnDismiss() override; void OnDismiss() override;
private: private:
@@ -97,6 +107,10 @@ private:
void render(wxDC& dc); void render(wxDC& dc);
int hoverIndex();
int selectedItem();
friend class ComboBox; friend class ComboBox;
void messureSize(); void messureSize();
void autoPosition(); void autoPosition();

View File

@@ -55,7 +55,8 @@ void TextInput::Create(wxWindow * parent,
text_ctrl = nullptr; text_ctrl = nullptr;
StaticBox::Create(parent, wxID_ANY, pos, size, style); StaticBox::Create(parent, wxID_ANY, pos, size, style);
wxWindow::SetLabel(label); wxWindow::SetLabel(label);
style &= ~wxRIGHT; assert((style & wxRIGHT) == 0);
style &= ~wxALIGN_MASK;
state_handler.attach({&label_color, & text_color}); state_handler.attach({&label_color, & text_color});
state_handler.update_binds(); state_handler.update_binds();
text_ctrl = new TextCtrl(this, wxID_ANY, text, {4, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER); text_ctrl = new TextCtrl(this, wxID_ANY, text, {4, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER);
@@ -165,7 +166,7 @@ void TextInput::DoSetSize(int x, int y, int width, int height, int sizeFlags)
wxSize szIcon = this->icon.GetBmpSize(); wxSize szIcon = this->icon.GetBmpSize();
textPos.x += szIcon.x; textPos.x += szIcon.x;
} }
bool align_right = GetWindowStyle() & wxRIGHT; bool align_right = GetWindowStyle() & wxALIGN_RIGHT;
if (align_right) if (align_right)
textPos.x += labelSize.x; textPos.x += labelSize.x;
if (text_ctrl) { if (text_ctrl) {
@@ -199,21 +200,26 @@ void TextInput::render(wxDC& dc)
StaticBox::render(dc); StaticBox::render(dc);
int states = state_handler.states(); int states = state_handler.states();
wxSize size = GetSize(); wxSize size = GetSize();
bool align_right = GetWindowStyle() & wxRIGHT; bool align_center = GetWindowStyle() & wxALIGN_CENTER_HORIZONTAL;
bool align_right = GetWindowStyle() & wxALIGN_RIGHT;
// start draw // start draw
wxPoint pt = {5, 0}; wxPoint pt = {5, 0};
if (icon.bmp().IsOk()) { if (icon.bmp().IsOk()) {
wxSize szIcon = icon.GetBmpSize(); wxSize szIcon = icon.GetBmpSize();
pt.y = (size.y - szIcon.y) / 2; 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); dc.DrawBitmap(icon.bmp(), pt);
pt.x += szIcon.x + 0; pt.x += szIcon.x + 0;
} }
auto text = wxWindow::GetLabel(); auto text = wxWindow::GetLabel();
if (!text.IsEmpty()) { if (!text.IsEmpty()) {
wxSize textSize = text_ctrl->GetSize(); wxSize textSize = text_ctrl->GetSize();
if (align_right) { if (align_right || align_center) {
if (pt.x + labelSize.x > size.x) if (pt.x + labelSize.x + 5 > size.x)
text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x); text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x - 5);
pt.y = (size.y - labelSize.y) / 2; pt.y = (size.y - labelSize.y) / 2;
} else { } else {
pt.x += textSize.x; pt.x += textSize.x;