From 53ffbf6cf58523b5ea1a2c3a3bc5d94a6b4cb1a2 Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Thu, 7 Nov 2019 01:13:32 +0700 Subject: [PATCH 01/17] Allow ignoring margins in wxRendererNative::GetCheckBoxSize() Add ability to get the size of the checkbox without any margins by passing wxCONTROL_CELL flag: this can be useful when the checkbox is part of some "cell", e.g. wxGrid one, and doesn't need any extra margins around it. Currently wxCONTROL_CELL is only really used by wxGTK2 implementation. --- include/wx/renderer.h | 6 +++--- interface/wx/renderer.h | 9 ++++++-- src/generic/renderg.cpp | 6 +++--- src/gtk/renderer.cpp | 42 ++++++++++++++++++++++++++++++------- src/msw/renderer.cpp | 10 ++++----- src/osx/carbon/renderer.cpp | 4 ++-- 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/include/wx/renderer.h b/include/wx/renderer.h index 4a4b92375a..da22e391cb 100644 --- a/include/wx/renderer.h +++ b/include/wx/renderer.h @@ -259,7 +259,7 @@ public: int flags = 0) = 0; // Returns the default size of a check box. - virtual wxSize GetCheckBoxSize(wxWindow *win) = 0; + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) = 0; // Returns the default size of a check mark. virtual wxSize GetCheckMarkSize(wxWindow *win) = 0; @@ -496,8 +496,8 @@ public: int flags = 0) wxOVERRIDE { m_rendererNative.DrawCheckMark( win, dc, rect, flags ); } - virtual wxSize GetCheckBoxSize(wxWindow *win) wxOVERRIDE - { return m_rendererNative.GetCheckBoxSize(win); } + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) wxOVERRIDE + { return m_rendererNative.GetCheckBoxSize(win, flags); } virtual wxSize GetCheckMarkSize(wxWindow *win) wxOVERRIDE { return m_rendererNative.GetCheckMarkSize(win); } diff --git a/interface/wx/renderer.h b/interface/wx/renderer.h index 0cb0685fc7..c73779682a 100644 --- a/interface/wx/renderer.h +++ b/interface/wx/renderer.h @@ -237,7 +237,7 @@ public: virtual void DrawCheckMark(wxWindow *win, wxDC& dc, const wxRect& rect, int flags = 0 ); - virtual wxSize GetCheckBoxSize(wxWindow *win); + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0); virtual wxSize GetCheckMarkSize(wxWindow *win); @@ -573,8 +573,13 @@ public: @param win A valid, i.e. non-null, window pointer which is used to get the theme defining the checkbox size under some platforms. + + @param flags The only acceptable flag is @c wxCONTROL_CELL which means + that just the size of the checkbox itself is returned, without any + margins that are included by default. This parameter is only + available in wxWidgets 3.1.4 or later. */ - virtual wxSize GetCheckBoxSize(wxWindow* win) = 0; + virtual wxSize GetCheckBoxSize(wxWindow* win, int flags = 0) = 0; /** Returns the size of a check mark. diff --git a/src/generic/renderg.cpp b/src/generic/renderg.cpp index 4a328ffaa8..b8fc63a3c0 100644 --- a/src/generic/renderg.cpp +++ b/src/generic/renderg.cpp @@ -111,7 +111,7 @@ public: const wxRect& rect, int flags = 0) wxOVERRIDE; - virtual wxSize GetCheckBoxSize(wxWindow *win) wxOVERRIDE; + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) wxOVERRIDE; virtual wxSize GetCheckMarkSize(wxWindow *win) wxOVERRIDE; @@ -731,7 +731,7 @@ wxRendererGeneric::DrawCheckMark(wxWindow *WXUNUSED(win), dc.DrawCheckMark(rect); } -wxSize wxRendererGeneric::GetCheckBoxSize(wxWindow *win) +wxSize wxRendererGeneric::GetCheckBoxSize(wxWindow *win, int WXUNUSED(flags)) { wxCHECK_MSG( win, wxSize(0, 0), "Must have a valid window" ); @@ -740,7 +740,7 @@ wxSize wxRendererGeneric::GetCheckBoxSize(wxWindow *win) wxSize wxRendererGeneric::GetCheckMarkSize(wxWindow *win) { - return GetCheckBoxSize(win); + return GetCheckBoxSize(win, wxCONTROL_CELL); } wxSize wxRendererGeneric::GetExpanderSize(wxWindow *win) diff --git a/src/gtk/renderer.cpp b/src/gtk/renderer.cpp index e2dddc134a..9aa438b8ee 100644 --- a/src/gtk/renderer.cpp +++ b/src/gtk/renderer.cpp @@ -133,7 +133,7 @@ public: virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0) wxOVERRIDE; - virtual wxSize GetCheckBoxSize(wxWindow *win) wxOVERRIDE; + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) wxOVERRIDE; virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win) wxOVERRIDE; }; @@ -569,7 +569,7 @@ static void CheckBoxSize(wxGtkStyleContext& sc, int& w, int& h, GtkBorder* extra #endif // __WXGTK3__ wxSize -wxRendererGTK::GetCheckBoxSize(wxWindow* win) +wxRendererGTK::GetCheckBoxSize(wxWindow* win, int flags) { wxSize size; // Even though we don't use the window in this implementation, still check @@ -578,6 +578,8 @@ wxRendererGTK::GetCheckBoxSize(wxWindow* win) wxCHECK_MSG(win, size, "Must have a valid window"); #ifdef __WXGTK3__ + wxUnusedVar(flags); + wxGtkStyleContext sc(win->GetContentScaleFactor()); sc.AddCheckButton(); if (gtk_check_version(3,20,0) == NULL) @@ -597,13 +599,27 @@ wxRendererGTK::GetCheckBoxSize(wxWindow* win) g_value_unset(&value); } #else // !__WXGTK3__ - gint indicator_size, indicator_spacing; + gint indicator_size, indicator_spacing, focus_width, focus_pad; gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(), "indicator_size", &indicator_size, "indicator_spacing", &indicator_spacing, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, NULL); - size.x = size.y = indicator_size + indicator_spacing * 2; + size.x = indicator_size + indicator_spacing * 2; + + // If wxCONTROL_CELL is set then we want to get the size of wxCheckBox + // control to draw the check mark centered and at the same position as + // wxCheckBox do. So we should add margins instead of removing. + // See gtk_real_check_button_draw_indicator: + // https://github.com/GNOME/gtk/blob/GTK_2_16_0/gtk/gtkcheckbutton.c#L374 + if ( flags & wxCONTROL_CELL ) + { + size.x += 2 * (focus_width + focus_pad); + } + + size.y = size.x; #endif // !__WXGTK3__ return size; @@ -618,10 +634,12 @@ wxRendererGTK::DrawCheckBox(wxWindow*, #ifndef __WXGTK3__ GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget(); - gint indicator_size, indicator_spacing; + gint indicator_size, indicator_spacing, focus_width, focus_pad; gtk_widget_style_get(button, "indicator_size", &indicator_size, "indicator_spacing", &indicator_spacing, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, NULL); GtkStateType state; @@ -710,6 +728,16 @@ wxRendererGTK::DrawCheckBox(wxWindow*, if (gdk_window == NULL) return; + gint offsetX = indicator_spacing; + + // If wxCONTROL_CELL is set then we want to draw the check mark + // at the same position as wxCheckBox do. + // See wxRendererGTK::GetCheckBoxSize. + if ( flags & wxCONTROL_CELL ) + { + offsetX += focus_width + focus_pad; + } + gtk_paint_check ( gtk_widget_get_style(button), @@ -719,8 +747,8 @@ wxRendererGTK::DrawCheckBox(wxWindow*, NULL, button, "cellcheck", - dc.LogicalToDeviceX(rect.x) + indicator_spacing, - dc.LogicalToDeviceY(rect.y) + indicator_spacing, + dc.LogicalToDeviceX(rect.x) + offsetX, + dc.LogicalToDeviceY(rect.y) + (rect.height - indicator_size) / 2, indicator_size, indicator_size ); #endif diff --git a/src/msw/renderer.cpp b/src/msw/renderer.cpp index 6645283603..71ca2a489b 100644 --- a/src/msw/renderer.cpp +++ b/src/msw/renderer.cpp @@ -162,7 +162,7 @@ public: wxTitleBarButton button, int flags = 0) wxOVERRIDE; - virtual wxSize GetCheckBoxSize(wxWindow *win) wxOVERRIDE; + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) wxOVERRIDE; virtual int GetHeaderButtonHeight(wxWindow *win) wxOVERRIDE; @@ -288,7 +288,7 @@ public: wxTitleBarButton button, int flags = 0) wxOVERRIDE; - virtual wxSize GetCheckBoxSize(wxWindow *win) wxOVERRIDE; + virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) wxOVERRIDE; virtual wxSize GetCheckMarkSize(wxWindow* win) wxOVERRIDE; @@ -548,7 +548,7 @@ wxRendererMSW::DrawTitleBarBitmap(wxWindow *win, DoDrawFrameControl(DFC_CAPTION, kind, win, dc, rect, flags); } -wxSize wxRendererMSW::GetCheckBoxSize(wxWindow* win) +wxSize wxRendererMSW::GetCheckBoxSize(wxWindow* win, int WXUNUSED(flags)) { // We must have a valid window in order to return the size which is correct // for the display this window is on. @@ -893,7 +893,7 @@ wxRendererXP::DrawTitleBarBitmap(wxWindow *win, DoDrawButtonLike(hTheme, part, dc, rect, flags); } -wxSize wxRendererXP::GetCheckBoxSize(wxWindow* win) +wxSize wxRendererXP::GetCheckBoxSize(wxWindow* win, int flags) { wxCHECK_MSG( win, wxSize(0, 0), "Must have a valid window" ); @@ -907,7 +907,7 @@ wxSize wxRendererXP::GetCheckBoxSize(wxWindow* win) return wxSize(checkSize.cx, checkSize.cy); } } - return m_rendererNative.GetCheckBoxSize(win); + return m_rendererNative.GetCheckBoxSize(win, flags); } wxSize wxRendererXP::GetCheckMarkSize(wxWindow* win) diff --git a/src/osx/carbon/renderer.cpp b/src/osx/carbon/renderer.cpp index a7bc8dcc06..933ca65ebf 100644 --- a/src/osx/carbon/renderer.cpp +++ b/src/osx/carbon/renderer.cpp @@ -90,7 +90,7 @@ public: const wxRect& rect, int flags = 0) wxOVERRIDE; - virtual wxSize GetCheckBoxSize(wxWindow* win) wxOVERRIDE; + virtual wxSize GetCheckBoxSize(wxWindow* win, int flags = 0) wxOVERRIDE; virtual void DrawComboBoxDropButton(wxWindow *win, wxDC& dc, @@ -491,7 +491,7 @@ wxRendererMac::DrawCheckBox(wxWindow *win, kind, kThemeAdornmentNone); } -wxSize wxRendererMac::GetCheckBoxSize(wxWindow* win) +wxSize wxRendererMac::GetCheckBoxSize(wxWindow* win, int WXUNUSED(flags)) { // Even though we don't use the window in this implementation, still check // that it's valid to avoid surprises when running the same code under the From 6a21d6f2e401cf77f4d5fd74d127096fc4390ac6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Nov 2019 01:03:26 +0100 Subject: [PATCH 02/17] Add missing required header to wx/generic/private/grid.h Make this header self-sufficient, instead of requiring wx/headerctrl.h to be included before including it. Remove the now unnecessary wx/headerctrl.h inclusion from src/generic/grideditors.cpp. --- include/wx/generic/private/grid.h | 2 ++ src/generic/grideditors.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index cd92f64fe5..c67f462902 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -15,6 +15,8 @@ #if wxUSE_GRID +#include "wx/headerctrl.h" + // Internally used (and hence intentionally not exported) event telling wxGrid // to hide the currently shown editor. wxDECLARE_EVENT( wxEVT_GRID_HIDE_EDITOR, wxCommandEvent ); diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index f8799997e3..e5b008697b 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -37,7 +37,6 @@ #include "wx/spinctrl.h" #include "wx/tokenzr.h" #include "wx/renderer.h" -#include "wx/headerctrl.h" #include "wx/datectrl.h" #include "wx/generic/gridsel.h" From 3ca9491c5f2ca782958d9ad76d392e2babde8846 Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Fri, 1 Nov 2019 04:02:26 +0700 Subject: [PATCH 03/17] Improve grid editors placing Remove the code in wxGrid::ShowCellEditControl() which moves grid editors unnecessarily and also remove workarounds that were required because of it in the editors SetSize() functions. This helps to ensure that the editor is placed at the same position the renderer draws the cell value, so that it doesn't jump around annoyingly when editing starts (which was especially noticeable for boolean-valued cells). --- include/wx/generic/private/grid.h | 7 ++ src/generic/grid.cpp | 55 +++++++++++++--- src/generic/gridctrl.cpp | 49 ++++---------- src/generic/grideditors.cpp | 106 ++++++------------------------ 4 files changed, 84 insertions(+), 133 deletions(-) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index c67f462902..d0c01a16af 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -1007,5 +1007,12 @@ private: wxGridDataTypeInfoArray m_typeinfo; }; +// Returns the rect of the check box in a cell with the given alignmens +// and the size. +// The function is used by wxGridCellBoolEditor and wxGridCellBoolRenderer. +wxRect wxGetGridCheckBoxRect(const wxSize& checkBoxSize, + const wxRect& cellRect, + int hAlign, int vAlign); + #endif // wxUSE_GRID #endif // _WX_GENERIC_GRID_PRIVATE_H_ diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index f940fbbd40..2648f750a9 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -111,6 +111,9 @@ const int GRID_HASH_SIZE = 100; // operation const int DRAG_SENSITIVITY = 3; +// the space between the cell edge and the checkbox mark +const int GRID_CELL_CHECKBOX_MARGIN_X = 2; + } // anonymous namespace #include "wx/arrimpl.cpp" @@ -6980,18 +6983,10 @@ void wxGrid::ShowCellEditControl() if (rect.x < 0) nXMove = rect.x; -#ifndef __WXQT__ - // cell is shifted by one pixel - // However, don't allow x or y to become negative - // since the SetSize() method interprets that as - // "don't change." - if (rect.x > 0) - rect.x--; - if (rect.y > 0) - rect.y--; -#else +#ifdef __WXQT__ // Substract 1 pixel in every dimension to fit in the cell area. // If not, Qt will draw the control outside the cell. + // TODO: Check offsets under Qt. rect.Deflate(1, 1); #endif @@ -10337,4 +10332,44 @@ wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) return editor; } +wxRect wxGetGridCheckBoxRect(const wxSize& checkBoxSize, + const wxRect& cellRect, + int hAlign, int WXUNUSED(vAlign)) +{ + // TODO: support vAlign + + wxRect checkBoxRect; + checkBoxRect.SetY(cellRect.y + cellRect.height / 2 - checkBoxSize.y / 2); + + wxCoord minSize = wxMin(cellRect.width, cellRect.height); + if ( checkBoxRect.GetWidth() >= minSize || checkBoxRect.GetHeight() >= minSize ) + { + // let the checkbox mark be even smaller than the min size + // to leave some space between cell edges and the checkbox + const int newSize = wxMax(1, minSize - 2); + checkBoxRect.SetWidth(newSize); + checkBoxRect.SetHeight(newSize); + } + else + { + checkBoxRect.SetSize(checkBoxSize); + } + + if ( hAlign & wxALIGN_CENTER_HORIZONTAL ) + { + checkBoxRect.SetX(cellRect.x + cellRect.width / 2 - checkBoxSize.x / 2); + } + else if ( hAlign & wxALIGN_RIGHT ) + { + checkBoxRect.SetX(cellRect.x + cellRect.width + - checkBoxSize.x - GRID_CELL_CHECKBOX_MARGIN_X); + } + else // ( hAlign == wxALIGN_LEFT ) and invalid alignment value + { + checkBoxRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN_X); + } + + return checkBoxRect; +} + #endif // wxUSE_GRID diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index f6df548502..a72c4b1674 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -31,6 +31,7 @@ #include "wx/tokenzr.h" #include "wx/renderer.h" +#include "wx/generic/private/grid.h" // ---------------------------------------------------------------------------- // wxGridCellRenderer @@ -936,7 +937,8 @@ wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, // compute it only once (no locks for MT safeness in GUI thread...) if ( !ms_sizeCheckMark.x ) { - ms_sizeCheckMark = wxRendererNative::Get().GetCheckBoxSize(&grid); + ms_sizeCheckMark = + wxRendererNative::Get().GetCheckBoxSize(&grid, wxCONTROL_CELL); } return ms_sizeCheckMark; @@ -951,43 +953,16 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, { wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); - // draw a check mark in the centre (ignoring alignment - TODO) - wxSize size = GetBestSize(grid, attr, dc, row, col); - - // don't draw outside the cell - wxCoord minSize = wxMin(rect.width, rect.height); - if ( size.x >= minSize || size.y >= minSize ) - { - // and even leave (at least) 1 pixel margin - size.x = size.y = minSize; - } - - // draw a border around checkmark int vAlign, hAlign; attr.GetAlignment(&hAlign, &vAlign); - wxRect rectBorder; - if (hAlign == wxALIGN_CENTRE) - { - rectBorder.x = rect.x + rect.width / 2 - size.x / 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } - else if (hAlign == wxALIGN_LEFT) - { - rectBorder.x = rect.x + 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } - else if (hAlign == wxALIGN_RIGHT) - { - rectBorder.x = rect.x + rect.width - size.x - 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } + const wxRect + checkBoxRect = wxGetGridCheckBoxRect + ( + GetBestSize(grid, attr, dc, row, col), + rect, + hAlign, vAlign + ); bool value; if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) @@ -1000,11 +975,11 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, value = wxGridCellBoolEditor::IsTrueValue(cellval); } - int flags = 0; + int flags = wxCONTROL_CELL; if (value) flags |= wxCONTROL_CHECKED; - wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); + wxRendererNative::Get().DrawCheckBox( &grid, dc, checkBoxRect, flags ); } #endif // wxUSE_GRID diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index e5b008697b..08954cc7fe 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -440,38 +440,17 @@ void wxGridCellTextEditor::SetSize(const wxRect& rectOrig) // Make the edit control large enough to allow for internal margins // - // TODO: remove this if the text ctrl sizing is improved esp. for unix + // TODO: remove this if the text ctrl sizing is improved // -#if defined(__WXGTK__) - if (rect.x != 0) - { - rect.x += 1; - rect.y += 1; - rect.width -= 1; - rect.height -= 1; - } -#elif defined(__WXMSW__) - if ( rect.x == 0 ) - rect.x += 2; - else - rect.x += 3; - - if ( rect.y == 0 ) - rect.y += 2; - else - rect.y += 3; +#if defined(__WXMSW__) + rect.x += 2; + rect.y += 2; rect.width -= 2; rect.height -= 2; -#elif defined(__WXOSX__) - rect.x += 1; - rect.y += 1; - - rect.width -= 1; - rect.height -= 1; -#else - int extra_x = ( rect.x > 2 ) ? 2 : 1; - int extra_y = ( rect.y > 2 ) ? 2 : 1; +#elif !defined(__WXGTK__) + int extra_x = 2; + int extra_y = 2; #if defined(__WXMOTIF__) extra_x *= 2; @@ -1239,73 +1218,28 @@ void wxGridCellBoolEditor::Create(wxWindow* parent, void wxGridCellBoolEditor::SetSize(const wxRect& r) { - bool resize = false; - wxSize size = m_control->GetSize(); - wxCoord minSize = wxMin(r.width, r.height); - - // check if the checkbox is not too big/small for this cell - wxSize sizeBest = m_control->GetBestSize(); - if ( !(size == sizeBest) ) - { - // reset to default size if it had been made smaller - size = sizeBest; - - resize = true; - } - - if ( size.x >= minSize || size.y >= minSize ) - { - // leave 1 pixel margin - size.x = size.y = minSize - 2; - - resize = true; - } - - if ( resize ) - { - m_control->SetSize(size); - } - - // position it in the centre of the rectangle (TODO: support alignment?) - -#if defined(__WXGTK__) || defined (__WXMOTIF__) - // the checkbox without label still has some space to the right in wxGTK, - // so shift it to the right - size.x -= 8; -#elif defined(__WXMSW__) - // here too, but in other way - size.x += 1; - size.y -= 2; -#endif - int hAlign = wxALIGN_CENTRE; int vAlign = wxALIGN_CENTRE; if (GetCellAttr()) GetCellAttr()->GetAlignment(& hAlign, & vAlign); - int x = 0, y = 0; - if (hAlign == wxALIGN_LEFT) - { - x = r.x + 2; + const wxRect + checkBoxRect = wxGetGridCheckBoxRect + ( + wxRendererNative::Get(). + GetCheckBoxSize(GetWindow(), wxCONTROL_CELL), + r, + hAlign, vAlign + ); -#ifdef __WXMSW__ - x += 2; -#endif - - y = r.y + r.height / 2 - size.y / 2; - } - else if (hAlign == wxALIGN_RIGHT) + // resize the control if required + if ( m_control->GetSize() != checkBoxRect.GetSize() ) { - x = r.x + r.width - size.x - 2; - y = r.y + r.height / 2 - size.y / 2; - } - else if (hAlign == wxALIGN_CENTRE) - { - x = r.x + r.width / 2 - size.x / 2; - y = r.y + r.height / 2 - size.y / 2; + m_control->SetSize(checkBoxRect.GetSize()); } - m_control->Move(x, y); + // and move it + m_control->Move(checkBoxRect.GetPosition()); } void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr) From dacf25ac99e3ca2a78d254ea0991ba2a2d68f194 Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Sun, 10 Nov 2019 19:19:09 +0700 Subject: [PATCH 04/17] Support wxNO_BORDER for wxCheckBox under GTK2 --- src/gtk/checkbox.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gtk/checkbox.cpp b/src/gtk/checkbox.cpp index d11c7ef205..d33908be4d 100644 --- a/src/gtk/checkbox.cpp +++ b/src/gtk/checkbox.cpp @@ -146,6 +146,11 @@ bool wxCheckBox::Create(wxWindow *parent, g_object_ref(m_widget); SetLabel( label ); + if ( style & wxNO_BORDER ) + { + gtk_container_set_border_width(GTK_CONTAINER(m_widgetCheckbox), 0); + } + g_signal_connect (m_widgetCheckbox, "toggled", G_CALLBACK (gtk_checkbox_toggled_callback), this); From f4756eaa2f29171381bcf8a81c9108629a6e4c7c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 02:49:40 +0100 Subject: [PATCH 05/17] Centre checkboxes in wxGrid vertically by default Make the alignment used by default in wxGridCellBoolRenderer and wxGridCellBoolEditor consistent and centre the checkbox vertically in both of them: previously only the editor tried to do it, but failed, because the code wrongly overwrote the default alignment as it used GetAlignment() instead of GetNonDefaultAlignment(), while the renderer didn't even try. --- src/generic/gridctrl.cpp | 5 +++-- src/generic/grideditors.cpp | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index a72c4b1674..ea0d6d7a15 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -953,8 +953,9 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, { wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); - int vAlign, hAlign; - attr.GetAlignment(&hAlign, &vAlign); + int hAlign = wxALIGN_LEFT; + int vAlign = wxALIGN_CENTRE_VERTICAL; + attr.GetNonDefaultAlignment(&hAlign, &vAlign); const wxRect checkBoxRect = wxGetGridCheckBoxRect diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 08954cc7fe..65755042c9 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1218,10 +1218,10 @@ void wxGridCellBoolEditor::Create(wxWindow* parent, void wxGridCellBoolEditor::SetSize(const wxRect& r) { - int hAlign = wxALIGN_CENTRE; - int vAlign = wxALIGN_CENTRE; + int hAlign = wxALIGN_LEFT; + int vAlign = wxALIGN_CENTRE_VERTICAL; if (GetCellAttr()) - GetCellAttr()->GetAlignment(& hAlign, & vAlign); + GetCellAttr()->GetNonDefaultAlignment(&hAlign, &vAlign); const wxRect checkBoxRect = wxGetGridCheckBoxRect From 851d11ba2cf7ae6677960ffa350122e3aac20110 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 01:07:20 +0100 Subject: [PATCH 06/17] Determine the checkbox size in wxGetGridCheckBoxRect() itself It doesn't make much sense to pass the size to the function supposed to compute it, so call wxRendererNative::GetCheckBoxSize() from the function itself instead of forcing its callers to do it. No real changes. --- include/wx/generic/private/grid.h | 16 ++++++++++------ src/generic/grid.cpp | 10 +++++++--- src/generic/gridctrl.cpp | 12 +++++------- src/generic/grideditors.cpp | 8 +------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index d0c01a16af..35225b6a52 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -1007,12 +1007,16 @@ private: wxGridDataTypeInfoArray m_typeinfo; }; -// Returns the rect of the check box in a cell with the given alignmens -// and the size. -// The function is used by wxGridCellBoolEditor and wxGridCellBoolRenderer. -wxRect wxGetGridCheckBoxRect(const wxSize& checkBoxSize, - const wxRect& cellRect, - int hAlign, int vAlign); +// Returns the rectangle for showing a check box in a cell with the given +// alignment. +// +// The function is used by wxGridCellBoolEditor and wxGridCellBoolRenderer to +// draw a check mark and position wxCheckBox respectively. +wxRect +wxGetGridCheckBoxRect(wxWindow* win, + const wxRect& cellRect, + int hAlign, + int vAlign); #endif // wxUSE_GRID #endif // _WX_GENERIC_GRID_PRIVATE_H_ diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 2648f750a9..bae0b7d7b5 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -10332,13 +10332,17 @@ wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) return editor; } -wxRect wxGetGridCheckBoxRect(const wxSize& checkBoxSize, +wxRect wxGetGridCheckBoxRect(wxWindow* win, const wxRect& cellRect, - int hAlign, int WXUNUSED(vAlign)) + int hAlign, + int WXUNUSED(vAlign)) { - // TODO: support vAlign + const wxSize checkBoxSize = + wxRendererNative::Get().GetCheckBoxSize(win, wxCONTROL_CELL); wxRect checkBoxRect; + + // TODO: support vAlign checkBoxRect.SetY(cellRect.y + cellRect.height / 2 - checkBoxSize.y / 2); wxCoord minSize = wxMin(cellRect.width, cellRect.height); diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index ea0d6d7a15..29e3056da5 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -937,8 +937,11 @@ wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, // compute it only once (no locks for MT safeness in GUI thread...) if ( !ms_sizeCheckMark.x ) { + // Use rectangle big enough for the check box to fit into it. + const wxRect r(0, 0, 1000, 1000); + ms_sizeCheckMark = - wxRendererNative::Get().GetCheckBoxSize(&grid, wxCONTROL_CELL); + wxGetGridCheckBoxRect(&grid, r, wxALIGN_LEFT, wxALIGN_TOP).GetSize(); } return ms_sizeCheckMark; @@ -958,12 +961,7 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, attr.GetNonDefaultAlignment(&hAlign, &vAlign); const wxRect - checkBoxRect = wxGetGridCheckBoxRect - ( - GetBestSize(grid, attr, dc, row, col), - rect, - hAlign, vAlign - ); + checkBoxRect = wxGetGridCheckBoxRect(&grid, rect, hAlign, vAlign); bool value; if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 65755042c9..c07686f469 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1224,13 +1224,7 @@ void wxGridCellBoolEditor::SetSize(const wxRect& r) GetCellAttr()->GetNonDefaultAlignment(&hAlign, &vAlign); const wxRect - checkBoxRect = wxGetGridCheckBoxRect - ( - wxRendererNative::Get(). - GetCheckBoxSize(GetWindow(), wxCONTROL_CELL), - r, - hAlign, vAlign - ); + checkBoxRect = wxGetGridCheckBoxRect(GetWindow(), r, hAlign, vAlign); // resize the control if required if ( m_control->GetSize() != checkBoxRect.GetSize() ) From 04b6fc2f9079f5376ed8c7f09bf41c72768fcd42 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 01:06:45 +0100 Subject: [PATCH 07/17] Add another cell with boolean renderer/editor to the sample No real changes, just test the boolean cells appearance with the default white background too. --- samples/grid/griddemo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index aa6f348e5e..bc68dae5dc 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -521,6 +521,10 @@ GridFrame::GridFrame() grid->SetCellEditor(3, 0, new wxGridCellBoolEditor); grid->SetCellBackgroundColour(3, 0, wxColour(255, 127, 127)); + grid->SetCellRenderer(3, 1, new wxGridCellBoolRenderer); + grid->SetCellEditor(3, 1, new wxGridCellBoolEditor); + grid->SetCellValue(3, 1, "1"); + wxGridCellAttr *attr; attr = new wxGridCellAttr; attr->SetTextColour(*wxBLUE); From 4f6b29fb0c781c3bf0a2d26d2c401b1fdfd309b1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 01:30:32 +0100 Subject: [PATCH 08/17] Remove "X" suffix from GRID_CELL_CHECKBOX_MARGIN_X The same margin will be used in the vertical direction too. --- src/generic/grid.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index bae0b7d7b5..b8ba71be7b 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -112,7 +112,7 @@ const int GRID_HASH_SIZE = 100; const int DRAG_SENSITIVITY = 3; // the space between the cell edge and the checkbox mark -const int GRID_CELL_CHECKBOX_MARGIN_X = 2; +const int GRID_CELL_CHECKBOX_MARGIN = 2; } // anonymous namespace @@ -10366,11 +10366,11 @@ wxRect wxGetGridCheckBoxRect(wxWindow* win, else if ( hAlign & wxALIGN_RIGHT ) { checkBoxRect.SetX(cellRect.x + cellRect.width - - checkBoxSize.x - GRID_CELL_CHECKBOX_MARGIN_X); + - checkBoxSize.x - GRID_CELL_CHECKBOX_MARGIN); } else // ( hAlign == wxALIGN_LEFT ) and invalid alignment value { - checkBoxRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN_X); + checkBoxRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN); } return checkBoxRect; From 57f89c626c991a48807ef1ee9909ac39f671009a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 01:31:31 +0100 Subject: [PATCH 09/17] Fix the code which tried to account for too small cell rect It didn't actually manage to do it as it used wrong comparison. --- src/generic/grid.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index b8ba71be7b..4c80aee83b 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -10337,28 +10337,23 @@ wxRect wxGetGridCheckBoxRect(wxWindow* win, int hAlign, int WXUNUSED(vAlign)) { - const wxSize checkBoxSize = + wxSize checkBoxSize = wxRendererNative::Get().GetCheckBoxSize(win, wxCONTROL_CELL); - wxRect checkBoxRect; - - // TODO: support vAlign - checkBoxRect.SetY(cellRect.y + cellRect.height / 2 - checkBoxSize.y / 2); - - wxCoord minSize = wxMin(cellRect.width, cellRect.height); - if ( checkBoxRect.GetWidth() >= minSize || checkBoxRect.GetHeight() >= minSize ) + // Keep square aspect ratio for the checkbox, but ensure that it fits into + // the available space, even if it's smaller than the standard size. + const wxCoord minSize = wxMin(cellRect.width, cellRect.height); + if ( checkBoxSize.x >= minSize || checkBoxSize.y >= minSize ) { - // let the checkbox mark be even smaller than the min size - // to leave some space between cell edges and the checkbox - const int newSize = wxMax(1, minSize - 2); - checkBoxRect.SetWidth(newSize); - checkBoxRect.SetHeight(newSize); - } - else - { - checkBoxRect.SetSize(checkBoxSize); + // It must still have positive size, however. + const int fittingSize = wxMax(1, minSize - 2*GRID_CELL_CHECKBOX_MARGIN); + + checkBoxSize.x = + checkBoxSize.y = fittingSize; } + wxRect checkBoxRect(checkBoxSize); + if ( hAlign & wxALIGN_CENTER_HORIZONTAL ) { checkBoxRect.SetX(cellRect.x + cellRect.width / 2 - checkBoxSize.x / 2); @@ -10373,6 +10368,9 @@ wxRect wxGetGridCheckBoxRect(wxWindow* win, checkBoxRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN); } + // TODO: support vAlign + checkBoxRect.SetY(cellRect.y + cellRect.height / 2 - checkBoxSize.y / 2); + return checkBoxRect; } From 6b63016fb2aaa93a5ce7531013fd70a4fa94b581 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 01:33:31 +0100 Subject: [PATCH 10/17] Use helper wxRect::CentreIn() No real changes, just make the code a bit more concise and clear. --- src/generic/grid.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 4c80aee83b..8c0bc850e7 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -10356,7 +10356,7 @@ wxRect wxGetGridCheckBoxRect(wxWindow* win, if ( hAlign & wxALIGN_CENTER_HORIZONTAL ) { - checkBoxRect.SetX(cellRect.x + cellRect.width / 2 - checkBoxSize.x / 2); + checkBoxRect = checkBoxRect.CentreIn(cellRect, wxHORIZONTAL); } else if ( hAlign & wxALIGN_RIGHT ) { @@ -10369,7 +10369,7 @@ wxRect wxGetGridCheckBoxRect(wxWindow* win, } // TODO: support vAlign - checkBoxRect.SetY(cellRect.y + cellRect.height / 2 - checkBoxSize.y / 2); + checkBoxRect = checkBoxRect.CentreIn(cellRect, wxVERTICAL); return checkBoxRect; } From ab02d36e102fd4d680e292a27c4db0388bb6ff24 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 22 Nov 2019 01:35:11 +0100 Subject: [PATCH 11/17] Account for vertical alignment in wxGetGridCheckBoxRect() too Do it if only for consistency with the horizontal alignment. --- src/generic/grid.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 8c0bc850e7..bfbaf2b352 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -10335,7 +10335,7 @@ wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) wxRect wxGetGridCheckBoxRect(wxWindow* win, const wxRect& cellRect, int hAlign, - int WXUNUSED(vAlign)) + int vAlign) { wxSize checkBoxSize = wxRendererNative::Get().GetCheckBoxSize(win, wxCONTROL_CELL); @@ -10368,8 +10368,19 @@ wxRect wxGetGridCheckBoxRect(wxWindow* win, checkBoxRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN); } - // TODO: support vAlign - checkBoxRect = checkBoxRect.CentreIn(cellRect, wxVERTICAL); + if ( vAlign & wxALIGN_CENTER_VERTICAL ) + { + checkBoxRect = checkBoxRect.CentreIn(cellRect, wxVERTICAL); + } + else if ( vAlign & wxALIGN_BOTTOM ) + { + checkBoxRect.SetY(cellRect.y + cellRect.height + - checkBoxRect.y - GRID_CELL_CHECKBOX_MARGIN); + } + else // wxALIGN_TOP + { + checkBoxRect.SetY(cellRect.y + GRID_CELL_CHECKBOX_MARGIN); + } return checkBoxRect; } From 9ae808dc86ab21266cb6f442799834e40c4dcc12 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 27 Nov 2019 23:37:44 +0100 Subject: [PATCH 12/17] Synchronize structure of DrawCheckBox() and GetCheckBoxSize() Put GTK 3 version first in both functions instead of putting it in the middle (!) of GTK 2 code in the former one for some reason. No real changes, the code was just moved around (this commit is best viewed with "git diff --color-moved"). --- src/gtk/renderer.cpp | 68 +++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/gtk/renderer.cpp b/src/gtk/renderer.cpp index 9aa438b8ee..507be9155c 100644 --- a/src/gtk/renderer.cpp +++ b/src/gtk/renderer.cpp @@ -620,7 +620,7 @@ wxRendererGTK::GetCheckBoxSize(wxWindow* win, int flags) } size.y = size.x; -#endif // !__WXGTK3__ +#endif // __WXGTK3__/!__WXGTK3__ return size; } @@ -631,38 +631,6 @@ wxRendererGTK::DrawCheckBox(wxWindow*, const wxRect& rect, int flags ) { -#ifndef __WXGTK3__ - GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget(); - - gint indicator_size, indicator_spacing, focus_width, focus_pad; - gtk_widget_style_get(button, - "indicator_size", &indicator_size, - "indicator_spacing", &indicator_spacing, - "focus-line-width", &focus_width, - "focus-padding", &focus_pad, - NULL); - - GtkStateType state; - - if ( flags & wxCONTROL_PRESSED ) - state = GTK_STATE_ACTIVE; - else if ( flags & wxCONTROL_DISABLED ) - state = GTK_STATE_INSENSITIVE; - else if ( flags & wxCONTROL_CURRENT ) - state = GTK_STATE_PRELIGHT; - else - state = GTK_STATE_NORMAL; - - GtkShadowType shadow_type; - - if ( flags & wxCONTROL_UNDETERMINED ) - shadow_type = GTK_SHADOW_ETCHED_IN; - else if ( flags & wxCONTROL_CHECKED ) - shadow_type = GTK_SHADOW_IN; - else - shadow_type = GTK_SHADOW_OUT; -#endif - #ifdef __WXGTK3__ cairo_t* cr = wxGetGTKDrawable(dc); if (cr == NULL) @@ -723,7 +691,37 @@ wxRendererGTK::DrawCheckBox(wxWindow*, gtk_render_check(sc, cr, x, y, w, h); gtk_style_context_restore(sc); } -#else +#else // !__WXGTK3__ + GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget(); + + gint indicator_size, indicator_spacing, focus_width, focus_pad; + gtk_widget_style_get(button, + "indicator_size", &indicator_size, + "indicator_spacing", &indicator_spacing, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + GtkStateType state; + + if ( flags & wxCONTROL_PRESSED ) + state = GTK_STATE_ACTIVE; + else if ( flags & wxCONTROL_DISABLED ) + state = GTK_STATE_INSENSITIVE; + else if ( flags & wxCONTROL_CURRENT ) + state = GTK_STATE_PRELIGHT; + else + state = GTK_STATE_NORMAL; + + GtkShadowType shadow_type; + + if ( flags & wxCONTROL_UNDETERMINED ) + shadow_type = GTK_SHADOW_ETCHED_IN; + else if ( flags & wxCONTROL_CHECKED ) + shadow_type = GTK_SHADOW_IN; + else + shadow_type = GTK_SHADOW_OUT; + GdkWindow* gdk_window = wxGetGTKDrawable(dc); if (gdk_window == NULL) return; @@ -751,7 +749,7 @@ wxRendererGTK::DrawCheckBox(wxWindow*, dc.LogicalToDeviceY(rect.y) + (rect.height - indicator_size) / 2, indicator_size, indicator_size ); -#endif +#endif // __WXGTK3__/!__WXGTK3__ } void From e2bb8b05bf1b69876bb24c3c3da321472ca172f2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 27 Nov 2019 23:59:36 +0100 Subject: [PATCH 13/17] Factor out common checkbox-related code into CheckBoxInfo No real changes, just extract the same code used in both GTK 2 and GTK 3 implementations of GetCheckBoxSize() and DrawCheckBox() in a helper class which is now just used from both places. This makes the code more clear and hopefully easier to maintain in the future. --- src/gtk/renderer.cpp | 214 +++++++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 98 deletions(-) diff --git a/src/gtk/renderer.cpp b/src/gtk/renderer.cpp index 507be9155c..c2d01e4a39 100644 --- a/src/gtk/renderer.cpp +++ b/src/gtk/renderer.cpp @@ -549,24 +549,99 @@ wxRendererGTK::DrawComboBoxDropButton(wxWindow *win, DrawDropArrow(win,dc,rect); } -#ifdef __WXGTK3__ -static void CheckBoxSize(wxGtkStyleContext& sc, int& w, int& h, GtkBorder* extra = NULL) +// Helper used by GetCheckBoxSize() and DrawCheckBox(). +namespace { - gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL, - "min-width", &w, "min-height", &h, NULL); - GtkBorder border, padding; - gtk_style_context_get_border(sc, GTK_STATE_FLAG_NORMAL, &border); - gtk_style_context_get_padding(sc, GTK_STATE_FLAG_NORMAL, &padding); - border.left += padding.left; - border.right += padding.right; - border.top += padding.top; - border.bottom += padding.bottom; - w += border.left + border.right; - h += border.top + border.bottom; - if (extra) - *extra = border; -} -#endif // __WXGTK3__ + +struct CheckBoxInfo +{ +#ifdef __WXGTK3__ + CheckBoxInfo(wxGtkStyleContext& sc, int flags) + { + wxUnusedVar(flags); + + sc.AddCheckButton(); + if (gtk_check_version(3,20,0) == NULL) + { + sc.Add("check"); + gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL, + "min-width", &indicator_width, + "min-height", &indicator_height, + NULL); + + GtkBorder border, padding; + gtk_style_context_get_border(sc, GTK_STATE_FLAG_NORMAL, &border); + gtk_style_context_get_padding(sc, GTK_STATE_FLAG_NORMAL, &padding); + + margin_left = border.left + padding.left; + margin_top = border.top + padding.top; + margin_right = border.right + padding.right; + margin_bottom = border.bottom + padding.bottom; + } + else + { + GValue value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_INT); + + gtk_style_context_get_style_property(sc, "indicator-size", &value); + indicator_width = + indicator_height = g_value_get_int(&value); + + gtk_style_context_get_style_property(sc, "indicator-spacing", &value); + margin_left = + margin_top = + margin_right = + margin_bottom = g_value_get_int(&value); + + g_value_unset(&value); + } + } +#else // !__WXGTK3__ + CheckBoxInfo(GtkWidget* button, int flags) + { + gint indicator_size, indicator_margin; + gtk_widget_style_get(button, + "indicator_size", &indicator_size, + "indicator_spacing", &indicator_margin, + NULL); + + // If wxCONTROL_CELL is set then we want to get the size of wxCheckBox + // control to draw the check mark centered and at the same position as + // wxCheckBox does, so offset the check mark itself by the focus margin + // in the same way as gtk_real_check_button_draw_indicator() does it, see + // https://github.com/GNOME/gtk/blob/GTK_2_16_0/gtk/gtkcheckbutton.c#L374 + if ( flags & wxCONTROL_CELL ) + { + gint focus_width, focus_pad; + gtk_widget_style_get(button, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + indicator_margin += focus_width + focus_pad; + } + + // In GTK 2 width and height are the same and so are left/right and + // top/bottom. + indicator_width = + indicator_height = indicator_size; + + margin_left = + margin_top = + margin_right = + margin_bottom = indicator_margin; + } +#endif // __WXGTK3__/!__WXGTK3__ + + gint indicator_width, + indicator_height; + gint margin_left, + margin_top, + margin_right, + margin_bottom; +}; + +} // anonymous namespace wxSize wxRendererGTK::GetCheckBoxSize(wxWindow* win, int flags) @@ -578,50 +653,18 @@ wxRendererGTK::GetCheckBoxSize(wxWindow* win, int flags) wxCHECK_MSG(win, size, "Must have a valid window"); #ifdef __WXGTK3__ - wxUnusedVar(flags); - wxGtkStyleContext sc(win->GetContentScaleFactor()); - sc.AddCheckButton(); - if (gtk_check_version(3,20,0) == NULL) - { - sc.Add("check"); - CheckBoxSize(sc, size.x, size.y); - } - else - { - GValue value = G_VALUE_INIT; - g_value_init(&value, G_TYPE_INT); - gtk_style_context_get_style_property(sc, "indicator-size", &value); - size.x = g_value_get_int(&value); - gtk_style_context_get_style_property(sc, "indicator-spacing", &value); - size.x += 2 * g_value_get_int(&value); - size.y = size.x; - g_value_unset(&value); - } + + const CheckBoxInfo info(sc, flags); #else // !__WXGTK3__ - gint indicator_size, indicator_spacing, focus_width, focus_pad; - gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(), - "indicator_size", &indicator_size, - "indicator_spacing", &indicator_spacing, - "focus-line-width", &focus_width, - "focus-padding", &focus_pad, - NULL); + GtkWidget* button = wxGTKPrivate::GetCheckButtonWidget(); - size.x = indicator_size + indicator_spacing * 2; - - // If wxCONTROL_CELL is set then we want to get the size of wxCheckBox - // control to draw the check mark centered and at the same position as - // wxCheckBox do. So we should add margins instead of removing. - // See gtk_real_check_button_draw_indicator: - // https://github.com/GNOME/gtk/blob/GTK_2_16_0/gtk/gtkcheckbutton.c#L374 - if ( flags & wxCONTROL_CELL ) - { - size.x += 2 * (focus_width + focus_pad); - } - - size.y = size.x; + const CheckBoxInfo info(button, flags); #endif // __WXGTK3__/!__WXGTK3__ + size.x = info.indicator_width + info.margin_left + info.margin_right; + size.y = info.indicator_height + info.margin_top + info.margin_bottom; + return size; } @@ -650,41 +693,32 @@ wxRendererGTK::DrawCheckBox(wxWindow*, if (flags & wxCONTROL_CURRENT) state |= GTK_STATE_FLAG_PRELIGHT; - int w, h; wxGtkStyleContext sc(dc.GetContentScaleFactor()); - sc.AddCheckButton(); + + const CheckBoxInfo info(sc, flags); + + const int w = info.indicator_width + info.margin_left + info.margin_right; + const int h = info.indicator_height + info.margin_top + info.margin_bottom; + + const int x = rect.x + (rect.width - w) / 2; + const int y = rect.y + (rect.height - h) / 2; + if (gtk_check_version(3,20,0) == NULL) { - sc.Add("check"); - GtkBorder extra; - CheckBoxSize(sc, w, h, &extra); - - int x = rect.x + (rect.width - w) / 2; - int y = rect.y + (rect.height - h) / 2; gtk_style_context_set_state(sc, GtkStateFlags(state)); gtk_render_background(sc, cr, x, y, w, h); gtk_render_frame(sc, cr, x, y, w, h); // check is rendered in content area - x += extra.left; - y += extra.top; - w -= extra.left + extra.right; - h -= extra.top + extra.bottom; - gtk_render_check(sc, cr, x, y, w, h); + gtk_render_check(sc, cr, + x + info.margin_left, y + info.margin_top, + info.indicator_width, info.indicator_height); } else { - GValue value = G_VALUE_INIT; - g_value_init(&value, G_TYPE_INT); - gtk_style_context_get_style_property(sc, "indicator-size", &value); - w = h = g_value_get_int(&value); - g_value_unset(&value); - // need save/restore for GTK+ 3.6 & 3.8 gtk_style_context_save(sc); gtk_style_context_set_state(sc, GtkStateFlags(state)); - const int x = rect.x + (rect.width - w) / 2; - const int y = rect.y + (rect.height - h) / 2; gtk_render_background(sc, cr, x, y, w, h); gtk_render_frame(sc, cr, x, y, w, h); gtk_style_context_add_class(sc, "check"); @@ -692,15 +726,9 @@ wxRendererGTK::DrawCheckBox(wxWindow*, gtk_style_context_restore(sc); } #else // !__WXGTK3__ - GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget(); + GtkWidget* button = wxGTKPrivate::GetCheckButtonWidget(); - gint indicator_size, indicator_spacing, focus_width, focus_pad; - gtk_widget_style_get(button, - "indicator_size", &indicator_size, - "indicator_spacing", &indicator_spacing, - "focus-line-width", &focus_width, - "focus-padding", &focus_pad, - NULL); + const CheckBoxInfo info(button, flags); GtkStateType state; @@ -726,16 +754,6 @@ wxRendererGTK::DrawCheckBox(wxWindow*, if (gdk_window == NULL) return; - gint offsetX = indicator_spacing; - - // If wxCONTROL_CELL is set then we want to draw the check mark - // at the same position as wxCheckBox do. - // See wxRendererGTK::GetCheckBoxSize. - if ( flags & wxCONTROL_CELL ) - { - offsetX += focus_width + focus_pad; - } - gtk_paint_check ( gtk_widget_get_style(button), @@ -745,9 +763,9 @@ wxRendererGTK::DrawCheckBox(wxWindow*, NULL, button, "cellcheck", - dc.LogicalToDeviceX(rect.x) + offsetX, - dc.LogicalToDeviceY(rect.y) + (rect.height - indicator_size) / 2, - indicator_size, indicator_size + dc.LogicalToDeviceX(rect.x) + info.margin_left, + dc.LogicalToDeviceY(rect.y) + (rect.height - info.indicator_height) / 2, + info.indicator_width, info.indicator_height ); #endif // __WXGTK3__/!__WXGTK3__ } From 2eb312b5f9b76212734edea45e3e755868c67a75 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 28 Nov 2019 02:06:45 +0100 Subject: [PATCH 14/17] Avoid changing checkbox background in wxGrid under non-MSW Doing this makes the checkbox unusable with the default GTK 3 theme as the default grid background colour (white) is the same as the colour of the check mark -- so changing the checkbox background to it makes it invisible. Work around this by adding a new SetTransparentPartColour() method that can be used by wxGrid (and, in the future, user code if we decide that this is really the best solution to this problem that we can provide) to make the checkbox blend in with its background without actually changing its appearance. --- include/wx/checkbox.h | 5 +++++ include/wx/msw/checkbox.h | 5 +++++ src/generic/grideditors.cpp | 25 ++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/wx/checkbox.h b/include/wx/checkbox.h index 00e73dca9e..473ec494f4 100644 --- a/include/wx/checkbox.h +++ b/include/wx/checkbox.h @@ -99,6 +99,11 @@ public: virtual bool HasTransparentBackground() wxOVERRIDE { return true; } + // This semi-private function is currently used to allow wxMSW checkbox to + // blend in with its parent background colour without changing the + // background colour of the checkbox itself under the other platforms. + virtual void SetTransparentPartColour(const wxColour& WXUNUSED(col)) { } + // wxCheckBox-specific processing after processing the update event virtual void DoUpdateWindowUI(wxUpdateUIEvent& event) wxOVERRIDE { diff --git a/include/wx/msw/checkbox.h b/include/wx/msw/checkbox.h index f8b12c1f3b..e8d042a1d6 100644 --- a/include/wx/msw/checkbox.h +++ b/include/wx/msw/checkbox.h @@ -45,6 +45,11 @@ public: // override some base class virtuals virtual void SetLabel(const wxString& label) wxOVERRIDE; + virtual void SetTransparentPartColour(const wxColour& col) wxOVERRIDE + { + SetBackgroundColour(col); + } + virtual bool MSWCommand(WXUINT param, WXWORD id) wxOVERRIDE; virtual void Command(wxCommandEvent& event) wxOVERRIDE; diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index c07686f469..08abef4efa 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1240,10 +1240,33 @@ void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr) { m_control->Show(show); + // Under MSW we need to set the checkbox background colour to the same + // colour as is used by the cell in order for it to blend in, but using + // just SetBackgroundColour() would be wrong as this would actually change + // the background of the checkbox with e.g. GTK 3, making it unusable in + // any theme where the check mark colour is the same, or close to, our + // background colour -- which happens to be the case for the default GTK 3 + // theme, making this a rather serious problem. + // + // One possible workaround would be to set the foreground colour too, but + // wxRendererNative methods used in wxGridCellBoolRenderer don't currently + // take the colours into account, so this would mean that starting to edit + // a boolean field would change its colours, which would be jarring (and + // especially so as we currently set custom colours for all cells, not just + // those that really need them). + // + // A more portable solution could be to create a parent window using the + // background colour if it's different from the default and reparent the + // checkbox under it, so that the parent window colour showed through the + // transparent parts of the checkbox, but this would be more complicated + // for no real gain in practice. + // + // So, finally, just use the bespoke function that will change the + // background under MSW, but doesn't do anything elsewhere. if ( show ) { wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY; - CBox()->SetBackgroundColour(colBg); + CBox()->SetTransparentPartColour(colBg); } } From edd0d9b68fea39e7141594697651290771daad9a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 28 Nov 2019 23:57:15 +0100 Subject: [PATCH 15/17] Ensure DrawCheckBox() fits inside the rectangle passed to it This results in rather ugly checkboxes when the rectangle is too small, and even "critical" GTK warnings with wxGTK 3, but is still arguably better than drawing outside of the provided rectangle. --- src/gtk/renderer.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/gtk/renderer.cpp b/src/gtk/renderer.cpp index c2d01e4a39..c26901817c 100644 --- a/src/gtk/renderer.cpp +++ b/src/gtk/renderer.cpp @@ -633,6 +633,35 @@ struct CheckBoxInfo } #endif // __WXGTK3__/!__WXGTK3__ + // Make sure we fit into the provided rectangle, eliminating margins and + // even reducing the size if necessary. + void FitInto(const wxRect& rect) + { + if ( indicator_width > rect.width ) + { + indicator_width = rect.width; + margin_left = + margin_right = 0; + } + else if ( indicator_width + margin_left + margin_right > rect.width ) + { + margin_left = + margin_right = (rect.width - indicator_width) / 2; + } + + if ( indicator_height > rect.height ) + { + indicator_height = rect.height; + margin_top = + margin_bottom = 0; + } + else if ( indicator_height + margin_top + margin_bottom > rect.height ) + { + margin_top = + margin_bottom = (rect.height - indicator_height) / 2; + } + } + gint indicator_width, indicator_height; gint margin_left, @@ -695,7 +724,8 @@ wxRendererGTK::DrawCheckBox(wxWindow*, wxGtkStyleContext sc(dc.GetContentScaleFactor()); - const CheckBoxInfo info(sc, flags); + CheckBoxInfo info(sc, flags); + info.FitInto(rect); const int w = info.indicator_width + info.margin_left + info.margin_right; const int h = info.indicator_height + info.margin_top + info.margin_bottom; @@ -728,7 +758,8 @@ wxRendererGTK::DrawCheckBox(wxWindow*, #else // !__WXGTK3__ GtkWidget* button = wxGTKPrivate::GetCheckButtonWidget(); - const CheckBoxInfo info(button, flags); + CheckBoxInfo info(button, flags); + info.FitInto(rect); GtkStateType state; From eadee0572921008e4ada0434bb9dad560c7299f7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 29 Nov 2019 04:31:14 +0100 Subject: [PATCH 16/17] Don't change CSS for wxCheckNox with wxNO_BORDER in wxGTK 3 This makes the checkbox look ugly as it's clearly not supposed to be rendered without the border at all, so it's better to do nothing than mangle its CSS. It could be better to add some virtual GTKTurnOffBorder() method that could be overridden to do nothing in wxCheckBox and we should consider doing this if there are other classes for which wxNO_BORDER breaks their appearance, but for now, as long as it's the only case in which we need to do this, just turn wxNO_BORDER off when calling PostCreation(). --- src/gtk/checkbox.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/gtk/checkbox.cpp b/src/gtk/checkbox.cpp index d33908be4d..0579417ebc 100644 --- a/src/gtk/checkbox.cpp +++ b/src/gtk/checkbox.cpp @@ -156,8 +156,22 @@ bool wxCheckBox::Create(wxWindow *parent, m_parent->DoAddChild( this ); +#ifdef __WXGTK3__ + // CSS added if the window has wxNO_BORDER inside base class PostCreation() + // makes checkbox look broken in the default GTK 3 theme, so avoid doing + // this by temporarily turning this flag off. + if ( style & wxNO_BORDER ) + ToggleWindowStyle(wxNO_BORDER); +#endif + PostCreation(size); +#ifdef __WXGTK3__ + // Turn it back on if necessary. + if ( style & wxNO_BORDER ) + ToggleWindowStyle(wxNO_BORDER); +#endif + return true; } From abc8841f0bbd77ea8a74d7c5bf02bbe2466f30c6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 29 Nov 2019 04:57:59 +0100 Subject: [PATCH 17/17] Use default wxCheckBox size in wxGridCellBoolEditor Using wxRendererNative::GetCheckBoxSize() as the size of wxCheckBox just doesn't work with GTK 3, as the control has additional padding between its actual contents and the focus rectangle, which means that its actual size must be greater than the size to be passed to DrawCheckBox() in order to draw a checkbox of the same size. However it isn't really necessary to resize wxCheckBox at all, it's enough for DrawCheckBox() to produce a check mark of the same size as that shown in a default-sized wxCheckBox and this does work in wxGTK. So keep the default size of wxCheckBox to make everything work. This means wxGetGridCheckBoxRect() is not useful any more, so replace it with wxGetContentRect() which just positions a rectangle of the given size inside another one (this should probably be moved somewhere else, as it's more general than wxGrid). --- include/wx/generic/private/grid.h | 12 +++++----- src/generic/grid.cpp | 38 +++++++++++++++---------------- src/generic/gridctrl.cpp | 10 ++++---- src/generic/grideditors.cpp | 13 ++++------- 4 files changed, 32 insertions(+), 41 deletions(-) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index 35225b6a52..172738df55 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -1007,16 +1007,16 @@ private: wxGridDataTypeInfoArray m_typeinfo; }; -// Returns the rectangle for showing a check box in a cell with the given -// alignment. +// Returns the rectangle for showing something of the given size in a cell with +// the given alignment. // // The function is used by wxGridCellBoolEditor and wxGridCellBoolRenderer to // draw a check mark and position wxCheckBox respectively. wxRect -wxGetGridCheckBoxRect(wxWindow* win, - const wxRect& cellRect, - int hAlign, - int vAlign); +wxGetContentRect(wxSize contentSize, + const wxRect& cellRect, + int hAlign, + int vAlign); #endif // wxUSE_GRID #endif // _WX_GENERIC_GRID_PRIVATE_H_ diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index bfbaf2b352..d4983e04e0 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -10332,57 +10332,55 @@ wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) return editor; } -wxRect wxGetGridCheckBoxRect(wxWindow* win, - const wxRect& cellRect, - int hAlign, - int vAlign) +wxRect +wxGetContentRect(wxSize contentSize, + const wxRect& cellRect, + int hAlign, + int vAlign) { - wxSize checkBoxSize = - wxRendererNative::Get().GetCheckBoxSize(win, wxCONTROL_CELL); - // Keep square aspect ratio for the checkbox, but ensure that it fits into // the available space, even if it's smaller than the standard size. const wxCoord minSize = wxMin(cellRect.width, cellRect.height); - if ( checkBoxSize.x >= minSize || checkBoxSize.y >= minSize ) + if ( contentSize.x >= minSize || contentSize.y >= minSize ) { // It must still have positive size, however. const int fittingSize = wxMax(1, minSize - 2*GRID_CELL_CHECKBOX_MARGIN); - checkBoxSize.x = - checkBoxSize.y = fittingSize; + contentSize.x = + contentSize.y = fittingSize; } - wxRect checkBoxRect(checkBoxSize); + wxRect contentRect(contentSize); if ( hAlign & wxALIGN_CENTER_HORIZONTAL ) { - checkBoxRect = checkBoxRect.CentreIn(cellRect, wxHORIZONTAL); + contentRect = contentRect.CentreIn(cellRect, wxHORIZONTAL); } else if ( hAlign & wxALIGN_RIGHT ) { - checkBoxRect.SetX(cellRect.x + cellRect.width - - checkBoxSize.x - GRID_CELL_CHECKBOX_MARGIN); + contentRect.SetX(cellRect.x + cellRect.width + - contentSize.x - GRID_CELL_CHECKBOX_MARGIN); } else // ( hAlign == wxALIGN_LEFT ) and invalid alignment value { - checkBoxRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN); + contentRect.SetX(cellRect.x + GRID_CELL_CHECKBOX_MARGIN); } if ( vAlign & wxALIGN_CENTER_VERTICAL ) { - checkBoxRect = checkBoxRect.CentreIn(cellRect, wxVERTICAL); + contentRect = contentRect.CentreIn(cellRect, wxVERTICAL); } else if ( vAlign & wxALIGN_BOTTOM ) { - checkBoxRect.SetY(cellRect.y + cellRect.height - - checkBoxRect.y - GRID_CELL_CHECKBOX_MARGIN); + contentRect.SetY(cellRect.y + cellRect.height + - contentRect.y - GRID_CELL_CHECKBOX_MARGIN); } else // wxALIGN_TOP { - checkBoxRect.SetY(cellRect.y + GRID_CELL_CHECKBOX_MARGIN); + contentRect.SetY(cellRect.y + GRID_CELL_CHECKBOX_MARGIN); } - return checkBoxRect; + return contentRect; } #endif // wxUSE_GRID diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index 29e3056da5..585a8e6341 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -937,11 +937,8 @@ wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, // compute it only once (no locks for MT safeness in GUI thread...) if ( !ms_sizeCheckMark.x ) { - // Use rectangle big enough for the check box to fit into it. - const wxRect r(0, 0, 1000, 1000); - ms_sizeCheckMark = - wxGetGridCheckBoxRect(&grid, r, wxALIGN_LEFT, wxALIGN_TOP).GetSize(); + wxRendererNative::Get().GetCheckBoxSize(&grid, wxCONTROL_CELL); } return ms_sizeCheckMark; @@ -960,8 +957,9 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, int vAlign = wxALIGN_CENTRE_VERTICAL; attr.GetNonDefaultAlignment(&hAlign, &vAlign); - const wxRect - checkBoxRect = wxGetGridCheckBoxRect(&grid, rect, hAlign, vAlign); + const wxRect checkBoxRect = + wxGetContentRect(GetBestSize(grid, attr, dc, row, col), + rect, hAlign, vAlign); bool value; if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 08abef4efa..3fb11821ea 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1223,16 +1223,11 @@ void wxGridCellBoolEditor::SetSize(const wxRect& r) if (GetCellAttr()) GetCellAttr()->GetNonDefaultAlignment(&hAlign, &vAlign); - const wxRect - checkBoxRect = wxGetGridCheckBoxRect(GetWindow(), r, hAlign, vAlign); + const wxRect checkBoxRect = + wxGetContentRect(m_control->GetSize(), r, hAlign, vAlign); - // resize the control if required - if ( m_control->GetSize() != checkBoxRect.GetSize() ) - { - m_control->SetSize(checkBoxRect.GetSize()); - } - - // and move it + // Don't resize the checkbox, it should have its default (and fitting) + // size, but do move it to the right position. m_control->Move(checkBoxRect.GetPosition()); }