Merge branch 'msw-text-ctrl-backspace'
Implement support for Ctrl+Backspace for all MSW text controls. See https://github.com/wxWidgets/wxWidgets/pull/2317
This commit is contained in:
@@ -294,6 +294,13 @@ private:
|
||||
virtual void MSWProcessSpecialKey(wxKeyEvent& event) wxOVERRIDE;
|
||||
#endif // wxUSE_OLE
|
||||
|
||||
// Do we need to handle Ctrl+Backspace ourselves?
|
||||
bool MSWNeedsToHandleCtrlBackspace() const;
|
||||
|
||||
// Delete backwards until the start of the previous word before caret in a
|
||||
// way compatible with the standard MSW Ctrl+Backspace shortcut.
|
||||
void MSWDeleteWordBack();
|
||||
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
|
||||
// Used by EN_MAXTEXT handler to increase the size limit (will do nothing
|
||||
|
@@ -81,6 +81,9 @@ protected:
|
||||
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer) wxOVERRIDE;
|
||||
#endif // wxUSE_OLE
|
||||
|
||||
// Returns true if this control uses standard file names completion.
|
||||
bool MSWUsesStandardAutoComplete() const;
|
||||
|
||||
// Helper for wxTE_PROCESS_ENTER handling: activates the default button in
|
||||
// the dialog containing this control if any.
|
||||
bool ClickDefaultButtonIfPossible();
|
||||
@@ -100,9 +103,13 @@ private:
|
||||
// be called and the default implementation asserts if this is not the case.
|
||||
virtual void MSWProcessSpecialKey(wxKeyEvent& event);
|
||||
|
||||
// Get the auto-complete object creating it if necessary. Returns NULL if
|
||||
// creating it failed.
|
||||
wxTextAutoCompleteData *GetOrCreateCompleter();
|
||||
// Check if we really have auto-complete data. This is not the same as just
|
||||
// checking if m_autoCompleteData is NULL, see the code for more details.
|
||||
bool MSWHasAutoCompleteData() const;
|
||||
|
||||
// Check that we have auto-complete data, creating it if necessary. Returns
|
||||
// false if creating it failed.
|
||||
bool MSWEnsureHasAutoCompleteData();
|
||||
|
||||
// Various auto-completion-related stuff, only used if any of AutoComplete()
|
||||
// methods are called. Use the function above to access it.
|
||||
|
@@ -2116,6 +2116,10 @@ bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
|
||||
case VK_HOME:
|
||||
case VK_END:
|
||||
return false;
|
||||
|
||||
case VK_BACK:
|
||||
if ( MSWNeedsToHandleCtrlBackspace() )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // Shift is pressed
|
||||
@@ -2206,8 +2210,119 @@ void wxTextCtrl::MSWProcessSpecialKey(wxKeyEvent& event)
|
||||
|
||||
#endif // wxUSE_OLE
|
||||
|
||||
void wxTextCtrl::MSWDeleteWordBack()
|
||||
{
|
||||
// Surprisingly the behaviour of Ctrl+Backspace is different in all three
|
||||
// cases where it's supported by MSW itself:
|
||||
//
|
||||
// 1. Rich edit controls simply ignore selection and handle it as usual.
|
||||
// 2. Plain edit controls don't do anything when there is selection.
|
||||
// 3. Notepad in Windows 10 1809 and later deletes just the selection.
|
||||
//
|
||||
// The latter behaviour seems the most useful, so do it like this here too.
|
||||
if ( HasSelection() )
|
||||
{
|
||||
RemoveSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// This variable contains one end of the range to delete, the rest of this
|
||||
// function is concerned with finding the starting end of this range.
|
||||
const long end = GetInsertionPoint();
|
||||
|
||||
long col, line;
|
||||
if ( !PositionToXY(end, &col, &line) )
|
||||
return;
|
||||
|
||||
// We stop at the start of line, so remember it.
|
||||
const long start = XYToPosition(0, line);
|
||||
|
||||
const wxString& text = GetLineText(line);
|
||||
|
||||
// The way it works in rich text controls or when SHAutoComplete() is used
|
||||
// is that it deletes everything until the first span of alphanumeric
|
||||
// characters it finds (moving backwards). But the implementation of the
|
||||
// same shortcut in in notepad in Windows 10 versions 1809 and later
|
||||
// doesn't behave in quite the same way and doesn't handle alphanumeric
|
||||
// characters specially, i.e. it just stops on space. This seems more
|
||||
// useful and simpler to implement, and it probably will become standard in
|
||||
// the future, so do it like this here too.
|
||||
|
||||
// First skip all space starting from the character to the left of the
|
||||
// current one.
|
||||
long current = end;
|
||||
for ( ;; )
|
||||
{
|
||||
if ( current == start )
|
||||
{
|
||||
// When there is nothing but spaces to the left until the start of
|
||||
// line, we need to delete these spaces (if any) as well as the new
|
||||
// line separating this line from the previous one (if any).
|
||||
if ( line > 0 )
|
||||
{
|
||||
// This function is only used with plain EDITs which use "\r\n"
|
||||
// and so we need to subtract 2 to account for the new line.
|
||||
current -= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// We start from the previous character.
|
||||
--current;
|
||||
|
||||
// Did we find the end of the previous "word"?
|
||||
if ( text[current - start] != ' ' )
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
if ( current == start )
|
||||
{
|
||||
// We don't delete the new line in this case, as we're going to
|
||||
// delete some non-spaces in this line.
|
||||
break;
|
||||
}
|
||||
|
||||
--current;
|
||||
|
||||
if ( text[current - start] == ' ' )
|
||||
{
|
||||
// Don't delete the space itself.
|
||||
++current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Remove(current, end);
|
||||
}
|
||||
|
||||
bool wxTextCtrl::MSWNeedsToHandleCtrlBackspace() const
|
||||
{
|
||||
// We want to handle the undocumented Ctrl+Backspace shortcut only if it's
|
||||
// not handled by the control itself, which is a bit tricky because it's
|
||||
// normally only handled by rich edit controls, but plain EDIT ones may
|
||||
// also handle it if they use SHAutoComplete().
|
||||
return !HasFlag(wxTE_READONLY) &&
|
||||
!IsRich() &&
|
||||
!MSWUsesStandardAutoComplete();
|
||||
}
|
||||
|
||||
void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
|
||||
{
|
||||
// Handle Ctrl+Backspace if necessary: this is not a documented standard
|
||||
// shortcut, but it's a de facto standard and people expect it to work.
|
||||
if ( MSWNeedsToHandleCtrlBackspace() &&
|
||||
event.GetModifiers() == wxMOD_CONTROL &&
|
||||
event.GetKeyCode() == WXK_BACK )
|
||||
{
|
||||
MSWDeleteWordBack();
|
||||
return;
|
||||
}
|
||||
|
||||
// richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages
|
||||
// when Ctrl-V, X or C is pressed and this prevents wxClipboardTextEvent
|
||||
// from working. So we work around it by intercepting these shortcuts
|
||||
|
@@ -659,6 +659,10 @@ private:
|
||||
wxDECLARE_NO_COPY_CLASS(wxTextAutoCompleteData);
|
||||
};
|
||||
|
||||
// Special pointer value which indicates that we're using SHAutoComplete().
|
||||
static wxTextAutoCompleteData* const wxDUMMY_SHAUTOCOMPLETE_DATA =
|
||||
reinterpret_cast<wxTextAutoCompleteData*>(-1);
|
||||
|
||||
#endif // HAS_AUTOCOMPLETE
|
||||
|
||||
// ============================================================================
|
||||
@@ -679,7 +683,8 @@ wxTextEntry::wxTextEntry()
|
||||
wxTextEntry::~wxTextEntry()
|
||||
{
|
||||
#ifdef HAS_AUTOCOMPLETE
|
||||
delete m_autoCompleteData;
|
||||
if ( MSWHasAutoCompleteData() )
|
||||
delete m_autoCompleteData;
|
||||
#endif // HAS_AUTOCOMPLETE
|
||||
}
|
||||
|
||||
@@ -833,8 +838,11 @@ bool wxTextEntry::DoAutoCompleteFileNames(int flags)
|
||||
|
||||
// Disable the other kinds of completion now that we use the built-in file
|
||||
// names completion.
|
||||
if ( m_autoCompleteData )
|
||||
m_autoCompleteData->DisableCompletion();
|
||||
if ( MSWHasAutoCompleteData() )
|
||||
delete m_autoCompleteData;
|
||||
|
||||
// Set it to the special value indicating that we're using SHAutoComplete().
|
||||
m_autoCompleteData = wxDUMMY_SHAUTOCOMPLETE_DATA;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -846,27 +854,43 @@ void wxTextEntry::MSWProcessSpecialKey(wxKeyEvent& WXUNUSED(event))
|
||||
wxFAIL_MSG(wxS("Must be overridden if can be called"));
|
||||
}
|
||||
|
||||
wxTextAutoCompleteData *wxTextEntry::GetOrCreateCompleter()
|
||||
bool wxTextEntry::MSWUsesStandardAutoComplete() const
|
||||
{
|
||||
if ( !m_autoCompleteData )
|
||||
return m_autoCompleteData == wxDUMMY_SHAUTOCOMPLETE_DATA;
|
||||
}
|
||||
|
||||
bool wxTextEntry::MSWHasAutoCompleteData() const
|
||||
{
|
||||
// We use special wxDUMMY_SHAUTOCOMPLETE_DATA for the pointer to indicate
|
||||
// that we're using SHAutoComplete(), so we need to check for it too, and
|
||||
// not just whether the pointer is non-NULL.
|
||||
return m_autoCompleteData != NULL
|
||||
&& m_autoCompleteData != wxDUMMY_SHAUTOCOMPLETE_DATA;
|
||||
}
|
||||
|
||||
bool wxTextEntry::MSWEnsureHasAutoCompleteData()
|
||||
{
|
||||
if ( !MSWHasAutoCompleteData() )
|
||||
{
|
||||
wxTextAutoCompleteData * const ac = new wxTextAutoCompleteData(this);
|
||||
if ( ac->IsOk() )
|
||||
m_autoCompleteData = ac;
|
||||
else
|
||||
if ( !ac->IsOk() )
|
||||
{
|
||||
delete ac;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_autoCompleteData = ac;
|
||||
}
|
||||
|
||||
return m_autoCompleteData;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
|
||||
{
|
||||
wxTextAutoCompleteData * const ac = GetOrCreateCompleter();
|
||||
if ( !ac )
|
||||
if ( !MSWEnsureHasAutoCompleteData() )
|
||||
return false;
|
||||
|
||||
ac->ChangeStrings(choices);
|
||||
m_autoCompleteData->ChangeStrings(choices);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -876,14 +900,13 @@ bool wxTextEntry::DoAutoCompleteCustom(wxTextCompleter *completer)
|
||||
// First deal with the case when we just want to disable auto-completion.
|
||||
if ( !completer )
|
||||
{
|
||||
if ( m_autoCompleteData )
|
||||
if ( MSWHasAutoCompleteData() )
|
||||
m_autoCompleteData->DisableCompletion();
|
||||
//else: Nothing to do, we hadn't used auto-completion even before.
|
||||
}
|
||||
else // Have a valid completer.
|
||||
{
|
||||
wxTextAutoCompleteData * const ac = GetOrCreateCompleter();
|
||||
if ( !ac )
|
||||
if ( !MSWEnsureHasAutoCompleteData() )
|
||||
{
|
||||
// Delete the custom completer for consistency with the case when
|
||||
// we succeed to avoid memory leaks in user code.
|
||||
@@ -892,7 +915,7 @@ bool wxTextEntry::DoAutoCompleteCustom(wxTextCompleter *completer)
|
||||
}
|
||||
|
||||
// This gives ownership of the custom completer to m_autoCompleteData.
|
||||
if ( !ac->ChangeCustomCompleter(completer) )
|
||||
if ( !m_autoCompleteData->ChangeCustomCompleter(completer) )
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user