]> git.saurik.com Git - wxWidgets.git/commitdiff
added wxTextEntry::SetHint() (a.k.a. cue banner or placeholder string)
authorVadim Zeitlin <vadim@wxwidgets.org>
Mon, 2 Mar 2009 12:25:01 +0000 (12:25 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Mon, 2 Mar 2009 12:25:01 +0000 (12:25 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59263 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

21 files changed:
docs/changes.txt
include/wx/cocoa/combobox.h
include/wx/gtk/combobox.h
include/wx/gtk/textctrl.h
include/wx/gtk/textentry.h
include/wx/gtk1/combobox.h
include/wx/motif/combobox.h
include/wx/msw/combobox.h
include/wx/msw/textentry.h
include/wx/os2/combobox.h
include/wx/osx/combobox.h
include/wx/richtext/richtextctrl.h
include/wx/srchctrl.h
include/wx/textctrl.h
include/wx/textentry.h
include/wx/univ/combobox.h
interface/wx/textentry.h
samples/widgets/widgets.cpp
src/common/textentrycmn.cpp
src/msw/combobox.cpp
src/msw/textentry.cpp

index cf53c69a10bd078c5d153688ddd4134a667c42fa..f2572d699dfed63158bf1ff44abcb964709f067d 100644 (file)
@@ -406,6 +406,7 @@ All (GUI):
 - Added support for labels for toolbar controls (Vince Harron).
 - Added wxMessageDialog::SetMessage() and SetExtendedMessage().
 - Added wxListCtrl::Set/GetColumnsOrder() (Yury Voronov).
+- Added wxTextEntry::SetHint().
 - Made wxLogWindow thread-safe (Barbara Maren Winkler).
 - Added wxWindow::AlwaysShowScrollbars() (Julian Scheid).
 - Added wxMouseEvent::GetClickCount() (Julian Scheid).
index 2b6e0b48826ac9d115c9669fb08caafa2ed32425..d0f1ad8eda76fd1a34838488f87446194df4a8fa 100644 (file)
@@ -149,6 +149,10 @@ public:
     virtual void GetSelection(long *from, long *to) const;
     virtual bool IsEditable() const;
     virtual void SetEditable(bool editable);
+
+private:
+    // implement wxTextEntry pure virtual method
+    virtual wxWindow *GetEditableWindow() { return this; }
 };
 
 #endif // __WX_COCOA_COMBOBOX_H__
index 438dc7c3adb14767fcd8c8f4c8d3fde9c04ee0eb..d715d1dae889bf79cd6fcdcd0825173c701e4f6f 100644 (file)
@@ -142,7 +142,7 @@ protected:
 
 private:
     // From wxTextEntry:
-    virtual const wxWindow *GetEditableWindow() const { return this; }
+    virtual wxWindow *GetEditableWindow() { return this; }
     virtual GtkEditable *GetEditable() const;
     virtual void EnableTextChangedEvents(bool enable)
     {
index a06a8a63455ab536b9b139a2693b91d239a5dea3..a4371c7c98f5dca3e056eb0282b4c9e99852b581 100644 (file)
@@ -177,7 +177,6 @@ protected:
 
 private:
     // overridden wxTextEntry virtual methods
-    virtual const wxWindow *GetEditableWindow() const { return this; }
     virtual GtkEditable *GetEditable() const;
     virtual void EnableTextChangedEvents(bool enable);
 
index eb329722cd28ea830fa853abf296fe41a5b969c9..91db71ae1117938ee63acbc4a160e04720bc958f 100644 (file)
@@ -54,10 +54,6 @@ public:
     void SendMaxLenEvent();
 
 private:
-    // implement this to return the associated window, it will be used for
-    // event generation
-    virtual const wxWindow *GetEditableWindow() const = 0;
-
     // implement this to return the associated GtkEntry or another widget
     // implementing GtkEditable
     virtual GtkEditable *GetEditable() const = 0;
index d064f57a334005c34c1907c6cdf40fc11c44893b..079d0f56d0f1e8909e23fb88ef369e1689beff8e 100644 (file)
@@ -169,6 +169,9 @@ protected:
 
     virtual wxSize DoGetBestSize() const;
 
+    // implement wxTextEntry pure virtual method
+    virtual wxWindow *GetEditableWindow() { return this; }
+
     // Widgets that use the style->base colour for the BG colour should
     // override this and return true.
     virtual bool UseGTKStyleBase() const { return true; }
index 63eb9018e49eef56e299eefce39520908b9369f9..e7e3598ed691469ae16e38f578d8a65b73d3cb76 100644 (file)
@@ -107,6 +107,8 @@ protected:
                            int width, int height,
                            int sizeFlags = wxSIZE_AUTO);
 
+    // implement wxTextEntry pure virtual methods
+    virtual wxWindow *GetEditableWindow() { return this; }
     virtual WXWidget GetTextWidget() const;
 
 private:
index e96d4d95c47a0a1e628cffc2297b3be610223c0f..8339b4cb0c65b635f51d0414760eb607bc20e826 100644 (file)
@@ -116,6 +116,11 @@ public:
 
     virtual WXDWORD MSWGetStyle(long style, WXDWORD *exstyle) const;
 
+#if wxUSE_UXTHEME
+    // override wxTextEntry method to work around Windows bug
+    virtual bool SetHint(const wxString& hint);
+#endif // wxUSE_UXTHEME
+
 protected:
 #if wxUSE_TOOLTIPS
     virtual void DoSetToolTip(wxToolTip *tip);
@@ -136,8 +141,9 @@ protected:
     }
 
 private:
