Skip to content

Commit d790ac3

Browse files
徐扬斌coremail-cyt
authored andcommitted
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 0b638b0 commit d790ac3

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)