From 087f0c77427daaa5182456385e55f3737748d409 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 6 Feb 2023 19:47:42 +0100 Subject: [PATCH] Improve size and handling of in-place editor in wxGenericTreeCtrl Squash merge the changes from master with some additional tweaks to preserve ABI-compatibility and allow building with wxUSE_UNICODE==0 in this branch. See #23001, #23205. (cherry picked from commit 3e571ef2535ebaf3fe4ebb5f04fd05708be0458a) --- docs/changes.txt | 1 + samples/treectrl/treetest.cpp | 2 +- src/generic/treectlg.cpp | 87 ++++++++++++++++++++++++++++------- src/gtk/control.cpp | 7 +++ src/gtk/textctrl.cpp | 25 +++++----- src/msw/textctrl.cpp | 7 ++- 6 files changed, 96 insertions(+), 33 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 332b53a922..45e4732d13 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -272,6 +272,7 @@ wxGTK: - Fix display artefacts when using AUI without compositor under X11 (#23135). - Allow selecting and copying text in wxMessageDialog (Ian McInerney, #23039). - Fix initial size of top-level window on Wayland (#23041). +- Improve size and behaviour of in-place editor in wxTreeCtrl (taler21, #23001). wxMSW: diff --git a/samples/treectrl/treetest.cpp b/samples/treectrl/treetest.cpp index 283909a898..a6ba8003ec 100644 --- a/samples/treectrl/treetest.cpp +++ b/samples/treectrl/treetest.cpp @@ -291,7 +291,7 @@ MyFrame::MyFrame(const wxString& title, int x, int y, int w, int h) tree_menu->Append(TreeTest_DecSpacing, "Reduce spacing by 5 points\tCtrl-R"); item_menu->Append(TreeTest_Dump, "&Dump item children"); - item_menu->Append(TreeTest_Rename, "&Rename item..."); + item_menu->Append(TreeTest_Rename, "&Rename item...\tF2"); item_menu->AppendSeparator(); item_menu->Append(TreeTest_SetBold, "Make item &bold"); diff --git a/src/generic/treectlg.cpp b/src/generic/treectlg.cpp index 2f199d73a5..ae35a7d528 100644 --- a/src/generic/treectlg.cpp +++ b/src/generic/treectlg.cpp @@ -103,6 +103,8 @@ protected: void OnKeyUp( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); + void IncreaseSizeForText( const wxString& text ); + bool AcceptChanges(); void Finish( bool setfocus ); @@ -428,6 +430,11 @@ wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner, m_owner = owner; m_aboutToFinish = false; + // Create the text hidden to show it with the correct size -- which we + // can't determine before creating it. + Hide(); + Create(m_owner, wxID_ANY, m_startValue); + wxRect rect; m_owner->GetBoundingRect(m_itemEdited, rect, true); @@ -442,8 +449,27 @@ wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner, rect.height += 4; #endif // platforms - (void)Create(m_owner, wxID_ANY, m_startValue, - rect.GetPosition(), rect.GetSize()); + const wxSize textSize = rect.GetSize(); + wxSize fullSize = GetSizeFromTextSize(textSize); + if ( fullSize.y > textSize.y ) + { + // It's ok to extend the rect to the right horizontally, which happens + // when we just change its size without changing its position below, + // but when extending it vertically, we need to keep it centered. + rect.y -= (fullSize.y - textSize.y + 1) / 2; + } + + // Also check that the control fits into the parent window. + const int totalWidth = m_owner->GetClientSize().x; + if ( rect.x + fullSize.x > totalWidth ) + { + fullSize.x = totalWidth - rect.x; + } + + rect.SetSize(fullSize); + + SetSize(rect); + Show(); SelectAll(); } @@ -533,30 +559,57 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) break; default: + if ( !m_aboutToFinish ) + { +#if wxUSE_UNICODE + wxChar ch = event.GetUnicodeKey(); +#else + wxChar ch = event.m_keyCode < 256 && + event.m_keyCode >= 0 && + wxIsprint(event.m_keyCode) + ? (wxChar)event.m_keyCode + : WXK_NONE; +#endif + if ( ch != WXK_NONE ) + { + wxString value = GetValue(); + + long from, to; + GetSelection( &from, &to ); + if ( from != to ) + { + value.Remove( from, to - from ); + } + + IncreaseSizeForText( value + ch ); + } + } event.Skip(); } } void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) { - if ( !m_aboutToFinish ) - { - // auto-grow the textctrl: - wxSize parentSize = m_owner->GetSize(); - wxPoint myPos = GetPosition(); - wxSize mySize = GetSize(); - int sx, sy; - GetTextExtent(GetValue() + wxT("M"), &sx, &sy); - if (myPos.x + sx > parentSize.x) - sx = parentSize.x - myPos.x; - if (mySize.x > sx) - sx = mySize.x; - SetSize(sx, wxDefaultCoord); - } - + // This function is only preserved in 3.2 branch to avoid warnings from the + // ABI compatibility checked, as this class (wrongly) uses public visibility + // there, even though it's not public at all -- and so we can't remove any + // of its functions, even if they're not needed any longer. event.Skip(); } +void wxTreeTextCtrl::IncreaseSizeForText( const wxString& text ) +{ + // auto-grow the textctrl: + wxSize parentSize = m_owner->GetClientSize(); + wxPoint myPos = GetPosition(); + wxSize mySize = GetSize(); + int sx = GetSizeFromText(text).x; + if (myPos.x + sx > parentSize.x) + sx = parentSize.x - myPos.x; + if (sx > mySize.x) + SetSize(sx, wxDefaultCoord); +} + void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event ) { if ( !m_aboutToFinish ) diff --git a/src/gtk/control.cpp b/src/gtk/control.cpp index f4a25ea146..b5b8a6b8dc 100644 --- a/src/gtk/control.cpp +++ b/src/gtk/control.cpp @@ -373,6 +373,13 @@ wxSize wxControl::GTKGetEntryMargins(GtkEntry* entry) const GtkStyleContext* sc = gtk_widget_get_style_context(GTK_WIDGET(entry)); gtk_style_context_get_padding(sc, gtk_style_context_get_state(sc), &border); #else + if (gtk_entry_get_has_frame(entry)) + { + GtkStyle* style = GTK_WIDGET(entry)->style; + size.x += 2 * style->xthickness; + size.y += 2 * style->ythickness; + } + // Equivalent to the GTK2 private function _gtk_entry_effective_inner_border() GtkBorder border = { 2, 2, 2, 2 }; diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index f3420ea9a8..9f7cb3b0de 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -2156,14 +2156,13 @@ wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const { wxASSERT_MSG( m_widget, wxS("GetSizeFromTextSize called before creation") ); - wxSize tsize(xlen, 0); int cHeight = GetCharHeight(); + wxSize tsize(xlen, cHeight); if ( IsSingleLine() ) { if ( HasFlag(wxBORDER_NONE) ) { - tsize.y = cHeight; #ifdef __WXGTK3__ tsize.IncBy(9, 0); #else @@ -2174,10 +2173,16 @@ wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const { // default height tsize.y = GTKGetPreferredSize(m_widget).y; - // Add the margins we have previously set, but only the horizontal border - // as vertical one has been taken account at GTKGetPreferredSize(). - // Also get other GTK+ margins. - tsize.IncBy( GTKGetEntryMargins(GetEntry()).x, 0); +#ifdef __WXGTK3__ + // Add the margins we have previously set. + tsize.IncBy( GTKGetEntryMargins(GetEntry()) ); +#else + // For GTK 2 these margins are too big, so hard code something more + // reasonable, this is not great but should be fine considering + // that it's very unlikely that GTK 2 is going to evolve, making + // this inappropriate. + tsize.IncBy(20, 0); +#endif } } @@ -2189,7 +2194,6 @@ wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const tsize.IncBy(GTKGetPreferredSize(GTK_WIDGET(m_scrollBar[1])).x + 3, 0); // height - tsize.y = cHeight; if ( ylen <= 0 ) { tsize.y = 1 + cHeight * wxMax(wxMin(GetNumberOfLines(), 10), 2); @@ -2205,10 +2209,9 @@ wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const } } - // Perhaps the user wants something different from CharHeight, or ylen - // is used as the height of a multiline text. - if ( ylen > 0 ) - tsize.IncBy(0, ylen - cHeight); + // We should always use at least the specified height if it's valid. + if ( ylen > tsize.y ) + tsize.y = ylen; return tsize; } diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index bf4bd99cf9..62ce865521 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -2801,10 +2801,9 @@ wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const hText += EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy) - cy; } - // Perhaps the user wants something different from CharHeight, or ylen - // is used as the height of a multiline text. - if ( ylen > 0 ) - hText += ylen - GetCharHeight(); + // We should always use at least the specified height if it's valid. + if ( ylen > hText ) + hText = ylen; return wxSize(wText, hText); }