Skip to content

Commit

Permalink
Roomnames (#3992)
Browse files Browse the repository at this point in the history
* Map: Add an option to show room names below their IDs.

This is a fairly straightforward extension.

TODO: use a separate font.

* basic support for a separate font for room names

* Use black room names on light background

* Room name display and font sizing fixes

Room name display is now independent of room number display.
The cut-off for the font size is lower (4 instead of 7).

Room names are scaled as if the number 88 would be printed in the room
box, which works for all practical purposes.

Font size discovery is now done in floating point, and by scaling with
factors 1.2 (up) and 1.05 (down) which is faster *and* looks way better.

* Don't show room names in grid mode

* Tweak font size calculation

Ensure that we start with a reasonable font size

* Added functions to get/set a room name's offset

float x/y, relative to the room rectangle (and in its units).

TODO: document in the wiki:

getRoomNameOffset(id)
setRoomNameOffset(id, x_offset, y_offset)

* Save file version updated to 21

Added:
* global: room name font + size adjustment
* per room: label offset

* remove LayoutDirection directives

* Re-sorted the grid layout items in ui/profile_prefs

* Fix room name placement

directly under the room's rectangle, no strange offset

* Add userdata flag "room.ui_showName" indicating whether to show room name

* Move room label position to userdata

* Cleanup

The flag whether to display room labels (both globally and per-room)
is now stored in userdata.

Reverted:

ad6d3 Re-sorted the grid layout items in ui/profile_prefs
76834 remove LayoutDirection directives
5d81e Save file version updated to 21

TODO: flipping the global flag crashes for no good reason.

* Crash workaround

Not inlining "setUserDataBool" triggers a double-free bug
(Debian amd64, Qt 5.14.2-3, gcc 10.2.0-9).

I'm investigating this; in the meantime, this patch should be an
acceptable workaround.

* Update src/mudlet-lua/lua/GUIUtils.lua

* Appease codefactor

* Hide showRoomNames checkbox if no room name infrastructure exists

as exhibited by map userData "room.ui_showName" not being present at all

Co-authored-by: Vadim Peretokin <vperetokin@gmail.com>
  • Loading branch information
smurfix and vadi2 committed Oct 23, 2020
1 parent 285c011 commit c0d3faf
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 28 deletions.
109 changes: 98 additions & 11 deletions src/T2DMap.cpp
Expand Up @@ -627,7 +627,7 @@ void T2DMap::addSymbolToPixmapCache(const QString key, const bool gridMode)
* 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 )
bool T2DMap::sizeFontToFitTextInRect( QFont & font, const QRectF & boundaryRect, const QString & text, const quint8 percentageMargin, qreal minFontSize )
{
QFont _font = font;

Expand All @@ -639,28 +639,28 @@ bool T2DMap::sizeFontToFitTextInRect( QFont & font, const QRectF & boundaryRect,
return false;
}

qreal fontSize = font.pointSizeF();
qreal fontSize = qMax(minFontSize, font.pointSizeF()); // protect against too-small initial value
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

// Increase the test font (using somewhat-large steps) until it does not fit any more
QRectF neededRect;
QPixmap _pixmap(qRound(1.0 + boundaryRect.width()), qRound(1.0 + boundaryRect.height()));
QPainter _painter(&_pixmap);
do {
fontSize = fontSize + 1.0;
fontSize *= 1.2;
_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
// Now decrease (using smaller steps) until it fits again
bool isSizeTooSmall = false;
static qreal minFontSize = 7.0;
do {
fontSize = fontSize - 1.0;
fontSize /= 1.05;
_font.setPointSizeF(fontSize);
if (fontSize < minFontSize) {
isSizeTooSmall = true;
Expand Down Expand Up @@ -704,16 +704,29 @@ void T2DMap::initiateSpeedWalk(const int speedWalkStartRoomId, const int speedWa
// player's room if it is visible. This is so it is drawn LAST (and any effects,
// or extra markings for it do not get overwritten by the drawing of the other
// rooms)...
inline void T2DMap::drawRoom(QPainter& painter, QFont& roomVNumFont, QPen& pen, TRoom* pRoom, const bool isGridMode, const bool areRoomIdsLegible, const int speedWalkStartRoomId, const float rx, const float ry, const bool picked)
inline void T2DMap::drawRoom(QPainter& painter, QFont& roomVNumFont, QFont& mapNameFont, QPen& pen, TRoom* pRoom, const bool isGridMode, const bool areRoomIdsLegible, bool showRoomName, const int speedWalkStartRoomId, const float rx, const float ry, const bool picked)
{
const int currentRoomId = pRoom->getId();
pRoom->rendered = false;
QRectF roomRectangle;
QRectF roomNameRectangle;
double realHeight;
if (isGridMode) {
realHeight = mRoomHeight;
roomRectangle = QRectF(rx - mRoomWidth / 2.0, ry - mRoomHeight / 2.0, mRoomWidth, mRoomHeight);
} else {
roomRectangle = QRectF(rx - (mRoomWidth * rSize) / 2.0, ry - (mRoomHeight * rSize) / 2.0, mRoomWidth * rSize, mRoomHeight * rSize);
// this dance is necessary to put the name just below the room rect, later
realHeight = mRoomHeight * rSize;
roomRectangle = QRectF(rx - (mRoomWidth * rSize) / 2.0, ry - realHeight / 2.0, mRoomWidth * rSize, realHeight);
}

roomNameRectangle = roomRectangle.adjusted(-2000, realHeight, 2000, realHeight);

painter.save();
painter.setFont(mapNameFont);
roomNameRectangle = painter.boundingRect(roomNameRectangle, Qt::TextSingleLine|Qt::AlignTop|Qt::AlignCenter, pRoom->name);
painter.restore();

// We should be using the full area for testing for clicks even though
// we only show a smaller one if the user has dialed down the room size
// on NON-grid mode areas:
Expand Down Expand Up @@ -903,6 +916,49 @@ inline void T2DMap::drawRoom(QPainter& painter, QFont& roomVNumFont, QPen& pen,
}

}
// If there is a room name, draw it?
if (showRoomName) {
showRoomName = getUserDataBool(pRoom->userData, ROOM_UI_SHOWNAME, false);
}
if (showRoomName) {
painter.save();

QString namePosData = pRoom->userData.value(ROOM_UI_NAMEPOS);
if (!namePosData.isEmpty()) {
QPointF nameOffset {0, 0};
QStringList posXY = namePosData.split(" ");
bool ok1, ok2;
double posX, posY;

switch (posXY.count()) {
case 1:
// one value: treat as Y offset
posY = posXY[0].toDouble(&ok1);
if (ok1) {
nameOffset.setY(posY);
}
break;
case 2:
posX = posXY[0].toDouble(&ok1);
posY = posXY[1].toDouble(&ok2);
if (ok1 && ok2) {
nameOffset.setX(posX);
nameOffset.setY(posY);
}
break;
}
roomNameRectangle.adjust(mRoomWidth * nameOffset.x(),
mRoomHeight * nameOffset.y(),
mRoomWidth * nameOffset.x(),
mRoomHeight * nameOffset.y());
}
auto roomNameColor = QColor((mpHost->mBgColor_2.lightness() > 127)
? Qt::black : Qt::white);
painter.setPen(QPen(roomNameColor));
painter.setFont(mapNameFont);
painter.drawText(roomNameRectangle, Qt::AlignCenter, pRoom->name);
painter.restore();
}

// Change these from const to static to tweak them whilst running in a debugger...!
const float allInsideTipOffsetFactor = 1 / 20.0f;
Expand Down Expand Up @@ -1230,6 +1286,22 @@ void T2DMap::paintEvent(QPaintEvent* e)
mMapSymbolFont.setOverline(false);
mMapSymbolFont.setStrikeOut(false);

// the room name's font defaults to the symbol's
// but may be overridden
auto mapNameFont = mpMap->mMapSymbolFont;
QString fontName = mpMap->mUserData.value(ROOM_UI_NAMEFONT);
if (!fontName.isEmpty()) {
QFont font;
if (font.fromString(fontName)) {
mapNameFont = font;
}
}
mapNameFont.setBold(false);
mapNameFont.setItalic(false);
mapNameFont.setUnderline(false);
mapNameFont.setOverline(false);
mapNameFont.setStrikeOut(false);

QList<int> exitList;
QList<int> oneWayExits;
int playerRoomId = mpMap->mRoomIdHash.value(mpMap->mProfileName);
Expand Down Expand Up @@ -1292,6 +1364,7 @@ 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) {
/*
Expand Down Expand Up @@ -1326,6 +1399,20 @@ void T2DMap::paintEvent(QPaintEvent* e)
isFontBigEnoughToShowRoomVnum = sizeFontToFitTextInRect(roomVNumFont, roomTestRect, QStringLiteral("8").repeated(mMaxRoomIdDigits), roomVnumMargin);
}

bool showRoomNames = mpMap->getRoomNamesShown() && !playerArea->gridMode;
if (showRoomNames) {
/*
* Like above, except that we use the room height as the font size.
*/
mapNameFont.setBold(true);

mapNameFont.setStyleStrategy(QFont::StyleStrategy(QFont::PreferNoShaping|QFont::PreferAntialias|QFont::PreferOutline));

double sizeAdjust = 0; // TODO add userdata setting to adjust this
mapNameFont.setPointSizeF(static_cast<qreal>(mRoomWidth) * rSize * pow(1.1, sizeAdjust) / 2.0);
showRoomNames = (mapNameFont.pointSizeF() > 3.0);
}

TArea* pArea = playerArea;

int zLevel = mOz;
Expand Down Expand Up @@ -1423,12 +1510,12 @@ void T2DMap::paintEvent(QPaintEvent* e)
playerRoomOnWidgetCoordinates = QPointF(static_cast<qreal>(rx), static_cast<qreal>(ry));
} else {
// Not the player's room:
drawRoom(painter, roomVNumFont, pen, room, pArea->gridMode, isFontBigEnoughToShowRoomVnum, playerRoomId, rx, ry, __Pick);
drawRoom(painter, roomVNumFont, mapNameFont, pen, room, pArea->gridMode, isFontBigEnoughToShowRoomVnum, showRoomNames, playerRoomId, rx, ry, __Pick);
}
} // End of while loop for each room in area

if (isPlayerRoomVisible) {
drawRoom(painter, roomVNumFont, pen, playerRoom, pArea->gridMode, isFontBigEnoughToShowRoomVnum, playerRoomId, static_cast<float>(playerRoomOnWidgetCoordinates.x()), static_cast<float>(playerRoomOnWidgetCoordinates.y()), __Pick);
drawRoom(painter, roomVNumFont, mapNameFont, pen, playerRoom, pArea->gridMode, isFontBigEnoughToShowRoomVnum, showRoomNames, playerRoomId, static_cast<float>(playerRoomOnWidgetCoordinates.x()), static_cast<float>(playerRoomOnWidgetCoordinates.y()), __Pick);
painter.save();
QPen transparentPen(Qt::transparent);
QPainterPath myPath;
Expand Down
4 changes: 2 additions & 2 deletions src/T2DMap.h
Expand Up @@ -214,8 +214,8 @@ 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);
void drawRoom(QPainter&, QFont&, QPen&, TRoom*, const bool isGridMode, const bool areRoomIdsLegible, const int, const float, const float, const bool);
bool sizeFontToFitTextInRect(QFont&, const QRectF&, const QString&, const quint8 percentageMargin = 10, const qreal minFontSize = 7.0);
void drawRoom(QPainter&, QFont&, QFont&, QPen&, TRoom*, const bool isGridMode, const bool areRoomIdsLegible, bool showRoomNames, const int, const float, const float, const bool);
void paintMapInfo(const QElapsedTimer& renderTimer, QPainter& painter, const bool showingCurrentArea, QColor& infoColor);
void paintAreaExits(QPainter& painter, QPen& pen, QList<int>& exitList, QList<int>& oneWayExits, const TArea* pArea, int zLevel, float exitWidth);
void initiateSpeedWalk(const int speedWalkStartRoomId, const int speedWalkTargetRoomId);
Expand Down
12 changes: 1 addition & 11 deletions src/TLuaInterpreter.cpp
Expand Up @@ -1411,17 +1411,7 @@ int TLuaInterpreter::updateMap(lua_State* L)
{
Host& host = getHostFromLua(L);
if (host.mpMap) {
#if defined(INCLUDE_3DMAPPER)
if (host.mpMap->mpM) {
host.mpMap->mpM->update();
}
#endif
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mNewMoveAction = true;
host.mpMap->mpMapper->mp2dMap->update();
}
}
host.mpMap->update();
}
return 0;
}
Expand Down
35 changes: 34 additions & 1 deletion src/TMap.cpp
Expand Up @@ -1451,7 +1451,6 @@ bool TMap::restore(QString location, bool downloadIfNotFound)
|QFont::PreferOutline | QFont::PreferAntialias | QFont::PreferQuality
|QFont::PreferNoShaping
));

