1. better kbd interface to wxTextCtrl (selection...)

2. mouse input handling


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/wxUNIVERSAL@8397 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2000-09-18 19:07:24 +00:00
parent c7a0b9e59e
commit 634b9eb4e4
10 changed files with 347 additions and 43 deletions

4
TODO
View File

@@ -1,7 +1,9 @@
All All
* problem with horz scrolling: the focus rect isn't drawn entirely... * problem with lbox horz scrolling: the focus rect isn't drawn entirely...
* write sample testing all listbox styles/events * write sample testing all listbox styles/events
* text ctrl horz scrolling
* text ctrl wxTE_XXX styles support
MSW MSW

View File

@@ -25,6 +25,7 @@ class WXDLLEXPORT wxCheckListBox;
class WXDLLEXPORT wxListBox; class WXDLLEXPORT wxListBox;
class WXDLLEXPORT wxRenderer; class WXDLLEXPORT wxRenderer;
class WXDLLEXPORT wxScrollBar; class WXDLLEXPORT wxScrollBar;
class WXDLLEXPORT wxTextCtrl;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// types of the standard input handlers which can be passed to // types of the standard input handlers which can be passed to
@@ -297,6 +298,13 @@ public:
const wxMouseEvent& event); const wxMouseEvent& event);
virtual bool HandleMouseMove(wxControl *control, virtual bool HandleMouseMove(wxControl *control,
const wxMouseEvent& event); const wxMouseEvent& event);
protected:
// get the position of the mouse click
static long HitTest(const wxTextCtrl *text, const wxPoint& pos);
// capture data
wxTextCtrl *m_winCapture;
}; };
#endif // _WX_UNIV_INPHAND_H_ #endif // _WX_UNIV_INPHAND_H_

View File

