implement column resizing events in wxHeaderCtrl

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57190 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2008-12-08 14:40:42 +00:00
parent 4bfd0ed552
commit aef252d9da
9 changed files with 295 additions and 70 deletions

View File

@@ -359,11 +359,6 @@ private:
m_sortAscending = true;
}
// like SetWidth() but does not ask the header window of the
// wxDataViewCtrl to reflect the width-change.
void SetInternalWidth(int width);
wxString m_title;
int m_width,
m_minWidth;
@@ -482,8 +477,11 @@ public: // utility functions not part of the API
// called by header window after reorder
void ColumnMoved( wxDataViewColumn* col, unsigned int new_pos );
// updates the header window after a change in a column setting
void OnColumnChange();
// update the display after a change to an individual column
void OnColumnChange(unsigned int idx);
// update after a change to the number of columns
void OnColumnsCountChanged();
wxWindow *GetMainWindow() { return (wxWindow*) m_clientArea; }

View File

@@ -13,6 +13,7 @@
#include "wx/event.h"
#include "wx/vector.h"
#include "wx/overlay.h"
// ----------------------------------------------------------------------------
// wxHeaderCtrl
@@ -64,6 +65,7 @@ private:
// event handlers
void OnPaint(wxPaintEvent& event);
void OnMouse(wxMouseEvent& event);
void OnCaptureLost(wxMouseCaptureLostEvent& event);
// return the horizontal start position of the given column
int GetColStart(unsigned int idx) const;
@@ -84,6 +86,17 @@ private:
// column 1 but close enough to the divider separating it from column 0)
int FindColumnAtPos(int x, bool& onSeparator) const;
// end any drag operation currently in progress (resizing or reordering)
void EndDragging();
// and the resizing operation currently in progress and generate an event
// about it with its cancelled flag set if width is -1
void EndResizing(int width);
// update the current position of the resizing marker if xPhysical is a
// valid physical coordinate value or remove it entirely if it is -1
void UpdateResizingMarker(int xPhysical);
// number of columns in the control currently
unsigned int m_numColumns;
@@ -91,9 +104,16 @@ private:
// index of the column under mouse or -1 if none
unsigned int m_hover;
// the column being resized or -1 if there is no resizing operation in
// progress
unsigned int m_colBeingResized;
// the horizontal scroll offset
int m_scrollOffset;
// the overlay display used during the dragging operations
wxOverlay m_overlay;
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxHeaderCtrl)

View File

@@ -269,25 +269,45 @@ class WXDLLIMPEXP_CORE wxHeaderCtrlEvent : public wxNotifyEvent
{
public:
wxHeaderCtrlEvent(wxEventType commandType = wxEVT_NULL, int winid = 0)
: wxNotifyEvent(commandType, winid)
: wxNotifyEvent(commandType, winid),
m_col(-1),
m_width(0),
m_cancelled(false)
{
}
wxHeaderCtrlEvent(const wxHeaderCtrlEvent& event)
: wxNotifyEvent(event),
m_col(event.m_col)
m_col(event.m_col),
m_width(event.m_width),
m_cancelled(event.m_cancelled)
{
}
// the column which this event pertains to: valid for all header events
int GetColumn() const { return m_col; }
void SetColumn(int col) { m_col = col; }
// the width of the column: valid for column resizing/dragging events only
int GetWidth() const { return m_width; }
void SetWidth(int width) { m_width = width; }
// was the drag operation cancelled or did it complete successfully?
bool IsCancelled() const { return m_cancelled; }
void SetCancelled() { m_cancelled = true; }
virtual wxEvent *Clone() const { return new wxHeaderCtrlEvent(*this); }
protected:
// the column affected by the event
int m_col;
// the current width for the dragging events
int m_width;
// was the drag operation cancelled?
bool m_cancelled;
private:
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxHeaderCtrlEvent)
};
@@ -303,6 +323,10 @@ extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_BEGIN_DRAG;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_DRAGGING;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_END_DRAG;
typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&);
#define wxHeaderCtrlEventHandler(func) \
@@ -322,4 +346,8 @@ typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&);
#define EVT_HEADER_SEPARATOR_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(SEPARATOR_DCLICK, id, fn)
#define EVT_HEADER_BEGIN_DRAG(id, fn) wx__DECLARE_HEADER_EVT(BEGIN_DRAG, id, fn)
#define EVT_HEADER_DRAGGING(id, fn) wx__DECLARE_HEADER_EVT(DRAGGING, id, fn)
#define EVT_HEADER_END_DRAG(id, fn) wx__DECLARE_HEADER_EVT(END_DRAG, id, fn)
#endif // _WX_HEADERCTRL_H_