if (mVersion >= 14) {
int areaSize;
ifs >> areaSize;
Expand Down Expand Up @@ -2544,3 +2543,37 @@ QString TMap::getMmpMapLocation() const
{
return mMmpMapLocation;
}

bool TMap::getRoomNamesPresent()
{
return mUserData.contains(ROOM_UI_SHOWNAME);
}

bool TMap::getRoomNamesShown()
{
return getUserDataBool(mUserData, ROOM_UI_SHOWNAME, false);
}

void TMap::setRoomNamesShown(bool shown)
{
setUserDataBool(mUserData, ROOM_UI_SHOWNAME, shown);
}

void TMap::update()
{
#if defined(INCLUDE_3DMAPPER)
if (mpM) {
mpM->update();
}
#endif
if (mpMapper) {
mpMapper->showRoomNames->setVisible(getRoomNamesPresent());
mpMapper->showRoomNames->setChecked(getRoomNamesShown());

if (mpMapper->mp2dMap) {
mpMapper->mp2dMap->mNewMoveAction = true;
mpMapper->mp2dMap->update();
}
}
}

7 changes: 7 additions & 0 deletions src/TMap.h
Expand Up @@ -102,6 +102,7 @@ class TMap : public QObject
void tidyMap(int area);
bool setExit(int from, int to, int dir);
bool setRoomCoordinates(int id, int x, int y, int z);
void update();

// Was init( Host * ) but host pointer was not used and it does not initialise a map!
void audit();
Expand Down Expand Up @@ -234,6 +235,12 @@ class TMap : public QObject
// Disables font substitution if set:
bool mIsOnlyMapSymbolFontToBeUsed;

// has setRoomNamesShown ever been called on this map?
bool getRoomNamesPresent();
// show room labels on the map?
bool getRoomNamesShown();
void setRoomNamesShown(bool shown);

// location of an MMP map provided by the game
QString mMmpMapLocation;

Expand Down
28 changes: 28 additions & 0 deletions src/TRoomDB.cpp
Expand Up @@ -30,6 +30,10 @@
#include <QRegularExpression>
#include "post_guard.h"

const QString ROOM_UI_SHOWNAME = QStringLiteral("room.ui_showName");
const QString ROOM_UI_NAMEPOS = QStringLiteral("room.ui_nameOffset");
const QString ROOM_UI_NAMEFONT = QStringLiteral("room.ui_nameFont");
const QString ROOM_UI_NAMESIZE = QStringLiteral("room.ui_nameSize");

TRoomDB::TRoomDB(TMap* pMap)
: mpMap(pMap)
Expand Down Expand Up @@ -1326,3 +1330,27 @@ void TRoomDB::setAreaRooms(const int areaId, const QSet<int>& roomIds)

pA->calcSpan(); // The area extents will need recalculation after adding the rooms
}

