added incremental keyboard search to wxTreeCtrl
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15741 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -167,6 +167,7 @@ All (GUI):
|
|||||||
- added wxTextCtrl::EmulateKeyPress()
|
- added wxTextCtrl::EmulateKeyPress()
|
||||||
- Added wxMouseCaptureChangedEvent
|
- Added wxMouseCaptureChangedEvent
|
||||||
- Added custom character filtering to wxTextValidator
|
- Added custom character filtering to wxTextValidator
|
||||||
|
- wxTreeCtrl now supports incremental keyboard search
|
||||||
|
|
||||||
wxMSW:
|
wxMSW:
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// Name: treectrl.h
|
// Name: wx/generic/treectlg.h
|
||||||
// Purpose: wxTreeCtrl class
|
// Purpose: wxTreeCtrl class
|
||||||
// Author: Robert Roebling
|
// Author: Robert Roebling
|
||||||
// Modified by:
|
// Modified by:
|
||||||
@@ -31,6 +31,7 @@ class WXDLLEXPORT wxGenericTreeItem;
|
|||||||
class WXDLLEXPORT wxTreeItemData;
|
class WXDLLEXPORT wxTreeItemData;
|
||||||
|
|
||||||
class WXDLLEXPORT wxTreeRenameTimer;
|
class WXDLLEXPORT wxTreeRenameTimer;
|
||||||
|
class WXDLLEXPORT wxTreeFindTimer;
|
||||||
class WXDLLEXPORT wxTreeTextCtrl;
|
class WXDLLEXPORT wxTreeTextCtrl;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -341,21 +342,19 @@ public:
|
|||||||
void OnIdle( wxIdleEvent &event );
|
void OnIdle( wxIdleEvent &event );
|
||||||
|
|
||||||
// implementation helpers
|
// implementation helpers
|
||||||
void SendDeleteEvent(wxGenericTreeItem *itemBeingDeleted);
|
|
||||||
|
|
||||||
void DrawBorder(const wxTreeItemId& item);
|
|
||||||
void DrawLine(const wxTreeItemId& item, bool below);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class wxGenericTreeItem;
|
friend class wxGenericTreeItem;
|
||||||
friend class wxTreeRenameTimer;
|
friend class wxTreeRenameTimer;
|
||||||
|
friend class wxTreeFindTimer;
|
||||||
friend class wxTreeTextCtrl;
|
friend class wxTreeTextCtrl;
|
||||||
|
|
||||||
wxFont m_normalFont;
|
wxFont m_normalFont;
|
||||||
wxFont m_boldFont;
|
wxFont m_boldFont;
|
||||||
|
|
||||||
wxGenericTreeItem *m_anchor;
|
wxGenericTreeItem *m_anchor;
|
||||||
wxGenericTreeItem *m_current, *m_key_current, *m_currentEdit;
|
wxGenericTreeItem *m_current,
|
||||||
|
*m_key_current,
|
||||||
|
*m_currentEdit;
|
||||||
unsigned short m_indent;
|
unsigned short m_indent;
|
||||||
unsigned short m_spacing;
|
unsigned short m_spacing;
|
||||||
int m_lineHeight;
|
int m_lineHeight;
|
||||||
@@ -383,22 +382,36 @@ protected:
|
|||||||
wxTimer *m_renameTimer;
|
wxTimer *m_renameTimer;
|
||||||
wxString m_renameRes;
|
wxString m_renameRes;
|
||||||
|
|
||||||
wxBitmap *m_arrowRight,*m_arrowDown;
|
wxBitmap *m_arrowRight,
|
||||||
|
*m_arrowDown;
|
||||||
|
|
||||||
|
// incremental search data
|
||||||
|
wxString m_findPrefix;
|
||||||
|
wxTimer *m_findTimer;
|
||||||
|
|
||||||
// the common part of all ctors
|
// the common part of all ctors
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
// misc helpers
|
// misc helpers
|
||||||
|
void SendDeleteEvent(wxGenericTreeItem *itemBeingDeleted);
|
||||||
|
|
||||||
|
void DrawBorder(const wxTreeItemId& item);
|
||||||
|
void DrawLine(const wxTreeItemId& item, bool below);
|
||||||
|
void DrawDropEffect(wxGenericTreeItem *item);
|
||||||
|
|
||||||
wxTreeItemId DoInsertItem(const wxTreeItemId& parent,
|
wxTreeItemId DoInsertItem(const wxTreeItemId& parent,
|
||||||
size_t previous,
|
size_t previous,
|
||||||
const wxString& text,
|
const wxString& text,
|
||||||
int image, int selectedImage,
|
int image, int selectedImage,
|
||||||
wxTreeItemData *data);
|
wxTreeItemData *data);
|
||||||
|
|
||||||
|
// find the first item starting with the given prefix after the given item
|
||||||
|
wxTreeItemId FindItem(const wxTreeItemId& id, const wxString& prefix) const;
|
||||||
|
|
||||||
bool HasButtons(void) const
|
bool HasButtons(void) const
|
||||||
{ return (m_imageListButtons != NULL)
|
{ return (m_imageListButtons != NULL)
|
||||||
|| HasFlag(wxTR_TWIST_BUTTONS|wxTR_HAS_BUTTONS); }
|
|| HasFlag(wxTR_TWIST_BUTTONS|wxTR_HAS_BUTTONS); }
|
||||||
|
|
||||||
protected:
|
|
||||||
void CalculateLineHeight();
|
void CalculateLineHeight();
|
||||||
int GetLineHeight(wxGenericTreeItem *item) const;
|
int GetLineHeight(wxGenericTreeItem *item) const;
|
||||||
void PaintLevel( wxGenericTreeItem *item, wxDC& dc, int level, int &y );
|
void PaintLevel( wxGenericTreeItem *item, wxDC& dc, int level, int &y );
|
||||||
@@ -426,8 +439,6 @@ protected:
|
|||||||
bool TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select);
|
bool TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select);
|
||||||
void UnselectAllChildren( wxGenericTreeItem *item );
|
void UnselectAllChildren( wxGenericTreeItem *item );
|
||||||
|
|
||||||
void DrawDropEffect(wxGenericTreeItem *item);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
DECLARE_DYNAMIC_CLASS(wxGenericTreeCtrl)
|
DECLARE_DYNAMIC_CLASS(wxGenericTreeCtrl)
|
||||||
|
@@ -45,7 +45,6 @@
|
|||||||
class WXDLLEXPORT wxGenericTreeItem;
|
class WXDLLEXPORT wxGenericTreeItem;
|
||||||
|
|
||||||
WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
|
WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
|
||||||
//WX_DEFINE_OBJARRAY(wxArrayTreeItemIds);
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// constants
|
// constants
|
||||||
@@ -53,7 +52,7 @@ WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
|
|||||||
|
|
||||||
static const int NO_IMAGE = -1;
|
static const int NO_IMAGE = -1;
|
||||||
|
|
||||||
#define PIXELS_PER_UNIT 10
|
static const int PIXELS_PER_UNIT = 10;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Aqua arrows
|
// Aqua arrows
|
||||||
@@ -111,9 +110,13 @@ static const char *aqua_arrow_down[] = {
|
|||||||
class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
|
class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// start editing the current item after half a second (if the mouse hasn't
|
||||||
|
// been clicked/moved)
|
||||||
|
static const int DELAY = 500;
|
||||||
|
|
||||||
wxTreeRenameTimer( wxGenericTreeCtrl *owner );
|
wxTreeRenameTimer( wxGenericTreeCtrl *owner );
|
||||||
|
|
||||||
void Notify();
|
virtual void Notify();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxGenericTreeCtrl *m_owner;
|
wxGenericTreeCtrl *m_owner;
|
||||||
@@ -149,6 +152,22 @@ private:
|
|||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
|
||||||
|
// for a sufficiently long time
|
||||||
|
class WXDLLEXPORT wxTreeFindTimer : public wxTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// reset the current prefix after half a second of inactivity
|
||||||
|
static const int DELAY = 500;
|
||||||
|
|
||||||
|
wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; }
|
||||||
|
|
||||||
|
virtual void Notify() { m_owner->m_findPrefix.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxGenericTreeCtrl *m_owner;
|
||||||
|
};
|
||||||
|
|
||||||
// a tree item
|
// a tree item
|
||||||
class WXDLLEXPORT wxGenericTreeItem
|
class WXDLLEXPORT wxGenericTreeItem
|
||||||
{
|
{
|
||||||
@@ -694,7 +713,9 @@ void wxGenericTreeCtrl::Init()
|
|||||||
m_isDragging = FALSE;
|
m_isDragging = FALSE;
|
||||||
m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
|
m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
|
||||||
|
|
||||||
m_renameTimer = new wxTreeRenameTimer( this );
|
m_renameTimer = NULL;
|
||||||
|
m_findTimer = NULL;
|
||||||
|
|
||||||
m_lastOnSame = FALSE;
|
m_lastOnSame = FALSE;
|
||||||
|
|
||||||
m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
||||||
@@ -768,15 +789,20 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl()
|
|||||||
delete m_hilightBrush;
|
delete m_hilightBrush;
|
||||||
delete m_hilightUnfocusedBrush;
|
delete m_hilightUnfocusedBrush;
|
||||||
|
|
||||||
if (m_arrowRight) delete m_arrowRight;
|
delete m_arrowRight;
|
||||||
if (m_arrowDown) delete m_arrowDown;
|
delete m_arrowDown;
|
||||||
|
|
||||||
DeleteAllItems();
|
DeleteAllItems();
|
||||||
|
|
||||||
delete m_renameTimer;
|
delete m_renameTimer;
|
||||||
if (m_ownsImageListNormal) delete m_imageListNormal;
|
delete m_findTimer;
|
||||||
if (m_ownsImageListState) delete m_imageListState;
|
|
||||||
if (m_ownsImageListButtons) delete m_imageListButtons;
|
if (m_ownsImageListNormal)
|
||||||
|
delete m_imageListNormal;
|
||||||
|
if (m_ownsImageListState)
|
||||||
|
delete m_imageListState;
|
||||||
|
if (m_ownsImageListButtons)
|
||||||
|
delete m_imageListButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -1160,6 +1186,25 @@ wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
|
|||||||
return wxTreeItemId();
|
return wxTreeItemId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find the first item starting with the given prefix after the given item
|
||||||
|
wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
|
||||||
|
const wxString& prefixOrig) const
|
||||||
|
{
|
||||||
|
// match is case insensitive as this is more convenient to the user: having
|
||||||
|
// to press Shift-letter to go to the item starting with a capital letter
|
||||||
|
// would be too bothersome
|
||||||
|
wxString prefix = prefixOrig.Lower();
|
||||||
|
|
||||||
|
wxTreeItemId id = idParent;
|
||||||
|
|
||||||
|
while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) )
|
||||||
|
{
|
||||||
|
id = GetNext(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// operations
|
// operations
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -2363,7 +2408,9 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
// right : open if parent and go next
|
// right : open if parent and go next
|
||||||
// home : go to root
|
// home : go to root
|
||||||
// end : go to last item without opening parents
|
// end : go to last item without opening parents
|
||||||
switch (event.KeyCode())
|
// alnum : start or continue searching for the item with this prefix
|
||||||
|
int keyCode = event.KeyCode();
|
||||||
|
switch ( keyCode )
|
||||||
{
|
{
|
||||||
case '+':
|
case '+':
|
||||||
case WXK_ADD:
|
case WXK_ADD:
|
||||||
@@ -2423,7 +2470,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
// otherwise we return to where we came from
|
// otherwise we return to where we came from
|
||||||
SelectItem( prev, unselect_others, extended_select );
|
SelectItem( prev, unselect_others, extended_select );
|
||||||
m_key_current= (wxGenericTreeItem*) prev.m_pItem;
|
m_key_current= (wxGenericTreeItem*) prev.m_pItem;
|
||||||
EnsureVisible( prev );
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2441,7 +2487,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
|
|
||||||
SelectItem( prev, unselect_others, extended_select );
|
SelectItem( prev, unselect_others, extended_select );
|
||||||
m_key_current=(wxGenericTreeItem*) prev.m_pItem;
|
m_key_current=(wxGenericTreeItem*) prev.m_pItem;
|
||||||
EnsureVisible( prev );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2457,7 +2502,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
}
|
}
|
||||||
if (prev)
|
if (prev)
|
||||||
{
|
{
|
||||||
EnsureVisible( prev );
|
|
||||||
SelectItem( prev, unselect_others, extended_select );
|
SelectItem( prev, unselect_others, extended_select );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2477,7 +2521,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
wxTreeItemId child = GetFirstChild( m_key_current, cookie );
|
wxTreeItemId child = GetFirstChild( m_key_current, cookie );
|
||||||
SelectItem( child, unselect_others, extended_select );
|
SelectItem( child, unselect_others, extended_select );
|
||||||
m_key_current=(wxGenericTreeItem*) child.m_pItem;
|
m_key_current=(wxGenericTreeItem*) child.m_pItem;
|
||||||
EnsureVisible( child );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2495,7 +2538,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
{
|
{
|
||||||
SelectItem( next, unselect_others, extended_select );
|
SelectItem( next, unselect_others, extended_select );
|
||||||
m_key_current=(wxGenericTreeItem*) next.m_pItem;
|
m_key_current=(wxGenericTreeItem*) next.m_pItem;
|
||||||
EnsureVisible( next );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2521,7 +2563,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
|
|
||||||
if ( last.IsOk() )
|
if ( last.IsOk() )
|
||||||
{
|
{
|
||||||
EnsureVisible( last );
|
|
||||||
SelectItem( last, unselect_others, extended_select );
|
SelectItem( last, unselect_others, extended_select );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2531,21 +2572,54 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
|
|||||||
case WXK_HOME:
|
case WXK_HOME:
|
||||||
{
|
{
|
||||||
wxTreeItemId prev = GetRootItem();
|
wxTreeItemId prev = GetRootItem();
|
||||||
if (!prev) break;
|
if (!prev)
|
||||||
if (HasFlag(wxTR_HIDE_ROOT))
|
break;
|
||||||
|
|
||||||
|
if ( HasFlag(wxTR_HIDE_ROOT) )
|
||||||
{
|
{
|
||||||
long dummy;
|
long dummy;
|
||||||
prev = GetFirstChild(prev, dummy);
|
prev = GetFirstChild(prev, dummy);
|
||||||
if (!prev) break;
|
if (!prev)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
EnsureVisible( prev );
|
|
||||||
SelectItem( prev, unselect_others, extended_select );
|
SelectItem( prev, unselect_others, extended_select );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// do not use wxIsalnum() here
|
||||||
|
if ( !event.HasModifiers() && isalnum(keyCode) )
|
||||||
|
{
|
||||||
|
// find the next item starting with the given prefix
|
||||||
|
char ch = (char)keyCode;
|
||||||
|
|
||||||
|
wxTreeItemId id = FindItem(m_current, m_findPrefix + ch);
|
||||||
|
if ( !id.IsOk() )
|
||||||
|
{
|
||||||
|
// no such item
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectItem(id);
|
||||||
|
|
||||||
|
m_findPrefix += ch;
|
||||||
|
|
||||||
|
// also start the timer to reset the current prefix if the user
|
||||||
|
// doesn't press any more alnum keys soon -- we wouldn't want
|
||||||
|
// to use this prefix for a new item search
|
||||||
|
if ( !m_findTimer )
|
||||||
|
{
|
||||||
|
m_findTimer = new wxTreeFindTimer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
|
wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
|
||||||
@@ -2814,11 +2888,18 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
|
|||||||
if ( (item == m_current) &&
|
if ( (item == m_current) &&
|
||||||
(flags & wxTREE_HITTEST_ONITEMLABEL) &&
|
(flags & wxTREE_HITTEST_ONITEMLABEL) &&
|
||||||
HasFlag(wxTR_EDIT_LABELS) )
|
HasFlag(wxTR_EDIT_LABELS) )
|
||||||
|
{
|
||||||
|
if ( m_renameTimer )
|
||||||
{
|
{
|
||||||
if ( m_renameTimer->IsRunning() )
|
if ( m_renameTimer->IsRunning() )
|
||||||
m_renameTimer->Stop();
|
m_renameTimer->Stop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_renameTimer = new wxTreeRenameTimer( this );
|
||||||
|
}
|
||||||
|
|
||||||
m_renameTimer->Start( 100, TRUE );
|
m_renameTimer->Start( wxTreeRenameTimer::DELAY, TRUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lastOnSame = FALSE;
|
m_lastOnSame = FALSE;
|
||||||
@@ -2858,7 +2939,9 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
|
|||||||
if ( event.LeftDClick() )
|
if ( event.LeftDClick() )
|
||||||
{
|
{
|
||||||
// double clicking should not start editing the item label
|
// double clicking should not start editing the item label
|
||||||
|
if ( m_renameTimer )
|
||||||
m_renameTimer->Stop();
|
m_renameTimer->Stop();
|
||||||
|
|
||||||
m_lastOnSame = FALSE;
|
m_lastOnSame = FALSE;
|
||||||
|
|
||||||
// send activate event first
|
// send activate event first
|
||||||
|
Reference in New Issue
Block a user