@@ -97,10 +97,13 @@ public:
int indexAccel = -1, int indexAccel = -1,
wxRect *rectBounds = NULL) = 0; wxRect *rectBounds = NULL) = 0;
// draw a line of the text ctrl // draw a line of the text ctrl optionally highlighting the characters in
// the given range
virtual void DrawTextLine(wxDC& dc, virtual void DrawTextLine(wxDC& dc,
const wxString& text, const wxString& text,
const wxRect &rect, const wxRect &rect,
int selStart = -1,
int selEnd = -1,
int flags = 0) = 0; int flags = 0) = 0;
// draw the border and optionally return the rectangle containing the // draw the border and optionally return the rectangle containing the
@@ -300,6 +303,8 @@ public:
virtual void DrawTextLine(wxDC& dc, virtual void DrawTextLine(wxDC& dc,
const wxString& text, const wxString& text,
const wxRect &rect, const wxRect &rect,
int selStart = -1,
int selEnd = -1,
int flags = 0) int flags = 0)
{ m_renderer->DrawTextLine(dc, text, rect, flags); } { m_renderer->DrawTextLine(dc, text, rect, flags); }
virtual void DrawBorder(wxDC& dc, virtual void DrawBorder(wxDC& dc,
@@ -423,7 +428,7 @@ public:
// operations // operations
void DrawLabel(const wxBitmap& bitmap = wxNullBitmap, void DrawLabel(const wxBitmap& bitmap = wxNullBitmap,
wxCoord marginX = 0, wxCoord marginY = 0); wxCoord marginX = 0, wxCoord marginY = 0);
void DrawTextLine(const wxString& text); void DrawTextLine(const wxString& text, int selStart = -1, int selEnd = -1);
void DrawItems(const wxListBox *listbox, void DrawItems(const wxListBox *listbox,
size_t itemFirst, size_t itemLast); size_t itemFirst, size_t itemLast);
void DrawCheckItems(const wxCheckListBox *listbox, void DrawCheckItems(const wxCheckListBox *listbox,

View File

@@ -18,6 +18,8 @@
class WXDLLEXPORT wxCaret; class WXDLLEXPORT wxCaret;
#include "wx/scrolwin.h" // for wxScrollHelper
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxTextCtrl actions // wxTextCtrl actions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -48,11 +50,15 @@ class WXDLLEXPORT wxCaret;
#define wxACTION_TEXT_PREFIX_SEL _T("sel") #define wxACTION_TEXT_PREFIX_SEL _T("sel")
#define wxACTION_TEXT_PREFIX_DEL _T("del") #define wxACTION_TEXT_PREFIX_DEL _T("del")
// mouse selection
#define wxACTION_TEXT_ANCHOR_SEL _T("anchorsel")
#define wxACTION_TEXT_EXTEND_SEL _T("extendsel")
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxTextCtrl // wxTextCtrl
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class WXDLLEXPORT wxTextCtrl : public wxTextCtrlBase class WXDLLEXPORT wxTextCtrl : public wxTextCtrlBase, public wxScrollHelper
{ {
public: public:
// creation // creation
@@ -155,6 +161,11 @@ public:
long GetWordStart() const; long GetWordStart() const;
long GetWordEnd() const; long GetWordEnd() const;
// selection helpers
bool HasSelection() const { return m_selStart != -1; }
void ClearSelection();
void RemoveSelection();
// implementation only from now on // implementation only from now on
// ------------------------------- // -------------------------------
@@ -162,6 +173,11 @@ public:
virtual bool IsContainerWindow() const { return TRUE; } virtual bool IsContainerWindow() const { return TRUE; }
virtual wxBorder GetDefaultBorder() const { return wxBORDER_SUNKEN; } virtual wxBorder GetDefaultBorder() const { return wxBORDER_SUNKEN; }
// perform an action
virtual bool PerformAction(const wxControlAction& action,
long numArg = -1,
const wxString& strArg = wxEmptyString);
protected: protected:
// draw the text // draw the text
virtual void DoDraw(wxControlRenderer *renderer); virtual void DoDraw(wxControlRenderer *renderer);
@@ -170,9 +186,6 @@ protected:
virtual wxSize DoGetBestClientSize() const; virtual wxSize DoGetBestClientSize() const;
// input support // input support
virtual bool PerformAction(const wxControlAction& action,
long numArg = -1,
const wxString& strArg = wxEmptyString);
virtual wxString GetInputHandlerType() const; virtual wxString GetInputHandlerType() const;
// common part of all ctors // common part of all ctors
@@ -194,7 +207,8 @@ private:
m_curRow; m_curRow;
// selection // selection
long m_selStart, long m_selAnchor,
m_selStart,
m_selEnd; m_selEnd;
// flags // flags

View File

@@ -407,7 +407,8 @@ void MyUnivFrame::OnListBox(wxCommandEvent& event)
void MyUnivFrame::OnTextChange(wxCommandEvent& event) void MyUnivFrame::OnTextChange(wxCommandEvent& event)
{ {
wxLogDebug(_T("Text control value changed: now '%s'"), event.GetString()); wxLogDebug(_T("Text control value changed: now '%s'"),
event.GetString().c_str());
} }
void MyUnivFrame::OnLeftUp(wxMouseEvent& event) void MyUnivFrame::OnLeftUp(wxMouseEvent& event)

View File

@@ -42,7 +42,6 @@
#endif #endif
#ifndef WX_PRECOMP #ifndef WX_PRECOMP
#include "wx/wx.h"
#endif #endif
#include "wx/image.h" #include "wx/image.h"

View File

@@ -630,10 +630,12 @@ void wxControlRenderer::DoDrawItems(const wxListBox *lbox,
} }
} }
void wxControlRenderer::DrawTextLine(const wxString& text) void wxControlRenderer::DrawTextLine(const wxString& text,
int selStart, int selEnd)
{ {
m_dc.SetFont(m_window->GetFont()); m_dc.SetFont(m_window->GetFont());
m_dc.SetTextForeground(m_window->GetForegroundColour()); m_dc.SetTextForeground(m_window->GetForegroundColour());
m_renderer->DrawTextLine(m_dc, text, m_rect, m_window->GetStateFlags()); m_renderer->DrawTextLine(m_dc, text, m_rect, selStart, selEnd,
m_window->GetStateFlags());
} }

View File

@@ -57,6 +57,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
void wxTextCtrl::Init() void wxTextCtrl::Init()
{ {
m_selAnchor =
m_selStart = m_selStart =
m_selEnd = -1; m_selEnd = -1;
@@ -103,18 +104,7 @@ void wxTextCtrl::SetValue(const wxString& value)
if ( m_value == value ) if ( m_value == value )
return; return;
m_value = value; Replace(0, GetLastPosition(), value);
if ( IsSingleLine() )
{
SetInsertionPointEnd();
}
else
{
SetInsertionPoint(0);
}
Refresh();
} }
wxString wxTextCtrl::GetValue() const wxString wxTextCtrl::GetValue() const
@@ -129,7 +119,7 @@ void wxTextCtrl::Clear()
void wxTextCtrl::Replace(long from, long to, const wxString& text) void wxTextCtrl::Replace(long from, long to, const wxString& text)
{ {
wxCHECK_RET( from >= 0 && to >= 0 && from <= to, wxCHECK_RET( from >= 0 && to >= 0 && from <= to && to <= GetLastPosition(),
_T("invalid range in wxTextCtrl::Replace") ); _T("invalid range in wxTextCtrl::Replace") );
// replace the part of the text with the new value // replace the part of the text with the new value
@@ -141,9 +131,13 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
} }
m_value = valueNew; m_value = valueNew;
// update current position // update the current position
SetInsertionPoint(from + text.length()); SetInsertionPoint(from + text.length());
// and the selection (do it after setting the cursor to have correct value
// for selection anchor)
ClearSelection();
// FIXME shouldn't refresh everything of course // FIXME shouldn't refresh everything of course
Refresh(); Refresh();
} }
@@ -240,13 +234,49 @@ void wxTextCtrl::GetSelection(long* from, long* to) const
void wxTextCtrl::SetSelection(long from, long to) void wxTextCtrl::SetSelection(long from, long to)
{ {
if ( from != m_selStart || to != m_selEnd ) if ( from == -1 || to == -1 )
{ {
m_selStart = from; ClearSelection();
m_selEnd = to;
// TODO: update display
} }
else // valid sel range
{
if ( from >= to )
{
long tmp = from;
from = to;
to = tmp;
}
wxCHECK_RET( to <= GetLastPosition(),
_T("invalid range in wxTextCtrl::SetSelection") );
if ( from != m_selStart || to != m_selEnd )
{
m_selStart = from;
m_selEnd = to;
Refresh();
}
//else: nothing to do
}
}
void wxTextCtrl::ClearSelection()
{
m_selStart =
m_selEnd = -1;
m_selAnchor = m_curPos;
Refresh();
}
void wxTextCtrl::RemoveSelection()
{
if ( !HasSelection() )
return;
Remove(m_selStart, m_selEnd);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -330,11 +360,11 @@ int wxTextCtrl::GetNumberOfLines() const
long wxTextCtrl::XYToPosition(long x, long y) const long wxTextCtrl::XYToPosition(long x, long y) const
{ {
// note that this method should accept any values of x and y and return -1
// if they are out of range
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
wxASSERT_MSG( y == 0, _T("invalid XYToPosition() parameter") ); return x > GetLastPosition() || y > 0 ? -1 : x;
return x;
} }
else // multiline else // multiline
{ {
@@ -517,7 +547,7 @@ void wxTextCtrl::DoDraw(wxControlRenderer *renderer)
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
// just redraw everything // just redraw everything
renderer->DrawTextLine(m_value); renderer->DrawTextLine(m_value, (int)m_selStart, (int)m_selEnd);
} }
else else
{ {
@@ -561,11 +591,16 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
bool textChanged = FALSE; bool textChanged = FALSE;
wxString action; wxString action;
bool del = FALSE; bool del = FALSE,
sel = FALSE;
if ( actionOrig.StartsWith(wxACTION_TEXT_PREFIX_DEL, &action) ) if ( actionOrig.StartsWith(wxACTION_TEXT_PREFIX_DEL, &action) )
{ {
del = TRUE; del = TRUE;
} }
else if ( actionOrig.StartsWith(wxACTION_TEXT_PREFIX_SEL, &action) )
{
sel = TRUE;
}
else // not selection nor delete action else // not selection nor delete action
{ {
action = actionOrig; action = actionOrig;
@@ -600,11 +635,22 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
{ {
if ( !strArg.empty() ) if ( !strArg.empty() )
{ {
// replace the selection with the new text
RemoveSelection();
WriteText(strArg); WriteText(strArg);
textChanged = TRUE; textChanged = TRUE;
} }
} }
else if ( action == wxACTION_TEXT_ANCHOR_SEL )
{
newPos = numArg;
}
else if ( action == wxACTION_TEXT_EXTEND_SEL )
{
SetSelection(m_selAnchor, numArg);
}
else else
{ {
return wxControl::PerformAction(action, numArg, strArg); return wxControl::PerformAction(action, numArg, strArg);
@@ -622,18 +668,37 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
if ( del ) if ( del )
{ {
// delete everything between current opsition and the new one // if we have the selection, remove just it
if ( m_curPos != newPos ) if ( HasSelection() )
{ {
Remove(m_curPos, newPos); RemoveSelection();
}
else
{
// otherwise delete everything between current position and
// the new one
if ( m_curPos != newPos )
{
Remove(m_curPos, newPos);
textChanged = TRUE; textChanged = TRUE;
}
} }
} }
else else // cursor movement command
{ {
// just go there // just go there
SetInsertionPoint(newPos); SetInsertionPoint(newPos);
if ( sel )
{
SetSelection(m_selAnchor, m_curPos);
}
else // simple movement
{
// clear the existing selection
ClearSelection();
}
} }
} }
@@ -673,6 +738,114 @@ void wxTextCtrl::OnChar(wxKeyEvent& event)
wxStdTextCtrlInputHandler::wxStdTextCtrlInputHandler(wxInputHandler *inphand) wxStdTextCtrlInputHandler::wxStdTextCtrlInputHandler(wxInputHandler *inphand)
: wxStdInputHandler(inphand) : wxStdInputHandler(inphand)
{ {
m_winCapture = (wxTextCtrl *)NULL;
}
/* static */
long wxStdTextCtrlInputHandler::HitTest(const wxTextCtrl *text,
const wxPoint& pos)
{
int x, y;
text->CalcUnscrolledPosition(pos.x, pos.y, &x, &y);
// row calculation is simple as we assume that all lines have the same
// height
int row = y / text->GetCharHeight();
int rowMax = text->GetNumberOfLines() - 1;
if ( row > rowMax )
{
// clicking below the text is the same as clicking on the last line
row = rowMax;
}
// if the line is empty, the column can only be the first one
wxString line = text->GetLineText(row);
int col;
wxClientDC dc(wxConstCast(text, wxTextCtrl));
dc.SetFont(text->GetFont());
wxCoord width;
dc.GetTextExtent(line, &width, NULL);
if ( x >= width )
{
// clicking beyond the end of line is equivalent to clicking at
// the end of it
col = line.length();
}
else // we're inside the line
{
// now calculate the column: first, approximate it with fixed-width
// value and then calculate the correct value iteratively: note that
// we use the first character of the line instead of (average)
// GetCharWidth(): it is common to have lines of dashes, for example,
// and this should give us much better approximation in such case
dc.GetTextExtent(line[0], &width, NULL);
col = x / width;
// matchDir is -1 if we must move left, +1 to move right and 0 when
// we're exactly on the character we need
int matchDir = 0;
for ( ;; )
{
// check that we didn't go beyond the line boundary
if ( col < 0 )
{
col = 0;
break;
}
if ( col > line.length() )
{
col = line.length();
break;
}
wxString strBefore(line, (size_t)col);
dc.GetTextExtent(strBefore, &width, NULL);
if ( width >= x )
{
if ( matchDir == 1 )
{
// we were going to the right and, finally, moved beyond
// the original position: so the current is the next after
// the correct one
col--;
break;
}
else if ( matchDir == 0 )
{
// we just started iterating, now we know that we should
// move to the left
matchDir = -1;
}
//else: we are still to the right of the target, continue
}
else // width < x
{
// same logic as above ...
if ( matchDir == -1 )
{
// ... except that we don't need to back track
break;
}
else if ( matchDir == 0 )
{
// go to the right
matchDir = 1;
}
}
// this is not supposed to happen
wxASSERT_MSG( matchDir, _T("logic error in wxTextCtrl::HitTest") );
if ( matchDir == 1 )
col++;
else
col--;
}
}
return text->XYToPosition(col, row);
} }
bool wxStdTextCtrlInputHandler::HandleKey(wxControl *control, bool wxStdTextCtrlInputHandler::HandleKey(wxControl *control,
@@ -720,9 +893,15 @@ bool wxStdTextCtrlInputHandler::HandleKey(wxControl *control,
case WXK_BACK: case WXK_BACK:
action << wxACTION_TEXT_PREFIX_DEL << wxACTION_TEXT_LEFT; action << wxACTION_TEXT_PREFIX_DEL << wxACTION_TEXT_LEFT;
break; break;
// something else
default:
// reset the action as it could be already set to one of the
// prefixes
action = wxACTION_NONE;
} }
if ( !!action ) if ( action != wxACTION_NONE )
{ {
control->PerformAction(action, -1, str); control->PerformAction(action, -1, str);
@@ -735,12 +914,55 @@ bool wxStdTextCtrlInputHandler::HandleKey(wxControl *control,
bool wxStdTextCtrlInputHandler::HandleMouse(wxControl *control, bool wxStdTextCtrlInputHandler::HandleMouse(wxControl *control,
const wxMouseEvent& event) const wxMouseEvent& event)
{ {
if ( event.LeftDown() )
{
wxASSERT_MSG( !m_winCapture, _T("left button going down twice?") );
wxTextCtrl *text = wxStaticCast(control, wxTextCtrl);
m_winCapture = text;
m_winCapture->CaptureMouse();
text->HideCaret();
long pos = HitTest(text, event.GetPosition());
if ( pos != -1 )
{
text->PerformAction(wxACTION_TEXT_ANCHOR_SEL, pos);
}
}
else if ( event.LeftDClick() )
{
// TODO: select the word the cursor is on
}
else if ( event.LeftUp() )
{
if ( m_winCapture )
{
m_winCapture->ShowCaret();
m_winCapture->ReleaseMouse();
m_winCapture = (wxTextCtrl *)NULL;
}
}
return wxStdInputHandler::HandleMouse(control, event); return wxStdInputHandler::HandleMouse(control, event);
} }
bool wxStdTextCtrlInputHandler::HandleMouseMove(wxControl *control, bool wxStdTextCtrlInputHandler::HandleMouseMove(wxControl *control,
const wxMouseEvent& event) const wxMouseEvent& event)
{ {
if ( m_winCapture )
{
// track it
wxTextCtrl *text = wxStaticCast(m_winCapture, wxTextCtrl);
long pos = HitTest(text, event.GetPosition());
if ( pos != -1 )
{
text->PerformAction(wxACTION_TEXT_EXTEND_SEL, pos);
}
}
return wxStdInputHandler::HandleMouseMove(control, event); return wxStdInputHandler::HandleMouseMove(control, event);
} }

View File

@@ -75,6 +75,8 @@ public:
virtual void DrawTextLine(wxDC& dc, virtual void DrawTextLine(wxDC& dc,
const wxString& text, const wxString& text,
const wxRect &rect, const wxRect &rect,
int selStart = -1,
int selEnd = -1,
int flags = 0); int flags = 0);
virtual void DrawBorder(wxDC& dc, virtual void DrawBorder(wxDC& dc,
wxBorder border, wxBorder border,
@@ -914,9 +916,54 @@ void wxGTKRenderer::DrawButtonLabel(wxDC& dc,
void wxGTKRenderer::DrawTextLine(wxDC& dc, void wxGTKRenderer::DrawTextLine(wxDC& dc,
const wxString& text, const wxString& text,
const wxRect &rect, const wxRect &rect,
int selStart,
int selEnd,
int flags) int flags)
{ {
dc.DrawText(text, rect.x, rect.y); if ( selStart == -1 )
{
// just draw it as is
dc.DrawText(text, rect.x, rect.y);
}
else // we have selection
{
wxCoord width,
x = rect.x;
// draw the part before selection
wxString s(text, (size_t)selStart);
if ( !s.empty() )
{
dc.DrawText(s, x, rect.y);
dc.GetTextExtent(s, &width, NULL);
x += width;
}
// draw the selection itself
s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
if ( !s.empty() )
{
wxColour colFg = dc.GetTextForeground();
dc.SetTextForeground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
dc.SetTextBackground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT));
dc.SetBackgroundMode(wxSOLID);
dc.DrawText(s, x, rect.y);
dc.GetTextExtent(s, &width, NULL);
x += width;
dc.SetBackgroundMode(wxTRANSPARENT);
dc.SetTextForeground(colFg);
}
// draw the final part
s = text.c_str() + selEnd;
if ( !s.empty() )
{
dc.DrawText(s, x, rect.y);
}
}
} }
void wxGTKRenderer::DrawItem(wxDC& dc, void wxGTKRenderer::DrawItem(wxDC& dc,

View File

@@ -92,6 +92,8 @@ public:
virtual void DrawTextLine(wxDC& dc, virtual void DrawTextLine(wxDC& dc,
const wxString& text, const wxString& text,
const wxRect &rect, const wxRect &rect,
int selStart = -1,
int selEnd = -1,
int flags = 0); int flags = 0);
virtual void DrawBorder(wxDC& dc, virtual void DrawBorder(wxDC& dc,
wxBorder border, wxBorder border,
@@ -1394,6 +1396,8 @@ void wxWin32Renderer::DrawButtonLabel(wxDC& dc,
void wxWin32Renderer::DrawTextLine(wxDC& dc, void wxWin32Renderer::DrawTextLine(wxDC& dc,
const wxString& text, const wxString& text,
const wxRect &rect, const wxRect &rect,
int selStart,
int selEnd,
int flags) int flags)
{ {
dc.DrawText(text, rect.x + 2, rect.y + 1); dc.DrawText(text, rect.x + 2, rect.y + 1);