Merge branch 'gtk-enter-leave-window'

Fixes for wxGTK enter/leave window events.

Closes #11848.

See #24339.
This commit is contained in:
Vadim Zeitlin 2024-02-29 03:01:38 +01:00
commit b03ce7711b
37 changed files with 360 additions and 104 deletions

View file

@ -88,6 +88,23 @@ template<typename T> void InitMouseEvent(wxWindowGTK *win,
event.SetTimestamp( gdk_event->time ); 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 } // namespace wxGTKImpl
#endif // _GTK_PRIVATE_EVENT_H_ #endif // _GTK_PRIVATE_EVENT_H_

View file

@ -171,14 +171,9 @@ public:
virtual GtkWidget* GetConnectWidget(); virtual GtkWidget* GetConnectWidget();
void ConnectWidget( GtkWidget *widget ); 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 // Returns true if GTK callbacks are blocked due to a drag event being in
// GTK callbacks without return value to check if the event should be // progress.
// ignored: if this returns true, the event shouldn't be handled
bool GTKShouldIgnoreEvent() const; bool GTKShouldIgnoreEvent() const;

View file

@ -145,6 +145,8 @@ void ActivityIndicatorWidgetsPage::RecreateWidget()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
GetAttrs().m_defaultFlags); GetAttrs().m_defaultFlags);
NotifyWidgetRecreation(m_indicator);
m_sizerIndicator->AddStretchSpacer(); m_sizerIndicator->AddStretchSpacer();
m_sizerIndicator->Add(m_indicator, wxSizerFlags().Centre()); m_sizerIndicator->Add(m_indicator, wxSizerFlags().Centre());
m_sizerIndicator->AddStretchSpacer(); m_sizerIndicator->AddStretchSpacer();

View file

@ -478,6 +478,8 @@ void BitmapComboBoxWidgetsPage::CreateCombo()
m_combobox->SetPopupMaxHeight(600); m_combobox->SetPopupMaxHeight(600);
#endif #endif
NotifyWidgetRecreation(m_combobox);
unsigned int count = items.GetCount(); unsigned int count = items.GetCount();
for ( unsigned int n = 0; n < count; n++ ) for ( unsigned int n = 0; n < count; n++ )
{ {

View file

@ -562,6 +562,8 @@ void ButtonWidgetsPage::CreateButton()
m_sizerNote->Show(m_chkCommandLink->GetValue()); m_sizerNote->Show(m_chkCommandLink->GetValue());
#endif #endif
NotifyWidgetRecreation(m_button);
if ( !showsBitmap && m_chkTextAndBitmap->GetValue() ) if ( !showsBitmap && m_chkTextAndBitmap->GetValue() )
{ {
showsBitmap = true; showsBitmap = true;

View file

@ -276,6 +276,8 @@ void CheckBoxWidgetsPage::CreateCheckbox()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
NotifyWidgetRecreation(m_checkbox);
m_sizerCheckbox->Add(0, 0, 1, wxCENTRE); m_sizerCheckbox->Add(0, 0, 1, wxCENTRE);
m_sizerCheckbox->Add(m_checkbox, 1, wxCENTRE); m_sizerCheckbox->Add(m_checkbox, 1, wxCENTRE);
m_sizerCheckbox->Add(0, 0, 1, wxCENTRE); m_sizerCheckbox->Add(0, 0, 1, wxCENTRE);

View file

@ -303,6 +303,8 @@ void ChoiceWidgetsPage::CreateChoice()
0, nullptr, 0, nullptr,
flags); flags);
NotifyWidgetRecreation(m_choice);
m_choice->Set(items); m_choice->Set(items);
m_sizerChoice->Add(m_choice, 0, wxGROW | wxALL, 5); m_sizerChoice->Add(m_choice, 0, wxGROW | wxALL, 5);
m_sizerChoice->Layout(); m_sizerChoice->Layout();

View file

@ -191,6 +191,8 @@ void ColourPickerWidgetsPage::CreatePicker()
m_clrPicker = new wxColourPickerCtrl(this, PickerPage_Colour, *wxRED, m_clrPicker = new wxColourPickerCtrl(this, PickerPage_Colour, *wxRED,
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
style); style);
NotifyWidgetRecreation(m_clrPicker);
} }
void ColourPickerWidgetsPage::RecreatePicker() void ColourPickerWidgetsPage::RecreatePicker()

