mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-09-28 15:50:17 +08:00
Provide safe API for detecting closing wxTipWindow
Previous API was ugly and suffered from a race condition as the window could be closed before the constructor returned. Provide a safer API using weak-pointer-like wxTipWindow::Ref class that is guaranteed to stop being valid when the tip window is closed. Also implement two-stage constructions for this class, as usual. Closes #26070. Closes #26071.
This commit is contained in:
+50
-2
@@ -26,7 +26,55 @@ class WXDLLIMPEXP_FWD_CORE wxTipWindowView;
|
||||
class WXDLLIMPEXP_CORE wxTipWindow : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
// the mandatory ctor parameters are: the parent window and the text to
|
||||
// wxTipWindow may close itself, so provide a smart pointer that acts as a
|
||||
// weak reference to wxTipWindow.
|
||||
//
|
||||
// Note that this is a move-only type (because wxTipWindow::Close() only
|
||||
// sets a single wxTipWindow* to nullptr).
|
||||
//
|
||||
// Note that this is not a wxWeakRef<> because this is set to nullptr when
|
||||
// wxTipWindow is closed, which may be "long" before wxTipWindow is
|
||||
// destroyed, bug wxWeakRef<> is set to nullptr on object destruction
|
||||
class WXDLLIMPEXP_CORE Ref
|
||||
{
|
||||
public:
|
||||
Ref() = default;
|
||||
~Ref();
|
||||
Ref(const Ref&) = delete;
|
||||
Ref(Ref&& other);
|
||||
Ref& operator=(const Ref&) = delete;
|
||||
Ref& operator=(Ref&& other);
|
||||
|
||||
Ref& operator=(std::nullptr_t);
|
||||
|
||||
bool operator!=(std::nullptr_t) const { return m_ptr != nullptr; }
|
||||
explicit operator bool() const { return m_ptr; }
|
||||
wxTipWindow* operator->() const { return m_ptr; }
|
||||
|
||||
private:
|
||||
wxTipWindow* m_ptr = nullptr;
|
||||
|
||||
friend wxTipWindow;
|
||||
};
|
||||
|
||||
// replace the deprecated single-step constructor
|
||||
// see Create() for parameters
|
||||
static Ref New(wxWindow *parent,
|
||||
const wxString& text,
|
||||
wxCoord maxLength = 100,
|
||||
wxRect *rectBound = nullptr);
|
||||
|
||||
wxTipWindow();
|
||||
|
||||
wxDEPRECATED_MSG("Using this has a race condition; use New() instead")
|
||||
wxTipWindow(wxWindow *parent,
|
||||
const wxString& text,
|
||||
wxCoord maxLength = 100,
|
||||
wxTipWindow** windowPtr = nullptr,
|
||||
wxRect *rectBound = nullptr)
|
||||
{ (void)Create(parent, text, maxLength, windowPtr, rectBound); }
|
||||
|
||||
// the mandatory parameters are: the parent window and the text to
|
||||
// show
|
||||
//
|
||||
// optionally you may also specify the length at which the lines are going
|
||||
@@ -34,7 +82,7 @@ public:
|
||||
//
|
||||
// windowPtr and rectBound are just passed to SetTipWindowPtr() and
|
||||
// SetBoundingRect() - see below
|
||||
wxTipWindow(wxWindow *parent,
|
||||
bool Create(wxWindow *parent,
|
||||
const wxString& text,
|
||||
wxCoord maxLength = 100,
|
||||
wxTipWindow** windowPtr = nullptr,
|
||||
|
||||
+113
-2
@@ -25,7 +25,116 @@ class wxTipWindow : public wxWindow
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Constructor. The tip is shown immediately after the window is constructed.
|
||||
The (weak) reference to wxTipWindow.
|
||||
|
||||
wxTipWindow may close itself at any moment, so creating it as usual,
|
||||
with `new` and using a raw pointer to it is dangerous. Instead, use
|
||||
New() to create it and use the returned Ref which is guaranteed to
|
||||
become invalid when the tip window is closed.
|
||||
|
||||
Note that the objects of this type can't be copied, but can be moved.
|
||||
|
||||
To test if this object is still valid, it can be compared to @NULL or
|
||||
its explicit bool conversion operator can be used, i.e. either this:
|
||||
|
||||
@code
|
||||
wxTipWindow::Ref tip = wxTipWindow::New(...);
|
||||
if ( tip != nullptr )
|
||||
tip->DoSomething();
|
||||
@endcode
|
||||
|
||||
or this:
|
||||
|
||||
@code
|
||||
wxTipWindow::Ref tip = wxTipWindow::New(...);
|
||||
if ( tip )
|
||||
tip->DoSomething();
|
||||
@endcode
|
||||
|
||||
works fine.
|
||||
|
||||
@since 3.3.2
|
||||
*/
|
||||
class Ref
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Default constructor.
|
||||
|
||||
The object is created in invalid state.
|
||||
*/
|
||||
Ref();
|
||||
|
||||
/**
|
||||
Put object in invalid state.
|
||||
*/
|
||||
Ref& operator=(std::nullptr_t);
|
||||
|
||||
/**
|
||||
Returns whether the object is in a valid state.
|
||||
|
||||
Object is in valid state if it is associated with a still open
|
||||
wxTipWindow.
|
||||
*/
|
||||
bool operator!=(std::nullptr_t) const;
|
||||
|
||||
/**
|
||||
Returns whether the tracked object is valid.
|
||||
|
||||
This is equivalent to comparing the object to @NULL.
|
||||
*/
|
||||
explicit operator bool() const;
|
||||
|
||||
/**
|
||||
Returns a pointer to the tracked object.
|
||||
|
||||
May only be called if the object is valid.
|
||||
*/
|
||||
wxTipWindow* operator->() const;
|
||||
};
|
||||
|
||||
/*
|
||||
Replace the deprecated single-step constructor. See
|
||||
Create() for parameters
|
||||
|
||||
@see Create()
|
||||
|
||||
@since 3.3.2
|
||||
*/
|
||||
static Ref New(wxWindow *parent,
|
||||
const wxString& text,
|
||||
wxCoord maxLength = 100,
|
||||
wxRect *rectBound = nullptr);
|
||||
|
||||
/**
|
||||
Default constructor.
|
||||
|
||||
If this constructor is used, Create() must be called later to actually
|
||||
create the window.
|
||||
|
||||
Prefer using New() instead.
|
||||
|
||||
@since 3.3.2
|
||||
*/
|
||||
wxTipWindow();
|
||||
|
||||
/**
|
||||
Use New() instead instead of the new code.
|
||||
|
||||
This constructor is preserved only for backward compatibility, don't
|
||||
use it in the new code.
|
||||
|
||||
@deprecated
|
||||
*/
|
||||
wxTipWindow(wxWindow* parent, const wxString& text,
|
||||
wxCoord maxLength = 100,
|
||||
wxTipWindow** windowPtr = nullptr,
|
||||
wxRect* rectBounds = nullptr);
|
||||
|
||||
/**
|
||||
Construct the actual window object.
|
||||
|
||||
The tip is shown immediately.
|
||||
|
||||
@param parent
|
||||
The parent window, must be non-null
|
||||
@@ -40,8 +149,10 @@ public:
|
||||
@param rectBounds
|
||||
If non-null, passed to SetBoundingRect() below, please see its
|
||||
documentation for the description of this parameter
|
||||
|
||||
@since 3.3.2
|
||||
*/
|
||||
wxTipWindow(wxWindow* parent, const wxString& text,
|
||||
bool Create(wxWindow* parent, const wxString& text,
|
||||
wxCoord maxLength = 100,
|
||||
wxTipWindow** windowPtr = nullptr,
|
||||
wxRect* rectBounds = nullptr);
|
||||
|
||||
@@ -763,10 +763,6 @@ MyFrame::MyFrame(const wxString& title)
|
||||
SetOwnBackgroundColour(m_canvas->GetBackgroundColour());
|
||||
#endif // wxUSE_INFOBAR
|
||||
|
||||
#if wxUSE_TIPWINDOW
|
||||
m_tipWindow = nullptr;
|
||||
#endif // wxUSE_TIPWINDOW
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Test MSW-specific function allowing to access the "system" menu.
|
||||
wxMenu * const menu = MSWGetSystemMenu();
|
||||
@@ -2858,14 +2854,13 @@ void MyFrame::OnShowTip(wxCommandEvent& WXUNUSED(event))
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tipWindow = new wxTipWindow
|
||||
m_tipWindow = wxTipWindow::New
|
||||
(
|
||||
this,
|
||||
"This is just some text to be shown in the tip "
|
||||
"window, broken into multiple lines, each less "
|
||||
"than 60 logical pixels wide.",
|
||||
FromDIP(60),
|
||||
&m_tipWindow
|
||||
FromDIP(60)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ private:
|
||||
void OnShowTip(wxCommandEvent& event);
|
||||
void OnUpdateShowTipUI(wxUpdateUIEvent& event);
|
||||
|
||||
wxTipWindow *m_tipWindow;
|
||||
wxTipWindow::Ref m_tipWindow;
|
||||
#endif // wxUSE_TIPWINDOW
|
||||
|
||||
bool m_confirmExit;
|
||||
|
||||
@@ -360,7 +360,7 @@ bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
|
||||
#endif // wxUSE_MS_HTML_HELP
|
||||
{
|
||||
#if wxUSE_TIPWINDOW
|
||||
static wxTipWindow* s_tipWindow = nullptr;
|
||||
static wxTipWindow::Ref s_tipWindow;
|
||||
|
||||
if ( s_tipWindow )
|
||||
{
|
||||
@@ -370,8 +370,8 @@ bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
|
||||
s_tipWindow->Close();
|
||||
}
|
||||
|
||||
s_tipWindow = new wxTipWindow((wxWindow *)window, text,
|
||||
100, &s_tipWindow);
|
||||
s_tipWindow = wxTipWindow::New((wxWindow *)window, text,
|
||||
100);
|
||||
#else // !wxUSE_TIPWINDOW
|
||||
// we tried wxCHMHelpController but it failed and we don't have
|
||||
// wxTipWindow to fall back on, so
|
||||
|
||||
+82
-3
@@ -96,13 +96,90 @@ wxEND_EVENT_TABLE()
|
||||
// wxTipWindow
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxTipWindow::wxTipWindow(wxWindow *parent,
|
||||
wxTipWindow::Ref::~Ref()
|
||||
{
|
||||
if (m_ptr)
|
||||
{
|
||||
m_ptr->SetTipWindowPtr(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
wxTipWindow::Ref::Ref(Ref&& other)
|
||||
{
|
||||
m_ptr = other.m_ptr;
|
||||
if (m_ptr)
|
||||
{
|
||||
other.m_ptr = nullptr;
|
||||
m_ptr->SetTipWindowPtr(&m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
wxTipWindow::Ref& wxTipWindow::Ref::operator=(Ref&& other)
|
||||
{
|
||||
if (m_ptr != other.m_ptr)
|
||||
{
|
||||
if (m_ptr)
|
||||
{
|
||||
m_ptr->SetTipWindowPtr(nullptr);
|
||||
}
|
||||
|
||||
m_ptr = other.m_ptr;
|
||||
if (m_ptr)
|
||||
{
|
||||
other.m_ptr = nullptr;
|
||||
m_ptr->SetTipWindowPtr(&m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
wxTipWindow::Ref& wxTipWindow::Ref::operator=(std::nullptr_t)
|
||||
{
|
||||
if (m_ptr)
|
||||
{
|
||||
m_ptr->SetTipWindowPtr(nullptr);
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
wxTipWindow::Ref wxTipWindow::New(wxWindow *parent,
|
||||
const wxString& text,
|
||||
wxCoord maxLength /*= 100*/,
|
||||
wxRect *rectBound /*= nullptr*/)
|
||||
{
|
||||
std::unique_ptr<wxTipWindow> temp(new wxTipWindow);
|
||||
wxTipWindow::Ref retval;
|
||||
retval.m_ptr = temp.get();
|
||||
|
||||
if (!temp->Create(parent, text, maxLength, &retval.m_ptr, rectBound))
|
||||
{
|
||||
return Ref();
|
||||
}
|
||||
|
||||
temp.release();
|
||||
return retval;
|
||||
}
|
||||
|
||||
wxTipWindow::wxTipWindow()
|
||||
{
|
||||
SetTipWindowPtr(nullptr);
|
||||
m_view = nullptr;
|
||||
}
|
||||
|
||||
bool wxTipWindow::Create(wxWindow *parent,
|
||||
const wxString& text,
|
||||
wxCoord maxLength,
|
||||
wxTipWindow** windowPtr,
|
||||
wxRect *rectBounds)
|
||||
: wxPopupTransientWindow(parent)
|
||||
{
|
||||
if (!wxPopupTransientWindow::Create(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetTipWindowPtr(windowPtr);
|
||||
if ( rectBounds )
|
||||
{
|
||||
@@ -158,6 +235,8 @@ wxTipWindow::wxTipWindow(wxWindow *parent,
|
||||
#ifdef __WXGTK__
|
||||
m_view->CaptureMouse();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxTipWindow::~wxTipWindow()
|
||||
@@ -167,7 +246,7 @@ wxTipWindow::~wxTipWindow()
|
||||
*m_windowPtr = nullptr;
|
||||
}
|
||||
#ifdef __WXGTK__
|
||||
if ( m_view->HasCapture() )
|
||||
if ( m_view && m_view->HasCapture() )
|
||||
m_view->ReleaseMouse();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ bool wxHtmlHelpController::DisplaySection(int sectionNo)
|
||||
bool wxHtmlHelpController::DisplayTextPopup(const wxString& text, const wxPoint& WXUNUSED(pos))
|
||||
{
|
||||
#if wxUSE_TIPWINDOW
|
||||
static wxTipWindow* s_tipWindow = nullptr;
|
||||
static wxTipWindow::Ref s_tipWindow;
|
||||
|
||||
if (s_tipWindow)
|
||||
{
|
||||
@@ -318,7 +318,7 @@ bool wxHtmlHelpController::DisplayTextPopup(const wxString& text, const wxPoint&
|
||||
|
||||
if ( !text.empty() )
|
||||
{
|
||||
s_tipWindow = new wxTipWindow(wxTheApp->GetTopWindow(), text, 100, & s_tipWindow);
|
||||
s_tipWindow = wxTipWindow::New(wxTheApp->GetTopWindow(), text, 100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user