-    // this is the overridden wxTextEntry method which should only be called
-    // when we do have an edit control so it asserts if this is not the case
+    // there are the overridden wxTextEntry methods which should only be called
+    // when we do have an edit control so they assert if this is not the case
+    virtual wxWindow *GetEditableWindow();
     virtual WXHWND GetEditHWND() const;
 
     // common part of all ctors
index e3a523b825786965524d53afe007c345a10a9eb1..25ee6c62cd0f7b9643ed8a91de1e9316241be0ac 100644 (file)
@@ -54,6 +54,11 @@ public:
 
     virtual void SetMaxLength(unsigned long len);
 
+#if wxUSE_UXTHEME
+    virtual bool SetHint(const wxString& hint);
+    virtual wxString GetHint() const;
+#endif // wxUSE_UXTHEME
+
 protected:
     // this is really a hook for multiline text controls as the single line
     // ones don't need to ever scroll to show the selection but having it here
index f95d442a2f0783ad08021153265a120889064aa6..b5e7d293cb1460b46040c05e698e7172396c56c6 100644 (file)
@@ -122,8 +122,8 @@ class WXDLLIMPEXP_CORE wxComboBox : public wxChoice,
                                       );
 
 private:
-    // implement wxTextEntry pure virtual: it implements all the operations for
-    // the simple EDIT controls
+    // implement wxTextEntry pure virtual methods
+    virtual wxWindow *GetEditableWindow() { return this; }
     virtual WXHWND GetEditHWND() const { return m_hWnd; }
 
     DECLARE_DYNAMIC_CLASS(wxComboBox)
index 69c96872f9de6ed49ac7f916d56e29318c960462..e115a4c8474063f19ed18c4fc83e288d6cc5eec0 100644 (file)
@@ -154,6 +154,9 @@ protected:
 
     virtual void SetClientDataType(wxClientDataType clientDataItemsType);
 
+    // implement wxTextEntry pure virtual method
+    virtual wxWindow *GetEditableWindow() { return this; }
+
     // the subcontrols
     wxComboBoxText*     m_text;
     wxComboBoxChoice*   m_choice;
index 5d60b8e042c884444d82b4cc43903dda3a99c5a2..b308b57d87a52da7b7776848498cdd656d0fae05 100644 (file)
@@ -771,16 +771,13 @@ public:
      static const wxArrayString& GetAvailableFontNames();
      static void ClearAvailableFontNames();
 
+     // FIXME: this does not work, it allows this code to compile but will fail
+     //        during run-time
 #ifdef __WXMSW__
     virtual WXHWND GetEditHWND() const { return GetHWND(); }
 #endif
 #ifdef __WXGTK__
