1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-10-31 00:07:39 +02:00

UI improvements for lobby screen:

- Show description of current chat room in header
- Show count of online accounts / active rooms
- Highlight currently selected chat room
This commit is contained in:
Ivan Savenko
2024-03-21 18:00:05 +02:00
parent b3c503922f
commit 4dce0479ba
10 changed files with 211 additions and 49 deletions

View File

@@ -72,17 +72,19 @@
"vcmi.lobby.noUnderground" : "no underground",
"vcmi.lobby.sortDate" : "Sorts maps by change date",
"vcmi.lobby.login.title" : "VCMI Lobby",
"vcmi.lobby.login.title" : "VCMI Online Lobby",
"vcmi.lobby.login.username" : "Username:",
"vcmi.lobby.login.connecting" : "Connecting...",
"vcmi.lobby.login.error" : "Connection error: %s",
"vcmi.lobby.login.create" : "New Account",
"vcmi.lobby.login.login" : "Login",
"vcmi.lobby.header.rooms" : "Game Rooms",
"vcmi.lobby.header.rooms" : "Game Rooms - %d",
"vcmi.lobby.header.channels" : "Chat Channels",
"vcmi.lobby.header.chat" : "Game Chat",
"vcmi.lobby.header.chat.global" : "Global Game Chat - %s", // %s -> language name
"vcmi.lobby.header.chat.match" : "Previous game chat from %s", // %s -> game start date & time
"vcmi.lobby.header.chat.private" : "Private chat with %s", // %s -> nickname of another player
"vcmi.lobby.header.history" : "Your Previous Games",
"vcmi.lobby.header.players" : "Players Online",
"vcmi.lobby.header.players" : "Players Online - %d",
"vcmi.lobby.match.solo" : "Solo Game",
"vcmi.lobby.match.duel" : "Game with %s",
"vcmi.lobby.match.multi" : "%d players",

View File

@@ -72,17 +72,19 @@
"vcmi.lobby.noUnderground" : "немає підземелля",
"vcmi.lobby.sortDate" : "Сортувати мапи за датою зміни",
"vcmi.lobby.login.title" : "VCMI Лобі",
"vcmi.lobby.login.title" : "Онлайн лобі VCMI",
"vcmi.lobby.login.username" : "Логін:",
"vcmi.lobby.login.connecting" : "Підключення...",
"vcmi.lobby.login.error" : "Помилка з'єднання: %s",
"vcmi.lobby.login.create" : "Створити акаунт",
"vcmi.lobby.login.login" : "Увійти",
"vcmi.lobby.header.rooms" : "Кімнати",
"vcmi.lobby.header.rooms" : "Активні кімнати - %d",
"vcmi.lobby.header.channels" : "Канали чату",
"vcmi.lobby.header.chat" : "Чат гри",
"vcmi.lobby.header.chat.global" : "Глобальний ігровий чат - %s", // %s -> language name
"vcmi.lobby.header.chat.match" : "Чат минулої гри від %s", // %s -> game start date & time
"vcmi.lobby.header.chat.private" : "Приватний чат з %s", // %s -> nickname of another player
"vcmi.lobby.header.history" : "Ваші попередні ігри",
"vcmi.lobby.header.players" : "Гравці в мережі",
"vcmi.lobby.header.players" : "Гравці в мережі - %d",
"vcmi.lobby.match.solo" : "Одиночна гра",
"vcmi.lobby.match.duel" : "Гра з %s",
"vcmi.lobby.match.multi" : "%d гравців",

View File