View file

@ -476,6 +476,8 @@ void ComboboxWidgetsPage::CreateCombo()
delete m_combobox; delete m_combobox;
m_combobox = newCb; m_combobox = newCb;
m_combobox->SetId(ComboPage_Combo); m_combobox->SetId(ComboPage_Combo);
NotifyWidgetRecreation(m_combobox);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View file

@ -285,6 +285,8 @@ void DatePickerWidgetsPage::CreateDatePicker()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
style); style);
NotifyWidgetRecreation(m_datePicker);
m_sizerDatePicker->Add(0, 0, 1, wxCENTRE); m_sizerDatePicker->Add(0, 0, 1, wxCENTRE);
m_sizerDatePicker->Add(m_datePicker, 1, wxCENTRE); m_sizerDatePicker->Add(m_datePicker, 1, wxCENTRE);
m_sizerDatePicker->Add(0, 0, 1, wxCENTRE); m_sizerDatePicker->Add(0, 0, 1, wxCENTRE);

View file

@ -302,6 +302,8 @@ void DirCtrlWidgetsPage::CreateDirCtrl(bool defaultPath)
delete m_dirCtrl; delete m_dirCtrl;
m_dirCtrl = dirCtrl; m_dirCtrl = dirCtrl;
NotifyWidgetRecreation(m_dirCtrl);
// relayout the sizer // relayout the sizer
GetSizer()->Layout(); GetSizer()->Layout();
} }

View file

@ -209,6 +209,8 @@ void DirPickerWidgetsPage::CreatePicker()
wxGetHomeDir(), "Hello!", wxGetHomeDir(), "Hello!",
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
style); style);
NotifyWidgetRecreation(m_dirPicker);
} }
void DirPickerWidgetsPage::RecreatePicker() void DirPickerWidgetsPage::RecreatePicker()

View file

@ -198,6 +198,8 @@ void EditableListboxWidgetsPage::CreateLbox()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
NotifyWidgetRecreation(m_lbox);
m_lbox->SetStrings(items); m_lbox->SetStrings(items);
m_sizerLbox->Add(m_lbox, 1, wxGROW | wxALL, 5); m_sizerLbox->Add(m_lbox, 1, wxGROW | wxALL, 5);
m_sizerLbox->Layout(); m_sizerLbox->Layout();

View file

@ -275,6 +275,8 @@ void FileCtrlWidgetsPage::CreateFileCtrl()
delete m_fileCtrl; delete m_fileCtrl;
m_fileCtrl = fileCtrl; m_fileCtrl = fileCtrl;
NotifyWidgetRecreation(m_fileCtrl);
// relayout the sizer // relayout the sizer
GetSizer()->Layout(); GetSizer()->Layout();
} }

View file

@ -248,6 +248,8 @@ void FilePickerWidgetsPage::CreatePicker()
"Hello!", "*", "Hello!", "*",
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
style); style);
NotifyWidgetRecreation(m_filePicker);
} }
void FilePickerWidgetsPage::RecreatePicker() void FilePickerWidgetsPage::RecreatePicker()

View file

@ -186,6 +186,8 @@ void FontPickerWidgetsPage::CreatePicker()
*wxSWISS_FONT, *wxSWISS_FONT,
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
style); style);
NotifyWidgetRecreation(m_fontPicker);
} }
void FontPickerWidgetsPage::RecreatePicker() void FontPickerWidgetsPage::RecreatePicker()

View file

@ -307,6 +307,9 @@ void GaugeWidgetsPage::CreateGauge()
m_gauge = new wxGauge(this, GaugePage_Gauge, m_range, m_gauge = new wxGauge(this, GaugePage_Gauge, m_range,
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
NotifyWidgetRecreation(m_gauge);
m_gauge->SetValue(val); m_gauge->SetValue(val);
if ( flags & wxGA_VERTICAL ) if ( flags & wxGA_VERTICAL )

View file

@ -200,6 +200,8 @@ void HeaderCtrlWidgetsPage::RecreateWidget()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
NotifyWidgetRecreation(m_header);
m_header->Bind(wxEVT_HEADER_RESIZING, &HeaderCtrlWidgetsPage::OnResizing, this); m_header->Bind(wxEVT_HEADER_RESIZING, &HeaderCtrlWidgetsPage::OnResizing, this);
m_header->Bind(wxEVT_HEADER_BEGIN_RESIZE, &HeaderCtrlWidgetsPage::OnBeginResize, this); m_header->Bind(wxEVT_HEADER_BEGIN_RESIZE, &HeaderCtrlWidgetsPage::OnBeginResize, this);
m_header->Bind(wxEVT_HEADER_END_RESIZE, &HeaderCtrlWidgetsPage::OnEndResize, this); m_header->Bind(wxEVT_HEADER_END_RESIZE, &HeaderCtrlWidgetsPage::OnEndResize, this);

View file

@ -288,6 +288,8 @@ void HyperlinkWidgetsPage::CreateHyperlink()
delete m_hyperlink; delete m_hyperlink;
m_hyperlink = hyp; m_hyperlink = hyp;
NotifyWidgetRecreation(m_hyperlink);
// relayout the sizer // relayout the sizer
GetSizer()->Layout(); GetSizer()->Layout();
} }

