Enhancement (#362)

1. 新增隐藏礼物选项
2. 无效技能ui显示🔒
3. 过期房间字符串显示删除线
5. 按钮键长按查看技能详情
6. 筛选房间功能
7. “禁用lua扩展”禁用
8. 调整服务器“从收藏移除”的ui,改为三点展开
9. 调整红温缩进
10. 房间内限制玩家名称长度(自己除外)
11. 玩家详情显示判定区
12. 房间内一览
13. 武将一览语音键增加按钮复制代码与文本(长按复制代码),悬停显示
14. 手牌排序多选:(默认)类型、点数、花色
15. 技能次数提示,指定为正数或0显示
16. 修复ArrangeCardsBox的报错
17. 手牌拖拽排序
18. 武将技能按顺序添加
This commit is contained in:
Nyutanislavsky 2024-09-18 23:53:38 +08:00 committed by GitHub
parent 3d942a24b0
commit e8aacf1888
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 922 additions and 104 deletions

View File

@ -56,11 +56,8 @@ Flickable {
skillDesc.append(ret) skillDesc.append(ret)
} }
data.skill.forEach(t => { data.skill.forEach(t => {
skillDesc.append("<b>" + luatr(t.name) + "</b>: " + t.description) skillDesc.append((t.is_related_skill ? "<font color=\"purple\"><b>" : "<b>") + luatr(t.name) +
}); "</b>: " + t.description + (t.is_related_skill ? "</font>" : ""));
data.related_skill.forEach(t => {
skillDesc.append("<font color=\"purple\"><b>" + luatr(t.name) +
"</b>: " + t.description + "</font>")
}); });
skillDesc.append("\n"); skillDesc.append("\n");
}); });

View File

@ -212,5 +212,17 @@ Flickable {
skillDesc.append("--------------------"); skillDesc.append("--------------------");
skillDesc.append("<b>" + luatr(t.name) + "</b>: " + luatr(":" + t.name)); skillDesc.append("<b>" + luatr(t.name) + "</b>: " + luatr(":" + t.name));
}); });
const judge = leval(
`(function()
local p = ClientInstance:getPlayerById(${id})
return p.player_cards[Player.Judge]
end)()`
);
judge.forEach(cid => {
const t = lcall("GetCardData", cid);
skillDesc.append("--------------------");
skillDesc.append("<b>" + luatr(t.name) + "</b>: " + luatr(":" + t.name));
});
} }
} }

View File

@ -40,7 +40,7 @@ ListView {
} }
Button { Button {
text: "Return to Bottom" text: luatr("Return to Bottom")
visible: root.currentIndex !== logModel.count - 1 visible: root.currentIndex !== logModel.count - 1
onClicked: root.currentIndex = logModel.count - 1; onClicked: root.currentIndex = logModel.count - 1;
} }

View File

@ -20,12 +20,14 @@ QtObject {
property string preferedMode property string preferedMode
property int preferedPlayerNum property int preferedPlayerNum
property int preferredGeneralNum property int preferredGeneralNum
property var preferredFilter
property string ladyImg property string ladyImg
property real bgmVolume property real bgmVolume
property bool disableMsgAudio property bool disableMsgAudio
property bool hideUseless property bool hideUseless
property bool hideObserverChatter property bool hideObserverChatter
property bool rotateTableCard property bool rotateTableCard
property bool hidePresents
// property list<string> disabledGenerals: [] // property list<string> disabledGenerals: []
// property list<var> disableGeneralSchemes: [] // property list<var> disableGeneralSchemes: []
// property int disableSchemeIdx: 0 // property int disableSchemeIdx: 0
@ -126,6 +128,13 @@ QtObject {
preferedMode = conf.preferedMode ?? "aaa_role_mode"; preferedMode = conf.preferedMode ?? "aaa_role_mode";
preferedPlayerNum = conf.preferedPlayerNum ?? 2; preferedPlayerNum = conf.preferedPlayerNum ?? 2;
preferredGeneralNum = conf.preferredGeneralNum ?? 3; preferredGeneralNum = conf.preferredGeneralNum ?? 3;
preferredFilter = conf.preferredFilter ?? {
name: "", //
id: "", // ID
modes : [], //
full : 2, // 012
hasPassword : 2, // 012
};
ladyImg = conf.ladyImg ?? AppPath + "/image/lady"; ladyImg = conf.ladyImg ?? AppPath + "/image/lady";
Backend.volume = conf.effectVolume ?? 50.; Backend.volume = conf.effectVolume ?? 50.;
bgmVolume = conf.bgmVolume ?? 50.; bgmVolume = conf.bgmVolume ?? 50.;
@ -133,6 +142,7 @@ QtObject {
hideUseless = conf.hideUseless ?? false; hideUseless = conf.hideUseless ?? false;
hideObserverChatter = conf.hideObserverChatter ?? false; hideObserverChatter = conf.hideObserverChatter ?? false;
rotateTableCard = conf.rotateTableCard ?? false; rotateTableCard = conf.rotateTableCard ?? false;
hidePresents = conf.hidePresents ?? false;
preferredTimeout = conf.preferredTimeout ?? 15; preferredTimeout = conf.preferredTimeout ?? 15;
preferredLuckTime = conf.preferredLuckTime ?? 0; preferredLuckTime = conf.preferredLuckTime ?? 0;
firstRun = conf.firstRun ?? true; firstRun = conf.firstRun ?? true;
@ -165,6 +175,7 @@ QtObject {
// conf.disabledPack = disabledPack; // conf.disabledPack = disabledPack;
conf.preferedMode = preferedMode; conf.preferedMode = preferedMode;
conf.preferedPlayerNum = preferedPlayerNum; conf.preferedPlayerNum = preferedPlayerNum;
conf.preferredFilter = preferredFilter;
conf.ladyImg = ladyImg; conf.ladyImg = ladyImg;
conf.preferredGeneralNum = preferredGeneralNum; conf.preferredGeneralNum = preferredGeneralNum;
conf.effectVolume = Backend.volume; conf.effectVolume = Backend.volume;
@ -173,6 +184,7 @@ QtObject {
conf.hideUseless = hideUseless; conf.hideUseless = hideUseless;
conf.hideObserverChatter = hideObserverChatter; conf.hideObserverChatter = hideObserverChatter;
conf.rotateTableCard = rotateTableCard; conf.rotateTableCard = rotateTableCard;
conf.hidePresents = hidePresents;
conf.preferredTimeout = preferredTimeout; conf.preferredTimeout = preferredTimeout;
conf.preferredLuckTime = preferredLuckTime; conf.preferredLuckTime = preferredLuckTime;
conf.firstRun = firstRun; conf.firstRun = firstRun;

View File

@ -70,5 +70,13 @@ ColumnLayout {
} }
} }
Switch {
text: luatr("Hide presents")
checked: config.hidePresents
onCheckedChanged: {
config.hidePresents = checked;
}
}
} }
} }