View File

@@ -71,13 +71,10 @@ private:
enum Operation { Set, Insert };
void DoSetOrInsertItem(Operation oper, unsigned int idx);
// send an event of the given type for the given column, return true if it
// was processed
bool SendEvent(wxEventType evtType, unsigned int idx);
// send a click or double click event (depending on dblclk value) for the
// click with the given button on the given item
bool SendClickEvent(bool dblclk, int button, unsigned int idx);
// get the event type corresponding to a click or double click event
// (depending on dblclk value) with the specified (using MSW convention)
// mouse button
wxEventType GetClickEventType(bool dblclk, int button);
// the image list: initially NULL, created on demand

View File

@@ -69,6 +69,22 @@
(this action is commonly used to resize the column to fit its
contents width and the control provides UpdateColumnWidthToFit() method
to make implementing this easier).
@event{EVT_HEADER_BEGIN_DRAG(id, func)}
The user started to drag the column with the specified index (this
can only happen if wxHeaderColumn::IsResizeable() returned true for
this column). The event can be vetoed to prevent the control from
being resized, if it isn't, the dragging and end drag events will
be generated later.
@event{EVT_HEADER_DRAGGING(id, func)}
The user is dragging the column with the specified index and its
current width is wxHeaderCtrlEvent::GetWidth(). The event can be
vetoed to stop the dragging operation completely at any time.
@event{EVT_HEADER_END_DRAG(id, func)}
The user stopped dragging the column. If
wxHeaderCtrlEvent::IsCancelled() returns @true, nothing should
be done, otherwise the column should normally be resized to the
value of wxHeaderCtrlEvent::GetWidth().
@endEventTable
@library{wxcore}
@@ -389,6 +405,22 @@ class wxHeaderCtrlEvent : public wxNotifyEvent
public:
/**
Return the index of the column affected by this event.
This method can be called for all header control events.
*/
int GetColumn() const;
/**
Return the current width of the column.
This method can only be called for the dragging events.
*/
int GetWidth() const;
/**
Return @true if the drag operation was cancelled.
This method can only be called for the end drag event.
*/
bool IsCancelled() const;
};

View File

@@ -177,3 +177,7 @@ const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_BEGIN_DRAG = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_DRAGGING = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_END_DRAG = wxNewEventType();

View File

@@ -102,6 +102,7 @@ protected:
int widthContents = owner->GetBestColumnWidth(idx);
owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
owner->OnColumnChange(idx);
return true;
}
@@ -136,6 +137,28 @@ private:
event.Skip();
}
void OnBeginDrag(wxHeaderCtrlEvent& event)
{
if ( !GetColumn(event.GetColumn()).IsResizeable() )
event.Veto();
}
void OnDragging(wxHeaderCtrlEvent& event)
{
const wxHeaderColumnBase& col = GetColumn(event.GetColumn());
const int minWidth = col.GetMinWidth();
if ( event.GetWidth() < minWidth )
event.Veto();
}
void OnEndDrag(wxHeaderCtrlEvent& event)
{
const unsigned col = event.GetColumn();
GetColumn(col).SetWidth(event.GetWidth());
GetOwner()->OnColumnChange(col);
}
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow)
};
@@ -143,6 +166,10 @@ private:
BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
EVT_HEADER_BEGIN_DRAG(wxID_ANY, wxDataViewHeaderWindow::OnBeginDrag)
EVT_HEADER_DRAGGING(wxID_ANY, wxDataViewHeaderWindow::OnDragging)
EVT_HEADER_END_DRAG(wxID_ANY, wxDataViewHeaderWindow::OnEndDrag)
END_EVENT_TABLE()
//-----------------------------------------------------------------------------
@@ -3288,7 +3315,7 @@ bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
return false;
m_cols.Append( col );
OnColumnChange();
OnColumnsCountChanged();
return true;
}
@@ -3298,7 +3325,7 @@ bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
return false;
m_cols.Insert( col );
OnColumnChange();
OnColumnsCountChanged();
return true;
}
@@ -3308,11 +3335,19 @@ bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
return false;
m_cols.Insert( pos, col );
OnColumnChange();
OnColumnsCountChanged();
return true;
}
void wxDataViewCtrl::OnColumnChange()
void wxDataViewCtrl::OnColumnChange(unsigned int idx)
{
if ( m_headerArea )
m_headerArea->UpdateColumn(idx);
m_clientArea->UpdateDisplay();
}
void wxDataViewCtrl::OnColumnsCountChanged()
{
if (m_headerArea)
m_headerArea->SetColumnCount(GetColumnCount());
@@ -3335,20 +3370,9 @@ unsigned int wxDataViewCtrl::GetColumnCount() const
return m_cols.GetCount();
}
wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int pos ) const
wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
{
wxDataViewColumnList::const_iterator iter;
unsigned int i = 0;
for (iter = m_cols.begin(); iter!=m_cols.end(); iter++)
{
if (i == pos)
return *iter;
if ((*iter)->IsHidden())
continue;
i ++;
}
return NULL;
return m_cols[idx];
}
void wxDataViewCtrl::ColumnMoved( wxDataViewColumn* col, unsigned int new_pos )
@@ -3371,7 +3395,7 @@ bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
return false;
m_cols.Erase(ret);
OnColumnChange();
OnColumnsCountChanged();
return true;
}
@@ -3379,7 +3403,7 @@ bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
bool wxDataViewCtrl::ClearColumns()
{
m_cols.Clear();
OnColumnChange();
OnColumnsCountChanged();
return true;
}

