From: Vadim Zeitlin Date: Wed, 15 Sep 2010 22:10:14 +0000 (+0000) Subject: Fix several bugs in generic text entry hints implementation. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/a7aeddacf9dfcb9dca64fccb5f65f387bcff0fb2 Fix several bugs in generic text entry hints implementation. The text controls contents and colour was not updated correctly in several situations (see #12475). The old code was completely wrong as it didn't store the actual value of the control at all and so could never work. Do store and update it now and show the hint if and only if the real contents is empty. Also handle "text updated" event to correctly update the hint when the control becomes [non-]empty. Closes #12475. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65551 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/textentry.h b/include/wx/textentry.h index 99018a9488..8decb6f5c7 100644 --- a/include/wx/textentry.h +++ b/include/wx/textentry.h @@ -279,6 +279,10 @@ private: // hint-related stuff, only allocated if/when SetHint() is used wxTextEntryHintData *m_hintData; + + // It needs to call our Do{Get,Set}Value() to work with the real control + // contents. + friend class wxTextEntryHintData; }; #ifdef __WXUNIVERSAL__ diff --git a/src/common/textentrycmn.cpp b/src/common/textentrycmn.cpp index 9dc8ca9461..672eeb2807 100644 --- a/src/common/textentrycmn.cpp +++ b/src/common/textentrycmn.cpp @@ -42,48 +42,76 @@ class WXDLLIMPEXP_CORE wxTextEntryHintData wxBIND_OR_CONNECT_HACK_ONLY_BASE_CLAS public: wxTextEntryHintData(wxTextEntryBase *entry, wxWindow *win) : m_entry(entry), - m_win(win) + m_win(win), + m_text(m_entry->GetValue()) { wxBIND_OR_CONNECT_HACK(win, wxEVT_SET_FOCUS, wxFocusEventHandler, wxTextEntryHintData::OnSetFocus, this); wxBIND_OR_CONNECT_HACK(win, wxEVT_KILL_FOCUS, wxFocusEventHandler, wxTextEntryHintData::OnKillFocus, this); - - // we don't have any hint yet - m_showsHint = false; + wxBIND_OR_CONNECT_HACK(win, wxEVT_COMMAND_TEXT_UPDATED, + wxCommandEventHandler, + wxTextEntryHintData::OnTextChanged, this); } // default dtor is ok - // are we showing the hint right now? - bool ShowsHint() const { return m_showsHint; } + // Get the real text of the control such as it was before we replaced it + // with the hint. + const wxString& GetText() const { return m_text; } + // Set the hint to show, shouldn't be empty normally. + // + // This should be called after creating a new wxTextEntryHintData object + // and may be called more times in the future. void SetHintString(const wxString& hint) { m_hint = hint; - if ( m_showsHint ) - { - // update it immediately - m_entry->ChangeValue(hint); - } - //else: the new hint will be shown later + if ( !m_win->HasFocus() ) + ShowHintIfAppropriate(); + //else: The new hint will be shown later when we lose focus. } const wxString& GetHintString() const { return m_hint; } private: - void OnSetFocus(wxFocusEvent& event) + // Show the hint in the window if we should do it, i.e. if the window + // doesn't have any text of its own. + void ShowHintIfAppropriate() { - // hide the hint if we were showing it - if ( m_showsHint ) + // Never overwrite existing window text. + if ( !m_text.empty() ) + return; + + // Save the old text colour and set a more inconspicuous one for the + // hint. + m_colFg = m_win->GetForegroundColour(); + m_win->SetForegroundColour(*wxLIGHT_GREY); + + m_entry->DoSetValue(m_hint, wxTextEntryBase::SetValue_NoEvent); + } + + // Restore the original text colour if we had changed it to show the hint + // and not restored it yet. + void RestoreTextColourIfNecessary() + { + if ( m_colFg.IsOk() ) { - // Clear() would send an event which we don't want, so do it like - // this - m_entry->ChangeValue(wxString()); m_win->SetForegroundColour(m_colFg); + m_colFg = wxColour(); + } + } + + void OnSetFocus(wxFocusEvent& event) + { + // If we had been showing the hint before, remove it now and restore + // the normal colour. + if ( m_text.empty() ) + { + RestoreTextColourIfNecessary(); - m_showsHint = false; + m_entry->DoSetValue(wxString(), wxTextEntryBase::SetValue_NoEvent); } event.Skip(); @@ -91,20 +119,30 @@ private: void OnKillFocus(wxFocusEvent& event) { - // restore the hint if the user didn't do anything in the control - if ( m_entry->IsEmpty() ) - { - m_entry->ChangeValue(m_hint); + // Restore the hint if the user didn't enter anything. + ShowHintIfAppropriate(); - m_colFg = m_win->GetForegroundColour(); - m_win->SetForegroundColour(*wxLIGHT_GREY); + event.Skip(); + } - m_showsHint = true; - } + void OnTextChanged(wxCommandEvent& event) + { + // Update the stored window text. + // + // Notice that we can't use GetValue() nor wxCommandEvent::GetString() + // which uses it internally because this would just forward back to us + // so go directly to the private method which returns the real control + // contents. + m_text = m_entry->DoGetValue(); + + // If this event is generated because of calling SetValue(), the + // control may still have the hint text colour, reset it in this case. + RestoreTextColourIfNecessary(); event.Skip(); } + // the text control we're associated with (as its interface and its window) wxTextEntryBase * const m_entry; wxWindow * const m_win; @@ -112,12 +150,12 @@ private: // the original foreground colour of m_win before we changed it wxColour m_colFg; - // the hint passed to wxTextEntry::SetHint() + // The hint passed to wxTextEntry::SetHint(), never empty. wxString m_hint; - // true if we're currently showing it, for this we must be empty and not - // have focus - bool m_showsHint; + // The real text of the window. + wxString m_text; + wxDECLARE_NO_COPY_CLASS(wxTextEntryHintData); }; @@ -137,7 +175,7 @@ wxTextEntryBase::~wxTextEntryBase() wxString wxTextEntryBase::GetValue() const { - return m_hintData && m_hintData->ShowsHint() ? wxString() : DoGetValue(); + return m_hintData ? m_hintData->GetText() : DoGetValue(); } wxString wxTextEntryBase::GetRange(long from, long to) const @@ -252,10 +290,20 @@ bool wxTextEntryBase::CanPaste() const bool wxTextEntryBase::SetHint(const wxString& hint) { - if ( !m_hintData ) - m_hintData = new wxTextEntryHintData(this, GetEditableWindow()); + if ( !hint.empty() ) + { + if ( !m_hintData ) + m_hintData = new wxTextEntryHintData(this, GetEditableWindow()); - m_hintData->SetHintString(hint); + m_hintData->SetHintString(hint); + } + else if ( m_hintData ) + { + // Setting empty hint removes any currently set one. + delete m_hintData; + m_hintData = NULL; + } + //else: Setting empty hint when we don't have any doesn't do anything. return true; }