View File

@ -0,0 +1,288 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Flickable {
id: root
height: parent.height
width: layout.width
anchors.fill: parent
anchors.margins: 16
clip: true
contentWidth: layout.width
contentHeight: layout.height
ScrollBar.vertical: ScrollBar {}
ScrollBar.horizontal: ScrollBar {} // considering long game mode name
signal finished()
ColumnLayout {
id: layout
anchors.top: parent.top
Item { Layout.fillHeight: true }
// roomId, roomName, gameMode, playerNum, capacity, hasPassword, outdated
GridLayout {
columns: 2
// roomName
RowLayout {
anchors.rightMargin: 8
spacing: 16
Text {
text: luatr("Room Name")
font.bold: true
font.pixelSize: 14
}
TextField {
id: name
maximumLength: 64
font.pixelSize: 18
Layout.rightMargin: 16
Layout.fillWidth: true
text: config.preferredFilter.name
}
}
// roomId
RowLayout {
anchors.rightMargin: 8
spacing: 16
Text {
text: luatr("Room ID")
font.bold: true
font.pixelSize: 14
}
TextField {
id: id
maximumLength: 64
font.pixelSize: 18
Layout.rightMargin: 16
Layout.fillWidth: true
text: config.preferredFilter.id
}
}
}
// gameMode
ButtonGroup {
id: childModes
exclusive: false
checkState: parentModeBox.checkState
}
CheckBox {
id: parentModeBox
text: luatr("Game Mode")
font.bold: true
checkState: childModes.checkState
}
GridLayout {
columns: 6
Repeater {
id: modes
model: ListModel {
id: gameModeList
}
CheckBox {
text: name
checked: config.preferredFilter.modes.includes(name)
leftPadding: indicator.width
ButtonGroup.group: childModes
}
}
}
RowLayout {
anchors.rightMargin: 8
// spacing: 64
// Layout.fillWidth: true
// full
Column {
ButtonGroup {
id: childFull
exclusive: false
checkState: parentFullBox.checkState
}
CheckBox {
id: parentFullBox
text: luatr("Room Fullness")
font.bold: true
checkState: childFull.checkState
}
GridLayout {
columns: 6
Repeater {
id: fullStates
model: ["Full", "Not Full"]
CheckBox {
text: luatr(modelData)
checked: config.preferredFilter.full === index
leftPadding: indicator.width
ButtonGroup.group: childFull
}
}
}
}
// hasPassword
Column {
ButtonGroup {
id: childPw
exclusive: false
checkState: parentPwBox.checkState
}
CheckBox {
id: parentPwBox
text: luatr("Room Password")
font.bold: true
checkState: childPw.checkState
}
GridLayout {
columns: 6
Repeater {
id: pwStates
model: ["Has Password", "No Password"]
CheckBox {
text: luatr(modelData)
checked: config.preferredFilter.hasPassword === index
leftPadding: indicator.width
ButtonGroup.group: childPw
}
}
}
}
Button {
text: luatr("Clear")
onClicked: {
opTimer.start();
config.preferredFilter = {
name: "",
id: "",
modes : [],
full : 2,
hasPassword : 2,
}
config.preferredFilterChanged();
ClientInstance.notifyServer("RefreshRoomList", "");
lobby_dialog.item.finished();
}
}
Button {
text: luatr("Filter")
// width: 200
// enabled: !opTimer.running
onClicked: {
// opTimer.start();
filterRoom();
root.finished();
}
}
}
// capacity
/*
Column {
ButtonGroup {
id: childCapacity
exclusive: false
checkState: parentCapacityBox.checkState
}
CheckBox {
id: parentCapacityBox
text: luatr("Room Capacity")
font.bold: true
checkState: childCapacity.checkState
}
GridLayout {
columns: 6
Repeater {
id: capacityStates
model: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
CheckBox {
text: modelData
checked: false
leftPadding: indicator.width
ButtonGroup.group: childCapacity
}
}
}
}
*/
Component.onCompleted: {
const mode_data = lcall("GetGameModes");
mode_data.forEach(d => {
gameModeList.append(d);
});
}
}
function filterRoom() {
let f = config.preferredFilter;
f.name = name.text;
f.id = id.text;
// mode
let modeList = [];
if (parentModeBox.checkState === Qt.PartiallyChecked) {
for (let index = 0; index < modes.count; index++) {
var tCheckBox = modes.itemAt(index)
if (tCheckBox.checked) {modeList.push(tCheckBox.text)}
}
}
f.modes = modeList;
f.full = parentFullBox.checkState === Qt.PartiallyChecked ? (fullStates.itemAt(0).checked ? 0 : 1) : 2;
f.hasPassword = parentPwBox.checkState === Qt.PartiallyChecked ? (pwStates.itemAt(0).checked ? 0 : 1) : 2;
// capacity
/*
let capacityList = [];
if (parentCapacityBox.checkState === Qt.PartiallyChecked) {
for (let index = 0; index < capacityStates.count; index++) {
var nCheckBox = capacityStates.itemAt(index)
if (nCheckBox.checked) {capacityList.push(parseInt(nCheckBox.text))}
}
}
*/
config.preferredFilterChanged();
for (let i = roomModel.count - 1; i >= 0; i--) {
const r = roomModel.get(i);
if ((name.text !== '' && !r.roomName.includes(name.text))
|| (id.text !== '' && !r.roomId.toString().includes(id.text))
|| (modeList.length > 0 && !modeList.includes(luatr(r.gameMode)))
|| (f.full !== 2 &&
(f.full === 0 ? r.playerNum < r.capacity : r.playerNum >= r.capacity))
|| (f.hasPassword !== 2 &&
(f.hasPassword === 0 ? !r.hasPassword : r.hasPassword))
// || (capacityList.length > 0 && !capacityList.includes(r.capacity))
) {
roomModel.remove(i);
}
}
}
}

View File