View file

@ -506,6 +506,8 @@ void ListboxWidgetsPage::CreateLbox()
flags); flags);
} }
NotifyWidgetRecreation(m_lbox);
m_sizerLbox->Add(m_lbox, 1, wxGROW | wxALL, 5); m_sizerLbox->Add(m_lbox, 1, wxGROW | wxALL, 5);
m_sizerLbox->Layout(); m_sizerLbox->Layout();
} }

View file

@ -308,6 +308,8 @@ void NativeWidgetsPage::RecreateWidget()
delete m_nativeWindow; delete m_nativeWindow;
m_nativeWindow = new NativeWindow(this); m_nativeWindow = new NativeWindow(this);
NotifyWidgetRecreation(m_nativeWindow);
m_sizerCtrl->Clear(); m_sizerCtrl->Clear();
if ( m_chkExpand->IsChecked() ) if ( m_chkExpand->IsChecked() )
{ {

View file

@ -365,6 +365,8 @@ void BookWidgetsPage::RecreateBook()
m_book = CreateBook(flags); m_book = CreateBook(flags);
NotifyWidgetRecreation(m_book);
CreateImageList(); CreateImageList();
if ( oldBook ) if ( oldBook )

View file

@ -527,6 +527,8 @@ void ODComboboxWidgetsPage::CreateCombo()
0, nullptr, 0, nullptr,
flags); flags);
NotifyWidgetRecreation(m_combobox);
unsigned int count = items.GetCount(); unsigned int count = items.GetCount();
for ( unsigned int n = 0; n < count; n++ ) for ( unsigned int n = 0; n < count; n++ )
{ {

View file

@ -358,6 +358,8 @@ void RadioWidgetsPage::CreateRadio()
majorDim, majorDim,
flags); flags);
NotifyWidgetRecreation(m_radio);
if ( sel >= 0 && (size_t)sel < count ) if ( sel >= 0 && (size_t)sel < count )
{ {
m_radio->SetSelection(sel); m_radio->SetSelection(sel);

View file

@ -178,6 +178,8 @@ void SearchCtrlWidgetsPage::CreateControl()
m_srchCtrl = new wxSearchCtrl(this, -1, wxEmptyString, wxDefaultPosition, m_srchCtrl = new wxSearchCtrl(this, -1, wxEmptyString, wxDefaultPosition,
FromDIP(wxSize(150, -1)), style); FromDIP(wxSize(150, -1)), style);
NotifyWidgetRecreation(m_srchCtrl);
} }
void SearchCtrlWidgetsPage::RecreateWidget() void SearchCtrlWidgetsPage::RecreateWidget()

View file

