Add wxTextEntry::ForceUpper()

Allow automatically converting lower-case letters entered into wxTextCtrl to
upper-case equivalents. Provide generic fallback and implement the method
natively for all the major platforms.

Also update the text sample to show it in action.
This commit is contained in:
Vadim Zeitlin 2015-11-25 03:40:40 +01:00
parent 99d9a81e28
commit 69b66e9e2e
14 changed files with 238 additions and 18 deletions

View File

@ -90,6 +90,7 @@ All (GUI):
- Allow customizing window shown by wxBusyInfo.
- Add wxAddRemoveCtrl.
- Add wxAppProgressIndicator for MSW (Chaobin Zhang) and OS X (Tobias Taschner).
- Add wxTextEntry::ForceUpper().
- Add wxEVT_MAGNIFY mouse event (Joost Nieuwenhuijse).
- Add wxProcess::Activate().
- Fix setting colours of labels in wxSlider.

View File

@ -21,7 +21,7 @@ typedef struct _GtkEntry GtkEntry;
class WXDLLIMPEXP_CORE wxTextEntry : public wxTextEntryBase
{
public:
wxTextEntry() { }
wxTextEntry() { m_isUpperCase = false; }
// implement wxTextEntryBase pure virtual methods
virtual void WriteText(const wxString& text) wxOVERRIDE;
@ -47,6 +47,7 @@ public:
virtual void SetEditable(bool editable) wxOVERRIDE;
virtual void SetMaxLength(unsigned long len) wxOVERRIDE;
virtual void ForceUpper() wxOVERRIDE;
#ifdef __WXGTK3__
virtual bool SetHint(const wxString& hint) wxOVERRIDE;
@ -56,6 +57,7 @@ public:
// implementation only from now on
void SendMaxLenEvent();
bool GTKEntryOnInsertText(const char* text);
bool GTKIsUpperCase() const { return m_isUpperCase; }
protected:
// This method must be called from the derived class Create() to connect
@ -85,7 +87,12 @@ private:
// implement this to return the associated GtkEntry
virtual GtkEntry *GetEntry() const = 0;
bool m_isUpperCase;
};
// We don't need the generic version.
#define wxHAS_NATIVE_TEXT_FORCEUPPER
#endif // _WX_GTK_TEXTENTRY_H_

View File

@ -47,6 +47,7 @@ public:
virtual void SetEditable(bool editable);
virtual void SetMaxLength(unsigned long len);
virtual void ForceUpper();
#if wxUSE_UXTHEME
virtual bool SetHint(const wxString& hint);
@ -98,5 +99,8 @@ private:
#endif // wxUSE_OLE
};
// We don't need the generic version.
#define wxHAS_NATIVE_TEXT_FORCEUPPER
#endif // _WX_MSW_TEXTENTRY_H_

View File

@ -14,6 +14,8 @@
#include "wx/combobox.h"
#include "wx/osx/private.h"
@class wxTextEntryFormatter;
// implementation exposed, so that search control can pull it
class wxNSTextFieldControl : public wxWidgetCocoaImpl, public wxTextWidgetImpl
@ -29,7 +31,10 @@ public :
virtual bool CanClipMaxLength() const { return true; }
virtual void SetMaxLength(unsigned long len);
virtual bool CanForceUpper() { return true; }
virtual void ForceUpper();
virtual wxString GetStringValue() const ;
virtual void SetStringValue( const wxString &str) ;
virtual void Copy() ;
@ -57,6 +62,9 @@ protected :
private:
// Common part of both ctors.
void Init(WXWidget w);
// Get our formatter, creating it if necessary.
wxTextEntryFormatter* GetFormatter();
};
class wxNSTextViewControl : public wxWidgetCocoaImpl, public wxTextWidgetImpl

View File

@ -655,7 +655,10 @@ public :
virtual bool CanClipMaxLength() const { return false; }
virtual void SetMaxLength(unsigned long WXUNUSED(len)) {}
virtual bool CanForceUpper() { return false; }
virtual void ForceUpper() {}
virtual bool GetStyle( long position, wxTextAttr& style);
virtual void SetStyle( long start, long end, const wxTextAttr& style ) ;
virtual void Copy() ;

View File

@ -50,6 +50,8 @@ public:
// in a single line text control
virtual void SetMaxLength(unsigned long len);
virtual void ForceUpper();
// writing text inserts it at the current position;
// appending always inserts it at the end
virtual void WriteText(const wxString& text);

View File

@ -138,10 +138,16 @@ public:
virtual void SetEditable(bool editable) = 0;
// input restrictions
// ------------------
// set the max number of characters which may be entered in a single line
// text control
virtual void SetMaxLength(unsigned long WXUNUSED(len)) { }
// convert any lower-case characters to upper-case on the fly in this entry
virtual void ForceUpper();
// hints
// -----
@ -208,6 +214,10 @@ public:
SuppressTextChangedEvents();
}
// change the entry value to be in upper case only, if needed (i.e. if it's
// not already the case)
void ConvertToUpperCase();
protected:
// flags for DoSetValue(): common part of SetValue() and ChangeValue() and
// also used to implement WriteText() in wxMSW

