From 9c3fc87265dcd0b634027bd900c2c3050c0094b9 Mon Sep 17 00:00:00 2001 From: gambas Date: Tue, 24 Nov 2020 21:37:55 +0100 Subject: [PATCH] Keyboard management fixes. [GB.GTK] * BUG: Fix management of default and cancel buttons shortcuts. * BUG: Workaround the default accelerator management of GTK, so that they are triggered only if no other widget handle the keyboard event. [GB.GTK3] * BUG: Fix management of default and cancel buttons shortcuts. * BUG: Workaround the default accelerator management of GTK, so that they are triggered only if no other widget handle the keyboard event. --- gb.gtk/src/gapplication.cpp | 4 +-- gb.gtk/src/gcombobox.cpp | 6 ++-- gb.gtk/src/gcombobox.h | 6 ++-- gb.gtk/src/gcontrol.cpp | 1 + gb.gtk/src/gcontrol.h | 5 +++- gb.gtk/src/gkey.cpp | 59 ++++++++++++++++++++++++------------- gb.gtk/src/gmainwindow.cpp | 42 ++++++++++++++++++++++++-- gb.gtk/src/gtextarea.cpp | 2 ++ gb.gtk/src/gtextbox.cpp | 4 +-- gb.gtk/src/gtextbox.h | 4 +-- 10 files changed, 96 insertions(+), 37 deletions(-) diff --git a/gb.gtk/src/gapplication.cpp b/gb.gtk/src/gapplication.cpp index e78860df3..4b73a1bff 100644 --- a/gb.gtk/src/gapplication.cpp +++ b/gb.gtk/src/gapplication.cpp @@ -823,14 +823,14 @@ __FOUND_WIDGET: if (event->key.keyval) gKey::_last_key_press = event->key.keyval; - send_to_window = control->isWindow(); + send_to_window = false; //control->isWindow(); goto __HANDLE_EVENT; case GDK_KEY_RELEASE: if (event->key.keyval) gKey::_last_key_release = event->key.keyval; - send_to_window = control->isWindow(); + send_to_window = false; //control->isWindow(); goto __HANDLE_EVENT; default: diff --git a/gb.gtk/src/gcombobox.cpp b/gb.gtk/src/gcombobox.cpp index 8527e40b3..d4e95ca36 100644 --- a/gb.gtk/src/gcombobox.cpp +++ b/gb.gtk/src/gcombobox.cpp @@ -440,7 +440,7 @@ void gComboBox::setRealForeground(gColor color) } #endif -int gComboBox::count() +int gComboBox::count() const { return tree->rowCount(); } @@ -487,7 +487,7 @@ int gComboBox::length() return gTextBox::length(); } -bool gComboBox::isSorted() +bool gComboBox::isSorted() const { return tree->isSorted(); } @@ -718,7 +718,7 @@ int gComboBox::minimumHeight() } -bool gComboBox::isReadOnly() +bool gComboBox::isReadOnly() const { return entry == NULL; } diff --git a/gb.gtk/src/gcombobox.h b/gb.gtk/src/gcombobox.h index 927c9f2d1..b81680626 100644 --- a/gb.gtk/src/gcombobox.h +++ b/gb.gtk/src/gcombobox.h @@ -33,13 +33,13 @@ public: gComboBox(gContainer *parent); ~gComboBox(); - int count(); + int count() const; int index(); char* itemText(int ind); virtual int length(); //char** list(); - virtual bool isReadOnly(); - bool isSorted(); + virtual bool isReadOnly() const; + bool isSorted() const; virtual char *text(); void setIndex(int vl); diff --git a/gb.gtk/src/gcontrol.cpp b/gb.gtk/src/gcontrol.cpp index 90c5c40d3..491b490c3 100644 --- a/gb.gtk/src/gcontrol.cpp +++ b/gb.gtk/src/gcontrol.cpp @@ -363,6 +363,7 @@ void gControl::initAll(gContainer *parent) _is_button = false; _is_drawingarea = false; _has_native_popup = false; + _eat_return_key = false; onFinish = NULL; onFocusEvent = NULL; diff --git a/gb.gtk/src/gcontrol.h b/gb.gtk/src/gcontrol.h index 44816d63b..5cf46bd19 100644 --- a/gb.gtk/src/gcontrol.h +++ b/gb.gtk/src/gcontrol.h @@ -173,6 +173,8 @@ public: bool canFocusOnClick() const; void setCanFocus(bool vl); + bool eatReturnKey() const { return _eat_return_key; } + gControl *proxy() const { return _proxy; } bool setProxy(gControl *proxy); @@ -302,7 +304,8 @@ public: unsigned _is_window : 1; // I am a window unsigned _is_button : 1; // I am a button unsigned _is_drawingarea : 1; // I am a drawing area - unsigned _has_native_popup : 1; // I have a native popup menu + unsigned _has_native_popup : 1; // I have a native popup menu + unsigned _eat_return_key : 1; // If the control eats the return key void removeParent() { pr = NULL; } void initSignals(); diff --git a/gb.gtk/src/gkey.cpp b/gb.gtk/src/gkey.cpp index 7e438fc51..321d8b53d 100644 --- a/gb.gtk/src/gkey.cpp +++ b/gb.gtk/src/gkey.cpp @@ -445,7 +445,7 @@ bool gKey::raiseEvent(int type, gControl *control, const char *text) bool cancel = false; #if DEBUG_IM - fprintf(stderr, "gKey::raiseEvent to %p %s\n", control, control->name()); + fprintf(stderr, "gKey::raiseEvent %s to %p %s\n", type == gEvent_KeyPress ? "KeyPress" : "KeyRelease", control, control->name()); #endif if (text) @@ -471,11 +471,19 @@ __KEY_TRY_PROXY: { //fprintf(stderr, "gEvent_KeyPress on %p %s\n", control, control->name()); //fprintf(stderr, "onKeyEvent: %p %d %p %s\n", event, type, control, control->name()); + #if DEBUG_IM + fprintf(stderr, "--> %s\n", control->name()); + #endif cancel = control->onKeyEvent(control, type); } if (cancel) + { + #if DEBUG_IM + fprintf(stderr, "--> cancel\n"); + #endif return true; + } if (control->_proxy_for) { @@ -498,7 +506,7 @@ gboolean gcb_key_event(GtkWidget *widget, GdkEvent *event, gControl *control) bool cancel; #if DEBUG_IM - fprintf(stderr, "gcb_key_event %s for %p %s / active = %p %s\n", event->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" : "GDK_KEY_RELEASE", control, control->name(), gApplication::activeControl(), gApplication::activeControl() ? gApplication::activeControl()->name() : "-"); + fprintf(stderr, "gcb_key_event: %s for %p %s / active = %p %s\n", event->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" : "GDK_KEY_RELEASE", control, control->name(), gApplication::activeControl(), gApplication::activeControl() ? gApplication::activeControl()->name() : "-"); #endif /*if (!control->_grab && gApplication::activeControl()) @@ -540,30 +548,39 @@ gboolean gcb_key_event(GtkWidget *widget, GdkEvent *event, gControl *control) if (cancel) return true; - win = control->window(); - - if (event->key.keyval == GDK_Escape) + if (type == gEvent_KeyPress) { - if (control->_grab) - { - gApplication::exitLoop(control); - return true; - } + win = control->window(); - if (check_button(win->_cancel)) + if (event->key.keyval == GDK_Escape) { - win->_cancel->setFocus(); - win->_cancel->animateClick(type == gEvent_KeyRelease); - return true; + if (control->_grab) + { + gApplication::exitLoop(control); + return true; + } + + if (check_button(win->_cancel)) + { + #if DEBUG_IM + fprintf(stderr, "gcb_key_event: cancel button\n"); + #endif + win->_cancel->setFocus(); + win->_cancel->animateClick(type == gEvent_KeyRelease); + return true; + } } - } - else if (event->key.keyval == GDK_Return || event->key.keyval == GDK_KP_Enter) - { - if (check_button(win->_default)) + else if (event->key.keyval == GDK_Return || event->key.keyval == GDK_KP_Enter) { - win->_default->setFocus(); - win->_default->animateClick(type == gEvent_KeyRelease); - return true; + if (!control->eatReturnKey() && check_button(win->_default)) + { + #if DEBUG_IM + fprintf(stderr, "gcb_key_event: default button\n"); + #endif + win->_default->setFocus(); + win->_default->animateClick(type == gEvent_KeyRelease); + return true; + } } } diff --git a/gb.gtk/src/gmainwindow.cpp b/gb.gtk/src/gmainwindow.cpp index 1d462ec0e..cd98e91c9 100644 --- a/gb.gtk/src/gmainwindow.cpp +++ b/gb.gtk/src/gmainwindow.cpp @@ -44,6 +44,11 @@ //#define DEBUG_RESIZE 1 +GList *gMainWindow::windows = NULL; +gMainWindow *gMainWindow::_active = NULL; +gMainWindow *gMainWindow::_current = NULL; + + #define CHECK_STATE(_var, _state) \ if (event->changed_mask & _state) \ { \ @@ -332,14 +337,45 @@ static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gMainWindow *data) } #endif +static gboolean my_key_press_event(GtkWidget *widget, GdkEventKey *event) +{ + GtkWindow *window = GTK_WINDOW(widget); + gboolean handled = FALSE; + + /* handle focus widget key events */ + if (!handled) + handled = gtk_window_propagate_key_event (window, event); + + /* Chain up, invokes binding set */ + if (!handled) + { + GtkWidgetClass *parent_klass = (GtkWidgetClass*)g_type_class_peek(g_type_parent(GTK_TYPE_WINDOW)); + handled = parent_klass->key_press_event(widget, event); + } + + /* handle mnemonics and accelerators */ + if (!handled) + handled = gtk_window_activate_key(window, event); + + return handled; +} -GList *gMainWindow::windows = NULL; -gMainWindow *gMainWindow::_active = NULL; -gMainWindow *gMainWindow::_current = NULL; +//------------------------------------------------------------------------- void gMainWindow::initialize() { + // workaround GTK+ accelerator management + + static bool _init = FALSE; + if (!_init) + { + GtkWidgetClass *klass = (GtkWidgetClass*)g_type_class_peek(GTK_TYPE_WINDOW); + klass->key_press_event = my_key_press_event; + _init = TRUE; + } + + //fprintf(stderr, "new window: %p in %p\n", this, parent()); stack = 0; diff --git a/gb.gtk/src/gtextarea.cpp b/gb.gtk/src/gtextarea.cpp index a220ffc74..55ab04717 100644 --- a/gb.gtk/src/gtextarea.cpp +++ b/gb.gtk/src/gtextarea.cpp @@ -480,6 +480,7 @@ gTextArea::gTextArea(gContainer *parent) : gControl(parent) _use_wheel = true; _fix_spacing_tag = NULL; _has_native_popup = true; + _eat_return_key = true; onChange = 0; onCursor = 0; @@ -572,6 +573,7 @@ void gTextArea::setReadOnly(bool vl) { gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), !vl); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview), !vl); + _eat_return_key = !vl; } GtkTextIter *gTextArea::getIterAt(int pos) diff --git a/gb.gtk/src/gtextbox.cpp b/gb.gtk/src/gtextbox.cpp index 48d9c649c..947dc61ef 100644 --- a/gb.gtk/src/gtextbox.cpp +++ b/gb.gtk/src/gtextbox.cpp @@ -160,7 +160,7 @@ void gTextBox::initEntry() //g_signal_connect(getInputMethod(), "commit", G_CALLBACK(cb_im_commit), (gpointer)this); } -char* gTextBox::text() const +char* gTextBox::text() { return (char*)gtk_entry_get_text(GTK_ENTRY(entry)); } @@ -288,7 +288,7 @@ void gTextBox::insert(char *txt, int len) gtk_editable_insert_text(GTK_EDITABLE(entry), txt, len, &pos); } -int gTextBox::length() const +int gTextBox::length() { const gchar *buf; diff --git a/gb.gtk/src/gtextbox.h b/gb.gtk/src/gtextbox.h index 60798fa21..136d37b39 100644 --- a/gb.gtk/src/gtextbox.h +++ b/gb.gtk/src/gtextbox.h @@ -33,11 +33,11 @@ public: //"Properties" int alignment() const; bool hasBorder() const { return _has_border; } - virtual int length() const; + virtual int length(); int maxLength() const; bool password() const; int position() const; - virtual char *text() const; + virtual char *text(); virtual char *placeholder() const; virtual bool isReadOnly() const; int selLength() const;