diff --git a/include/wx/gtk/private/event.h b/include/wx/gtk/private/event.h index 9811422df3..ac3a3eaf8a 100644 --- a/include/wx/gtk/private/event.h +++ b/include/wx/gtk/private/event.h @@ -88,6 +88,23 @@ template void InitMouseEvent(wxWindowGTK *win, event.SetTimestamp( gdk_event->time ); } +// Update the window currently known to be under the mouse pointer. +// +// Returns true if it was updated, false if this window was already known to +// contain the mouse pointer. +bool SetWindowUnderMouse(wxWindowGTK* win); + +// Implementation of enter/leave window callbacks. +gboolean +WindowEnterCallback(GtkWidget* widget, + GdkEventCrossing* event, + wxWindowGTK* win); + +gboolean +WindowLeaveCallback(GtkWidget* widget, + GdkEventCrossing* event, + wxWindowGTK* win); + } // namespace wxGTKImpl #endif // _GTK_PRIVATE_EVENT_H_ diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index 0b26076a09..cbef80ab3d 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -171,14 +171,9 @@ public: virtual GtkWidget* GetConnectWidget(); void ConnectWidget( GtkWidget *widget ); - // Called from several event handlers, if it returns true or false, the - // same value should be immediately returned by the handler without doing - // anything else. If it returns -1, the handler should continue as usual - int GTKCallbackCommonPrologue(struct _GdkEventAny *event) const; - // Simplified form of GTKCallbackCommonPrologue() which can be used from - // GTK callbacks without return value to check if the event should be - // ignored: if this returns true, the event shouldn't be handled + // Returns true if GTK callbacks are blocked due to a drag event being in + // progress. bool GTKShouldIgnoreEvent() const; diff --git a/samples/widgets/activityindicator.cpp b/samples/widgets/activityindicator.cpp index ce5eb84a32..18b7f33748 100644 --- a/samples/widgets/activityindicator.cpp +++ b/samples/widgets/activityindicator.cpp @@ -145,6 +145,8 @@ void ActivityIndicatorWidgetsPage::RecreateWidget() wxDefaultPosition, wxDefaultSize, GetAttrs().m_defaultFlags); + NotifyWidgetRecreation(m_indicator); + m_sizerIndicator->AddStretchSpacer(); m_sizerIndicator->Add(m_indicator, wxSizerFlags().Centre()); m_sizerIndicator->AddStretchSpacer(); diff --git a/samples/widgets/bmpcombobox.cpp b/samples/widgets/bmpcombobox.cpp index 1e6cac8cc4..120dce6bca 100644 --- a/samples/widgets/bmpcombobox.cpp +++ b/samples/widgets/bmpcombobox.cpp @@ -478,6 +478,8 @@ void BitmapComboBoxWidgetsPage::CreateCombo() m_combobox->SetPopupMaxHeight(600); #endif + NotifyWidgetRecreation(m_combobox); + unsigned int count = items.GetCount(); for ( unsigned int n = 0; n < count; n++ ) { diff --git a/samples/widgets/button.cpp b/samples/widgets/button.cpp index da02a26668..68e7351238 100644 --- a/samples/widgets/button.cpp +++ b/samples/widgets/button.cpp @@ -562,6 +562,8 @@ void ButtonWidgetsPage::CreateButton() m_sizerNote->Show(m_chkCommandLink->GetValue()); #endif + NotifyWidgetRecreation(m_button); + if ( !showsBitmap && m_chkTextAndBitmap->GetValue() ) { showsBitmap = true; diff --git a/samples/widgets/checkbox.cpp b/samples/widgets/checkbox.cpp index e3e0c1f623..ae500b1fc7 100644 --- a/samples/widgets/checkbox.cpp +++ b/samples/widgets/checkbox.cpp @@ -276,6 +276,8 @@ void CheckBoxWidgetsPage::CreateCheckbox() wxDefaultPosition, wxDefaultSize, flags); + NotifyWidgetRecreation(m_checkbox); + m_sizerCheckbox->Add(0, 0, 1, wxCENTRE); m_sizerCheckbox->Add(m_checkbox, 1, wxCENTRE); m_sizerCheckbox->Add(0, 0, 1, wxCENTRE); diff --git a/samples/widgets/choice.cpp b/samples/widgets/choice.cpp index bf9f76f3bf..a13f8032b1 100644 --- a/samples/widgets/choice.cpp +++ b/samples/widgets/choice.cpp @@ -303,6 +303,8 @@ void ChoiceWidgetsPage::CreateChoice() 0, nullptr, flags); + NotifyWidgetRecreation(m_choice); + m_choice->Set(items); m_sizerChoice->Add(m_choice, 0, wxGROW | wxALL, 5); m_sizerChoice->Layout(); diff --git a/samples/widgets/clrpicker.cpp b/samples/widgets/clrpicker.cpp index 011ca7d14e..21d88caa62 100644 --- a/samples/widgets/clrpicker.cpp +++ b/samples/widgets/clrpicker.cpp @@ -191,6 +191,8 @@ void ColourPickerWidgetsPage::CreatePicker() m_clrPicker = new wxColourPickerCtrl(this, PickerPage_Colour, *wxRED, wxDefaultPosition, wxDefaultSize, style); + + NotifyWidgetRecreation(m_clrPicker); } void ColourPickerWidgetsPage::RecreatePicker() diff --git a/samples/widgets/combobox.cpp b/samples/widgets/combobox.cpp index af2d78050f..2ba1453e3a 100644 --- a/samples/widgets/combobox.cpp +++ b/samples/widgets/combobox.cpp @@ -476,6 +476,8 @@ void ComboboxWidgetsPage::CreateCombo() delete m_combobox; m_combobox = newCb; m_combobox->SetId(ComboPage_Combo); + + NotifyWidgetRecreation(m_combobox); } // ---------------------------------------------------------------------------- diff --git a/samples/widgets/datepick.cpp b/samples/widgets/datepick.cpp index 28e3a9cda5..5fc40af483 100644 --- a/samples/widgets/datepick.cpp +++ b/samples/widgets/datepick.cpp @@ -285,6 +285,8 @@ void DatePickerWidgetsPage::CreateDatePicker() wxDefaultPosition, wxDefaultSize, style); + NotifyWidgetRecreation(m_datePicker); + m_sizerDatePicker->Add(0, 0, 1, wxCENTRE); m_sizerDatePicker->Add(m_datePicker, 1, wxCENTRE); m_sizerDatePicker->Add(0, 0, 1, wxCENTRE); diff --git a/samples/widgets/dirctrl.cpp b/samples/widgets/dirctrl.cpp index e0ec7b79d9..3bb23254d0 100644 --- a/samples/widgets/dirctrl.cpp +++ b/samples/widgets/dirctrl.cpp @@ -302,6 +302,8 @@ void DirCtrlWidgetsPage::CreateDirCtrl(bool defaultPath) delete m_dirCtrl; m_dirCtrl = dirCtrl; + NotifyWidgetRecreation(m_dirCtrl); + // relayout the sizer GetSizer()->Layout(); } diff --git a/samples/widgets/dirpicker.cpp b/samples/widgets/dirpicker.cpp index 02a70c4172..60dd09dd0b 100644 --- a/samples/widgets/dirpicker.cpp +++ b/samples/widgets/dirpicker.cpp @@ -209,6 +209,8 @@ void DirPickerWidgetsPage::CreatePicker() wxGetHomeDir(), "Hello!", wxDefaultPosition, wxDefaultSize, style); + + NotifyWidgetRecreation(m_dirPicker); } void DirPickerWidgetsPage::RecreatePicker() diff --git a/samples/widgets/editlbox.cpp b/samples/widgets/editlbox.cpp index 1eea201d3d..97f469bd0e 100644 --- a/samples/widgets/editlbox.cpp +++ b/samples/widgets/editlbox.cpp @@ -198,6 +198,8 @@ void EditableListboxWidgetsPage::CreateLbox() wxDefaultPosition, wxDefaultSize, flags); + NotifyWidgetRecreation(m_lbox); + m_lbox->SetStrings(items); m_sizerLbox->Add(m_lbox, 1, wxGROW | wxALL, 5); m_sizerLbox->Layout(); diff --git a/samples/widgets/filectrl.cpp b/samples/widgets/filectrl.cpp index f7c0a051d4..7c213a711e 100644 --- a/samples/widgets/filectrl.cpp +++ b/samples/widgets/filectrl.cpp @@ -275,6 +275,8 @@ void FileCtrlWidgetsPage::CreateFileCtrl() delete m_fileCtrl; m_fileCtrl = fileCtrl; + NotifyWidgetRecreation(m_fileCtrl); + // relayout the sizer GetSizer()->Layout(); } diff --git a/samples/widgets/filepicker.cpp b/samples/widgets/filepicker.cpp index 48da5ebfa1..adf87281e3 100644 --- a/samples/widgets/filepicker.cpp +++ b/samples/widgets/filepicker.cpp @@ -248,6 +248,8 @@ void FilePickerWidgetsPage::CreatePicker() "Hello!", "*", wxDefaultPosition, wxDefaultSize, style); + + NotifyWidgetRecreation(m_filePicker); } void FilePickerWidgetsPage::RecreatePicker() diff --git a/samples/widgets/fontpicker.cpp b/samples/widgets/fontpicker.cpp index 052a74c564..498a902ff4 100644 --- a/samples/widgets/fontpicker.cpp +++ b/samples/widgets/fontpicker.cpp @@ -186,6 +186,8 @@ void FontPickerWidgetsPage::CreatePicker() *wxSWISS_FONT, wxDefaultPosition, wxDefaultSize, style); + + NotifyWidgetRecreation(m_fontPicker); } void FontPickerWidgetsPage::RecreatePicker() diff --git a/samples/widgets/gauge.cpp b/samples/widgets/gauge.cpp index d8a247784d..84800d0f54 100644 --- a/samples/widgets/gauge.cpp +++ b/samples/widgets/gauge.cpp @@ -307,6 +307,9 @@ void GaugeWidgetsPage::CreateGauge() m_gauge = new wxGauge(this, GaugePage_Gauge, m_range, wxDefaultPosition, wxDefaultSize, flags); + + NotifyWidgetRecreation(m_gauge); + m_gauge->SetValue(val); if ( flags & wxGA_VERTICAL ) diff --git a/samples/widgets/headerctrl.cpp b/samples/widgets/headerctrl.cpp index 99682204c3..8b449c23f2 100644 --- a/samples/widgets/headerctrl.cpp +++ b/samples/widgets/headerctrl.cpp @@ -200,6 +200,8 @@ void HeaderCtrlWidgetsPage::RecreateWidget() wxDefaultPosition, wxDefaultSize, flags); + NotifyWidgetRecreation(m_header); + m_header->Bind(wxEVT_HEADER_RESIZING, &HeaderCtrlWidgetsPage::OnResizing, this); m_header->Bind(wxEVT_HEADER_BEGIN_RESIZE, &HeaderCtrlWidgetsPage::OnBeginResize, this); m_header->Bind(wxEVT_HEADER_END_RESIZE, &HeaderCtrlWidgetsPage::OnEndResize, this); diff --git a/samples/widgets/hyperlnk.cpp b/samples/widgets/hyperlnk.cpp index 4f10b56904..d9dd511017 100644 --- a/samples/widgets/hyperlnk.cpp +++ b/samples/widgets/hyperlnk.cpp @@ -288,6 +288,8 @@ void HyperlinkWidgetsPage::CreateHyperlink() delete m_hyperlink; m_hyperlink = hyp; + NotifyWidgetRecreation(m_hyperlink); + // relayout the sizer GetSizer()->Layout(); } diff --git a/samples/widgets/listbox.cpp b/samples/widgets/listbox.cpp index ba12939b08..4fc7395e38 100644 --- a/samples/widgets/listbox.cpp +++ b/samples/widgets/listbox.cpp @@ -506,6 +506,8 @@ void ListboxWidgetsPage::CreateLbox() flags); } + NotifyWidgetRecreation(m_lbox); + m_sizerLbox->Add(m_lbox, 1, wxGROW | wxALL, 5); m_sizerLbox->Layout(); } diff --git a/samples/widgets/native.cpp b/samples/widgets/native.cpp index b27da69ed0..99902d32bf 100644 --- a/samples/widgets/native.cpp +++ b/samples/widgets/native.cpp @@ -308,6 +308,8 @@ void NativeWidgetsPage::RecreateWidget() delete m_nativeWindow; m_nativeWindow = new NativeWindow(this); + NotifyWidgetRecreation(m_nativeWindow); + m_sizerCtrl->Clear(); if ( m_chkExpand->IsChecked() ) { diff --git a/samples/widgets/notebook.cpp b/samples/widgets/notebook.cpp index e01dcdd094..e656fabada 100644 --- a/samples/widgets/notebook.cpp +++ b/samples/widgets/notebook.cpp @@ -365,6 +365,8 @@ void BookWidgetsPage::RecreateBook() m_book = CreateBook(flags); + NotifyWidgetRecreation(m_book); + CreateImageList(); if ( oldBook ) diff --git a/samples/widgets/odcombobox.cpp b/samples/widgets/odcombobox.cpp index b451073f31..0d97e957d4 100644 --- a/samples/widgets/odcombobox.cpp +++ b/samples/widgets/odcombobox.cpp @@ -527,6 +527,8 @@ void ODComboboxWidgetsPage::CreateCombo() 0, nullptr, flags); + NotifyWidgetRecreation(m_combobox); + unsigned int count = items.GetCount(); for ( unsigned int n = 0; n < count; n++ ) { diff --git a/samples/widgets/radiobox.cpp b/samples/widgets/radiobox.cpp index 40fdb0aec0..b7ff0555ff 100644 --- a/samples/widgets/radiobox.cpp +++ b/samples/widgets/radiobox.cpp @@ -358,6 +358,8 @@ void RadioWidgetsPage::CreateRadio() majorDim, flags); + NotifyWidgetRecreation(m_radio); + if ( sel >= 0 && (size_t)sel < count ) { m_radio->SetSelection(sel); diff --git a/samples/widgets/searchctrl.cpp b/samples/widgets/searchctrl.cpp index 38c98211d5..c9dbdb8411 100644 --- a/samples/widgets/searchctrl.cpp +++ b/samples/widgets/searchctrl.cpp @@ -178,6 +178,8 @@ void SearchCtrlWidgetsPage::CreateControl() m_srchCtrl = new wxSearchCtrl(this, -1, wxEmptyString, wxDefaultPosition, FromDIP(wxSize(150, -1)), style); + + NotifyWidgetRecreation(m_srchCtrl); } void SearchCtrlWidgetsPage::RecreateWidget() diff --git a/samples/widgets/slider.cpp b/samples/widgets/slider.cpp index 7446f3236b..429c933ec9 100644 --- a/samples/widgets/slider.cpp +++ b/samples/widgets/slider.cpp @@ -517,6 +517,8 @@ void SliderWidgetsPage::CreateSlider() wxDefaultPosition, wxDefaultSize, flags); + NotifyWidgetRecreation(m_slider); + if ( m_slider->HasFlag(wxSL_VERTICAL) ) { m_sizerSlider->AddStretchSpacer(1); diff --git a/samples/widgets/spinbtn.cpp b/samples/widgets/spinbtn.cpp index 5a618b609b..57f41ce72a 100644 --- a/samples/widgets/spinbtn.cpp +++ b/samples/widgets/spinbtn.cpp @@ -416,6 +416,8 @@ void SpinBtnWidgetsPage::CreateSpin() wxDefaultPosition, wxDefaultSize, flags); + NotifyWidgetRecreation(m_spinbtn); + m_spinbtn->SetValue(val); m_spinbtn->SetRange(m_min, m_max); @@ -425,12 +427,16 @@ void SpinBtnWidgetsPage::CreateSpin() flags | textFlags, m_min, m_max, val); + NotifyWidgetRecreation(m_spinctrl); + m_spinctrldbl = new wxSpinCtrlDouble(this, SpinBtnPage_SpinCtrlDouble, wxString::Format("%d", val), wxDefaultPosition, wxDefaultSize, flags | textFlags, m_min, m_max, val, 0.1); + NotifyWidgetRecreation(m_spinctrldbl); + // Add spacers, labels and spin controls to the sizer. m_sizerSpin->Add(0, 0, 1); m_sizerSpin->Add(new wxStaticText(this, wxID_ANY, "wxSpinButton"), diff --git a/samples/widgets/statbmp.cpp b/samples/widgets/statbmp.cpp index 90054d7c10..a26279385a 100644 --- a/samples/widgets/statbmp.cpp +++ b/samples/widgets/statbmp.cpp @@ -161,6 +161,8 @@ void StatBmpWidgetsPage::RecreateWidget() style); } + NotifyWidgetRecreation(m_statbmp); + wxStaticBitmapBase::ScaleMode scaleMode = (wxStaticBitmapBase::ScaleMode) m_scaleRadio->GetSelection(); m_statbmp->SetScaleMode(scaleMode); if ( m_statbmp->GetScaleMode() != scaleMode ) diff --git a/samples/widgets/static.cpp b/samples/widgets/static.cpp index dcb5db2477..abb59ca2c8 100644 --- a/samples/widgets/static.cpp +++ b/samples/widgets/static.cpp @@ -525,9 +525,13 @@ void StaticWidgetsPage::CreateStatic() #endif // wxUSE_MARKUP } + NotifyWidgetRecreation(m_statText); + m_statText->SetToolTip("Tooltip for a label inside the box"); #if wxUSE_MARKUP + NotifyWidgetRecreation(m_statMarkup); + m_statMarkup->SetLabelMarkup(m_textLabelWithMarkup->GetValue()); if ( m_chkGreen->GetValue() ) @@ -538,6 +542,8 @@ void StaticWidgetsPage::CreateStatic() m_statLine = new wxStaticLine(staticBox, wxID_ANY, wxDefaultPosition, wxDefaultSize, isVert ? wxLI_VERTICAL : wxLI_HORIZONTAL); + + NotifyWidgetRecreation(m_statLine); #endif // wxUSE_STATLINE m_sizerStatBox->Add(m_statText, 0, wxGROW); diff --git a/samples/widgets/textctrl.cpp b/samples/widgets/textctrl.cpp index 606cf7fc82..588a062b6f 100644 --- a/samples/widgets/textctrl.cpp +++ b/samples/widgets/textctrl.cpp @@ -787,6 +787,8 @@ void TextWidgetsPage::CreateText() m_text = new WidgetsTextCtrl(m_sizerText->GetStaticBox(), TextPage_Textctrl, valueOld, flags); + NotifyWidgetRecreation(m_text); + #if 0 if ( m_chkFilename->GetValue() ) ; diff --git a/samples/widgets/timepick.cpp b/samples/widgets/timepick.cpp index 4cd937f704..b4e68b08d0 100644 --- a/samples/widgets/timepick.cpp +++ b/samples/widgets/timepick.cpp @@ -198,6 +198,8 @@ void TimePickerWidgetsPage::CreateTimePicker() wxDefaultPosition, wxDefaultSize, style); + NotifyWidgetRecreation(m_timePicker); + m_sizerTimePicker->Add(0, 0, 1, wxCENTRE); m_sizerTimePicker->Add(m_timePicker, 1, wxCENTRE); m_sizerTimePicker->Add(0, 0, 1, wxCENTRE); diff --git a/samples/widgets/toggle.cpp b/samples/widgets/toggle.cpp index ae443c22da..54eaa282c1 100644 --- a/samples/widgets/toggle.cpp +++ b/samples/widgets/toggle.cpp @@ -461,6 +461,9 @@ void ToggleWidgetsPage::CreateToggle() wxDefaultPosition, wxDefaultSize, flags); } + + NotifyWidgetRecreation(m_toggle); + m_toggle->SetValue(value); #ifdef wxHAS_BITMAPTOGGLEBUTTON diff --git a/samples/widgets/widgets.cpp b/samples/widgets/widgets.cpp index 9add0d857d..cf3ad97986 100644 --- a/samples/widgets/widgets.cpp +++ b/samples/widgets/widgets.cpp @@ -183,6 +183,10 @@ public: // real implementation of WidgetsPage method with the same name bool IsUsingLogWindow() const; + // connects handlers showing some interesting widget events to the given + // widget + void ConnectToWidgetEvents(wxWindow* w); + private: #if USE_LOG wxLog* m_logTarget; @@ -253,11 +257,6 @@ protected: WidgetsPage *CurrentPage(); private: - void OnWidgetFocus(wxFocusEvent& event); - void OnWidgetContextMenu(wxContextMenuEvent& event); - - void ConnectToWidgetEvents(); - // the panel containing everything wxPanel *m_panel; @@ -430,6 +429,51 @@ bool WidgetsApp::IsUsingLogWindow() const #endif // USE_LOG } +namespace +{ + +void OnFocus(wxFocusEvent& event) +{ + // Don't show annoying message boxes when starting or closing the sample, + // only log these events in our own logger. + if ( wxGetApp().IsUsingLogWindow() ) + { + wxWindow* win = (wxWindow*)event.GetEventObject(); + wxLogMessage("Widget '%s' %s focus", win->GetClassInfo()->GetClassName(), + event.GetEventType() == wxEVT_SET_FOCUS ? "got" : "lost"); + } + + event.Skip(); +} + +} // anonymous namespace + +void WidgetsApp::ConnectToWidgetEvents(wxWindow* w) +{ + w->Bind(wxEVT_SET_FOCUS, OnFocus); + w->Bind(wxEVT_KILL_FOCUS, OnFocus); + + w->Bind(wxEVT_ENTER_WINDOW, [w](wxMouseEvent& event) + { + wxLogMessage("Mouse entered into '%s'", w->GetClassInfo()->GetClassName()); + event.Skip(); + }); + w->Bind(wxEVT_LEAVE_WINDOW, [w](wxMouseEvent& event) + { + wxLogMessage("Mouse left '%s'", w->GetClassInfo()->GetClassName()); + event.Skip(); + }); + + w->Bind(wxEVT_CONTEXT_MENU, [w](wxContextMenuEvent& event) + { + wxLogMessage("Context menu event for '%s' at %dx%d", + w->GetClassInfo()->GetClassName(), + event.GetPosition().x, + event.GetPosition().y); + event.Skip(); + }); +} + // ---------------------------------------------------------------------------- // WidgetsFrame construction // ---------------------------------------------------------------------------- @@ -727,24 +771,6 @@ WidgetsPage *WidgetsFrame::CurrentPage() return wxStaticCast(page, WidgetsPage); } -void WidgetsFrame::ConnectToWidgetEvents() -{ - const Widgets& widgets = CurrentPage()->GetWidgets(); - - for ( Widgets::const_iterator it = widgets.begin(); - it != widgets.end(); - ++it ) - { - wxWindow* const w = *it; - wxCHECK_RET(w, "null widget"); - - w->Bind(wxEVT_SET_FOCUS, &WidgetsFrame::OnWidgetFocus, this); - w->Bind(wxEVT_KILL_FOCUS, &WidgetsFrame::OnWidgetFocus, this); - - w->Bind(wxEVT_CONTEXT_MENU, &WidgetsFrame::OnWidgetContextMenu, this); - } -} - WidgetsFrame::~WidgetsFrame() { #if USE_LOG @@ -802,7 +828,18 @@ void WidgetsFrame::OnPageChanged(WidgetsBookCtrlEvent& event) curPage->SetScrollRate(10, 10); curPage->FitInside(); - ConnectToWidgetEvents(); + auto& app = wxGetApp(); + for ( const auto w : CurrentPage()->GetWidgets() ) + { + app.ConnectToWidgetEvents(w); + } + + // From now on, we're interested in these notifications as we'll need + // to reconnect to the widget events if it's recreated (unfortunately + // we can't rely getting them on creation as some page don't generate + // them -- but neither can we rely on not getting them as some pages do + // generate them, hence the use of m_notifyRecreate flag). + curPage->EnableRecreationNotifications(); } // re-apply the attributes to the widget(s) @@ -971,11 +1008,6 @@ void WidgetsFrame::OnSetBorder(wxCommandEvent& event) WidgetsPage *page = CurrentPage(); page->RecreateWidget(); - - ConnectToWidgetEvents(); - - // re-apply the attributes to the widget(s) - page->SetUpWidget(); } void WidgetsFrame::OnSetVariant(wxCommandEvent& event) @@ -1259,31 +1291,6 @@ void WidgetsFrame::OnSetHint(wxCommandEvent& WXUNUSED(event)) #endif // wxUSE_MENUS -void WidgetsFrame::OnWidgetFocus(wxFocusEvent& event) -{ - // Don't show annoying message boxes when starting or closing the sample, - // only log these events in our own logger. - if ( wxGetApp().IsUsingLogWindow() ) - { - wxWindow* win = (wxWindow*)event.GetEventObject(); - wxLogMessage("Widget '%s' %s focus", win->GetClassInfo()->GetClassName(), - event.GetEventType() == wxEVT_SET_FOCUS ? "got" : "lost"); - } - - event.Skip(); -} - -void WidgetsFrame::OnWidgetContextMenu(wxContextMenuEvent& event) -{ - wxWindow* win = (wxWindow*)event.GetEventObject(); - wxLogMessage("Context menu event for %s at %dx%d", - win->GetClassInfo()->GetClassName(), - event.GetPosition().x, - event.GetPosition().y); - - event.Skip(); -} - // ---------------------------------------------------------------------------- // WidgetsPageInfo // ---------------------------------------------------------------------------- @@ -1462,6 +1469,19 @@ wxCheckBox *WidgetsPage::CreateCheckBoxAndAddToSizer(wxSizer *sizer, return checkbox; } +void WidgetsPage::NotifyWidgetRecreation(wxWindow* widget) +{ + if ( !m_notifyRecreate ) + { + // We're in the process of initialization, don't notify yet. + return; + } + + SetUpWidget(); + + wxGetApp().ConnectToWidgetEvents(widget); +} + /* static */ bool WidgetsPage::IsUsingLogWindow() { diff --git a/samples/widgets/widgets.h b/samples/widgets/widgets.h index c89acaec5c..6378568486 100644 --- a/samples/widgets/widgets.h +++ b/samples/widgets/widgets.h @@ -149,6 +149,13 @@ public: // this is currently used only to take into account the border flags virtual void RecreateWidget() = 0; + // notify the main window about the widget recreation if it didn't happen + // due to a call to RecreateWidget() + void NotifyWidgetRecreation(wxWindow* widget); + + // enable notifications about the widget recreation disabled initially + void EnableRecreationNotifications() { m_notifyRecreate = true; } + // apply current attributes to the widget(s) void SetUpWidget(); @@ -191,6 +198,9 @@ protected: public: // the head of the linked list containinginfo about all pages static WidgetsPageInfo *ms_widgetPages; + +private: + bool m_notifyRecreate = false; }; // ---------------------------------------------------------------------------- diff --git a/src/gtk/choice.cpp b/src/gtk/choice.cpp index 778fb5288e..f47f7c277a 100644 --- a/src/gtk/choice.cpp +++ b/src/gtk/choice.cpp @@ -17,6 +17,7 @@ #endif #include "wx/gtk/private.h" +#include "wx/gtk/private/event.h" #include "wx/gtk/private/eventsdisabler.h" #include "wx/gtk/private/list.h" #include "wx/gtk/private/value.h" @@ -33,6 +34,26 @@ gtk_choice_changed_callback( GtkWidget *WXUNUSED(widget), wxChoice *choice ) choice->SendSelectionChangedEvent(wxEVT_CHOICE); } +#ifdef __WXGTK3__ + +static gboolean +wx_gtk_choice_enter_notify(GtkWidget* widget, + GdkEventCrossing* gdk_event, + wxChoice *choice) +{ + return wxGTKImpl::WindowEnterCallback(widget, gdk_event, choice); +} + +static gboolean +wx_gtk_choice_leave_notify(GtkWidget* widget, + GdkEventCrossing* gdk_event, + wxChoice* choice) +{ + return wxGTKImpl::WindowLeaveCallback(widget, gdk_event, choice); +} + +#endif // __WXGTK3__ + } //----------------------------------------------------------------------------- @@ -104,6 +125,35 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id, g_signal_connect_after (m_widget, "changed", G_CALLBACK (gtk_choice_changed_callback), this); +#ifdef __WXGTK3__ + // Internal structure of GtkComboBoxText is complicated: it contains a + // GtkBox which contains a GtkToggleButton which contains another GtkBox + // which, in turn, contains GtkCellView (and more). + // + // And it's this internal GtkToggleButton which receives the mouse events + // and not the main widget itself, so find it and connect to its events. + + // We could find it either by using gtk_container_forall() to get the box + // inside GtkComboBoxText and then get its only child, or by doing what we + // do here and getting GtkCellView directly and then getting its parent, + // which is simpler because GtkComboBoxText sets things up in such a way + // that its only child is the GtkCellView (even if, again, this is not how + // things really are internally). + auto cellView = gtk_bin_get_child(GTK_BIN(m_widget)); + wxCHECK_MSG( cellView, true, "No cell view in GtkComboBoxText?" ); + + auto box = gtk_widget_get_parent(cellView); + + auto button = gtk_widget_get_parent(box); + wxCHECK_MSG( GTK_IS_TOGGLE_BUTTON(button), true, + "Unexpected grandparent of GtkCellView in GtkComboBoxText" ); + + g_signal_connect(button, "enter_notify_event", + G_CALLBACK(wx_gtk_choice_enter_notify), this); + g_signal_connect(button, "leave_notify_event", + G_CALLBACK(wx_gtk_choice_leave_notify), this); +#endif // __WXGTK3__ + return true; } diff --git a/src/gtk/srchctrl.cpp b/src/gtk/srchctrl.cpp index 988df1abb0..1088fcd293 100644 --- a/src/gtk/srchctrl.cpp +++ b/src/gtk/srchctrl.cpp @@ -20,6 +20,7 @@ #include "wx/utils.h" #include "wx/gtk/private.h" +#include "wx/gtk/private/event.h" #include "wx/gtk/private/gtk3-compat.h" @@ -93,6 +94,28 @@ wx_gtk_icon_press(GtkEntry* WXUNUSED(entry), } } +static gboolean +wx_gtk_entry_event(GtkEntry* WXUNUSED(entry), + GdkEvent* event, + wxSearchCtrl* ctrl) +{ + if ( event->type == GDK_MOTION_NOTIFY ) + { + // GtkEntry "event" signal handler ignores motion events happening over + // inactive icons, but we want to notify the window about the mouse + // entering it when they happen. + if ( wxGTKImpl::SetWindowUnderMouse(ctrl) ) + { + wxMouseEvent mouseEvent(wxEVT_ENTER_WINDOW); + wxGTKImpl::InitMouseEvent(ctrl, mouseEvent, (GdkEventMotion*)event); + + ctrl->GTKProcessEvent(mouseEvent); + } + } + + return FALSE; +} + } // ============================================================================ @@ -209,6 +232,8 @@ void wxSearchCtrl::GTKCreateSearchEntryWidget() } g_signal_connect(m_entry, "icon-press", G_CALLBACK(wx_gtk_icon_press), this); + + g_signal_connect(m_entry, "event", G_CALLBACK(wx_gtk_entry_event), this); } GtkEditable *wxSearchCtrl::GetEditable() const diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 23d85f0b23..0f9e4233df 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -72,6 +72,8 @@ typedef guint KeySym; #define PANGO_VERSION_CHECK(a,b,c) 0 #endif +constexpr const char* TRACE_MOUSE = "mouse"; + //----------------------------------------------------------------------------- // documentation on internals //----------------------------------------------------------------------------- @@ -232,6 +234,23 @@ static wxWindowGTK *gs_deferredFocusOut = nullptr; GdkEvent *g_lastMouseEvent = nullptr; int g_lastButtonNumber = 0; +static wxWindowGTK* g_windowUnderMouse = nullptr; + +namespace wxGTKImpl +{ + +bool SetWindowUnderMouse(wxWindowGTK* win) +{ + if ( g_windowUnderMouse == win ) + return false; + + g_windowUnderMouse = win; + + return true; +} + +} // namespace wxGTKImpl + #ifdef wxHAS_XKB namespace { @@ -1667,38 +1686,19 @@ bool wxWindowGTK::GTKShouldIgnoreEvent() const return g_blockEventsOnDrag; } -int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const +// Some callbacks check for just g_blockEventsOnDrag but others check for both +// it and g_blockEventsOnScroll. It's not really clear why, but define a helper +// function performing the latter check too for now to avoid changing the +// behaviour of the existing code. +namespace { - if (g_blockEventsOnDrag) - return TRUE; - if (g_blockEventsOnScroll) - return TRUE; - if (!GTKIsOwnWindow(event->window)) - return FALSE; - - return -1; +bool AreGTKEventsBlocked() +{ + return g_blockEventsOnDrag || g_blockEventsOnScroll; } -// overloads for all GDK event types we use here: we need to have this as -// GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact, -// derives from it in the sense that the structs have the same layout -#define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \ - static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \ - { \ - return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \ - } - -wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton) -wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion) -wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing) - -#undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD - -#define wxCOMMON_CALLBACK_PROLOGUE(event, win) \ - const int rc = wxGtkCallbackCommonPrologue(event, win); \ - if ( rc != -1 ) \ - return rc +} // anonymous namespace // all event handlers must have C linkage as they're called from GTK+ C code extern "C" @@ -1722,7 +1722,8 @@ gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget), wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event); - wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); + if ( AreGTKEventsBlocked() ) + return FALSE; g_lastButtonNumber = gdk_event->button; @@ -1856,7 +1857,8 @@ gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget), { wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event); - wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); + if ( AreGTKEventsBlocked() ) + return FALSE; g_lastButtonNumber = 0; @@ -1958,7 +1960,8 @@ gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget), { wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event); - wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); + if ( AreGTKEventsBlocked() ) + return FALSE; g_lastMouseEvent = (GdkEvent*) gdk_event; @@ -2243,19 +2246,49 @@ wx_window_focus_callback(GtkWidget *widget, return FALSE; } +} // extern "C" + //----------------------------------------------------------------------------- // "enter_notify_event" //----------------------------------------------------------------------------- -static gboolean -gtk_window_enter_callback( GtkWidget*, - GdkEventCrossing *gdk_event, - wxWindowGTK *win ) +gboolean +wxGTKImpl::WindowEnterCallback(GtkWidget* widget, + GdkEventCrossing* gdk_event, + wxWindowGTK* win) { - wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); + wxLogTrace(TRACE_MOUSE, "Window enter in %s (window %p) for window %p", + wxDumpWindow(win), gtk_widget_get_window(widget), gdk_event->window); + + if ( AreGTKEventsBlocked() ) + return FALSE; // Event was emitted after a grab - if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; + if (gdk_event->mode != GDK_CROSSING_NORMAL) + { + wxLogTrace(TRACE_MOUSE, "Ignore event with mode %d", gdk_event->mode); + return FALSE; + } + + if ( g_windowUnderMouse == win ) + { + // This can happen if the enter event was generated from another + // callback, as is the case for wxSearchCtrl, for example. + wxLogTrace(TRACE_MOUSE, "Reentering window %s", wxDumpWindow(win)); + return FALSE; + } + + if ( g_windowUnderMouse ) + { + // We must not have got the leave event for the previous window, so + // generate it now -- better late than never. + wxMouseEvent event( wxEVT_LEAVE_WINDOW ); + InitMouseEvent(g_windowUnderMouse, event, gdk_event); + + (void)g_windowUnderMouse->GTKProcessEvent(event); + } + + g_windowUnderMouse = win; wxMouseEvent event( wxEVT_ENTER_WINDOW ); InitMouseEvent(win, event, gdk_event); @@ -2266,22 +2299,45 @@ gtk_window_enter_callback( GtkWidget*, return win->GTKProcessEvent(event); } +extern "C" { + +static gboolean +gtk_window_enter_callback( GtkWidget* widget, + GdkEventCrossing *gdk_event, + wxWindowGTK *win ) +{ + return wxGTKImpl::WindowEnterCallback(widget, gdk_event, win); +} + +} // extern "C" + //----------------------------------------------------------------------------- // "leave_notify_event" //----------------------------------------------------------------------------- -static gboolean -gtk_window_leave_callback( GtkWidget*, - GdkEventCrossing *gdk_event, - wxWindowGTK *win ) +gboolean +wxGTKImpl::WindowLeaveCallback(GtkWidget* widget, + GdkEventCrossing* gdk_event, + wxWindowGTK* win) { - wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); + wxLogTrace(TRACE_MOUSE, "Window leave in %s (window %p) for window %p", + wxDumpWindow(win), gtk_widget_get_window(widget), gdk_event->window); + + if ( AreGTKEventsBlocked() ) + return FALSE; if (win->m_needCursorReset) win->GTKUpdateCursor(); // Event was emitted after an ungrab - if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; + if (gdk_event->mode != GDK_CROSSING_NORMAL) + { + wxLogTrace(TRACE_MOUSE, "Ignore event with mode %d", gdk_event->mode); + return FALSE; + } + + if ( win == g_windowUnderMouse ) + g_windowUnderMouse = nullptr; wxMouseEvent event( wxEVT_LEAVE_WINDOW ); InitMouseEvent(win, event, gdk_event); @@ -2289,6 +2345,16 @@ gtk_window_leave_callback( GtkWidget*, return win->GTKProcessEvent(event); } +extern "C" { + +static gboolean +gtk_window_leave_callback( GtkWidget* widget, + GdkEventCrossing *gdk_event, + wxWindowGTK *win ) +{ + return wxGTKImpl::WindowLeaveCallback(widget, gdk_event, win); +} + //----------------------------------------------------------------------------- // "value_changed" from scrollbar //----------------------------------------------------------------------------- @@ -2912,6 +2978,9 @@ wxWindowGTK::~wxWindowGTK() if ( g_captureWindow == this ) g_captureWindow = nullptr; + if ( g_windowUnderMouse == this ) + g_windowUnderMouse = nullptr; + if (m_wxwindow) { GTKDisconnect(m_wxwindow);