Merge branch 'grid-selection-refactoring'

Completely overhauled selection handling in wxGrid.

Make various ways of extending selection (using Shift-arrow keys,
Ctrl-Shift-arrows, Shift-click etc) work as expected from the user point
of view instead of producing various bizarre results. Also improve
row/column header click selection as well as Ctrl/Shift-Space handling.

Internally, store selection as just a vector of blocks, independently of
the selection mode, and provide a simple API for iterating over it which
remains usable even with selections containing millions of cells (as
long as they're still composed of only a few blocks, which is the case
in practice).

Add more tests and add display of the current selection to the sample.

See https://github.com/wxWidgets/wxWidgets/pull/1772
This commit is contained in:
Vadim Zeitlin
2020-04-15 15:32:09 +02:00
9 changed files with 2322 additions and 1502 deletions

View File

@@ -1816,6 +1816,250 @@ public:
bool operator!() const;
};
/**
Represents coordinates of a block of cells in the grid.
An object of this class contains coordinates of the left top and the bottom right
corners of a block.
@since 3.1.4
*/
class wxGridBlockCoords
{
public:
/**
Default constructor initializes the object to invalid state.
Initially the coordinates are invalid (-1) and so operator!() for an
uninitialized wxGridBlockCoords returns @true.
*/
wxGridBlockCoords();
/**
Constructor taking a coordinates of the left top and the bottom right
corners.
*/
wxGridBlockCoords(int topRow, int leftCol, int bottomRow, int rightCol);
/**
Return the row of the left top corner.
*/
int GetTopRow() const;
/**
Set the row of the left top corner.
*/
void SetTopRow(int row);
/**
Return the column of the left top corner.
*/
int GetLeftCol() const;
/**
Set the column of the left top corner.
*/
void SetLeftCol(int col);
/**
Return the row of the bottom right corner.
*/
int GetBottomRow() const;
/**
Set the row of the bottom right corner.
*/
void SetBottomRow(int row);
/**
Return the column of the bottom right corner.
*/
int GetRightCol() const;
/**
Set the column of the bottom right corner.
*/
void SetRightCol(int col);
/**
Return the coordinates of the top left corner.
*/
wxGridCellCoords GetTopLeft() const
{
return wxGridCellCoords(m_topRow, m_leftCol);
}
/**
Return the coordinates of the bottom right corner.
*/
wxGridCellCoords GetBottomRight() const
{
return wxGridCellCoords(m_bottomRow, m_rightCol);
}
/**
Return the canonicalized block where top left coordinates is less
then bottom right coordinates.
*/
wxGridBlockCoords Canonicalize() const;
/**
Whether the blocks intersect.
@return
@true, if the block intersects with the other, @false, otherwise.
*/
bool Intersects(const wxGridBlockCoords& other) const
{
return m_topRow <= other.m_bottomRow && m_bottomRow >= other.m_topRow &&
m_leftCol <= other.m_rightCol && m_rightCol >= other.m_leftCol;
}
/**
Check whether this block contains the given cell.
@return
@true, if the block contains the cell, @false otherwise.
*/
bool ContainsCell(const wxGridCellCoords& cell) const;
/**
Check whether this block contains another one.
@return
@true if @a other is entirely contained within this block.
*/
bool ContainsBlock(const wxGridBlockCoords& other) const;
/**
Calculates the result blocks by subtracting the other block from this
block.
@param other
The block to subtract from this block.
@param splitOrientation
The block splitting orientation (either @c wxHORIZONTAL or
@c wxVERTICAL).
@return
Up to 4 blocks. If block doesn't exist in the result, it has value
of @c wxGridNoBlockCoords.
*/
wxGridBlockDiffResult
Difference(const wxGridBlockCoords& other, int splitOrientation) const;
/**
Calculates the symmetric difference of the blocks.
@param other
The block to subtract from this block.
@return
Up to 4 blocks. If block doesn't exist in the result, it has value
of @c wxGridNoBlockCoords.
*/
wxGridBlockDiffResult
SymDifference(const wxGridBlockCoords& other) const;
/**
Equality operator.
*/
bool operator==(const wxGridBlockCoords& other) const;
/**
Inequality operator.
*/
bool operator!=(const wxGridBlockCoords& other) const;
/**
Checks whether the cells block is invalid.
Returns @true only if all components are -1. Notice that if one
of the components (but not all) is -1, this method returns @false even
if the object is invalid. This is done because objects in such state
should actually never exist, i.e. either all components should be -1
or none of them should be -1.
*/
bool operator!() const;
private:
int m_topRow;
int m_leftCol;
int m_bottomRow;
int m_rightCol;
};
/**
@class wxGridBlockDiffResult
The helper struct uses as a result type for difference functions of
@c wxGridBlockCoords class.
Parts can be uninitialized (equals to @c wxGridNoBlockCoords), that means
that the corresponding part doesn't exists in the result.
@since 3.1.4
*/
struct wxGridBlockDiffResult
{
wxGridBlockCoords m_parts[4];
};
/**
Represents a collection of grid blocks that can be iterated over.
This class provides read-only access to the blocks making up the grid
selection in the most general case.
Note that objects of this class can only be returned by wxGrid, but not
constructed in the application code.
The preferable way to iterate over it is using C++11 range-for loop:
@code
for ( const auto& block: grid->GetSelectedBlocks() ) {
... do something with block ...
}
@endcode
When not using C++11, iteration has to be done manually:
@code
wxGridBlocks range = grid->GetSelectedBlocks();
for ( wxGridBlocks::iterator it = range.begin();
it != range.end();
++it ) {
... do something with *it ...
}
@endcode
@since 3.1.4
*/
class wxGridBlocks
{
public:
/**
Read-only forward iterator type.
This is an opaque type, which satisfies the forward iterator
requirements, i.e. provides all the expected operations, such as
comparison, dereference and pre- and post-increment.
*/
class iterator
{
iterator();
const wxGridBlockCoords& operator*() const;
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& it) const;
bool operator!=(const iterator& it) const;
};
/// Return iterator corresponding to the beginning of the range.
iterator begin() const;
/// Return iterator corresponding to the end of the range.
iterator end() const;
};
/**
@class wxGridTableBase
@@ -4419,6 +4663,21 @@ public:
*/
void DeselectCell( int row, int col );
/**
Returns a range of grid selection blocks.
The returned range can be iterated over, e.g. with C++11 range-for loop:
@code
for ( const auto block: grid->GetSelectedBlocks() ) {
if ( block.Intersects(myBlock) )
break;
}
@endcode
@since 3.1.4
*/
wxGridBlocks GetSelectedBlocks() const;
/**
Returns an array of individually selected cells.
@@ -4434,6 +4693,9 @@ public:
a grid with a million of columns, we don't want to create an array with
a million of entries in this function, instead it returns an empty
array and GetSelectedCols() returns an array containing one element).
The function can be slow for the big grids, use GetSelectedBlocks()
in the new code.
*/
wxGridCellCoordsArray GetSelectedCells() const;
@@ -4445,6 +4707,9 @@ public:
individually selected but not those being part of the block selection
or being selected in virtue of all of their cells being selected
individually, please see GetSelectedCells() for more details.
The function can be slow for the big grids, use GetSelectedBlocks()
in the new code.
*/
wxArrayInt GetSelectedCols() const;
@@ -4456,6 +4721,9 @@ public:
selected but not those being part of the block selection or being
selected in virtue of all of their cells being selected individually,
please see GetSelectedCells() for more details.
The function can be slow for the big grids, use GetSelectedBlocks()
in the new code.
*/
wxArrayInt GetSelectedRows() const;
@@ -4471,6 +4739,9 @@ public:
Please see GetSelectedCells() for more information about the selection
representation in wxGrid.
The function can be slow for the big grids, use GetSelectedBlocks()
in the new code.
@see GetSelectionBlockTopLeft()
*/
wxGridCellCoordsArray GetSelectionBlockBottomRight() const;
@@ -4481,6 +4752,9 @@ public:
Please see GetSelectedCells() for more information about the selection
representation in wxGrid.
The function can be slow for the big grids, use GetSelectedBlocks()
in the new code.
@see GetSelectionBlockBottomRight()
*/
wxGridCellCoordsArray GetSelectionBlockTopLeft() const;
@@ -4641,6 +4915,17 @@ public:
*/
void MakeCellVisible(const wxGridCellCoords& coords);
/**
Returns the topmost row of the current visible area.
Returns -1 if the grid doesn't have any rows.
*/
int GetFirstFullyVisibleRow() const;
/**
Returns the leftmost column of the current visible area.
Returns -1 if the grid doesn't have any columns.
*/
int GetFirstFullyVisibleColumn() const;
/**
Sets the number of pixels per horizontal scroll increment.