@@ -134,33 +134,67 @@ std::shared_ptr<CListBox> GlobalLobbyWidget::getRoomList()
return widget<CListBox>("roomList");
}
std::shared_ptr<CListBox> GlobalLobbyWidget::getChannelList()
{
return widget<CListBox>("channelList");
}
std::shared_ptr<CListBox> GlobalLobbyWidget::getMatchList()
{
return widget<CListBox>("matchList");
}
GlobalLobbyChannelCardBase::GlobalLobbyChannelCardBase(GlobalLobbyWindow * window, const std::string & channelType, const std::string & channelName)
std::shared_ptr<CLabel> GlobalLobbyWidget::getGameChatHeader()
{
return widget<CLabel>("headerGameChat");
}
std::shared_ptr<CLabel> GlobalLobbyWidget::getAccountListHeader()
{
return widget<CLabel>("headerAccountList");
}
std::shared_ptr<CLabel> GlobalLobbyWidget::getRoomListHeader()
{
return widget<CLabel>("headerRoomList");
}
std::shared_ptr<CLabel> GlobalLobbyWidget::getChannelListHeader()
{
return widget<CLabel>("headerChannelList");
}
std::shared_ptr<CLabel> GlobalLobbyWidget::getMatchListHeader()
{
return widget<CLabel>("headerMatchList");
}
GlobalLobbyChannelCardBase::GlobalLobbyChannelCardBase(GlobalLobbyWindow * window, const Point & dimensions, const std::string & channelType, const std::string & channelName, const std::string & channelDescription)
: window(window)
, channelType(channelType)
, channelName(channelName)
, channelDescription(channelDescription)
{
pos.w = dimensions.x;
pos.h = dimensions.y;
addUsedEvents(LCLICK);
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
if (window->isChannelOpen(channelType, channelName))
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), Colors::YELLOW, 2);
else
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
}
void GlobalLobbyChannelCardBase::clickPressed(const Point & cursorPosition)
{
window->doOpenChannel(channelType, channelName);
window->doOpenChannel(channelType, channelName, channelDescription);
}
GlobalLobbyAccountCard::GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription)
: GlobalLobbyChannelCardBase(window, "player", accountDescription.accountID)
: GlobalLobbyChannelCardBase(window, Point(130, 40), "player", accountDescription.accountID, accountDescription.displayName)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 130;
pos.h = 40;
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64));
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName);
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
}
@@ -198,26 +232,16 @@ GlobalLobbyRoomCard::GlobalLobbyRoomCard(GlobalLobbyWindow * window, const Globa
}
GlobalLobbyChannelCard::GlobalLobbyChannelCard(GlobalLobbyWindow * window, const std::string & channelName)
: GlobalLobbyChannelCardBase(window, "global", channelName)
: GlobalLobbyChannelCardBase(window, Point(146, 40), "global", channelName, Languages::getLanguageOptions(channelName).nameNative)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 146;
pos.h = 40;
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64));
labelName = std::make_shared<CLabel>(5, 20, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, Languages::getLanguageOptions(channelName).nameNative);
}
GlobalLobbyMatchCard::GlobalLobbyMatchCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & matchDescription)
: GlobalLobbyChannelCardBase(window, "match", matchDescription.gameRoomID)
: GlobalLobbyChannelCardBase(window, Point(130, 40), "match", matchDescription.gameRoomID, matchDescription.startDateFormatted)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 130;
pos.h = 40;
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64));
labelMatchDate = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, matchDescription.startDateFormatted);
MetaString opponentDescription;

View File

