From ef524931cc4370be540475ac018dad4fe7841ec2 Mon Sep 17 00:00:00 2001 From: Pavel Kalugin Date: Tue, 9 Apr 2019 04:51:26 +0300 Subject: [PATCH 1/6] Fix selection background colour in wxHtmlWindow Use inactive selection colour when the window doesn't have focus. --- include/wx/html/htmlcell.h | 9 +++++++++ src/generic/htmllbox.cpp | 4 +++- src/html/htmlcell.cpp | 7 ++++++- src/html/htmlwin.cpp | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h index a4fefc2eee..e3adf20e6e 100644 --- a/include/wx/html/htmlcell.h +++ b/include/wx/html/htmlcell.h @@ -115,8 +115,17 @@ public: class WXDLLIMPEXP_HTML wxDefaultHtmlRenderingStyle : public wxHtmlRenderingStyle { public: + explicit wxDefaultHtmlRenderingStyle(const wxWindowBase* wnd = NULL) + : m_wnd(wnd) + {} + virtual wxColour GetSelectedTextColour(const wxColour& clr) wxOVERRIDE; virtual wxColour GetSelectedTextBgColour(const wxColour& clr) wxOVERRIDE; + +private: + const wxWindowBase* const m_wnd; + + wxDECLARE_NO_COPY_CLASS(wxDefaultHtmlRenderingStyle); }; diff --git a/src/generic/htmllbox.cpp b/src/generic/htmllbox.cpp index 011f6cc557..e1c3720235 100644 --- a/src/generic/htmllbox.cpp +++ b/src/generic/htmllbox.cpp @@ -159,7 +159,9 @@ private: class wxHtmlListBoxStyle : public wxDefaultHtmlRenderingStyle { public: - wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) : m_hlbox(hlbox) { } + wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) + : wxDefaultHtmlRenderingStyle(&hlbox), m_hlbox(hlbox) + { } virtual wxColour GetSelectedTextColour(const wxColour& colFg) wxOVERRIDE { diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp index f5b115823d..0ce3a3e8a3 100644 --- a/src/html/htmlcell.cpp +++ b/src/html/htmlcell.cpp @@ -65,7 +65,12 @@ wxColour wxDefaultHtmlRenderingStyle:: GetSelectedTextBgColour(const wxColour& WXUNUSED(clr)) { - return wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); + // By default we use the fixed standard selection colour, but if we're + // associated with a window use the colour appropriate for the window + // state, i.e. grey out selection when it's not in focus. + + return wxSystemSettings::GetColour(!m_wnd || m_wnd->HasFocus() ? + wxSYS_COLOUR_HIGHLIGHT : wxSYS_COLOUR_BTNSHADOW); } diff --git a/src/html/htmlwin.cpp b/src/html/htmlwin.cpp index d3373316a5..0585f12a1a 100644 --- a/src/html/htmlwin.cpp +++ b/src/html/htmlwin.cpp @@ -1138,7 +1138,7 @@ void wxHtmlWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) dc->SetLayoutDirection(GetLayoutDirection()); wxHtmlRenderingInfo rinfo; - wxDefaultHtmlRenderingStyle rstyle; + wxDefaultHtmlRenderingStyle rstyle(this); rinfo.SetSelection(m_selection); rinfo.SetStyle(&rstyle); m_Cell->Draw(*dc, 0, 0, From 3db142e58f8419d5ec3247d0cd92a7f00b9e2734 Mon Sep 17 00:00:00 2001 From: Pavel Kalugin Date: Tue, 9 Apr 2019 07:02:02 +0300 Subject: [PATCH 2/6] Redraw selection in wxHtmlWindow on focus event --- include/wx/html/htmlwin.h | 1 + src/html/htmlwin.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/wx/html/htmlwin.h b/include/wx/html/htmlwin.h index 1dfc31b5cd..472fffbc81 100644 --- a/include/wx/html/htmlwin.h +++ b/include/wx/html/htmlwin.h @@ -408,6 +408,7 @@ protected: void OnMouseMove(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseUp(wxMouseEvent& event); + void OnFocusEvent(wxFocusEvent& event); #if wxUSE_CLIPBOARD void OnKeyUp(wxKeyEvent& event); void OnDoubleClick(wxMouseEvent& event); diff --git a/src/html/htmlwin.cpp b/src/html/htmlwin.cpp index 0585f12a1a..f3c8bbf632 100644 --- a/src/html/htmlwin.cpp +++ b/src/html/htmlwin.cpp @@ -1184,7 +1184,31 @@ void wxHtmlWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) } } +void wxHtmlWindow::OnFocusEvent(wxFocusEvent& event) +{ + event.Skip(); + // Redraw selection, because its background colour depends on + // whether the window has keyboard focus or not. + + if ( !m_selection || m_selection->IsEmpty() ) + return; + + const wxHtmlCell* fromCell = m_selection->GetFromCell(); + const wxHtmlCell* toCell = m_selection->GetToCell(); + wxCHECK_RET(fromCell || toCell, + "Unexpected: selection is set but cells are not"); + if ( !fromCell ) + fromCell = toCell; + if ( !toCell ) + toCell = fromCell; + + const wxPoint topLeft = fromCell->GetAbsPos(); + const wxPoint bottomRight = toCell->GetAbsPos() + + wxSize(toCell->GetWidth(), toCell->GetHeight()); + RefreshRect(wxRect(CalcScrolledPosition(topLeft), + CalcScrolledPosition(bottomRight))); +} void wxHtmlWindow::OnSize(wxSizeEvent& event) @@ -1656,6 +1680,8 @@ wxBEGIN_EVENT_TABLE(wxHtmlWindow, wxScrolledWindow) EVT_MOTION(wxHtmlWindow::OnMouseMove) EVT_PAINT(wxHtmlWindow::OnPaint) EVT_ERASE_BACKGROUND(wxHtmlWindow::OnEraseBackground) + EVT_SET_FOCUS(wxHtmlWindow::OnFocusEvent) + EVT_KILL_FOCUS(wxHtmlWindow::OnFocusEvent) #if wxUSE_CLIPBOARD EVT_LEFT_DCLICK(wxHtmlWindow::OnDoubleClick) EVT_ENTER_WINDOW(wxHtmlWindow::OnMouseEnter) From ba5547d6403bd3c5185b822ab2f019d8073f207a Mon Sep 17 00:00:00 2001 From: Pavel Kalugin Date: Tue, 9 Apr 2019 07:14:47 +0300 Subject: [PATCH 3/6] Use Ctrl+Ins to copy the selection in wxHtmlWindow --- src/html/htmlwin.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/html/htmlwin.cpp b/src/html/htmlwin.cpp index f3c8bbf632..890d647232 100644 --- a/src/html/htmlwin.cpp +++ b/src/html/htmlwin.cpp @@ -1526,14 +1526,12 @@ void wxHtmlWindow::OnMouseLeave(wxMouseEvent& event) void wxHtmlWindow::OnKeyUp(wxKeyEvent& event) { - if ( IsSelectionEnabled() && - (event.GetKeyCode() == 'C' && event.CmdDown()) ) + if ( IsSelectionEnabled() && event.GetModifiers() == wxMOD_CONTROL && + (event.GetKeyCode() == 'C' || event.GetKeyCode() == WXK_INSERT) ) { wxClipboardTextEvent evt(wxEVT_TEXT_COPY, GetId()); - evt.SetEventObject(this); - - GetEventHandler()->ProcessEvent(evt); + ProcessWindowEvent(evt); } else { From 65771f685b08d3a244ff6f4e2198168473b962cc Mon Sep 17 00:00:00 2001 From: Pavel Kalugin Date: Tue, 9 Apr 2019 21:47:32 +0300 Subject: [PATCH 4/6] Add wxHtmlCell::GetRect() --- include/wx/html/htmlcell.h | 7 ++++++- src/html/htmlcell.cpp | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h index e3adf20e6e..d95fe95a2e 100644 --- a/include/wx/html/htmlcell.h +++ b/include/wx/html/htmlcell.h @@ -329,7 +329,12 @@ public: // Returns absolute position of the cell on HTML canvas. // If rootCell is provided, then it's considered to be the root of the // hierarchy and the returned value is relative to it. - wxPoint GetAbsPos(wxHtmlCell *rootCell = NULL) const; + wxPoint GetAbsPos(const wxHtmlCell *rootCell = NULL) const; + + // Returns minimum bounding rectangle of this cell in coordinates, relative + // to the rootCell, if it is provided, or relative to the result of + // GetRootCell() if the rootCell is NULL. + wxRect GetRect(const wxHtmlCell *rootCell = NULL) const; // Returns root cell of the hierarchy (i.e. grand-grand-...-parent that // doesn't have a parent itself) diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp index 0ce3a3e8a3..a559df99f2 100644 --- a/src/html/htmlcell.cpp +++ b/src/html/htmlcell.cpp @@ -221,10 +221,10 @@ wxHtmlCell *wxHtmlCell::FindCellByPos(wxCoord x, wxCoord y, } -wxPoint wxHtmlCell::GetAbsPos(wxHtmlCell *rootCell) const +wxPoint wxHtmlCell::GetAbsPos(const wxHtmlCell *rootCell) const { wxPoint p(m_PosX, m_PosY); - for (wxHtmlCell *parent = m_Parent; parent && parent != rootCell; + for (const wxHtmlCell *parent = m_Parent; parent && parent != rootCell; parent = parent->m_Parent) { p.x += parent->m_PosX; @@ -233,6 +233,11 @@ wxPoint wxHtmlCell::GetAbsPos(wxHtmlCell *rootCell) const return p; } +wxRect wxHtmlCell::GetRect(const wxHtmlCell* rootCell) const +{ + return wxRect(GetAbsPos(rootCell), wxSize(m_Width, m_Height)); +} + wxHtmlCell *wxHtmlCell::GetRootCell() const { wxHtmlCell *c = wxConstCast(this, wxHtmlCell); From bce4bf1c8e9803d19f7db178188ed91bf4131b70 Mon Sep 17 00:00:00 2001 From: Pavel Kalugin Date: Wed, 10 Apr 2019 09:00:50 +0300 Subject: [PATCH 5/6] Fix calculation of the selection bounding rectangle --- src/html/htmlwin.cpp | 102 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/src/html/htmlwin.cpp b/src/html/htmlwin.cpp index 890d647232..8fdbe56cf6 100644 --- a/src/html/htmlwin.cpp +++ b/src/html/htmlwin.cpp @@ -1184,6 +1184,90 @@ void wxHtmlWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) } } +namespace +{ + +// Returns true if leftCell is an ancestor of rightCell. +bool IsAncestor(const wxHtmlCell* leftCell, const wxHtmlCell* rightCell) +{ + for ( const wxHtmlCell* parent = rightCell->GetParent(); + parent; parent = parent->GetParent() ) + { + if ( leftCell == parent ) + return true; + } + return false; +} + +// Returns minimum bounding rectangle of all the cells between fromCell +// and toCell, inclusive. +wxRect GetBoundingRect(const wxHtmlCell* const fromCell, + const wxHtmlCell* const toCell) +{ + wxCHECK_MSG(fromCell || toCell, wxRect(), "At least one cell is required"); + + // Check if we have only one cell or the cells are equal. + if ( !fromCell ) + return toCell->GetRect(); + else if ( !toCell || fromCell == toCell ) + return fromCell->GetRect(); + + // Check if one of the cells is an ancestor of the other. + if ( IsAncestor(fromCell, toCell) ) + return fromCell->GetRect(); + else if ( IsAncestor(toCell, fromCell) ) + return toCell->GetRect(); + + // Combine MBRs, starting with the fromCell. + wxRect boundingRect = fromCell->GetRect(); + + // For each subtree toward the lowest common ancestor, + // combine MBRs until the (subtree of) toCell is reached. + for ( const wxHtmlCell *startCell = fromCell, + *parent = fromCell->GetParent(); + parent; + startCell = parent, parent = parent->GetParent() ) + { + if ( IsAncestor(parent, toCell) ) + { + // Combine all the cells up to the toCell or its subtree. + for ( const wxHtmlCell* nextCell = startCell->GetNext(); + nextCell; + nextCell = nextCell->GetNext() ) + { + if ( nextCell == toCell ) + return boundingRect.Union(toCell->GetRect()); + + if ( IsAncestor(nextCell, toCell) ) + { + return boundingRect.Union(GetBoundingRect( + nextCell->GetFirstTerminal(), toCell)); + } + + boundingRect.Union(nextCell->GetRect()); + } + + wxFAIL_MSG("Unexpected: toCell is not reachable from the fromCell"); + return GetBoundingRect(toCell, fromCell); + } + else + { + // Combine rest of current container. + for ( const wxHtmlCell* nextCell = startCell->GetNext(); + nextCell; + nextCell = nextCell->GetNext() ) + { + boundingRect.Union(nextCell->GetRect()); + } + } + } + + wxFAIL_MSG("The cells have no common ancestor"); + return wxRect(); +} + +} // namespace + void wxHtmlWindow::OnFocusEvent(wxFocusEvent& event) { event.Skip(); @@ -1198,16 +1282,16 @@ void wxHtmlWindow::OnFocusEvent(wxFocusEvent& event) const wxHtmlCell* toCell = m_selection->GetToCell(); wxCHECK_RET(fromCell || toCell, "Unexpected: selection is set but cells are not"); - if ( !fromCell ) - fromCell = toCell; - if ( !toCell ) - toCell = fromCell; - const wxPoint topLeft = fromCell->GetAbsPos(); - const wxPoint bottomRight = toCell->GetAbsPos() + - wxSize(toCell->GetWidth(), toCell->GetHeight()); - RefreshRect(wxRect(CalcScrolledPosition(topLeft), - CalcScrolledPosition(bottomRight))); + wxRect boundingRect = GetBoundingRect(fromCell, toCell); + + boundingRect = wxRect + ( + CalcScrolledPosition(boundingRect.GetTopLeft()), + CalcScrolledPosition(boundingRect.GetBottomRight()) + ); + + RefreshRect(boundingRect); } From a7dc1c0e0f60d80fb1a3cbaa909d3c28ebc44151 Mon Sep 17 00:00:00 2001 From: Pavel Kalugin Date: Thu, 11 Apr 2019 08:01:57 +0300 Subject: [PATCH 6/6] Fix text jumps during selection dragging on MSW When selecting the text in wxHtmlWindow by dragging the mouse the text to the right of the selection sometimes jumps horizontally. This happens only on MSW and only for TrueType fonts. The reason is that wxDC::GetPartialTextExtents() and wxDC::GetTextExtent() give different results if the text contains characters with underhangs or overhangs. Fix this by caching results of wxDC::GetPartialTextExtents() and using them instead of calling wxDC::GetTextExtent(). --- include/wx/html/htmlcell.h | 17 +++++++++++++++-- src/html/htmlcell.cpp | 28 +++++++++++++++++----------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h index d95fe95a2e..0068c8c173 100644 --- a/include/wx/html/htmlcell.h +++ b/include/wx/html/htmlcell.h @@ -35,7 +35,8 @@ public: wxHtmlSelection() : m_fromPos(wxDefaultPosition), m_toPos(wxDefaultPosition), m_fromCharacterPos(-1), m_toCharacterPos(-1), - m_fromCell(NULL), m_toCell(NULL) {} + m_fromCell(NULL), m_toCell(NULL), + m_extBeforeSel(0), m_extBeforeSelEnd(0) {} // this version is used for the user selection defined with the mouse void Set(const wxPoint& fromPos, const wxHtmlCell *fromCell, @@ -58,6 +59,11 @@ public: wxCoord GetFromCharacterPos () const { return m_fromCharacterPos; } wxCoord GetToCharacterPos () const { return m_toCharacterPos; } + void SetExtentBeforeSelection(unsigned ext) { m_extBeforeSel = ext; } + void SetExtentBeforeSelectionEnd(unsigned ext) { m_extBeforeSelEnd = ext; } + unsigned GetExtentBeforeSelection() const { return m_extBeforeSel; } + unsigned GetExtentBeforeSelectionEnd() const { return m_extBeforeSelEnd; } + bool IsEmpty() const { return m_fromPos == wxDefaultPosition && m_toPos == wxDefaultPosition; } @@ -66,6 +72,12 @@ private: wxPoint m_fromPos, m_toPos; wxCoord m_fromCharacterPos, m_toCharacterPos; const wxHtmlCell *m_fromCell, *m_toCell; + + // Extent of the text before selection start. + unsigned m_extBeforeSel; + + // Extent of the text from the beginning to the selection end. + unsigned m_extBeforeSelEnd; }; @@ -431,7 +443,8 @@ protected: void SetSelectionPrivPos(const wxDC& dc, wxHtmlSelection *s) const; void Split(const wxDC& dc, const wxPoint& selFrom, const wxPoint& selTo, - unsigned& pos1, unsigned& pos2) const; + unsigned& pos1, unsigned& pos2, + unsigned& ext1, unsigned& ext2) const; wxString m_Word; bool m_allowLinebreak; diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp index a559df99f2..a853cc0084 100644 --- a/src/html/htmlcell.cpp +++ b/src/html/htmlcell.cpp @@ -343,7 +343,8 @@ void wxHtmlWordCell::SetPreviousWord(wxHtmlWordCell *cell) // where s2 and s3 start: void wxHtmlWordCell::Split(const wxDC& dc, const wxPoint& selFrom, const wxPoint& selTo, - unsigned& pos1, unsigned& pos2) const + unsigned& pos1, unsigned& pos2, + unsigned& ext1, unsigned& ext2) const { wxPoint pt1 = (selFrom == wxDefaultPosition) ? wxDefaultPosition : selFrom - GetAbsPos(); @@ -400,21 +401,30 @@ void wxHtmlWordCell::Split(const wxDC& dc, pos2 = j; wxASSERT( pos2 >= pos1 ); + + ext1 = pos1 == 0 ? 0 : (pos1 < widths.size() ? widths[pos1-1] : widths.Last()); + ext2 = pos2 == 0 ? 0 : (pos2 < widths.size() ? widths[pos2-1] : widths.Last()); } void wxHtmlWordCell::SetSelectionPrivPos(const wxDC& dc, wxHtmlSelection *s) const { - unsigned p1, p2; + unsigned p1, p2, ext1, ext2; Split(dc, this == s->GetFromCell() ? s->GetFromPos() : wxDefaultPosition, this == s->GetToCell() ? s->GetToPos() : wxDefaultPosition, - p1, p2); + p1, p2, ext1, ext2); if ( this == s->GetFromCell() ) - s->SetFromCharacterPos (p1); // selection starts here + { + s->SetFromCharacterPos(p1); // selection starts here + s->SetExtentBeforeSelection(ext1); + } if ( this == s->GetToCell() ) - s->SetToCharacterPos (p2); // selection ends here + { + s->SetToCharacterPos(p2); // selection ends here + s->SetExtentBeforeSelectionEnd(ext2); + } } @@ -459,7 +469,6 @@ void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, // Selection changing, we must draw the word piecewise: wxHtmlSelection *s = info.GetSelection(); wxString txt; - int w, h; int ofs = 0; // NB: this is quite a hack: in order to compute selection boundaries @@ -479,8 +488,7 @@ void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, { txt = m_Word.Mid(0, part1); dc.DrawText(txt, x + m_PosX, y + m_PosY); - dc.GetTextExtent(txt, &w, &h); - ofs += w; + ofs += s->GetExtentBeforeSelection(); } SwitchSelState(dc, info, true); @@ -490,11 +498,9 @@ void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, if ( (size_t)part2 < m_Word.length() ) { - dc.GetTextExtent(txt, &w, &h); - ofs += w; SwitchSelState(dc, info, false); txt = m_Word.Mid(part2); - dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY); + dc.DrawText(txt, x + m_PosX + s->GetExtentBeforeSelectionEnd(), y + m_PosY); } else drawSelectionAfterCell = true;