bool getUserDataBool(const QMap<QString, QString>& userData, const QString& key, bool defaultValue)
{
if (!userData.contains(key)) {
return defaultValue;
}
QString value = userData.value(key);
if (value.isEmpty()) {
return defaultValue;
}
switch (value[0].unicode()) {
case 'y': case 'Y':
case 't': case 'T':
case '1':
return true;
case 'n': case 'N':
case 'f': case 'F':
case '0':
return false;
default:
return defaultValue;
}
}

19 changes: 19 additions & 0 deletions src/TRoomDB.h
Expand Up @@ -34,6 +34,11 @@ class TArea;
class TMap;
class TRoom;

// well-known userData tags
extern const QString ROOM_UI_SHOWNAME;
extern const QString ROOM_UI_NAMEPOS;
extern const QString ROOM_UI_NAMEFONT; // global only
extern const QString ROOM_UI_NAMESIZE; // TODO

class TRoomDB
{
Expand Down Expand Up @@ -112,4 +117,18 @@ class TRoomDB
friend class XMLimport;
};

// helpers to get/set bools from userdata, required for storing some bool
// values there instead of upticking the map format
bool getUserDataBool(const QMap<QString, QString>& userData, const QString& key, bool defaultValue = false);

// this needs to be inlined due to a compiler and/or Qt bug.
static inline void setUserDataBool(QMap<QString, QString>& userData, const QString& key, bool value)
{
if (value) {
userData[key] = QStringLiteral("1");
} else {
userData[key] = QStringLiteral("0");
}
}

