diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index 5320beefcc..bca90cdbfc 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -61,7 +61,10 @@ ComboBox::ComboBox(wxWindow * parent, for (int i = 0; i < n; ++i) Append(choices[i]); } -int ComboBox::GetSelection() const { return drop.GetSelection(); } +int ComboBox::GetSelection() const +{ + return drop.GetSelection(); +} void ComboBox::SetSelection(int n) { @@ -233,6 +236,11 @@ wxBitmap ComboBox::GetItemBitmap(unsigned int n) return icons[n].GetBitmapFor(m_parent); } +void ComboBox::OnKeyDown(wxKeyEvent &event) +{ + keyDown(event); +} + int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items, unsigned int pos, void ** clientData, @@ -290,10 +298,15 @@ void ComboBox::keyDown(wxKeyEvent& event) { int key_code = event.GetKeyCode(); switch (key_code) { -#ifndef __WXOSX__ case WXK_RETURN: if (drop_down) { drop.DismissAndNotify(); + + wxCommandEvent e(wxEVT_COMBOBOX); + e.SetEventObject(this); + e.SetId(GetId()); + e.SetInt(GetSelection()); + GetEventHandler()->ProcessEvent(e); } else if (drop.HasDismissLongTime()) { drop.autoPosition(); drop_down = true; @@ -302,7 +315,6 @@ void ComboBox::keyDown(wxKeyEvent& event) GetEventHandler()->ProcessEvent(e); } break; -#endif case WXK_UP: case WXK_DOWN: case WXK_LEFT: @@ -314,20 +326,22 @@ void ComboBox::keyDown(wxKeyEvent& event) } else { break; } - { - wxCommandEvent e(wxEVT_COMBOBOX); - e.SetEventObject(this); - e.SetId(GetId()); - e.SetInt(GetSelection()); - GetEventHandler()->ProcessEvent(e); - } break; case WXK_TAB: HandleAsNavigationKey(event); break; - default: + default: { + if (drop.IsShown() && HasFlag(wxCB_READONLY)) { + for (size_t n = 0; n < texts.size(); n++) { + if (texts[n].StartsWith(wxString(static_cast(key_code)))) { + SetSelection(int(n)); + break; + } + } + } event.Skip(); break; + } } } diff --git a/src/slic3r/GUI/Widgets/ComboBox.hpp b/src/slic3r/GUI/Widgets/ComboBox.hpp index 1268acec04..b94acf9312 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.hpp +++ b/src/slic3r/GUI/Widgets/ComboBox.hpp @@ -66,6 +66,8 @@ public: wxBitmap GetItemBitmap(unsigned int n); + void OnKeyDown(wxKeyEvent& event); + protected: virtual int DoInsertItems(const wxArrayStringsAdapter &items, unsigned int pos, diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index 4aac437458..6eccc07e35 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -1,4 +1,5 @@ #include "DropDown.hpp" +#include "ComboBox.hpp" #include "../GUI_App.hpp" #include "../OptionsGroup.hpp" @@ -60,10 +61,60 @@ DropDown::DropDown(wxWindow * parent, Create(parent, style); } +#ifdef __WXGTK__ +static gint gtk_popup_key_press (GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win ) +{ + // Ignore events sent out before we connected to the signal + if (win->m_time >= ((GdkEventKey*)gdk_event)->time) + return FALSE; + + GtkWidget *child = gtk_get_event_widget (gdk_event); + + /* We don't ask for button press events on the grab widget, so + * if an event is reported directly to the grab widget, it must + * be on a window outside the application (and thus we remove + * the popup window). Otherwise, we check if the widget is a child + * of the grab widget, and only remove the popup window if it + * is not. */ + if (child != widget) { + while (child) { + if (child == widget) + return FALSE; + child = gtk_widget_get_parent(child); + } + } + + gchar* keyval = gdk_keyval_name(((GdkEventKey*)gdk_event)->keyval); + const long keyCode = strcmp(keyval, "Up") == 0 ? WXK_UP : + strcmp(keyval, "Down") == 0 ? WXK_DOWN : + strcmp(keyval, "Left") == 0 ? WXK_LEFT : + strcmp(keyval, "Right") == 0 ? WXK_RIGHT : + strcmp(keyval, "Return") == 0 ? WXK_RETURN : WXK_NONE; + + if (keyCode != WXK_NONE) { + wxKeyEvent event( wxEVT_KEY_DOWN, win->GetId()); + event.m_keyCode = keyCode; + event.SetEventObject( win ); + (void)win->HandleWindowEvent( event ); + } + + return TRUE; +} +#endif + void DropDown::Create(wxWindow * parent, long style) { wxPopupTransientWindow::Create(parent); +#ifdef __WXGTK__ + g_signal_connect (m_widget, "key_press_event", G_CALLBACK (gtk_popup_key_press), this); + + Bind(wxEVT_KEY_DOWN, [parent](wxKeyEvent &e) { + if (ComboBox* cb = dynamic_cast(parent)) + cb->OnKeyDown(e); + }); +#endif + if (!wxOSX) SetBackgroundStyle(wxBG_STYLE_PAINT); state_handler.attach({&border_color, &text_color, &selector_border_color, &selector_background_color}); state_handler.update_binds(); @@ -95,6 +146,8 @@ void DropDown::SetSelection(int n) n = -1; if (selection == n) return; selection = n; + if (IsShown()) + autoPosition(); paintNow(); } @@ -212,7 +265,11 @@ void DropDown::SetTransparentBG(wxDC& dc, wxWindow* win) } constexpr int slider_width = 12; +#ifdef __WXOSX__ +constexpr int slider_step = 1; +#else constexpr int slider_step = 5; +#endif constexpr int items_padding = 2; /* @@ -423,14 +480,14 @@ void DropDown::autoPosition() if (use_content_width && texts.size() <= 15) size.x += 6; size.y = drect.GetBottom() - GetPosition().y - 10; wxWindow::SetSize(size); - if (selection >= 0) { - if (offset.y + rowSize.y * (selection + 1) > size.y) - offset.y = size.y - rowSize.y * (selection + 1); - else if (offset.y + rowSize.y * selection < 0) - offset.y = -rowSize.y * selection; - } } } + if (selection >= 0) { + if (offset.y + rowSize.y * (selection + 1) > size.y) + offset.y = size.y - rowSize.y * (selection + 3); + else if (offset.y + rowSize.y * selection < 0) + offset.y = -rowSize.y * selection; + } } void DropDown::mouseDown(wxMouseEvent& event) @@ -461,6 +518,10 @@ void DropDown::mouseReleased(wxMouseEvent& event) if (HasCapture()) ReleaseMouse(); if (hover_item >= 0) { // not moved +#ifdef __WXOSX__ + // To avoid cases, when some dialog appears after item selection, but DropDown is still shown + Hide(); +#endif sendDropDownEvent(); DismissAndNotify(); } @@ -507,6 +568,8 @@ void DropDown::mouseMove(wxMouseEvent &event) void DropDown::mouseWheelMoved(wxMouseEvent &event) { + if (event.GetWheelRotation() == 0) + return; auto delta = event.GetWheelRotation() > 0 ? rowSize.y : -rowSize.y; wxPoint pt2 = offset + wxPoint{0, slider_step * delta}; int text_size = int(texts.size());