From 650d04f1cec2cab201a1fa02d518119bd871a637 Mon Sep 17 00:00:00 2001 From: Stephen Lyons Date: Wed, 17 Apr 2019 05:02:04 +0100 Subject: [PATCH] Enhance: draw RoomId numbers better in 2D Mapper (#2489) They now are scaled so that they fit the room symbol square - and are suppressed if they are too small to show. Importantly this is done on the length of the longest number found in the area so that there will not be truncation - either they will all fit or none will. Also, leading zeros are used so that they are all the same size... Signed-off-by: Stephen Lyons --- src/T2DMap.cpp | 133 +++++++++++++++++++++++++++++++++++++++++++------ src/T2DMap.h | 11 +++- 2 files changed, 126 insertions(+), 18 deletions(-) diff --git a/src/T2DMap.cpp b/src/T2DMap.cpp index 5e48790b198..f354493594c 100644 --- a/src/T2DMap.cpp +++ b/src/T2DMap.cpp @@ -613,6 +613,64 @@ void T2DMap::addSymbolToPixmapCache(const QString key, const bool gridMode) } } +/* + * Helper used to size fonts establishes the font size to use to draw the given + * sample text centralized inside the given boundary (or rather the boundary + * reduced by the margin (0-40) as a percentage). This margin is defaulted to + * 10%. + */ +bool T2DMap::sizeFontToFitTextInRect( QFont & font, const QRectF & boundaryRect, const QString & text, const quint8 percentageMargin ) +{ + QFont _font = font; + + if (percentageMargin > 40) { + qWarning() << "T2DMap::sizeFontToFitTextInRect(...) percentage margin" << percentageMargin << "exceeded recommended maximum (40%) !"; + } + if (text.isEmpty()) { + qWarning() << "T2DMap::sizeFontToFitTextInRect(...) called with no sample text!"; + return false; + } + + qreal fontSize = font.pointSizeF(); + QRectF testRect(boundaryRect.width() * (100 - percentageMargin) / 200.0, + boundaryRect.height() * (100 - percentageMargin) / 200.0, + boundaryRect.width() * (100 - percentageMargin) / 100.0, + boundaryRect.height() * (100 - percentageMargin) / 100.); + // Increase the test font by one, then check to see that it does NOT fit + QRectF neededRect; + QPixmap _pixmap(qRound(1.0 + boundaryRect.width()), qRound(1.0 + boundaryRect.height())); + QPainter _painter(&_pixmap); + do { + fontSize = fontSize + 1.0; + _font.setPointSizeF(fontSize); + _painter.setFont(_font); + + neededRect = _painter.boundingRect(testRect, Qt::AlignCenter | Qt::TextSingleLine | Qt::TextIncludeTrailingSpaces, text); + } while (testRect.contains(neededRect)); + + // Now decrease until it does + bool isSizeTooSmall = false; + static qreal minFontSize = 7.0; + do { + fontSize = fontSize - 1.0; + _font.setPointSizeF(fontSize); + if (fontSize < minFontSize) { + isSizeTooSmall = true; + } + + _painter.setFont(_font); + + neededRect = _painter.boundingRect(testRect, Qt::AlignCenter | Qt::TextSingleLine | Qt::TextIncludeTrailingSpaces, text); + } while ((!isSizeTooSmall) && (!testRect.contains(neededRect))); + + if (isSizeTooSmall) { + return false; + } + + font.setPointSizeF(fontSize); + return true; +} + // Revised to use a QCache to hold QPixmap * to generated images for room symbols void T2DMap::paintEvent(QPaintEvent* e) { @@ -724,6 +782,47 @@ void T2DMap::paintEvent(QPaintEvent* e) mRX = qRound(mRoomWidth * ((xspan / 2.0) - ox)); mRY = qRound(mRoomHeight * ((yspan / 2.0) - oy)); + QFont roomVNumFont = mpMap->mMapSymbolFont; + bool isFontBigEnoughToShowRoomVnum = false; + if (mShowRoomID) { + /* + * If we are to show the room Id numbers - find out the number of digits + * that we will need to use; actually, knowing the digit count is also + * useful for the room selection widget so perform this check EVERY time. + * TODO: Eventually move this check to the TArea class and just redo it + * when areas' room content changes. + */ + int maxUsedRoomId = 0; + QSetIterator itRoomId(playerArea->getAreaRooms()); + while (itRoomId.hasNext()) { + maxUsedRoomId = qMax(maxUsedRoomId, itRoomId.next()); + } + mMaxRoomIdDigits = static_cast(QString::number(maxUsedRoomId).length()); + + QRectF roomTestRect; + if (playerArea->gridMode) { + roomTestRect = QRectF(0, 0, static_cast(mRoomWidth), static_cast(mRoomHeight)); + } else { + roomTestRect = QRectF(0, 0, static_cast(mRoomWidth) * rSize, static_cast(mRoomHeight) * rSize); + } + static quint8 roomVnumMargin = 10; + // Peer review has suggested that under/overlining the room id numbers + // is not helpful after all: + // roomVNumFont.setUnderline(true); + // roomVNumFont.setOverline(true); + roomVNumFont.setBold(true); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + // QFont::PreferNoShaping is only available in Qt 5.10 or later + // QFont::PreferOutline will help to select a font that will scale to any + // size - which is important for good rendering over a range of sizes + // QFont::PreferAntialias will look better - except perhaps at very small + // sizes (but we prevent that by checking in the method call afterwards): + roomVNumFont.setStyleStrategy(QFont::StyleStrategy(QFont::PreferNoShaping|QFont::PreferAntialias|QFont::PreferOutline)); +#else + roomVNumFont.setStyleStrategy(QFont::StyleStrategy(QFont::PreferAntialias|QFont::PreferOutline)); +#endif + isFontBigEnoughToShowRoomVnum = sizeFontToFitTextInRect(roomVNumFont, roomTestRect, QStringLiteral("8").repeated(mMaxRoomIdDigits), roomVnumMargin); + } // This could be the coordinates of the center of the window? int px = qRound((mRoomWidth * xspan) / 2.0); @@ -1564,7 +1663,7 @@ void T2DMap::paintEvent(QPaintEvent* e) painter.fillRect(roomRectangle, roomColor); } - if (!mShowRoomID && !room->mSymbol.isEmpty()) { + if (!(mShowRoomID && isFontBigEnoughToShowRoomVnum) && !room->mSymbol.isEmpty()) { QString pixmapKey; if (roomColor.lightness() > 127) { pixmapKey = QStringLiteral("B_%1").arg(room->mSymbol); @@ -1582,23 +1681,24 @@ void T2DMap::paintEvent(QPaintEvent* e) if (!pix) { qWarning("T2DMap::paintEvent() Alert: mSymbolPixmapCache failure, too many items to cache all of them for: \"%s\"", room->mSymbol.toUtf8().constData()); } else { - /* For the non-scaling QPainter::drawPixmap() used now we + /* + * For the non-scaling QPainter::drawPixmap() used now we * have to position the generated pixmap containing the * particular symbol for this room to Y when it would * position it at X - this should be faster than the previous * scaling QPainter::drawPixmap() as that would scale the * pixmap to fit the Room Rectangle! * - * |<------->| roomRectangle.width() + * |<------->| roomRectangle.width() * roomRectangle.topLeft-->X---------+ - * | Room | - * | Y---+ | - * | |Pix| | - * | +---+ | - * |Rectangle| - * +---------+ - * |<->|<--symbolRect.width() - * x-offset---->|<>|<-- (roomRectangle.width() - symbolRect.width())/2.0 + * | Room | + * | Y---+ | + * | |Pix| | + * | +---+ | + * |Rectangle| + * +---------+ + * |<->|<--symbolRect.width() + * x-offset---->|<>|<-- (roomRectangle.width() - symbolRect.width())/2.0 * similarly for the y-offset */ @@ -1624,17 +1724,18 @@ void T2DMap::paintEvent(QPaintEvent* e) painter.drawPath(diameterPath); } - if (mShowRoomID) { - QPen roomIdPen = painter.pen(); + if (mShowRoomID && isFontBigEnoughToShowRoomVnum) { + painter.save(); QColor roomIdColor; - if (roomColor.red() + roomColor.green() + roomColor.blue() > 200) { + if (roomColor.lightness() > 127) { roomIdColor = QColor(Qt::black); } else { roomIdColor = QColor(Qt::white); } painter.setPen(QPen(roomIdColor)); - painter.drawText(roomRectangle, Qt::AlignHCenter | Qt::AlignVCenter, QString::number(currentAreaRoom)); - painter.setPen(roomIdPen); + painter.setFont(roomVNumFont); + painter.drawText(roomRectangle, Qt::AlignHCenter | Qt::AlignVCenter, QStringLiteral("%1").arg(currentAreaRoom, mMaxRoomIdDigits, 10, QLatin1Char('0'))); + painter.restore(); } if (mShiftMode && currentAreaRoom == mpMap->mRoomIdHash.value(mpMap->mProfileName)) { diff --git a/src/T2DMap.h b/src/T2DMap.h index ad1c073a824..667bef08a53 100644 --- a/src/T2DMap.h +++ b/src/T2DMap.h @@ -4,7 +4,8 @@ /*************************************************************************** * Copyright (C) 2008-2012 by Heiko Koehn - KoehnHeiko@googlemail.com * * Copyright (C) 2014 by Ahmed Charles - acharles@outlook.com * - * Copyright (C) 2016, 2018 by Stephen Lyons - slysven@virginmedia.com * + * Copyright (C) 2016, 2018-2019 by Stephen Lyons * + * - slysven@virginmedia.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -209,6 +210,7 @@ public slots: void resizeMultiSelectionWidget(); std::pair getMousePosition(); bool checkButtonIsForGivenDirection(const QPushButton*, const QString&, const int&); + bool sizeFontToFitTextInRect(QFont&, const QRectF&, const QString&, const quint8 percentageMargin = 10); bool mDialogLock; @@ -240,7 +242,12 @@ public slots: QFont mMapSymbolFont; QPointer mpCreateRoomAction; - + // Calculated near the top of the paintEvent() and is used in a couple of + // places where we need to pad roomIds to the same widths, it also affects + // the font size used to show roomId numbers (longer numbers need a smaller + // font to fit - as the numbers are NOW scaled to fit inside the room symbol + // - and they are suppressed if they would be too small.); + quint8 mMaxRoomIdDigits; private slots: void slot_createRoom();