@ -517,6 +517,8 @@ void SliderWidgetsPage::CreateSlider()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
NotifyWidgetRecreation(m_slider);
if ( m_slider->HasFlag(wxSL_VERTICAL) ) if ( m_slider->HasFlag(wxSL_VERTICAL) )
{ {
m_sizerSlider->AddStretchSpacer(1); m_sizerSlider->AddStretchSpacer(1);

View file

@ -416,6 +416,8 @@ void SpinBtnWidgetsPage::CreateSpin()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
NotifyWidgetRecreation(m_spinbtn);
m_spinbtn->SetValue(val); m_spinbtn->SetValue(val);
m_spinbtn->SetRange(m_min, m_max); m_spinbtn->SetRange(m_min, m_max);
@ -425,12 +427,16 @@ void SpinBtnWidgetsPage::CreateSpin()
flags | textFlags, flags | textFlags,
m_min, m_max, val); m_min, m_max, val);
NotifyWidgetRecreation(m_spinctrl);
m_spinctrldbl = new wxSpinCtrlDouble(this, SpinBtnPage_SpinCtrlDouble, m_spinctrldbl = new wxSpinCtrlDouble(this, SpinBtnPage_SpinCtrlDouble,
wxString::Format("%d", val), wxString::Format("%d", val),
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags | textFlags, flags | textFlags,
m_min, m_max, val, 0.1); m_min, m_max, val, 0.1);
NotifyWidgetRecreation(m_spinctrldbl);
// Add spacers, labels and spin controls to the sizer. // Add spacers, labels and spin controls to the sizer.
m_sizerSpin->Add(0, 0, 1); m_sizerSpin->Add(0, 0, 1);
m_sizerSpin->Add(new wxStaticText(this, wxID_ANY, "wxSpinButton"), m_sizerSpin->Add(new wxStaticText(this, wxID_ANY, "wxSpinButton"),

View file

@ -161,6 +161,8 @@ void StatBmpWidgetsPage::RecreateWidget()
style); style);
} }
NotifyWidgetRecreation(m_statbmp);
wxStaticBitmapBase::ScaleMode scaleMode = (wxStaticBitmapBase::ScaleMode) m_scaleRadio->GetSelection(); wxStaticBitmapBase::ScaleMode scaleMode = (wxStaticBitmapBase::ScaleMode) m_scaleRadio->GetSelection();
m_statbmp->SetScaleMode(scaleMode); m_statbmp->SetScaleMode(scaleMode);
if ( m_statbmp->GetScaleMode() != scaleMode ) if ( m_statbmp->GetScaleMode() != scaleMode )

View file

@ -525,9 +525,13 @@ void StaticWidgetsPage::CreateStatic()
#endif // wxUSE_MARKUP #endif // wxUSE_MARKUP
} }
NotifyWidgetRecreation(m_statText);
m_statText->SetToolTip("Tooltip for a label inside the box"); m_statText->SetToolTip("Tooltip for a label inside the box");
#if wxUSE_MARKUP #if wxUSE_MARKUP
NotifyWidgetRecreation(m_statMarkup);
m_statMarkup->SetLabelMarkup(m_textLabelWithMarkup->GetValue()); m_statMarkup->SetLabelMarkup(m_textLabelWithMarkup->GetValue());
if ( m_chkGreen->GetValue() ) if ( m_chkGreen->GetValue() )
@ -538,6 +542,8 @@ void StaticWidgetsPage::CreateStatic()
m_statLine = new wxStaticLine(staticBox, wxID_ANY, m_statLine = new wxStaticLine(staticBox, wxID_ANY,
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
isVert ? wxLI_VERTICAL : wxLI_HORIZONTAL); isVert ? wxLI_VERTICAL : wxLI_HORIZONTAL);
NotifyWidgetRecreation(m_statLine);
#endif // wxUSE_STATLINE #endif // wxUSE_STATLINE
m_sizerStatBox->Add(m_statText, 0, wxGROW); m_sizerStatBox->Add(m_statText, 0, wxGROW);

View file

@ -787,6 +787,8 @@ void TextWidgetsPage::CreateText()
m_text = new WidgetsTextCtrl(m_sizerText->GetStaticBox(), TextPage_Textctrl, valueOld, flags); m_text = new WidgetsTextCtrl(m_sizerText->GetStaticBox(), TextPage_Textctrl, valueOld, flags);
NotifyWidgetRecreation(m_text);
#if 0 #if 0
if ( m_chkFilename->GetValue() ) if ( m_chkFilename->GetValue() )
; ;

View file

@ -198,6 +198,8 @@ void TimePickerWidgetsPage::CreateTimePicker()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
style); style);
NotifyWidgetRecreation(m_timePicker);
m_sizerTimePicker->Add(0, 0, 1, wxCENTRE); m_sizerTimePicker->Add(0, 0, 1, wxCENTRE);
m_sizerTimePicker->Add(m_timePicker, 1, wxCENTRE); m_sizerTimePicker->Add(m_timePicker, 1, wxCENTRE);
m_sizerTimePicker->Add(0, 0, 1, wxCENTRE); m_sizerTimePicker->Add(0, 0, 1, wxCENTRE);

View file