@ -22,9 +22,11 @@ Flickable {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 8 anchors.topMargin: 8
/*
Switch { Switch {
text: luatr("Disable Extension") text: luatr("Disable Extension")
} }
*/
RowLayout { RowLayout {
Text { Text {

View File

@ -1,8 +1,10 @@
module Fk.LobbyElement module Fk.LobbyElement
AudioSetting 1.0 AudioSetting.qml AudioSetting 1.0 AudioSetting.qml
BanGeneralSetting 1.0 BanGeneralSetting.qml
BGSetting 1.0 BGSetting.qml BGSetting 1.0 BGSetting.qml
CreateRoom 1.0 CreateRoom.qml CreateRoom 1.0 CreateRoom.qml
EditProfile 1.0 EditProfile.qml EditProfile 1.0 EditProfile.qml
FilterRoom 1.0 FilterRoom.qml
PersonalSettings 1.0 PersonalSettings.qml PersonalSettings 1.0 PersonalSettings.qml
RoomGeneralSettings 1.0 RoomGeneralSettings.qml RoomGeneralSettings 1.0 RoomGeneralSettings.qml
RoomPackageSettings 1.0 RoomPackageSettings.qml RoomPackageSettings 1.0 RoomPackageSettings.qml

View File

@ -8,6 +8,7 @@ import Fk.RoomElement
Item { Item {
id: root id: root
objectName: "CardsOverview"
property bool loaded: false property bool loaded: false
@ -196,6 +197,7 @@ Item {
property var cards property var cards
function updateCard() { function updateCard() {
const data = lcall("GetCardData", cid); const data = lcall("GetCardData", cid);
detailFlickable.contentY = 0; //
const suitTable = { const suitTable = {
spade: "♠", heart: '<font color="red">♥</font>', spade: "♠", heart: '<font color="red">♥</font>',
club: "♣", diamond: '<font color="red">♦</font>', club: "♣", diamond: '<font color="red">♦</font>',
@ -234,6 +236,7 @@ Item {
} }
Flickable { Flickable {
id: detailFlickable
flickableDirection: Flickable.VerticalFlick flickableDirection: Flickable.VerticalFlick
contentHeight: detailLayout.height contentHeight: detailLayout.height
width: parent.width - 40 width: parent.width - 40
@ -323,6 +326,7 @@ Item {
Button { Button {
text: luatr("Quit") text: luatr("Quit")
anchors.right: parent.right anchors.right: parent.right
visible: mainStack.currentItem.objectName === "CardsOverview"
onClicked: { onClicked: {
mainStack.pop(); mainStack.pop();
} }

View File

@ -9,6 +9,7 @@ import "RoomLogic.js" as RoomLogic
Item { Item {
id: root id: root
objectName: "GeneralsOverview"
property bool loaded: false property bool loaded: false
property int stat: 0 // 0=normal 1=banPkg 2=banChara property int stat: 0 // 0=normal 1=banPkg 2=banChara
@ -165,6 +166,7 @@ Item {
return luatr("BanGeneral"); return luatr("BanGeneral");
} }
enabled: stat !== 1 enabled: stat !== 1
visible: mainStack.currentItem.objectName === "GeneralsOverview"
onClicked: { onClicked: {
if (stat === 0) { if (stat === 0) {
stat = 2; stat = 2;
@ -182,6 +184,7 @@ Item {
return luatr("BanPackage"); return luatr("BanPackage");
} }
enabled: stat !== 2 enabled: stat !== 2
visible: mainStack.currentItem.objectName === "GeneralsOverview"
onClicked: { onClicked: {
if (stat === 0) { if (stat === 0) {
stat = 1; stat = 1;
@ -194,6 +197,7 @@ Item {
ToolButton { ToolButton {
text: luatr("Quit") text: luatr("Quit")
font.pixelSize: 20 font.pixelSize: 20
visible: mainStack.currentItem.objectName === "GeneralsOverview"
onClicked: { onClicked: {
mainStack.pop(); mainStack.pop();
config.saveConf(); config.saveConf();
@ -283,9 +287,9 @@ Item {
const gdata = lcall("GetGeneralData", modelData); const gdata = lcall("GetGeneralData", modelData);
const pack = gdata.package; const pack = gdata.package;
if (s.banPkg[pack]) { if (s.banPkg[pack]) {
if (s.banPkg[pack].includes(modelData)) return '启用'; if (s.banPkg[pack].includes(modelData)) return luatr('Enable');
} else { } else {
if (!!s.normalPkg[pack]?.includes(modelData)) return '禁'; if (!!s.normalPkg[pack]?.includes(modelData)) return luatr('Prohibit');
} }
} }
anchors.centerIn: parent anchors.centerIn: parent
@ -370,17 +374,9 @@ Item {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: { text: {
const orig = '$' + name + (idx ? idx.toString() : ""); const orig = '$' + name + (specific ? '_' + detailGeneralCard.name : "")
const orig_trans = luatr(orig);
// try general specific
const orig_g = '$' + name + '_' + detailGeneralCard.name
+ (idx ? idx.toString() : ""); + (idx ? idx.toString() : "");
const orig_g_trans = luatr(orig_g); const orig_trans = luatr(orig);
if (orig_g_trans !== orig_g) {
return orig_g_trans;
}
if (orig_trans !== orig) { if (orig_trans !== orig) {
return orig_trans; return orig_trans;
@ -396,10 +392,51 @@ Item {
callbacks["LogEvent"]({ callbacks["LogEvent"]({
type: "PlaySkillSound", type: "PlaySkillSound",
name: name, name: name,
general: detailGeneralCard.name, general: specific ? detailGeneralCard.name : null, //
i: idx, i: idx,
}); });
} }
onPressAndHold: {
Backend.copyToClipboard('$' + name + (specific ? '_' + detailGeneralCard.name : "")
+ (idx ? idx.toString() : "") + ':');
toast.show(luatr("Audio Code Copy Success"));
}
ToolButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: 32
Layout.preferredHeight: 32
visible: parent.hovered
text: "⋮"
onClicked: {
if (skillAudioMenu.visible){
skillAudioMenu.close();
} else {
skillAudioMenu.open();
}
}
Menu {
id: skillAudioMenu
MenuItem {
text: luatr("Copy Audio Code")
onTriggered: {
Backend.copyToClipboard('$' + name + (specific ? '_' + detailGeneralCard.name : "")
+ (idx ? idx.toString() : "") + ':');
toast.show(luatr("Audio Code Copy Success"));
}
}
MenuItem {
text: luatr("Copy Audio Text")
onTriggered: {
Backend.copyToClipboard(luatr('$' + name + (specific ? '_' + detailGeneralCard.name : "")
+ (idx ? idx.toString() : "")));
toast.show(luatr("Audio Text Copy Success"));
}
}
}
}
} }
} }
@ -427,7 +464,7 @@ Item {
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
ret = true; ret = true;
audioModel.append({ name: skill, idx: i }); audioModel.append({ name: skill, idx: i, specific: true });
} else { } else {
if (i > 0) break; if (i > 0) break;
} }
@ -445,7 +482,7 @@ Item {
skill + (i !== 0 ? i.toString() : "") + ".mp3"; skill + (i !== 0 ? i.toString() : "") + ".mp3";
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
audioModel.append({ name: skill, idx: i }); audioModel.append({ name: skill, idx: i, specific: false});
} else { } else {
if (i > 0) break; if (i > 0) break;
} }
@ -465,6 +502,7 @@ Item {
function updateGeneral() { function updateGeneral() {
detailGeneralCard.name = general; detailGeneralCard.name = general;
detailFlickable.contentY = 0; //
const data = lcall("GetGeneralDetail", general); const data = lcall("GetGeneralDetail", general);
generalText.clear(); generalText.clear();
audioModel.clear(); audioModel.clear();
@ -479,14 +517,8 @@ Item {
} }
data.skill.forEach(t => { data.skill.forEach(t => {
generalText.append("<b>" + luatr(t.name) + generalText.append((t.is_related_skill ? "<font color=\"purple\"><b>" : "<b>") + luatr(t.name) +
"</b>: " + t.description); "</b>: " + t.description + (t.is_related_skill ? "</font>" : ""));
addSkillAudio(t.name);
});
data.related_skill.forEach(t => {
generalText.append("<font color=\"purple\"><b>" + luatr(t.name) +
"</b>: " + t.description + "</font>");
addSkillAudio(t.name); addSkillAudio(t.name);
}); });
@ -521,6 +553,8 @@ Item {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
font.pixelSize: 16 font.pixelSize: 16
lineHeight: 21
lineHeightMode: Text.FixedHeight
function trans(str) { function trans(str) {
const ret = luatr(str); const ret = luatr(str);
if (ret === str) { if (ret === str) {
@ -530,13 +564,18 @@ Item {
} }
text: { text: {
const general = generalDetail.general; const general = generalDetail.general;
return [ const gdata = lcall("GetGeneralData", general);
luatr(lcall("GetGeneralData", general).package), let ret = [
luatr(gdata.package),
luatr("Title") + trans("#" + general), luatr("Title") + trans("#" + general),
luatr("Designer") + trans("designer:" + general), luatr("Designer") + trans("designer:" + general),
luatr("Voice Actor") + trans("cv:" + general), luatr("Voice Actor") + trans("cv:" + general),
luatr("Illustrator") + trans("illustrator:" + general), luatr("Illustrator") + trans("illustrator:" + general),
].join("<br>"); ].join("<br>");
if (gdata.hidden) {
ret += "<br><font color=\"grey\">" + luatr("Hidden General") + "</font>";
}
return ret;
} }
} }
@ -547,6 +586,7 @@ Item {
Button { Button {
text: luatr("Set as Avatar") text: luatr("Set as Avatar")
visible: mainStack.currentItem.objectName === "GeneralsOverview"
enabled: detailGeneralCard.name !== "" && !opTimer.running enabled: detailGeneralCard.name !== "" && !opTimer.running
&& Self.avatar !== detailGeneralCard.name && Self.avatar !== detailGeneralCard.name
onClicked: { onClicked: {
@ -562,6 +602,7 @@ Item {
} }
Flickable { Flickable {
id: detailFlickable
flickableDirection: Flickable.VerticalFlick flickableDirection: Flickable.VerticalFlick
contentHeight: detailLayout.height contentHeight: detailLayout.height
width: parent.width - 40 - generalInfo.width width: parent.width - 40 - generalInfo.width
@ -628,6 +669,44 @@ Item {
Backend.playSound("./packages/" + extension + "/audio/death/" Backend.playSound("./packages/" + extension + "/audio/death/"
+ general); + general);
} }
onPressAndHold: {
Backend.copyToClipboard("$~" + generalDetail.general);
toast.show(luatr("Audio Code Copy Success"));
}
ToolButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: 32
Layout.preferredHeight: 32
visible: parent.hovered
text: "⋮"
onClicked: {
if (deathAudioMenu.visible){
deathAudioMenu.close();
} else {
deathAudioMenu.open();
}
}
Menu {
id: deathAudioMenu
MenuItem {
text: luatr("Copy Audio Code")
onTriggered: {
Backend.copyToClipboard("$~" + generalDetail.general);
toast.show(luatr("Audio Code Copy Success"));
}
}
MenuItem {
text: luatr("Copy Audio Text")
onTriggered: {
Backend.copyToClipboard(luatr("~" + generalDetail.general));
toast.show(luatr("Audio Text Copy Success"));
}
}
}
}
} }
} }
} }

View File

@ -102,6 +102,32 @@ Item {
} }
} }
ToolButton {
x: parent.width - 32
y: parent.height / 2 - 8
Layout.preferredWidth: 32
Layout.preferredHeight: 32
visible: !!favorite
text: "⋮"
onClicked: {
if (menu.visible){
menu.close();
} else {
menu.open();
}
}
Menu {
id: menu
MenuItem {
text: qsTr("Remove from Favorites")
onTriggered: {
removeFavorite(addr, port);
}
}
}
}
ColumnLayout { ColumnLayout {
x: 6 x: 6
height: parent.height height: parent.height
@ -125,7 +151,7 @@ Item {
height: childrenRect.height height: childrenRect.height
width: serverList.width width: serverList.width
Text { Text {
text: "已收藏服务器与公共服务器列表" text: qsTr("List of Favorites and Public Servers")
font.pixelSize: 18 font.pixelSize: 18
x: 32; y: 8 x: 32; y: 8
} }
@ -170,14 +196,14 @@ Item {
id: addressEdit id: addressEdit
maximumLength: 64 maximumLength: 64
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "服务器地址" placeholderText: qsTr("Server Address")
text: selectedServer?.addr ?? "" text: selectedServer?.addr ?? ""
} }
TextField { TextField {
id: portEdit id: portEdit
maximumLength: 6 maximumLength: 6
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "端口" placeholderText: qsTr("Port")
text: selectedServer?.port ?? "" text: selectedServer?.port ?? ""
} }
Flickable { Flickable {
@ -199,21 +225,21 @@ Item {
id: usernameEdit id: usernameEdit
maximumLength: 32 maximumLength: 32
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "用户名" placeholderText: qsTr("Username")
text: selectedServer?.username ?? "" text: selectedServer?.username ?? ""
} }
TextField { TextField {
id: passwordEdit id: passwordEdit
maximumLength: 32 maximumLength: 32
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "密码" placeholderText: qsTr("Password")
passwordCharacter: "*" passwordCharacter: "*"
echoMode: TextInput.Password echoMode: TextInput.Password
text: selectedServer?.password ?? "" text: selectedServer?.password ?? ""
} }
} }
Button { Button {
text: "登录(首次登录自动注册)" text: qsTr("LOGIN (Auto-registration)")
Layout.fillWidth: true Layout.fillWidth: true
enabled: !!(addressEdit.text && portEdit.text && enabled: !!(addressEdit.text && portEdit.text &&
usernameEdit.text && passwordEdit.text) usernameEdit.text && passwordEdit.text)
@ -237,9 +263,9 @@ Item {
} }
} }
Button { Button {
text: "从收藏夹删除" text: qsTr("Remove from Favorites")
Layout.fillWidth: true Layout.fillWidth: true
visible: !!(selectedServer?.favorite) visible: false // !!(selectedServer?.favorite) //
onClicked: { onClicked: {
removeFavorite(selectedServer.addr, selectedServer.port); removeFavorite(selectedServer.addr, selectedServer.port);
} }

View File

@ -35,6 +35,7 @@ Item {
text: roomName text: roomName
// color: outdated ? "gray" : "black" // color: outdated ? "gray" : "black"
font.pixelSize: 16 font.pixelSize: 16
font.strikeout: outdated
// elide: Label.ElideRight // elide: Label.ElideRight
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
@ -44,6 +45,7 @@ Item {
Text { Text {
id: roomIdText id: roomIdText
text: luatr(gameMode) + ' #' + roomId text: luatr(gameMode) + ' #' + roomId
font.strikeout: outdated
anchors.top: roomNameText.bottom anchors.top: roomNameText.bottom
anchors.left: roomNameText.left anchors.left: roomNameText.left
} }
@ -119,7 +121,7 @@ Item {
text: "在未来的版本中这一块区域将增加更多实用的功能,<br>"+ text: "在未来的版本中这一块区域将增加更多实用的功能,<br>"+
"例如直接查看房间的各种配置信息<br>"+ "例如直接查看房间的各种配置信息<br>"+
"以及更多与禁将有关的实用功能!"+ "以及更多与禁将有关的实用功能!"+
"<font color='gray'>注:绿色按钮为试做型UI 后面优化</font>" "<font color='gray'>注:色按钮为试做型UI 后面优化</font>"
font.pixelSize: 18 font.pixelSize: 18
} }
@ -195,6 +197,13 @@ Item {
ClientInstance.notifyServer("RefreshRoomList", ""); ClientInstance.notifyServer("RefreshRoomList", "");
} }
} }
Button {
text: luatr("Filter")
onClicked: {
lobby_dialog.sourceComponent = Qt.createComponent("../LobbyElement/FilterRoom.qml"); //roomFilterDialog;
lobby_drawer.open();
}
}
Button { Button {
text: luatr("Create Room") text: luatr("Create Room")
onClicked: { onClicked: {
@ -313,7 +322,7 @@ Item {
} }
} }
Button { Button {
text: luatr("Scenarios Overview") text: luatr("Modes Overview")
onClicked: { onClicked: {
mainStack.push(mainWindow.modesOverviewPage); mainStack.push(mainWindow.modesOverviewPage);
} }

View File

@ -13,6 +13,7 @@ Item {
property int padding: 5 property int padding: 5
signal clicked signal clicked
signal rightClicked
id: button id: button
width: icon.width + title.implicitWidth + padding * 2 width: icon.width + title.implicitWidth + padding * 2
@ -44,7 +45,19 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
gesturePolicy: TapHandler.WithinBounds gesturePolicy: TapHandler.WithinBounds
onTapped: if (parent.enabled) parent.clicked() onTapped: (p, btn) => {
if (parent.enabled) {
if (btn === Qt.LeftButton || btn === Qt.NoButton) {
parent.clicked();
} else if (btn === Qt.RightButton) {
parent.rightClicked();
}
}
}
onLongPressed: {
parent.rightClicked();
}
} }
HoverHandler { HoverHandler {

View File

@ -5,6 +5,7 @@ import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
Item { Item {
objectName: "ModesOverview"
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: 10 spacing: 10
@ -30,6 +31,7 @@ Item {
TapHandler { TapHandler {
onTapped: { onTapped: {
listView.currentIndex = index; listView.currentIndex = index;
detailFlickable.contentY = 0; //
} }
} }
} }
@ -40,6 +42,7 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
color: "#88EEEEEE" color: "#88EEEEEE"
Flickable { Flickable {
id: detailFlickable
width: parent.width - 16 width: parent.width - 16
height: parent.height - 16 height: parent.height - 16
anchors.centerIn: parent anchors.centerIn: parent
@ -62,6 +65,7 @@ Item {
Button { Button {
text: luatr("Quit") text: luatr("Quit")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
visible: mainStack.currentItem.objectName === "ModesOverview"
onClicked: { onClicked: {
mainStack.pop(); mainStack.pop();
} }
@ -72,5 +76,6 @@ Item {
for (let d of mode_data) { for (let d of mode_data) {
modeList.append(d); modeList.append(d);
} }
listView.currentIndex = 0;
} }
} }

View File

@ -100,11 +100,12 @@ Item {
Menu { Menu {
id: menuContainer id: menuContainer
y: menuButton.height - 12 y: menuButton.height - 12
width: 100 width: parent.width * 1.8
MenuItem { MenuItem {
id: quitButton id: quitButton
text: luatr("Quit") text: luatr("Quit")
icon.source: AppPath + "/image/modmaker/back"
onClicked: { onClicked: {
if (config.replaying) { if (config.replaying) {
Backend.controlReplayer("shutdown"); Backend.controlReplayer("shutdown");
@ -117,10 +118,57 @@ Item {
} }
} }
MenuItem {
id: volumeButton
text: luatr("Audio Settings")
icon.source: AppPath + "/image/button/tileicon/configure"
onClicked: {
volumeDialog.open();
}
}
Menu {
title: luatr("Overview")
icon.source: AppPath + "/image/button/tileicon/rule_summary"
icon.width: 24
icon.height: 24
icon.color: palette.windowText
MenuItem {
id: generalButton
text: luatr("Generals Overview")
icon.source: AppPath + "/image/button/tileicon/general_overview"
onClicked: {
overviewLoader.overviewType = "Generals";
overviewDialog.open();
overviewLoader.item.loadPackages();
}
}
MenuItem {
id: cardslButton
text: luatr("Cards Overview")
icon.source: AppPath + "/image/button/tileicon/card_overview"
onClicked: {
overviewLoader.overviewType = "Cards";
overviewDialog.open();
overviewLoader.item.loadPackages();
}
}
MenuItem {
id: modesButton
text: luatr("Modes Overview")
icon.source: AppPath + "/image/misc/paper"
onClicked: {
overviewLoader.overviewType = "Modes";
overviewDialog.open();
}
}
}
MenuItem { MenuItem {
id: surrenderButton id: surrenderButton
enabled: !config.observing && !config.replaying enabled: !config.observing && !config.replaying && isStarted
text: luatr("Surrender") text: luatr("Surrender")
icon.source: AppPath + "/image/misc/surrender"
onClicked: { onClicked: {
const photo = getPhoto(Self.id); const photo = getPhoto(Self.id);
if (isStarted && !(photo.dead && photo.rest <= 0)) { if (isStarted && !(photo.dead && photo.rest <= 0)) {
@ -130,21 +178,13 @@ Item {
luatr('Surrender is disabled in this mode'); luatr('Surrender is disabled in this mode');
} else { } else {
surrenderDialog.informativeText = surrenderCheck surrenderDialog.informativeText = surrenderCheck
.map(str => `${luatr(str.text)}${str.passed ? '√' : '×'}`) .map(str => `${luatr(str.text)}${str.passed ? '✓' : '✗'}`)
.join('<br>'); .join('<br>');
} }
surrenderDialog.open(); surrenderDialog.open();
} }
} }
} }
MenuItem {
id: volumeButton
text: luatr("Audio Settings")
onClicked: {
volumeDialog.open();
}
}
} }
} }
@ -468,7 +508,68 @@ Item {
MetroButton { MetroButton {
text: luatr("Sort Cards") text: luatr("Sort Cards")
textFont.pixelSize: 28 textFont.pixelSize: 28
onClicked: Logic.resortHandcards(); onClicked: {
if (lcall("CanSortHandcards", Self.id)) {
let sortMethods = [];
for (let index = 0; index < sortMenuRepeater.count; index++) {
var tCheckBox = sortMenuRepeater.itemAt(index)
sortMethods.push(tCheckBox.checked)
}
Logic.sortHandcards(sortMethods);
}
}
onRightClicked: {
if (sortMenu.visible) {
sortMenu.close();
} else {
sortMenu.open();
}
}
Menu {
id: sortMenu
x: parent.width
y: -25
width: parent.width * 2
background: Rectangle {
color: "black"
border.width: 3
border.color: "white"
opacity: 0.8
}
Repeater {
id: sortMenuRepeater
model: ["Sort by Type", "Sort by Number", "Sort by Suit"]
CheckBox {
id: control
text: "<font color='white'>" + luatr(modelData) + "</font>"
checked: modelData === "Sort by Type"
font.pixelSize: 20
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.leftPadding
y: control.height / 2 - height / 2
radius: 3
border.color: "white"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 2
color: control.down ? "#17a81a" : "#21be2b"
visible: control.checked
}
}
}
}
}
} }
MetroButton { MetroButton {
text: luatr("Chat") text: luatr("Chat")
@ -994,6 +1095,28 @@ Item {
} }
} }
Popup {
id: overviewDialog
width: realMainWin.width * 0.75
height: realMainWin.height * 0.75
anchors.centerIn: parent
background: Rectangle {
color: "#EEEEEEEE"
radius: 5
border.color: "#A6967A"
border.width: 1
}
Loader {
id: overviewLoader
property string overviewType: "Generals"
anchors.centerIn: parent
width: parent.width / mainWindow.scale
height: parent.height / mainWindow.scale
scale: mainWindow.scale
source: AppPath + "/Fk/Pages/" + overviewType + "Overview.qml"
}
}
GlowText { GlowText {
anchors.centerIn: dashboard anchors.centerIn: dashboard
visible: Logic.getPhoto(Self.id).rest > 0 && !config.observing visible: Logic.getPhoto(Self.id).rest > 0 && !config.observing
@ -1127,6 +1250,8 @@ Item {
const general = luatr(data.general); const general = luatr(data.general);
if (msg.startsWith("!")) { if (msg.startsWith("!")) {
if (config.hidePresents)
return true;
const splited = msg.split(":"); const splited = msg.split(":");
const type = splited[0].slice(1); const type = splited[0].slice(1);
switch (type) { switch (type) {

View File

@ -297,13 +297,27 @@ function moveCards(moves) {
} }
} }
const suitInteger = {
spade: 1, heart: 3,
club: 2, diamond: 4,
}
function sortHandcards(sortMethods) {
function resortHandcards() {
if (!dashboard.handcardArea.cards.length) { if (!dashboard.handcardArea.cards.length) {
return; return;
} }
const cardType = sortMethods[0];
const cardNum = sortMethods[1];
const cardSuit = sortMethods[2];
if (!cardType && !cardNum && !cardSuit) {
return;
}
let sortOutputs = [];
let sortedStatus = [];
const subtypeString2Number = { const subtypeString2Number = {
["none"]: Card.SubtypeNone, ["none"]: Card.SubtypeNone,
["delayed_trick"]: Card.SubtypeDelayedTrick, ["delayed_trick"]: Card.SubtypeDelayedTrick,
@ -318,51 +332,61 @@ function resortHandcards() {
return c.cid; return c.cid;
}) })
dashboard.handcardArea.cards.sort((prev, next) => { let sortedByType = true;
if (prev.footnote === next.footnote) { let handcards
if (prev.type === next.type) { if (cardType) {
const prevSubtypeNumber = subtypeString2Number[prev.subtype]; handcards = dashboard.handcardArea.cards.slice(0);
const nextSubtypeNumber = subtypeString2Number[next.subtype]; handcards.sort((prev, next) => {
if (prevSubtypeNumber === nextSubtypeNumber) { if (prev.footnote === next.footnote) {
const splitedPrevName = prev.name.split('__'); if (prev.type === next.type) {
const prevTrueName = splitedPrevName[splitedPrevName.length - 1]; const prevSubtypeNumber = subtypeString2Number[prev.subtype];
const nextSubtypeNumber = subtypeString2Number[next.subtype];
if (prevSubtypeNumber === nextSubtypeNumber) {
const splitedPrevName = prev.name.split('__');
const prevTrueName = splitedPrevName[splitedPrevName.length - 1];
const splitedNextName = next.name.split('__'); const splitedNextName = next.name.split('__');
const nextTrueName = splitedNextName[splitedNextName.length - 1]; const nextTrueName = splitedNextName[splitedNextName.length - 1];
if (prevTrueName === nextTrueName) { if (prevTrueName === nextTrueName) {
return prev.cid - next.cid; return prev.cid - next.cid;
} else {
return prevTrueName > nextTrueName ? -1 : 1;
}
} else { } else {
return prevTrueName > nextTrueName ? -1 : 1; return prevSubtypeNumber - nextSubtypeNumber;
} }
} else { } else {
return prevSubtypeNumber - nextSubtypeNumber; return prev.type - next.type;
} }
} else { } else {
return prev.type - next.type; return prev.footnote > next.footnote ? 1 : -1;
} }
} else { });
return prev.footnote > next.footnote ? 1 : -1;
}
});
let i = 0; // Check if the cards are sorted by type
let resort = true; let i = 0;
dashboard.handcardArea.cards.forEach(c => { handcards.every(c => {
if (hand[i] !== c.cid) { if (hand[i] !== c.cid) {
resort = false; sortedByType = false;
return; return false;
} }
i++; i++;
}) return true;
})
sortOutputs.push(handcards);
sortedStatus.push(sortedByType);
}
if (resort) { let sortedByNum = true;
dashboard.handcardArea.cards.sort((prev, next) => { if (cardNum) {
handcards = dashboard.handcardArea.cards.slice(0);
handcards.sort((prev, next) => {
if (prev.footnote === next.footnote) { if (prev.footnote === next.footnote) {
if (prev.number === next.number) { // 按点数排 if (prev.number === next.number) {
if (prev.suit === next.suit) { if (suitInteger[prev.suit] === suitInteger[next.suit]) {
return prev.cid - next.cid; return prev.cid - next.cid;
} else { } else {
return prev.suit - next.suit; return suitInteger[prev.suit] - suitInteger[next.suit];
} }
} else { } else {
return prev.number - next.number; return prev.number - next.number;
@ -371,8 +395,61 @@ function resortHandcards() {
return prev.footnote > next.footnote ? 1 : -1; return prev.footnote > next.footnote ? 1 : -1;
} }
}); });
let i = 0;
handcards.every(c => {
if (hand[i] !== c.cid) {
sortedByNum = false;
return false;
}
i++;
return true;
})
sortOutputs.push(handcards);
sortedStatus.push(sortedByNum);
} }
let sortedBySuit = true;
if (cardSuit) {
handcards = dashboard.handcardArea.cards.slice(0);
handcards.sort((prev, next) => {
if (prev.footnote === next.footnote) {
if (suitInteger[prev.suit] === suitInteger[next.suit]) {
if (prev.number === next.number) {
return prev.cid - next.cid;
} else {
return prev.number - next.number;
}
} else {
return suitInteger[prev.suit] - suitInteger[next.suit];
}
} else {
return prev.footnote > next.footnote ? 1 : -1;
}
});
let i = 0;
handcards.every(c => {
if (hand[i] !== c.cid) {
sortedBySuit = false;
return false;
}
i++;
return true;
})
sortOutputs.push(handcards);
sortedStatus.push(sortedBySuit);
}
let output
for (let i = 0; i < sortedStatus.length; i++) {
if (sortedStatus[i]) {
let j = i < sortedStatus.length - 1 ? i + 1 : 0;
output = sortOutputs[j];
break;
}
}
if (!output) output = sortOutputs[0];
dashboard.handcardArea.cards = output;
dashboard.handcardArea.updateCardPosition(true); dashboard.handcardArea.updateCardPosition(true);
} }
@ -933,6 +1010,18 @@ callbacks["UpdateCard"] = (j) => {
card.setData(lcall("GetCardData", id)); card.setData(lcall("GetCardData", id));
} }
callbacks["UpdateSkill"] = (j) => {
const all_skills = [roomScene.dashboard.skillButtons, roomScene.dashboard.notActiveButtons];
for (const skills of all_skills) {
for (let i = 0; i < skills.count; i++) {
const item = skills.itemAt(i);
const dat = lcall("GetSkillStatus", item.orig);
item.locked = dat.locked;
item.times = dat.times;
}
}
}
callbacks["StartGame"] = (jsonData) => { callbacks["StartGame"] = (jsonData) => {
roomScene.isStarted = true; roomScene.isStarted = true;

View File

@ -82,8 +82,7 @@ GraphicsBox {
} }
Row { Row {
anchors.bottom: parent.bottom Layout.alignment: Qt.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
spacing: 32 spacing: 32
MetroButton { MetroButton {

View File

@ -69,7 +69,7 @@ Item {
signal rightClicked() signal rightClicked()
signal doubleClicked() signal doubleClicked()
signal thrown() signal thrown()
signal released() signal released(var card)
signal entered() signal entered()
signal exited() signal exited()
signal moveFinished() signal moveFinished()
@ -287,7 +287,7 @@ Item {
onGrabChanged: (transtition, point) => { onGrabChanged: (transtition, point) => {
if (transtition !== PointerDevice.UngrabExclusive) return; if (transtition !== PointerDevice.UngrabExclusive) return;
parent.released(); parent.released(root);
if (autoBack) if (autoBack)
goBackAnimation.start(); goBackAnimation.start();
} }

View File

@ -17,6 +17,7 @@ RowLayout {
property int selected_card: -1 property int selected_card: -1
property alias skillButtons: skillPanel.skill_buttons property alias skillButtons: skillPanel.skill_buttons
property alias notActiveButtons: skillPanel.not_active_buttons
property var expanded_piles: ({}) // name -> int[] property var expanded_piles: ({}) // name -> int[]
property var extra_cards: [] property var extra_cards: []

View File

@ -13,6 +13,7 @@ Item {
property alias style: textItem.style property alias style: textItem.style
property alias styleColor: textItem.styleColor property alias styleColor: textItem.styleColor
property alias wrapMode: textItem.wrapMode property alias wrapMode: textItem.wrapMode
property alias elide: textItem.elide
property alias lineHeight: textItem.lineHeight property alias lineHeight: textItem.lineHeight
property alias glow: glowItem property alias glow: glowItem

View File

@ -7,6 +7,7 @@ Item {
property alias cards: cardArea.cards property alias cards: cardArea.cards
property alias length: cardArea.length property alias length: cardArea.length
property var selectedCards: [] property var selectedCards: []
property var movepos
signal cardSelected(int cardId, bool selected) signal cardSelected(int cardId, bool selected)
@ -32,9 +33,11 @@ Item {
function filterInputCard(card) function filterInputCard(card)
{ {
card.autoBack = true; card.autoBack = true;
card.draggable = true; card.draggable = lcall("CanSortHandcards", Self.id);
card.selectable = false; card.selectable = false;
card.clicked.connect(adjustCards); card.clicked.connect(adjustCards);
card.released.connect(updateCardReleased);
card.xChanged.connect(updateCardDragging);
} }
function remove(outputs) function remove(outputs)
@ -46,6 +49,8 @@ Item {
card.draggable = false; card.draggable = false;
card.selectable = false; card.selectable = false;
card.selectedChanged.disconnect(adjustCards); card.selectedChanged.disconnect(adjustCards);
card.released.disconnect(updateCardReleased);
card.xChanged.disconnect(updateCardDragging);
card.prohibitReason = ""; card.prohibitReason = "";
} }
return result; return result;
@ -84,6 +89,49 @@ Item {
} }
} }
function updateCardDragging()
{
let _card, c;
let index;
for (index = 0; index < cards.length; index++) {
c = cards[index];
if (c.dragging) {
_card = c;
break;
}
}
if (!_card) return;
_card.goBackAnim.stop();
_card.opacity = 0.8
let card;
movepos = null;
for (let i = 0; i < cards.length; i++) {
card = cards[i];
if (card.dragging) continue;
if (card.x > _card.x) {
movepos = i - (index < i ? 1 : 0);
break;
}
}
if (movepos == null) { //
movepos = cards.length;
}
}
function updateCardReleased(_card)
{
let i;
if (movepos != null) {
i = cards.indexOf(_card);
cards.splice(i, 1);
cards.splice(movepos, 0, _card);
movepos = null;
}
updateCardPosition(true);
}
function adjustCards() function adjustCards()
{ {
area.updateCardPosition(true); area.updateCardPosition(true);

View File

@ -566,6 +566,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 2 anchors.topMargin: 2
width: parent.width - role.width - hp.width - 20
font.pixelSize: 16 font.pixelSize: 16
text: { text: {
@ -574,7 +575,8 @@ Item {
ret = luatr("<Blocked> ") + ret; ret = luatr("<Blocked> ") + ret;
return ret; return ret;
} }
elide: root.playerid === Self.id ? Text.ElideNone : Text.ElideMiddle
horizontalAlignment: Qt.AlignHCenter
glow.radius: 8 glow.radius: 8
} }

View File

@ -7,6 +7,7 @@ Flickable {
id: root id: root
property alias skill_buttons: skill_buttons property alias skill_buttons: skill_buttons
property alias prelight_buttons: prelight_buttons property alias prelight_buttons: prelight_buttons
property alias not_active_buttons: not_active_buttons
clip: true clip: true
contentWidth: panel.width contentWidth: panel.width
@ -91,6 +92,7 @@ Flickable {
columnSpacing: 2 columnSpacing: 2
rowSpacing: 2 rowSpacing: 2
Repeater { Repeater {
id: not_active_buttons
model: not_active_skills model: not_active_skills
onItemAdded: parent.forceLayout() onItemAdded: parent.forceLayout()
SkillButton { SkillButton {

View File

@ -11,6 +11,8 @@ Item {
property string orig: "" property string orig: ""
property bool pressed: false property bool pressed: false
property bool prelighted: false property bool prelighted: false
property bool locked: false
property int times: -1
onEnabledChanged: { onEnabledChanged: {
if (!enabled) if (!enabled)
@ -86,6 +88,76 @@ Item {
} }
} }
Image {
source: AppPath + "/image/button/skill/locked.png"
scale: 0.8
z: 2
visible: root.locked
opacity: 0.8
anchors.centerIn: parent
}
Item {
width: 12
height: 12
visible: root.times > -1
anchors.right: parent.right
anchors.rightMargin: type === "notactive" ? -13 : 5
anchors.top: parent.top
anchors.topMargin: 5
Rectangle {
width: Math.max(15, 1.4 * count.contentWidth)
height: 15
radius: width * 0.5
x: (parent.width - width) / 2
y: -1.5
color: "transparent"
border.color: "#D2AD4A"
border.width: 1.1
}
Text {
id: count
anchors.centerIn: parent
font.pixelSize: 16
font.family: fontLibian.name
font.bold: true
text: root.times
z: 1.5
}
Glow {
source: count
anchors.fill: count
color: "black"
spread: 0.3
radius: 5
}
LinearGradient {
anchors.fill: count
z: 3
source: count
gradient: Gradient {
GradientStop {
position: 0
color: "#FEF7C2"
}
GradientStop {
position: 0.8
color: "#D2AD4A"
}
GradientStop {
position: 1
color: "#BE9878"
}
}
}
}
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
onTapped: (p, btn) => { onTapped: (p, btn) => {
@ -95,6 +167,10 @@ Item {
skillDetail.open(); skillDetail.open();
} }
} }
onLongPressed: {
skillDetail.open();
}
} }
Popup { Popup {

View File

@ -105,10 +105,10 @@ Item {
for (i = 0; i < outputs.length; i++) { for (i = 0; i < outputs.length; i++) {
let exists = false; let exists = false;
for (j = 0; j < result.length; j++) { for (j = 0; j < result.length; j++) {
if (result[j].cid === outputs[i]) { if (result[j].cid === outputs[i]) {
exists = true; exists = true;
break; break;
} }
} }
if (!exists) if (!exists)
vanished.push(outputs[i]); vanished.push(outputs[i]);

BIN
image/misc/paper.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

BIN
image/misc/surrender.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -1 +0,0 @@
Subproject commit 4fd2070d099d1f967d1070d72beb0fae2cb6e4be

View File

@ -227,6 +227,10 @@
<source>Edit Server</source> <source>Edit Server</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>List of Favorites and Public Servers</source>
<translation></translation>
</message>
<message> <message>
<source>Refresh List</source> <source>Refresh List</source>
<translation></translation> <translation></translation>
@ -239,6 +243,14 @@
<source>Go Back</source> <source>Go Back</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Server Address</source>
<translation></translation>
</message>
<message>
<source>Port</source>
<translation></translation>
</message>
<message> <message>
<source>@VersionMismatch</source> <source>@VersionMismatch</source>
@ -286,6 +298,14 @@
<source>Delete Server</source> <source>Delete Server</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>LOGIN (Auto-registration)</source>
<translation></translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
@ -549,8 +569,7 @@
<source>tutor_msg_3</source> <source>tutor_msg_3</source>
<translation>&lt;br> <translation>&lt;br>
&lt;br> &lt;br>
-> &lt;br> -> </translation>
IP是175.178.66.93</translation>
</message> </message>
<message> <message>
<source>tutor_msg_4</source> <source>tutor_msg_4</source>
@ -561,8 +580,8 @@
</message> </message>
<message> <message>
<source>tutor_msg_5</source> <source>tutor_msg_5</source>
<translation>pdf <translation>&lt;a href="https://fkbook-all-in-one.readthedocs.io">https://fkbook-all-in-one.readthedocs.io&lt;/a>&lt;br>。
pdf都是由开发者们编写
</translation> </translation>
</message> </message>
<message> <message>