Skip to content

Commit be09c5f

Browse files
author
徐扬斌
committed
Fix DND reordering of generic verion wxHeaderCtrl
The old DND reorder implementation is buggy and wrong in many ways. This is a rework that fix almost everything with better user experiences, better dropped pos indication. ToDo: Better explanation code comments for several main changes. I'm now too busy to leave an explanation with enough details.
1 parent 4f22df0 commit be09c5f

File tree

2 files changed

+137
-36
lines changed

2 files changed

+137
-36
lines changed

include/wx/generic/headerctrlg.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ class WXDLLIMPEXP_CORE wxHeaderCtrl : public wxHeaderCtrlBase
5252

5353

5454
private:
55+
enum class Region{
56+
NoWhere,
57+
LeftHalf,
58+
RightHalf,
59+
Separator
60+
};
5561
// implement base class pure virtuals
5662
virtual void DoSetCount(unsigned int count) wxOVERRIDE;
5763
virtual unsigned int DoGetCount() const wxOVERRIDE;
@@ -96,11 +102,14 @@ class WXDLLIMPEXP_CORE wxHeaderCtrl : public wxHeaderCtrlBase
96102
// position is near the divider at the right end of this column (notice
97103
// that this means that we return column 0 even if the position is over
98104
// column 1 but close enough to the divider separating it from column 0)
99-
unsigned int FindColumnAtPoint(int x, bool *onSeparator = NULL) const;
105+
unsigned int FindColumnAtPoint(int x, Region& pos_region) const;
100106

101107
// return the result of FindColumnAtPoint() if it is a valid column,
102108
// otherwise the index of the last (rightmost) displayed column
103-
unsigned int FindColumnClosestToPoint(int xPhysical) const;
109+
unsigned int FindColumnClosestToPoint(int xPhysical, Region& pos_region) const;
110+
111+
unsigned int FindColumnAfter(const unsigned int column_idx) const;
112+
unsigned int FindColumnBefore(const unsigned int column_idx) const;
104113

105114
// return true if a drag resizing operation is currently in progress
106115
bool IsResizing() const;

src/generic/headerctrlg.cpp

Lines changed: 126 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ int wxHeaderCtrl::GetColStart(unsigned int idx) const
156156
if ( col.IsShown() )
157157
pos += col.GetWidth();
158158
}
159-
159+
160160
return pos;
161161
}
162162

@@ -167,7 +167,7 @@ int wxHeaderCtrl::GetColEnd(unsigned int idx) const
167167
return x + GetColumn(idx).GetWidth();
168168
}
169169

170-
unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, bool *onSeparator) const
170+
unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, Region& pos_region) const
171171
{
172172
int pos = 0;
173173
int xLogical = xPhysical - m_scrollOffset;
@@ -178,7 +178,8 @@ unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, bool *onSeparator) c
178178
const wxHeaderColumn& col = GetColumn(idx);
179179
if ( col.IsHidden() )
180180
continue;
181-
181+
182+
const auto last_col_end = pos;
182183
pos += col.GetWidth();
183184

184185
// TODO: don't hardcode sensitivity
@@ -188,33 +189,34 @@ unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, bool *onSeparator) c
188189
// line separating it from the next column
189190
if ( col.IsResizeable() && abs(xLogical - pos) < separatorClickMargin )
190191
{
191-
if ( onSeparator )
192-
*onSeparator = true;
192+
pos_region = Region::Separator;
193193
return idx;
194194
}
195195

196196
// inside this column?
197-
if ( xLogical < pos )
197+
if ( xLogical < pos && xLogical >= last_col_end)
198198
{
199-
if ( onSeparator )
200-
*onSeparator = false;
199+
if ( xLogical - last_col_end < pos - xLogical)
200+
pos_region = Region::LeftHalf;
201+
else
202+
pos_region = Region::RightHalf;
201203
return idx;
202204
}
203205
}
204206

205-
if ( onSeparator )
206-
*onSeparator = false;
207+
pos_region = Region::NoWhere;
207208
return COL_NONE;
208209
}
209210