View File

@@ -57,7 +57,8 @@ const unsigned COL_NONE = (unsigned)-1;
void wxHeaderCtrl::Init()
{
m_numColumns = 0;
m_hover = COL_NONE;
m_hover =
m_colBeingResized = COL_NONE;
m_scrollOffset = 0;
}
@@ -209,6 +210,55 @@ void wxHeaderCtrl::RefreshColsAfter(unsigned int idx)
RefreshRect(rect);
}
// ----------------------------------------------------------------------------
// wxHeaderCtrl dragging
// ----------------------------------------------------------------------------
void wxHeaderCtrl::UpdateResizingMarker(int xPhysical)
{
// unfortunately drawing the marker over the parent window doesn't work as
// it's usually covered by another window (the main control view) so just
// draw the marker over the header itself, even if it makes it not very
// useful
wxClientDC dc(this);
wxDCOverlay dcover(m_overlay, &dc);
dcover.Clear();
if ( xPhysical != -1 )
{
dc.SetPen(*wxLIGHT_GREY_PEN);
dc.DrawLine(xPhysical, 0, xPhysical, GetClientSize().y);
}
}
void wxHeaderCtrl::EndDragging()
{
UpdateResizingMarker(-1);
m_overlay.Reset();
}
void wxHeaderCtrl::EndResizing(int width)
{
wxASSERT_MSG( m_colBeingResized != COL_NONE,
"shouldn't be called if we're not resizing" );
EndDragging();
wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_END_DRAG, GetId());
event.SetEventObject(this);
event.SetColumn(m_colBeingResized);
if ( width == -1 )
event.SetCancelled();
else
event.SetWidth(width);
GetEventHandler()->ProcessEvent(event);
m_colBeingResized = COL_NONE;
}
// ----------------------------------------------------------------------------
// wxHeaderCtrl event handlers
// ----------------------------------------------------------------------------
@@ -217,6 +267,8 @@ BEGIN_EVENT_TABLE(wxHeaderCtrl, wxHeaderCtrlBase)
EVT_PAINT(wxHeaderCtrl::OnPaint)
EVT_MOUSE_EVENTS(wxHeaderCtrl::OnMouse)
EVT_MOUSE_CAPTURE_LOST(wxHeaderCtrl::OnCaptureLost)
END_EVENT_TABLE()
void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
@@ -283,18 +335,42 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
}
}
void wxHeaderCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
{
if ( m_colBeingResized != COL_NONE )
EndResizing(-1);
}
void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
{
// do this in advance to allow simply returning if we're not interested,
// we'll undo it if we do handle the event below
mevent.Skip();
// account for the control displacement
const int x = mevent.GetX() - m_scrollOffset;
const int xPhysical = mevent.GetX();
const int xLogical = xPhysical - m_scrollOffset;
// first deal with the [continuation of any] dragging operations in
// progress
if ( m_colBeingResized != COL_NONE )
{
if ( mevent.LeftUp() )
EndResizing(xLogical - GetColStart(m_colBeingResized));
else // update the live separator position
UpdateResizingMarker(xPhysical);
return;
}
// find if the event is over a column at all
bool onSeparator;
const unsigned col = mevent.Leaving()
? (onSeparator = false, COL_NONE)
: FindColumnAtPos(x, onSeparator);
: FindColumnAtPos(xLogical, onSeparator);
// update the highlighted column if it changed
if ( col != m_hover )
@@ -306,9 +382,6 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
RefreshColIfNotNone(m_hover);
}
if ( col == COL_NONE )
return;
// update mouse cursor as it moves around
if ( mevent.Moving() )
{
@@ -316,20 +389,30 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
return;
}
// all the other events only make sense when they happen over a column
if ( col == COL_NONE )
return;
// enter various dragging modes on left mouse press
if ( mevent.LeftDown() )
{
// TODO
if ( onSeparator )
// resize column
;
else
// drag column
{
// start resizing the column
m_colBeingResized = col;
UpdateResizingMarker(xPhysical);
}
else // on column itself
{
// TODO: drag column
;
}
return;
}
// determine the type of header event corresponding to this mouse event
// determine the type of header event corresponding to click events
wxEventType evtType = wxEVT_NULL;
const bool click = mevent.ButtonUp(),
dblclk = mevent.ButtonDClick();

