// SPDX-License-Identifier: GPL-3.0-or-later import QtQuick import QtQuick.Controls import QtQuick.Window import QtQuick.Layouts import Fk.LobbyElement import Fk.Common import "Logic.js" as Logic Item { id: root property alias roomModel: roomModel property string password Rectangle { width: parent.width / 2 - roomListLayout.width / 2 height: parent.height * 0.7 anchors.top: exitButton.bottom anchors.bottom: createRoomButton.top anchors.right: parent.right anchors.rightMargin: 20 color: "#88EEEEEE" radius: 6 Flickable { id: flickableContainer ScrollBar.vertical: ScrollBar {} anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 flickableDirection: Flickable.VerticalFlick width: parent.width - 10 height: parent.height - 10 contentHeight: bulletin_info.height clip: true Text { id: bulletin_info width: parent.width wrapMode: TextEdit.WrapAnywhere textFormat: Text.MarkdownText text: Backend.translate('Bulletin Info') } } } Component { id: roomDelegate Item { height: 48 width: roomList.width RowLayout { anchors.fill: parent spacing: 16 Text { text: roomId color: "grey" } Text { horizontalAlignment: Text.AlignLeft Layout.fillWidth: true text: roomName + (hasPassword ? "(🔒)" : "") font.pixelSize: 20 } Text { text: Backend.translate(gameMode) } Text { color: (playerNum == capacity) ? "red" : "black" text: playerNum + "/" + capacity font.pixelSize: 20 font.bold: true } Button { text: (playerNum < capacity) ? Backend.translate("Enter") : Backend.translate("Observe") onClicked: { if (hasPassword) { lobby_dialog.sourceComponent = enterPassword; lobby_dialog.item.roomId = roomId; lobby_dialog.item.playerNum = playerNum; lobby_dialog.item.capacity = capacity; lobby_drawer.open(); } else { enterRoom(roomId, playerNum, capacity, ""); } } } } } } ListModel { id: roomModel } PersonalSettings { } RowLayout { id: roomListLayout anchors.centerIn: parent width: childrenRect.width height: parent.height Item { Layout.preferredWidth: root.width * 0.6 Layout.fillHeight: true Rectangle { width: parent.width * 0.8 height: parent.height * 0.8 anchors.centerIn: parent color: "#88EEEEEE" radius: 16 Text { width: parent.width horizontalAlignment: Text.AlignHCenter text: Backend.translate("Room List") } ListView { id: roomList height: parent.height * 0.9 width: parent.width * 0.95 contentHeight: roomDelegate.height * count ScrollBar.vertical: ScrollBar {} anchors.centerIn: parent delegate: roomDelegate model: roomModel } } } } Button { id: createRoomButton anchors.bottom: buttonRow.top anchors.right: parent.right width: 120 display: AbstractButton.TextUnderIcon icon.name: "media-playback-start" text: Backend.translate("Create Room") onClicked: { lobby_dialog.sourceComponent = Qt.createComponent("../LobbyElement/CreateRoom.qml"); lobby_drawer.open(); config.observing = false; } } RowLayout { id: buttonRow anchors.right: parent.right anchors.bottom: parent.bottom Button { text: Backend.translate("Generals Overview") onClicked: { mainStack.push(mainWindow.generalsOverviewPage); mainStack.currentItem.loadPackages(); } } Button { text: Backend.translate("Cards Overview") onClicked: { mainStack.push(mainWindow.cardsOverviewPage); mainStack.currentItem.loadPackages(); } } Button { text: Backend.translate("Scenarios Overview") onClicked: { mainStack.push(mainWindow.modesOverviewPage); } } Button { text: Backend.translate("Replay") } Button { text: Backend.translate("About") onClicked: { mainStack.push(mainWindow.aboutPage); } } } Button { id: exitButton anchors.right: parent.right text: Backend.translate("Exit Lobby") display: AbstractButton.TextBesideIcon icon.name: "application-exit" onClicked: { toast.show("Goodbye."); mainStack.pop(); Backend.quitLobby(); } } Drawer { id: lobby_drawer width: parent.width * 0.4 / mainWindow.scale height: parent.height / mainWindow.scale dim: false clip: true dragMargin: 0 scale: mainWindow.scale transformOrigin: Item.TopLeft Loader { id: lobby_dialog anchors.fill: parent onSourceChanged: { if (item === null) return; item.finished.connect(() => { sourceComponent = undefined; lobby_drawer.close(); }); } onSourceComponentChanged: sourceChanged(); } } Component { id: enterPassword ColumnLayout { property int roomId property int playerNum property int capacity signal finished() anchors.fill: parent anchors.margins: 16 Text { text: Backend.translate("Please input room's password") } TextField { id: passwordEdit onTextChanged: root.password = text; } Button { text: "OK" onClicked: { enterRoom(roomId, playerNum, capacity, root.password); parent.finished(); } } Component.onCompleted: { passwordEdit.text = ""; } } } function enterRoom(roomId, playerNum, capacity, pw) { if (playerNum < capacity) { config.observing = false; mainWindow.busy = true; ClientInstance.notifyServer( "EnterRoom", JSON.stringify([roomId, pw]) ); } else { config.observing = true; mainWindow.busy = true; ClientInstance.notifyServer( "ObserveRoom", JSON.stringify([roomId, pw]) ); } } property int lobbyPlayerNum: 0 property int serverPlayerNum: 0 function updateOnlineInfo() { } onLobbyPlayerNumChanged: updateOnlineInfo(); onServerPlayerNumChanged: updateOnlineInfo(); Rectangle { id: info color: "#88EEEEEE" width: childrenRect.width + 8 height: childrenRect.height + 4 anchors.bottom: parent.bottom anchors.left: parent.left radius: 4 Text { x: 4; y: 2 font.pixelSize: 16 text: Backend.translate("$OnlineInfo") .arg(lobbyPlayerNum).arg(serverPlayerNum) + "\n" + "Powered by FreeKill " + FkVersion } } ChatBox { id: lobbyChat anchors.bottom: info.top width: info.width height: root.height * 0.6 isLobby: true color: "#88EEEEEE" radius: 4 } Danmaku { id: danmaku width: parent.width } function addToChat(pid, raw, msg) { if (raw.type !== 1) return; msg = msg.replace(/\{emoji([0-9]+)\}/g, ''); raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, ''); lobbyChat.append(msg); danmaku.sendLog("" + raw.userName + ": " + raw.msg); } function sendDanmaku(msg) { danmaku.sendLog(msg); lobbyChat.append(msg); } Component.onCompleted: { toast.show(Backend.translate("$WelcomeToLobby")); } }