210-
unsigned int wxHeaderCtrl::FindColumnClosestToPoint(int xPhysical) const
211+
unsigned int wxHeaderCtrl::FindColumnClosestToPoint(int xPhysical, Region& pos_region) const
211212
{
212-
const unsigned int colIndexAtPoint = FindColumnAtPoint(xPhysical);
213-
213+
const unsigned int colIndexAtPoint = FindColumnAtPoint(xPhysical, pos_region);
214+
214215
// valid column found?
215216
if ( colIndexAtPoint != COL_NONE )
216217
return colIndexAtPoint;
217-
218+
219+
pos_region = Region::NoWhere;
218220
// if not, xPhysical must be beyond the rightmost column, so return its
219221
// index instead -- if we have it
220222
const unsigned int count = GetColumnCount();
@@ -224,6 +226,37 @@ unsigned int wxHeaderCtrl::FindColumnClosestToPoint(int xPhysical) const
224226
return m_colIndices[count - 1];
225227
}
226228

229+
unsigned int wxHeaderCtrl::FindColumnAfter(const unsigned int column_idx) const{
230+
const unsigned count = GetColumnCount();
231+
auto after_idx = COL_NONE;
232+
//auto target_column_found = false;
233+
for ( unsigned n = 0; n < count; n++ )
234+
{
235+
if (m_colIndices[n] == column_idx && n + 1 < count ){
236+
after_idx = m_colIndices[n + 1];
237+
break;
238+
}
239+
}
240+
return after_idx;
241+
}
242+
243+
unsigned int wxHeaderCtrl::FindColumnBefore(const unsigned int column_idx) const{
244+
const unsigned count = GetColumnCount();
245+
auto before_idx = COL_NONE;
246+
auto target_column_found = false;
247+
for ( unsigned n = 0; n < count; n++ )
248+
{
249+
if (m_colIndices[n] == column_idx){
250+
target_column_found = true;
251+
break;
252+
}
253+
before_idx = m_colIndices[n];
254+
}
255+
if (not target_column_found)
256+
before_idx = COL_NONE;
257+
return before_idx;
258+
}
259+
227260
// ----------------------------------------------------------------------------
228261
// wxHeaderCtrl repainting
229262
// ----------------------------------------------------------------------------
@@ -389,14 +422,21 @@ void wxHeaderCtrl::UpdateReorderingMarker(int xPhysical)
389422

390423
// and also a hint indicating where it is going to be inserted if it's
391424
// dropped now
392-
unsigned int col = FindColumnClosestToPoint(xPhysical);
425+
auto hover_region = Region::NoWhere;
426+
unsigned int col = FindColumnClosestToPoint(xPhysical, hover_region);
393427
if ( col != COL_NONE )
394428
{
395429
static const int DROP_MARKER_WIDTH = 4;
396430

397431
dc.SetBrush(*wxBLUE);
398-
dc.DrawRectangle(GetColEnd(col) - DROP_MARKER_WIDTH/2, 0,
399-
DROP_MARKER_WIDTH, y);
432+
if (hover_region == Region::LeftHalf){
433+
dc.DrawRectangle(GetColStart(col) - DROP_MARKER_WIDTH/2, 0,
434+
DROP_MARKER_WIDTH, y);
435+
}
436+
else if (hover_region != Region::NoWhere){
437+
dc.DrawRectangle(GetColEnd(col) - DROP_MARKER_WIDTH/2, 0,
438+
DROP_MARKER_WIDTH, y);
439+
}
400440
}
401441
}
402442

@@ -432,10 +472,39 @@ bool wxHeaderCtrl::EndReordering(int xPhysical)
432472
ReleaseMouse();
433473

434474
const int colOld = m_colBeingReordered;
435-
const unsigned colNew = FindColumnClosestToPoint(xPhysical);
475+
auto dropped_region = Region::NoWhere;
476+
unsigned colNew = FindColumnClosestToPoint(xPhysical, dropped_region);
436477