#endif // MUDLET_TROOMDB_H
10 changes: 10 additions & 0 deletions src/dlgMapper.cpp
Expand Up @@ -81,6 +81,9 @@ dlgMapper::dlgMapper( QWidget * parent, Host * pH, TMap * pM )
showRoomIDs->setChecked(mpHost->mShowRoomID);
mp2dMap->mShowRoomID = mpHost->mShowRoomID;

showRoomNames->setVisible(mpMap->getRoomNamesPresent());
showRoomNames->setChecked(mpMap->getRoomNamesShown());

panel->setVisible(mpHost->mShowPanel);
connect(bubbles, &QAbstractButton::clicked, this, &dlgMapper::slot_bubbles);
connect(showInfo, &QAbstractButton::clicked, this, &dlgMapper::slot_info);
Expand All @@ -96,6 +99,7 @@ dlgMapper::dlgMapper( QWidget * parent, Host * pH, TMap * pM )
connect(showArea, qOverload<const QString&>(&QComboBox::activated), mp2dMap, &T2DMap::slot_switchArea);
connect(dim2, &QAbstractButton::pressed, this, &dlgMapper::show2dView);
connect(showRoomIDs, &QCheckBox::stateChanged, this, &dlgMapper::slot_toggleShowRoomIDs);
connect(showRoomNames, &QCheckBox::stateChanged, this, &dlgMapper::slot_toggleShowRoomNames);

// Explicitly set the font otherwise it changes between the Application and
// the default System one as the mapper is docked and undocked!
Expand Down Expand Up @@ -173,6 +177,12 @@ void dlgMapper::slot_toggleShowRoomIDs(int s)
mp2dMap->update();
}

void dlgMapper::slot_toggleShowRoomNames(int s)
{
mpMap->setRoomNamesShown(s == Qt::Checked);
mp2dMap->update();
}

void dlgMapper::slot_toggleStrongHighlight(int v)
{
mpHost->mMapStrongHighlight = v == Qt::Checked ? true : false;
Expand Down
1 change: 1 addition & 0 deletions src/dlgMapper.h
Expand Up @@ -57,6 +57,7 @@ public slots:
void slot_bubbles();
void slot_info();
void slot_toggleShowRoomIDs(int s);
void slot_toggleShowRoomNames(int s);
void slot_toggleStrongHighlight(int v);
void show2dView();
void slot_togglePanel();
Expand Down

0 comments on commit c0d3faf

Please sign in to comment.