Merge branch 'fix-html-selection'
Improvements to wxHtmlWindow selection handling: use inactive background for it when the window doesn't have focus; allow copying it with Ctrl-Ins and fix annoying changes in layout while selecting. Closes https://github.com/vadz/wxWidgets/pull/12
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -115,8 +127,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);
|
||||
};
|
||||
|
||||
|
||||
@@ -320,7 +341,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)
|
||||
@@ -417,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;
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -216,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;
|
||||
@@ -228,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);
|
||||
@@ -333,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();
|
||||
@@ -390,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -449,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
|
||||
@@ -469,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);
|
||||
@@ -480,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;
|
||||
|
@@ -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,
|
||||
@@ -1184,7 +1184,115 @@ 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();
|
||||
|
||||
// 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");
|
||||
|
||||
wxRect boundingRect = GetBoundingRect(fromCell, toCell);
|
||||
|
||||
boundingRect = wxRect
|
||||
(
|
||||
CalcScrolledPosition(boundingRect.GetTopLeft()),
|
||||
CalcScrolledPosition(boundingRect.GetBottomRight())
|
||||
);
|
||||
|
||||
RefreshRect(boundingRect);
|
||||
}
|
||||
|
||||
|
||||
void wxHtmlWindow::OnSize(wxSizeEvent& event)
|
||||
@@ -1502,14 +1610,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
|
||||
{
|
||||
@@ -1656,6 +1762,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)
|
||||
|
Reference in New Issue
Block a user