@@ -35,6 +35,12 @@ public:
std::shared_ptr<CListBox> getRoomList();
std::shared_ptr<CListBox> getChannelList();
std::shared_ptr<CListBox> getMatchList();
std::shared_ptr<CLabel> getGameChatHeader();
std::shared_ptr<CLabel> getAccountListHeader();
std::shared_ptr<CLabel> getRoomListHeader();
std::shared_ptr<CLabel> getChannelListHeader();
std::shared_ptr<CLabel> getMatchListHeader();
};
class GlobalLobbyChannelCardBase : public CIntObject
@@ -42,10 +48,13 @@ class GlobalLobbyChannelCardBase : public CIntObject
GlobalLobbyWindow * window;
std::string channelType;
std::string channelName;
std::string channelDescription;
void clickPressed(const Point & cursorPosition) override;
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
public:
GlobalLobbyChannelCardBase(GlobalLobbyWindow * window, const std::string & channelType, const std::string & channelName);
GlobalLobbyChannelCardBase(GlobalLobbyWindow * window, const Point & dimensions, const std::string & channelType, const std::string & channelName, const std::string & channelDescription);
};
class GlobalLobbyAccountCard : public GlobalLobbyChannelCardBase
@@ -75,7 +84,6 @@ public:
class GlobalLobbyChannelCard : public GlobalLobbyChannelCardBase
{
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
std::shared_ptr<CLabel> labelName;
public:
@@ -84,7 +92,6 @@ public:
class GlobalLobbyMatchCard : public GlobalLobbyChannelCardBase
{
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
std::shared_ptr<CLabel> labelMatchDate;
std::shared_ptr<CLabel> labelMatchOpponent;

View File

@@ -22,6 +22,7 @@
#include "../widgets/ObjectLists.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/Languages.h"
#include "../../lib/MetaString.h"
GlobalLobbyWindow::GlobalLobbyWindow()
@@ -33,10 +34,17 @@ GlobalLobbyWindow::GlobalLobbyWindow()
center();
widget->getAccountNameLabel()->setText(settings["lobby"]["displayName"].String());
doOpenChannel("global", "english");
doOpenChannel("global", "english", Languages::getLanguageOptions("english").nameNative);
widget->getChannelListHeader()->setText(MetaString::createFromTextID("vcmi.lobby.header.channels").toString());
}
void GlobalLobbyWindow::doOpenChannel(const std::string & channelType, const std::string & channelName)
bool GlobalLobbyWindow::isChannelOpen(const std::string & testChannelType, const std::string & testChannelName)
{
return testChannelType == currentChannelType && testChannelName == currentChannelName;
}
void GlobalLobbyWindow::doOpenChannel(const std::string & channelType, const std::string & channelName, const std::string & roomDescription)
{
currentChannelType = channelType;
currentChannelName = channelName;
@@ -47,6 +55,18 @@ void GlobalLobbyWindow::doOpenChannel(const std::string & channelType, const std
for (auto const & entry : history)
onGameChatMessage(entry.displayName, entry.messageText, entry.timeFormatted, channelType, channelName);
MetaString text;
text.appendTextID("vcmi.lobby.header.chat." + channelType);
text.replaceRawString(roomDescription);
widget->getGameChatHeader()->setText(text.toString());
// Update currently selected item in UI
widget->getAccountList()->reset();
widget->getChannelList()->reset();
widget->getMatchList()->reset();
redraw();
}
void GlobalLobbyWindow::doSendChatMessage()
@@ -109,6 +129,10 @@ void GlobalLobbyWindow::onActiveAccounts(const std::vector<GlobalLobbyAccount> &
widget->getAccountList()->reset();
else
widget->getAccountList()->resize(accounts.size());
MetaString text = MetaString::createFromTextID("vcmi.lobby.header.players");
text.replaceNumber(accounts.size());
widget->getAccountListHeader()->setText(text.toString());
}
void GlobalLobbyWindow::onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms)
@@ -117,6 +141,10 @@ void GlobalLobbyWindow::onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms
widget->getRoomList()->reset();
else
widget->getRoomList()->resize(rooms.size());
MetaString text = MetaString::createFromTextID("vcmi.lobby.header.rooms");
text.replaceNumber(rooms.size());
widget->getRoomListHeader()->setText(text.toString());
}
void GlobalLobbyWindow::onMatchesHistory(const std::vector<GlobalLobbyRoom> & history)
@@ -125,6 +153,10 @@ void GlobalLobbyWindow::onMatchesHistory(const std::vector<GlobalLobbyRoom> & hi
widget->getMatchList()->reset();
else
widget->getMatchList()->resize(history.size());
MetaString text = MetaString::createFromTextID("vcmi.lobby.header.history");
text.replaceNumber(history.size());
widget->getMatchListHeader()->setText(text.toString());
}
void GlobalLobbyWindow::onJoinedRoom()

View File

@@ -30,10 +30,13 @@ public:
void doSendChatMessage();
void doCreateGameRoom();
void doOpenChannel(const std::string & channelType, const std::string & channelName);
void doOpenChannel(const std::string & channelType, const std::string & channelName, const std::string & roomDescription);
void doInviteAccount(const std::string & accountID);
void doJoinRoom(const std::string & roomID);
/// Returns true if provided chat channel is the one that is currently open in UI
bool isChannelOpen(const std::string & channelType, const std::string & channelName);
// Callbacks for network packs
void onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when, const std::string & channelType, const std::string & channelName);

View File