View File

@ -212,6 +212,17 @@ public:
*/
virtual void Cut();
/**
Convert all text entered into the control to upper case.
Call this method to ensure that all text entered into the control is
converted on the fly to upper case. If the control is not empty, its
existing contents is also converted to upper case.
@since 3.1.0
*/
void ForceUpper();
/**
Returns the insertion point, or cursor, position.

View File

@ -1093,6 +1093,10 @@ MyPanel::MyPanel( wxFrame *frame, int x, int y, int w, int h )
wxSize size2 = m_limited->GetSizeFromTextSize(m_limited->GetTextExtent("WWWWWWWW"));
m_limited->SetSizeHints(size2, size2);
wxTextCtrl* upperOnly = new MyTextCtrl(this, wxID_ANY, "Only upper case",
wxDefaultPosition, wxDefaultSize);
upperOnly->ForceUpper();
// multi line text controls
wxString string3L("Read only\nMultiline\nFitted size");
@ -1194,6 +1198,7 @@ MyPanel::MyPanel( wxFrame *frame, int x, int y, int w, int h )
column1->Add( m_password, 0, wxALL | wxEXPAND, 10 );
column1->Add( m_readonly, 0, wxALL, 10 );
column1->Add( m_limited, 0, wxALL, 10 );
column1->Add( upperOnly, 0, wxALL, 10 );
column1->Add( m_horizontal, 1, wxALL | wxEXPAND, 10 );
wxBoxSizer *column2 = new wxBoxSizer(wxVERTICAL);

View File

@ -310,6 +310,66 @@ bool wxTextEntryBase::CanPaste() const
return false;
}
// ----------------------------------------------------------------------------
// input restrictions
// ----------------------------------------------------------------------------
#ifndef wxHAS_NATIVE_TEXT_FORCEUPPER
namespace
{
// Poor man's lambda: helper for binding ConvertToUpperCase() to the event
struct ForceUpperFunctor
{
explicit ForceUpperFunctor(wxTextEntryBase* entry)
: m_entry(entry)
{
}
void operator()(wxCommandEvent& event)
{
event.Skip();
m_entry->ConvertToUpperCase();
}
wxTextEntryBase* const m_entry;
};
} // anonymous namespace
#endif // !wxHAS_NATIVE_TEXT_FORCEUPPER
void wxTextEntryBase::ConvertToUpperCase()
{
const wxString& valueOld = GetValue();
const wxString& valueNew = valueOld.Upper();
if ( valueNew != valueOld )
{
long from, to;
GetSelection(&from, &to);
ChangeValue(valueNew);
SetSelection(from, to);
}
}
void wxTextEntryBase::ForceUpper()
{
// Do nothing if this method is never called because a native override is
// provided: this is just a tiny size-saving optimization, nothing else.
#ifndef wxHAS_NATIVE_TEXT_FORCEUPPER
wxWindow* const win = GetEditableWindow();
wxCHECK_RET( win, wxS("can't be called before creating the window") );
// Convert the current control contents to upper case
ConvertToUpperCase();
// And ensure that any text entered in the future is converted too
win->Bind(wxEVT_TEXT, ForceUpperFunctor(this));
#endif // !wxHAS_NATIVE_TEXT_FORCEUPPER
}
// ----------------------------------------------------------------------------
// hints support
// ----------------------------------------------------------------------------

View File

@ -33,6 +33,7 @@
#include <gtk/gtk.h>
#include "wx/gtk/private.h"
#include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/string.h"
// ============================================================================
// signal handlers implementation
@ -43,8 +44,8 @@ extern "C"
void
wx_gtk_insert_text_callback(GtkEditable *editable,
const gchar * new_text,
gint WXUNUSED(new_text_length),
gint * WXUNUSED(position),
gint new_text_length,
gint * position,
wxTextEntry *text)
{
GtkEntry *entry = GTK_ENTRY (editable);
@ -78,6 +79,40 @@ wx_gtk_insert_text_callback(GtkEditable *editable,
}
}
// Check if we have to convert all input to upper-case
if ( !handled && text->GTKIsUpperCase() )
{
const wxGtkString upper(g_utf8_strup(new_text, new_text_length));
// Use the converted text to generate events
if ( !text->GTKEntryOnInsertText(upper) )
{
// Event not handled, so do insert the text: we have to do it
// ourselves to use the upper-case version of it
// Prevent recursive call to this handler again
g_signal_handlers_block_by_func
(
editable,
(gpointer)wx_gtk_insert_text_callback,
text
);
gtk_editable_insert_text(editable, upper, strlen(upper), position);
g_signal_handlers_unblock_by_func
(
editable,
(gpointer)wx_gtk_insert_text_callback,
text
);
}
// Don't call the default handler in any case, either the event was
// handled in the user code or we've already inserted the text.
handled = true;
}
if ( !handled && text->GTKEntryOnInsertText(new_text) )
{
// If we already handled the new text insertion, don't do it again.
@ -386,7 +421,7 @@ void wxTextEntry::SetEditable(bool editable)
}
// ----------------------------------------------------------------------------
// max text length
// input restrictions
// ----------------------------------------------------------------------------
void wxTextEntry::SetMaxLength(unsigned long len)
@ -411,6 +446,16 @@ void wxTextEntry::SendMaxLenEvent()
win->HandleWindowEvent(event);
}
void wxTextEntry::ForceUpper()
{
if ( !m_isUpperCase )
{
ConvertToUpperCase();
m_isUpperCase = true;
}
}
// ----------------------------------------------------------------------------
// IM handling
// ----------------------------------------------------------------------------

View File

@ -898,7 +898,7 @@ void wxTextEntry::SetEditable(bool editable)
}
// ----------------------------------------------------------------------------
// max length
// input restrictions
// ----------------------------------------------------------------------------
void wxTextEntry::SetMaxLength(unsigned long len)
@ -913,6 +913,15 @@ void wxTextEntry::SetMaxLength(unsigned long len)
::SendMessage(GetEditHwnd(), EM_LIMITTEXT, len, 0);
}
void wxTextEntry::ForceUpper()
{
ConvertToUpperCase();
const HWND hwnd = GetEditHwnd();
const LONG styleOld = ::GetWindowLong(hwnd, GWL_STYLE);
::SetWindowLong(hwnd, GWL_STYLE, styleOld | ES_UPPERCASE);
}
// ----------------------------------------------------------------------------
// hints
// ----------------------------------------------------------------------------

View File

@ -116,21 +116,23 @@ protected :
NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
// a minimal NSFormatter that just avoids getting too long entries
@interface wxMaximumLengthFormatter : NSFormatter
// an NSFormatter used to implement SetMaxLength() and ForceUpper() methods
@interface wxTextEntryFormatter : NSFormatter
{
int maxLength;
wxTextEntry* field;
bool forceUpper;
}
@end
@implementation wxMaximumLengthFormatter
@implementation wxTextEntryFormatter
- (id)init
{
self = [super init];
maxLength = 0;
forceUpper = false;
return self;
}
@ -139,6 +141,11 @@ NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
maxLength = maxlen;
}
- (void) forceUpper
{
forceUpper = true;
}
- (NSString *)stringForObjectValue:(id)anObject
{
if(![anObject isKindOfClass:[NSString class]])
@ -155,12 +162,25 @@ NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
- (BOOL)isPartialStringValid:(NSString **)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString **)error
{
int len = [*partialStringPtr length];
if ( maxLength > 0 && len > maxLength )
if ( maxLength > 0 )
{
field->SendMaxLenEvent();
return NO;
if ( [*partialStringPtr length] > maxLength )
{
field->SendMaxLenEvent();
return NO;
}
}
if ( forceUpper )
{
NSString* upper = [*partialStringPtr uppercaseString];
if ( ![*partialStringPtr isEqual:upper] )
{
*partialStringPtr = upper;
return NO;
}
}
return YES;
}
@ -869,12 +889,30 @@ void wxNSTextFieldControl::SetStringValue( const wxString &str)
[m_textField setStringValue: wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
}
wxTextEntryFormatter* wxNSTextFieldControl::GetFormatter()
{
// We only ever call setFormatter with wxTextEntryFormatter, so the cast is
// safe.
wxTextEntryFormatter*
formatter = (wxTextEntryFormatter*)[m_textField formatter];
if ( !formatter )
{
formatter = [[[wxTextEntryFormatter alloc] init] autorelease];
[formatter setTextEntry:GetTextEntry()];
[m_textField setFormatter:formatter];
}
return formatter;
}
void wxNSTextFieldControl::SetMaxLength(unsigned long len)
{
wxMaximumLengthFormatter* formatter = [[[wxMaximumLengthFormatter alloc] init] autorelease];
[formatter setMaxLength:len];
[formatter setTextEntry:GetTextEntry()];
[m_textField setFormatter:formatter];
[GetFormatter() setMaxLength:len];
}
void wxNSTextFieldControl::ForceUpper()
{
[GetFormatter() forceUpper];
}
void wxNSTextFieldControl::Copy()

View File

@ -81,6 +81,23 @@ void wxTextEntry::SetMaxLength(unsigned long len)
m_maxLength = len ;
}
void wxTextEntry::ForceUpper()
{
wxTextWidgetImpl* const textPeer = GetTextPeer();
wxCHECK_RET( textPeer, "Must create the control first" );
if ( textPeer->CanForceUpper() )
{
ConvertToUpperCase();
textPeer->ForceUpper();
}
else
{
wxTextEntryBase::ForceUpper();
}
}
// Clipboard operations
void wxTextEntry::Copy()