View File

@@ -260,16 +260,7 @@ void wxHeaderCtrl::DoSetOrInsertItem(Operation oper, unsigned int idx)
// wxHeaderCtrl events
// ----------------------------------------------------------------------------
bool wxHeaderCtrl::SendEvent(wxEventType evtType, unsigned int idx)
{
wxHeaderCtrlEvent event(evtType, GetId());
event.SetEventObject(this);
event.SetColumn(idx);
return GetEventHandler()->ProcessEvent(event);
}
bool wxHeaderCtrl::SendClickEvent(bool dblclk, int button, unsigned int idx)
wxEventType wxHeaderCtrl::GetClickEventType(bool dblclk, int button)
{
wxEventType evtType;
switch ( button )
@@ -291,23 +282,27 @@ bool wxHeaderCtrl::SendClickEvent(bool dblclk, int button, unsigned int idx)
default:
wxFAIL_MSG( wxS("unexpected event type") );
return false;
evtType = wxEVT_NULL;
}
return SendEvent(evtType, idx);
return evtType;
}
bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
{
NMHEADER * const nmhdr = (NMHEADER *)lParam;
const int idx = nmhdr->iItem;
wxEventType evtType = wxEVT_NULL;
int idx = nmhdr->iItem;
int width = 0;
switch ( const UINT code = nmhdr->hdr.code )
{
// click events
// ------------
case HDN_ITEMCLICK:
case HDN_ITEMDBLCLICK:
if ( SendClickEvent(code == HDN_ITEMDBLCLICK, nmhdr->iButton, idx) )
return true;
evtType = GetClickEventType(code == HDN_ITEMDBLCLICK, nmhdr->iButton);
break;
// although we should get the notifications about the right clicks
@@ -317,20 +312,64 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
case NM_RDBLCLK:
{
POINT pt;
const int col = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
if ( col != wxNOT_FOUND )
{
if ( SendClickEvent(code == NM_RDBLCLK, 1, col) )
return true;
}
idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
if ( idx != wxNOT_FOUND )
evtType = GetClickEventType(code == NM_RDBLCLK, 1);
//else: ignore clicks outside any column
}
break;
case HDN_DIVIDERDBLCLICK:
if ( SendEvent(wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK, idx) )
return true;
evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
break;
// column resizing events
// ----------------------
// see comments in wxListCtrl::MSWOnNotify() for why we catch both
// ASCII and Unicode versions of this message
case HDN_BEGINTRACKA:
case HDN_BEGINTRACKW:
evtType = wxEVT_COMMAND_HEADER_BEGIN_DRAG;
// fall through
case HDN_TRACKA:
case HDN_TRACKW:
if ( evtType == wxEVT_NULL )
evtType = wxEVT_COMMAND_HEADER_DRAGGING;
// fall through
case HDN_ENDTRACKA:
case HDN_ENDTRACKW:
if ( evtType == wxEVT_NULL )
evtType = wxEVT_COMMAND_HEADER_END_DRAG;
width = nmhdr->pitem->cxy;
break;
}
// do generate the corresponding wx event
if ( evtType != wxEVT_NULL )
{
wxHeaderCtrlEvent event(evtType, GetId());
event.SetEventObject(this);
event.SetColumn(idx);
event.SetWidth(width);
if ( GetEventHandler()->ProcessEvent(event) )
{
if ( !event.IsAllowed() )
{
// all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
// interpret TRUE return value as meaning to stop the control
// default handling of the message
*result = TRUE;
}
return true;
}
}
return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);