@@ -82,6 +82,8 @@ void CLabel::setAutoRedraw(bool value)
void CLabel::setText(const std::string & Txt)
{
assert(TextOperations::isValidUnicodeString(Txt));
text = Txt;
trimText();

View File

@@ -0,0 +1,90 @@
{
"type" : "object",
"$schema" : "http://json-schema.org/draft-06/schema",
"title" : "Lobby protocol: matchesHistory",
"description" : "Sent by server to initialized or update list of previous matches by player",
"required" : [ "type", "matchesHistory" ],
"additionalProperties" : false,
"properties" : {
"type" :
{
"type" : "string",
"const" : "matchesHistory"
},
"matchesHistory" :
{
"type" : "array",
"description" : "List of previously played matches",
"items" :
{
"type" : "object",
"additionalProperties" : false,
"required" : [ "gameRoomID", "hostAccountID", "hostAccountDisplayName", "description", "participants", "status", "playerLimit", "ageSeconds" ],
"properties" : {
"gameRoomID" :
{
"type" : "string",
"description" : "Unique ID of game room"
},
"hostAccountID" :
{
"type" : "string",
"description" : "ID of account that created and hosts this game room"
},
"hostAccountDisplayName" :
{
"type" : "string",
"description" : "Display name of account that created and hosts this game room"
},
"description" :
{
"type" : "string",
"description" : "Auto-generated description of this room"
},
"participants" :
{
"type" : "array",
"description" : "List of accounts in the room, including host",
"items" :
{
"type" : "object",
"additionalProperties" : false,
"required" : [ "accountID", "displayName" ],
"properties" : {
"accountID" :
{
"type" : "string",
"description" : "Unique ID of an account"
},
"displayName" :
{
"type" : "string",
"description" : "Display name of an account"
}
}
}
},
"status" :
{
"type" : "string",
"enum" : [ "idle", "public", "private", "busy", "cancelled", "closed" ],
"description" : "Current status of game room"
},
"playerLimit" :
{
"type" : "number",
"minimum" : 1,
"maximum" : 8,
"description" : "Maximum number of players that can join this room, including host"
},
"ageSeconds" :
{
"type" : "number",
"description" : "Age of this room in seconds. For example, 10 means that this room was created 10 seconds ago"
}
}
}
}
}
}

View File

@@ -51,9 +51,9 @@
"rect": {"x": 5, "y": 50, "w": 250, "h": 500}
},
{
"name" : "headerRoomList",
"type": "labelTitle",
"position": {"x": 15, "y": 53},
"text" : "vcmi.lobby.header.rooms"
"position": {"x": 15, "y": 53}
},
{
"type" : "lobbyItemList",
@@ -71,9 +71,9 @@
"rect": {"x": 270, "y": 50, "w": 150, "h": 140}
},
{
"name" : "headerChannelList",
"type": "labelTitle",
"position": {"x": 280, "y": 53},
"text" : "vcmi.lobby.header.channels"
"position": {"x": 280, "y": 53}
},
{
"type" : "lobbyItemList",
@@ -89,9 +89,9 @@
"rect": {"x": 270, "y": 210, "w": 150, "h": 380}
},
{
"name" : "headerMatchList",
"type": "labelTitle",
"position": {"x": 280, "y": 213},
"text" : "vcmi.lobby.header.history"
"position": {"x": 280, "y": 213}
},
{
"type" : "lobbyItemList",
@@ -109,9 +109,9 @@
"rect": {"x": 430, "y": 50, "w": 430, "h": 515}
},
{
"name" : "headerGameChat",
"type": "labelTitle",
"position": {"x": 440, "y": 53},
"text" : "vcmi.lobby.header.chat"
"position": {"x": 440, "y": 53}
},
{
"type": "textBox",
@@ -139,9 +139,9 @@
"rect": {"x": 870, "y": 50, "w": 150, "h": 540}
},
{
"name": "headerAccountList",
"type": "labelTitle",
"position": {"x": 880, "y": 53},
"text" : "vcmi.lobby.header.players"
"position": {"x": 880, "y": 53}
},
{
"type" : "lobbyItemList",

View File

@@ -41,7 +41,7 @@ std::string LobbyServer::sanitizeChatMessage(const std::string & inputString) co
// - control characters ('\0' ... ' ')
// - '{' and '}' symbols to avoid formatting
// - other non-printable characters?
return inputString;
return boost::trim_copy(inputString);
}
NetworkConnectionPtr LobbyServer::findAccount(const std::string & accountID) const
@@ -285,9 +285,9 @@ void LobbyServer::onDisconnected(const NetworkConnectionPtr & connection, const
std::string gameRoomID = activeGameRooms.at(connection);
database->setGameRoomStatus(gameRoomID, LobbyRoomState::CLOSED);
for(const auto & connection : activeAccounts)
if (database->isPlayerInGameRoom(connection.second, gameRoomID))
sendMatchesHistory(connection.first);
for(const auto & accountConnection : activeAccounts)
if (database->isPlayerInGameRoom(accountConnection.second, gameRoomID))
sendMatchesHistory(accountConnection.first);
activeGameRooms.erase(connection);
}