# HG changeset patch # User Markus Mützel # Date 1697303592 -7200 # Node ID ee5d0bded6d428dfcf666a3a99066a118210741c # Parent 2e933fe82ecb9489c33b87f7d35a2b0eaae91c95 gui: Improve support for fractional scaling in command widget on Windows. For long lines, the incorrect character might be repainted when moving the cursor. Use fractional character widths to derive the index of the character in a line at the current cursor position in pixels. * libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp (QConsolePrivate::m_charSize): Use floating point type QSizeF. (QConsolePrivate::updateConsoleSize): Determine (approximate) average character width of used font in floating point precision. (QConsolePrivate::posToCell, QConsolePrivate::cursorRect, QConsolePrivate::boundingRect, QWinTerminalImpl::viewPaintEvent): Adapt to using average character width in floating point precision. diff -r 2e933fe82ecb -r ee5d0bded6d4 libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp --- a/libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp Sat Oct 14 08:44:20 2023 -0400 +++ b/libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp Sat Oct 14 19:13:12 2023 +0200 @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -35,6 +36,7 @@ #include #include +#include #include #include #include @@ -44,7 +46,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -237,7 +240,7 @@ bool m_inWheelEvent; QString m_title; - QSize m_charSize; + QSizeF m_charSize; QSize m_bufferSize; QRect m_consoleRect; bool m_auto_scroll; @@ -494,10 +497,12 @@ QPoint QConsolePrivate::posToCell (const QPoint& p) { - // FIXME: This might become inaccurate for very long lines if the actual - // character width is fractional. - return QPoint (m_consoleRect.left () + p.x () / m_charSize.width (), - m_consoleRect.top () + p.y () / m_charSize.height ()); + return QPoint (m_consoleRect.left () + + std::round (static_cast (p.x ()) + / m_charSize.width ()), + m_consoleRect.top () + + std::round (static_cast (p.y ()) + / m_charSize.height ())); } QString QConsolePrivate::getSelection (void) @@ -1019,11 +1024,22 @@ QFontMetrics fm = m_consoleView->fontMetrics (); QSize winSize = m_consoleView->size (); - m_charSize.rwidth () = fm.averageCharWidth (); + // QFontMetrics::averageCharWidth returns the average character width of the + // used font rounded(?) to the nearest integer. However, the actual + // character width might be fractional on screens with non-scalar DPI + // scaling. Take a large sample and divide by the number of characters in + // the sample to get a more accurate (fractional) average character width of + // the used font. + QString sample ('m'); + sample = sample.repeated (160); // Is 160 a large enough sample? + m_charSize.rwidth () + = static_cast (fm.horizontalAdvance (sample)) / 160.; m_charSize.rheight () = fm.lineSpacing (); - m_consoleRect.setWidth (winSize.width () / m_charSize.width ()); - m_consoleRect.setHeight (winSize.height () / m_charSize.height ()); + m_consoleRect.setWidth (std::floor (static_cast (winSize.width ()) + / m_charSize.width ())); + m_consoleRect.setHeight (std::floor (static_cast (winSize.height ()) + / m_charSize.height ())); // Don't shrink the size of the buffer. That way wide lines won't be // truncated and will reappear if the window is enlarged again later. @@ -1466,13 +1482,13 @@ // Integer precision is good enough for the line height and for the // dimensions of the marker. - int cw = m_charSize.width (); - int ch = m_charSize.height (); + qreal cw = m_charSize.width (); + qreal ch = m_charSize.height (); // Make sure the cursor starts *right* of the previous character. return QRect (fm.horizontalAdvance (sample), (m_cursorPos.y () - m_consoleRect.y ()) * ch, - cw, ch); + std::round (cw), ch); } QRect @@ -1487,12 +1503,12 @@ // Integer precision is good enough for the line height and for the // dimensions of the marker. - int cw = m_charSize.width (); - int ch = m_charSize.height (); + qreal cw = m_charSize.width (); + qreal ch = m_charSize.height (); return QRect (fm.horizontalAdvance (sample)-1, (m_cursorPos.y () - m_consoleRect.y ()) * ch, - cw+2, ch); + std::round (cw+2), ch); } ////////////////////////////////////////////////////////////////////////////// @@ -1593,16 +1609,18 @@ { QPainter p (w); - int cw = d->m_charSize.width (); - int ch = d->m_charSize.height (); + qreal cw = d->m_charSize.width (); + qreal ch = d->m_charSize.height (); QRect updateRect = event->rect (); p.fillRect (updateRect, QBrush (d->backgroundColor ())); - int cx1 = updateRect.left () / cw; + int cx1 = std::round (static_cast (updateRect.left ()) / cw); int cy1 = updateRect.top () / ch; - int cx2 = qMin (d->m_consoleRect.width () - 1, updateRect.right () / cw); - int cy2 = qMin (d->m_consoleRect.height () - 1, updateRect.bottom () / ch); + int cx2 = std::round (static_cast (updateRect.right ()) / cw); + cx2 = qMin (d->m_consoleRect.width () - 1, cx2); + int cy2 = std::round (static_cast (updateRect.bottom ()) / ch); + cy2 = qMin (d->m_consoleRect.height () - 1, cy2); if (cx1 > d->m_consoleRect.width () - 1 || cy1 > d->m_consoleRect.height () - 1)