@ -461,6 +461,9 @@ void ToggleWidgetsPage::CreateToggle()
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
flags); flags);
} }
NotifyWidgetRecreation(m_toggle);
m_toggle->SetValue(value); m_toggle->SetValue(value);
#ifdef wxHAS_BITMAPTOGGLEBUTTON #ifdef wxHAS_BITMAPTOGGLEBUTTON

View file

@ -183,6 +183,10 @@ public:
// real implementation of WidgetsPage method with the same name // real implementation of WidgetsPage method with the same name
bool IsUsingLogWindow() const; bool IsUsingLogWindow() const;
// connects handlers showing some interesting widget events to the given
// widget
void ConnectToWidgetEvents(wxWindow* w);
private: private:
#if USE_LOG #if USE_LOG
wxLog* m_logTarget; wxLog* m_logTarget;
@ -253,11 +257,6 @@ protected:
WidgetsPage *CurrentPage(); WidgetsPage *CurrentPage();
private: private:
void OnWidgetFocus(wxFocusEvent& event);
void OnWidgetContextMenu(wxContextMenuEvent& event);
void ConnectToWidgetEvents();
// the panel containing everything // the panel containing everything
wxPanel *m_panel; wxPanel *m_panel;
@ -430,6 +429,51 @@ bool WidgetsApp::IsUsingLogWindow() const
#endif // USE_LOG #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 // WidgetsFrame construction
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -727,24 +771,6 @@ WidgetsPage *WidgetsFrame::CurrentPage()
return wxStaticCast(page, WidgetsPage); 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() WidgetsFrame::~WidgetsFrame()
{ {
#if USE_LOG #if USE_LOG
@ -802,7 +828,18 @@ void WidgetsFrame::OnPageChanged(WidgetsBookCtrlEvent& event)
curPage->SetScrollRate(10, 10); curPage->SetScrollRate(10, 10);
curPage->FitInside(); 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) // re-apply the attributes to the widget(s)
@ -971,11 +1008,6 @@ void WidgetsFrame::OnSetBorder(wxCommandEvent& event)
WidgetsPage *page = CurrentPage(); WidgetsPage *page = CurrentPage();
page->RecreateWidget(); page->RecreateWidget();
ConnectToWidgetEvents();
// re-apply the attributes to the widget(s)
page->SetUpWidget();
} }
void WidgetsFrame::OnSetVariant(wxCommandEvent& event) void WidgetsFrame::OnSetVariant(wxCommandEvent& event)
@ -1259,31 +1291,6 @@ void WidgetsFrame::OnSetHint(wxCommandEvent& WXUNUSED(event))
#endif // wxUSE_MENUS #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 // WidgetsPageInfo
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -1462,6 +1469,19 @@ wxCheckBox *WidgetsPage::CreateCheckBoxAndAddToSizer(wxSizer *sizer,
return checkbox; 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 */ /* static */
bool WidgetsPage::IsUsingLogWindow() bool WidgetsPage::IsUsingLogWindow()
{ {

View file

@ -149,6 +149,13 @@ public:
// this is currently used only to take into account the border flags // this is currently used only to take into account the border flags
virtual void RecreateWidget() = 0; 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) // apply current attributes to the widget(s)
void SetUpWidget(); void SetUpWidget();
@ -191,6 +198,9 @@ protected:
public: public:
// the head of the linked list containinginfo about all pages // the head of the linked list containinginfo about all pages
static WidgetsPageInfo *ms_widgetPages; static WidgetsPageInfo *ms_widgetPages;
private:
bool m_notifyRecreate = false;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View file

@ -17,6 +17,7 @@
#endif #endif
#include "wx/gtk/private.h" #include "wx/gtk/private.h"
#include "wx/gtk/private/event.h"
#include "wx/gtk/private/eventsdisabler.h" #include "wx/gtk/private/eventsdisabler.h"
#include "wx/gtk/private/list.h" #include "wx/gtk/private/list.h"
#include "wx/gtk/private/value.h" #include "wx/gtk/private/value.h"
@ -33,6 +34,26 @@ gtk_choice_changed_callback( GtkWidget *WXUNUSED(widget), wxChoice *choice )
choice->SendSelectionChangedEvent(wxEVT_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_signal_connect_after (m_widget, "changed",
G_CALLBACK (gtk_choice_changed_callback), this); 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; return true;
} }

View file

@ -20,6 +20,7 @@
#include "wx/utils.h" #include "wx/utils.h"
#include "wx/gtk/private.h" #include "wx/gtk/private.h"
#include "wx/gtk/private/event.h"
#include "wx/gtk/private/gtk3-compat.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, "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 GtkEditable *wxSearchCtrl::GetEditable() const

View file

@ -72,6 +72,8 @@ typedef guint KeySym;
#define PANGO_VERSION_CHECK(a,b,c) 0 #define PANGO_VERSION_CHECK(a,b,c) 0
#endif #endif
constexpr const char* TRACE_MOUSE = "mouse";
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// documentation on internals // documentation on internals
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -232,6 +234,23 @@ static wxWindowGTK *gs_deferredFocusOut = nullptr;
GdkEvent *g_lastMouseEvent = nullptr; GdkEvent *g_lastMouseEvent = nullptr;
int g_lastButtonNumber = 0; 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 #ifdef wxHAS_XKB
namespace namespace
{ {
@ -1667,38 +1686,19 @@ bool wxWindowGTK::GTKShouldIgnoreEvent() const
return g_blockEventsOnDrag; 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)) bool AreGTKEventsBlocked()
return FALSE; {
return g_blockEventsOnDrag || g_blockEventsOnScroll;
return -1;
} }
// overloads for all GDK event types we use here: we need to have this as } // anonymous namespace
// 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
// all event handlers must have C linkage as they're called from GTK+ C code // all event handlers must have C linkage as they're called from GTK+ C code
extern "C" extern "C"
@ -1722,7 +1722,8 @@ gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget),
wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event); wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); if ( AreGTKEventsBlocked() )
return FALSE;
g_lastButtonNumber = gdk_event->button; g_lastButtonNumber = gdk_event->button;
@ -1856,7 +1857,8 @@ gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget),
{ {
wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event); wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); if ( AreGTKEventsBlocked() )
return FALSE;
g_lastButtonNumber = 0; g_lastButtonNumber = 0;
@ -1958,7 +1960,8 @@ gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
{ {
wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event); wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event);
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win); if ( AreGTKEventsBlocked() )
return FALSE;
g_lastMouseEvent = (GdkEvent*) gdk_event; g_lastMouseEvent = (GdkEvent*) gdk_event;
@ -2243,19 +2246,49 @@ wx_window_focus_callback(GtkWidget *widget,
return FALSE; return FALSE;
} }
} // extern "C"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// "enter_notify_event" // "enter_notify_event"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static gboolean gboolean
gtk_window_enter_callback( GtkWidget*, wxGTKImpl::WindowEnterCallback(GtkWidget* widget,
GdkEventCrossing *gdk_event, GdkEventCrossing* gdk_event,
wxWindowGTK *win ) 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 // 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 ); wxMouseEvent event( wxEVT_ENTER_WINDOW );
InitMouseEvent(win, event, gdk_event); InitMouseEvent(win, event, gdk_event);
@ -2266,22 +2299,45 @@ gtk_window_enter_callback( GtkWidget*,
return win->GTKProcessEvent(event); 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" // "leave_notify_event"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static gboolean gboolean
gtk_window_leave_callback( GtkWidget*, wxGTKImpl::WindowLeaveCallback(GtkWidget* widget,
GdkEventCrossing *gdk_event, GdkEventCrossing* gdk_event,
wxWindowGTK *win ) 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) if (win->m_needCursorReset)
win->GTKUpdateCursor(); win->GTKUpdateCursor();
// Event was emitted after an ungrab // 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 ); wxMouseEvent event( wxEVT_LEAVE_WINDOW );
InitMouseEvent(win, event, gdk_event); InitMouseEvent(win, event, gdk_event);
@ -2289,6 +2345,16 @@ gtk_window_leave_callback( GtkWidget*,
return win->GTKProcessEvent(event); 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 // "value_changed" from scrollbar
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -2912,6 +2978,9 @@ wxWindowGTK::~wxWindowGTK()
if ( g_captureWindow == this ) if ( g_captureWindow == this )
g_captureWindow = nullptr; g_captureWindow = nullptr;
if ( g_windowUnderMouse == this )
g_windowUnderMouse = nullptr;
if (m_wxwindow) if (m_wxwindow)
{ {
GTKDisconnect(m_wxwindow); GTKDisconnect(m_wxwindow);