-    // implement this to return the associated window, it will be used for
-    // event generation
-    virtual const wxWindow *GetEditableWindow() const { return NULL; }
-
-    // implement this to return the associated GtkEntry or another widget
-    // implementing GtkEditable
+    virtual const wxWindow *GetEditableWindow() { return this; }
     virtual GtkEditable *GetEditable() const { return NULL; }
 #endif
 
index ccd27a29be1e6c6c793da7fd7eb1edb6ff6f2803..948aa2c0cefa464d7449a4673607831c064321da 100644 (file)
@@ -64,6 +64,10 @@ public:
 
     virtual void ShowCancelButton( bool show ) = 0;
     virtual bool IsCancelButtonVisible() const = 0;
+
+private:
+    // implement wxTextEntry pure virtual method
+    virtual wxWindow *GetEditableWindow() { return this; }
 };
 
 
index c1fb7706322d797d03a951559f1214a3cc7f7c10..318af3e53f96206704c78d65a9930a08f992a9f3 100644 (file)
@@ -692,6 +692,9 @@ protected:
     virtual bool DoLoadFile(const wxString& file, int fileType);
     virtual bool DoSaveFile(const wxString& file, int fileType);
 
+private:
+    // implement the wxTextEntry pure virtual method
+    virtual wxWindow *GetEditableWindow() { return this; }
 
     wxDECLARE_NO_COPY_CLASS(wxTextCtrlBase);
     DECLARE_ABSTRACT_CLASS(wxTextCtrlBase)
index 28bffe991379838ccd2489f6acf0f55e1926d142..1194b61f90f0cf0ef00baca474e37f814645c943 100644 (file)
@@ -16,6 +16,7 @@
 typedef long wxTextPos;
 
 class WXDLLIMPEXP_FWD_BASE wxArrayString;
+class WXDLLIMPEXP_FWD_CORE wxTextEntryHintData;
 
 // ----------------------------------------------------------------------------
 // wxTextEntryBase
@@ -24,8 +25,8 @@ class WXDLLIMPEXP_FWD_BASE wxArrayString;
 class WXDLLIMPEXP_CORE wxTextEntryBase
 {
 public:
-    wxTextEntryBase() { m_eventsBlock = 0; }
-    virtual ~wxTextEntryBase() { }
+    wxTextEntryBase() { m_eventsBlock = 0; m_hintData = NULL; }
+    virtual ~wxTextEntryBase();
 
 
     // accessing the value
@@ -129,6 +130,17 @@ public:
     virtual void SetMaxLength(unsigned long WXUNUSED(len)) { }
 
 
+    // hints
+    // -----
+
+    // hint is the (usually greyed out) text shown in the control as long as
+    // it's empty and doesn't have focus, it is typically used in controls used
+    // for searching to let the user know what is supposed to be entered there
+
+    virtual bool SetHint(const wxString& hint);
+    virtual wxString GetHint() const;
+
+
 protected:
     // flags for DoSetValue(): common part of SetValue() and ChangeValue() and
     // also used to implement WriteText() in wxMSW
@@ -172,6 +184,11 @@ protected:
     bool EventsAllowed() const { return m_eventsBlock == 0; }
 
 private:
+    // override this to return the associated window, it will be used for event
+    // generation and also by generic hints implementation
+    virtual wxWindow *GetEditableWindow() = 0;
+
+
     // suppress or resume the text changed events generation: don't use these
     // functions directly, use EventsSuppressor class above instead
     void SuppressTextChangedEvents()
@@ -196,6 +213,9 @@ private:
 
     // if this counter is non-null, events are blocked
     unsigned m_eventsBlock;
+
+    // hint-related stuff, only allocated if/when SetHint() is used
+    wxTextEntryHintData *m_hintData;
 };
 
 #ifdef __WXUNIVERSAL__
index 2b26eb5180bf2427f7cf9f23dc1a45fc0d371c14..30a46691f686a4507be97b56cdacb1f648657aa6 100644 (file)
@@ -166,6 +166,9 @@ protected:
     wxListBox *GetLBox() const { return m_lbox; }
 
 private:
+    // implement wxTextEntry pure virtual method
+    virtual wxWindow *GetEditableWindow() { return this; }
+
     // the popup listbox
     wxListBox *m_lbox;
 
index df1ddc9100869dd9b61066b8879a94b21a117d2d..bec4b6a9285dac45a7094eefb8b8eaf3ea25f3c7 100644 (file)
@@ -351,6 +351,36 @@ public:
     */
     virtual void SelectAll();
 
+    /**
+        Sets a hint shown in an empty unfocused text control.
+
+        The hints are usually used to indicate to the user what is supposed to
+        be entered into the given entry field, e.g. a common use of them is to
+        show an explanation of what can be entered in a wxSearchCtrl.
+
+        The hint is shown (usually greyed out) for an empty control until it
+        gets focus and is shown again if the control loses it and remains
+        empty. It won't be shown once the control has a non-empty value,
+        although it will be shown again if the control contents is cleared.
+        Because of this, it generally only makes sense to use hints with the
+        controls which are initially empty.
+
+        Notice that hints are known as <em>cue banners</em> under MSW or
+        <em>placeholder strings</em> under OS X.
+
+        @since 2.9.0
+     */
+    virtual void SetHint(const wxString& hint);
+
+    /**
+        Returns the current hint string.
+
+        See SetHint() for more information about hints.
+
+        @since 2.9.0
+     */
+    virtual wxString GetHint() const;
+
     /**
         Sets the new text control value.
 
index 19d86f02e3d752860886b995aeb3f85146404869..b3da0a0d2c130ddd097ca18918f1de84145262b6 100644 (file)
@@ -97,6 +97,8 @@ enum
     TextEntry_DisableAutoComplete = TextEntry_Begin,
     TextEntry_AutoCompleteFixed,
     TextEntry_AutoCompleteFilenames,
+
+    TextEntry_SetHint,
     TextEntry_End
 };
 
@@ -164,10 +166,13 @@ protected:
     void OnToggleGlobalBusyCursor(wxCommandEvent& event);
     void OnToggleBusyCursor(wxCommandEvent& event);
 
+    // wxTextEntry-specific tests
     void OnDisableAutoComplete(wxCommandEvent& event);
     void OnAutoCompleteFixed(wxCommandEvent& event);
     void OnAutoCompleteFilenames(wxCommandEvent& event);
 
+    void OnSetHint(wxCommandEvent& event);
+
     void OnUpdateTextUI(wxUpdateUIEvent& event)
     {
         event.Enable( CurrentPage()->GetTextEntry() != NULL );
@@ -308,6 +313,8 @@ BEGIN_EVENT_TABLE(WidgetsFrame, wxFrame)
     EVT_MENU(TextEntry_AutoCompleteFixed,     WidgetsFrame::OnAutoCompleteFixed)
     EVT_MENU(TextEntry_AutoCompleteFilenames, WidgetsFrame::OnAutoCompleteFilenames)
 
+    EVT_MENU(TextEntry_SetHint, WidgetsFrame::OnSetHint)
+
     EVT_UPDATE_UI_RANGE(TextEntry_Begin, TextEntry_End - 1,
                         WidgetsFrame::OnUpdateTextUI)
 
@@ -419,6 +426,8 @@ WidgetsFrame::WidgetsFrame(const wxString& title)
                                    _T("Fixed-&list auto-completion"));
     menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteFilenames,
                                    _T("&Files names auto-completion"));
+    menuTextEntry->AppendSeparator();
+    menuTextEntry->Append(TextEntry_SetHint, "Set help &hint");
 
     mbar->Append(menuTextEntry, _T("&Text"));
 
@@ -948,6 +957,25 @@ void WidgetsFrame::OnAutoCompleteFilenames(wxCommandEvent& WXUNUSED(event))
         wxLogMessage("AutoCompleteFileNames() failed.");
 }
 
+void WidgetsFrame::OnSetHint(wxCommandEvent& WXUNUSED(event))
+{
+    wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
+    wxCHECK_RET( entry, "menu item should be disabled" );
+
+    static wxString s_hint("Type here");
+    wxString
+        hint = wxGetTextFromUser("Text hint:", "Widgets sample", s_hint, this);
+    if ( hint.empty() )
+        return;
+
+    s_hint = hint;
+
+    if ( entry->SetHint(hint) )
+        wxLogMessage("Set hint to \"%s\".", hint);
+    else
+        wxLogMessage("Text hints not supported.");
+}
+
 #endif // wxUSE_MENUS
 
 // ----------------------------------------------------------------------------
index b179499381aabc301d04f1350149301da92d740e..a0e31da051ed15c4719ce20d9dd5eb228d5a503d 100644 (file)
 #include "wx/textentry.h"
 #include "wx/clipbrd.h"
 
+// ----------------------------------------------------------------------------
+// wxTextEntryHintData
+// ----------------------------------------------------------------------------
+
+class WXDLLIMPEXP_CORE wxTextEntryHintData wxBIND_OR_CONNECT_HACK_ONLY_BASE_CLASS
+{
+public:
+    wxTextEntryHintData(wxTextEntryBase *entry, wxWindow *win)
+        : m_entry(entry),
+          m_win(win)
+    {
+        wxBIND_OR_CONNECT_HACK(win, wxEVT_SET_FOCUS, wxFocusEventHandler,
+                                wxTextEntryHintData::OnSetFocus, this);
+        wxBIND_OR_CONNECT_HACK(win, wxEVT_KILL_FOCUS, wxFocusEventHandler,
+                                wxTextEntryHintData::OnKillFocus, this);
+    }
+
+    // default dtor is ok
+
+    void SetHintString(const wxString& hint)
+    {
+        m_hint = hint;
+
+        if ( ShowsHint() )
+        {
+            // update it immediately
+            m_entry->ChangeValue(hint);
+        }
+        //else: the new hint will be shown later
+    }
+
+    const wxString& GetHintString() const { return m_hint; }
+
+private:
+    // are we showing the hint right now?
+    bool ShowsHint() const
+    {
+        return m_entry->GetValue() == m_hint;
+    }
+
+    void OnSetFocus(wxFocusEvent& event)
+    {
+        // hide the hint if we were showing it
+        if ( ShowsHint() )
+        {
+            // Clear() would send an event which we don't want, so do it like
+            // this
+            m_entry->ChangeValue(wxString());
+            m_win->SetForegroundColour(m_colFg);
+        }
+
+        event.Skip();
+    }
+
+    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);
+
+            m_colFg = m_win->GetForegroundColour();
+            m_win->SetForegroundColour(*wxLIGHT_GREY);
+        }
+
+        event.Skip();
+    }
+
+
+    wxTextEntryBase * const m_entry;
+    wxWindow * const m_win;
+
+    wxColour m_colFg;
+
+    wxString m_hint;
+
+    wxDECLARE_NO_COPY_CLASS(wxTextEntryHintData);
+};
+
 // ============================================================================
 // wxTextEntryBase implementation
 // ============================================================================
 
+wxTextEntryBase::~wxTextEntryBase()
+{
+    delete m_hintData;
+}
+
+// ----------------------------------------------------------------------------
+// text operations
+// ----------------------------------------------------------------------------
+
 wxString wxTextEntryBase::GetRange(long from, long to) const
 {
     wxString sel;
@@ -77,6 +165,10 @@ void wxTextEntryBase::Replace(long from, long to, const wxString& value)
     WriteText(value);
 }
 
+// ----------------------------------------------------------------------------
+// selection
+// ----------------------------------------------------------------------------
+
 bool wxTextEntryBase::HasSelection() const
 {
     long from, to;
@@ -101,6 +193,10 @@ wxString wxTextEntryBase::GetStringSelection() const
     return GetRange(from, to);
 }
 
+// ----------------------------------------------------------------------------
+// clipboard
+// ----------------------------------------------------------------------------
+
 bool wxTextEntryBase::CanCopy() const
 {
     return HasSelection();
@@ -131,4 +227,23 @@ bool wxTextEntryBase::CanPaste() const
     return false;
 }
 
+// ----------------------------------------------------------------------------
+// hints support
+// ----------------------------------------------------------------------------
+
+bool wxTextEntryBase::SetHint(const wxString& hint)
+{
+    if ( !m_hintData )
+        m_hintData = new wxTextEntryHintData(this, GetEditableWindow());
+
+    m_hintData->SetHintString(hint);
+
+    return true;
+}
+
+wxString wxTextEntryBase::GetHint() const
+{
+    return m_hintData ? m_hintData->GetHintString() : wxString();
+}
+
 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
index ca40d744fc56cea493733b7e4658ce687a1ae27c..7ae8dfc956e54695c33dd03ce5c3cb43edc1dd01 100644 (file)
 #include "wx/wupdlock.h"
 #include "wx/msw/private.h"
 
+#if wxUSE_UXTHEME
+    #include "wx/msw/uxtheme.h"
+#endif
+
 #if wxUSE_TOOLTIPS
     #include "wx/tooltip.h"
 #endif // wxUSE_TOOLTIPS
@@ -445,6 +449,14 @@ WXHWND wxComboBox::GetEditHWND() const
     return hWndEdit;
 }
 
+wxWindow *wxComboBox::GetEditableWindow()
+{
+    wxASSERT_MSG( !HasFlag(wxCB_READONLY),
+                  _T("read-only combobox doesn't have any edit control") );
+
+    return this;
+}
+
 // ----------------------------------------------------------------------------
 // wxComboBox creation
 // ----------------------------------------------------------------------------
@@ -665,4 +677,22 @@ void wxComboBox::DoSetToolTip(wxToolTip *tip)
 
 #endif // wxUSE_TOOLTIPS
 
+#if wxUSE_UXTHEME
+
+bool wxComboBox::SetHint(const wxString& hintOrig)
+{
+    wxString hint(hintOrig);
+    if ( wxUxThemeEngine::GetIfActive() )
+    {
+        // under XP (but not Vista) there is a bug in cue banners
+        // implementation for combobox edit control: the first character is
+        // partially chopped off, so prepend a space to make it fully visible
+        hint.insert(0, " ");
+    }
+
+    return wxTextEntry::SetHint(hint);
+}
+
+#endif // wxUSE_UXTHEME
+
 #endif // wxUSE_COMBOBOX
index 7a30e5daf8d80c8d090937e899ac1470ae0bd6ff..968f913fd17d583f1814d0fc8217cae4d3b6ffd0 100644 (file)
 
 #include "wx/msw/private.h"
 
+#if wxUSE_UXTHEME
+    #include "wx/msw/uxtheme.h"
+#endif
+
 #define GetEditHwnd() ((HWND)(GetEditHWND()))
 
 // ----------------------------------------------------------------------------
@@ -416,4 +420,44 @@ void wxTextEntry::SetMaxLength(unsigned long len)
     ::SendMessage(GetEditHwnd(), EM_LIMITTEXT, len, 0);
 }
 
+// ----------------------------------------------------------------------------
+// hints
+// ----------------------------------------------------------------------------
+
+#if wxUSE_UXTHEME
+
+#ifndef EM_SETCUEBANNER
+    #define EM_SETCUEBANNER 0x1501
+    #define EM_GETCUEBANNER 0x1502
+#endif
+
+bool wxTextEntry::SetHint(const wxString& hint)
+{
+    if ( wxUxThemeEngine::GetIfActive() )
+    {
+        // notice that this message always works with Unicode strings
+        if ( ::SendMessage(GetEditHwnd(), EM_SETCUEBANNER,
+                             0, (LPARAM)(const wchar_t *)hint.wc_str()) )
+            return true;
+    }
+
+    return wxTextEntryBase::SetHint(hint);
+}
+
+wxString wxTextEntry::GetHint() const
+{
+    if ( wxUxThemeEngine::GetIfActive() )
+    {
+        wchar_t buf[256];
+        if ( ::SendMessage(GetEditHwnd(), EM_GETCUEBANNER,
+                            (WPARAM)buf, WXSIZEOF(buf)) )
+            return buf;
+    }
+
+    return wxTextEntryBase::GetHint();
+}
+
+
+#endif // wxUSE_UXTHEME
+
 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX