Skip to content

Commit

Permalink
Enhance: draw RoomId numbers better in 2D Mapper (Mudlet#2489)
Browse files Browse the repository at this point in the history
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 <slysven@virginmedia.com>
  • Loading branch information
SlySven authored Apr 17, 2019
1 parent 9de0aec commit 650d04f
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 18 deletions.
133 changes: 117 additions & 16 deletions src/T2DMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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<int> itRoomId(playerArea->getAreaRooms());
while (itRoomId.hasNext()) {
maxUsedRoomId = qMax(maxUsedRoomId, itRoomId.next());
}
mMaxRoomIdDigits = static_cast<quint8>(QString::number(maxUsedRoomId).length());

QRectF roomTestRect;
if (playerArea->gridMode) {
roomTestRect = QRectF(0, 0, static_cast<qreal>(mRoomWidth), static_cast<qreal>(mRoomHeight));
} else {
roomTestRect = QRectF(0, 0, static_cast<qreal>(mRoomWidth) * rSize, static_cast<qreal>(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);
Expand Down Expand Up @@ -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);
Expand All @@ -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
*/

Expand All @@ -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)) {
Expand Down
11 changes: 9 additions & 2 deletions src/T2DMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down Expand Up @@ -209,6 +210,7 @@ public slots:
void resizeMultiSelectionWidget();
std::pair<int, int> getMousePosition();
bool checkButtonIsForGivenDirection(const QPushButton*, const QString&, const int&);
bool sizeFontToFitTextInRect(QFont&, const QRectF&, const QString&, const quint8 percentageMargin = 10);

bool mDialogLock;

Expand Down Expand Up @@ -240,7 +242,12 @@ public slots:
QFont mMapSymbolFont;

QPointer<QAction> 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();
Expand Down

0 comments on commit 650d04f

Please sign in to comment.