mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2025-12-20 22:32:47 -08:00
Compare commits
286 Commits
2023-03-03
...
2024-12-21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07ee271478 | ||
|
|
4823cce622 | ||
|
|
23099f7e8b | ||
|
|
5bdbd51fa8 | ||
|
|
a0e5871c6e | ||
|
|
3cf0904651 | ||
|
|
2bd06ff0fd | ||
|
|
6ea333d0f1 | ||
|
|
91d2485940 | ||
|
|
0d99b2bcf4 | ||
|
|
a54a424f84 | ||
|
|
3514699f5b | ||
|
|
d196988cab | ||
|
|
03aff83135 | ||
|
|
17e6bfaca6 | ||
|
|
90281262be | ||
|
|
5bbc118920 | ||
|
|
dde2f8b9ad | ||
|
|
d41aa30e10 | ||
|
|
231d0380a7 | ||
|
|
a5c509981b | ||
|
|
71b01e6110 | ||
|
|
c716f85962 | ||
|
|
245d51caea | ||
|
|
e588917f6c | ||
|
|
27e5d21b6b | ||
|
|
b894b75e6a | ||
|
|
116397cdb3 | ||
|
|
a6b5abf271 | ||
|
|
fd5a649246 | ||
|
|
e8e57989ba | ||
|
|
03db4ccce6 | ||
|
|
c9d5d5609c | ||
|
|
ac16206ddb | ||
|
|
5f8bcbd02d | ||
|
|
a0f74134bb | ||
|
|
0463a6fd70 | ||
|
|
a5de633c64 | ||
|
|
b2ad2acff3 | ||
|
|
4ee6ff73e0 | ||
|
|
628bdde939 | ||
|
|
e9b78c1c59 | ||
|
|
315cbc0925 | ||
|
|
69741d858c | ||
|
|
20d99a78b6 | ||
|
|
2d68393e07 | ||
|
|
8cb1470643 | ||
|
|
8d9b27bf47 | ||
|
|
0c5d9f1a7d | ||
|
|
a7d88c06c1 | ||
|
|
2735000fcf | ||
|
|
a39de270cd | ||
|
|
10f11213d3 | ||
|
|
3b49cbf73b | ||
|
|
e4cfe08113 | ||
|
|
fa02cb885c | ||
|
|
69b864fa02 | ||
|
|
b9ed9a6c0b | ||
|
|
5156495b47 | ||
|
|
b92047bc3f | ||
|
|
70559d32df | ||
|
|
bb84b75db9 | ||
|
|
f634177973 | ||
|
|
e33ff37c82 | ||
|
|
d2bc7f6ac0 | ||
|
|
5ef1ca06f5 | ||
|
|
1d8651bc00 | ||
|
|
17eabf2004 | ||
|
|
37bb1367db | ||
|
|
24b5dab456 | ||
|
|
c6bfc8b8ea | ||
|
|
f2b0fa164e | ||
|
|
0ca8bdb3a8 | ||
|
|
a8471f62bc | ||
|
|
5f1c03682f | ||
|
|
3255ed3ffb | ||
|
|
c51b54c0c5 | ||
|
|
a3f0807d47 | ||
|
|
27055944df | ||
|
|
7b1653034b | ||
|
|
39d8ca050f | ||
|
|
50274cb66d | ||
|
|
bd60a9fd2e | ||
|
|
83409c32c4 | ||
|
|
1bc92623dc | ||
|
|
f73196841a | ||
|
|
86a4b130ff | ||
|
|
f4e2f117c3 | ||
|
|
8ef92d26c5 | ||
|
|
c8336df49d | ||
|
|
c2fe3cda35 | ||
|
|
c54f47efbf | ||
|
|
3c40cc4b7d | ||
|
|
f0fb77bade | ||
|
|
e894e78346 | ||
|
|
dd04c610ec | ||
|
|
2e674efe50 | ||
|
|
4d394c31f9 | ||
|
|
11d58abbc3 | ||
|
|
5f4ad87a47 | ||
|
|
e43a21866c | ||
|
|
6652012f4c | ||
|
|
0c4e8ca290 | ||
|
|
230a2c5c62 | ||
|
|
590fb7f533 | ||
|
|
e8b88248f2 | ||
|
|
c6ba1b6a4e | ||
|
|
c4c52bd8c0 | ||
|
|
c633a792f5 | ||
|
|
8d5421d9da | ||
|
|
b041f4ace2 | ||
|
|
d26f96db9e | ||
|
|
fa999880ee | ||
|
|
d1e0f9dfc5 | ||
|
|
2d86938375 | ||
|
|
4865269a73 | ||
|
|
038ce3dcec | ||
|
|
43b997fe40 | ||
|
|
44e92f61ca | ||
|
|
b4bfa17cee | ||
|
|
500b694cc6 | ||
|
|
b998282304 | ||
|
|
b704216553 | ||
|
|
03ec02a749 | ||
|
|
248ea82573 | ||
|
|
bbe125beee | ||
|
|
95cd1c6f87 | ||
|
|
1c2107ae8f | ||
|
|
e826e17c6c | ||
|
|
b111f0921c | ||
|
|
090a48515c | ||
|
|
b8555d8c42 | ||
|
|
cf1f4f12a9 | ||
|
|
ef4413633a | ||
|
|
c5bb38e907 | ||
|
|
9f515fc804 | ||
|
|
245edcefdd | ||
|
|
153f73c308 | ||
|
|
315837b267 | ||
|
|
ea8da24215 | ||
|
|
1ab723ca64 | ||
|
|
f8bc6cf998 | ||
|
|
8687163cca | ||
|
|
e261e16d99 | ||
|
|
bdcd083eea | ||
|
|
c4bf9eb61c | ||
|
|
0994d10410 | ||
|
|
291c535edb | ||
|
|
f04702fdd1 | ||
|
|
b7fbc12ac0 | ||
|
|
e2ab8db958 | ||
|
|
34d70980e8 | ||
|
|
e45c4042fe | ||
|
|
ce8092318e | ||
|
|
c95cc1dd9d | ||
|
|
1f72877728 | ||
|
|
93b40343d9 | ||
|
|
ba10108207 | ||
|
|
c28f66d673 | ||
|
|
59f327f97a | ||
|
|
872c92a244 | ||
|
|
2303880b87 | ||
|
|
0e97cc1712 | ||
|
|
d550e42441 | ||
|
|
4279753030 | ||
|
|
2f6c018b7a | ||
|
|
be5d42baba | ||
|
|
f174614496 | ||
|
|
e8c7fba8b0 | ||
|
|
5c49283023 | ||
|
|
ad56b431a3 | ||
|
|
b0d8a33d5f | ||
|
|
1715bcb216 | ||
|
|
96caeaca72 | ||
|
|
c8723ae935 | ||
|
|
94e39c044c | ||
|
|
9776ea53c9 | ||
|
|
675d07dac0 | ||
|
|
1217820288 | ||
|
|
90e1a3cb76 | ||
|
|
7c1095ea50 | ||
|
|
203e916a07 | ||
|
|
7201e34b38 | ||
|
|
6d032c378f | ||
|
|
badd8952f5 | ||
|
|
7209eddb2d | ||
|
|
a7ffd43b29 | ||
|
|
724ba69483 | ||
|
|
1716801437 | ||
|
|
fa727524dc | ||
|
|
28f80e18a0 | ||
|
|
4acc8bfe80 | ||
|
|
9f86ed7887 | ||
|
|
cb18a55338 | ||
|
|
78a928464c | ||
|
|
07a8cd0a5f | ||
|
|
b73ef58567 | ||
|
|
519531f3a0 | ||
|
|
4b8e47d079 | ||
|
|
ed170f7e07 | ||
|
|
6bb559874c | ||
|
|
9cd68e25b3 | ||
|
|
72ac441598 | ||
|
|
f5fe56c85d | ||
|
|
d4fc1be2cc | ||
|
|
7b3617a273 | ||
|
|
3e8adae3de | ||
|
|
9943133d6d | ||
|
|
a5706a47af | ||
|
|
b3b911c64d | ||
|
|
05beb4fcaf | ||
|
|
324b50e381 | ||
|
|
186f4289e9 | ||
|
|
cb90a8356b | ||
|
|
e9c502ab32 | ||
|
|
f728520e97 | ||
|
|
c1b0d50237 | ||
|
|
b9cfc29059 | ||
|
|
6bf7c79891 | ||
|
|
2bd0e58354 | ||
|
|
ee674cb0cf | ||
|
|
dd1b354d48 | ||
|
|
d3e96f4a99 | ||
|
|
90e2eb3db9 | ||
|
|
102be6a350 | ||
|
|
be6152948c | ||
|
|
f14bf4b205 | ||
|
|
123ac2ca25 | ||
|
|
7216b976ec | ||
|
|
7fb698cfbf | ||
|
|
b0470ab678 | ||
|
|
0deb037035 | ||
|
|
064b362d60 | ||
|
|
6bbe228a84 | ||
|
|
a8ba8b6ab5 | ||
|
|
e850f6c2a5 | ||
|
|
e9eb7d6db1 | ||
|
|
56d21321d0 | ||
|
|
3888a74212 | ||
|
|
0035e29f9e | ||
|
|
90679d5669 | ||
|
|
ac5dc2578a | ||
|
|
671e6823be | ||
|
|
0d76662311 | ||
|
|
8dd59cf3cf | ||
|
|
332d25dc00 | ||
|
|
0fa81a77dc | ||
|
|
9a74d8f72d | ||
|
|
8c539351e3 | ||
|
|
e3552fc0ae | ||
|
|
ca308636c3 | ||
|
|
244cb847fb | ||
|
|
176c52daf2 | ||
|
|
ee3525ec64 | ||
|
|
adce921be7 | ||
|
|
20ceb1c284 | ||
|
|
db4364b8f8 | ||
|
|
48d6435e09 | ||
|
|
7c20e9ab34 | ||
|
|
cb52605928 | ||
|
|
bd3100dcda | ||
|
|
afb7c35cfd | ||
|
|
5b694a55d2 | ||
|
|
f750a4cd72 | ||
|
|
eddeaaf52a | ||
|
|
2b42bee424 | ||
|
|
b9706c0cc1 | ||
|
|
800b21b000 | ||
|
|
d1736a25bb | ||
|
|
f269e5fe58 | ||
|
|
45a5296013 | ||
|
|
5f0ab2a177 | ||
|
|
70ab02987a | ||
|
|
421da882d8 | ||
|
|
1a40102f71 | ||
|
|
1fbc10cd77 | ||
|
|
87462398d8 | ||
|
|
b33246b29f | ||
|
|
dbffe30f63 | ||
|
|
9ce450d0b0 | ||
|
|
f5f8acf1fd | ||
|
|
304ed3cd60 | ||
|
|
07248692ce | ||
|
|
cef99cba71 | ||
|
|
cab5f29b57 | ||
|
|
55a2f75d16 |
@@ -9,6 +9,7 @@ RUN pacman --sync --refresh --sysupgrade --needed --noconfirm \
|
||||
mariadb-libs \
|
||||
protobuf \
|
||||
qt6-base \
|
||||
qt6-imageformats \
|
||||
qt6-multimedia \
|
||||
qt6-svg \
|
||||
qt6-tools \
|
||||
@@ -1,25 +0,0 @@
|
||||
FROM debian:10
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-default \
|
||||
qtbase5-dev \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
@@ -17,6 +17,7 @@ RUN apt-get update && \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-image-formats-plugins \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:kinetic
|
||||
FROM debian:12
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
@@ -15,9 +15,10 @@ RUN apt-get update && \
|
||||
libprotobuf-dev \
|
||||
libqt6multimedia6 \
|
||||
libqt6sql6-mysql \
|
||||
libqt6svg6-dev \
|
||||
libqt6websockets6-dev \
|
||||
qt6-svg-dev \
|
||||
qt6-websockets-dev \
|
||||
protobuf-compiler \
|
||||
qt6-image-formats-plugins \
|
||||
qt6-l10n-tools \
|
||||
qt6-multimedia-dev \
|
||||
qt6-tools-dev \
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM fedora:36
|
||||
FROM fedora:40
|
||||
|
||||
RUN dnf install -y \
|
||||
ccache \
|
||||
@@ -8,6 +8,7 @@ RUN dnf install -y \
|
||||
mariadb-devel \
|
||||
protobuf-devel \
|
||||
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
||||
qt6-qtimageformats \
|
||||
rpm-build \
|
||||
xz-devel \
|
||||
zlib-devel \
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM fedora:37
|
||||
FROM fedora:41
|
||||
|
||||
RUN dnf install -y \
|
||||
ccache \
|
||||
@@ -8,6 +8,7 @@ RUN dnf install -y \
|
||||
mariadb-devel \
|
||||
protobuf-devel \
|
||||
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
||||
qt6-qtimageformats \
|
||||
rpm-build \
|
||||
xz-devel \
|
||||
zlib-devel \
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:focal
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
@@ -18,6 +18,7 @@ RUN apt-get update && \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-default \
|
||||
qt5-image-formats-plugins \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
@@ -18,6 +18,7 @@ RUN apt-get update && \
|
||||
libqt6svg6-dev \
|
||||
libqt6websockets6-dev \
|
||||
protobuf-compiler \
|
||||
qt6-image-formats-plugins \
|
||||
qt6-l10n-tools \
|
||||
qt6-multimedia-dev \
|
||||
qt6-tools-dev \
|
||||
27
.ci/Ubuntu24.04/Dockerfile
Normal file
27
.ci/Ubuntu24.04/Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
libgl-dev \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt6multimedia6 \
|
||||
libqt6sql6-mysql \
|
||||
qt6-svg-dev \
|
||||
qt6-websockets-dev \
|
||||
protobuf-compiler \
|
||||
qt6-image-formats-plugins \
|
||||
qt6-l10n-tools \
|
||||
qt6-multimedia-dev \
|
||||
qt6-tools-dev \
|
||||
qt6-tools-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
@@ -1,24 +0,0 @@
|
||||
FROM ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-default \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
@@ -160,7 +160,13 @@ cmake .. "${flags[@]}"
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build project"
|
||||
cmake --build . "${buildflags[@]}"
|
||||
if [[ $RUNNER_OS == Windows ]]; then
|
||||
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
|
||||
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
|
||||
cmake --build . "${buildflags[@]}" -- -p:UseMultiToolTask=true -p:EnableClServerMode=true
|
||||
else
|
||||
cmake --build . "${buildflags[@]}"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
if [[ $USE_CCACHE ]]; then
|
||||
@@ -183,6 +189,12 @@ fi
|
||||
|
||||
if [[ $MAKE_PACKAGE ]]; then
|
||||
echo "::group::Create package"
|
||||
|
||||
if [[ $RUNNER_OS == macOS ]]; then
|
||||
# Workaround https://github.com/actions/runner-images/issues/7522
|
||||
echo "killing XProtectBehaviorService"; sudo pkill -9 XProtect >/dev/null || true;
|
||||
echo "waiting for XProtectBehaviorService kill"; while pgrep "XProtect"; do sleep 3; done;
|
||||
fi
|
||||
cmake --build . --target package --config "$BUILDTYPE"
|
||||
echo "::endgroup::"
|
||||
|
||||
|
||||
@@ -4,25 +4,24 @@
|
||||
git push -d origin --REPLACE-WITH-BETA-LIST--
|
||||
-->
|
||||
|
||||
<!-- This list of binaries should be updated every time the ci is changed to
|
||||
<!-- This list of binaries should be updated every time the CI is changed to
|
||||
include different targets -->
|
||||
<pre>
|
||||
<b>Pre-compiled binaries we serve:</b>
|
||||
- <kbd>Windows 7+ (32-bit)</kbd>
|
||||
- <kbd>Windows 7+</kbd>
|
||||
- <kbd>Windows 10+</kbd>
|
||||
- <kbd>macOS 10.15+</kbd> ("Catalina")
|
||||
- <kbd>macOS 11+</kbd> ("Big Sur")
|
||||
- <kbd>Ubuntu 18.04</kbd> ("Bionic Beaver")
|
||||
- <kbd>Ubuntu 20.04</kbd> ("Focal Fossa")
|
||||
- <kbd>Ubuntu 22.04</kbd> ("Jammy Jellyfish")
|
||||
- <kbd>Ubuntu 22.10</kbd> ("Kinetic Kudu")
|
||||
- <kbd>Debian 10</kbd> ("Buster")
|
||||
- <kbd>Windows 7+</kbd>
|
||||
- <kbd>macOS 14+</kbd> ("Sonoma") / Apple M
|
||||
- <kbd>macOS 13+</kbd> ("Ventura") / Intel
|
||||
- <kbd>Ubuntu 24.04 LTS</kbd> ("Noble Numbat")
|
||||
- <kbd>Ubuntu 22.04 LTS</kbd> ("Jammy Jellyfish")
|
||||
- <kbd>Ubuntu 20.04 LTS</kbd> ("Focal Fossa")
|
||||
- <kbd>Debian 12</kbd> ("Bookworm")
|
||||
- <kbd>Debian 11</kbd> ("Bullseye")
|
||||
- <kbd>Fedora 36</kbd>
|
||||
- <kbd>Fedora 37</kbd>
|
||||
<kbd>We are also packaged in Arch Linux's official community repository, courtesy of @FFY00</kbd></i>
|
||||
<kbd>General linux support is available via a flatpak package (Flathub)</kbd></i>
|
||||
- <kbd>Fedora 41</kbd>
|
||||
- <kbd>Fedora 40</kbd>
|
||||
|
||||
<i>We are also packaged in <kbd>Arch Linux</kbd>'s official "extra" repository, courtesy of @FFY00</i>
|
||||
<i>General Linux support is available via a <kbd>flatpak</kbd> package (Flathub)</i>
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -30,22 +29,24 @@ include different targets -->
|
||||
|
||||
We're pleased to announce the newest official release: <kbd>--REPLACE-WITH-RELEASE-TITLE--</kbd>
|
||||
|
||||
We hope you enjoy the changes made and we have listed all changes, with their corresponding tickets, since the last version of Cockatrice was released for your convenience.
|
||||
We hope you enjoy the changes made! All improvements with their corresponding tickets since the last version of Cockatrice are listed in the changelog below.
|
||||
|
||||
If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a GitHub Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
|
||||
If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
|
||||
|
||||
For any information relating to Cockatrice, please take a look at our official site: **https://cockatrice.github.io**
|
||||
For basic information related to the app and getting started, please take a look at our official site: **https://cockatrice.github.io**
|
||||
|
||||
If you'd like to help contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-). We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution.
|
||||
If you'd like to help and contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-).
|
||||
We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution.
|
||||
|
||||
|
||||
## Upgrading Cockatrice
|
||||
<!-- this optional section puts a warning banner for problems with updating
|
||||
> ⚠️ **With this release, we no longer provide a ready-to-install binary for:**
|
||||
> [!IMPORTANT]
|
||||
> **With this release, we no longer provide a ready-to-install binary for:**
|
||||
> --DEPRECATED-OS-HERE--
|
||||
-->
|
||||
|
||||
- Run the internal software updater: <kbd>Help → Check for Client Updates</kbd>
|
||||
Run the internal software updater: <kbd>Help → Check for Client Updates</kbd>
|
||||
|
||||
Don't forget to update your card database right after! (<kbd>Help → Check for Card Updates...</kbd>)
|
||||
|
||||
@@ -62,14 +63,14 @@ Remove empty headers when done.
|
||||
-->
|
||||
|
||||
<!-- Highlights of the release -->
|
||||
### ⚠️ Important:
|
||||
### 🔖 Highlights:
|
||||
### ✨ New Features:
|
||||
### 🐛 Fixed Bugs / Resolved issues:
|
||||
### 🐛 Fixed Bugs / Resolved Issues:
|
||||
|
||||
<!-- Complete list of changes (foldable) -->
|
||||
<details>
|
||||
<summary>
|
||||
📘 <b>Show all changes</b> (--REPLACE-WITH-COMMIT-COUNT-- commits)
|
||||
<b>Show all changes</b> (--REPLACE-WITH-COMMIT-COUNT-- commits)
|
||||
</summary>
|
||||
|
||||
### User Interface
|
||||
@@ -90,5 +91,6 @@ Remove empty headers when done.
|
||||
|
||||
## Special Thanks
|
||||
<!-- Personalise this a bit! -->
|
||||
We continue to find it amazing that so many people contribute their time, knowledge, code, testing and more to the project. We'd like to thank the entire Cockatrice community for their efforts.
|
||||
It's amazing that so many people contribute their time, knowledge, code, testing and more to the project.
|
||||
We'd like to thank the entire Cockatrice community for their efforts! 🙏
|
||||
<!-- We'd like to especially recognize @ZeldaZach, --ADD-CONTRIBUTORS-HERE-- for their help in preparing so many amazing new features for the user base. -->
|
||||
|
||||
58
.ci/update_translation_source_strings.sh
Executable file
58
.ci/update_translation_source_strings.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ci script to update translation files
|
||||
# usage:
|
||||
# $0 cockatrice/cockatrice_en@source.ts cockatrice/src common
|
||||
# or
|
||||
# FILE="cockatrice/cockatrice_en@source.ts"
|
||||
# DIRS="cockatrice/src common"
|
||||
# $0
|
||||
# note: directories can't contain spaces
|
||||
|
||||
# check parameters
|
||||
if [[ ! $FILE ]]; then
|
||||
FILE="$1"
|
||||
shift
|
||||
fi
|
||||
if [[ ! $FILE ]]; then
|
||||
echo "no output file selected" >&2
|
||||
exit 2;
|
||||
fi
|
||||
if [[ ! $DIRS ]]; then
|
||||
DIRS="$*"
|
||||
fi
|
||||
if [[ ! $DIRS ]]; then
|
||||
echo "no source directories selected to translate" >&2
|
||||
exit 2;
|
||||
fi
|
||||
if [[ ! -e $FILE ]]; then
|
||||
echo "output file does not exist at: $FILE" >&2
|
||||
exit 3;
|
||||
fi
|
||||
|
||||
# print version
|
||||
if ! lupdate -version; then
|
||||
echo "failed to run lupdate" >&2
|
||||
exit 4;
|
||||
fi
|
||||
|
||||
# run lupdate, duplicating the output in stderr and saving it
|
||||
# for convenience we ignore that $DIRS will be split on spaces
|
||||
# shellcheck disable=SC2086
|
||||
if ! got="$(lupdate $DIRS -ts "$FILE" | tee /dev/stderr)"; then
|
||||
echo "failed to update $FILE with $DIRS" >&2
|
||||
exit 4;
|
||||
fi
|
||||
|
||||
# trim output
|
||||
# the line we are interested in is:
|
||||
# Found xxx source text(s) (x new and xxx already existing)
|
||||
output="${got##* source text(s) (}" # get stuff in between brackets
|
||||
output="${output%%)*}" # trim everything after first )
|
||||
if [[ $output == "$got" ]]; then
|
||||
echo "could not parse generated output" >&2
|
||||
exit 4;
|
||||
fi
|
||||
|
||||
# write output to ci environment file
|
||||
echo "output=$output" >> "$GITHUB_OUTPUT"
|
||||
14
.ci/update_translation_source_strings_template.md
Normal file
14
.ci/update_translation_source_strings_template.md
Normal file
@@ -0,0 +1,14 @@
|
||||
Updated source strings for translations:
|
||||
- {{ .cockatrice_output }} (Cockatrice)
|
||||
- {{ .oracle_output }} (Oracle)
|
||||
|
||||
<br>
|
||||
|
||||
Last changes are based on commit {{ .commit }}.
|
||||
|
||||
---
|
||||
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-push.yml`. Review [action runs][2].*<br>
|
||||
*After merging, all changes to the source language are available for translation at [Transifex][1] shortly.*
|
||||
|
||||
[1]: https://app.transifex.com/cockatrice/cockatrice/
|
||||
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-push.yml?query=branch%3Amaster
|
||||
73
.github/CONTRIBUTING.md
vendored
73
.github/CONTRIBUTING.md
vendored
@@ -290,20 +290,21 @@ be included in the next release 👍
|
||||
|
||||
Basic workflow for translations:
|
||||
1. Developer adds a `tr("foo")` string in the code;
|
||||
2. Every few days, a maintainer updates the `*_en@source.ts files` with the new strings;
|
||||
3. Transifex picks up the new files from GitHub every 24 hours;
|
||||
4. Translators translate the new untranslated strings on Transifex;
|
||||
5. Before a release, a maintainer fetches the updated translations from Transifex.
|
||||
2. CI updates the `*_en@source.ts files` regularly and creates a PR automatically;
|
||||
3. Maintainer verifies and merges the change;
|
||||
4. Transifex picks up the new files from GitHub automatically;
|
||||
5. Translators translate the new untranslated strings on Transifex;
|
||||
6. Before a release, a maintainer fetches the updated translations from Transifex.
|
||||
|
||||
### Using Translations (for developers) ###
|
||||
|
||||
All the user-interface strings inside Cockatrice's source code must be written
|
||||
in English(US).
|
||||
All user interface strings inside Cockatrice's source code must be written
|
||||
in English (US).
|
||||
Translations to other languages are managed using [Transifex](
|
||||
https://www.transifex.com/projects/p/cockatrice/).
|
||||
|
||||
Adding a new string to translate is as easy as adding the string in the
|
||||
'tr("")' function, the string will be picked up as translatable automatically
|
||||
`tr("")` function, the string will be picked up as translatable automatically
|
||||
and translated as needed.
|
||||
For example, setting the text of a label in the way that the string
|
||||
`"My name is:"` can be translated:
|
||||
@@ -312,7 +313,7 @@ nameLabel.setText(tr("My name is:"));
|
||||
```
|
||||
|
||||
To translate a string that would have plural forms you can add the amount to
|
||||
the tr call, also you can add an extra string as a hint for translators:
|
||||
the tr() call, also you can add an extra string as a hint for translators:
|
||||
```c++
|
||||
QString message = tr("Everyone draws %n cards", "pop up message", amount);
|
||||
```
|
||||
@@ -321,20 +322,46 @@ https://doc.qt.io/qt-5/i18n-source-translation.html#handling-plurals)
|
||||
|
||||
If you're about to propose a change that adds or modifies any translatable
|
||||
string in the code, you don't need to take care of adding the new strings to
|
||||
the translation files.
|
||||
Every few days, or when a lot of new strings have been added, someone from the
|
||||
development team will take care of extracting all the new strings and adding
|
||||
them to the english translation files and making them available to translators
|
||||
on Transifex.
|
||||
the translation files.<br>
|
||||
We have an automated process to update our language source files on a schedule
|
||||
and provide the translators on Transifex with the new contents.<br>
|
||||
Maintainers can also manually trigger this on demand.
|
||||
|
||||
### Maintaining Translations (for maintainers) ###
|
||||
|
||||
When new translatable strings have been added to the code, a maintainer should
|
||||
make them available to translators on Transifex. Every few days, or when a lot
|
||||
of new strings have been added, a maintainer should take care of extracting all
|
||||
the new strings and add them to the english translation files.
|
||||
When new translatable strings have been added to the code, a maintainer has to
|
||||
make them available to translators on Transifex.
|
||||
|
||||
To update the english translation files, re-run cmake enabling the appropriate
|
||||
To help with that, we have an automated CI workflow, that regularly looks at the
|
||||
code in the master branch, extracts all strings and updates dedicated source string
|
||||
files with any changes. These updates are not commited right away, the CI creates a
|
||||
PR for reviewing instead.<br>
|
||||
After approval, our translation tool automatically picks the changes up and deploys
|
||||
them to our translators. Be mindful when merging only a few changes!
|
||||
|
||||
Once a release is planned, or when a lot of strings have been added or changed, a
|
||||
maintainer can manually trigger a CI run to extract all strings on demand.
|
||||
|
||||
<details>
|
||||
<summary><b>Manually trigger CI run (Workflow Dispatch)</b></summary>
|
||||
|
||||
Maintainers can always request the CI to run on demand if it's required.
|
||||
|
||||
Go to the `Actions` tab and select our dedicated translation workflow:
|
||||
https://github.com/Cockatrice/Cockatrice/actions/workflows/translations.yml
|
||||
|
||||
You see a "This workflow has a workflow_dispatch event trigger." hint at the top of
|
||||
the list.<br>
|
||||
Select `Run workflow` on the right and trigger a run from master branch.
|
||||
|
||||
The CI will now check for changed strings and create a PR if there are any updates.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Manually update source strings locally</b></summary>
|
||||
|
||||
To update the english source files for translation, re-run cmake enabling the appropriate
|
||||
parameter and then run make:
|
||||
```sh
|
||||
cd cockatrice/build
|
||||
@@ -357,11 +384,13 @@ It is recommended to disable the parameter afterwards using:
|
||||
```sh
|
||||
cmake .. -DUPDATE_TRANSLATIONS=OFF
|
||||
```
|
||||
Now you are ready to propose your change.
|
||||
Now you are ready to commit your changes and open a PR.
|
||||
|
||||
Once your change gets merged, Transifex will pick up the modified files
|
||||
automatically (checked every 24 hours) and update the interface where
|
||||
translators will be able to translate the new strings.
|
||||
</details>
|
||||
|
||||
Once the changes get merged, Transifex will pick up the modified files
|
||||
automatically (checked every few hours) and update their online editor where
|
||||
translators will be able to translate the new strings right in the browser.
|
||||
|
||||
### Releasing Translations (for maintainers) ###
|
||||
|
||||
|
||||
49
.github/dependabot.yml
vendored
Normal file
49
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Configuration options: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# # Enable version updates for git submodules
|
||||
# Not yet possible to bump only on tags or releases, see:
|
||||
# https://github.com/dependabot/dependabot-core/issues/1639
|
||||
# https://github.com/dependabot/dependabot-core/issues/2192
|
||||
# Alternative: Action that updates submodule and can be manually run on demand (workflow_dispatch)
|
||||
# - package-ecosystem: "gitsubmodule"
|
||||
# # Look for `.gitmodules` in the `root` directory
|
||||
# directory: "/"
|
||||
# # Check for updates once a month
|
||||
# schedule:
|
||||
# interval: "monthly"
|
||||
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
# open-pull-requests-limit: 1
|
||||
|
||||
# # Enable version updates for Docker
|
||||
# Not yet possible to bump from one LTS version to the next and skip others, see:
|
||||
# https://github.com/dependabot/dependabot-core/issues/2247
|
||||
# - package-ecosystem: "docker"
|
||||
# # Look for a `Dockerfile` in the `root` directory
|
||||
# directory: "/"
|
||||
# # Check for updates once a week
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
# open-pull-requests-limit: 1
|
||||
|
||||
# Enable version updates for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
# Directory must be set to "/" to check for workflow files in .github/workflows
|
||||
directory: "/"
|
||||
# Check for updates to GitHub Actions once a week
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
open-pull-requests-limit: 2
|
||||
|
||||
# # Enable version updates for npm
|
||||
# - package-ecosystem: "npm"
|
||||
# # Look for `package.json` and `lock` files in the `webclient` subdirectory
|
||||
# directory: "/webclient"
|
||||
# # Check the npm registry for updates once a week
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
# open-pull-requests-limit: 5
|
||||
257
.github/workflows/desktop-build.yml
vendored
257
.github/workflows/desktop-build.yml
vendored
@@ -8,6 +8,7 @@ on:
|
||||
- '**.md'
|
||||
- 'webclient/**'
|
||||
- '.github/workflows/web-*.yml'
|
||||
- '.github/workflows/translations-*.yml'
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
@@ -15,6 +16,12 @@ on:
|
||||
- '**.md'
|
||||
- 'webclient/**'
|
||||
- '.github/workflows/web-*.yml'
|
||||
- '.github/workflows/translations-*.yml'
|
||||
|
||||
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on master)
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} @ ${{ github.ref_name }}"
|
||||
cancel-in-progress: ${{ github.ref_name != 'master' }}
|
||||
|
||||
jobs:
|
||||
configure:
|
||||
@@ -24,31 +31,26 @@ jobs:
|
||||
tag: ${{steps.configure.outputs.tag}}
|
||||
sha: ${{steps.configure.outputs.sha}}
|
||||
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
with:
|
||||
access_token: ${{github.token}} # needs other token https://github.com/styfle/cancel-workflow-action/issues/7
|
||||
|
||||
steps:
|
||||
- name: Configure
|
||||
id: configure
|
||||
shell: bash
|
||||
run: |
|
||||
tag_regex='^refs/tags/'
|
||||
if [[ $GITHUB_EVENT_NAME == pull-request ]]; then # pull request
|
||||
if [[ $GITHUB_EVENT_NAME == pull-request ]]; then # pull request
|
||||
sha="${{github.event.pull_request.head.sha}}"
|
||||
elif [[ $GITHUB_REF =~ $tag_regex ]]; then # release
|
||||
elif [[ $GITHUB_REF =~ $tag_regex ]]; then # release
|
||||
sha="$GITHUB_SHA"
|
||||
tag="${GITHUB_REF/refs\/tags\//}"
|
||||
echo "tag=$tag" >>"$GITHUB_OUTPUT"
|
||||
else # push to branch
|
||||
else # push to branch
|
||||
sha="$GITHUB_SHA"
|
||||
fi
|
||||
echo "sha=$sha" >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout
|
||||
if: steps.configure.outputs.tag != null
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -83,82 +85,84 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# these names correspond to the files in .ci/$distro
|
||||
# These names correspond to the files in ".ci/$distro$version"
|
||||
include:
|
||||
- distro: ArchLinux
|
||||
package: skip # we are packaged in arch already
|
||||
- distro: Arch
|
||||
package: skip # We are packaged in Arch already
|
||||
allow-failure: yes
|
||||
|
||||
- distro: Debian10
|
||||
- distro: Debian
|
||||
version: 11
|
||||
package: DEB
|
||||
test: skip # running tests on all distros is superfluous
|
||||
test: skip # Running tests on all distros is superfluous
|
||||
|
||||
- distro: Debian11
|
||||
- distro: Debian
|
||||
version: 12
|
||||
package: DEB
|
||||
|
||||
- distro: Fedora36
|
||||
- distro: Fedora
|
||||
version: 40
|
||||
package: RPM
|
||||
test: skip
|
||||
test: skip # Running tests on all distros is superfluous
|
||||
|
||||
- distro: Fedora37
|
||||
- distro: Fedora
|
||||
version: 41
|
||||
package: RPM
|
||||
|
||||
- distro: UbuntuBionic
|
||||
- distro: Ubuntu
|
||||
version: 20.04
|
||||
package: DEB
|
||||
test: skip # Ubuntu 20.04 has a broken Qt for debug builds
|
||||
|
||||
- distro: Ubuntu
|
||||
version: 22.04
|
||||
package: DEB
|
||||
test: skip # Running tests on all distros is superfluous
|
||||
|
||||
- distro: Ubuntu
|
||||
version: 24.04
|
||||
package: DEB
|
||||
|
||||
- distro: UbuntuFocal
|
||||
package: DEB
|
||||
test: skip # UbuntuFocal has a broken qt for debug builds
|
||||
|
||||
- distro: UbuntuJammy
|
||||
package: DEB
|
||||
test: skip # running tests on all distros is superfluous
|
||||
|
||||
- distro: UbuntuKinetic
|
||||
package: DEB
|
||||
|
||||
name: ${{matrix.distro}}
|
||||
name: ${{matrix.distro}} ${{matrix.version}}
|
||||
needs: configure
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
||||
env:
|
||||
NAME: ${{matrix.distro}}
|
||||
CACHE: /tmp/${{matrix.distro}}-cache # ${{runner.temp}} does not work?
|
||||
# cache size over the entire repo is 10Gi link:
|
||||
NAME: ${{matrix.distro}}${{matrix.version}}
|
||||
CACHE: /tmp/${{matrix.distro}}${{matrix.version}}-cache # ${{runner.temp}} does not work?
|
||||
# Cache size over the entire repo is 10Gi:
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||
CCACHE_SIZE: 200M
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get cache timestamp
|
||||
- name: Generate cache timestamp
|
||||
id: cache_timestamp
|
||||
shell: bash
|
||||
run: echo "timestamp=$(date -u '+%Y%m%d%H%M%S')" >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
timestamp: ${{steps.cache_timestamp.outputs.timestamp}}
|
||||
with:
|
||||
path: ${{env.CACHE}}
|
||||
key: docker-${{matrix.distro}}-cache-${{env.timestamp}}
|
||||
key: docker-${{matrix.distro}}${{matrix.version}}-cache-${{env.timestamp}}
|
||||
restore-keys: |
|
||||
docker-${{matrix.distro}}-cache-
|
||||
docker-${{matrix.distro}}${{matrix.version}}-cache-
|
||||
|
||||
- name: Build ${{matrix.distro}} Docker image
|
||||
- name: Build ${{matrix.distro}} ${{matrix.version}} Docker image
|
||||
shell: bash
|
||||
run: source .ci/docker.sh --build
|
||||
|
||||
- name: Build debug and test
|
||||
if: matrix.test != 'skip'
|
||||
shell: bash
|
||||
env:
|
||||
distro: '${{matrix.distro}}'
|
||||
run: |
|
||||
source .ci/docker.sh
|
||||
RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 2
|
||||
RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 4
|
||||
|
||||
- name: Build release package
|
||||
id: build
|
||||
@@ -166,20 +170,19 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
SUFFIX: '-${{matrix.distro}}'
|
||||
distro: '${{matrix.distro}}'
|
||||
SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
|
||||
type: '${{matrix.package}}'
|
||||
run: |
|
||||
source .ci/docker.sh
|
||||
RUN --server --release --package "$type" --dir "$BUILD_DIR" \
|
||||
--ccache "$CCACHE_SIZE" --parallel 2
|
||||
--ccache "$CCACHE_SIZE" --parallel 4
|
||||
.ci/name_build.sh
|
||||
|
||||
- name: Upload artifact
|
||||
if: matrix.package != 'skip'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{matrix.distro}}-package
|
||||
name: ${{matrix.distro}}${{matrix.version}}-package
|
||||
path: ${{steps.build.outputs.path}}
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -198,40 +201,30 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: Debug # tests only
|
||||
os: macos-latest
|
||||
xcode: 14.2
|
||||
qt_version: 6.*
|
||||
qt_modules: "qtmultimedia qtwebsockets"
|
||||
- target: 13
|
||||
soc: Intel
|
||||
os: macos-13
|
||||
xcode: "14.3.1"
|
||||
type: Release
|
||||
core_count: 4
|
||||
make_package: 1
|
||||
|
||||
- target: 14
|
||||
soc: Apple
|
||||
os: macos-14
|
||||
xcode: "15.4"
|
||||
type: Release
|
||||
core_count: 3
|
||||
make_package: 1
|
||||
|
||||
- target: 14
|
||||
soc: Apple
|
||||
os: macos-14
|
||||
xcode: "15.4"
|
||||
type: Debug
|
||||
do_tests: 1
|
||||
core_count: 3
|
||||
|
||||
- target: 10.15_Catalina
|
||||
os: macos-11
|
||||
xcode: 11.7 # allows using macOS 10.15 SDK
|
||||
qt_version: 6.2.* # LTS (is compatible with 10.14 and 10.15)
|
||||
qt_modules: "qtmultimedia qtwebsockets"
|
||||
type: Release
|
||||
do_tests: 1
|
||||
make_package: 1
|
||||
|
||||
- target: 11_Big_Sur
|
||||
os: macos-11
|
||||
xcode: 12.5.1
|
||||
qt_version: homebrew
|
||||
type: Release
|
||||
do_tests: 1
|
||||
make_package: 1
|
||||
|
||||
# - target: 12_Monterey
|
||||
# os: macos-12
|
||||
# xcode: 13.3
|
||||
# qt_version: homebrew
|
||||
# type: Release
|
||||
# do_tests: 1
|
||||
# make_package: 1
|
||||
|
||||
name: macOS ${{matrix.target}}
|
||||
name: macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
|
||||
needs: configure
|
||||
runs-on: ${{matrix.os}}
|
||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
||||
@@ -241,48 +234,36 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies using homebrew
|
||||
- name: Install dependencies using Homebrew
|
||||
shell: bash
|
||||
# cmake cannot find the mysql connector
|
||||
# neither of these works: mariadb-connector-c mysql-connector-c++
|
||||
# CMake cannot find the MySQL connector
|
||||
# Neither of these works: mariadb-connector-c mysql-connector-c++
|
||||
env:
|
||||
install_qt: ${{matrix.qt_version}}
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||
run: |
|
||||
brew update
|
||||
brew install protobuf
|
||||
if [[ $install_qt == homebrew ]]; then
|
||||
brew install qt --force-bottle
|
||||
else # for some reason the tests fail with the action installed qt?
|
||||
brew install googletest
|
||||
fi
|
||||
|
||||
- name: Install Qt ${{matrix.qt_version}} for ${{matrix.target}}
|
||||
if: matrix.qt_version != 'homebrew'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
cache: true
|
||||
setup-python: false
|
||||
version: ${{matrix.qt_version}}
|
||||
modules: ${{matrix.qt_modules}}
|
||||
brew install protobuf qt --force-bottle
|
||||
|
||||
- name: Build on Xcode ${{matrix.xcode}}
|
||||
shell: bash
|
||||
id: build
|
||||
env:
|
||||
BUILDTYPE: '${{matrix.type}}'
|
||||
MAKE_TEST: '${{matrix.do_tests}}'
|
||||
MAKE_TEST: 1
|
||||
MAKE_PACKAGE: '${{matrix.make_package}}'
|
||||
PACKAGE_SUFFIX: '-macOS-${{matrix.target}}'
|
||||
# Mac machines actually have 3 cores
|
||||
run: .ci/compile.sh --server --parallel 3
|
||||
PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}'
|
||||
# macOS runner have 3 cores usually - only the macos-13 image has 4:
|
||||
# https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
|
||||
run: .ci/compile.sh --server --parallel ${{matrix.core_count}}
|
||||
|
||||
- name: Upload artifact
|
||||
if: matrix.make_package
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macOS-${{matrix.target}}-dmg
|
||||
name: macOS${{matrix.target}}${{ matrix.soc == 'Intel' && '_Intel' || '' }}${{ matrix.type == 'Debug' && '_Debug' || '' }}-dmg
|
||||
path: ${{steps.build.outputs.path}}
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -301,80 +282,68 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: Win-32bit
|
||||
bit: 32
|
||||
arch: x86
|
||||
cmake_generator_platform: Win32
|
||||
qt_version: 5.15.*
|
||||
qt_arch: msvc2019
|
||||
qt_tools: "tools_openssl_x86"
|
||||
|
||||
- target: Win7+-64bit
|
||||
bit: 64
|
||||
arch: x64
|
||||
cmake_generator_platform: x64
|
||||
- target: 7
|
||||
qt_version: 5.15.*
|
||||
qt_arch: msvc2019_64
|
||||
qt_tools: "tools_openssl_x64"
|
||||
|
||||
- target: Win10+-64bit
|
||||
bit: 64
|
||||
arch: x64
|
||||
cmake_generator_platform: x64
|
||||
qt_version: 6.3.*
|
||||
- target: 10
|
||||
qt_version: 6.6.*
|
||||
qt_arch: msvc2019_64
|
||||
qt_tools: "tools_openssl_x64"
|
||||
qt_modules: "qtmultimedia qtwebsockets"
|
||||
qt_modules: "qtimageformats qtmultimedia qtwebsockets"
|
||||
|
||||
name: ${{matrix.target}}
|
||||
name: Windows ${{matrix.target}}
|
||||
needs: configure
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CMAKE_GENERATOR: 'Visual Studio 16 2019'
|
||||
CMAKE_GENERATOR: 'Visual Studio 17 2022'
|
||||
|
||||
steps:
|
||||
- name: Add msbuild to PATH
|
||||
id: add-msbuild
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
msbuild-architecture: x64
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Qt ${{matrix.qt_version}} for ${{matrix.target}}
|
||||
uses: jurplel/install-qt-action@v3
|
||||
- name: Install Qt ${{matrix.qt_version}}
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
cache: true
|
||||
setup-python: false
|
||||
version: ${{matrix.qt_version}}
|
||||
arch: win${{matrix.bit}}_${{matrix.qt_arch}}
|
||||
arch: win64_${{matrix.qt_arch}}
|
||||
tools: ${{matrix.qt_tools}}
|
||||
modules: ${{matrix.qt_modules}}
|
||||
|
||||
- name: Run vcpkg
|
||||
uses: lukka/run-vcpkg@v10.6
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
runVcpkgInstall: true
|
||||
appendedCacheKey: ${{matrix.bit}}-bit
|
||||
doNotCache: false
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: '${{matrix.arch}}-windows'
|
||||
VCPKG_DEFAULT_TRIPLET: 'x64-windows'
|
||||
VCPKG_DISABLE_METRICS: 1
|
||||
|
||||
- name: Build Cockatrice
|
||||
id: build
|
||||
shell: bash
|
||||
env:
|
||||
PACKAGE_SUFFIX: '-${{matrix.target}}'
|
||||
PACKAGE_SUFFIX: '-Win${{matrix.target}}'
|
||||
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
|
||||
CMAKE_GENERATOR_PLATFORM: '${{matrix.cmake_generator_platform}}'
|
||||
QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win${{matrix.bit}}_${{matrix.qt_arch}}'
|
||||
run: .ci/compile.sh --server --release --test --package --parallel 2
|
||||
CMAKE_GENERATOR_PLATFORM: 'x64'
|
||||
QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}'
|
||||
# No need for --parallel flag, MTT is added in the compile script to let cmake/msbuild manage core count,
|
||||
# project and process parallelism: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
|
||||
run: .ci/compile.sh --server --release --test --package
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{matrix.target}}-installer
|
||||
name: Windows${{matrix.target}}-installer
|
||||
path: ${{steps.build.outputs.path}}
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
3
.github/workflows/desktop-lint.yml
vendored
3
.github/workflows/desktop-lint.yml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
- '**.md'
|
||||
- 'webclient/**'
|
||||
- '.github/workflows/web-*.yml'
|
||||
- '.github/workflows/translations-*.yml'
|
||||
|
||||
jobs:
|
||||
format:
|
||||
@@ -13,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 20 # should be enough to find merge base
|
||||
|
||||
|
||||
72
.github/workflows/translations-pull.yml
vendored
Normal file
72
.github/workflows/translations-pull.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Update Translations
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# runs in the middle of each month starting a quarter (UTC) = two weeks after new strings are built
|
||||
- cron: '0 0 15 1,4,7,10 *'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/translations-pull.yml'
|
||||
|
||||
jobs:
|
||||
translations:
|
||||
# Do not run the scheduled workflow on forks
|
||||
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
||||
|
||||
name: Pull languages
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Pull translated strings from Transifex
|
||||
uses: transifex/cli-action@v2
|
||||
with:
|
||||
# used config file: https://github.com/Cockatrice/Cockatrice/blob/master/.tx/config
|
||||
# https://github.com/transifex/cli#pulling-files-from-transifex
|
||||
token: ${{ secrets.TX_TOKEN }}
|
||||
args: pull --force --all
|
||||
|
||||
- name: Create pull request
|
||||
if: github.event_name != 'pull_request'
|
||||
id: create_pr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
add-paths: |
|
||||
cockatrice/translations/*.ts
|
||||
oracle/translations/*.ts
|
||||
webclient/public/locales/*/translation.json
|
||||
commit-message: Update translation files
|
||||
# author is the owner of the commit
|
||||
author: github-actions <github-actions@github.com>
|
||||
branch: ci-update_translations
|
||||
delete-branch: true
|
||||
title: 'Update translations'
|
||||
body: |
|
||||
Pulled all translated strings from [Transifex][1].
|
||||
|
||||
---
|
||||
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-pull.yml`. Review [action runs][2].*<br>
|
||||
*After merging, all new languages and translations are available in the next build.*
|
||||
|
||||
[1]: https://app.transifex.com/cockatrice/cockatrice/
|
||||
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-pull.yml?query=branch%3Amaster
|
||||
labels: |
|
||||
CI
|
||||
Translation
|
||||
draft: false
|
||||
|
||||
- name: PR Status
|
||||
if: github.event_name != 'pull_request'
|
||||
shell: bash
|
||||
env:
|
||||
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }}
|
||||
run: |
|
||||
if [[ "$STATUS" == "none" ]]; then
|
||||
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY
|
||||
87
.github/workflows/translations-push.yml
vendored
Normal file
87
.github/workflows/translations-push.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: Update Translation Source
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# runs at the start of each quarter (UTC)
|
||||
- cron: '0 0 1 1,4,7,10 *'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/translations-push.yml'
|
||||
|
||||
jobs:
|
||||
translations:
|
||||
# Do not run the scheduled workflow on forks
|
||||
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
||||
|
||||
name: Push strings
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install lupdate
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends qttools5-dev-tools
|
||||
|
||||
- name: Update Cockatrice translation source
|
||||
id: cockatrice
|
||||
shell: bash
|
||||
env:
|
||||
FILE: 'cockatrice/cockatrice_en@source.ts'
|
||||
DIRS: 'cockatrice/src common'
|
||||
run: .ci/update_translation_source_strings.sh
|
||||
|
||||
- name: Update Oracle translation source
|
||||
id: oracle
|
||||
shell: bash
|
||||
env:
|
||||
FILE: 'oracle/oracle_en@source.ts'
|
||||
DIRS: 'oracle/src'
|
||||
run: .ci/update_translation_source_strings.sh
|
||||
|
||||
- name: Render template
|
||||
id: template
|
||||
uses: chuhlomin/render-template@v1
|
||||
with:
|
||||
template: .ci/update_translation_source_strings_template.md
|
||||
vars: |
|
||||
cockatrice_output: ${{ steps.cockatrice.outputs.output }}
|
||||
oracle_output: ${{ steps.oracle.outputs.output }}
|
||||
commit: ${{ github.sha }}
|
||||
|
||||
- name: Create pull request
|
||||
if: github.event_name != 'pull_request'
|
||||
id: create_pr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
add-paths: |
|
||||
cockatrice/cockatrice_en@source.ts
|
||||
oracle/oracle_en@source.ts
|
||||
commit-message: Update translation source strings
|
||||
# author is the owner of the commit
|
||||
author: github-actions <github-actions@github.com>
|
||||
branch: ci-update_translation_source
|
||||
delete-branch: true
|
||||
title: 'Update source strings'
|
||||
body: ${{ steps.template.outputs.result }}
|
||||
labels: |
|
||||
CI
|
||||
Translation
|
||||
draft: false
|
||||
|
||||
- name: PR Status
|
||||
if: github.event_name != 'pull_request'
|
||||
shell: bash
|
||||
env:
|
||||
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }}
|
||||
run: |
|
||||
if [[ "$STATUS" == "none" ]]; then
|
||||
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY
|
||||
66
.github/workflows/translations.yml
vendored
66
.github/workflows/translations.yml
vendored
@@ -1,66 +0,0 @@
|
||||
name: Update translation source
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# runs once per month
|
||||
- cron: '0 0 1 * *'
|
||||
|
||||
jobs:
|
||||
translations:
|
||||
# Do not run the scheduled workflow on forks
|
||||
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Install lupdate
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends qttools5-dev-tools
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Update cockatrice translations
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s globstar # globstar is needed for recursive **
|
||||
lupdate -version
|
||||
echo "reading the following source files:"
|
||||
# note: there are three strings to translate in common right now
|
||||
echo {cockatrice,common}/**/*.{cpp,h}
|
||||
echo "$(echo {cockatrice,common}/**/*.{cpp,h} | wc -w) files total"
|
||||
lupdate {cockatrice,common}/**/*.{cpp,h} -ts cockatrice/translations/cockatrice_en@source.ts
|
||||
|
||||
- name: Update oracle translations
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s globstar # globstar is needed for recursive **
|
||||
lupdate -version
|
||||
echo "reading the following source files:"
|
||||
echo oracle/**/*.{cpp,h}
|
||||
echo "$(echo oracle/**/*.{cpp,h} | wc -w) files total"
|
||||
lupdate oracle/**/*.{cpp,h} -ts oracle/translations/oracle_en@source.ts
|
||||
|
||||
- name: Check for updates
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
set +e # do not fail, just save the exit state
|
||||
git diff --exit-code
|
||||
echo "deploy=$?" >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: Commit changes
|
||||
if: steps.check.outputs.deploy == '1'
|
||||
shell: bash
|
||||
working-directory: ${{env.OUTPUT_PATH}}
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git add cockatrice/translations/cockatrice_en@source.ts oracle/translations/oracle_en@source.ts
|
||||
git commit -m "Automated translation update ( $GITHUB_SHA )"
|
||||
git push
|
||||
deploy_commit=$(git rev-parse HEAD)
|
||||
echo "Created commit: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/commit/$deploy_commit"
|
||||
5
.github/workflows/web-build.yml
vendored
5
.github/workflows/web-build.yml
vendored
@@ -33,10 +33,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{matrix.node_version}}
|
||||
cache: 'npm'
|
||||
@@ -50,4 +50,3 @@ jobs:
|
||||
|
||||
- name: Test app
|
||||
run: npm run test
|
||||
|
||||
|
||||
4
.github/workflows/web-lint.yml
vendored
4
.github/workflows/web-lint.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'webclient/package-lock.json'
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@ preferences
|
||||
compile_commands.json
|
||||
.vs/
|
||||
.vscode/
|
||||
.cache
|
||||
.gdb_history
|
||||
|
||||
27
.tx/config
27
.tx/config
@@ -1,13 +1,26 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
host = https://app.transifex.com
|
||||
|
||||
[cockatrice.cockatrice-translations-cockatrice-en-source-ts--master]
|
||||
[o:cockatrice:p:cockatrice:r:cockatrice-cockatrice-en-source-ts--master]
|
||||
resource_name = Cockatrice
|
||||
source_lang = en
|
||||
source_file = cockatrice/cockatrice_en@source.ts
|
||||
file_filter = cockatrice/translations/cockatrice_<lang>.ts
|
||||
source_file = cockatrice/translations/cockatrice_en@source.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
minimum_perc = 10
|
||||
|
||||
[cockatrice.oracle-translations-oracle-en-source-ts--master]
|
||||
[o:cockatrice:p:cockatrice:r:oracle-oracle-en-source-ts--master]
|
||||
resource_name = Oracle
|
||||
source_lang = en
|
||||
source_file = oracle/oracle_en@source.ts
|
||||
file_filter = oracle/translations/oracle_<lang>.ts
|
||||
source_file = oracle/translations/oracle_en@source.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
minimum_perc = 10
|
||||
|
||||
[o:cockatrice:p:cockatrice:r:webclient-src-i18n-default-json--master]
|
||||
resource_name = Webclient
|
||||
source_lang = en
|
||||
source_file = webclient/src/i18n-default.json
|
||||
file_filter = webclient/public/locales/<lang>/translation.json
|
||||
type = KEYVALUEJSON
|
||||
minimum_perc = 10
|
||||
|
||||
@@ -74,16 +74,16 @@ endif()
|
||||
|
||||
# A project name is needed for CPack
|
||||
# Version can be overriden by git tags, see cmake/getversion.cmake
|
||||
project("Cockatrice" VERSION 2.8.1)
|
||||
project("Cockatrice" VERSION 2.10.0)
|
||||
|
||||
# Set release name if not provided via env/cmake var
|
||||
if(NOT DEFINED GIT_TAG_RELEASENAME)
|
||||
set(GIT_TAG_RELEASENAME "Prismatic Bridge")
|
||||
set(GIT_TAG_RELEASENAME "Rings of the Wild")
|
||||
endif()
|
||||
|
||||
# Use c++17 for all targets
|
||||
# Use c++20 for all targets
|
||||
set(CMAKE_CXX_STANDARD
|
||||
17
|
||||
20
|
||||
CACHE STRING "C++ ISO Standard"
|
||||
)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
@@ -140,10 +140,14 @@ endif()
|
||||
|
||||
# Define proper compilation flags
|
||||
if(MSVC)
|
||||
# Visual Studio: Maximum optimization, disable warning C4251, establish C++17 compatibility
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD /wd4251 /Zc:__cplusplus /std:c++17 /permissive-")
|
||||
# Generate complete debugging information
|
||||
#set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
|
||||
# Visual Studio: Disable Warning C4251, C++20 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics
|
||||
set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++20 /permissive- /W4 /MP /EHsc")
|
||||
# Visual Studio: Maximum Optimization, Multi-threaded DLL
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD")
|
||||
# Visual Studio: No Optimization, Multi-threaded Debug DLL, Debug Symbols
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Od /MDd /Zi")
|
||||
|
||||
add_compile_definitions(_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING)
|
||||
elseif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
# linux/gcc, bsd/gcc, windows/mingw
|
||||
include(CheckCXXCompilerFlag)
|
||||
@@ -156,7 +160,7 @@ elseif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++20")
|
||||
endif()
|
||||
|
||||
set(ADDITIONAL_DEBUG_FLAGS
|
||||
@@ -179,7 +183,7 @@ else()
|
||||
# other: osx/llvm, bsd/llvm
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||
if(WARNING_AS_ERROR)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Werror")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Werror -Wno-unused-parameter")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra")
|
||||
endif()
|
||||
@@ -216,16 +220,26 @@ include(FindQtRuntime)
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
# Find other needed libraries
|
||||
find_package(Protobuf REQUIRED)
|
||||
if(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
|
||||
find_package(Protobuf CONFIG)
|
||||
if(NOT Protobuf_FOUND)
|
||||
find_package(Protobuf REQUIRED)
|
||||
endif()
|
||||
|
||||
if(${Protobuf_VERSION} VERSION_LESS "3.21.0.0" AND NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
|
||||
message(FATAL_ERROR "No protoc command found!")
|
||||
else()
|
||||
message(STATUS "Found Protobuf ${Protobuf_VERSION} at: ${Protobuf_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
#Find OpenSSL
|
||||
if(WIN32)
|
||||
find_package(Win32SslRuntime)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
if(OPENSSL_FOUND)
|
||||
include_directories(${OPENSSL_INCLUDE_DIRS})
|
||||
else()
|
||||
message(
|
||||
WARNING
|
||||
"Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#Find VCredist
|
||||
@@ -252,13 +266,15 @@ if(UNIX)
|
||||
set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}")
|
||||
set(CPACK_SYSTEM_NAME "OSX")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice/resources/appicon.icns")
|
||||
set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeDMGSetup.script")
|
||||
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/dmgBackground.tif")
|
||||
else()
|
||||
# linux
|
||||
if(CPACK_GENERATOR STREQUAL "RPM")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
|
||||
set(CPACK_RPM_MAIN_COMPONENT "cockatrice")
|
||||
if(Qt6_FOUND)
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt6-qttools, qt6-qtsvg, qt6-qtmultimedia")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt6-qttools, qt6-qtsvg, qt6-qtmultimedia, qt6-qtimageformats")
|
||||
elseif(Qt5_FOUND)
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt5-qttools, qt5-qtsvg, qt5-qtmultimedia")
|
||||
endif()
|
||||
@@ -280,7 +296,7 @@ if(UNIX)
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
|
||||
if(Qt6_FOUND)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins, qt6-image-formats-plugins")
|
||||
elseif(Qt5_FOUND)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
|
||||
endif()
|
||||
|
||||
17
README.md
17
README.md
@@ -5,7 +5,7 @@
|
||||
<p align='center'>
|
||||
<a href="#cockatrice"><b>Cockatrice</b></a> <b>|</b>
|
||||
<a href="#download-">Download</a> <b>|</b>
|
||||
<a href="#get-involved--">Get Involved</a> <b>|</b>
|
||||
<a href="#get-involved-">Get Involved</a> <b>|</b>
|
||||
<a href="#community-resources">Community</a> <b>|</b>
|
||||
<a href="#translations-">Translations</a> <b>|</b>
|
||||
<a href="#build--">Build</a> <b>|</b>
|
||||
@@ -18,7 +18,7 @@
|
||||
<br><pre>
|
||||
<b>To get started, ⇢ [view our webpage](https://cockatrice.github.io/)</b><br>
|
||||
<b>To get support or suggest changes ⇢ [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))</b>
|
||||
<b>To help with development, see how to [get involved](#get-involved--)</b>
|
||||
<b>To help with development, see how to [get involved](#get-involved-)</b>
|
||||
</pre><br>
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ Downloads are available for full releases and the current beta version in develo
|
||||
- To be a Cockatrice Beta Tester, use this version. Find more information [here](https://github.com/Cockatrice/Cockatrice/wiki/Release-Channels)!
|
||||
|
||||
|
||||
# Get Involved [](https://discord.gg/3Z9yzmA) [](https://gitter.im/Cockatrice/Cockatrice)
|
||||
# Get Involved [](https://discord.gg/3Z9yzmA)
|
||||
|
||||
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with the project or fellow users of the app. The Cockatrice developers are also available on [Gitter](https://gitter.im/Cockatrice/Cockatrice). Come here to talk about the application, features, or just to hang out.<br>
|
||||
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with the project, contributors or fellow users of the app. Come here to talk about the application, features, or just to hang out.<br>
|
||||
For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
|
||||
|
||||
To contribute code to the project, please review [the guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md).
|
||||
@@ -65,13 +65,9 @@ Cockatrice uses the [Google Developer Documentation Style Guide](https://develop
|
||||
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
|
||||
|
||||
|
||||
# Translations [](https://www.transifex.com/projects/p/cockatrice/)
|
||||
# Translations [](https://transifex.com/cockatrice/cockatrice/)
|
||||
|
||||
Cockatrice uses Transifex for translations. You can help us bring Cockatrice and Oracle to your language or just edit single wordings right from within your browser by visiting our [Transifex project page](https://www.transifex.com/projects/p/cockatrice/).<br>
|
||||
|
||||
| Cockatrice | Oracle |
|
||||
|:-:|:-:|
|
||||
| [](https://www.transifex.com/projects/p/cockatrice/) | [](https://www.transifex.com/projects/p/cockatrice/) |
|
||||
Cockatrice uses Transifex for translations. You can help us bring Cockatrice, Oracle and Webatrice to your language or just adjust single wordings right from within your browser by visiting our [Transifex project page](https://transifex.com/cockatrice/cockatrice/).<br>
|
||||
|
||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about contributing!<br>
|
||||
|
||||
@@ -116,7 +112,6 @@ The following flags can be passed to `cmake`:
|
||||
- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no).
|
||||
- `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```.
|
||||
- `-DFORCE_USE_QT5=1` Skip looking for Qt6 before trying to find Qt5
|
||||
- `-DOPEN_SSL_PATH=C:/Path/To/Tools/OpenSSL/Win_x64/bin"` Designate the OpenSSL Path if you're using non-standard directives
|
||||
|
||||
|
||||
# Run
|
||||
|
||||
55
cmake/CMakeDMGSetup.script
Normal file
55
cmake/CMakeDMGSetup.script
Normal file
@@ -0,0 +1,55 @@
|
||||
on run argv
|
||||
set image_name to item 1 of argv
|
||||
|
||||
tell application "Finder"
|
||||
tell disk image_name
|
||||
|
||||
-- wait for the image to finish mounting
|
||||
set open_attempts to 0
|
||||
repeat while open_attempts < 4
|
||||
try
|
||||
open
|
||||
delay 1
|
||||
set open_attempts to 5
|
||||
close
|
||||
on error errStr number errorNumber
|
||||
set open_attempts to open_attempts + 1
|
||||
delay 10
|
||||
end try
|
||||
end repeat
|
||||
delay 5
|
||||
|
||||
-- open the image the first time and save a DS_Store with just
|
||||
-- background and icon setup
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set theViewOptions to the icon view options of container window
|
||||
set background picture of theViewOptions to file ".background:background.tif"
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
delay 5
|
||||
close
|
||||
|
||||
-- next setup the position of the app and Applications symlink
|
||||
-- plus hide all the window decoration
|
||||
open
|
||||
update without registering applications
|
||||
tell container window
|
||||
set sidebar width to 0
|
||||
set statusbar visible to false
|
||||
set toolbar visible to false
|
||||
set the bounds to { 400, 100, 1400, 922 }
|
||||
set position of item "Cockatrice.app" to { 139, 214 }
|
||||
set position of item "Oracle.app" to { 139, 414 }
|
||||
set position of item "Servatrice.app" to { 139, 614 }
|
||||
set position of item "dbconverter.app" to { 1400, 1400 }
|
||||
set position of item "Applications" to { 861, 414 }
|
||||
end tell
|
||||
update without registering applications
|
||||
delay 5
|
||||
close
|
||||
|
||||
end tell
|
||||
delay 1
|
||||
end tell
|
||||
end run
|
||||
@@ -10,7 +10,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
# VS 2017 uses vcredist_ARCH.exe, VS 2022 uses vc_redist.ARCH.exe
|
||||
set(REDIST_FILE_NAMES vcredist_${REDIST_ARCH}.exe vc_redist.${REDIST_ARCH}.exe)
|
||||
set(REDIST_FILE_NAMES vcredist_${REDIST_ARCH}.exe vcredist.${REDIST_ARCH}.exe vc_redist.${REDIST_ARCH}.exe)
|
||||
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
@@ -24,7 +24,7 @@ if(WIN32)
|
||||
get_filename_component(_path ${_path}/../../ ABSOLUTE)
|
||||
|
||||
foreach(redist_file ${REDIST_FILE_NAMES})
|
||||
if(EXISTS "${_path}/${REDIST_FILE}")
|
||||
if(EXISTS "${_path}/${redist_file}")
|
||||
set(VCREDISTRUNTIME_FOUND "YES")
|
||||
set(VCREDISTRUNTIME_FILE ${_path}/${redist_file})
|
||||
break()
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
# Find the OpenSSL runtime libraries (.dll) for Windows that
|
||||
# will be needed by Qt in order to access https urls.
|
||||
if(NOT DEFINED WIN32 OR NOT ${WIN32})
|
||||
message(STATUS "Non-Windows device trying to execute FindWin32SslRuntime, skipping")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
|
||||
message(STATUS "Looking for OpenSSL for ${CMAKE_GENERATOR_PLATFORM}")
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
${OPEN_SSL_PATH}
|
||||
"$ENV{VCPKG_PACKAGES_DIR}/x64-windows/bin"
|
||||
"C:/OpenSSL-Win64/bin"
|
||||
"C:/OpenSSL-Win64"
|
||||
"C:/Tools/vcpkg/installed/x64-windows/bin"
|
||||
"${_programfiles}/OpenSSL-Win64"
|
||||
"D:/a/Cockatrice/Qt/Tools/OpenSSL/Win_x64/bin"
|
||||
)
|
||||
unset(_programfiles)
|
||||
elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32")
|
||||
message(STATUS "Looking for OpenSSL for ${CMAKE_GENERATOR_PLATFORM}")
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
${OPEN_SSL_PATH}
|
||||
"$ENV{VCPKG_PACKAGES_DIR}/x86-windows/bin"
|
||||
"C:/OpenSSL-Win32/bin"
|
||||
"C:/OpenSSL-Win32"
|
||||
"C:/OpenSSL"
|
||||
"C:/Tools/vcpkg/installed/x86-windows/bin"
|
||||
"${_programfiles}/OpenSSL"
|
||||
"${_programfiles}/OpenSSL-Win32"
|
||||
"D:/a/Cockatrice/Qt/Tools/OpenSSL/Win_x86/bin"
|
||||
)
|
||||
unset(_programfiles)
|
||||
endif()
|
||||
|
||||
message(STATUS "Looking for OpenSSL @ ${CMAKE_GENERATOR_PLATFORM} in ${_OPENSSL_ROOT_PATHS}")
|
||||
if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
|
||||
find_file(
|
||||
WIN32SSLRUNTIME_LIBEAY
|
||||
NAMES libcrypto-1_1-x64.dll libcrypto.dll
|
||||
PATHS ${_OPENSSL_ROOT_PATHS}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
find_file(
|
||||
WIN32SSLRUNTIME_SSLEAY
|
||||
NAMES libssl-1_1-x64.dll libssl.dll
|
||||
PATHS ${_OPENSSL_ROOT_PATHS}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32")
|
||||
find_file(
|
||||
WIN32SSLRUNTIME_LIBEAY
|
||||
NAMES libcrypto-1_1.dll libcrypto.dll
|
||||
PATHS ${_OPENSSL_ROOT_PATHS}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
find_file(
|
||||
WIN32SSLRUNTIME_SSLEAY
|
||||
NAMES libssl-1_1.dll libssl.dll
|
||||
PATHS ${_OPENSSL_ROOT_PATHS}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY)
|
||||
set(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}")
|
||||
set(WIN32SSLRUNTIME_FOUND "YES")
|
||||
message(STATUS "Found OpenSSL ${WIN32SSLRUNTIME_LIBRARIES}")
|
||||
else()
|
||||
set(WIN32SSLRUNTIME_FOUND "NO")
|
||||
message(
|
||||
WARNING
|
||||
"Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime."
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(WIN32SSLRUNTIME_LIBEAY WIN32SSLRUNTIME_SSLEAY)
|
||||
@@ -235,6 +235,13 @@ ${If} $PortableMode = 0
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
|
||||
; Enable Windows User-Mode Dumps
|
||||
; https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" "DumpFolder" "%LOCALAPPDATA%\CrashDumps\Cockatrice"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" "DumpCount" "5"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" "DumpType" "2"
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayVersion" "@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
@@ -248,20 +255,20 @@ ${If} $PortableMode = 0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMajor" "@CPACK_PACKAGE_VERSION_MAJOR@"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMinor" "@CPACK_PACKAGE_VERSION_MINOR@"
|
||||
|
||||
IfFileExists "$INSTDIR\vcredist_x86.exe" VcRedist86Exists PastVcRedist86Check
|
||||
IfFileExists "$INSTDIR\vc_redist.x86.exe" VcRedist86Exists PastVcRedist86Check
|
||||
VcRedist86Exists:
|
||||
ExecWait '"$INSTDIR\vcredist_x86.exe" /passive /norestart'
|
||||
ExecWait '"$INSTDIR\vc_redist.x86.exe" /passive /norestart'
|
||||
DetailPrint "Wait to ensure unlock of vc_redist file after installation..."
|
||||
Sleep 3000
|
||||
Delete "$INSTDIR\vcredist_x86.exe"
|
||||
Delete "$INSTDIR\vc_redist.x86.exe"
|
||||
PastVcRedist86Check:
|
||||
|
||||
IfFileExists "$INSTDIR\vcredist_x64.exe" VcRedist64Exists PastVcRedist64Check
|
||||
IfFileExists "$INSTDIR\vc_redist.x64.exe" VcRedist64Exists PastVcRedist64Check
|
||||
VcRedist64Exists:
|
||||
ExecWait '"$INSTDIR\vcredist_x64.exe" /passive /norestart'
|
||||
ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart'
|
||||
DetailPrint "Sleep to ensure unlock of vc_redist file after installation..."
|
||||
Sleep 3000
|
||||
Delete "$INSTDIR\vcredist_x64.exe"
|
||||
Delete "$INSTDIR\vc_redist.x64.exe"
|
||||
PastVcRedist64Check:
|
||||
|
||||
${Else}
|
||||
|
||||
BIN
cmake/dmgBackground.tif
Normal file
BIN
cmake/dmgBackground.tif
Normal file
Binary file not shown.
@@ -19,7 +19,7 @@ function(get_commit_id)
|
||||
PARENT_SCOPE
|
||||
)
|
||||
set(PROJECT_VERSION_LABEL
|
||||
"custom(${GIT_COM_ID})"
|
||||
"custom-${GIT_COM_ID}"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endfunction()
|
||||
|
||||
@@ -5,127 +5,156 @@
|
||||
project(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
||||
set(cockatrice_SOURCES
|
||||
src/abstractcarddragitem.cpp
|
||||
src/abstractcarditem.cpp
|
||||
src/abstractclient.cpp
|
||||
src/abstractcounter.cpp
|
||||
src/abstractgraphicsitem.cpp
|
||||
src/arrowitem.cpp
|
||||
src/arrowtarget.cpp
|
||||
src/carddatabase.cpp
|
||||
src/carddatabasemodel.cpp
|
||||
src/carddbparser/carddatabaseparser.cpp
|
||||
src/carddbparser/cockatricexml3.cpp
|
||||
src/carddbparser/cockatricexml4.cpp
|
||||
src/carddragitem.cpp
|
||||
src/cardfilter.cpp
|
||||
src/cardframe.cpp
|
||||
src/cardinfopicture.cpp
|
||||
src/cardinfotext.cpp
|
||||
src/cardinfowidget.cpp
|
||||
src/carditem.cpp
|
||||
src/cardlist.cpp
|
||||
src/cardzone.cpp
|
||||
src/chatview/chatview.cpp
|
||||
src/counter_general.cpp
|
||||
src/customlineedit.cpp
|
||||
src/deck_loader.cpp
|
||||
src/decklistmodel.cpp
|
||||
src/deckstats_interface.cpp
|
||||
src/deckview.cpp
|
||||
src/dlg_connect.cpp
|
||||
src/dlg_create_token.cpp
|
||||
src/dlg_creategame.cpp
|
||||
src/dlg_edit_avatar.cpp
|
||||
src/dlg_edit_password.cpp
|
||||
src/dlg_edit_tokens.cpp
|
||||
src/dlg_edit_user.cpp
|
||||
src/dlg_filter_games.cpp
|
||||
src/dlg_forgotpasswordchallenge.cpp
|
||||
src/dlg_forgotpasswordrequest.cpp
|
||||
src/dlg_forgotpasswordreset.cpp
|
||||
src/dlg_load_deck_from_clipboard.cpp
|
||||
src/dlg_load_remote_deck.cpp
|
||||
src/dlg_manage_sets.cpp
|
||||
src/dlg_register.cpp
|
||||
src/dlg_settings.cpp
|
||||
src/dlg_tip_of_the_day.cpp
|
||||
src/dlg_update.cpp
|
||||
src/dlg_viewlog.cpp
|
||||
src/filter_string.cpp
|
||||
src/filterbuilder.cpp
|
||||
src/filtertree.cpp
|
||||
src/filtertreemodel.cpp
|
||||
src/gamescene.cpp
|
||||
src/gameselector.cpp
|
||||
src/gamesmodel.cpp
|
||||
src/gameview.cpp
|
||||
src/gettextwithmax.cpp
|
||||
src/handcounter.cpp
|
||||
src/handle_public_servers.cpp
|
||||
src/handzone.cpp
|
||||
src/keysignals.cpp
|
||||
src/lineeditcompleter.cpp
|
||||
src/localclient.cpp
|
||||
src/localserver.cpp
|
||||
src/localserverinterface.cpp
|
||||
src/logger.cpp
|
||||
src/game/cards/abstract_card_drag_item.cpp
|
||||
src/game/cards/abstract_card_item.cpp
|
||||
src/client/game_logic/abstract_client.cpp
|
||||
src/game/board/abstract_counter.cpp
|
||||
src/game/board/abstract_graphics_item.cpp
|
||||
src/game/board/arrow_item.cpp
|
||||
src/game/board/arrow_target.cpp
|
||||
src/game/cards/card_database.cpp
|
||||
src/game/cards/card_database_manager.cpp
|
||||
src/game/cards/card_database_model.cpp
|
||||
src/game/cards/card_database_parser/card_database_parser.cpp
|
||||
src/game/cards/card_database_parser/cockatrice_xml_3.cpp
|
||||
src/game/cards/card_database_parser/cockatrice_xml_4.cpp
|
||||
src/game/cards/card_drag_item.cpp
|
||||
src/game/filters/filter_card.cpp
|
||||
src/client/ui/widgets/cards/card_info_frame_widget.cpp
|
||||
src/client/ui/widgets/cards/card_info_picture_widget.cpp
|
||||
src/client/ui/widgets/cards/card_info_text_widget.cpp
|
||||
src/client/ui/widgets/cards/card_info_display_widget.cpp
|
||||
src/client/ui/widgets/cards/card_size_widget.cpp
|
||||
src/game/cards/card_item.cpp
|
||||
src/game/cards/card_list.cpp
|
||||
src/game/zones/card_zone.cpp
|
||||
src/server/chat_view/chat_view.cpp
|
||||
src/game/board/counter_general.cpp
|
||||
src/deck/custom_line_edit.cpp
|
||||
src/deck/deck_loader.cpp
|
||||
src/deck/deck_list_model.cpp
|
||||
src/deck/deck_stats_interface.cpp
|
||||
src/deck/deck_view.cpp
|
||||
src/dialogs/dlg_connect.cpp
|
||||
src/dialogs/dlg_create_token.cpp
|
||||
src/dialogs/dlg_create_game.cpp
|
||||
src/dialogs/dlg_edit_avatar.cpp
|
||||
src/dialogs/dlg_edit_password.cpp
|
||||
src/dialogs/dlg_edit_tokens.cpp
|
||||
src/dialogs/dlg_edit_user.cpp
|
||||
src/dialogs/dlg_filter_games.cpp
|
||||
src/dialogs/dlg_forgot_password_challenge.cpp
|
||||
src/dialogs/dlg_forgot_password_request.cpp
|
||||
src/dialogs/dlg_forgot_password_reset.cpp
|
||||
src/dialogs/dlg_load_deck_from_clipboard.cpp
|
||||
src/dialogs/dlg_load_remote_deck.cpp
|
||||
src/dialogs/dlg_manage_sets.cpp
|
||||
src/dialogs/dlg_move_top_cards_until.cpp
|
||||
src/dialogs/dlg_register.cpp
|
||||
src/dialogs/dlg_roll_dice.cpp
|
||||
src/dialogs/dlg_settings.cpp
|
||||
src/dialogs/dlg_tip_of_the_day.cpp
|
||||
src/dialogs/dlg_update.cpp
|
||||
src/dialogs/dlg_view_log.cpp
|
||||
src/game/filters/filter_string.cpp
|
||||
src/game/filters/filter_builder.cpp
|
||||
src/game/filters/filter_tree.cpp
|
||||
src/game/filters/filter_tree_model.cpp
|
||||
src/client/ui/layouts/flow_layout.cpp
|
||||
src/client/ui/layouts/horizontal_flow_layout.cpp
|
||||
src/client/ui/layouts/vertical_flow_layout.cpp
|
||||
src/client/ui/widgets/general/layout_containers/flow_widget.cpp
|
||||
src/game/game_scene.cpp
|
||||
src/game/game_selector.cpp
|
||||
src/game/games_model.cpp
|
||||
src/game/game_view.cpp
|
||||
src/client/get_text_with_max.cpp
|
||||
src/game/hand_counter.cpp
|
||||
src/server/handle_public_servers.cpp
|
||||
src/game/zones/hand_zone.cpp
|
||||
src/client/game_logic/key_signals.cpp
|
||||
src/client/ui/line_edit_completer.cpp
|
||||
src/server/local_client.cpp
|
||||
src/server/local_server.cpp
|
||||
src/server/local_server_interface.cpp
|
||||
src/utility/logger.cpp
|
||||
src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp
|
||||
src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
|
||||
src/client/ui/widgets/general/display/labeled_input.cpp
|
||||
src/client/ui/widgets/general/display/dynamic_font_size_label.cpp
|
||||
src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp
|
||||
src/client/ui/widgets/general/display/shadow_background_label.cpp
|
||||
src/main.cpp
|
||||
src/messagelogwidget.cpp
|
||||
src/pending_command.cpp
|
||||
src/phase.cpp
|
||||
src/phasestoolbar.cpp
|
||||
src/pictureloader.cpp
|
||||
src/pilezone.cpp
|
||||
src/pixmapgenerator.cpp
|
||||
src/player.cpp
|
||||
src/playerlistwidget.cpp
|
||||
src/playertarget.cpp
|
||||
src/releasechannel.cpp
|
||||
src/remoteclient.cpp
|
||||
src/remotedecklist_treewidget.cpp
|
||||
src/remotereplaylist_treewidget.cpp
|
||||
src/replay_timeline_widget.cpp
|
||||
src/selectzone.cpp
|
||||
src/sequenceEdit/sequenceedit.cpp
|
||||
src/setsmodel.cpp
|
||||
src/settings/carddatabasesettings.cpp
|
||||
src/settings/downloadsettings.cpp
|
||||
src/settings/gamefilterssettings.cpp
|
||||
src/settings/layoutssettings.cpp
|
||||
src/settings/messagesettings.cpp
|
||||
src/settings/serverssettings.cpp
|
||||
src/settings/settingsmanager.cpp
|
||||
src/settingscache.cpp
|
||||
src/shortcutssettings.cpp
|
||||
src/soundengine.cpp
|
||||
src/spoilerbackgroundupdater.cpp
|
||||
src/stackzone.cpp
|
||||
src/tab.cpp
|
||||
src/tab_account.cpp
|
||||
src/tab_admin.cpp
|
||||
src/tab_deck_editor.cpp
|
||||
src/tab_deck_storage.cpp
|
||||
src/tab_game.cpp
|
||||
src/tab_logs.cpp
|
||||
src/tab_message.cpp
|
||||
src/tab_replays.cpp
|
||||
src/tab_room.cpp
|
||||
src/tab_server.cpp
|
||||
src/tab_supervisor.cpp
|
||||
src/tablezone.cpp
|
||||
src/tappedout_interface.cpp
|
||||
src/thememanager.cpp
|
||||
src/tip_of_the_day.cpp
|
||||
src/translatecountername.cpp
|
||||
src/update_downloader.cpp
|
||||
src/user_context_menu.cpp
|
||||
src/userconnection_information.cpp
|
||||
src/userinfobox.cpp
|
||||
src/userlist.cpp
|
||||
src/window_main.cpp
|
||||
src/zoneviewwidget.cpp
|
||||
src/zoneviewzone.cpp
|
||||
src/server/message_log_widget.cpp
|
||||
src/client/ui/layouts/overlap_layout.cpp
|
||||
src/client/ui/widgets/general/layout_containers/overlap_widget.cpp
|
||||
src/client/ui/widgets/general/layout_containers/overlap_control_widget.cpp
|
||||
src/server/pending_command.cpp
|
||||
src/game/phase.cpp
|
||||
src/client/ui/phases_toolbar.cpp
|
||||
src/client/ui/picture_loader.cpp
|
||||
src/game/zones/pile_zone.cpp
|
||||
src/client/ui/pixel_map_generator.cpp
|
||||
src/game/player/player.cpp
|
||||
src/game/player/player_list_widget.cpp
|
||||
src/game/player/player_target.cpp
|
||||
src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/card_amount_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp
|
||||
src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
|
||||
src/client/network/release_channel.cpp
|
||||
src/server/remote/remote_client.cpp
|
||||
src/server/remote/remote_decklist_tree_widget.cpp
|
||||
src/server/remote/remote_replay_list_tree_widget.cpp
|
||||
src/client/network/replay_timeline_widget.cpp
|
||||
src/game/zones/select_zone.cpp
|
||||
src/utility/sequence_edit.cpp
|
||||
src/client/network/sets_model.cpp
|
||||
src/settings/card_database_settings.cpp
|
||||
src/settings/download_settings.cpp
|
||||
src/settings/game_filters_settings.cpp
|
||||
src/settings/layouts_settings.cpp
|
||||
src/settings/message_settings.cpp
|
||||
src/settings/servers_settings.cpp
|
||||
src/settings/settings_manager.cpp
|
||||
src/settings/cache_settings.cpp
|
||||
src/settings/shortcuts_settings.cpp
|
||||
src/settings/shortcut_treeview.cpp
|
||||
src/client/sound_engine.cpp
|
||||
src/client/network/spoiler_background_updater.cpp
|
||||
src/game/zones/stack_zone.cpp
|
||||
src/client/tabs/tab.cpp
|
||||
src/client/tabs/tab_account.cpp
|
||||
src/client/tabs/tab_admin.cpp
|
||||
src/client/tabs/tab_deck_editor.cpp
|
||||
src/client/tabs/tab_deck_storage.cpp
|
||||
src/client/tabs/tab_game.cpp
|
||||
src/client/tabs/tab_logs.cpp
|
||||
src/client/tabs/tab_message.cpp
|
||||
src/client/tabs/tab_replays.cpp
|
||||
src/client/tabs/tab_room.cpp
|
||||
src/client/tabs/tab_server.cpp
|
||||
src/client/tabs/tab_supervisor.cpp
|
||||
src/game/zones/table_zone.cpp
|
||||
src/client/tapped_out_interface.cpp
|
||||
src/client/ui/theme_manager.cpp
|
||||
src/client/ui/tip_of_the_day.cpp
|
||||
src/client/translate_counter_name.cpp
|
||||
src/client/update_downloader.cpp
|
||||
src/server/user/user_context_menu.cpp
|
||||
src/server/user/user_info_connection.cpp
|
||||
src/server/user/user_info_box.cpp
|
||||
src/server/user/user_list.cpp
|
||||
src/client/ui/window_main.cpp
|
||||
src/game/zones/view_zone_widget.cpp
|
||||
src/game/zones/view_zone.cpp
|
||||
${VERSION_STRING_CPP}
|
||||
)
|
||||
|
||||
@@ -311,15 +340,7 @@ if(WIN32)
|
||||
PATTERN "audio/qtaudio_wasapi.dll"
|
||||
PATTERN "audio/qtaudio_windows.dll"
|
||||
PATTERN "iconengines/qsvgicon.dll"
|
||||
PATTERN "imageformats/qgif.dll"
|
||||
PATTERN "imageformats/qicns.dll"
|
||||
PATTERN "imageformats/qico.dll"
|
||||
PATTERN "imageformats/qjpeg.dll"
|
||||
PATTERN "imageformats/qsvg.dll"
|
||||
PATTERN "imageformats/qtga.dll"
|
||||
PATTERN "imageformats/qtiff.dll"
|
||||
PATTERN "imageformats/qwbmp.dll"
|
||||
PATTERN "imageformats/qwebp.dll"
|
||||
PATTERN "imageformats/*.dll"
|
||||
PATTERN "mediaservice/dsengine.dll"
|
||||
PATTERN "mediaservice/wmfengine.dll"
|
||||
PATTERN "platforms/qdirect2d.dll"
|
||||
@@ -357,8 +378,8 @@ Data = Resources\")
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
if(WIN32SSLRUNTIME_FOUND)
|
||||
install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./)
|
||||
if(OPENSSL_FOUND)
|
||||
install(FILES ${OPENSSL_INCLUDE_DIRS} DESTINATION ./)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -351,10 +351,10 @@
|
||||
<file>resources/tips/images/cockatrice_wiki.png</file>
|
||||
<file>resources/tips/images/coin_flip.png</file>
|
||||
<file>resources/tips/images/counter_expression.png</file>
|
||||
<file>resources/tips/images/discord.png</file>
|
||||
<file>resources/tips/images/face_down.png</file>
|
||||
<file>resources/tips/images/filter_games.png</file>
|
||||
<file>resources/tips/images/github_logo.png</file>
|
||||
<file>resources/tips/images/gitter.png</file>
|
||||
<file>resources/tips/images/setpt.png</file>
|
||||
<file>resources/tips/images/shortcuts.png</file>
|
||||
<file>resources/tips/images/themes.png</file>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
## Syntax Help
|
||||
## Search Syntax Help
|
||||
-----
|
||||
The search bar recognizes a set of special commands similar to some other card databases. Here is a list with examples. Each entry can be clicked to test the query and has a small explanation. Note that all searches are case insensitive.
|
||||
The search bar recognizes a set of special commands similar to some other card databases.<br>
|
||||
In this list of examples below, each entry has an explanation and can be clicked to test the query. Note that all searches are case insensitive.
|
||||
<dl>
|
||||
<dt>Name:</dt>
|
||||
<dd>[birds of paradise](#birds of paradise) <small>(Any card name containing the words birds, of, and paradise)</small></dd>
|
||||
|
||||
BIN
cockatrice/resources/tips/images/discord.png
Normal file
BIN
cockatrice/resources/tips/images/discord.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -7,9 +7,9 @@
|
||||
</tip>
|
||||
<tip>
|
||||
<title>Suggesting New Tips</title>
|
||||
<text>You can suggest new Tips of the Day by reaching out to the development team on <a href="https://gitter.im/cockatrice/cockatrice">Gitter</a>!</text>
|
||||
<image>gitter.png</image>
|
||||
<date>2018-03-01</date>
|
||||
<text>You can suggest new Tips of the Day by reaching out to the development team on <a href="https://discord.gg/3Z9yzmA">Discord</a>!</text>
|
||||
<image>discord.png</image>
|
||||
<date>2023-10-18</date>
|
||||
</tip>
|
||||
<tip>
|
||||
<title>Reporting Bugs</title>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "cardinfopicture.h"
|
||||
|
||||
#include "carditem.h"
|
||||
#include "main.h"
|
||||
#include "pictureloader.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QWidget>
|
||||
|
||||
CardInfoPicture::CardInfoPicture(QWidget *parent) : QWidget(parent), info(nullptr), pixmapDirty(true)
|
||||
{
|
||||
setMinimumHeight(100);
|
||||
}
|
||||
|
||||
void CardInfoPicture::setCard(CardInfoPtr card)
|
||||
{
|
||||
if (info) {
|
||||
disconnect(info.data(), nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
info = card;
|
||||
|
||||
if (info) {
|
||||
connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
|
||||
}
|
||||
|
||||
updatePixmap();
|
||||
}
|
||||
|
||||
void CardInfoPicture::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
updatePixmap();
|
||||
}
|
||||
|
||||
void CardInfoPicture::updatePixmap()
|
||||
{
|
||||
pixmapDirty = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void CardInfoPicture::loadPixmap()
|
||||
{
|
||||
if (info)
|
||||
PictureLoader::getPixmap(resizedPixmap, info, size());
|
||||
else
|
||||
PictureLoader::getCardBackPixmap(resizedPixmap, size());
|
||||
}
|
||||
|
||||
void CardInfoPicture::paintEvent(QPaintEvent *)
|
||||
{
|
||||
if (width() == 0 || height() == 0)
|
||||
return;
|
||||
|
||||
if (pixmapDirty)
|
||||
loadPixmap();
|
||||
|
||||
QPainter painter(this);
|
||||
style()->drawItemPixmap(&painter, rect(), Qt::AlignHCenter, resizedPixmap);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef CARDINFOPICTURE_H
|
||||
#define CARDINFOPICTURE_H
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class AbstractCardItem;
|
||||
|
||||
class CardInfoPicture : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
CardInfoPtr info;
|
||||
QPixmap resizedPixmap;
|
||||
bool pixmapDirty;
|
||||
|
||||
public:
|
||||
CardInfoPicture(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
void paintEvent(QPaintEvent *);
|
||||
void loadPixmap();
|
||||
public slots:
|
||||
void setCard(CardInfoPtr card);
|
||||
void updatePixmap();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
#include "cardlist.h"
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "carditem.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
CardList::CardList(bool _contentsKnown) : QList<CardItem *>(), contentsKnown(_contentsKnown)
|
||||
{
|
||||
}
|
||||
|
||||
CardItem *CardList::findCard(const int id, const bool remove, int *position)
|
||||
{
|
||||
if (!contentsKnown) {
|
||||
if (empty())
|
||||
return 0;
|
||||
CardItem *temp = at(0);
|
||||
if (remove)
|
||||
removeAt(0);
|
||||
if (position)
|
||||
*position = id;
|
||||
return temp;
|
||||
} else
|
||||
for (int i = 0; i < size(); i++) {
|
||||
CardItem *temp = at(i);
|
||||
if (temp->getId() == id) {
|
||||
if (remove)
|
||||
removeAt(i);
|
||||
if (position)
|
||||
*position = i;
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class CardList::compareFunctor
|
||||
{
|
||||
private:
|
||||
int flags;
|
||||
|
||||
public:
|
||||
explicit compareFunctor(int _flags) : flags(_flags)
|
||||
{
|
||||
}
|
||||
inline bool operator()(CardItem *a, CardItem *b) const
|
||||
{
|
||||
if (flags & SortByType) {
|
||||
QString t1 = a->getInfo() ? a->getInfo()->getMainCardType() : QString();
|
||||
QString t2 = b->getInfo() ? b->getInfo()->getMainCardType() : QString();
|
||||
if ((t1 == t2) && (flags & SortByName))
|
||||
return a->getName() < b->getName();
|
||||
return t1 < t2;
|
||||
} else
|
||||
return a->getName() < b->getName();
|
||||
}
|
||||
};
|
||||
|
||||
void CardList::sort(int flags)
|
||||
{
|
||||
compareFunctor cf(flags);
|
||||
std::sort(begin(), end(), cf);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef CARDLIST_H
|
||||
#define CARDLIST_H
|
||||
|
||||
#include <QList>
|
||||
|
||||
class CardItem;
|
||||
|
||||
class CardList : public QList<CardItem *>
|
||||
{
|
||||
private:
|
||||
class compareFunctor;
|
||||
|
||||
protected:
|
||||
bool contentsKnown;
|
||||
|
||||
public:
|
||||
enum SortFlags
|
||||
{
|
||||
SortByName = 1,
|
||||
SortByType = 2
|
||||
};
|
||||
CardList(bool _contentsKnown);
|
||||
CardItem *findCard(const int id, const bool remove, int *position = NULL);
|
||||
bool getContentsKnown() const
|
||||
{
|
||||
return contentsKnown;
|
||||
}
|
||||
void sort(int flags = SortByName);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "abstractclient.h"
|
||||
#include "abstract_client.h"
|
||||
|
||||
#include "client_metatypes.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "featureset.h"
|
||||
#include "get_pb_extension.h"
|
||||
#include "pb/commands.pb.h"
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "pb/event_user_left.pb.h"
|
||||
#include "pb/event_user_message.pb.h"
|
||||
#include "pb/server_message.pb.h"
|
||||
#include "pending_command.h"
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
@@ -53,7 +52,7 @@ AbstractClient::AbstractClient(QObject *parent)
|
||||
FeatureSet features;
|
||||
features.initalizeFeatureList(clientFeatures);
|
||||
|
||||
connect(this, SIGNAL(sigQueuePendingCommand(PendingCommand *)), this, SLOT(queuePendingCommand(PendingCommand *)));
|
||||
connect(this, &AbstractClient::sigQueuePendingCommand, this, &AbstractClient::queuePendingCommand);
|
||||
}
|
||||
|
||||
AbstractClient::~AbstractClient()
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "keysignals.h"
|
||||
#include "key_signals.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "gettextwithmax.h"
|
||||
#include "get_text_with_max.h"
|
||||
|
||||
QString getTextWithMax(QWidget *parent,
|
||||
const QString &title,
|
||||
@@ -2,7 +2,7 @@
|
||||
#ifndef GETTEXTWITHMAX_H
|
||||
#define GETTEXTWITHMAX_H
|
||||
|
||||
#include "stringsizes.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "releasechannel.h"
|
||||
#include "release_channel.h"
|
||||
|
||||
#include "version_string.h"
|
||||
|
||||
@@ -38,11 +38,11 @@ void ReleaseChannel::checkForUpdates()
|
||||
QString releaseChannelUrl = getReleaseChannelUrl();
|
||||
qDebug() << "Searching for updates on the channel: " << releaseChannelUrl;
|
||||
response = netMan->get(QNetworkRequest(releaseChannelUrl));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(releaseListFinished()));
|
||||
connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished);
|
||||
}
|
||||
|
||||
// Different release channel checking functions for different operating systems
|
||||
#if defined(Q_OS_OSX)
|
||||
#if defined(Q_OS_MACOS)
|
||||
bool ReleaseChannel::downloadMatchesCurrentOS(const QString &fileName)
|
||||
{
|
||||
static QRegularExpression version_regex("macOS-(\\d+)\\.(\\d+)");
|
||||
@@ -158,7 +158,7 @@ void StableReleaseChannel::releaseListFinished()
|
||||
QString url = QString(STABLETAG_URL) + tagName;
|
||||
qDebug() << "Searching for commit hash corresponding to stable channel tag: " << tagName;
|
||||
response = netMan->get(QNetworkRequest(url));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(tagListFinished()));
|
||||
connect(response, &QNetworkReply::finished, this, &StableReleaseChannel::tagListFinished);
|
||||
}
|
||||
|
||||
void StableReleaseChannel::tagListFinished()
|
||||
@@ -260,7 +260,7 @@ void BetaReleaseChannel::releaseListFinished()
|
||||
|
||||
qDebug() << "Searching for a corresponding file on the beta channel: " << betaBuildDownloadUrl;
|
||||
response = netMan->get(QNetworkRequest(betaBuildDownloadUrl));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(fileListFinished()));
|
||||
connect(response, &QNetworkReply::finished, this, &BetaReleaseChannel::fileListFinished);
|
||||
}
|
||||
|
||||
void BetaReleaseChannel::fileListFinished()
|
||||
193
cockatrice/src/client/network/replay_timeline_widget.cpp
Normal file
193
cockatrice/src/client/network/replay_timeline_widget.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "replay_timeline_widget.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPalette>
|
||||
#include <QTimer>
|
||||
|
||||
ReplayTimelineWidget::ReplayTimelineWidget(QWidget *parent)
|
||||
: QWidget(parent), maxBinValue(1), maxTime(1), timeScaleFactor(1.0), currentVisualTime(0), currentProcessedTime(0),
|
||||
currentEvent(0)
|
||||
{
|
||||
replayTimer = new QTimer(this);
|
||||
connect(replayTimer, &QTimer::timeout, this, &ReplayTimelineWidget::replayTimerTimeout);
|
||||
|
||||
rewindBufferingTimer = new QTimer(this);
|
||||
rewindBufferingTimer->setSingleShot(true);
|
||||
connect(rewindBufferingTimer, &QTimer::timeout, this, &ReplayTimelineWidget::processRewind);
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::setTimeline(const QList<int> &_replayTimeline)
|
||||
{
|
||||
replayTimeline = _replayTimeline;
|
||||
histogram.clear();
|
||||
int binEndTime = BIN_LENGTH - 1;
|
||||
int binValue = 0;
|
||||
for (int i : replayTimeline) {
|
||||
if (i > binEndTime) {
|
||||
histogram.append(binValue);
|
||||
if (binValue > maxBinValue)
|
||||
maxBinValue = binValue;
|
||||
while (i > binEndTime + BIN_LENGTH) {
|
||||
histogram.append(0);
|
||||
binEndTime += BIN_LENGTH;
|
||||
}
|
||||
binValue = 1;
|
||||
binEndTime += BIN_LENGTH;
|
||||
} else
|
||||
++binValue;
|
||||
}
|
||||
histogram.append(binValue);
|
||||
if (!replayTimeline.isEmpty())
|
||||
maxTime = replayTimeline.last();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::paintEvent(QPaintEvent * /* event */)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||
|
||||
qreal binWidth = (qreal)width() / histogram.size();
|
||||
QPainterPath path;
|
||||
path.moveTo(0, height() - 1);
|
||||
for (int i = 0; i < histogram.size(); ++i)
|
||||
path.lineTo(qRound(i * binWidth), (height() - 1) * (1.0 - (qreal)histogram[i] / maxBinValue));
|
||||
path.lineTo(width() - 1, height() - 1);
|
||||
path.lineTo(0, height() - 1);
|
||||
painter.fillPath(path, Qt::black);
|
||||
|
||||
const QColor barColor = QColor::fromHsv(120, 255, 255, 100);
|
||||
quint64 w = (quint64)(width() - 1) * (quint64)currentVisualTime / maxTime;
|
||||
painter.fillRect(0, 0, static_cast<int>(w), height() - 1, barColor);
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
int newTime = static_cast<int>((qint64)maxTime * (qint64)event->position().x() / width());
|
||||
#else
|
||||
int newTime = static_cast<int>((qint64)maxTime * (qint64)event->x() / width());
|
||||
#endif
|
||||
// don't buffer rewinds from clicks, since clicks usually don't happen fast enough to require buffering
|
||||
skipToTime(newTime, false);
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::skipToTime(int newTime, bool doRewindBuffering)
|
||||
{
|
||||
// check boundary conditions
|
||||
if (newTime < 0) {
|
||||
newTime = 0;
|
||||
}
|
||||
if (newTime > maxTime) {
|
||||
newTime = maxTime;
|
||||
}
|
||||
|
||||
newTime -= newTime % TIMER_INTERVAL_MS; // Time should always be a multiple of the interval
|
||||
|
||||
const bool isBackwardsSkip = newTime < currentProcessedTime;
|
||||
currentVisualTime = newTime;
|
||||
|
||||
if (isBackwardsSkip) {
|
||||
handleBackwardsSkip(doRewindBuffering);
|
||||
} else {
|
||||
processNewEvents(FORWARD_SKIP);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// @param doRewindBuffering When true, if multiple backward skips are made in quick succession, only a single rewind
|
||||
/// is processed at the end. When false, the backwards skip will always cause an immediate rewind
|
||||
void ReplayTimelineWidget::handleBackwardsSkip(bool doRewindBuffering)
|
||||
{
|
||||
if (doRewindBuffering) {
|
||||
// We use a one-shot timer to implement the rewind buffering.
|
||||
// The rewind only happens once the timer runs out.
|
||||
// If another backwards skip happens, the timer will just get reset instead of rewinding.
|
||||
rewindBufferingTimer->stop();
|
||||
rewindBufferingTimer->start(SettingsCache::instance().getRewindBufferingMs());
|
||||
} else {
|
||||
// otherwise, process the rewind immediately
|
||||
processRewind();
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::processRewind()
|
||||
{
|
||||
// stop any queued-up rewinds
|
||||
rewindBufferingTimer->stop();
|
||||
|
||||
// process the rewind
|
||||
currentEvent = 0;
|
||||
emit rewound();
|
||||
processNewEvents(BACKWARD_SKIP);
|
||||
}
|
||||
|
||||
QSize ReplayTimelineWidget::sizeHint() const
|
||||
{
|
||||
return {-1, 50};
|
||||
}
|
||||
|
||||
QSize ReplayTimelineWidget::minimumSizeHint() const
|
||||
{
|
||||
return {400, 50};
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::replayTimerTimeout()
|
||||
{
|
||||
currentVisualTime += TIMER_INTERVAL_MS;
|
||||
|
||||
processNewEvents(NORMAL_PLAYBACK);
|
||||
|
||||
if (!(currentVisualTime % 1000))
|
||||
update();
|
||||
}
|
||||
|
||||
/// Processes all unprocessed events up to the current time.
|
||||
void ReplayTimelineWidget::processNewEvents(PlaybackMode playbackMode)
|
||||
{
|
||||
currentProcessedTime = currentVisualTime;
|
||||
|
||||
while ((currentEvent < replayTimeline.size()) && (replayTimeline[currentEvent] < currentProcessedTime)) {
|
||||
Player::EventProcessingOptions options;
|
||||
|
||||
// backwards skip => always skip reveal windows
|
||||
// forwards skip => skip reveal windows that don't happen within a big skip of the target
|
||||
if (playbackMode == BACKWARD_SKIP || currentProcessedTime - replayTimeline[currentEvent] > BIG_SKIP_MS)
|
||||
options |= Player::EventProcessingOption::SKIP_REVEAL_WINDOW;
|
||||
|
||||
// backwards skip => always skip tap animation
|
||||
if (playbackMode == BACKWARD_SKIP)
|
||||
options |= Player::EventProcessingOption::SKIP_TAP_ANIMATION;
|
||||
|
||||
emit processNextEvent(options);
|
||||
++currentEvent;
|
||||
}
|
||||
if (currentEvent == replayTimeline.size()) {
|
||||
emit replayFinished();
|
||||
replayTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::setTimeScaleFactor(qreal _timeScaleFactor)
|
||||
{
|
||||
timeScaleFactor = _timeScaleFactor;
|
||||
replayTimer->setInterval(static_cast<int>(TIMER_INTERVAL_MS / timeScaleFactor));
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::startReplay()
|
||||
{
|
||||
replayTimer->start(static_cast<int>(TIMER_INTERVAL_MS / timeScaleFactor));
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::stopReplay()
|
||||
{
|
||||
replayTimer->stop();
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::skipByAmount(int amount)
|
||||
{
|
||||
skipToTime(currentVisualTime + amount, amount < 0);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef REPLAY_TIMELINE_WIDGET
|
||||
#define REPLAY_TIMELINE_WIDGET
|
||||
|
||||
#include "../../game/player/player.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
@@ -12,23 +14,44 @@ class ReplayTimelineWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void processNextEvent();
|
||||
void processNextEvent(Player::EventProcessingOptions options);
|
||||
void replayFinished();
|
||||
void rewound();
|
||||
|
||||
private:
|
||||
enum PlaybackMode
|
||||
{
|
||||
NORMAL_PLAYBACK,
|
||||
FORWARD_SKIP,
|
||||
BACKWARD_SKIP
|
||||
};
|
||||
|
||||
static constexpr int TIMER_INTERVAL_MS = 200;
|
||||
static constexpr int BIN_LENGTH = 5000;
|
||||
|
||||
QTimer *replayTimer;
|
||||
QTimer *rewindBufferingTimer;
|
||||
QList<int> replayTimeline;
|
||||
QList<int> histogram;
|
||||
static const int binLength;
|
||||
int maxBinValue, maxTime;
|
||||
qreal timeScaleFactor;
|
||||
int currentTime;
|
||||
int currentVisualTime; // time currently displayed by the timeline
|
||||
int currentProcessedTime; // time that events are currently processed up to. Could differ from visual time due to
|
||||
// rewind buffering
|
||||
int currentEvent;
|
||||
|
||||
void skipToTime(int newTime, bool doRewindBuffering);
|
||||
void handleBackwardsSkip(bool doRewindBuffering);
|
||||
void processRewind();
|
||||
void processNewEvents(PlaybackMode playbackMode);
|
||||
private slots:
|
||||
void replayTimerTimeout();
|
||||
|
||||
public:
|
||||
static constexpr int SMALL_SKIP_MS = 1000;
|
||||
static constexpr int BIG_SKIP_MS = 10000;
|
||||
static constexpr qreal FAST_FORWARD_SCALE_FACTOR = 10.0;
|
||||
|
||||
explicit ReplayTimelineWidget(QWidget *parent = nullptr);
|
||||
void setTimeline(const QList<int> &_replayTimeline);
|
||||
QSize sizeHint() const override;
|
||||
@@ -41,6 +64,7 @@ public:
|
||||
public slots:
|
||||
void startReplay();
|
||||
void stopReplay();
|
||||
void skipByAmount(int amount); // use a negative amount to skip backwards
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "setsmodel.h"
|
||||
#include "sets_model.h"
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
@@ -195,6 +195,13 @@ void SetsModel::swapRows(int oldRow, int newRow)
|
||||
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
|
||||
}
|
||||
|
||||
void SetsModel::restoreOriginalOrder()
|
||||
{
|
||||
int numRows = rowCount();
|
||||
sets.defaultSort();
|
||||
emit dataChanged(index(0, 0), index(numRows - 1, columnCount() - 1));
|
||||
}
|
||||
|
||||
void SetsModel::sort(int column, Qt::SortOrder order)
|
||||
{
|
||||
QMultiMap<QString, CardSetPtr> setMap;
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef SETSMODEL_H
|
||||
#define SETSMODEL_H
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "../../game/cards/card_database.h"
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QMimeData>
|
||||
@@ -49,7 +49,8 @@ public:
|
||||
LongNameCol,
|
||||
ShortNameCol,
|
||||
SetTypeCol,
|
||||
ReleaseDateCol
|
||||
ReleaseDateCol,
|
||||
PriorityCol
|
||||
};
|
||||
enum Role
|
||||
{
|
||||
@@ -80,6 +81,7 @@ public:
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
||||
void save(CardDatabase *db);
|
||||
void restore(CardDatabase *db);
|
||||
void restoreOriginalOrder();
|
||||
};
|
||||
|
||||
class SetsDisplayModel : public QSortFilterProxyModel
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "spoilerbackgroundupdater.h"
|
||||
#include "spoiler_background_updater.h"
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "main.h"
|
||||
#include "settingscache.h"
|
||||
#include "window_main.h"
|
||||
#include "../../game/cards/card_database.h"
|
||||
#include "../../game/cards/card_database_manager.h"
|
||||
#include "../../main.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../ui/window_main.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCryptographicHash>
|
||||
@@ -44,10 +45,10 @@ void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
|
||||
|
||||
if (saveResults) {
|
||||
// This will write out to the file (used for spoiler.xml)
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile()));
|
||||
connect(reply, &QNetworkReply::finished, this, &SpoilerBackgroundUpdater::actDownloadFinishedSpoilersFile);
|
||||
} else {
|
||||
// This will check the status (used to see if we're in spoiler season or not)
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(actCheckIfSpoilerSeasonEnabled()));
|
||||
connect(reply, &QNetworkReply::finished, this, &SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
|
||||
|
||||
// Data written, so reload the card database
|
||||
qDebug() << "Spoiler Service Data Written";
|
||||
const auto reloadOk = QtConcurrent::run([] { db->loadCardDatabases(); });
|
||||
const auto reloadOk = QtConcurrent::run([] { CardDatabaseManager::getInstance()->loadCardDatabases(); });
|
||||
|
||||
// If the user has notifications enabled, let them know
|
||||
// when the database was last updated
|
||||
@@ -172,7 +173,11 @@ bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
|
||||
timeStamp.chop(6); // Remove " (UTC)"
|
||||
|
||||
auto utcTime = QLocale().toDateTime(timeStamp, "ddd, MMM dd yyyy, hh:mm:ss");
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||
utcTime.setTimeZone(QTimeZone::UTC);
|
||||
#else
|
||||
utcTime.setTimeSpec(Qt::UTC);
|
||||
#endif
|
||||
|
||||
QString localTime = utcTime.toLocalTime().toString("MMM d, hh:mm");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "soundengine.h"
|
||||
#include "sound_engine.h"
|
||||
|
||||
#include "settingscache.h"
|
||||
#include "../settings/cache_settings.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QMediaPlayer>
|
||||
@@ -15,8 +15,8 @@
|
||||
SoundEngine::SoundEngine(QObject *parent) : QObject(parent), player(nullptr)
|
||||
{
|
||||
ensureThemeDirectoryExists();
|
||||
connect(&SettingsCache::instance(), SIGNAL(soundThemeChanged()), this, SLOT(themeChangedSlot()));
|
||||
connect(&SettingsCache::instance(), SIGNAL(soundEnabledChanged()), this, SLOT(soundEnabledChanged()));
|
||||
connect(&SettingsCache::instance(), &SettingsCache::soundThemeChanged, this, &SoundEngine::themeChangedSlot);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::soundEnabledChanged, this, &SoundEngine::soundEnabledChanged);
|
||||
|
||||
soundEnabledChanged();
|
||||
themeChangedSlot();
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "tab.h"
|
||||
|
||||
#include "cardinfowidget.h"
|
||||
#include "../ui/widgets/cards/card_info_display_widget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
@@ -18,7 +18,7 @@ void Tab::showCardInfoPopup(const QPoint &pos, const QString &cardName)
|
||||
infoPopup->deleteLater();
|
||||
}
|
||||
currentCardName = cardName;
|
||||
infoPopup = new CardInfoWidget(
|
||||
infoPopup = new CardInfoDisplayWidget(
|
||||
cardName, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint);
|
||||
infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
class QMenu;
|
||||
class TabSupervisor;
|
||||
class CardInfoWidget;
|
||||
class CardInfoDisplayWidget;
|
||||
|
||||
class Tab : public QMainWindow
|
||||
{
|
||||
@@ -27,7 +27,7 @@ protected slots:
|
||||
private:
|
||||
QString currentCardName;
|
||||
bool contentsChanged;
|
||||
CardInfoWidget *infoPopup;
|
||||
CardInfoDisplayWidget *infoPopup;
|
||||
QList<QMenu *> tabMenus;
|
||||
|
||||
public:
|
||||
@@ -1,18 +1,18 @@
|
||||
#include "tab_account.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "customlineedit.h"
|
||||
#include "../../deck/custom_line_edit.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../server/user/user_info_box.h"
|
||||
#include "../../server/user/user_list.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "../sound_engine.h"
|
||||
#include "pb/event_add_to_list.pb.h"
|
||||
#include "pb/event_remove_from_list.pb.h"
|
||||
#include "pb/event_user_joined.pb.h"
|
||||
#include "pb/event_user_left.pb.h"
|
||||
#include "pb/response_list_users.pb.h"
|
||||
#include "pb/session_commands.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "soundengine.h"
|
||||
#include "stringsizes.h"
|
||||
#include "userinfobox.h"
|
||||
#include "userlist.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
@@ -30,29 +30,22 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor,
|
||||
userInfoBox = new UserInfoBox(client, true);
|
||||
userInfoBox->updateInfo(userInfo);
|
||||
|
||||
connect(allUsersList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(buddyList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(ignoreList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(allUsersList, &UserList::openMessageDialog, this, &TabUserLists::openMessageDialog);
|
||||
connect(buddyList, &UserList::openMessageDialog, this, &TabUserLists::openMessageDialog);
|
||||
connect(ignoreList, &UserList::openMessageDialog, this, &TabUserLists::openMessageDialog);
|
||||
|
||||
connect(client, SIGNAL(userJoinedEventReceived(const Event_UserJoined &)), this,
|
||||
SLOT(processUserJoinedEvent(const Event_UserJoined &)));
|
||||
connect(client, SIGNAL(userLeftEventReceived(const Event_UserLeft &)), this,
|
||||
SLOT(processUserLeftEvent(const Event_UserLeft &)));
|
||||
connect(client, SIGNAL(buddyListReceived(const QList<ServerInfo_User> &)), this,
|
||||
SLOT(buddyListReceived(const QList<ServerInfo_User> &)));
|
||||
connect(client, SIGNAL(ignoreListReceived(const QList<ServerInfo_User> &)), this,
|
||||
SLOT(ignoreListReceived(const QList<ServerInfo_User> &)));
|
||||
connect(client, SIGNAL(addToListEventReceived(const Event_AddToList &)), this,
|
||||
SLOT(processAddToListEvent(const Event_AddToList &)));
|
||||
connect(client, SIGNAL(removeFromListEventReceived(const Event_RemoveFromList &)), this,
|
||||
SLOT(processRemoveFromListEvent(const Event_RemoveFromList &)));
|
||||
connect(client, &AbstractClient::userJoinedEventReceived, this, &TabUserLists::processUserJoinedEvent);
|
||||
connect(client, &AbstractClient::userLeftEventReceived, this, &TabUserLists::processUserLeftEvent);
|
||||
connect(client, &AbstractClient::buddyListReceived, this, &TabUserLists::buddyListReceived);
|
||||
connect(client, &AbstractClient::ignoreListReceived, this, &TabUserLists::ignoreListReceived);
|
||||
connect(client, &AbstractClient::addToListEventReceived, this, &TabUserLists::processAddToListEvent);
|
||||
connect(client, &AbstractClient::removeFromListEventReceived, this, &TabUserLists::processRemoveFromListEvent);
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(Command_ListUsers());
|
||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
|
||||
SLOT(processListUsersResponse(const Response &)));
|
||||
connect(pend,
|
||||
static_cast<void (PendingCommand::*)(const Response &, const CommandContainer &, const QVariant &)>(
|
||||
&PendingCommand::finished),
|
||||
this, &TabUserLists::processListUsersResponse);
|
||||
client->sendCommand(pend);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
@@ -63,9 +56,9 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor,
|
||||
addBuddyEdit = new LineEditUnfocusable;
|
||||
addBuddyEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
addBuddyEdit->setPlaceholderText(tr("Add to Buddy List"));
|
||||
connect(addBuddyEdit, SIGNAL(returnPressed()), this, SLOT(addToBuddyList()));
|
||||
connect(addBuddyEdit, &LineEditUnfocusable::returnPressed, this, &TabUserLists::addToBuddyList);
|
||||
QPushButton *addBuddyButton = new QPushButton("Add");
|
||||
connect(addBuddyButton, SIGNAL(clicked()), this, SLOT(addToBuddyList()));
|
||||
connect(addBuddyButton, &QPushButton::clicked, this, &TabUserLists::addToBuddyList);
|
||||
addToBuddyList->addWidget(addBuddyEdit);
|
||||
addToBuddyList->addWidget(addBuddyButton);
|
||||
|
||||
@@ -73,9 +66,9 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor,
|
||||
addIgnoreEdit = new LineEditUnfocusable;
|
||||
addIgnoreEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
addIgnoreEdit->setPlaceholderText(tr("Add to Ignore List"));
|
||||
connect(addIgnoreEdit, SIGNAL(returnPressed()), this, SLOT(addToIgnoreList()));
|
||||
connect(addIgnoreEdit, &LineEditUnfocusable::returnPressed, this, &TabUserLists::addToIgnoreList);
|
||||
QPushButton *addIgnoreButton = new QPushButton("Add");
|
||||
connect(addIgnoreButton, SIGNAL(clicked()), this, SLOT(addToIgnoreList()));
|
||||
connect(addIgnoreButton, &QPushButton::clicked, this, &TabUserLists::addToIgnoreList);
|
||||
addToIgnoreList->addWidget(addIgnoreEdit);
|
||||
addToIgnoreList->addWidget(addIgnoreButton);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "tab_admin.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "pb/admin_commands.pb.h"
|
||||
#include "stringsizes.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
@@ -29,8 +29,8 @@ ShutdownDialog::ShutdownDialog(QWidget *parent) : QDialog(parent)
|
||||
minutesEdit->setMaximum(999);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &ShutdownDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &ShutdownDialog::reject);
|
||||
|
||||
QGridLayout *mainLayout = new QGridLayout;
|
||||
mainLayout->addWidget(reasonLabel, 0, 0);
|
||||
@@ -57,11 +57,11 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
|
||||
: Tab(_tabSupervisor, parent), locked(true), client(_client), fullAdmin(_fullAdmin)
|
||||
{
|
||||
updateServerMessageButton = new QPushButton;
|
||||
connect(updateServerMessageButton, SIGNAL(clicked()), this, SLOT(actUpdateServerMessage()));
|
||||
connect(updateServerMessageButton, &QPushButton::clicked, this, &TabAdmin::actUpdateServerMessage);
|
||||
shutdownServerButton = new QPushButton;
|
||||
connect(shutdownServerButton, SIGNAL(clicked()), this, SLOT(actShutdownServer()));
|
||||
connect(shutdownServerButton, &QPushButton::clicked, this, &TabAdmin::actShutdownServer);
|
||||
reloadConfigButton = new QPushButton;
|
||||
connect(reloadConfigButton, SIGNAL(clicked()), this, SLOT(actReloadConfig()));
|
||||
connect(reloadConfigButton, &QPushButton::clicked, this, &TabAdmin::actReloadConfig);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(updateServerMessageButton);
|
||||
@@ -74,10 +74,10 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
|
||||
adminGroupBox->setEnabled(false);
|
||||
|
||||
unlockButton = new QPushButton;
|
||||
connect(unlockButton, SIGNAL(clicked()), this, SLOT(actUnlock()));
|
||||
connect(unlockButton, &QPushButton::clicked, this, &TabAdmin::actUnlock);
|
||||
lockButton = new QPushButton;
|
||||
lockButton->setEnabled(false);
|
||||
connect(lockButton, SIGNAL(clicked()), this, SLOT(actLock()));
|
||||
connect(lockButton, &QPushButton::clicked, this, &TabAdmin::actLock);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(adminGroupBox);
|
||||
@@ -1,23 +1,25 @@
|
||||
#include "tab_deck_editor.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "carddatabasemodel.h"
|
||||
#include "cardframe.h"
|
||||
#include "decklistmodel.h"
|
||||
#include "deckstats_interface.h"
|
||||
#include "dlg_load_deck_from_clipboard.h"
|
||||
#include "filterbuilder.h"
|
||||
#include "filtertreemodel.h"
|
||||
#include "main.h"
|
||||
#include "../../client/game_logic/abstract_client.h"
|
||||
#include "../../client/tapped_out_interface.h"
|
||||
#include "../../client/ui/widgets/cards/card_info_frame_widget.h"
|
||||
#include "../../deck/deck_list_model.h"
|
||||
#include "../../deck/deck_stats_interface.h"
|
||||
#include "../../dialogs/dlg_load_deck_from_clipboard.h"
|
||||
#include "../../game/cards/card_database_manager.h"
|
||||
#include "../../game/cards/card_database_model.h"
|
||||
#include "../../game/filters/filter_builder.h"
|
||||
#include "../../game/filters/filter_tree_model.h"
|
||||
#include "../../main.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../ui/picture_loader.h"
|
||||
#include "../ui/pixel_map_generator.h"
|
||||
#include "../ui/widgets/printing_selector/printing_selector.h"
|
||||
#include "pb/command_deck_upload.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "pictureloader.h"
|
||||
#include "pixmapgenerator.h"
|
||||
#include "settingscache.h"
|
||||
#include "stringsizes.h"
|
||||
#include "tab_supervisor.h"
|
||||
#include "tappedout_interface.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
@@ -48,13 +50,6 @@
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
void SearchLineEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (treeView && ((event->key() == Qt::Key_Up) || (event->key() == Qt::Key_Down)))
|
||||
QCoreApplication::sendEvent(treeView, event);
|
||||
LineEditUnfocusable::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void TabDeckEditor::createDeckDock()
|
||||
{
|
||||
deckModel = new DeckListModel(this);
|
||||
@@ -68,9 +63,13 @@ void TabDeckEditor::createDeckDock()
|
||||
deckView->sortByColumn(1, Qt::AscendingOrder);
|
||||
deckView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
deckView->installEventFilter(&deckViewKeySignals);
|
||||
deckView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this,
|
||||
SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &)));
|
||||
connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this,
|
||||
SLOT(updatePrintingSelectorDeckView(const QModelIndex &, const QModelIndex &)));
|
||||
connect(deckView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actSwapCard()));
|
||||
connect(deckView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(decklistCustomMenu(QPoint)));
|
||||
connect(&deckViewKeySignals, SIGNAL(onShiftS()), this, SLOT(actSwapCard()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onEnter()), this, SLOT(actIncrement()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actIncrement()));
|
||||
@@ -162,7 +161,6 @@ void TabDeckEditor::createDeckDock()
|
||||
deckDock = new QDockWidget(this);
|
||||
deckDock->setObjectName("deckDock");
|
||||
|
||||
deckDock->setMinimumSize(QSize(200, 41));
|
||||
deckDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
deckDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
|
||||
QDockWidget::DockWidgetMovable);
|
||||
@@ -177,7 +175,7 @@ void TabDeckEditor::createDeckDock()
|
||||
|
||||
void TabDeckEditor::createCardInfoDock()
|
||||
{
|
||||
cardInfo = new CardFrame();
|
||||
cardInfo = new CardInfoFrameWidget();
|
||||
cardInfo->setObjectName("cardInfo");
|
||||
auto *cardInfoFrame = new QVBoxLayout;
|
||||
cardInfoFrame->setObjectName("cardInfoFrame");
|
||||
@@ -186,7 +184,6 @@ void TabDeckEditor::createCardInfoDock()
|
||||
cardInfoDock = new QDockWidget(this);
|
||||
cardInfoDock->setObjectName("cardInfoDock");
|
||||
|
||||
cardInfoDock->setMinimumSize(QSize(200, 41));
|
||||
cardInfoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
cardInfoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
|
||||
QDockWidget::DockWidgetMovable);
|
||||
@@ -261,6 +258,32 @@ void TabDeckEditor::createFiltersDock()
|
||||
connect(filterDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
|
||||
}
|
||||
|
||||
void TabDeckEditor::createPrintingSelectorDock()
|
||||
{
|
||||
printingSelector = new PrintingSelector(this, this, deckModel, deckView);
|
||||
printingSelector->setObjectName("printingSelector");
|
||||
auto *printingSelectorFrame = new QVBoxLayout;
|
||||
printingSelectorFrame->setObjectName("printingSelectorFrame");
|
||||
printingSelectorFrame->addWidget(printingSelector);
|
||||
|
||||
printingSelectorDock = new QDockWidget(this);
|
||||
printingSelectorDock->setObjectName("printingSelectorDock");
|
||||
|
||||
printingSelectorDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
printingSelectorDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
|
||||
QDockWidget::DockWidgetMovable);
|
||||
auto *printingSelectorDockContents = new QWidget();
|
||||
printingSelectorDockContents->setObjectName("printingSelectorDockContents");
|
||||
printingSelectorDockContents->setLayout(printingSelectorFrame);
|
||||
printingSelectorDock->setWidget(printingSelectorDockContents);
|
||||
|
||||
printingSelectorDock->installEventFilter(this);
|
||||
connect(printingSelectorDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
|
||||
|
||||
addDockWidget(Qt::RightDockWidgetArea, printingSelectorDock);
|
||||
printingSelectorDock->setFloating(false);
|
||||
}
|
||||
|
||||
void TabDeckEditor::createMenus()
|
||||
{
|
||||
aNewDeck = new QAction(QString(), this);
|
||||
@@ -339,6 +362,7 @@ void TabDeckEditor::createMenus()
|
||||
cardInfoDockMenu = viewMenu->addMenu(QString());
|
||||
deckDockMenu = viewMenu->addMenu(QString());
|
||||
filterDockMenu = viewMenu->addMenu(QString());
|
||||
printingSelectorDockMenu = viewMenu->addMenu(QString());
|
||||
|
||||
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
|
||||
aCardInfoDockVisible->setCheckable(true);
|
||||
@@ -361,6 +385,13 @@ void TabDeckEditor::createMenus()
|
||||
aFilterDockFloating->setCheckable(true);
|
||||
connect(aFilterDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
|
||||
|
||||
aPrintingSelectorDockVisible = printingSelectorDockMenu->addAction(QString());
|
||||
aPrintingSelectorDockVisible->setCheckable(true);
|
||||
connect(aPrintingSelectorDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
|
||||
aPrintingSelectorDockFloating = printingSelectorDockMenu->addAction(QString());
|
||||
aPrintingSelectorDockFloating->setCheckable(true);
|
||||
connect(aPrintingSelectorDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
aResetLayout = viewMenu->addAction(QString());
|
||||
@@ -376,7 +407,7 @@ void TabDeckEditor::createCentralFrame()
|
||||
{
|
||||
searchEdit = new SearchLineEdit;
|
||||
searchEdit->setObjectName("searchEdit");
|
||||
searchEdit->setPlaceholderText(tr("Search by card name"));
|
||||
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
|
||||
searchEdit->setClearButtonEnabled(true);
|
||||
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||
auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
||||
@@ -397,7 +428,7 @@ void TabDeckEditor::createCentralFrame()
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlC()), this, SLOT(copyDatabaseCellContents()));
|
||||
connect(help, &QAction::triggered, this, &TabDeckEditor::showSearchSyntaxHelp);
|
||||
|
||||
databaseModel = new CardDatabaseModel(db, true, this);
|
||||
databaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), true, this);
|
||||
databaseModel->setObjectName("databaseModel");
|
||||
databaseDisplayModel = new CardDatabaseDisplayModel(this);
|
||||
databaseDisplayModel->setSourceModel(databaseModel);
|
||||
@@ -416,6 +447,8 @@ void TabDeckEditor::createCentralFrame()
|
||||
connect(databaseView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(databaseCustomMenu(QPoint)));
|
||||
connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this,
|
||||
SLOT(updateCardInfoLeft(const QModelIndex &, const QModelIndex &)));
|
||||
connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this,
|
||||
SLOT(updatePrintingSelectorDatabase(const QModelIndex &, const QModelIndex &)));
|
||||
connect(databaseView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actAddCard()));
|
||||
|
||||
QByteArray dbHeaderState = SettingsCache::instance().layouts().getDeckEditorDbHeaderState();
|
||||
@@ -455,6 +488,7 @@ void TabDeckEditor::createCentralFrame()
|
||||
centralWidget = new QWidget(this);
|
||||
centralWidget->setObjectName("centralWidget");
|
||||
centralWidget->setLayout(centralFrame);
|
||||
centralWidget->setMaximumSize(900, 5000);
|
||||
setCentralWidget(centralWidget);
|
||||
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
|
||||
}
|
||||
@@ -465,11 +499,14 @@ void TabDeckEditor::databaseCustomMenu(QPoint point)
|
||||
const CardInfoPtr info = currentCardInfo();
|
||||
|
||||
// add to deck and sideboard options
|
||||
QAction *addToDeck, *addToSideboard;
|
||||
QAction *addToDeck, *addToSideboard, *selectPrinting;
|
||||
addToDeck = menu.addAction(tr("Add to Deck"));
|
||||
addToSideboard = menu.addAction(tr("Add to Sideboard"));
|
||||
selectPrinting = menu.addAction(tr("Select Printing"));
|
||||
|
||||
connect(addToDeck, SIGNAL(triggered()), this, SLOT(actAddCard()));
|
||||
connect(addToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard()));
|
||||
connect(selectPrinting, &QAction::triggered, this, [this, info] { this->showPrintingSelector(); });
|
||||
|
||||
// filling out the related cards submenu
|
||||
auto *relatedMenu = new QMenu(tr("Show Related cards"));
|
||||
@@ -488,36 +525,58 @@ void TabDeckEditor::databaseCustomMenu(QPoint point)
|
||||
menu.exec(databaseView->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void TabDeckEditor::decklistCustomMenu(QPoint point)
|
||||
{
|
||||
QMenu menu;
|
||||
const CardInfoPtr info = cardInfo->getInfo();
|
||||
|
||||
QAction *selectPrinting = menu.addAction(tr("Select Printing"));
|
||||
|
||||
connect(selectPrinting, &QAction::triggered, this, &TabDeckEditor::showPrintingSelector);
|
||||
|
||||
menu.exec(deckView->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void TabDeckEditor::showPrintingSelector()
|
||||
{
|
||||
printingSelector->setCard(cardInfo->getInfo(), DECK_ZONE_MAIN);
|
||||
printingSelector->updateDisplay();
|
||||
aPrintingSelectorDockVisible->setChecked(true);
|
||||
printingSelectorDock->setVisible(true);
|
||||
}
|
||||
|
||||
void TabDeckEditor::restartLayout()
|
||||
{
|
||||
deckDock->setVisible(true);
|
||||
cardInfoDock->setVisible(true);
|
||||
filterDock->setVisible(true);
|
||||
printingSelectorDock->setVisible(false);
|
||||
|
||||
deckDock->setFloating(false);
|
||||
cardInfoDock->setFloating(false);
|
||||
filterDock->setFloating(false);
|
||||
printingSelectorDock->setFloating(false);
|
||||
|
||||
aCardInfoDockVisible->setChecked(true);
|
||||
aDeckDockVisible->setChecked(true);
|
||||
aFilterDockVisible->setChecked(true);
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
|
||||
aCardInfoDockFloating->setChecked(false);
|
||||
aDeckDockFloating->setChecked(false);
|
||||
aFilterDockFloating->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setChecked(false);
|
||||
|
||||
addDockWidget(static_cast<Qt::DockWidgetArea>(2), deckDock);
|
||||
addDockWidget(static_cast<Qt::DockWidgetArea>(2), cardInfoDock);
|
||||
addDockWidget(static_cast<Qt::DockWidgetArea>(2), filterDock);
|
||||
addDockWidget(static_cast<Qt::DockWidgetArea>(2), printingSelectorDock);
|
||||
|
||||
splitDockWidget(cardInfoDock, deckDock, Qt::Horizontal);
|
||||
splitDockWidget(cardInfoDock, printingSelectorDock, Qt::Horizontal);
|
||||
splitDockWidget(printingSelectorDock, deckDock, Qt::Horizontal);
|
||||
splitDockWidget(cardInfoDock, printingSelectorDock, Qt::Horizontal);
|
||||
splitDockWidget(cardInfoDock, filterDock, Qt::Vertical);
|
||||
|
||||
deckDock->setMinimumWidth(360);
|
||||
deckDock->setMaximumWidth(360);
|
||||
|
||||
cardInfoDock->setMinimumSize(250, 360);
|
||||
cardInfoDock->setMaximumSize(250, 360);
|
||||
QTimer::singleShot(100, this, SLOT(freeDocksSize()));
|
||||
}
|
||||
|
||||
@@ -531,6 +590,11 @@ void TabDeckEditor::freeDocksSize()
|
||||
|
||||
filterDock->setMinimumSize(100, 100);
|
||||
filterDock->setMaximumSize(5000, 5000);
|
||||
|
||||
printingSelectorDock->setMinimumSize(525, 100);
|
||||
printingSelectorDock->setMaximumSize(5000, 5000);
|
||||
|
||||
centralWidget->setMaximumSize(900, 5000);
|
||||
}
|
||||
|
||||
void TabDeckEditor::refreshShortcuts()
|
||||
@@ -573,14 +637,17 @@ void TabDeckEditor::loadLayout()
|
||||
aCardInfoDockVisible->setChecked(cardInfoDock->isVisible());
|
||||
aFilterDockVisible->setChecked(filterDock->isVisible());
|
||||
aDeckDockVisible->setChecked(deckDock->isVisible());
|
||||
aPrintingSelectorDockVisible->setChecked(printingSelectorDock->isVisible());
|
||||
|
||||
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
|
||||
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
|
||||
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
|
||||
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
|
||||
|
||||
aCardInfoDockFloating->setChecked(cardInfoDock->isFloating());
|
||||
aFilterDockFloating->setChecked(filterDock->isFloating());
|
||||
aDeckDockFloating->setChecked(deckDock->isFloating());
|
||||
aPrintingSelectorDockFloating->setChecked(printingSelectorDock->isFloating());
|
||||
|
||||
cardInfoDock->setMinimumSize(layouts.getDeckEditorCardSize());
|
||||
cardInfoDock->setMaximumSize(layouts.getDeckEditorCardSize());
|
||||
@@ -591,6 +658,9 @@ void TabDeckEditor::loadLayout()
|
||||
deckDock->setMinimumSize(layouts.getDeckEditorDeckSize());
|
||||
deckDock->setMaximumSize(layouts.getDeckEditorDeckSize());
|
||||
|
||||
printingSelectorDock->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize());
|
||||
printingSelectorDock->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize());
|
||||
|
||||
QTimer::singleShot(100, this, SLOT(freeDocksSize()));
|
||||
}
|
||||
|
||||
@@ -606,6 +676,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
||||
createDeckDock();
|
||||
createCardInfoDock();
|
||||
createFiltersDock();
|
||||
createPrintingSelectorDock();
|
||||
|
||||
this->installEventFilter(this);
|
||||
|
||||
@@ -665,11 +736,13 @@ void TabDeckEditor::retranslateUi()
|
||||
cardInfoDock->setWindowTitle(tr("Card Info"));
|
||||
deckDock->setWindowTitle(tr("Deck"));
|
||||
filterDock->setWindowTitle(tr("Filters"));
|
||||
printingSelectorDock->setWindowTitle(tr("Printing Selector"));
|
||||
|
||||
viewMenu->setTitle(tr("&View"));
|
||||
cardInfoDockMenu->setTitle(tr("Card Info"));
|
||||
deckDockMenu->setTitle(tr("Deck"));
|
||||
filterDockMenu->setTitle(tr("Filters"));
|
||||
printingSelectorDockMenu->setTitle(tr("Printing"));
|
||||
|
||||
aCardInfoDockVisible->setText(tr("Visible"));
|
||||
aCardInfoDockFloating->setText(tr("Floating"));
|
||||
@@ -680,6 +753,9 @@ void TabDeckEditor::retranslateUi()
|
||||
aFilterDockVisible->setText(tr("Visible"));
|
||||
aFilterDockFloating->setText(tr("Floating"));
|
||||
|
||||
aPrintingSelectorDockVisible->setText(tr("Visible"));
|
||||
aPrintingSelectorDockFloating->setText(tr("Floating"));
|
||||
|
||||
aResetLayout->setText(tr("Reset layout"));
|
||||
}
|
||||
|
||||
@@ -705,6 +781,11 @@ void TabDeckEditor::updateComments()
|
||||
setSaveStatus(true);
|
||||
}
|
||||
|
||||
void TabDeckEditor::updateCardInfo(CardInfoPtr _card)
|
||||
{
|
||||
cardInfo->setCard(_card);
|
||||
}
|
||||
|
||||
void TabDeckEditor::updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
cardInfo->setCard(current.sibling(current.row(), 0).data().toString());
|
||||
@@ -714,8 +795,47 @@ void TabDeckEditor::updateCardInfoRight(const QModelIndex ¤t, const QModel
|
||||
{
|
||||
if (!current.isValid())
|
||||
return;
|
||||
if (!current.model()->hasChildren(current.sibling(current.row(), 0)))
|
||||
cardInfo->setCard(current.sibling(current.row(), 1).data().toString());
|
||||
if (!current.model()->hasChildren(current.sibling(current.row(), 0))) {
|
||||
cardInfo->setCard(current.sibling(current.row(), 1).data().toString(),
|
||||
current.sibling(current.row(), 4).data().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::updatePrintingSelectorDatabase(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
const QString cardName = current.sibling(current.row(), 0).data().toString();
|
||||
const QString cardProviderID = CardDatabaseManager::getInstance()->getPreferredPrintingProviderIdForCard(cardName);
|
||||
|
||||
if (!current.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current.model()->hasChildren(current.sibling(current.row(), 0))) {
|
||||
printingSelector->setCard(
|
||||
CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID), DECK_ZONE_MAIN);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::updatePrintingSelectorDeckView(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
const QString cardName = current.sibling(current.row(), 1).data().toString();
|
||||
const QString cardProviderID = current.sibling(current.row(), 4).data().toString();
|
||||
const QModelIndex gparent = current.parent().parent();
|
||||
|
||||
if (!gparent.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString();
|
||||
|
||||
if (!current.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current.model()->hasChildren(current.sibling(current.row(), 0))) {
|
||||
printingSelector->setCard(
|
||||
CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID), zoneName);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::updateSearch(const QString &search)
|
||||
@@ -736,9 +856,7 @@ bool TabDeckEditor::confirmClose()
|
||||
{
|
||||
if (modified) {
|
||||
tabSupervisor->setCurrentWidget(this);
|
||||
QMessageBox::StandardButton ret = QMessageBox::warning(
|
||||
this, tr("Are you sure?"), tr("The decklist has been modified.\nDo you want to save the changes?"),
|
||||
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
||||
int ret = createSaveConfirmationWindow()->exec();
|
||||
if (ret == QMessageBox::Save)
|
||||
return actSaveDeck();
|
||||
else if (ret == QMessageBox::Cancel)
|
||||
@@ -755,8 +873,16 @@ void TabDeckEditor::closeRequest()
|
||||
|
||||
void TabDeckEditor::actNewDeck()
|
||||
{
|
||||
if (!confirmClose())
|
||||
auto deckOpenLocation = confirmOpen(false);
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
deckModel->cleanList();
|
||||
nameEdit->setText(QString());
|
||||
@@ -768,8 +894,11 @@ void TabDeckEditor::actNewDeck()
|
||||
|
||||
void TabDeckEditor::actLoadDeck()
|
||||
{
|
||||
if (!confirmClose())
|
||||
auto deckOpenLocation = confirmOpen();
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFileDialog dialog(this, tr("Load deck"));
|
||||
dialog.setDirectory(SettingsCache::instance().getDeckPath());
|
||||
@@ -782,8 +911,12 @@ void TabDeckEditor::actLoadDeck()
|
||||
|
||||
auto *l = new DeckLoader;
|
||||
if (l->loadFromFile(fileName, fmt)) {
|
||||
setSaveStatus(false);
|
||||
setDeck(l);
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(l);
|
||||
} else {
|
||||
setSaveStatus(false);
|
||||
setDeck(l);
|
||||
}
|
||||
} else
|
||||
delete l;
|
||||
setSaveStatus(true);
|
||||
@@ -855,15 +988,23 @@ bool TabDeckEditor::actSaveDeckAs()
|
||||
|
||||
void TabDeckEditor::actLoadDeckFromClipboard()
|
||||
{
|
||||
if (!confirmClose())
|
||||
auto deckOpenLocation = confirmOpen();
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
DlgLoadDeckFromClipboard dlg(this);
|
||||
if (!dlg.exec())
|
||||
return;
|
||||
|
||||
setDeck(dlg.getDeckList());
|
||||
setModified(true);
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(dlg.getDeckList());
|
||||
} else {
|
||||
setDeck(dlg.getDeckList());
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
setSaveStatus(true);
|
||||
}
|
||||
|
||||
@@ -957,6 +1098,79 @@ void TabDeckEditor::recursiveExpand(const QModelIndex &index)
|
||||
deckView->expand(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into
|
||||
* account the `openDeckInNewTab` settting.
|
||||
*
|
||||
* @param openInSameTabIfBlank Open the deck in the same tab instead of a new tab if the current tab is completely
|
||||
* blank. Only relevant when the `openDeckInNewTab` setting is enabled.
|
||||
*
|
||||
* @returns An enum that indicates if and where to load the deck
|
||||
*/
|
||||
TabDeckEditor::DeckOpenLocation TabDeckEditor::confirmOpen(const bool openInSameTabIfBlank)
|
||||
{
|
||||
// handle `openDeckInNewTab` setting
|
||||
if (SettingsCache::instance().getOpenDeckInNewTab()) {
|
||||
if (openInSameTabIfBlank && isBlankNewDeck()) {
|
||||
return SAME_TAB;
|
||||
} else {
|
||||
return NEW_TAB;
|
||||
}
|
||||
}
|
||||
|
||||
// early return if deck is unmodified
|
||||
if (!modified) {
|
||||
return SAME_TAB;
|
||||
}
|
||||
|
||||
// do the save confirmation dialogue
|
||||
tabSupervisor->setCurrentWidget(this);
|
||||
|
||||
QMessageBox *msgBox = createSaveConfirmationWindow();
|
||||
QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole);
|
||||
|
||||
int ret = msgBox->exec();
|
||||
|
||||
// `exec()` returns an opaque value if a non-standard button was clicked.
|
||||
// Directly check if newTabButton was clicked before switching over the standard buttons.
|
||||
if (msgBox->clickedButton() == newTabButton) {
|
||||
return NEW_TAB;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case QMessageBox::Save:
|
||||
return actSaveDeck() ? SAME_TAB : CANCELLED;
|
||||
case QMessageBox::Discard:
|
||||
return SAME_TAB;
|
||||
default:
|
||||
return CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates the base save confirmation dialogue box.
|
||||
*
|
||||
* @returns A QMessageBox that can be further modified
|
||||
*/
|
||||
QMessageBox *TabDeckEditor::createSaveConfirmationWindow()
|
||||
{
|
||||
QMessageBox *msgBox = new QMessageBox(this);
|
||||
msgBox->setIcon(QMessageBox::Warning);
|
||||
msgBox->setWindowTitle(tr("Are you sure?"));
|
||||
msgBox->setText(tr("The decklist has been modified.\nDo you want to save the changes?"));
|
||||
msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
||||
return msgBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action.
|
||||
*/
|
||||
bool TabDeckEditor::isBlankNewDeck() const
|
||||
{
|
||||
DeckLoader *const deck = deckModel->getDeckList();
|
||||
return !modified && deck->getLastFileName().isEmpty() && deck->getLastRemoteDeckId() == -1;
|
||||
}
|
||||
|
||||
CardInfoPtr TabDeckEditor::currentCardInfo() const
|
||||
{
|
||||
const QModelIndex currentIndex = databaseView->selectionModel()->currentIndex();
|
||||
@@ -966,7 +1180,7 @@ CardInfoPtr TabDeckEditor::currentCardInfo() const
|
||||
|
||||
const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString();
|
||||
|
||||
return db->getCard(cardName);
|
||||
return CardDatabaseManager::getInstance()->getCard(cardName);
|
||||
}
|
||||
|
||||
void TabDeckEditor::addCardHelper(QString zoneName)
|
||||
@@ -977,7 +1191,7 @@ void TabDeckEditor::addCardHelper(QString zoneName)
|
||||
if (info->getIsToken())
|
||||
zoneName = DECK_ZONE_TOKENS;
|
||||
|
||||
QModelIndex newCardIndex = deckModel->addCard(info->getName(), zoneName);
|
||||
QModelIndex newCardIndex = deckModel->addPreferredPrintingCard(info->getName(), zoneName, false);
|
||||
recursiveExpand(newCardIndex);
|
||||
deckView->setCurrentIndex(newCardIndex);
|
||||
setModified(true);
|
||||
@@ -990,6 +1204,7 @@ void TabDeckEditor::actSwapCard()
|
||||
if (!currentIndex.isValid())
|
||||
return;
|
||||
const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString();
|
||||
const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 4).data().toString();
|
||||
const QModelIndex gparent = currentIndex.parent().parent();
|
||||
|
||||
if (!gparent.isValid())
|
||||
@@ -999,8 +1214,10 @@ void TabDeckEditor::actSwapCard()
|
||||
actDecrement();
|
||||
const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
|
||||
|
||||
// Third argument (true) says create the card no mater what, even if not in DB
|
||||
QModelIndex newCardIndex = deckModel->addCard(cardName, otherZoneName, true);
|
||||
// Third argument (true) says create the card no matter what, even if not in DB
|
||||
QModelIndex newCardIndex = deckModel->addCard(
|
||||
cardName, CardDatabaseManager::getInstance()->getSpecificSetForCard(cardName, cardProviderID), otherZoneName,
|
||||
true);
|
||||
recursiveExpand(newCardIndex);
|
||||
|
||||
setModified(true);
|
||||
@@ -1076,8 +1293,8 @@ void TabDeckEditor::actDecrementCardFromSideboard()
|
||||
|
||||
void TabDeckEditor::copyDatabaseCellContents()
|
||||
{
|
||||
QVariant data = databaseView->selectionModel()->currentIndex().data();
|
||||
QApplication::clipboard()->setText(data.toString());
|
||||
auto _data = databaseView->selectionModel()->currentIndex().data();
|
||||
QApplication::clipboard()->setText(_data.toString());
|
||||
}
|
||||
|
||||
void TabDeckEditor::actIncrement()
|
||||
@@ -1103,7 +1320,8 @@ void TabDeckEditor::setDeck(DeckLoader *_deck)
|
||||
deckView->expandAll();
|
||||
setModified(false);
|
||||
|
||||
PictureLoader::cacheCardPixmaps(db->getCards(deckModel->getDeckList()->getCardList()));
|
||||
PictureLoader::cacheCardPixmaps(
|
||||
CardDatabaseManager::getInstance()->getCards(deckModel->getDeckList()->getCardList()));
|
||||
deckView->expandAll();
|
||||
setModified(false);
|
||||
|
||||
@@ -1160,6 +1378,9 @@ bool TabDeckEditor::eventFilter(QObject *o, QEvent *e)
|
||||
} else if (o == filterDock) {
|
||||
aFilterDockVisible->setChecked(false);
|
||||
aFilterDockFloating->setEnabled(false);
|
||||
} else if (o == printingSelectorDock) {
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setEnabled(false);
|
||||
}
|
||||
}
|
||||
if (o == this && e->type() == QEvent::Hide) {
|
||||
@@ -1169,6 +1390,7 @@ bool TabDeckEditor::eventFilter(QObject *o, QEvent *e)
|
||||
layouts.setDeckEditorCardSize(cardInfoDock->size());
|
||||
layouts.setDeckEditorFilterSize(filterDock->size());
|
||||
layouts.setDeckEditorDeckSize(deckDock->size());
|
||||
layouts.setDeckEditorPrintingSelectorSize(printingSelectorDock->size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1193,6 +1415,12 @@ void TabDeckEditor::dockVisibleTriggered()
|
||||
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aPrintingSelectorDockVisible) {
|
||||
printingSelectorDock->setVisible(aPrintingSelectorDockVisible->isChecked());
|
||||
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::dockFloatingTriggered()
|
||||
@@ -1212,6 +1440,11 @@ void TabDeckEditor::dockFloatingTriggered()
|
||||
filterDock->setFloating(aFilterDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aPrintingSelectorDockFloating) {
|
||||
printingSelectorDock->setFloating(aPrintingSelectorDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::dockTopLevelChanged(bool topLevel)
|
||||
@@ -1231,6 +1464,11 @@ void TabDeckEditor::dockTopLevelChanged(bool topLevel)
|
||||
aFilterDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == printingSelectorDock) {
|
||||
aPrintingSelectorDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::saveDbHeaderState()
|
||||
@@ -1281,6 +1519,6 @@ void TabDeckEditor::showSearchSyntaxHelp()
|
||||
browser->document()->setDefaultStyleSheet(sheet);
|
||||
|
||||
browser->setHtml(text);
|
||||
connect(browser, &QTextBrowser::anchorClicked, [=](const QUrl &link) { searchEdit->setText(link.fragment()); });
|
||||
connect(browser, &QTextBrowser::anchorClicked, [this](const QUrl &link) { searchEdit->setText(link.fragment()); });
|
||||
browser->show();
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef WINDOW_DECKEDITOR_H
|
||||
#define WINDOW_DECKEDITOR_H
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "customlineedit.h"
|
||||
#include "keysignals.h"
|
||||
#include "../../deck/custom_line_edit.h"
|
||||
#include "../../game/cards/card_database.h"
|
||||
#include "../game_logic/key_signals.h"
|
||||
#include "../ui/widgets/printing_selector/printing_selector.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
@@ -14,7 +15,7 @@ class CardDatabaseDisplayModel;
|
||||
class DeckListModel;
|
||||
class QTreeView;
|
||||
|
||||
class CardFrame;
|
||||
class CardInfoFrameWidget;
|
||||
class QTextEdit;
|
||||
class QLabel;
|
||||
class DeckLoader;
|
||||
@@ -22,29 +23,12 @@ class Response;
|
||||
class FilterTreeModel;
|
||||
class FilterBuilder;
|
||||
class QGroupBox;
|
||||
class QMessageBox;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
class QPushButton;
|
||||
class QDockWidget;
|
||||
|
||||
class SearchLineEdit : public LineEditUnfocusable
|
||||
{
|
||||
private:
|
||||
QTreeView *treeView;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
public:
|
||||
SearchLineEdit() : LineEditUnfocusable(), treeView(nullptr)
|
||||
{
|
||||
}
|
||||
void setTreeView(QTreeView *_treeView)
|
||||
{
|
||||
treeView = _treeView;
|
||||
}
|
||||
};
|
||||
|
||||
class TabDeckEditor : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -54,8 +38,11 @@ private slots:
|
||||
void updateHash();
|
||||
void updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void updateCardInfoRight(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void updatePrintingSelectorDatabase(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void updatePrintingSelectorDeckView(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void updateSearch(const QString &search);
|
||||
void databaseCustomMenu(QPoint point);
|
||||
void decklistCustomMenu(QPoint point);
|
||||
|
||||
void actNewDeck();
|
||||
void actLoadDeck();
|
||||
@@ -100,6 +87,20 @@ private slots:
|
||||
void showSearchSyntaxHelp();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Which tab to open the new deck in
|
||||
*/
|
||||
enum DeckOpenLocation
|
||||
{
|
||||
CANCELLED,
|
||||
SAME_TAB,
|
||||
NEW_TAB
|
||||
};
|
||||
|
||||
DeckOpenLocation confirmOpen(const bool openInSameTabIfBlank = true);
|
||||
QMessageBox *createSaveConfirmationWindow();
|
||||
|
||||
bool isBlankNewDeck() const;
|
||||
CardInfoPtr currentCardInfo() const;
|
||||
void addCardHelper(QString zoneName);
|
||||
void offsetCountAtIndex(const QModelIndex &idx, int offset);
|
||||
@@ -113,7 +114,8 @@ private:
|
||||
|
||||
QTreeView *deckView;
|
||||
KeySignals deckViewKeySignals;
|
||||
CardFrame *cardInfo;
|
||||
CardInfoFrameWidget *cardInfo;
|
||||
PrintingSelector *printingSelector;
|
||||
SearchLineEdit *searchEdit;
|
||||
KeySignals searchKeySignals;
|
||||
|
||||
@@ -128,8 +130,8 @@ private:
|
||||
KeySignals filterViewKeySignals;
|
||||
QWidget *filterBox;
|
||||
|
||||
QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *analyzeDeckMenu,
|
||||
*saveDeckToClipboardMenu;
|
||||
QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu,
|
||||
*analyzeDeckMenu, *saveDeckToClipboardMenu;
|
||||
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard,
|
||||
*aSaveDeckToClipboardRaw, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout,
|
||||
*aClose;
|
||||
@@ -137,7 +139,7 @@ private:
|
||||
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;
|
||||
QAction *aResetLayout;
|
||||
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating, *aFilterDockVisible,
|
||||
*aFilterDockFloating;
|
||||
*aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
|
||||
|
||||
bool modified;
|
||||
QVBoxLayout *centralFrame;
|
||||
@@ -145,6 +147,7 @@ private:
|
||||
QDockWidget *cardInfoDock;
|
||||
QDockWidget *deckDock;
|
||||
QDockWidget *filterDock;
|
||||
QDockWidget *printingSelectorDock;
|
||||
QWidget *centralWidget;
|
||||
|
||||
public:
|
||||
@@ -158,12 +161,16 @@ public:
|
||||
void createDeckDock();
|
||||
void createCardInfoDock();
|
||||
void createFiltersDock();
|
||||
void createPrintingSelectorDock();
|
||||
void createMenus();
|
||||
void createCentralFrame();
|
||||
void updateCardInfo(CardInfoPtr _card);
|
||||
|
||||
public slots:
|
||||
void closeRequest() override;
|
||||
void showPrintingSelector();
|
||||
signals:
|
||||
void openDeckEditor(const DeckLoader *deckLoader);
|
||||
void deckEditorClosing(TabDeckEditor *tab);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include "tab_deck_storage.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "deck_loader.h"
|
||||
#include "../../deck/deck_loader.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../server/remote/remote_decklist_tree_widget.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "../get_text_with_max.h"
|
||||
#include "decklist.h"
|
||||
#include "gettextwithmax.h"
|
||||
#include "pb/command_deck_del.pb.h"
|
||||
#include "pb/command_deck_del_dir.pb.h"
|
||||
#include "pb/command_deck_download.pb.h"
|
||||
@@ -12,9 +15,6 @@
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/response_deck_download.pb.h"
|
||||
#include "pb/response_deck_upload.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "remotedecklist_treewidget.h"
|
||||
#include "settingscache.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
@@ -1,21 +1,32 @@
|
||||
#include "tab_game.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "arrowitem.h"
|
||||
#include "carddatabase.h"
|
||||
#include "cardframe.h"
|
||||
#include "carditem.h"
|
||||
#include "deck_loader.h"
|
||||
#include "deckview.h"
|
||||
#include "dlg_creategame.h"
|
||||
#include "dlg_load_remote_deck.h"
|
||||
#include "dlg_manage_sets.h"
|
||||
#include "gamescene.h"
|
||||
#include "gameview.h"
|
||||
#include "../../client/ui/widgets/cards/card_info_frame_widget.h"
|
||||
#include "../../deck/deck_loader.h"
|
||||
#include "../../deck/deck_view.h"
|
||||
#include "../../dialogs/dlg_create_game.h"
|
||||
#include "../../dialogs/dlg_load_remote_deck.h"
|
||||
#include "../../dialogs/dlg_manage_sets.h"
|
||||
#include "../../game/board/arrow_item.h"
|
||||
#include "../../game/cards/card_database.h"
|
||||
#include "../../game/cards/card_database_manager.h"
|
||||
#include "../../game/cards/card_item.h"
|
||||
#include "../../game/game_scene.h"
|
||||
#include "../../game/game_view.h"
|
||||
#include "../../game/player/player.h"
|
||||
#include "../../game/player/player_list_widget.h"
|
||||
#include "../../game/zones/view_zone.h"
|
||||
#include "../../game/zones/view_zone_widget.h"
|
||||
#include "../../main.h"
|
||||
#include "../../server/message_log_widget.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "../network/replay_timeline_widget.h"
|
||||
#include "../ui/line_edit_completer.h"
|
||||
#include "../ui/phases_toolbar.h"
|
||||
#include "../ui/picture_loader.h"
|
||||
#include "../ui/window_main.h"
|
||||
#include "get_pb_extension.h"
|
||||
#include "lineeditcompleter.h"
|
||||
#include "main.h"
|
||||
#include "messagelogwidget.h"
|
||||
#include "pb/command_concede.pb.h"
|
||||
#include "pb/command_deck_select.pb.h"
|
||||
#include "pb/command_delete_arrow.pb.h"
|
||||
@@ -45,18 +56,8 @@
|
||||
#include "pb/game_event_container.pb.h"
|
||||
#include "pb/game_replay.pb.h"
|
||||
#include "pb/response_deck_download.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "phasestoolbar.h"
|
||||
#include "pictureloader.h"
|
||||
#include "player.h"
|
||||
#include "playerlistwidget.h"
|
||||
#include "replay_timeline_widget.h"
|
||||
#include "settingscache.h"
|
||||
#include "stringsizes.h"
|
||||
#include "tab_supervisor.h"
|
||||
#include "window_main.h"
|
||||
#include "zoneviewwidget.h"
|
||||
#include "zoneviewzone.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QCompleter>
|
||||
@@ -255,7 +256,25 @@ void TabGame::refreshShortcuts()
|
||||
aResetLayout->setShortcuts(shortcuts.getShortcut("Player/aResetLayout"));
|
||||
}
|
||||
if (aFocusChat) {
|
||||
aFocusChat->setShortcuts(shortcuts.getShortcut("tab_game/aFocusChat"));
|
||||
aFocusChat->setShortcuts(shortcuts.getShortcut("Player/aFocusChat"));
|
||||
}
|
||||
if (aReplaySkipForward) {
|
||||
aReplaySkipForward->setShortcuts(shortcuts.getShortcut("Replays/aSkipForward"));
|
||||
}
|
||||
if (aReplaySkipBackward) {
|
||||
aReplaySkipBackward->setShortcuts(shortcuts.getShortcut("Replays/aSkipBackward"));
|
||||
}
|
||||
if (aReplaySkipForwardBig) {
|
||||
aReplaySkipForwardBig->setShortcuts(shortcuts.getShortcut("Replays/aSkipForwardBig"));
|
||||
}
|
||||
if (aReplaySkipBackwardBig) {
|
||||
aReplaySkipBackwardBig->setShortcuts(shortcuts.getShortcut("Replays/aSkipBackwardBig"));
|
||||
}
|
||||
if (replayPlayButton) {
|
||||
replayPlayButton->setShortcut(shortcuts.getSingleShortcut("Replays/playButton"));
|
||||
}
|
||||
if (replayFastForwardButton) {
|
||||
replayFastForwardButton->setShortcut(shortcuts.getSingleShortcut("Replays/fastForwardButton"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +326,8 @@ void DeckViewContainer::deckSelectFinished(const Response &r)
|
||||
{
|
||||
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
|
||||
DeckLoader newDeck(QString::fromStdString(resp.deck()));
|
||||
PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList()));
|
||||
// TODO CHANGE THIS TO BE SELECTED BY UUID
|
||||
PictureLoader::cacheCardPixmaps(CardDatabaseManager::getInstance()->getCards(newDeck.getCardList()));
|
||||
setDeck(newDeck);
|
||||
}
|
||||
|
||||
@@ -422,7 +442,9 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor,
|
||||
: Tab(_tabSupervisor), clients(_clients), gameInfo(event.game_info()), roomGameTypes(_roomGameTypes),
|
||||
hostId(event.host_id()), localPlayerId(event.player_id()), isLocalGame(_tabSupervisor->getIsLocalGame()),
|
||||
spectator(event.spectator()), judge(event.judge()), gameStateKnown(false), resuming(event.resuming()),
|
||||
currentPhase(-1), activeCard(nullptr), gameClosed(false), replay(nullptr), replayDock(nullptr)
|
||||
currentPhase(-1), activeCard(nullptr), gameClosed(false), replay(nullptr), replayPlayButton(nullptr),
|
||||
replayFastForwardButton(nullptr), aReplaySkipForward(nullptr), aReplaySkipBackward(nullptr),
|
||||
aReplaySkipForwardBig(nullptr), aReplaySkipBackwardBig(nullptr), replayDock(nullptr)
|
||||
{
|
||||
// THIS CTOR IS USED ON GAMES
|
||||
gameInfo.set_started(false);
|
||||
@@ -576,7 +598,7 @@ void TabGame::retranslateUi()
|
||||
|
||||
aResetLayout->setText(tr("Reset layout"));
|
||||
|
||||
cardInfo->retranslateUi();
|
||||
cardInfoFrameWidget->retranslateUi();
|
||||
|
||||
QMapIterator<int, Player *> i(players);
|
||||
while (i.hasNext())
|
||||
@@ -593,39 +615,40 @@ void TabGame::closeRequest()
|
||||
actLeaveGame();
|
||||
}
|
||||
|
||||
void TabGame::replayNextEvent()
|
||||
void TabGame::replayNextEvent(Player::EventProcessingOptions options)
|
||||
{
|
||||
processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), nullptr);
|
||||
processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), nullptr, options);
|
||||
}
|
||||
|
||||
void TabGame::replayFinished()
|
||||
{
|
||||
replayStartButton->setEnabled(true);
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
replayPlayButton->setChecked(false);
|
||||
}
|
||||
|
||||
void TabGame::replayStartButtonClicked()
|
||||
void TabGame::replayPlayButtonToggled(bool checked)
|
||||
{
|
||||
replayStartButton->setEnabled(false);
|
||||
replayPauseButton->setEnabled(true);
|
||||
replayFastForwardButton->setEnabled(true);
|
||||
|
||||
timelineWidget->startReplay();
|
||||
}
|
||||
|
||||
void TabGame::replayPauseButtonClicked()
|
||||
{
|
||||
replayStartButton->setEnabled(true);
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
|
||||
timelineWidget->stopReplay();
|
||||
if (checked) { // start replay
|
||||
timelineWidget->startReplay();
|
||||
} else { // pause replay
|
||||
timelineWidget->stopReplay();
|
||||
}
|
||||
}
|
||||
|
||||
void TabGame::replayFastForwardButtonToggled(bool checked)
|
||||
{
|
||||
timelineWidget->setTimeScaleFactor(checked ? 10.0 : 1.0);
|
||||
timelineWidget->setTimeScaleFactor(checked ? ReplayTimelineWidget::FAST_FORWARD_SCALE_FACTOR : 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles everything that needs to be reset when doing a replay rewind.
|
||||
*/
|
||||
void TabGame::replayRewind()
|
||||
{
|
||||
// reset chat log
|
||||
messageLog->clearChat();
|
||||
|
||||
// reset phase markers
|
||||
setActivePhase(-1);
|
||||
}
|
||||
|
||||
void TabGame::incrementGameTime()
|
||||
@@ -839,7 +862,9 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
|
||||
return newPlayer;
|
||||
}
|
||||
|
||||
void TabGame::processGameEventContainer(const GameEventContainer &cont, AbstractClient *client)
|
||||
void TabGame::processGameEventContainer(const GameEventContainer &cont,
|
||||
AbstractClient *client,
|
||||
Player::EventProcessingOptions options)
|
||||
{
|
||||
const GameEventContext &context = cont.context();
|
||||
messageLog->containerProcessingStarted(context);
|
||||
@@ -914,7 +939,7 @@ void TabGame::processGameEventContainer(const GameEventContainer &cont, Abstract
|
||||
qDebug() << "unhandled game event: invalid player id";
|
||||
break;
|
||||
}
|
||||
player->processGameEvent(eventType, event, context);
|
||||
player->processGameEvent(eventType, event, context, options);
|
||||
emitUserEvent();
|
||||
}
|
||||
}
|
||||
@@ -986,7 +1011,7 @@ PendingCommand *TabGame::prepareGameCommand(const QList<const ::google::protobuf
|
||||
return new PendingCommand(cont);
|
||||
}
|
||||
|
||||
void TabGame::startGame(bool resuming)
|
||||
void TabGame::startGame(bool _resuming)
|
||||
{
|
||||
currentPhase = -1;
|
||||
|
||||
@@ -999,7 +1024,7 @@ void TabGame::startGame(bool resuming)
|
||||
|
||||
mainWidget->setCurrentWidget(gamePlayAreaWidget);
|
||||
|
||||
if (!resuming) {
|
||||
if (!_resuming) {
|
||||
QMapIterator<int, Player *> playerIterator(players);
|
||||
while (playerIterator.hasNext())
|
||||
playerIterator.next().value()->setGameStarted();
|
||||
@@ -1087,7 +1112,8 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event,
|
||||
DeckViewContainer *deckViewContainer = deckViewContainers.value(playerId);
|
||||
if (playerInfo.has_deck_list()) {
|
||||
DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list()));
|
||||
PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList()));
|
||||
PictureLoader::cacheCardPixmaps(
|
||||
CardDatabaseManager::getInstance()->getCards(newDeck.getCardList()));
|
||||
deckViewContainer->setDeck(newDeck);
|
||||
player->setDeck(newDeck);
|
||||
}
|
||||
@@ -1354,7 +1380,7 @@ void TabGame::eventSetActivePhase(const Event_SetActivePhase &event,
|
||||
|
||||
void TabGame::newCardAdded(AbstractCardItem *card)
|
||||
{
|
||||
connect(card, SIGNAL(hovered(AbstractCardItem *)), cardInfo, SLOT(setCard(AbstractCardItem *)));
|
||||
connect(card, SIGNAL(hovered(AbstractCardItem *)), cardInfoFrameWidget, SLOT(setCard(AbstractCardItem *)));
|
||||
connect(card, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
|
||||
connect(card, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
|
||||
connect(card, SIGNAL(cardShiftClicked(QString)), this, SLOT(linkCardToChat(QString)));
|
||||
@@ -1704,32 +1730,55 @@ void TabGame::createPlayAreaWidget(bool bReplay)
|
||||
|
||||
void TabGame::createReplayDock()
|
||||
{
|
||||
// timeline widget
|
||||
timelineWidget = new ReplayTimelineWidget;
|
||||
timelineWidget->setTimeline(replayTimeline);
|
||||
connect(timelineWidget, SIGNAL(processNextEvent()), this, SLOT(replayNextEvent()));
|
||||
connect(timelineWidget, SIGNAL(processNextEvent(Player::EventProcessingOptions)), this,
|
||||
SLOT(replayNextEvent(Player::EventProcessingOptions)));
|
||||
connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished()));
|
||||
connect(timelineWidget, &ReplayTimelineWidget::rewound, messageLog, &ChatView::clearChat);
|
||||
connect(timelineWidget, &ReplayTimelineWidget::rewound, this, &TabGame::replayRewind);
|
||||
|
||||
// timeline skip shortcuts
|
||||
aReplaySkipForward = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipForward);
|
||||
connect(aReplaySkipForward, &QAction::triggered, this,
|
||||
[this] { timelineWidget->skipByAmount(ReplayTimelineWidget::SMALL_SKIP_MS); });
|
||||
|
||||
aReplaySkipBackward = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipBackward);
|
||||
connect(aReplaySkipBackward, &QAction::triggered, this,
|
||||
[this] { timelineWidget->skipByAmount(-ReplayTimelineWidget::SMALL_SKIP_MS); });
|
||||
|
||||
aReplaySkipForwardBig = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipForwardBig);
|
||||
connect(aReplaySkipForwardBig, &QAction::triggered, this,
|
||||
[this] { timelineWidget->skipByAmount(ReplayTimelineWidget::BIG_SKIP_MS); });
|
||||
|
||||
aReplaySkipBackwardBig = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipBackwardBig);
|
||||
connect(aReplaySkipBackwardBig, &QAction::triggered, this,
|
||||
[this] { timelineWidget->skipByAmount(-ReplayTimelineWidget::BIG_SKIP_MS); });
|
||||
|
||||
// buttons
|
||||
replayPlayButton = new QToolButton;
|
||||
replayPlayButton->setIconSize(QSize(32, 32));
|
||||
QIcon playButtonIcon = QIcon();
|
||||
playButtonIcon.addPixmap(QPixmap("theme:replay/start"), QIcon::Normal, QIcon::Off);
|
||||
playButtonIcon.addPixmap(QPixmap("theme:replay/pause"), QIcon::Normal, QIcon::On);
|
||||
replayPlayButton->setIcon(playButtonIcon);
|
||||
replayPlayButton->setCheckable(true);
|
||||
connect(replayPlayButton, SIGNAL(toggled(bool)), this, SLOT(replayPlayButtonToggled(bool)));
|
||||
|
||||
replayStartButton = new QToolButton;
|
||||
replayStartButton->setIconSize(QSize(32, 32));
|
||||
replayStartButton->setIcon(QPixmap("theme:replay/start"));
|
||||
connect(replayStartButton, SIGNAL(clicked()), this, SLOT(replayStartButtonClicked()));
|
||||
replayPauseButton = new QToolButton;
|
||||
replayPauseButton->setIconSize(QSize(32, 32));
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayPauseButton->setIcon(QPixmap("theme:replay/pause"));
|
||||
connect(replayPauseButton, SIGNAL(clicked()), this, SLOT(replayPauseButtonClicked()));
|
||||
replayFastForwardButton = new QToolButton;
|
||||
replayFastForwardButton->setIconSize(QSize(32, 32));
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward"));
|
||||
replayFastForwardButton->setCheckable(true);
|
||||
connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool)));
|
||||
|
||||
// putting everything together
|
||||
replayControlLayout = new QHBoxLayout;
|
||||
replayControlLayout->addWidget(timelineWidget, 10);
|
||||
replayControlLayout->addWidget(replayStartButton);
|
||||
replayControlLayout->addWidget(replayPauseButton);
|
||||
replayControlLayout->addWidget(replayPlayButton);
|
||||
replayControlLayout->addWidget(replayFastForwardButton);
|
||||
|
||||
replayControlWidget = new QWidget();
|
||||
@@ -1758,20 +1807,20 @@ void TabGame::createDeckViewContainerWidget(bool bReplay)
|
||||
deckViewContainerWidget->setLayout(deckViewContainerLayout);
|
||||
}
|
||||
|
||||
void TabGame::viewCardInfo(const QString &cardName)
|
||||
void TabGame::viewCardInfo(const QString &cardName, const QString &providerId) const
|
||||
{
|
||||
cardInfo->setCard(cardName);
|
||||
cardInfoFrameWidget->setCard(cardName, providerId);
|
||||
}
|
||||
|
||||
void TabGame::createCardInfoDock(bool bReplay)
|
||||
{
|
||||
Q_UNUSED(bReplay);
|
||||
|
||||
cardInfo = new CardFrame();
|
||||
cardInfoFrameWidget = new CardInfoFrameWidget();
|
||||
cardHInfoLayout = new QHBoxLayout;
|
||||
cardVInfoLayout = new QVBoxLayout;
|
||||
cardVInfoLayout->setContentsMargins(0, 0, 0, 0);
|
||||
cardVInfoLayout->addWidget(cardInfo);
|
||||
cardVInfoLayout->addWidget(cardInfoFrameWidget);
|
||||
cardVInfoLayout->addLayout(cardHInfoLayout);
|
||||
|
||||
cardBoxLayoutWidget = new QWidget;
|
||||
@@ -1813,7 +1862,7 @@ void TabGame::createPlayerListDock(bool bReplay)
|
||||
void TabGame::createMessageDock(bool bReplay)
|
||||
{
|
||||
messageLog = new MessageLogWidget(tabSupervisor, tabSupervisor, this);
|
||||
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
|
||||
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfoFrameWidget, SLOT(setCard(QString)));
|
||||
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
|
||||
connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#ifndef TAB_GAME_H
|
||||
#define TAB_GAME_H
|
||||
|
||||
#include "../../client/tearoff_menu.h"
|
||||
#include "../../game/player/player.h"
|
||||
#include "pb/event_leave.pb.h"
|
||||
#include "pb/serverinfo_game.pb.h"
|
||||
#include "tab.h"
|
||||
#include "tearoffmenu.h"
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QMap>
|
||||
@@ -15,7 +16,7 @@ class CardDatabase;
|
||||
class GameView;
|
||||
class DeckView;
|
||||
class GameScene;
|
||||
class CardFrame;
|
||||
class CardInfoFrameWidget;
|
||||
class MessageLogWidget;
|
||||
class QTimer;
|
||||
class QSplitter;
|
||||
@@ -47,7 +48,6 @@ class Event_Ping;
|
||||
class Event_GameSay;
|
||||
class Event_Kicked;
|
||||
class Event_ReverseTurn;
|
||||
class Player;
|
||||
class CardZone;
|
||||
class AbstractCardItem;
|
||||
class CardItem;
|
||||
@@ -146,9 +146,10 @@ private:
|
||||
int currentReplayStep;
|
||||
QList<int> replayTimeline;
|
||||
ReplayTimelineWidget *timelineWidget;
|
||||
QToolButton *replayStartButton, *replayPauseButton, *replayFastForwardButton;
|
||||
QToolButton *replayPlayButton, *replayFastForwardButton;
|
||||
QAction *aReplaySkipForward, *aReplaySkipBackward, *aReplaySkipForwardBig, *aReplaySkipBackwardBig;
|
||||
|
||||
CardFrame *cardInfo;
|
||||
CardInfoFrameWidget *cardInfoFrameWidget;
|
||||
PlayerListWidget *playerListWidget;
|
||||
QLabel *timeElapsedLabel;
|
||||
MessageLogWidget *messageLog;
|
||||
@@ -218,11 +219,11 @@ signals:
|
||||
void openDeckEditor(const DeckLoader *deck);
|
||||
void notIdle();
|
||||
private slots:
|
||||
void replayNextEvent();
|
||||
void replayNextEvent(Player::EventProcessingOptions options);
|
||||
void replayFinished();
|
||||
void replayStartButtonClicked();
|
||||
void replayPauseButtonClicked();
|
||||
void replayPlayButtonToggled(bool checked);
|
||||
void replayFastForwardButtonToggled(bool checked);
|
||||
void replayRewind();
|
||||
|
||||
void incrementGameTime();
|
||||
void adminLockChanged(bool lock);
|
||||
@@ -304,13 +305,15 @@ public:
|
||||
return activeCard;
|
||||
}
|
||||
|
||||
void processGameEventContainer(const GameEventContainer &cont, AbstractClient *client);
|
||||
void processGameEventContainer(const GameEventContainer &cont,
|
||||
AbstractClient *client,
|
||||
Player::EventProcessingOptions options);
|
||||
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
|
||||
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
|
||||
public slots:
|
||||
void sendGameCommand(PendingCommand *pend, int playerId = -1);
|
||||
void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1);
|
||||
void viewCardInfo(const QString &cardName);
|
||||
void viewCardInfo(const QString &cardName, const QString &providerId = "") const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "tab_logs.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "customlineedit.h"
|
||||
#include "dlg_manage_sets.h"
|
||||
#include "../../deck/custom_line_edit.h"
|
||||
#include "../../dialogs/dlg_manage_sets.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "pb/moderator_commands.pb.h"
|
||||
#include "pb/response_viewlog_history.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "stringsizes.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
@@ -89,7 +89,7 @@ void TabLog::getClicked()
|
||||
if (maximumResults->value() == 0)
|
||||
maximumResults->setValue(1000);
|
||||
|
||||
int dateRange;
|
||||
int dateRange = 0;
|
||||
if (lastHour->isChecked())
|
||||
dateRange = 1;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "tab_message.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "chatview/chatview.h"
|
||||
#include "customlineedit.h"
|
||||
#include "main.h"
|
||||
#include "../../deck/custom_line_edit.h"
|
||||
#include "../../main.h"
|
||||
#include "../../server/chat_view/chat_view.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "../sound_engine.h"
|
||||
#include "pb/event_user_message.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "pb/session_commands.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "settingscache.h"
|
||||
#include "soundengine.h"
|
||||
#include "stringsizes.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
@@ -159,7 +159,7 @@ void TabMessage::showSystemPopup(const Event_UserMessage &event)
|
||||
void TabMessage::messageClicked()
|
||||
{
|
||||
tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this));
|
||||
QApplication::setActiveWindow(this);
|
||||
activateWindow();
|
||||
emit maximizeClient();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "tab_replays.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../server/remote/remote_replay_list_tree_widget.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../game_logic/abstract_client.h"
|
||||
#include "pb/command_replay_delete_match.pb.h"
|
||||
#include "pb/command_replay_download.pb.h"
|
||||
#include "pb/command_replay_modify_match.pb.h"
|
||||
@@ -8,9 +11,6 @@
|
||||
#include "pb/game_replay.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/response_replay_download.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "remotereplaylist_treewidget.h"
|
||||
#include "settingscache.h"
|
||||
#include "tab_game.h"
|
||||
|
||||
#include <QAction>
|
||||
@@ -134,11 +134,11 @@ void TabReplays::actOpenLocalReplay()
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
QByteArray data = f.readAll();
|
||||
QByteArray _data = f.readAll();
|
||||
f.close();
|
||||
|
||||
GameReplay *replay = new GameReplay;
|
||||
replay->ParseFromArray(data.data(), data.size());
|
||||
replay->ParseFromArray(_data.data(), _data.size());
|
||||
|
||||
emit openReplay(replay);
|
||||
}
|
||||
@@ -146,6 +146,9 @@ void TabReplays::actOpenLocalReplay()
|
||||
void TabReplays::actDeleteLocalReplay()
|
||||
{
|
||||
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
||||
if (!curLeft.isValid())
|
||||
return;
|
||||
|
||||
if (QMessageBox::warning(this, tr("Delete local file"),
|
||||
tr("Are you sure you want to delete \"%1\"?").arg(localDirModel->fileName(curLeft)),
|
||||
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
|
||||
@@ -223,10 +226,10 @@ void TabReplays::downloadFinished(const Response &r,
|
||||
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
|
||||
QString filePath = extraData.toString();
|
||||
|
||||
const std::string &data = resp.replay_data();
|
||||
const std::string &_data = resp.replay_data();
|
||||
QFile f(filePath);
|
||||
f.open(QIODevice::WriteOnly);
|
||||
f.write((const char *)data.data(), data.size());
|
||||
f.write((const char *)_data.data(), _data.size());
|
||||
f.close();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "tab_room.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "chatview/chatview.h"
|
||||
#include "dlg_settings.h"
|
||||
#include "gameselector.h"
|
||||
#include "../../client/game_logic/abstract_client.h"
|
||||
#include "../../dialogs/dlg_settings.h"
|
||||
#include "../../game/game_selector.h"
|
||||
#include "../../main.h"
|
||||
#include "../../server/chat_view/chat_view.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../server/user/user_list.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "get_pb_extension.h"
|
||||
#include "main.h"
|
||||
#include "pb/event_join_room.pb.h"
|
||||
#include "pb/event_leave_room.pb.h"
|
||||
#include "pb/event_list_games.pb.h"
|
||||
@@ -13,12 +16,9 @@
|
||||
#include "pb/event_room_say.pb.h"
|
||||
#include "pb/room_commands.pb.h"
|
||||
#include "pb/serverinfo_room.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "settingscache.h"
|
||||
#include "stringsizes.h"
|
||||
#include "tab_account.h"
|
||||
#include "tab_supervisor.h"
|
||||
#include "userlist.h"
|
||||
#include "trice_limits.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCompleter>
|
||||
@@ -155,7 +155,7 @@ void TabRoom::retranslateUi()
|
||||
|
||||
void TabRoom::focusTab()
|
||||
{
|
||||
QApplication::setActiveWindow(this);
|
||||
activateWindow();
|
||||
tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this));
|
||||
emit maximizeClient();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef TAB_ROOM_H
|
||||
#define TAB_ROOM_H
|
||||
|
||||
#include "lineeditcompleter.h"
|
||||
#include "../ui/line_edit_completer.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QFocusEvent>
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "tab_server.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "../../client/game_logic/abstract_client.h"
|
||||
#include "../../server/pending_command.h"
|
||||
#include "../../server/user/user_list.h"
|
||||
#include "pb/event_list_rooms.pb.h"
|
||||
#include "pb/event_server_message.pb.h"
|
||||
#include "pb/response_join_room.pb.h"
|
||||
#include "pb/session_commands.pb.h"
|
||||
#include "pending_command.h"
|
||||
#include "tab_supervisor.h"
|
||||
#include "userlist.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include "abstractclient.h"
|
||||
#include "main.h"
|
||||
#include "../../client/game_logic/abstract_client.h"
|
||||
#include "../../main.h"
|
||||
#include "../../server/user/user_list.h"
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "../ui/pixel_map_generator.h"
|
||||
#include "pb/event_game_joined.pb.h"
|
||||
#include "pb/event_notify_user.pb.h"
|
||||
#include "pb/event_user_message.pb.h"
|
||||
@@ -11,8 +14,6 @@
|
||||
#include "pb/room_event.pb.h"
|
||||
#include "pb/serverinfo_room.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "pixmapgenerator.h"
|
||||
#include "settingscache.h"
|
||||
#include "tab_account.h"
|
||||
#include "tab_admin.h"
|
||||
#include "tab_deck_editor.h"
|
||||
@@ -23,7 +24,6 @@
|
||||
#include "tab_replays.h"
|
||||
#include "tab_room.h"
|
||||
#include "tab_server.h"
|
||||
#include "userlist.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
@@ -496,6 +496,7 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen)
|
||||
if (deckToOpen)
|
||||
tab->setDeck(new DeckLoader(*deckToOpen));
|
||||
connect(tab, SIGNAL(deckEditorClosing(TabDeckEditor *)), this, SLOT(deckEditorClosed(TabDeckEditor *)));
|
||||
connect(tab, SIGNAL(openDeckEditor(const DeckLoader *)), this, SLOT(addDeckEditorTab(const DeckLoader *)));
|
||||
int tabIndex = myAddTab(tab);
|
||||
addCloseButtonToTab(tab, tabIndex);
|
||||
deckEditorTabs.append(tab);
|
||||
@@ -541,7 +542,7 @@ void TabSupervisor::processGameEventContainer(const GameEventContainer &cont)
|
||||
{
|
||||
TabGame *tab = gameTabs.value(cont.game_id());
|
||||
if (tab)
|
||||
tab->processGameEventContainer(cont, qobject_cast<AbstractClient *>(sender()));
|
||||
tab->processGameEventContainer(cont, qobject_cast<AbstractClient *>(sender()), {});
|
||||
else
|
||||
qDebug() << "gameEvent: invalid gameId";
|
||||
}
|
||||
@@ -729,8 +730,8 @@ const ServerInfo_User *TabSupervisor::getOnlineUser(const QString &userName) con
|
||||
|
||||
for (i = userList.begin(); i != userList.end(); ++i)
|
||||
if (i.key().toLower() == userNameToMatchLower) {
|
||||
const ServerInfo_User &userInfo = i.value()->getUserInfo();
|
||||
return &userInfo;
|
||||
const ServerInfo_User &_userInfo = i.value()->getUserInfo();
|
||||
return &_userInfo;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef TAB_SUPERVISOR_H
|
||||
#define TAB_SUPERVISOR_H
|
||||
|
||||
#include "chatview/userlistProxy.h"
|
||||
#include "deck_loader.h"
|
||||
#include "../../deck/deck_loader.h"
|
||||
#include "../../server/chat_view/user_list_proxy.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QCommonStyle>
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "tappedout_interface.h"
|
||||
#include "tapped_out_interface.h"
|
||||
|
||||
#include "decklist.h"
|
||||
|
||||
@@ -14,7 +14,7 @@ TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *par
|
||||
: QObject(parent), cardDatabase(_cardDatabase)
|
||||
{
|
||||
manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *)));
|
||||
connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished);
|
||||
}
|
||||
|
||||
void TappedOutInterface::queryFinished(QNetworkReply *reply)
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef TAPPEDOUT_INTERFACE_H
|
||||
#define TAPPEDOUT_INTERFACE_H
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "../game/cards/card_database.h"
|
||||
#include "decklist.h"
|
||||
|
||||
#include <QObject>
|
||||
@@ -1,29 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "settingscache.h"
|
||||
#include "../settings/cache_settings.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
class TearOffMenu : public QMenu
|
||||
{
|
||||
public:
|
||||
TearOffMenu(const QString &title, QWidget *parent = nullptr) : QMenu(title, parent)
|
||||
explicit TearOffMenu(const QString &title, QWidget *parent = nullptr) : QMenu(title, parent)
|
||||
{
|
||||
connect(&SettingsCache::instance(), &SettingsCache::useTearOffMenusChanged, this,
|
||||
[=](bool state) { setTearOffEnabled(state); });
|
||||
[this](const bool state) { setTearOffEnabled(state); });
|
||||
setTearOffEnabled(SettingsCache::instance().getUseTearOffMenus());
|
||||
}
|
||||
|
||||
TearOffMenu(QWidget *parent = nullptr) : QMenu(parent)
|
||||
explicit TearOffMenu(QWidget *parent = nullptr) : QMenu(parent)
|
||||
{
|
||||
connect(&SettingsCache::instance(), &SettingsCache::useTearOffMenusChanged, this,
|
||||
[=](bool state) { setTearOffEnabled(state); });
|
||||
[this](const bool state) { setTearOffEnabled(state); });
|
||||
setTearOffEnabled(SettingsCache::instance().getUseTearOffMenus());
|
||||
}
|
||||
|
||||
TearOffMenu *addTearOffMenu(const QString &title)
|
||||
{
|
||||
TearOffMenu *menu = new TearOffMenu(title, this);
|
||||
auto *menu = new TearOffMenu(title, this);
|
||||
addMenu(menu);
|
||||
return menu;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "translatecountername.h"
|
||||
#include "translate_counter_name.h"
|
||||
|
||||
const QMap<QString, QString> TranslateCounterName::translated = {
|
||||
{"life", QT_TRANSLATE_NOOP("TranslateCounterName", "Life")},
|
||||
337
cockatrice/src/client/ui/layouts/flow_layout.cpp
Normal file
337
cockatrice/src/client/ui/layouts/flow_layout.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/**
|
||||
* @file flow_layout.cpp
|
||||
* @brief Implementation of the FlowLayout class, a custom layout for dynamically organizing widgets
|
||||
* in rows within the constraints of available width or parent scroll areas.
|
||||
*/
|
||||
|
||||
#include "flow_layout.h"
|
||||
|
||||
#include "../widgets/general/layout_containers/flow_widget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLayoutItem>
|
||||
#include <QScrollArea>
|
||||
#include <QStyle>
|
||||
|
||||
/**
|
||||
* @brief Constructs a FlowLayout instance with the specified parent widget, margin, and spacing values.
|
||||
* @param parent The parent widget for this layout.
|
||||
* @param margin The layout margin.
|
||||
* @param hSpacing The horizontal spacing between items.
|
||||
* @param vSpacing The vertical spacing between items.
|
||||
*/
|
||||
FlowLayout::FlowLayout(QWidget *parent, const int margin, const int hSpacing, const int vSpacing)
|
||||
: QLayout(parent), horizontalMargin(hSpacing), verticalMargin(vSpacing)
|
||||
{
|
||||
setContentsMargins(margin, margin, margin, margin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for FlowLayout, which cleans up all items in the layout.
|
||||
*/
|
||||
FlowLayout::~FlowLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = FlowLayout::takeAt(0))) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indicates the layout's support for expansion in both horizontal and vertical directions.
|
||||
* @return The orientations (Qt::Horizontal | Qt::Vertical) this layout can expand to fill.
|
||||
*/
|
||||
Qt::Orientations FlowLayout::expandingDirections() const
|
||||
{
|
||||
return Qt::Horizontal | Qt::Vertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indicates that this layout's height depends on its width.
|
||||
* @return True, as the layout adjusts its height to fit the specified width.
|
||||
*/
|
||||
bool FlowLayout::hasHeightForWidth() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the required height to display all items within the specified width.
|
||||
* @param width The available width for arranging items.
|
||||
* @return The total height needed to fit all items in rows constrained by the specified width.
|
||||
*/
|
||||
int FlowLayout::heightForWidth(const int width) const
|
||||
{
|
||||
int height = 0;
|
||||
int rowWidth = 0;
|
||||
int rowHeight = 0;
|
||||
|
||||
for (const QLayoutItem *item : items) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int itemWidth = item->sizeHint().width() + horizontalSpacing();
|
||||
if (rowWidth + itemWidth > width) { // Start a new row if the row width exceeds available width
|
||||
height += rowHeight + verticalSpacing();
|
||||
rowWidth = itemWidth;
|
||||
rowHeight = item->sizeHint().height() + verticalSpacing();
|
||||
} else {
|
||||
rowWidth += itemWidth;
|
||||
rowHeight = qMax(rowHeight, item->sizeHint().height());
|
||||
}
|
||||
}
|
||||
height += rowHeight; // Add the final row's height
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arranges layout items in rows within the specified rectangle bounds.
|
||||
* @param rect The area within which to position layout items.
|
||||
*/
|
||||
void FlowLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
QLayout::setGeometry(rect); // Sets the geometry of the layout based on the given rectangle.
|
||||
|
||||
int left, top, right, bottom;
|
||||
getContentsMargins(&left, &top, &right, &bottom); // Retrieves the layout's content margins.
|
||||
|
||||
// Adjust the rectangle to exclude margins.
|
||||
const QRect adjustedRect = rect.adjusted(+left, +top, -right, -bottom);
|
||||
|
||||
// Calculate the available width for items, considering either the adjusted rectangle's width
|
||||
// or the parent scroll area width, if applicable.
|
||||
const int availableWidth = qMax(adjustedRect.width(), getParentScrollAreaWidth());
|
||||
|
||||
// Arrange all rows of items within the available width and get the total height used.
|
||||
const int totalHeight = layoutAllRows(adjustedRect.x(), adjustedRect.y(), availableWidth);
|
||||
|
||||
// If the layout's parent is a QWidget, update its minimum size to ensure it can accommodate
|
||||
// the arranged items' dimensions.
|
||||
if (QWidget *parentWidgetPtr = parentWidget()) {
|
||||
parentWidgetPtr->setMinimumSize(availableWidth, totalHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arranges items in rows based on the available width.
|
||||
* Items are added to a row until the row's width exceeds `availableWidth`.
|
||||
* Then, a new row is started.
|
||||
* @param originX The starting x-coordinate for the row layout.
|
||||
* @param originY The starting y-coordinate for the row layout.
|
||||
* @param availableWidth The available width to lay out items.
|
||||
* @return The y-coordinate of the final row's end position.
|
||||
*/
|
||||
int FlowLayout::layoutAllRows(const int originX, const int originY, const int availableWidth)
|
||||
{
|
||||
QVector<QLayoutItem *> rowItems; // Temporary storage for items in the current row.
|
||||
int currentXPosition = originX; // Tracks the x-coordinate for placing items in the current row.
|
||||
int currentYPosition = originY; // Tracks the y-coordinate, updated after each row.
|
||||
|
||||
int rowHeight = 0; // Tracks the maximum height of items in the current row.
|
||||
|
||||
// Iterate through all layout items to arrange them.
|
||||
for (QLayoutItem *item : items) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->sizeHint(); // The suggested size for the item.
|
||||
const int itemWidth = itemSize.width() + horizontalSpacing();
|
||||
|
||||
// Check if the item fits in the current row's remaining width.
|
||||
if (currentXPosition + itemWidth > availableWidth) {
|
||||
// If not, layout the current row and start a new row.
|
||||
layoutSingleRow(rowItems, originX, currentYPosition);
|
||||
rowItems.clear(); // Clear the temporary storage for the new row.
|
||||
currentXPosition = originX; // Reset x-position to the start of the new row.
|
||||
currentYPosition += rowHeight + verticalSpacing(); // Move y-position down for the new row.
|
||||
rowHeight = 0; // Reset row height for the new row.
|
||||
}
|
||||
|
||||
// Add the item to the current row.
|
||||
rowItems.append(item);
|
||||
rowHeight = qMax(rowHeight, itemSize.height()); // Update the row height to the tallest item.
|
||||
currentXPosition += itemSize.width() + horizontalSpacing(); // Move x-position for the next item.
|
||||
}
|
||||
|
||||
// Layout the final row if there are remaining items.
|
||||
layoutSingleRow(rowItems, originX, currentYPosition);
|
||||
|
||||
currentYPosition += rowHeight; // Add the final row's height
|
||||
return currentYPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function for arranging a single row of items within specified bounds.
|
||||
* @param rowItems Items to be arranged in the row.
|
||||
* @param x The x-coordinate for starting the row.
|
||||
* @param y The y-coordinate for starting the row.
|
||||
*/
|
||||
void FlowLayout::layoutSingleRow(const QVector<QLayoutItem *> &rowItems, int x, const int y)
|
||||
{
|
||||
// Iterate through each item in the row and position it.
|
||||
for (QLayoutItem *item : rowItems) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemMaxSize = item->widget()->maximumSize(); // Get the item's maximum allowable size.
|
||||
// Constrain the item's width and height to its size hint or maximum size.
|
||||
int itemWidth = qMin(item->sizeHint().width(), itemMaxSize.width());
|
||||
int itemHeight = qMin(item->sizeHint().height(), itemMaxSize.height());
|
||||
// Set the item's geometry based on the calculated size and position.
|
||||
item->setGeometry(QRect(QPoint(x, y), QSize(itemWidth, itemHeight)));
|
||||
// Move the x-position for the next item, including horizontal spacing.
|
||||
x += itemWidth + horizontalSpacing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the preferred size for this layout.
|
||||
* @return The maximum of all item size hints as a QSize.
|
||||
*/
|
||||
QSize FlowLayout::sizeHint() const
|
||||
{
|
||||
QSize size;
|
||||
for (const QLayoutItem *item : items) {
|
||||
if (item != nullptr && !item->isEmpty()) {
|
||||
size = size.expandedTo(item->sizeHint());
|
||||
}
|
||||
}
|
||||
return size.isValid() ? size : QSize(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the minimum size required to display all layout items.
|
||||
* @return The minimum QSize needed by the layout.
|
||||
*/
|
||||
QSize FlowLayout::minimumSize() const
|
||||
{
|
||||
QSize size;
|
||||
for (const QLayoutItem *item : items) {
|
||||
if (item != nullptr && !item->isEmpty()) {
|
||||
size = size.expandedTo(item->minimumSize());
|
||||
}
|
||||
}
|
||||
|
||||
size.setWidth(qMin(size.width(), getParentScrollAreaWidth()));
|
||||
size.setHeight(qMin(size.height(), getParentScrollAreaHeight()));
|
||||
|
||||
return size.isValid() ? size : QSize(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a new item to the layout.
|
||||
* @param item The layout item to add.
|
||||
*/
|
||||
void FlowLayout::addItem(QLayoutItem *item)
|
||||
{
|
||||
if (item != nullptr) {
|
||||
items.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the count of items in the layout.
|
||||
* @return The number of layout items.
|
||||
*/
|
||||
int FlowLayout::count() const
|
||||
{
|
||||
return static_cast<int>(items.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the layout item at the specified index.
|
||||
* @param index The index of the item to retrieve.
|
||||
* @return A pointer to the item at the specified index, or nullptr if out of range.
|
||||
*/
|
||||
QLayoutItem *FlowLayout::itemAt(const int index) const
|
||||
{
|
||||
return (index >= 0 && index < items.size()) ? items.value(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes and returns the item at the specified index.
|
||||
* @param index The index of the item to remove.
|
||||
* @return A pointer to the removed item, or nullptr if out of range.
|
||||
*/
|
||||
QLayoutItem *FlowLayout::takeAt(const int index)
|
||||
{
|
||||
return (index >= 0 && index < items.size()) ? items.takeAt(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the horizontal spacing between items.
|
||||
* @return The horizontal spacing if set, otherwise a smart default.
|
||||
*/
|
||||
int FlowLayout::horizontalSpacing() const
|
||||
{
|
||||
return (horizontalMargin >= 0) ? horizontalMargin : smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the vertical spacing between items.
|
||||
* @return The vertical spacing if set, otherwise a smart default.
|
||||
*/
|
||||
int FlowLayout::verticalSpacing() const
|
||||
{
|
||||
return (verticalMargin >= 0) ? verticalMargin : smartSpacing(QStyle::PM_LayoutVerticalSpacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates smart spacing based on the parent widget style.
|
||||
* @param pm The pixel metric to calculate.
|
||||
* @return The calculated spacing value.
|
||||
*/
|
||||
int FlowLayout::smartSpacing(const QStyle::PixelMetric pm) const
|
||||
{
|
||||
QObject *parent = this->parent();
|
||||
|
||||
if (!parent) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parent->isWidgetType()) {
|
||||
const auto *pw = dynamic_cast<QWidget *>(parent);
|
||||
return pw->style()->pixelMetric(pm, nullptr, pw);
|
||||
}
|
||||
|
||||
return dynamic_cast<QLayout *>(parent)->spacing();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the width of the parent scroll area, if any.
|
||||
* @return The width of the scroll area's viewport, or 0 if not found.
|
||||
*/
|
||||
int FlowLayout::getParentScrollAreaWidth() const
|
||||
{
|
||||
QWidget *parent = parentWidget();
|
||||
|
||||
while (parent) {
|
||||
if (const auto *scrollArea = qobject_cast<QScrollArea *>(parent)) {
|
||||
return scrollArea->viewport()->width();
|
||||
}
|
||||
parent = parent->parentWidget();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the height of the parent scroll area, if any.
|
||||
* @return The height of the scroll area's viewport, or 0 if not found.
|
||||
*/
|
||||
int FlowLayout::getParentScrollAreaHeight() const
|
||||
{
|
||||
QWidget *parent = parentWidget();
|
||||
|
||||
while (parent) {
|
||||
if (const auto *scrollArea = qobject_cast<QScrollArea *>(parent)) {
|
||||
return scrollArea->viewport()->height();
|
||||
}
|
||||
parent = parent->parentWidget();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
43
cockatrice/src/client/ui/layouts/flow_layout.h
Normal file
43
cockatrice/src/client/ui/layouts/flow_layout.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef FLOW_LAYOUT_H
|
||||
#define FLOW_LAYOUT_H
|
||||
|
||||
#include <QLayout>
|
||||
#include <QList>
|
||||
#include <QWidget>
|
||||
#include <qstyle.h>
|
||||
|
||||
class FlowLayout : public QLayout
|
||||
{
|
||||
public:
|
||||
explicit FlowLayout(QWidget *parent = nullptr);
|
||||
FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing);
|
||||
~FlowLayout() override;
|
||||
|
||||
void addItem(QLayoutItem *item) override;
|
||||
[[nodiscard]] int count() const override;
|
||||
[[nodiscard]] QLayoutItem *itemAt(int index) const override;
|
||||
QLayoutItem *takeAt(int index) override;
|
||||
[[nodiscard]] int horizontalSpacing() const;
|
||||
|
||||
[[nodiscard]] Qt::Orientations expandingDirections() const override;
|
||||
[[nodiscard]] bool hasHeightForWidth() const override;
|
||||
[[nodiscard]] int heightForWidth(int width) const override;
|
||||
[[nodiscard]] int verticalSpacing() const;
|
||||
[[nodiscard]] int doLayout(const QRect &rect, bool testOnly) const;
|
||||
[[nodiscard]] int smartSpacing(QStyle::PixelMetric pm) const;
|
||||
[[nodiscard]] int getParentScrollAreaWidth() const;
|
||||
[[nodiscard]] int getParentScrollAreaHeight() const;
|
||||
|
||||
void setGeometry(const QRect &rect) override;
|
||||
virtual int layoutAllRows(int originX, int originY, int availableWidth);
|
||||
virtual void layoutSingleRow(const QVector<QLayoutItem *> &rowItems, int x, int y);
|
||||
[[nodiscard]] QSize sizeHint() const override;
|
||||
[[nodiscard]] QSize minimumSize() const override;
|
||||
|
||||
protected:
|
||||
QList<QLayoutItem *> items; // List to store layout items
|
||||
int horizontalMargin;
|
||||
int verticalMargin;
|
||||
};
|
||||
|
||||
#endif // FLOW_LAYOUT_H
|
||||
142
cockatrice/src/client/ui/layouts/horizontal_flow_layout.cpp
Normal file
142
cockatrice/src/client/ui/layouts/horizontal_flow_layout.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "horizontal_flow_layout.h"
|
||||
|
||||
/**
|
||||
* @brief Constructs a HorizontalFlowLayout instance with the specified parent widget.
|
||||
* This layout arranges items in columns within the given height, automatically adjusting its width.
|
||||
* @param parent The parent widget to which this layout belongs.
|
||||
* @param margin The layout margin.
|
||||
* @param hSpacing The horizontal spacing between items.
|
||||
* @param vSpacing The vertical spacing between items.
|
||||
*/
|
||||
HorizontalFlowLayout::HorizontalFlowLayout(QWidget *parent, const int margin, const int hSpacing, const int vSpacing)
|
||||
: FlowLayout(parent, margin, hSpacing, vSpacing)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for HorizontalFlowLayout, responsible for cleaning up layout items.
|
||||
*/
|
||||
HorizontalFlowLayout::~HorizontalFlowLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = FlowLayout::takeAt(0))) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the required width to display all items, given a specified height.
|
||||
* This method arranges items into columns and determines the total width needed.
|
||||
* @param height The available height for arranging layout items.
|
||||
* @return The total width required to fit all items, organized in columns constrained by the given height.
|
||||
*/
|
||||
int HorizontalFlowLayout::heightForWidth(const int height) const
|
||||
{
|
||||
int width = 0;
|
||||
int colWidth = 0;
|
||||
int colHeight = 0;
|
||||
|
||||
for (const QLayoutItem *item : items) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int itemHeight = item->sizeHint().height();
|
||||
if (colHeight + itemHeight > height) {
|
||||
width += colWidth;
|
||||
colHeight = itemHeight;
|
||||
colWidth = item->sizeHint().width();
|
||||
} else {
|
||||
colHeight += itemHeight;
|
||||
colWidth = qMax(colWidth, item->sizeHint().width());
|
||||
}
|
||||
}
|
||||
width += colWidth; // Add width of the last column
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the geometry of the layout items, arranging them in columns within the given height.
|
||||
* @param rect The rectangle area defining the layout space.
|
||||
*/
|
||||
void HorizontalFlowLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
const int availableHeight = qMax(rect.height(), getParentScrollAreaHeight());
|
||||
|
||||
const int totalWidth = layoutAllColumns(rect.x(), rect.y(), availableHeight);
|
||||
|
||||
if (QWidget *parentWidgetPtr = parentWidget()) {
|
||||
parentWidgetPtr->setMinimumSize(totalWidth, availableHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lays out items into columns according to the available height, starting from a given origin.
|
||||
* Each column is arranged within `availableHeight`, wrapping to a new column as necessary.
|
||||
* @param originX The x-coordinate for the layout start position.
|
||||
* @param originY The y-coordinate for the layout start position.
|
||||
* @param availableHeight The height within which each column is constrained.
|
||||
* @return The total width after arranging all columns.
|
||||
*/
|
||||
int HorizontalFlowLayout::layoutAllColumns(const int originX, const int originY, const int availableHeight)
|
||||
{
|
||||
QVector<QLayoutItem *> colItems; // Holds items for the current column
|
||||
int currentXPosition = originX; // Tracks the x-coordinate while placing items
|
||||
int currentYPosition = originY; // Tracks the y-coordinate, resetting for each new column
|
||||
|
||||
int colWidth = 0; // Tracks the maximum width of items in the current column
|
||||
|
||||
for (QLayoutItem *item : items) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->sizeHint(); // The suggested size for the current item
|
||||
|
||||
// Check if the current item fits in the remaining height of the current column
|
||||
if (currentYPosition + itemSize.height() > availableHeight) {
|
||||
// If not, layout the current column and start a new column
|
||||
layoutSingleColumn(colItems, currentXPosition, originY);
|
||||
colItems.clear(); // Reset the list for the new column
|
||||
currentYPosition = originY; // Reset y-position to the column's start
|
||||
currentXPosition += colWidth; // Move x-position to the next column
|
||||
colWidth = 0; // Reset column width for the new column
|
||||
}
|
||||
|
||||
// Add the item to the current column
|
||||
colItems.append(item);
|
||||
colWidth = qMax(colWidth, itemSize.width()); // Update the column's width to the widest item
|
||||
currentYPosition += itemSize.height(); // Move y-position for the next item
|
||||
}
|
||||
|
||||
// Layout the final column if there are any remaining items
|
||||
layoutSingleColumn(colItems, currentXPosition, originY);
|
||||
|
||||
// Return the total width used, including the last column's width
|
||||
return currentXPosition + colWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arranges a single column of items within specified x and y starting positions.
|
||||
* @param colItems A list of items to be arranged in the column.
|
||||
* @param x The starting x-coordinate for the column.
|
||||
* @param y The starting y-coordinate for the column.
|
||||
*/
|
||||
void HorizontalFlowLayout::layoutSingleColumn(const QVector<QLayoutItem *> &colItems, const int x, int y)
|
||||
{
|
||||
for (QLayoutItem *item : colItems) {
|
||||
if (item != nullptr && item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the maximum allowed size for the item
|
||||
QSize itemMaxSize = item->widget()->maximumSize();
|
||||
// Constrain the item's width and height to its size hint or maximum size
|
||||
const int itemWidth = qMin(item->sizeHint().width(), itemMaxSize.width());
|
||||
const int itemHeight = qMin(item->sizeHint().height(), itemMaxSize.height());
|
||||
// Set the item's geometry based on the computed size and position
|
||||
item->setGeometry(QRect(QPoint(x, y), QSize(itemWidth, itemHeight)));
|
||||
// Move the y-position down by the item's height to place the next item below
|
||||
y += itemHeight;
|
||||
}
|
||||
}
|
||||
19
cockatrice/src/client/ui/layouts/horizontal_flow_layout.h
Normal file
19
cockatrice/src/client/ui/layouts/horizontal_flow_layout.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef HORIZONTAL_FLOW_LAYOUT_H
|
||||
#define HORIZONTAL_FLOW_LAYOUT_H
|
||||
|
||||
#include "flow_layout.h"
|
||||
|
||||
class HorizontalFlowLayout : public FlowLayout
|
||||
{
|
||||
public:
|
||||
explicit HorizontalFlowLayout(QWidget *parent = nullptr, int margin = 0, int hSpacing = 0, int vSpacing = 0);
|
||||
~HorizontalFlowLayout() override;
|
||||
|
||||
[[nodiscard]] int heightForWidth(int height) const override;
|
||||
|
||||
void setGeometry(const QRect &rect) override;
|
||||
int layoutAllColumns(int originX, int originY, int availableHeight);
|
||||
static void layoutSingleColumn(const QVector<QLayoutItem *> &colItems, int x, int y);
|
||||
};
|
||||
|
||||
#endif // HORIZONTAL_FLOW_LAYOUT_H
|
||||
474
cockatrice/src/client/ui/layouts/overlap_layout.cpp
Normal file
474
cockatrice/src/client/ui/layouts/overlap_layout.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
#include "overlap_layout.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
/**
|
||||
* @class OverlapLayout
|
||||
* @brief Custom layout class to arrange widgets with overlapping positions.
|
||||
*
|
||||
* The OverlapLayout class is a QLayout subclass that arranges child widgets
|
||||
* in an overlapping configuration, allowing control over the overlap percentage
|
||||
* and the number of rows or columns based on the chosen layout direction. This
|
||||
* layout is particularly useful for visualizing elements that need to partially
|
||||
* stack over one another, either horizontally or vertically.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Constructs an OverlapLayout with the specified parameters.
|
||||
*
|
||||
* Initializes a new OverlapLayout with the given overlap percentage, row or column limit,
|
||||
* and layout direction. The overlap percentage determines how much each widget will
|
||||
* overlap with the previous one. If maxColumns or maxRows are set to zero, it implies
|
||||
* no limit in that respective dimension.
|
||||
*
|
||||
* @param overlapPercentage An integer representing the percentage of overlap between items (0-100).
|
||||
* @param maxColumns The maximum number of columns allowed in the layout when in horizontal orientation (0 for
|
||||
* unlimited).
|
||||
* @param maxRows The maximum number of rows allowed in the layout when in vertical orientation (0 for unlimited).
|
||||
* @param direction The orientation direction of the layout, either Qt::Horizontal or Qt::Vertical.
|
||||
* @param parent The parent widget of this layout.
|
||||
*/
|
||||
OverlapLayout::OverlapLayout(QWidget *parent,
|
||||
const int overlapPercentage,
|
||||
const int maxColumns,
|
||||
const int maxRows,
|
||||
const Qt::Orientation direction)
|
||||
: QLayout(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
|
||||
direction(direction)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for OverlapLayout, ensuring cleanup of all layout items.
|
||||
*
|
||||
* Iterates through all layout items and deletes them. This prevents memory
|
||||
* leaks by removing all child QLayoutItems stored in the layout.
|
||||
*/
|
||||
OverlapLayout::~OverlapLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = OverlapLayout::takeAt(0))) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a new item to the layout.
|
||||
*
|
||||
* Appends a QLayoutItem to the internal list, allowing it to be positioned within the
|
||||
* layout during the next geometry update. This method does not directly arrange the
|
||||
* items; it merely adds them to the layout’s tracking.
|
||||
*
|
||||
* @param item Pointer to the QLayoutItem being added to this layout.
|
||||
*/
|
||||
void OverlapLayout::addItem(QLayoutItem *item)
|
||||
{
|
||||
if (item != nullptr) {
|
||||
itemList.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the total count of items within the layout.
|
||||
*
|
||||
* Returns the number of items stored in the layout's internal item list.
|
||||
* This count reflects how many widgets or spacers the layout is currently managing.
|
||||
*
|
||||
* @return Integer count of items in the layout.
|
||||
*/
|
||||
int OverlapLayout::count() const
|
||||
{
|
||||
return static_cast<int>(itemList.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides access to a layout item at a specified index.
|
||||
*
|
||||
* Allows retrieval of a QLayoutItem from the layout’s internal list
|
||||
* by index. If the index is out of bounds, this function will return nullptr.
|
||||
*
|
||||
* @param index The index of the desired item.
|
||||
* @return Pointer to the QLayoutItem at the specified index, or nullptr if index is invalid.
|
||||
*/
|
||||
QLayoutItem *OverlapLayout::itemAt(const int index) const
|
||||
{
|
||||
return (index >= 0 && index < itemList.size()) ? itemList.value(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes and returns a layout item at the specified index.
|
||||
*
|
||||
* Removes a QLayoutItem from the layout at the given index, reducing the layout's count.
|
||||
* If the index is invalid, this function returns nullptr without any effect.
|
||||
*
|
||||
* @param index The index of the item to remove.
|
||||
* @return Pointer to the removed QLayoutItem, or nullptr if index is invalid.
|
||||
*/
|
||||
QLayoutItem *OverlapLayout::takeAt(const int index)
|
||||
{
|
||||
return (index >= 0 && index < itemList.size()) ? itemList.takeAt(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the geometry for the layout items, arranging them with the specified overlap.
|
||||
* @param rect The rectangle defining the area within which the layout should arrange items.
|
||||
*/
|
||||
void OverlapLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
// Call the base class implementation to ensure standard layout behavior.
|
||||
QLayout::setGeometry(rect);
|
||||
|
||||
// If there are no items to layout, exit early.
|
||||
if (itemList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the parent widget for size and margin calculations.
|
||||
const QWidget *parentWidget = this->parentWidget();
|
||||
if (!parentWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate available width and height, subtracting the parent's margins.
|
||||
int availableWidth = parentWidget->width();
|
||||
int availableHeight = parentWidget->height();
|
||||
const QMargins margins = parentWidget->contentsMargins();
|
||||
availableWidth -= margins.left() + margins.right();
|
||||
availableHeight -= margins.top() + margins.bottom();
|
||||
|
||||
// Determine the maximum item width and height among all layout items.
|
||||
int maxItemWidth = 0;
|
||||
int maxItemHeight = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item != nullptr && item->widget()) {
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemWidth = qMax(maxItemWidth, itemSize.width());
|
||||
maxItemHeight = qMax(maxItemHeight, itemSize.height());
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the overlap offsets based on the layout direction and overlap percentage.
|
||||
const int overlapOffsetWidth = (direction == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
|
||||
const int overlapOffsetHeight = (direction == Qt::Vertical) ? (maxItemHeight * overlapPercentage / 100) : 0;
|
||||
|
||||
// Determine the number of columns based on layout constraints and available space.
|
||||
int columns;
|
||||
if (direction == Qt::Horizontal) {
|
||||
if (maxColumns > 0) {
|
||||
// Calculate the maximum possible columns given the available width and overlap.
|
||||
const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
|
||||
// Use the smaller of maxColumns and availableColumns.
|
||||
columns = qMin(maxColumns, availableColumns);
|
||||
} else {
|
||||
// If no maxColumns constraint, allow as many columns as possible.
|
||||
columns = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
// If not a horizontal layout, column count is irrelevant.
|
||||
columns = INT_MAX;
|
||||
}
|
||||
|
||||
// Determine the number of rows based on layout constraints and available space.
|
||||
int rows;
|
||||
if (direction == Qt::Vertical) {
|
||||
if (maxRows > 0) {
|
||||
// Calculate the maximum possible rows given the available height and overlap.
|
||||
const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
|
||||
// Use the smaller of maxRows and availableRows.
|
||||
rows = qMin(maxRows, availableRows);
|
||||
} else {
|
||||
// If no maxRows constraint, allow as many rows as possible.
|
||||
rows = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
// If not a vertical layout, row count is irrelevant.
|
||||
rows = INT_MAX;
|
||||
}
|
||||
|
||||
// Initialize row and column indices.
|
||||
int currentRow = 0;
|
||||
int currentColumn = 0;
|
||||
|
||||
// Loop through all items and position them based on the calculated offsets.
|
||||
for (const auto item : itemList) {
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the position of the current item.
|
||||
const int xPos = rect.left() + currentColumn * (maxItemWidth - overlapOffsetWidth);
|
||||
const int yPos = rect.top() + currentRow * (maxItemHeight - overlapOffsetHeight);
|
||||
item->setGeometry(QRect(xPos, yPos, maxItemWidth, maxItemHeight));
|
||||
|
||||
// Update row and column indices based on the layout direction.
|
||||
if (direction == Qt::Horizontal) {
|
||||
currentColumn++;
|
||||
if (currentColumn >= columns) {
|
||||
currentColumn = 0;
|
||||
currentRow++;
|
||||
}
|
||||
} else {
|
||||
currentRow++;
|
||||
if (currentRow >= rows) {
|
||||
currentRow = 0;
|
||||
currentColumn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the preferred size for the layout, considering overlap and orientation.
|
||||
* @return The preferred layout size as a QSize object.
|
||||
*/
|
||||
QSize OverlapLayout::calculatePreferredSize() const
|
||||
{
|
||||
|
||||
// Determine the maximum item dimensions.
|
||||
int maxItemWidth = 0;
|
||||
int maxItemHeight = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!item->widget()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemWidth = qMax(maxItemWidth, itemSize.width());
|
||||
maxItemHeight = qMax(maxItemHeight, itemSize.height());
|
||||
}
|
||||
|
||||
// Calculate the overlap offsets.
|
||||
const int extra_for_overlap = (100 - overlapPercentage);
|
||||
const int overlapOffsetWidth =
|
||||
(direction == Qt::Horizontal) ? qRound((maxItemWidth / 100.0) * extra_for_overlap) : 0;
|
||||
const int overlapOffsetHeight =
|
||||
(direction == Qt::Vertical) ? qRound((maxItemHeight / 100.0) * extra_for_overlap) : 0;
|
||||
|
||||
// Variables to hold the total dimensions based on layout orientation.
|
||||
int totalWidth = 0;
|
||||
int totalHeight = 0;
|
||||
|
||||
// Calculate the total size based on the layout direction and constraints.
|
||||
if (direction == Qt::Horizontal) {
|
||||
// Determine the number of columns:
|
||||
// - Use maxColumns if it is greater than 0 (constraint set by the user).
|
||||
// - Otherwise, set the number of columns to the total number of items in the layout.
|
||||
const int numColumns = (maxColumns > 0) ? static_cast<int>(maxColumns) : static_cast<int>(itemList.size());
|
||||
|
||||
// Calculate the extra space required for overlaps between columns:
|
||||
// - Each overlap reduces the effective width of subsequent items.
|
||||
const int extra_space_for_overlaps = overlapOffsetWidth * (numColumns - 1);
|
||||
|
||||
// Total width:
|
||||
// - The first item's width is fully included (maxItemWidth).
|
||||
// - Add the space for all overlaps between adjacent items.
|
||||
totalWidth = maxItemWidth + extra_space_for_overlaps;
|
||||
|
||||
// Determine the number of rows:
|
||||
// - Use maxRows if maxColumns is set (to constrain the number of rows).
|
||||
// - Otherwise, assume a single row.
|
||||
const int numRows =
|
||||
(maxColumns > 0) ? qCeil(static_cast<double>(itemList.size()) / static_cast<double>(numColumns)) : 1;
|
||||
|
||||
// Total height:
|
||||
// - Multiply the number of rows by the item height (maxItemHeight).
|
||||
// - Subtract the height overlap between rows to avoid double-counting.
|
||||
totalHeight = maxItemHeight * numRows - (numRows - 1) * overlapOffsetHeight;
|
||||
} else if (direction == Qt::Vertical) {
|
||||
// Determine the number of columns:
|
||||
// - Use maxRows to calculate how many columns are needed if a row constraint exists.
|
||||
// - Otherwise, assume a single column.
|
||||
const int numColumns =
|
||||
(maxRows != 0) ? qCeil(static_cast<double>(itemList.size()) / static_cast<double>(maxRows)) : 1;
|
||||
|
||||
// Total width:
|
||||
// - Multiply the number of columns by the item width (maxItemWidth).
|
||||
// - Subtract the width overlap between columns to avoid double-counting.
|
||||
totalWidth = maxItemWidth * numColumns - (numColumns - 1) * overlapOffsetWidth;
|
||||
|
||||
// Determine the number of rows:
|
||||
// - Use maxRows if it is greater than 0 (constraint set by the user).
|
||||
// - Otherwise, set the number of rows to the total number of items in the layout.
|
||||
const int numRows = (maxRows > 0) ? static_cast<int>(maxRows) : static_cast<int>(itemList.size());
|
||||
|
||||
// Calculate the extra space required for overlaps between rows:
|
||||
// - Each overlap reduces the effective height of subsequent items.
|
||||
const int extraSpaceForOverlaps = overlapOffsetHeight * (numRows - 1);
|
||||
|
||||
// Total height:
|
||||
// - The first item's height is fully included (maxItemHeight).
|
||||
// - Add the space for all overlaps between adjacent items.
|
||||
totalHeight = maxItemHeight + extraSpaceForOverlaps;
|
||||
}
|
||||
|
||||
return {totalWidth, totalHeight};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size hint for the layout, based on preferred size calculations.
|
||||
*
|
||||
* Provides a recommended size for the layout, useful for layouts that need to fit within
|
||||
* a specific parent container size. This takes into account the preferred size and
|
||||
* any specific item size requirements.
|
||||
*
|
||||
* @return The layout's recommended QSize.
|
||||
*/
|
||||
QSize OverlapLayout::sizeHint() const
|
||||
{
|
||||
return calculatePreferredSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides the minimum size hint for the layout, ensuring functionality within constraints.
|
||||
*
|
||||
* Defines a minimum workable size for the layout to prevent excessive compression
|
||||
* that could distort item arrangement.
|
||||
*
|
||||
* @return The minimum QSize for this layout.
|
||||
*/
|
||||
QSize OverlapLayout::minimumSize() const
|
||||
{
|
||||
return calculatePreferredSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the layout's orientation direction.
|
||||
*
|
||||
* @param _direction The new orientation direction (Qt::Horizontal or Qt::Vertical).
|
||||
*/
|
||||
void OverlapLayout::setDirection(const Qt::Orientation _direction)
|
||||
{
|
||||
direction = _direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum number of columns for horizontal orientation.
|
||||
*
|
||||
* @param _maxColumns New maximum column count.
|
||||
*/
|
||||
void OverlapLayout::setMaxColumns(const int _maxColumns)
|
||||
{
|
||||
if (_maxColumns >= 0) {
|
||||
maxColumns = _maxColumns;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum number of rows for vertical orientation.
|
||||
*
|
||||
* @param _maxRows New maximum row count.
|
||||
*/
|
||||
void OverlapLayout::setMaxRows(const int _maxRows)
|
||||
{
|
||||
if (_maxRows >= 0) {
|
||||
maxRows = _maxRows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of columns for a vertical overlap layout based on the current width.
|
||||
*
|
||||
* This function determines the maximum number of columns that can fit within the layout's width
|
||||
* given the overlap percentage and item size, based on the current layout direction.
|
||||
*
|
||||
* @return Maximum number of columns that can fit within the layout width.
|
||||
*/
|
||||
int OverlapLayout::calculateMaxColumns() const
|
||||
{
|
||||
if (direction != Qt::Vertical || itemList.isEmpty()) {
|
||||
return 1; // Only relevant if the layout direction is vertical
|
||||
}
|
||||
|
||||
// Determine maximum item width
|
||||
int maxItemWidth = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item == nullptr || !item->widget()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemWidth = qMax(maxItemWidth, itemSize.width());
|
||||
}
|
||||
|
||||
const int availableWidth = parentWidget() ? parentWidget()->width() : 0;
|
||||
// Determine the maximum number of columns that can fit
|
||||
const int columns = availableWidth / maxItemWidth;
|
||||
|
||||
return qMax(1, columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of rows needed for a given number of columns in a vertical overlap layout.
|
||||
*
|
||||
* Determines how many rows are required to arrange all items given the calculated or specified number of columns.
|
||||
*
|
||||
* @param columns The number of columns available.
|
||||
* @return The total number of rows required.
|
||||
*/
|
||||
int OverlapLayout::calculateRowsForColumns(const int columns) const
|
||||
{
|
||||
if (direction != Qt::Vertical || itemList.isEmpty() || columns <= 0) {
|
||||
return 1; // Only relevant if the layout direction is vertical and there are items
|
||||
}
|
||||
|
||||
const int totalItems = static_cast<int>(itemList.size());
|
||||
|
||||
return qCeil(totalItems / columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of rows for a horizontal overlap layout based on the current height.
|
||||
*
|
||||
* This function determines the maximum number of rows that can fit within the layout's height
|
||||
* given the overlap percentage and item size, based on the current layout direction.
|
||||
*
|
||||
* @return Maximum number of rows that can fit within the layout height.
|
||||
*/
|
||||
int OverlapLayout::calculateMaxRows() const
|
||||
{
|
||||
if (direction != Qt::Horizontal || itemList.isEmpty()) {
|
||||
return 1; // Only relevant if the layout direction is horizontal
|
||||
}
|
||||
|
||||
// Determine maximum item height
|
||||
int maxItemHeight = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item == nullptr || !item->widget()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemHeight = qMax(maxItemHeight, itemSize.height());
|
||||
}
|
||||
|
||||
// Calculate the effective height of each item with the overlap applied
|
||||
const int overlapOffsetHeight = (maxItemHeight * (100 - overlapPercentage)) / 100;
|
||||
const int availableHeight = parentWidget() ? parentWidget()->height() : 0;
|
||||
|
||||
// Determine the maximum number of rows that can fit
|
||||
const int rows = availableHeight / overlapOffsetHeight;
|
||||
|
||||
return qMax(1, rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of columns needed for a given number of rows in a horizontal overlap layout.
|
||||
*
|
||||
* Determines how many columns are required to arrange all items given the calculated or specified number of rows.
|
||||
*
|
||||
* @param rows The number of rows available.
|
||||
* @return The total number of columns required.
|
||||
*/
|
||||
int OverlapLayout::calculateColumnsForRows(const int rows) const
|
||||
{
|
||||
if (direction != Qt::Horizontal || itemList.isEmpty() || rows <= 0) {
|
||||
return 1; // Only relevant if the layout direction is horizontal and there are items
|
||||
}
|
||||
|
||||
const int totalItems = static_cast<int>(itemList.size());
|
||||
|
||||
return qCeil(totalItems / rows);
|
||||
}
|
||||
44
cockatrice/src/client/ui/layouts/overlap_layout.h
Normal file
44
cockatrice/src/client/ui/layouts/overlap_layout.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef OVERLAP_LAYOUT_H
|
||||
#define OVERLAP_LAYOUT_H
|
||||
|
||||
#include <QLayout>
|
||||
#include <QList>
|
||||
#include <QWidget>
|
||||
|
||||
class OverlapLayout : public QLayout
|
||||
{
|
||||
public:
|
||||
OverlapLayout(QWidget *parent = nullptr,
|
||||
int overlapPercentage = 10,
|
||||
int maxColumns = 2,
|
||||
int maxRows = 2,
|
||||
Qt::Orientation direction = Qt::Horizontal);
|
||||
~OverlapLayout();
|
||||
|
||||
void addItem(QLayoutItem *item) override;
|
||||
int count() const override;
|
||||
QLayoutItem *itemAt(int index) const override;
|
||||
QLayoutItem *takeAt(int index) override;
|
||||
void setGeometry(const QRect &rect) override;
|
||||
QSize minimumSize() const override;
|
||||
QSize sizeHint() const override;
|
||||
void setMaxColumns(int _maxColumns);
|
||||
void setMaxRows(int _maxRows);
|
||||
int calculateMaxColumns() const;
|
||||
int calculateRowsForColumns(int columns) const;
|
||||
int calculateMaxRows() const;
|
||||
int calculateColumnsForRows(int rows) const;
|
||||
void setDirection(Qt::Orientation _direction);
|
||||
|
||||
private:
|
||||
QList<QLayoutItem *> itemList;
|
||||
int overlapPercentage;
|
||||
int maxColumns;
|
||||
int maxRows;
|
||||
Qt::Orientation direction;
|
||||
|
||||
// Calculate the preferred size of the layout
|
||||
QSize calculatePreferredSize() const;
|
||||
};
|
||||
|
||||
#endif // OVERLAP_LAYOUT_H
|
||||
144
cockatrice/src/client/ui/layouts/vertical_flow_layout.cpp
Normal file
144
cockatrice/src/client/ui/layouts/vertical_flow_layout.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "vertical_flow_layout.h"
|
||||
|
||||
/**
|
||||
* @brief Constructs a VerticalFlowLayout instance with the specified parent widget.
|
||||
* This layout arranges items in rows within the given width, automatically adjusting its height.
|
||||
* @param parent The parent widget to which this layout belongs.
|
||||
* @param margin The layout margin.
|
||||
* @param hSpacing The horizontal spacing between items.
|
||||
* @param vSpacing The vertical spacing between items.
|
||||
*/
|
||||
VerticalFlowLayout::VerticalFlowLayout(QWidget *parent, const int margin, const int hSpacing, const int vSpacing)
|
||||
: FlowLayout(parent, margin, hSpacing, vSpacing)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for VerticalFlowLayout, responsible for cleaning up layout items.
|
||||
*/
|
||||
VerticalFlowLayout::~VerticalFlowLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = FlowLayout::takeAt(0))) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the required height to display all items, given a specified width.
|
||||
* This method arranges items into rows and determines the total height needed.
|
||||
* @param width The available width for arranging layout items.
|
||||
* @return The total height required to fit all items, organized in rows constrained by the given width.
|
||||
*/
|
||||
int VerticalFlowLayout::heightForWidth(const int width) const
|
||||
{
|
||||
int height = 0;
|
||||
int rowWidth = 0;
|
||||
int rowHeight = 0;
|
||||
|
||||
for (const QLayoutItem *item : items) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int itemWidth = item->sizeHint().width() + horizontalSpacing();
|
||||
if (rowWidth + itemWidth > width) {
|
||||
height += rowHeight + verticalSpacing();
|
||||
rowWidth = itemWidth;
|
||||
rowHeight = item->sizeHint().height();
|
||||
} else {
|
||||
rowWidth += itemWidth;
|
||||
rowHeight = qMax(rowHeight, item->sizeHint().height());
|
||||
}
|
||||
}
|
||||
height += rowHeight; // Add height of the last row
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the geometry of the layout items, arranging them in rows within the given width.
|
||||
* @param rect The rectangle area defining the layout space.
|
||||
*/
|
||||
void VerticalFlowLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
// If we have a parent scroll area, we're clamped to that, else we use our own rectangle.
|
||||
const int availableWidth = getParentScrollAreaWidth() == 0 ? rect.width() : getParentScrollAreaWidth();
|
||||
|
||||
const int totalHeight = layoutAllRows(rect.x(), rect.y(), availableWidth);
|
||||
|
||||
if (QWidget *parentWidgetPtr = parentWidget()) {
|
||||
parentWidgetPtr->setMinimumSize(availableWidth, totalHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lays out items into rows according to the available width, starting from a given origin.
|
||||
* Each row is arranged within `availableWidth`, wrapping to a new row as necessary.
|
||||
* @param originX The x-coordinate for the layout start position.
|
||||
* @param originY The y-coordinate for the layout start position.
|
||||
* @param availableWidth The width within which each row is constrained.
|
||||
* @return The total height after arranging all rows.
|
||||
*/
|
||||
int VerticalFlowLayout::layoutAllRows(const int originX, const int originY, const int availableWidth)
|
||||
{
|
||||
QVector<QLayoutItem *> rowItems; // Holds items for the current row
|
||||
int currentXPosition = originX; // Tracks the x-coordinate while placing items
|
||||
int currentYPosition = originY; // Tracks the y-coordinate, moving down after each row
|
||||
|
||||
int rowHeight = 0; // Tracks the maximum height of items in the current row
|
||||
|
||||
for (QLayoutItem *item : items) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->sizeHint(); // The suggested size for the current item
|
||||
int itemWidth = itemSize.width() + horizontalSpacing(); // Item width plus spacing
|
||||
|
||||
// Check if the current item fits in the remaining width of the current row
|
||||
if (currentXPosition + itemWidth > availableWidth) {
|
||||
// If not, layout the current row and start a new row
|
||||
layoutSingleRow(rowItems, originX, currentYPosition);
|
||||
rowItems.clear(); // Reset the list for the new row
|
||||
currentXPosition = originX; // Reset x-position to the row's start
|
||||
currentYPosition += rowHeight + verticalSpacing(); // Move y-position down to the next row
|
||||
rowHeight = 0; // Reset row height for the new row
|
||||
}
|
||||
|
||||
// Add the item to the current row
|
||||
rowItems.append(item);
|
||||
rowHeight = qMax(rowHeight, itemSize.height()); // Update the row's height to the tallest item
|
||||
currentXPosition += itemWidth + horizontalSpacing(); // Move x-position for the next item
|
||||
}
|
||||
|
||||
// Layout the final row if there are any remaining items
|
||||
layoutSingleRow(rowItems, originX, currentYPosition);
|
||||
|
||||
// Return the total height used, including the last row's height
|
||||
return currentYPosition + rowHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arranges a single row of items within specified x and y starting positions.
|
||||
* @param rowItems A list of items to be arranged in the row.
|
||||
* @param x The starting x-coordinate for the row.
|
||||
* @param y The starting y-coordinate for the row.
|
||||
*/
|
||||
void VerticalFlowLayout::layoutSingleRow(const QVector<QLayoutItem *> &rowItems, int x, const int y)
|
||||
{
|
||||
for (QLayoutItem *item : rowItems) {
|
||||
if (item == nullptr || item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the maximum allowed size for the item
|
||||
QSize itemMaxSize = item->widget()->maximumSize();
|
||||
// Constrain the item's width and height to its size hint or maximum size
|
||||
const int itemWidth = qMin(item->sizeHint().width(), itemMaxSize.width());
|
||||
const int itemHeight = qMin(item->sizeHint().height(), itemMaxSize.height());
|
||||
// Set the item's geometry based on the computed size and position
|
||||
item->setGeometry(QRect(QPoint(x, y), QSize(itemWidth, itemHeight)));
|
||||
// Move the x-position to the right, leaving space for horizontal spacing
|
||||
x += itemWidth + horizontalSpacing();
|
||||
}
|
||||
}
|
||||
19
cockatrice/src/client/ui/layouts/vertical_flow_layout.h
Normal file
19
cockatrice/src/client/ui/layouts/vertical_flow_layout.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef VERTICAL_FLOW_LAYOUT_H
|
||||
#define VERTICAL_FLOW_LAYOUT_H
|
||||
|
||||
#include "flow_layout.h"
|
||||
|
||||
class VerticalFlowLayout : public FlowLayout
|
||||
{
|
||||
public:
|
||||
explicit VerticalFlowLayout(QWidget *parent = nullptr, int margin = 0, int hSpacing = 0, int vSpacing = 0);
|
||||
~VerticalFlowLayout() override;
|
||||
|
||||
[[nodiscard]] int heightForWidth(int width) const override;
|
||||
|
||||
void setGeometry(const QRect &rect) override;
|
||||
int layoutAllRows(int originX, int originY, int availableWidth) override;
|
||||
void layoutSingleRow(const QVector<QLayoutItem *> &rowItems, int x, int y) override;
|
||||
};
|
||||
|
||||
#endif // VERTICAL_FLOW_LAYOUT_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "lineeditcompleter.h"
|
||||
#include "line_edit_completer.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QCompleter>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user