]> git.saurik.com Git - wxWidgets.git/commitdiff
Fix several bugs in generic text entry hints implementation.
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 15 Sep 2010 22:10:14 +0000 (22:10 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 15 Sep 2010 22:10:14 +0000 (22:10 +0000)
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

include/wx/textentry.h
src/common/textentrycmn.cpp

index 99018a948831c645aeae3ad084acb0aa3cddde0a..8decb6f5c737ad0c0c38503401de2bdb5e040e1b 100644 (file)
@@ -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__
index 9dc8ca9461b1190665684c0f542ec4f0f267267d..672eeb2807009c63fee345dc743fd53444de8197 100644 (file)
@@ -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;
 }