From 1208aa116a8c4dd2b0d6103939a12b8a732d64f1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 20 Jun 2020 19:52:43 +0200 Subject: [PATCH 01/16] Accept space as starting key for editing numbers in wxGrid Not being able to use space for starting editing the values of the numeric columns was inconsistent with most (if not all) the other ones and so surprising and inconvenient. Make space work for these columns too, but just ignore it, i.e. in particular do not erase the current cell contents when it's pressed, to avoid changing the old behaviour too much. --- src/generic/grideditors.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index ae7eff24ae..af1a11cae1 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -836,10 +836,19 @@ bool wxGridCellNumberEditor::IsAcceptedKey(wxKeyEvent& event) if ( wxGridCellEditor::IsAcceptedKey(event) ) { int keycode = event.GetKeyCode(); - if ( (keycode < 128) && - (wxIsdigit(keycode) || keycode == '+' || keycode == '-')) + switch ( keycode ) { - return true; + // Accept +/- because they can be part of the number and space just + // because it's a convenient key to start editing with and is also + // consistent with many (all?) other editors, which allow starting + // editing using it. + case '+': + case '-': + case ' ': + return true; + + default: + return (keycode < 128) && wxIsdigit(keycode); } } From 3fa942795ea10d26388335aeda841fa1c3e9e8e2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 21 Jun 2020 01:13:55 +0200 Subject: [PATCH 02/16] Avoid annoying beep when using Alt-Backspace in MSW text controls Although the native (single line) text control understands Alt-Backspace as an accelerator for "Undo", using it was not really practical because it resulted in an annoying beep due to Alt-anything trying to find an item with matching mnemonic in the menu. Work around this native (mis)feature by pretending to handle this particular Alt-combination ourselves when the currently focused control is text-like, as indicated by it overriding WXGetTextEntry() -- this is not ideal, but seems to be the best we can currently do. An alternative could be to just mark Alt-Backspace as handled unconditionally. --- src/msw/toplevel.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 4c552f2604..aeabf46f43 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -290,6 +290,22 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX DoRestoreLastFocus(); } + else if ( id == SC_KEYMENU ) + { + // Alt-Backspace is understood as an accelerator for "Undo" + // by the native EDIT control, but pressing it results in a + // beep by default when the resulting SC_KEYMENU is handled + // by DefWindowProc(), so pretend to handle it ourselves if + // we're editing a text control to avoid the annoying beep. + if ( lParam == VK_BACK ) + { + if ( wxWindow* const focus = FindFocus() ) + { + if ( focus->WXGetTextEntry() ) + processed = true; + } + } + } #ifndef __WXUNIVERSAL__ // We need to generate events for the custom items added to the From 867cc2a3ebc7d844c2c662fb694f11220aaf98fc Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 21 Jun 2020 19:23:53 +0200 Subject: [PATCH 03/16] Handle clicks on grid edges normally when not using drag-resizing Clicking on (or near) the grid column or row edges was handled specially to allow dragging them in order to resize the column or row, but this doesn't need to be done if drag-resizing the columns or rows is not allowed in the first place and resulted in surprising user-visible behaviour: e.g. when using row selection, clicking mostly anywhere in the row selected it, except if the click happened to be between the two columns, in which case it didn't. Fix this and always select the row in such scenario now. Unfortunately, doing this required adding yet more CanDragXXX() functions in addition to the already impressive panoply of them in wxGrid, but we need CanDragGridColEdges() as none of the existing functions checked for m_useNativeHeader (there was instead an ad hoc check for it directly in the mouse handling code) and the row version had to be added for symmetry. --- include/wx/generic/grid.h | 22 +++++++++++++++++++++- interface/wx/grid.h | 20 ++++++++++++++++++++ src/generic/grid.cpp | 22 +++++++++++++++------- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 948aba4d0b..3215eece14 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1675,7 +1675,27 @@ public: void DisableRowResize(int row) { DoDisableLineResize(row, m_setFixedRows); } void DisableColResize(int col) { DoDisableLineResize(col, m_setFixedCols); } - // these functions return whether the given row/column can be + // These function return true if resizing rows/columns by dragging + // their edges inside the grid is enabled. Note that this doesn't cover + // dragging their separators in the label windows (which can be enabled + // for the columns even if dragging inside the grid is not), nor checks + // whether a particular row/column is resizeable or not, so you should + // always check CanDrag{Row,Col}Size() below too. + bool CanDragGridRowEdges() const + { + return m_canDragGridSize && m_canDragRowSize; + } + + bool CanDragGridColEdges() const + { + // When using the native header window we can only resize the columns by + // dragging the dividers in the header itself, but not by dragging them + // in the grid because we can't make the native control enter into the + // column resizing mode programmatically. + return m_canDragGridSize && m_canDragColSize && !m_useNativeHeader; + } + + // These functions return whether the given row/column can be // effectively resized: for this interactive resizing must be enabled // and this index must not have been passed to DisableRow/ColResize() bool CanDragRowSize(int row) const diff --git a/interface/wx/grid.h b/interface/wx/grid.h index 24d2dc8e0e..aa9a6b4a45 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -4322,6 +4322,26 @@ public: */ bool CanDragColSize(int col) const; + /** + Return @true if column edges inside the grid can be dragged to resize + the rows. + + @see CanDragGridSize(), CanDragColSize() + + @since 3.1.4 + */ + bool CanDragGridColEdges() const; + + /** + Return @true if row edges inside the grid can be dragged to resize the + rows. + + @see CanDragGridSize(), CanDragRowSize() + + @since 3.1.4 + */ + bool CanDragGridRowEdges() const; + /** Return @true if the dragging of grid lines to resize rows and columns is enabled or @false otherwise. diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index ec8aeaddbc..5a92397095 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -4577,8 +4577,20 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event, MakeCellVisible(coords); } } - else if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) + else { + // Clicking on (or very near) the separating lines shouldn't change the + // selection when it's used for resizing -- but should still do it if + // resizing is disabled (notice that we intentionally don't check for + // it being disabled for a particular row/column as it would be + // surprising to have different mouse behaviour in different parts of + // the same grid, so we only check for it being globally disabled). + if ( CanDragGridColEdges() && XToEdgeOfCol(pos.x) != wxNOT_FOUND ) + return; + + if ( CanDragGridRowEdges() && YToEdgeOfRow(pos.y) != wxNOT_FOUND ) + return; + DisableCellEditControl(); MakeCellVisible( coords ); @@ -4718,7 +4730,7 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), return; } - if ( dragRow >= 0 && CanDragGridSize() && CanDragRowSize(dragRow) ) + if ( dragRow >= 0 && CanDragGridRowEdges() && CanDragRowSize(dragRow) ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { @@ -4726,11 +4738,7 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); } } - // When using the native header window we can only resize the columns by - // dragging the dividers in it because we can't make it enter into the - // column resizing mode programmatically - else if ( dragCol >= 0 && !m_useNativeHeader && - CanDragGridSize() && CanDragColSize(dragCol) ) + else if ( dragCol >= 0 && CanDragGridColEdges() && CanDragColSize(dragCol) ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { From bf266ca348f7a26e00af89161f3172dcd490b589 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 21 Jun 2020 00:29:55 +0200 Subject: [PATCH 04/16] Avoid unnecessarily refreshing native header in wxGrid We don't need to refresh it after resizing, it takes care of itself and calling Refresh() just results in extra flicker. See #18794. --- src/generic/grid.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 5a92397095..50fc4c49e8 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -9844,7 +9844,10 @@ void wxGrid::DoSetColSize( int col, int width ) FurtherWindowPartRefresher refreshFurtherPart(x); - refreshFurtherPart(m_colLabelWin); + // Refreshing the native header is unnecessary, as it updates + // itself correctly anyhow, and just results in extra flicker. + if ( !IsUsingNativeHeader() ) + refreshFurtherPart(m_colLabelWin); refreshFurtherPart(m_gridWin); if ( m_frozenRowGridWin ) From f8a0438385784b61ad546d1a26f00aa0f230f2cf Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 21 Jun 2020 23:46:59 +0200 Subject: [PATCH 05/16] Also avoid updating wxHeaderCtrl column when resizing in wxGrid This is another attempt to get rid of the flicker when using the native header control with wxGrid under MSW and avoid calling UpdateColumn(), which is currently implemented in a very inefficient way in wxHeaderCtrl under MSW, during interactive resizing. See #18794. --- include/wx/generic/private/grid.h | 24 ++++++++++++++++++++++++ src/generic/grid.cpp | 11 ++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index 709cf0c167..8628824814 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -152,6 +152,14 @@ public: (owner->CanHideColumns() ? wxHD_ALLOW_HIDE : 0) | (owner->CanDragColMove() ? wxHD_ALLOW_REORDER : 0)) { + m_inResizing = 0; + } + + // Special method to call from wxGrid::DoSetColSize(), see comments there. + void UpdateIfNotResizing(unsigned int idx) + { + if ( !m_inResizing ) + UpdateColumn(idx); } protected: @@ -259,7 +267,20 @@ private: void OnResizing(wxHeaderCtrlEvent& event) { + // Calling wxGrid method results in a call to our own UpdateColumn() + // because it ends up in wxGrid::SetColSize() which must indeed update + // the column when it's called by the program -- but in the case where + // the size change comes from the column itself, it is useless and, in + // fact, harmful, as it results in extra flicker due to the inefficient + // implementation of UpdateColumn() in wxMSW wxHeaderCtrl, so skip + // calling it from our overridden version by setting this flag for the + // duration of this function execution and checking it in our + // UpdateIfNotResizing(). + m_inResizing++; + GetOwner()->DoHeaderDragResizeCol(event.GetWidth()); + + m_inResizing--; } void OnEndResize(wxHeaderCtrlEvent& event) @@ -281,6 +302,9 @@ private: wxVector m_columns; + // The count of OnResizing() call nesting, 0 if not inside it. + int m_inResizing; + wxDECLARE_EVENT_TABLE(); wxDECLARE_NO_COPY_CLASS(wxGridHeaderCtrl); }; diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 50fc4c49e8..2cf6315d41 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -9765,7 +9765,16 @@ void wxGrid::DoSetColSize( int col, int width ) return; if ( m_useNativeHeader ) - GetGridColHeader()->UpdateColumn(col); + { + // We have to update the native control if we're called from the + // program (directly or indirectly, e.g. via AutoSizeColumn()), but we + // want to avoid doing it when the column is being resized + // interactively, as this is unnecessary and results in very visible + // flicker, so take care to call the special method of our header + // control checking for whether it's being resized interactively + // instead of the usual UpdateColumn(). + static_cast(m_colLabelWin)->UpdateIfNotResizing(col); + } //else: will be refreshed when the header is redrawn for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ ) From 097f1a17cbf87ebf184322bfea1388596766cb0e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 01:53:59 +0200 Subject: [PATCH 06/16] Fix positioning of grid editor inside the grid client area The existing logic for adjusting the editor window position by moving it was flawed as it didn't take into account the fact that the editor could decide to use a different (and usually bigger) rectangle than the one we provided it with, if it doesn't fit into the given one. Fix it to ensure that the editor window is fully inside the grid client area by moving it only after letting it choose its own size. The existing code was also inconsistent as CalcDimensions() didn't take any adjustment to the cell rectangle done in ShowCellEditControl() into account at all and always just blithely assumed it was positioned at the cell top left corner, which resulted in wrong virtual size calculation and could make scrollbars appear when starting editing, as happened in the "Bugs table" example of the grid sample when showing any comboboxes in the last grid row. Fix this code to use the actual editor rectangle instead. --- src/generic/grid.cpp | 55 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 2cf6315d41..c2bcc35305 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -3015,22 +3015,17 @@ void wxGrid::CalcDimensions() // take into account editor if shown if ( IsCellEditControlShown() ) { - int w2, h2; int r = m_currentCellCoords.GetRow(); int c = m_currentCellCoords.GetCol(); - int x = GetColLeft(c); - int y = GetRowTop(r); // how big is the editor wxGridCellAttrPtr attr = GetCellAttrPtr(r, c); wxGridCellEditorPtr editor = attr->GetEditorPtr(this, r, c); - editor->GetWindow()->GetSize(&w2, &h2); - w2 += x; - h2 += y; - if ( w2 > w ) - w = w2; - if ( h2 > h ) - h = h2; + const wxRect rect = editor->GetWindow()->GetRect(); + if ( rect.GetRight() > w ) + w = rect.GetRight(); + if ( rect.GetBottom() > h ) + h = rect.GetBottom(); } wxPoint offset = GetGridWindowOffset(m_gridWin); @@ -7284,10 +7279,6 @@ void wxGrid::ShowCellEditControl() // convert to scrolled coords CalcGridWindowScrolledPosition( rect.x, rect.y, &rect.x, &rect.y, gridWindow ); - int nXMove = 0; - if (rect.x < 0) - nXMove = rect.x; - #ifdef __WXQT__ // Substract 1 pixel in every dimension to fit in the cell area. // If not, Qt will draw the control outside the cell. @@ -7336,10 +7327,6 @@ void wxGrid::ShowCellEditControl() maxWidth = rect.width; } - int client_right = gridWindow->GetClientSize().GetWidth(); - if (rect.x + maxWidth > client_right) - maxWidth = client_right - rect.x; - if ((maxWidth > rect.width) && (col < m_numCols) && m_table) { GetCellSize( row, col, &cell_rows, &cell_cols ); @@ -7358,17 +7345,35 @@ void wxGrid::ShowCellEditControl() else break; } - - if (rect.GetRight() > client_right) - rect.SetRight( client_right - 1 ); } editor->SetCellAttr( attr.get() ); editor->SetSize( rect ); - if (nXMove != 0) - editor->GetWindow()->Move( - editor->GetWindow()->GetPosition().x + nXMove, - editor->GetWindow()->GetPosition().y ); + + // Note that the actual rectangle used by the editor could be + // different from the one we proposed. + rect = editor->GetWindow()->GetRect(); + + // Ensure that the edit control fits into the visible part of the + // window by shifting it if necessary: we don't want to truncate + // any part of it by trying to position it too far to the left or + // top and we definitely don't want to start showing scrollbars by + // positioning it too far to the right or bottom. + const wxSize sizeMax = gridWindow->GetClientSize(); + if ( !wxRect(sizeMax).Contains(rect) ) + { + if ( rect.x < 0 ) + rect.x = 0; + if ( rect.y < 0 ) + rect.y = 0; + if ( rect.x > sizeMax.x - rect.width ) + rect.x = sizeMax.x - rect.width; + if ( rect.y > sizeMax.y - rect.height ) + rect.y = sizeMax.y - rect.height; + + editor->GetWindow()->Move(rect.x, rect.y); + } + editor->Show( true, attr.get() ); // recalc dimensions in case we need to From 74377ecf3813115b2d51efae462017220fc070b1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 02:18:21 +0200 Subject: [PATCH 07/16] Center date editor vertically in the grid Do it for consistency with the other editors and because it looks better when the date picker is taller than the line height. --- src/generic/grideditors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index af1a11cae1..35e64bb03d 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1800,7 +1800,7 @@ void wxGridCellDateEditor::SetSize(const wxRect& r) rect.SetWidth(wxMin(r.GetWidth(), 2*bestSize.GetWidth())); } - wxGridCellEditor::SetSize(rect); + wxGridCellEditor::SetSize(rect.CenterIn(r, wxVERTICAL)); } void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid) From f80ff6f459f9412fcd51c8bca9d2f7a27cd15f3b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 02:21:14 +0200 Subject: [PATCH 08/16] Tweak width of the date editor Don't make it larger than 150% than its normal width and also don't make it larger than its normal width at all if it's not going to be large enough to cover the entire cell anyhow -- this just looks strange, as the editor is both too wide and not wide enough. --- src/generic/grideditors.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 35e64bb03d..8c114143d9 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1793,11 +1793,12 @@ void wxGridCellDateEditor::SetSize(const wxRect& r) wxRect rect(r.GetPosition(), bestSize); - // Allow edit picker to become a bit wider, if necessary, but no more than - // twice as wide as the best width, otherwise they just look ugly. - if ( r.GetWidth() > bestSize.GetWidth() ) + // Allow date picker to become a bit wider, if necessary, but not too wide, + // otherwise it just looks ugly. + if ( r.GetWidth() > bestSize.GetWidth() + && r.GetWidth() < 3*bestSize.GetWidth()/2 ) { - rect.SetWidth(wxMin(r.GetWidth(), 2*bestSize.GetWidth())); + rect.SetWidth(r.GetWidth()); } wxGridCellEditor::SetSize(rect.CenterIn(r, wxVERTICAL)); From 6fb85af1f37ea1e234c0ab4e777e82d428e08d9c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 02:33:07 +0200 Subject: [PATCH 09/16] Simplify centering wxGridCellNumberEditor vertically There is no need to check that the rectangle fits as it's done by wxGrid itself, so just use a single wxRect::CenterIn() call instead of several lines doing it manually. No real changes. --- src/generic/grideditors.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 8c114143d9..6880a63e81 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -705,20 +705,7 @@ void wxGridCellNumberEditor::SetSize(const wxRect& rectCell) wxRect rectSpin(rectCell.GetPosition(), size); - // If possible, i.e. if we're not editing the topmost or leftmost cell, - // center the control rectangle in the cell. - if ( rectCell.GetTop() > 0 ) - { - rectSpin.SetTop(rectCell.GetTop() - - (rectSpin.GetHeight() - rectCell.GetHeight()) / 2); - } - if ( rectCell.GetLeft() > 0 ) - { - rectSpin.SetLeft(rectCell.GetLeft() - - (rectSpin.GetWidth() - rectCell.GetWidth()) / 2); - } - - wxGridCellEditor::SetSize(rectSpin); + wxGridCellEditor::SetSize(rectSpin.CenterIn(rectCell, wxVERTICAL)); } else #endif // wxUSE_SPINCTRL From a204981d742be538473f484ef8187b7af3ee423e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 02:39:12 +0200 Subject: [PATCH 10/16] Add a test of wxSpinCtrl-based editor to the grid sample Just show an example of wxGridCellNumberEditor with range limits in action to make testing it simpler. --- samples/grid/griddemo.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index 5e314e21b7..e03cbc9841 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -565,6 +565,8 @@ GridFrame::GridFrame() grid->SetCellValue(0, 9, "Integer\ncolumn"); grid->SetCellValue(1, 9, "17"); grid->SetCellValue(2, 9, "0"); + grid->SetCellEditor(2, 9, new wxGridCellNumberEditor(0, 100)); + grid->SetCellValue(2, 10, "<- This cell uses [0, 100] range"); grid->SetCellValue(3, 9, "-666"); grid->SetCellAlignment(3, 9, wxALIGN_CENTRE, wxALIGN_TOP); grid->SetCellValue(3, 10, "<- This numeric cell should be centred"); From dddcdf62df680e7d6e0020f88c8aec750dcdab15 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 02:42:32 +0200 Subject: [PATCH 11/16] Don't expand wxChoice-based grid editor vertically This just looks very strange if the row has much bigger height than default and it already isn't done for other editors, e.g. those using wxSpinCtrl or wxDatePickerCtrl, for the same reason, so this is also more consistent. --- src/generic/grideditors.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 6880a63e81..259dcfff3b 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1447,12 +1447,16 @@ void wxGridCellChoiceEditor::SetSize(const wxRect& rect) wxASSERT_MSG(m_control, wxT("The wxGridCellChoiceEditor must be created first!")); - // Check that the rectangle is big enough to fit the combobox, we can't - // afford truncating it. - wxSize size = rect.GetSize(); - size.IncTo(m_control->GetBestSize()); + // Use normal wxChoice size, except for extending it to fill the cell + // width: we can't be smaller because this could make the control unusable + // and we don't want to be taller because this looks unusual and weird. + wxSize size = m_control->GetBestSize(); + if ( size.x < rect.width ) + size.x = rect.width; - wxGridCellEditor::SetSize(wxRect(size).CentreIn(rect)); + wxRect rectChoice(rect.GetPosition(), size); + + wxGridCellEditor::SetSize(rectChoice.CenterIn(rect, wxVERTICAL)); } void wxGridCellChoiceEditor::PaintBackground(wxDC& dc, From 32edcde5bdd6057f26b74996697b909c1ca7cde8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 03:05:52 +0200 Subject: [PATCH 12/16] Remove unused wxGridCellChoiceEditor::PaintBackground() override This overridden method didn't do anything except calling the base class version, so just remove it. --- include/wx/generic/grideditors.h | 4 ---- src/generic/grideditors.cpp | 13 ------------- 2 files changed, 17 deletions(-) diff --git a/include/wx/generic/grideditors.h b/include/wx/generic/grideditors.h index 0abe429f88..6e53a89dd7 100644 --- a/include/wx/generic/grideditors.h +++ b/include/wx/generic/grideditors.h @@ -307,10 +307,6 @@ public: virtual void SetSize(const wxRect& rect) wxOVERRIDE; - virtual void PaintBackground(wxDC& dc, - const wxRect& rectCell, - const wxGridCellAttr& attr) wxOVERRIDE; - virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE; virtual bool EndEdit(int row, int col, const wxGrid* grid, const wxString& oldval, wxString *newval) wxOVERRIDE; diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 259dcfff3b..bff361f195 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1459,19 +1459,6 @@ void wxGridCellChoiceEditor::SetSize(const wxRect& rect) wxGridCellEditor::SetSize(rectChoice.CenterIn(rect, wxVERTICAL)); } -void wxGridCellChoiceEditor::PaintBackground(wxDC& dc, - const wxRect& rectCell, - const wxGridCellAttr& attr) -{ - // as we fill the entire client area, don't do anything here to minimize - // flicker - - // TODO: It doesn't actually fill the client area since the height of a - // combo always defaults to the standard. Until someone has time to - // figure out the right rectangle to paint, just do it the normal way. - wxGridCellEditor::PaintBackground(dc, rectCell, attr); -} - void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, From 7e79925fb70a9e327795c7929458efafc05e73d4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 22 Jun 2020 03:06:41 +0200 Subject: [PATCH 13/16] Do erase background even in wxGridCellTextEditor It is necessary to do it since the switch to double buffering wxGrid painting in ebbadae09a (Double buffer wxGridWindow drawing, 2020-01-28) as even a "full cell" editor such as wxGridCellTextEditor still doesn't fill the entire cell, as there are margins around it, and the backing bitmap could keep whatever junk happened to be there if we didn't erase it, so do erase it now. Remove the code doing the same thing from ShowCellEditControl(), however, as it's redundant and doesn't do anything except creating some flicker, and also doesn't work on the platforms not supporting the use of wxClientDC anyhow. --- include/wx/generic/grideditors.h | 4 ---- src/generic/grid.cpp | 10 +--------- src/generic/grideditors.cpp | 8 -------- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/include/wx/generic/grideditors.h b/include/wx/generic/grideditors.h index 6e53a89dd7..4ffd61ef62 100644 --- a/include/wx/generic/grideditors.h +++ b/include/wx/generic/grideditors.h @@ -60,10 +60,6 @@ public: wxEvtHandler* evtHandler) wxOVERRIDE; virtual void SetSize(const wxRect& rect) wxOVERRIDE; - virtual void PaintBackground(wxDC& dc, - const wxRect& rectCell, - const wxGridCellAttr& attr) wxOVERRIDE; - virtual bool IsAcceptedKey(wxKeyEvent& event) wxOVERRIDE; virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE; virtual bool EndEdit(int row, int col, const wxGrid* grid, diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index c2bcc35305..ee845891df 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -7265,15 +7265,6 @@ void wxGrid::ShowCellEditControl() m_currentCellCoords.SetCol( col ); } - // erase the highlight and the cell contents because the editor - // might not cover the entire cell - wxClientDC dc( gridWindow ); - PrepareDCFor(dc, gridWindow); - wxGridCellAttrPtr attr = GetCellAttrPtr(row, col); - dc.SetBrush(wxBrush(attr->GetBackgroundColour())); - dc.SetPen(*wxTRANSPARENT_PEN); - dc.DrawRectangle(rect); - rect.Offset(-GetGridWindowOffset(gridWindow)); // convert to scrolled coords @@ -7286,6 +7277,7 @@ void wxGrid::ShowCellEditControl() rect.Deflate(1, 1); #endif + wxGridCellAttrPtr attr = GetCellAttrPtr(row, col); wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col); if ( !editor->IsCreated() ) { diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index bff361f195..0524b997d5 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -426,14 +426,6 @@ void wxGridCellTextEditor::DoCreate(wxWindow* parent, wxGridCellEditor::Create(parent, id, evtHandler); } -void wxGridCellTextEditor::PaintBackground(wxDC& WXUNUSED(dc), - const wxRect& WXUNUSED(rectCell), - const wxGridCellAttr& WXUNUSED(attr)) -{ - // as we fill the entire client area, - // don't do anything here to minimize flicker -} - void wxGridCellTextEditor::SetSize(const wxRect& rectOrig) { wxRect rect(rectOrig); From c4fef08d39419598c5c76539355385df09db2620 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 25 Jun 2020 22:51:05 +0200 Subject: [PATCH 14/16] Disallow resizing rows by dragging cell corners if disabled Even when resizing rows by dragging their edges was disabled via DisableDragRowSize(), it was still possible to try to resize them when the mouse was over the cell corner. Fix this and remove the special case for the corners entirely, it's not necessary as the existing cases cover this one already. Just rearrange them in the right order to prefer column resizing, as this seems to be a much more commonly used operation than row resizing. --- src/generic/grid.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index ee845891df..4423bfa055 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -4716,24 +4716,10 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), int dragRow = YToEdgeOfRow( pos.y ); int dragCol = XToEdgeOfCol( pos.x ); - // Dragging on the corner of a cell to resize in both - // directions is not implemented yet... - // - if ( dragRow >= 0 && dragCol >= 0 ) - { - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); - return; - } - - if ( dragRow >= 0 && CanDragGridRowEdges() && CanDragRowSize(dragRow) ) - { - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - DoStartResizeRowOrCol(dragRow); - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); - } - } - else if ( dragCol >= 0 && CanDragGridColEdges() && CanDragColSize(dragCol) ) + // Dragging on the corner of a cell to resize in both directions is not + // implemented, so choose to resize the column when the cursor is over the + // cell corner, as this is a more common operation. + if ( dragCol >= 0 && CanDragGridColEdges() && CanDragColSize(dragCol) ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { @@ -4741,6 +4727,14 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, gridWindow, false); } } + else if ( dragRow >= 0 && CanDragGridRowEdges() && CanDragRowSize(dragRow) ) + { + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + { + DoStartResizeRowOrCol(dragRow); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); + } + } else // Neither on a row or col edge { if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) From 4b9161f546a82f3357f9501cddf2f2ac43c8f3ac Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 26 Jun 2020 02:16:07 +0200 Subject: [PATCH 15/16] Make header cells of numeric columns read only in the sample This avoid asserts when trying to edit them, as the corresponding editors expect the cell to have a numeric value. --- samples/grid/griddemo.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index e03cbc9841..282c5c68d7 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -543,12 +543,14 @@ GridFrame::GridFrame() // Some numeric columns with different formatting. grid->SetColFormatFloat(6); + grid->SetReadOnly(0, 6); grid->SetCellValue(0, 6, "Default\nfloat format"); grid->SetCellValue(1, 6, wxString::Format("%g", 3.1415)); grid->SetCellValue(2, 6, wxString::Format("%g", 1415.0)); grid->SetCellValue(3, 6, wxString::Format("%g", 12345.67890)); grid->SetColFormatFloat(7, 6, 2); + grid->SetReadOnly(0, 7); grid->SetCellValue(0, 7, "Width 6\nprecision 2"); grid->SetCellValue(1, 7, wxString::Format("%g", 3.1415)); grid->SetCellValue(2, 7, wxString::Format("%g", 1415.0)); @@ -556,12 +558,14 @@ GridFrame::GridFrame() grid->SetColFormatCustom(8, wxString::Format("%s:%i,%i,%s", wxGRID_VALUE_FLOAT, -1, 4, "g")); + grid->SetReadOnly(0, 8); grid->SetCellValue(0, 8, "Compact\nformat"); grid->SetCellValue(1, 8, "31415e-4"); grid->SetCellValue(2, 8, "1415"); grid->SetCellValue(3, 8, "123456789e-4"); grid->SetColFormatNumber(9); + grid->SetReadOnly(0, 9); grid->SetCellValue(0, 9, "Integer\ncolumn"); grid->SetCellValue(1, 9, "17"); grid->SetCellValue(2, 9, "0"); @@ -571,9 +575,11 @@ GridFrame::GridFrame() grid->SetCellAlignment(3, 9, wxALIGN_CENTRE, wxALIGN_TOP); grid->SetCellValue(3, 10, "<- This numeric cell should be centred"); + grid->SetReadOnly(0, 13); grid->SetCellValue(0, 13, "Localized date\ncolumn"); grid->SetColFormatDate(13); // Localized by default. grid->SetCellValue(1, 13, "Today"); + grid->SetReadOnly(0, 14); grid->SetCellValue(0, 14, "ISO 8601 date\ncolumn"); grid->SetColFormatDate(14, "%Y-%m-%d"); // ISO 8601 date format. grid->SetCellValue(1, 14, "Tomorrow"); From f5647828d0ba2a4aecb316ffbf6084457cadf46f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 26 Jun 2020 02:20:51 +0200 Subject: [PATCH 16/16] Respect cell alignment when positioning the editors Define the common logic for positioning editors not taking the entire cell area (i.e. basically anything other than wxGridCellTextEditor) in the new DoPositionEditor() function. Also use the cell and editor alignment to decide where to position the control if it's smaller than the cell, as it looks better if e.g. wxGridCellDateEditor appears near the place where the date is displayed instead of being centered in the middle of a wide column. --- include/wx/generic/grid.h | 10 +++++ src/generic/grideditors.cpp | 77 ++++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 3215eece14..a0d1dae75c 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -339,6 +339,16 @@ protected: // the dtor is private because only DecRef() can delete us virtual ~wxGridCellEditor(); + // Helper for the derived classes positioning the control according to the + // attribute alignment if the desired control size is smaller than the cell + // size, or centering it vertically if its size is bigger: this looks like + // the best compromise when the editor control doesn't fit into the cell. + void DoPositionEditor(const wxSize& size, + const wxRect& rectCell, + int hAlign = wxALIGN_LEFT, + int vAlign = wxALIGN_CENTRE_VERTICAL); + + // the actual window we show on screen (this variable should actually be // named m_window, but m_control is kept for backward compatibility) wxWindow* m_control; diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 0524b997d5..1bc48c46e2 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -331,6 +331,62 @@ void wxGridCellEditor::SetSize(const wxRect& rect) m_control->SetSize(rect, wxSIZE_ALLOW_MINUS_ONE); } +void wxGridCellEditor::DoPositionEditor(const wxSize& size, + const wxRect& rectCell, + int hAlign, + int vAlign) +{ + wxRect rect(rectCell.GetPosition(), size); + + // We center the control around the cell if it doesn't fit into it in one + // or both of directions, as this seems to look the best (difference is + // typically relatively small and by centering it, we divide it by two on + // each side, making it even smaller). + // + // For now just remember in which direction to do it in this variable and + // then do it at the end. + int centerDir = 0; + + // We're only going to need the alignment if the control is smaller than + // the cell in at least one direction. + if ( size.x < rectCell.width || size.y < rectCell.height ) + { + if ( GetCellAttr() ) + GetCellAttr()->GetNonDefaultAlignment(&hAlign, &vAlign); + } + + if ( size.x < rectCell.width ) + { + if ( hAlign == wxALIGN_CENTER_HORIZONTAL ) + centerDir |= wxHORIZONTAL; + else if ( hAlign == wxALIGN_RIGHT ) + rect.x = rectCell.x + rectCell.width - rect.width; + //else: nothing to do for the left alignment + } + else + { + centerDir |= wxHORIZONTAL; + } + + if ( size.y < rectCell.height ) + { + if ( vAlign == wxALIGN_CENTRE_VERTICAL ) + centerDir |= wxVERTICAL; + else if ( vAlign == wxALIGN_BOTTOM ) + rect.y = rectCell.y + rectCell.height - rect.height; + //else: nothing to do for the top alignment + } + else + { + centerDir |= wxVERTICAL; + } + + if ( centerDir ) + rect = rect.CenterIn(rectCell, centerDir); + + wxGridCellEditor::SetSize(rect); +} + void wxGridCellEditor::HandleReturn(wxKeyEvent& event) { event.Skip(); @@ -695,9 +751,7 @@ void wxGridCellNumberEditor::SetSize(const wxRect& rectCell) if ( size.y <= 0 ) size.y = rectCell.GetHeight(); - wxRect rectSpin(rectCell.GetPosition(), size); - - wxGridCellEditor::SetSize(rectSpin.CenterIn(rectCell, wxVERTICAL)); + DoPositionEditor(size, rectCell); } else #endif // wxUSE_SPINCTRL @@ -1446,9 +1500,7 @@ void wxGridCellChoiceEditor::SetSize(const wxRect& rect) if ( size.x < rect.width ) size.x = rect.width; - wxRect rectChoice(rect.GetPosition(), size); - - wxGridCellEditor::SetSize(rectChoice.CenterIn(rect, wxVERTICAL)); + DoPositionEditor(size, rect); } void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid) @@ -1759,19 +1811,18 @@ void wxGridCellDateEditor::SetSize(const wxRect& r) { wxASSERT_MSG(m_control, "The wxGridCellDateEditor must be created first!"); - const wxSize bestSize = DatePicker()->GetBestSize(); - - wxRect rect(r.GetPosition(), bestSize); + wxSize size = DatePicker()->GetBestSize(); // Allow date picker to become a bit wider, if necessary, but not too wide, // otherwise it just looks ugly. - if ( r.GetWidth() > bestSize.GetWidth() - && r.GetWidth() < 3*bestSize.GetWidth()/2 ) + if ( r.GetWidth() > size.GetWidth() + && r.GetWidth() < 3*size.GetWidth()/2 ) { - rect.SetWidth(r.GetWidth()); + size.x = r.GetWidth(); } - wxGridCellEditor::SetSize(rect.CenterIn(r, wxVERTICAL)); + // Use right alignment by default for consistency with the date renderer. + DoPositionEditor(size, r, wxALIGN_RIGHT); } void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid)