Browse Source

Merge pull request #2748 from Nordsoft91/lobby-chat

Advanced chat in lobby
Nordsoft91 2 years ago
parent
commit
43ef20efc5

+ 3 - 0
launcher/CMakeLists.txt

@@ -18,6 +18,7 @@ set(launcher_SRCS
 		lobby/lobby.cpp
 		lobby/lobby_moc.cpp
 		lobby/lobbyroomrequest_moc.cpp
+		lobby/chat_moc.cpp
 )
 
 set(launcher_HEADERS
@@ -39,6 +40,7 @@ set(launcher_HEADERS
 		lobby/lobby.h
 		lobby/lobby_moc.h
 		lobby/lobbyroomrequest_moc.h
+		lobby/chat_moc.h
 		main.h
 )
 
@@ -52,6 +54,7 @@ set(launcher_FORMS
 		updatedialog_moc.ui
 		lobby/lobby_moc.ui
 		lobby/lobbyroomrequest_moc.ui
+		lobby/chat_moc.ui
 )
 
 set(launcher_TS

+ 173 - 0
launcher/lobby/chat_moc.cpp

@@ -0,0 +1,173 @@
+/*
+ * chat_moc.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "chat_moc.h"
+#include "ui_chat_moc.h"
+
+Chat::Chat(QWidget *parent) :
+	QWidget(parent),
+	ui(new Ui::Chat)
+{
+	ui->setupUi(this);
+	
+	namesCompleter.setModel(ui->listUsers->model());
+	namesCompleter.setCompletionMode(QCompleter::InlineCompletion);
+	
+	ui->messageEdit->setCompleter(&namesCompleter);
+	
+	for([[maybe_unused]] auto i : {GLOBAL, ROOM})
+		chatDocuments.push_back(new QTextDocument(this));
+	
+	setChatId(GLOBAL);
+}
+
+Chat::~Chat()
+{
+	delete ui;
+}
+
+void Chat::setUsername(const QString & user)
+{
+	username = user;
+}
+
+void Chat::setSession(const QString & s)
+{
+	session = s;
+	
+	on_chatSwitch_clicked();
+}
+
+void Chat::setChannel(const QString & channel)
+{
+	static const QMap<QString, ChatId> chatNames{{"global", GLOBAL}, {"room", ROOM}};
+	
+	setChatId(chatNames.value(channel));
+}
+
+void Chat::addUser(const QString & user)
+{
+	ui->listUsers->addItem(new QListWidgetItem("@" + user));
+}
+
+void Chat::clearUsers()
+{
+	ui->listUsers->clear();
+}
+
+void Chat::chatMessage(const QString & title, const QString & channel, QString body, bool isSystem)
+{
+	const QTextCharFormat regularFormat;
+	const QString boldHtml = "<b>%1</b>";
+	const QString colorHtml = "<font color=\"%1\">%2</font>";
+	bool meMentioned = false;
+	bool isScrollBarBottom = (ui->chat->verticalScrollBar()->maximum() - ui->chat->verticalScrollBar()->value() < 24);
+	
+	static const QMap<QString, ChatId> chatNames{{"global", GLOBAL}, {"room", ROOM}};
+	QTextDocument * doc = ui->chat->document();
+	if(chatNames.contains(channel))
+		doc = chatDocuments[chatNames.value(channel)];
+	
+	QTextCursor curs(doc);
+	curs.movePosition(QTextCursor::End);
+	
+	QString titleColor = "Olive";
+	if(isSystem || title == "System")
+		titleColor = "ForestGreen";
+	if(title == username)
+		titleColor = "Gold";
+	
+	curs.insertHtml(boldHtml.arg(colorHtml.arg(titleColor, title + ": ")));
+	
+	QRegularExpression mentionRe("@[\\w\\d]+");
+	auto subBody = body;
+	int mem = 0;
+	for(auto match = mentionRe.match(subBody); match.hasMatch(); match = mentionRe.match(subBody))
+	{
+		body.insert(mem + match.capturedEnd(), QChar(-1));
+		body.insert(mem + match.capturedStart(), QChar(-1));
+		mem += match.capturedEnd() + 2;
+		subBody = body.right(body.size() - mem);
+	}
+	auto pieces = body.split(QChar(-1));
+	for(auto & block : pieces)
+	{
+		if(block.startsWith("@"))
+		{
+			if(block == "@" + username)
+			{
+				meMentioned = true;
+				curs.insertHtml(boldHtml.arg(colorHtml.arg("IndianRed", block)));
+			}
+			else
+				curs.insertHtml(colorHtml.arg("DeepSkyBlue", block));
+		}
+		else
+		{
+			if(isSystem)
+				curs.insertHtml(colorHtml.arg("ForestGreen", block));
+			else
+				curs.insertText(block, regularFormat);
+		}
+	}
+	curs.insertText("\n", regularFormat);
+	
+	if(doc == ui->chat->document() && (meMentioned || isScrollBarBottom))
+	{
+		ui->chat->ensureCursorVisible();
+		ui->chat->verticalScrollBar()->setValue(ui->chat->verticalScrollBar()->maximum());
+	}
+}
+
+void Chat::chatMessage(const QString & title, QString body, bool isSystem)
+{
+	chatMessage(title, "", body, isSystem);
+}
+
+void Chat::sysMessage(QString body)
+{
+	chatMessage("System", body, true);
+}
+
+void Chat::sendMessage()
+{
+	QString msg(ui->messageEdit->text());
+	ui->messageEdit->clear();
+	emit messageSent(msg);
+}
+
+void Chat::on_messageEdit_returnPressed()
+{
+	sendMessage();
+}
+
+void Chat::on_sendButton_clicked()
+{
+	sendMessage();
+}
+
+void Chat::on_chatSwitch_clicked()
+{
+	static const QMap<ChatId, QString> chatNames{{GLOBAL, "global"}, {ROOM, "room"}};
+	
+	if(chatId == GLOBAL && !session.isEmpty())
+		emit channelSwitch(chatNames[ROOM]);
+	else
+		emit channelSwitch(chatNames[GLOBAL]);
+}
+
+void Chat::setChatId(ChatId _chatId)
+{
+	static const QMap<ChatId, QString> chatNames{{GLOBAL, "Global"}, {ROOM, "Room"}};
+	
+	chatId = _chatId;
+	ui->chatSwitch->setText(chatNames[chatId] + " chat");
+	ui->chat->setDocument(chatDocuments[chatId]);
+}

+ 69 - 0
launcher/lobby/chat_moc.h

@@ -0,0 +1,69 @@
+/*
+ * chat_moc.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include <QWidget>
+#include <QCompleter>
+
+namespace Ui {
+class Chat;
+}
+
+class Chat : public QWidget
+{
+	Q_OBJECT
+	
+	enum ChatId
+	{
+		GLOBAL = 0,
+		ROOM
+	};
+	
+	QCompleter namesCompleter;
+	QString username, session;
+	ChatId chatId = GLOBAL;
+	
+	QVector<QTextDocument*> chatDocuments;
+	
+private:
+	void setChatId(ChatId);
+	void sendMessage();
+
+public:
+	explicit Chat(QWidget *parent = nullptr);
+	~Chat();
+	
+	void setUsername(const QString &);
+	void setSession(const QString &);
+	void setChannel(const QString &);
+	
+	void clearUsers();
+	void addUser(const QString & user);
+	
+	void chatMessage(const QString & title, const QString & channel, QString body, bool isSystem = false);
+	void chatMessage(const QString & title, QString body, bool isSystem = false);
+	
+signals:
+	void messageSent(QString);
+	void channelSwitch(QString);
+	
+public slots:
+	void sysMessage(QString body);
+
+private slots:
+	void on_messageEdit_returnPressed();
+
+	void on_sendButton_clicked();
+
+	void on_chatSwitch_clicked();
+
+private:
+	Ui::Chat *ui;
+};

+ 121 - 0
launcher/lobby/chat_moc.ui

@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Chat</class>
+ <widget class="QWidget" name="Chat">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>465</width>
+    <height>413</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout2">
+     <property name="bottomMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Users in lobby</string>
+       </property>
+       <property name="indent">
+        <number>-1</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="chatSwitch">
+       <property name="text">
+        <string>Global chat</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QListWidget" name="listUsers">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>96</height>
+      </size>
+     </property>
+     <property name="midLineWidth">
+      <number>0</number>
+     </property>
+     <property name="editTriggers">
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::NoSelection</enum>
+     </property>
+     <property name="isWrapping" stdset="0">
+      <bool>true</bool>
+     </property>
+     <property name="layoutMode">
+      <enum>QListView::SinglePass</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTextBrowser" name="chat"/>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>-1</number>
+     </property>
+     <property name="bottomMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLineEdit" name="messageEdit">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="placeholderText">
+        <string>type you message</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="sendButton">
+       <property name="text">
+        <string>send</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 19 - 4
launcher/lobby/lobby.h

@@ -12,7 +12,7 @@
 #include <QTcpSocket>
 #include <QAbstractSocket>
 
-const unsigned int ProtocolVersion = 4;
+const unsigned int ProtocolVersion = 5;
 const std::string ProtocolEncoding = "utf8";
 
 class ProtocolError: public std::runtime_error
@@ -24,10 +24,10 @@ public:
 enum ProtocolConsts
 {
 	//client consts
-	GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART, HERE, ALIVE, HOSTMODE,
+	GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART, HERE, ALIVE, HOSTMODE, SETCHANNEL,
 
 	//server consts
-	SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, START, STATUS, HOST, MODS, CLIENTMODS, USERS, HEALTH, GAMEMODE
+	SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, CHATCHANNEL, START, STATUS, HOST, MODS, CLIENTMODS, USERS, HEALTH, GAMEMODE, CHANNEL
 };
 
 const QMap<ProtocolConsts, QString> ProtocolStrings
@@ -88,6 +88,10 @@ const QMap<ProtocolConsts, QString> ProtocolStrings
 	//host sets game mode (new game or load game)
 	//%1: game mode - 0 for new game, 1 for load game
 	{HOSTMODE, "<HOSTMODE>%1"},
+	
+	//set new chat channel
+	//%1: channel name
+	{SETCHANNEL, "<CHANNEL>%1"},
 
 	//=== server commands ===
 	//server commands are started from :>>, arguments are enumerated by : symbol
@@ -149,9 +153,16 @@ const QMap<ProtocolConsts, QString> ProtocolStrings
 	
 	//received chat message
 	//arg[0]: sender username
-	//arg[1]: message text
+	//arg[1]: channel
+	//arg[2]: message text
 	{CHAT, "MSG"},
 	
+	//received chat message to specific channel
+	//arg[0]: sender username
+	//arg[1]: channel
+	//arg[2]: message text
+	{CHATCHANNEL, "MSGCH"},
+	
 	//list of users currently in lobby
 	//arg[0]: amount of players, following arguments depend on it
 	//arg[x]: username
@@ -164,6 +175,10 @@ const QMap<ProtocolConsts, QString> ProtocolStrings
 	//game mode (new game or load game) set by host
 	//arg[0]: game mode
 	{GAMEMODE, "GAMEMODE"},
+	
+	//chat channel changed
+	//arg[0]: channel name
+	{CHANNEL, "CHANNEL"},
 };
 
 class ServerCommand

+ 46 - 41
launcher/lobby/lobby_moc.cpp

@@ -34,9 +34,11 @@ Lobby::Lobby(QWidget *parent) :
 {
 	ui->setupUi(this);
 
-	connect(&socketLobby, SIGNAL(text(QString)), this, SLOT(sysMessage(QString)));
+	connect(&socketLobby, SIGNAL(text(QString)), ui->chatWidget, SLOT(sysMessage(QString)));
 	connect(&socketLobby, SIGNAL(receive(QString)), this, SLOT(dispatchMessage(QString)));
 	connect(&socketLobby, SIGNAL(disconnect()), this, SLOT(onDisconnected()));
+	connect(ui->chatWidget, SIGNAL(messageSent(QString)), this, SLOT(onMessageSent(QString)));
+	connect(ui->chatWidget, SIGNAL(channelSwitch(QString)), this, SLOT(onChannelSwitch(QString)));
 	
 	QString hostString("%1:%2");
 	hostString = hostString.arg(QString::fromStdString(settings["launcher"]["lobbyUrl"].String()));
@@ -110,7 +112,7 @@ void Lobby::serverCommand(const ServerCommand & command) try
 	{
 	case SRVERROR:
 		protocolAssert(args.size());
-		chatMessage("System error", args[0], true);
+		ui->chatWidget->chatMessage("System error", args[0], true);
 		if(authentificationStatus == AuthStatus::AUTH_NONE)
 			authentificationStatus = AuthStatus::AUTH_ERROR;
 		break;
@@ -119,7 +121,7 @@ void Lobby::serverCommand(const ServerCommand & command) try
 		protocolAssert(args.size());
 		hostSession = args[0];
 		session = args[0];
-		sysMessage("new session started");
+		ui->chatWidget->setSession(session);
 		break;
 
 	case SESSIONS:
@@ -151,15 +153,15 @@ void Lobby::serverCommand(const ServerCommand & command) try
 	case JOINED:
 	case KICKED:
 		protocolAssert(args.size() == 2);
-		joinStr = (command.command == JOINED ? "%1 joined to the session %2" : "%1 left session %2");
-
+		session = "";
+		ui->chatWidget->setSession(session);
 		if(args[1] == username)
 		{
 			hostModsMap.clear();
 			ui->buttonReady->setText("Ready");
 			ui->optNewGame->setChecked(true);
-			sysMessage(joinStr.arg("you", args[0]));
 			session = args[0];
+			ui->chatWidget->setSession(session);
 			bool isHost = command.command == JOINED && hostSession == session;
 			ui->optNewGame->setEnabled(isHost);
 			ui->optLoadGame->setEnabled(isHost);
@@ -167,7 +169,8 @@ void Lobby::serverCommand(const ServerCommand & command) try
 		}
 		else
 		{
-			sysMessage(joinStr.arg(args[1], args[0]));
+			joinStr = (command.command == JOINED ? "%1 joined to the session %2" : "%1 left session %2");
+			ui->chatWidget->sysMessage(joinStr.arg(args[1], args[0]));
 		}
 		break;
 
@@ -244,7 +247,22 @@ void Lobby::serverCommand(const ServerCommand & command) try
 		QString msg;
 		for(int i = 1; i < args.size(); ++i)
 			msg += args[i];
-		chatMessage(args[0], msg);
+		ui->chatWidget->chatMessage(args[0], msg);
+		break;
+		}
+			
+	case CHATCHANNEL: {
+		protocolAssert(args.size() > 2);
+		QString msg;
+		for(int i = 2; i < args.size(); ++i)
+			msg += args[i];
+		ui->chatWidget->chatMessage(args[0], args[1], msg);
+		break;
+		}
+			
+	case CHANNEL: {
+		protocolAssert(args.size() == 1);
+		ui->chatWidget->setChannel(args[0]);
 		break;
 		}
 			
@@ -258,10 +276,10 @@ void Lobby::serverCommand(const ServerCommand & command) try
 		amount = args[0].toInt();
 		
 		protocolAssert(amount == (args.size() - 1));
-		ui->listUsers->clear();
+		ui->chatWidget->clearUsers();
 		for(int i = 0; i < amount; ++i)
 		{
-			ui->listUsers->addItem(new QListWidgetItem(args[i + 1]));
+			ui->chatWidget->addUser(args[i + 1]);
 		}
 		break;
 	}
@@ -277,7 +295,7 @@ void Lobby::serverCommand(const ServerCommand & command) try
 	}
 
 	default:
-		sysMessage("Unknown server command");
+			ui->chatWidget->sysMessage("Unknown server command");
 	}
 
 	if(authentificationStatus == AuthStatus::AUTH_ERROR)
@@ -292,7 +310,7 @@ void Lobby::serverCommand(const ServerCommand & command) try
 }
 catch(const ProtocolError & e)
 {
-	chatMessage("System error", e.what(), true);
+	ui->chatWidget->chatMessage("System error", e.what(), true);
 }
 
 void Lobby::dispatchMessage(QString txt) try
@@ -316,12 +334,14 @@ void Lobby::dispatchMessage(QString txt) try
 }
 catch(const ProtocolError & e)
 {
-	chatMessage("System error", e.what(), true);
+	ui->chatWidget->chatMessage("System error", e.what(), true);
 }
 
 void Lobby::onDisconnected()
 {
 	authentificationStatus = AuthStatus::AUTH_NONE;
+	session = "";
+	ui->chatWidget->setSession(session);
 	ui->stackedWidget->setCurrentWidget(ui->sessionsPage);
 	ui->connectButton->setChecked(false);
 	ui->serverEdit->setEnabled(true);
@@ -331,37 +351,12 @@ void Lobby::onDisconnected()
 	ui->sessionsTable->setRowCount(0);
 }
 
-void Lobby::chatMessage(QString title, QString body, bool isSystem)
-{
-	QTextCharFormat fmtBody, fmtTitle;
-	fmtTitle.setFontWeight(QFont::Bold);
-	if(isSystem)
-		fmtBody.setFontWeight(QFont::DemiBold);
-	
-	QTextCursor curs(ui->chat->document());
-	curs.movePosition(QTextCursor::End);
-	curs.insertText(title + ": ", fmtTitle);
-	curs.insertText(body + "\n", fmtBody);
-	ui->chat->ensureCursorVisible();
-}
-
-void Lobby::sysMessage(QString body)
-{
-	chatMessage("System", body, true);
-}
-
 void Lobby::protocolAssert(bool expr)
 {
 	if(!expr)
 		throw ProtocolError("Protocol error");
 }
 
-void Lobby::on_messageEdit_returnPressed()
-{
-	socketLobby.send(ProtocolStrings[MESSAGE].arg(ui->messageEdit->text()));
-	ui->messageEdit->clear();
-}
-
 void Lobby::on_connectButton_toggled(bool checked)
 {
 	if(checked)
@@ -369,6 +364,7 @@ void Lobby::on_connectButton_toggled(bool checked)
 		ui->connectButton->setText(tr("Disconnect"));
 		authentificationStatus = AuthStatus::AUTH_NONE;
 		username = ui->userEdit->text();
+		ui->chatWidget->setUsername(username);
 		const int connectionTimeout = settings["launcher"]["connectionTimeout"].Integer();
 		
 		auto serverStrings = ui->serverEdit->text().split(":");
@@ -389,9 +385,9 @@ void Lobby::on_connectButton_toggled(bool checked)
 		ui->serverEdit->setEnabled(false);
 		ui->userEdit->setEnabled(false);
 
-		sysMessage("Connecting to " + serverUrl + ":" + QString::number(serverPort));
+		ui->chatWidget->sysMessage("Connecting to " + serverUrl + ":" + QString::number(serverPort));
 		//show text immediately
-		ui->chat->repaint();
+		ui->chatWidget->repaint();
 		qApp->processEvents();
 		
 		socketLobby.connectServer(serverUrl, serverPort, username, connectionTimeout);
@@ -401,7 +397,7 @@ void Lobby::on_connectButton_toggled(bool checked)
 		ui->connectButton->setText(tr("Connect"));
 		ui->serverEdit->setEnabled(true);
 		ui->userEdit->setEnabled(true);
-		ui->listUsers->clear();
+		ui->chatWidget->clearUsers();
 		hostModsMap.clear();
 		updateMods();
 		socketLobby.disconnectServer();
@@ -564,3 +560,12 @@ void Lobby::on_optLoadGame_toggled(bool checked)
 	}
 }
 
+void Lobby::onMessageSent(QString message)
+{
+	socketLobby.send(ProtocolStrings[MESSAGE].arg(message));
+}
+
+void Lobby::onChannelSwitch(QString channel)
+{
+	socketLobby.send(ProtocolStrings[SETCHANNEL].arg(channel));
+}

+ 2 - 4
launcher/lobby/lobby_moc.h

@@ -33,12 +33,10 @@ public slots:
 	void updateMods();
 
 private slots:
-	void on_messageEdit_returnPressed();
-
-	void chatMessage(QString title, QString body, bool isSystem = false);
-	void sysMessage(QString body);
 	void dispatchMessage(QString);
 	void serverCommand(const ServerCommand &);
+	void onMessageSent(QString message);
+	void onChannelSwitch(QString channel);
 
 	void on_connectButton_toggled(bool checked);
 

+ 49 - 47
launcher/lobby/lobby_moc.ui

@@ -14,6 +14,9 @@
    <string/>
   </property>
   <layout class="QGridLayout" name="gridLayout">
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
    <item row="0" column="3">
     <widget class="QLabel" name="label_4">
      <property name="sizePolicy">
@@ -62,69 +65,33 @@
    </item>
    <item row="1" column="0" colspan="6">
     <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>-1</number>
+     </property>
      <property name="bottomMargin">
       <number>0</number>
      </property>
      <item>
       <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <property name="leftMargin">
+        <number>10</number>
+       </property>
        <property name="bottomMargin">
         <number>0</number>
        </property>
        <item>
-        <widget class="QLabel" name="label_2">
-         <property name="text">
-          <string>Players in lobby</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QListWidget" name="listUsers">
+        <widget class="Chat" name="chatWidget" native="true">
          <property name="sizePolicy">
-          <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
-         <property name="maximumSize">
-          <size>
-           <width>16777215</width>
-           <height>96</height>
-          </size>
-         </property>
-         <property name="midLineWidth">
-          <number>0</number>
-         </property>
-         <property name="editTriggers">
-          <set>QAbstractItemView::NoEditTriggers</set>
-         </property>
-         <property name="selectionMode">
-          <enum>QAbstractItemView::NoSelection</enum>
-         </property>
-         <property name="isWrapping" stdset="0">
-          <bool>true</bool>
-         </property>
-         <property name="layoutMode">
-          <enum>QListView::SinglePass</enum>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QLabel" name="label_6">
-         <property name="text">
-          <string>Lobby chat</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPlainTextEdit" name="chat">
-         <property name="readOnly">
-          <bool>true</bool>
-         </property>
         </widget>
        </item>
-       <item>
-        <widget class="QLineEdit" name="messageEdit"/>
-       </item>
       </layout>
      </item>
      <item>
@@ -140,6 +107,18 @@
        </property>
        <widget class="QWidget" name="sessionsPage">
         <layout class="QGridLayout" name="gridLayout_2">
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
          <item row="1" column="0">
           <widget class="QPushButton" name="newButton">
            <property name="enabled">
@@ -210,6 +189,21 @@
        </widget>
        <widget class="QWidget" name="roomPage">
         <layout class="QGridLayout" name="gridLayout_3">
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <property name="verticalSpacing">
+          <number>-1</number>
+         </property>
          <item row="1" column="1">
           <widget class="QPushButton" name="kickButton">
            <property name="text">
@@ -304,6 +298,14 @@
    </item>
   </layout>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Chat</class>
+   <extends>QWidget</extends>
+   <header>lobby/chat_moc.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections/>
 </ui>