437478
m_colBeingReordered = COL_NONE;
438-
479+
auto reg_str = std::string{};
480+
switch(dropped_region){
481+
case Region::NoWhere:
482+
reg_str = "NoWhere";
483+
break;
484+
case Region::LeftHalf:
485+
reg_str = "LeftHalf";
486+
break;
487+
case Region::RightHalf:
488+
reg_str = "RightHalf";
489+
break;
490+
case Region::Separator:
491+
reg_str = "Separator";
492+
break;
493+
}
494+
//printf("Dropped to col %d, region : %s\n", colNew, reg_str.c_str());
495+
// The actual dropped pos should not simply be colNew, it should also depends on
496+
// which region the user dropped in.
497+
// if the user dropped the col on the RightHalf, the colNew should the one next to it on the right.
498+
auto located_by_previous_col = false;
499+
if ((dropped_region == Region::RightHalf || dropped_region == Region::Separator) && colNew != COL_NONE){
500+
//printf("Looking for the next column pos to inserted for col %d\n", colNew);
501+
auto nextColumn = FindColumnAfter(colNew);
502+
if (nextColumn != COL_NONE){
503+
//printf("Next col for col %d is %d\n", colNew, nextColumn);
504+
colNew = nextColumn;
505+
located_by_previous_col = true;
506+
}
507+
}
439508
// mouse drag must be longer than min distance m_dragOffset
440509
if ( xPhysical - GetColStart(colOld) == m_dragOffset )
441510
{
@@ -447,22 +516,45 @@ bool wxHeaderCtrl::EndReordering(int xPhysical)
447516
{
448517
return false;
449518
}
450-
451519
if ( static_cast<int>(colNew) != colOld )
452520
{
453521
wxHeaderCtrlEvent event(wxEVT_HEADER_END_REORDER, GetId());
454522
event.SetEventObject(this);
455523
event.SetColumn(colOld);
456-
457-
const unsigned pos = GetColumnPos(colNew);
458-
event.SetNewOrder(pos);
459-
460-
if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
461-
{
462-
// do reorder the columns
463-
DoMoveCol(colOld, pos);
524+
auto new_pos = GetColumnPos(colNew);
525+
auto old_pos = GetColumnPos(colOld);
526+
// when the user drag one col from left-to-right(i.e. from low pos to higher one),
527+
// the actual pos to dropped should be the one just before colNew, i.e. the one on the left hand side of colNew.
528+
auto move_left = false;
529+
if (old_pos < new_pos) {
530+
// the last column is a bit special, we should consider it differently.
531+
if (new_pos != GetColumnCount() - 1 || located_by_previous_col || dropped_region == Region::LeftHalf){
532+
colNew = FindColumnBefore(colNew);
533+
assert(colNew != COL_NONE);
534+
new_pos = GetColumnPos(colNew);
535+
}
536+
move_left = true;
537+
}
538+
// Simulate the reorder before we actually accept it.
539+
// Why???
540+
// ToDo: better code comments to explain the logic, a diagram should be better
541+
auto new_colIndices = m_colIndices;
542+
MoveColumnInOrderArray(new_colIndices, colOld, new_pos);
543+
auto old_after_pos = new_colIndices.Index(colNew);
544+
auto new_after_pos = new_colIndices.Index(colOld);
545+
if (old_after_pos != old_pos || new_after_pos != new_pos || move_left || old_pos > new_pos){
546+
event.SetNewOrder(new_pos);
547+
548+
//printf("Move col %d to %d, pos to %d\n", colOld, colNew, new_pos);
549+
if ( GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
550+
{
551+
// do reorder the columns
552+
DoMoveCol(colOld, new_pos);
553+
}
464554
}
465555
}
556+
//else
557+
//printf("ColNew and ColOld the same, do not reordering.\n");
466558

467559
// whether we moved the column or not, the user did move the mouse and so
468560
// did try to do it so return true
@@ -651,10 +743,10 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
651743

652744

653745
// find if the event is over a column at all
654-
bool onSeparator;
746+
Region mouse_region = Region::NoWhere;
655747
const unsigned col = mevent.Leaving()
656-
? (onSeparator = false, COL_NONE)
657-
: FindColumnAtPoint(xPhysical, &onSeparator);
748+
? COL_NONE
749+
: FindColumnAtPoint(xPhysical, mouse_region);
658750

659751

660752
// update the highlighted column if it changed
@@ -670,7 +762,7 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
670762
// update mouse cursor as it moves around
671763
if ( mevent.Moving() )
672764
{
673-
SetCursor(onSeparator ? wxCursor(wxCURSOR_SIZEWE) : wxNullCursor);
765+
SetCursor(mouse_region == Region::Separator ? wxCursor(wxCURSOR_SIZEWE) : wxNullCursor);
674766
return;
675767
}
676768

@@ -682,7 +774,7 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
682774
// enter various dragging modes on left mouse press
683775
if ( mevent.LeftDown() )
684776
{
685-
if ( onSeparator )
777+
if ( mouse_region == Region::Separator )
686778
{
687779
// start resizing the column
688780
wxASSERT_MSG( !IsResizing(), "reentering column resize mode?" );
@@ -712,7 +804,7 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
712804
{
713805
case wxMOUSE_BTN_LEFT:
714806
// treat left double clicks on separator specially
715-
if ( onSeparator && dblclk )
807+
if ( mouse_region == Region::Separator && dblclk )
716808
{
717809
evtType = wxEVT_HEADER_SEPARATOR_DCLICK;
718810
m_wasSeparatorDClick = true;

0 commit comments

Comments
 (0)