FreeKill/Fk/main.qml
notify 9d9217da2c
Cppdev (#350)
- 对旁观和重连进行优化,减轻服务器CPU负担
- 加强Lua与C++交互能力,现在可以直接传const QVariant &参数
- 借助上一条,删除了客户端侧代码绝大多数冗余的json.encode/JSON.parse
- 微调swig代码减少生成量,将int映射到lua integer而不是number
2024-04-19 20:53:19 +08:00

275 lines
6.9 KiB
QML

// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Dialogs
import QtQuick.Controls
import QtQuick.Window
import "Logic.js" as Logic
import Fk.Pages
Window {
id: realMainWin
visible: true
width: 960
height: 540
minimumWidth: 160
minimumHeight: 90
title: qsTr("FreeKill") + " v" + FkVersion
property var callbacks: Logic.callbacks
property list<string> tipList: []
Item {
id: mainWindow
width: (parent.width / parent.height < 960 / 540)
? 960 : 540 * parent.width / parent.height
height: (parent.width / parent.height > 960 / 540)
? 540 : 960 * parent.height / parent.width
scale: parent.width / width
anchors.centerIn: parent
property bool is_pending: false
property var pending_message: []
Config {
id: config
}
Image {
source: config.lobbyBg
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
}
FontLoader { id: fontLibian; source: AppPath + "/fonts/FZLBGBK.ttf" }
FontLoader { id: fontLi2; source: AppPath + "/fonts/FZLE.ttf" }
StackView {
id: mainStack
visible: !mainWindow.busy
// If error occurs during loading initialItem
// the program will fall into "polish()" loop
// initialItem: init
anchors.fill: parent
}
Component { id: init; Init {} }
Component { id: packageManage; PackageManage {} }
Component { id: modMaker; ModMaker {} }
Component { id: lobby; Lobby {} }
Component { id: generalsOverview; GeneralsOverview {} }
Component { id: cardsOverview; CardsOverview {} }
Component { id: modesOverview; ModesOverview {} }
Component { id: replay; Replay {} }
Component { id: room; Room {} }
Component { id: aboutPage; About {} }
property alias generalsOverviewPage: generalsOverview
property alias cardsOverviewPage: cardsOverview
property alias modesOverviewPage: modesOverview
property alias aboutPage: aboutPage
property alias replayPage: replay
property bool busy: false
property string busyText: ""
onBusyChanged: busyText = "";
property bool closing: false
BusyIndicator {
id: busyIndicator
running: true
anchors.centerIn: parent
visible: mainWindow.busy === true
}
Text {
anchors.top: busyIndicator.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 8
visible: mainWindow.busy === true
property int idx: 1
text: tipList[idx - 1] ?? ""
color: "#F0E5DA"
font.pixelSize: 20
font.family: fontLibian.name
style: Text.Outline
styleColor: "#3D2D1C"
textFormat: Text.RichText
width: parent.width * 0.7
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WrapAnywhere
onVisibleChanged: idx = 0;
Timer {
running: parent.visible
interval: 3600
repeat: true
onTriggered: {
const oldIdx = parent.idx;
while (parent.idx === oldIdx) {
parent.idx = Math.floor(Math.random() * tipList.length) + 1;
}
}
}
}
Item {
visible: mainWindow.busy === true && mainWindow.busyText !== ""
anchors.bottom: parent.bottom
height: 32
width: parent.width
Rectangle {
anchors.fill: parent
color: "#88EEEEEE"
}
Text {
anchors.centerIn: parent
text: mainWindow.busyText
font.pixelSize: 24
}
}
Popup {
id: errDialog
property string txt: ""
modal: true
anchors.centerIn: parent
width: Math.min(contentWidth + 24, realMainWin.width * 0.9)
height: Math.min(contentHeight + 24, realMainWin.height * 0.9)
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
padding: 12
contentItem: Text {
text: errDialog.txt
wrapMode: Text.WordWrap
TapHandler {
onTapped: errDialog.close();
}
}
}
ToastManager {
id: toast
}
Connections {
target: Backend
function onNotifyUI(command, jsonData) {
if (command === "ErrorDialog") {
errDialog.txt = jsonData;
errDialog.open();
return;
}
if (mainWindow.is_pending && command !== "ChangeSelf") {
mainWindow.pending_message.push({
command: command,
jsonData: jsonData,
});
} else {
if (command === "StartChangeSelf") {
mainWindow.is_pending = true;
}
mainWindow.handleMessage(command, jsonData);
}
}
}
function fetchMessage() {
const ret = pending_message.splice(0, 1)[0];
if (pending_message.length === 0) {
is_pending = false;
}
return ret;
}
function handleMessage(command, jsonData) {
const cb = callbacks[command]
if (typeof(cb) === "function") {
cb(jsonData);
} else {
callbacks["ErrorMsg"]("Unknown command " + command + "!");
}
}
}
Shortcut {
sequences: [ StandardKey.FullScreen ]
onActivated: {
if (realMainWin.visibility === Window.FullScreen)
realMainWin.showNormal();
else
realMainWin.showFullScreen();
}
}
Loader {
id: splashLoader
anchors.fill: parent
}
Component.onCompleted: {
mainStack.push(init);
if (config.firstRun) {
config.firstRun = false;
mainStack.push(Qt.createComponent("Tutorial.qml").createObject());
}
if (!Debugging) {
splashLoader.source = "Splash.qml";
splashLoader.item.disappeared.connect(() => {
splashLoader.source = "";
});
}
if (OS !== "Android" && OS !== "Web") {
x = config.winX;
y = config.winY;
width = config.winWidth;
height = config.winHeight;
}
const tips = Backend.loadTips();
tipList = tips.trim().split("\n");
}
MessageDialog {
id: exitMessageDialog
title: realMainWin.title
informativeText: qsTr("Are you sure to exit?")
buttons: MessageDialog.Ok | MessageDialog.Cancel
onButtonClicked: function (button, role) {
switch (button) {
case MessageDialog.Ok: {
mainWindow.closing = true;
config.winWidth = width;
config.winHeight = height;
config.saveConf();
Backend.quitLobby(false);
realMainWin.close();
break;
}
case MessageDialog.Cancel: {
exitMessageDialog.close();
}
}
}
}
onClosing: (closeEvent) => {
if (!mainWindow.closing) {
closeEvent.accepted = false;
exitMessageDialog.open();
}
}
// fake global functions
function lcall(funcName, ...params) {
return Backend.callLuaFunction(funcName, [...params]);
}
function leval(lua) {
return Backend.evalLuaExp(`return ${lua}`);
}
function luatr(src) {
return Backend.translate(src);
}
}