Fixed issues for new ComboBoxes:

* Navigation using Up/Down Arrows.
* Selection of first element which is started from input letter.
* OSX specific: Revert slider step to 1.
* Linux specific: Implemented possibility to use Up/Down & Left/Right arrows to navigate inside DropDown.
This commit is contained in:
YuSanka 2023-10-26 15:43:40 +02:00
parent 38b75d1ad2
commit 8bd5243b2b
3 changed files with 96 additions and 17 deletions

View File

@ -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<char>(key_code)))) {
SetSelection(int(n));
break;
}
}
}
event.Skip();
break;
}
}
}

View File

@ -66,6 +66,8 @@ public:
wxBitmap GetItemBitmap(unsigned int n);
void OnKeyDown(wxKeyEvent& event);
protected:
virtual int DoInsertItems(const wxArrayStringsAdapter &items,
unsigned int pos,

View File

@ -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<ComboBox*>(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());