Commit graph

6629 commits

Author SHA1 Message Date
tooomm
3931447729
[Build] Clean up CMake compiler flags and CI build script (#7219)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 45 release (push) Blocked by required conditions
Build Desktop / Fedora 45 debug (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
Compiler flags are applied once and for the right compiler:

- Detect the toolchain by CMAKE_CXX_COMPILER_ID (MSVC / GNU / Clang,
  warn on an unknown one).
- MSVC: drop /std:c++20 (CMAKE_CXX_STANDARD already adds it) and the
  outdated C4251 suppression, add /utf-8, and use /O2 instead of /Ox.
- GCC: drop the no-op -Wno-long-long and the macOS-only -std=gnu++20
  override; CMAKE_CXX_EXTENSIONS=OFF now owns the dialect.
- Use add_compile_definitions for SFMT_MEXP instead of add_definitions.

CI build script:

- Only run the schema version check for server builds.
- Accept boolean env flags as 1 and say so in the header.
- Print ccache, ctest and cpack versions next to their log groups.
- Only build the macOS vcpkg triplet when vcpkg is actually used.
- Drop the extra cmake --build verbosity.
- Install NSIS without chocolatey progress noise.

Co-authored-by: toomm <toomm@users.noreply.github.com>
2026-11-07 18:37:09 +01:00
tooomm
d7d1872638
[CI] Install only the Qt archives the desktop builds use (#7024)
The online installer ships the base package as ~25 archives; aqt installs all
of them unless `--archives` names the ones wanted. Restricting them to what
cmake/FindQtRuntime.cmake requires cuts the Windows Qt cache from 440 MB to
180 MB and the macOS thin Qt download from >200 MB to 60 MB.

The list lives once, in a workflow-level QT_ARCHIVES variable, because both
install steps consume it and the macOS thin-Qt cache key has to change with
it. The addons the build also needs (Multimedia, WebSockets, ShaderTools,
ImageFormats) are not base archives and stay in each leg's qt_modules;
qtdeclarative is required on Windows too, since the onboarding banner links
Qt6::Quick/QuickWidgets.
2026-11-07 18:34:33 +01:00
tooomm
0e9ba72924
[Docs] Move documentation sources to docs/ and include CONTRIBUTING (#7584)
Rename doc/ to docs/ so GitHub picks up docs/CONTRIBUTING.md and Doxygen keeps including it, and update all references to the old layout:

- Doxygen output now goes to docs-output/, exposed by .gitignore
- docs workflow triggers on docs/** and publishes docs-output/html
- theme submodule moved to docs/doxygen/theme
- schema URLs in both XML parsers point at docs/xsd/
- README, developer docs, desktop workflow filters, .dockerignore and gcovr config updated for the new paths

The v3 card database schema is kept as docs/xsd/cards_v3.xsd.
2026-11-07 17:47:10 +01:00
Mmonge123
257a584971
fix: keep only the roll-die shortcut active in the lobby (#6754)
Player menu shortcuts were bound as soon as a local player was added in the deck-selection lobby. Defer full activation until GameMetaInfo reports the game has started, and bind only Player/aRollDie while in the lobby. Counters follow the deactivation signal, so they cannot re-enable shortcuts before the game starts.
2026-11-07 17:38:33 +01:00
scotland0208
d9cd308a2e
Show the library edge on the card in limited deck views (#6741) (#6741)
Limited deck views (view top/bottom N cards, scry, surveil) made it
ambiguous which card was the top or bottom of the library. Label the card
itself with a small in-view marker instead of placing text above or below
the whole zone, which became ambiguous once the grid used several columns
or the window was resized.

The marker stays on the part of its card that the column leaves visible:
for a top view, the right end of the exposed strip of the first card; for
a bottom view, the bottom-left corner of the last, fully visible card.

The view logic now answers whether it is a limited library-end view and
which edge it shows. The graphics zone draws and positions a marker on
the first or last displayed card, and re-translates its tooltip with the
rest of the zone view.

Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
2026-11-07 17:24:40 +01:00
unf0rce
80986266b9
Fix orphaned arrows when the draw shortcut repeats (#6789)
Holding or spamming the draw-arrow shortcut fires the action again
before the previous local arrow has been placed. Every new arrow takes
the mouse grab from the previous one, so the previous arrow never
receives the release that deletes it. If the cursor had passed over a
card meanwhile, the orphan stayed visible on the board.

Track the in-progress arrow on the CardItem that started it and cancel
it, together with any child arrows, before starting a new one. The
attach-arrow shortcut (Ctrl+Alt+A) uses the same path, so it no longer
leaks arrows either.

Fixes #4329.

Co-authored-by: mb <mbaecker@unforce.de>
2026-11-07 16:53:53 +01:00
sentry[bot]
01572bfce2
[VisualDeckStorage] Replace QFutureWatcher tracking with QFuture continuations (#7691)
* [VisualDeckStorage] Cancel in-flight deck loads and enforce teardown order on destruction

* [VisualDeckStorage] Reformat activeWatchers comment

* [VisualDeckStorage] Use QFuture continuations instead of QFutureWatcher tracking

Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>

---------

Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
Co-authored-by: rickyrister <42636155+rickyrister@users.noreply.github.com>
2026-11-07 16:51:27 +01:00
ebbit1q
4b224f7fe5
Translate the filter string error at display time (#5283)
Keep the parser message untranslated so FilterStringLog stays in the source
language, and format the user-facing text in error() instead. That gives both
consumers what they need without localizing the log.

Q_DECLARE_TR_FUNCTIONS provides the tr() context without deriving from QObject,
which would have deleted copy/move assignment and broken the by-value uses in
PlayerActions and ViewZone. Drop the unreachable line-number branch; peglib
reports 1-based lines, so every message keeps master's "position" wording.

Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
2026-11-07 16:26:31 +01:00
BruebachL
7487a383a3
[CI] Prune all but the newest CodeQL trap cache (#7796)
The CodeQL job keeps one extracted database per analyzed commit, about 1.07 GB
each, restores the newest by prefix and never deletes any. Four had accumulated
by 2026-11-07: 4.26 GB, 40% of the repository's 10 GB cache quota, of which only
the newest can be restored again.

prune-caches skipped them deliberately, because their key shape belongs to
codeql-action rather than to a workflow of ours. Add that shape and keep exactly
the entry with the latest access date on master; a miss costs one re-extraction
in a job that reports rather than gates.

This is the room the Windows compiler cache needs: with the superseded entries
retired the quota holds 7.1 GB instead of 10.3 GB.
2026-11-07 13:56:38 +01:00
BruebachL
c6fadef000
[CI] Stop running ccache on macOS (#7792)
Every macOS leg restores nothing (persist_ccache is false), so ccache only
paid hashing on ~1160 compiles for the 3% of calls that repeat within one
build: macOS 15 measured 37 hits out of 1163. The restored cache this
replaces served 42% and saved about 32s on a 20 min leg.

Drop the three matrix keys, the brew install, and the restore/delete/save
steps, and leave the Linux docker path untouched.
2026-11-07 13:54:10 +01:00
RickyRister
eab217d1bf
[DlgCreateGame] Revert dlg to use exec instead of open (#7795) 2026-11-07 13:44:40 +01:00
BruebachL
d7dac8395b
[CI] Spend the ccache quota on the legs that still thrash (#7781)
* [CI] Spend the ccache quota on the legs that still thrash

Arch and Debian 13 were the two bounds that still evicted entries they
needed: on master 2026-11-06 Arch served 75% of calls at 96% full and
Debian 13 82% at 96%, while Fedora 45 debug/release served 99.5% at 80%.
Their working sets measure 820 MB and 770 MB, so 850M and 800M could not
hold one configuration.

Raise Arch to 1200M and Debian 13 to 1050M, and drop the macOS 14 cache:
its 800M bound served 42% and saved about 32s on a 20 min leg that is
already shorter than the Windows leg. That frees 800M and leaves the
bounds at 3.9 GB, with the measured quota at about 7.8 GB of 10.

Also document why pull requests, tags and nightlies never write: a PR
save would evict the warm set, tags build full DWARF, and a schedule
shares refs/heads/master with the push. No behavior change there.

* [CI] Correct the quota estimate in the ccache sizing comment

The 7.8 GB figure subtracted the dropped macOS bound without adding the
600 MB Arch/Debian increase. The fixed entries are about 4.4 GB, so
3.9 GB of bounds puts the estimate at about 8.3 GB of 10.

* [CI] Size the Arch and Debian bounds to the measured working set

* [CI] Keep the measured ccache bounds instead of raising them

Steady-state master shows Arch at 90.1% and Debian 13 at 96.75% per-run
hits on the existing 850M/800M bounds. The 75%/82% that motivated the
increase were warming runs right after the -g1 switch invalidated the
cache, not thrashing. Keep the bounds, keep the macOS 14 cache drop.
2026-11-07 13:02:27 +01:00
BruebachL
818c0161ed
[CI] Run the sanitizer leg nightly and stop caching it (#7780)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 45 release (push) Blocked by required conditions
Build Desktop / Fedora 45 debug (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
The Ubuntu 26.04 sanitize leg was the largest single cache entry in the 10 GB
quota (1.2G) and is not on the pull-request critical path: Windows sets that
wall clock. Moving it to build-linux-nightly drops the cache, since nightly
legs persist none, and frees the quota for the legs whose bounds still miss.

Measured on master 2026-11-06, two runs: Fedora 45 debug/release served 99.5%
of cacheable calls at 80% full, while Arch served 75% at 96% full, Debian 13
82% at 96% and macOS 14 42% at 99.96%. The sanitize cache was the largest of
the six and is now nightly-only.

Pull requests keep the uninstrumented suite on Debian 13 (Release) and Fedora
45 (Debug), so the only thing a PR loses is the instrumented run, which now
happens nightly and on tags. The leg keeps a 1.2G scratch ccache so the larger
instrumented objects do not thrash mid-build, and it is allow-failure so a
sanitizer finding cannot stop vcpkg-cache-import from importing the nightly
run's archives.

prune-caches reclaims ccache-Ubuntu26.04sanitize on the next master push.
2026-11-07 09:31:27 +01:00
tuckerandrew21
9fdbe45ffb
[PictureLoader] Fix malformed MIME wildcards in Accept header (#6609)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 45 release (push) Blocked by required conditions
Build Desktop / Fedora 45 debug (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
The Accept header added in ef87b54b was missing asterisks in two
MIME type wildcards: "image/" and "/*" instead of "image/*" and
"*/*". Servers may reject or mishandle requests with invalid MIME
types, preventing card images from loading.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-11-06 23:19:02 +01:00
tooomm
dec5e217a2
Remove leading spaces from message log zone strings (#4945)
The "from …" fragments built by getFromStr() carried a leading space so
the sentence templates could concatenate them directly (%2%3). That blank
is invisible on Transifex and most translations dropped it, merging the
card name and the zone phrase in the log (#4943).

Drop the blank from the tr() strings and add the separator in code where
the fragment is built. The space is only prepended when the fragment is
non-empty, so messages that put the zone in the card name instead — "the
top card of their library." — keep their punctuation tight.

The templates stay at %2%3, since %3 is empty on those paths.
2026-11-06 21:43:14 +01:00
tooomm
0798d28dc9
UI: Settings dialog layout fixes (#4531)
- Span chat and alert-word checkboxes across the full grid width
- Add right-aligned Color labels and HEX placeholders to the mention,
  highlight and channel color fields
- Reword the horizontal hand option to "requires more space"
2026-11-06 21:38:47 +01:00
tooomm
2d625575f6
[Translations] Give translators context for separator-bearing tr() strings (#6487)
Build the card menu's related card actions from complete strings with %1
placeholders instead of appending translated fragments. This also fixes
tr(cond ? "Transform into " : "Attach to "), which lupdate cannot extract:
neither string appears in cockatrice_en@source.ts, so neither was ever
translated.

Give the EDHRec tab title a %1 placeholder so French can render
"EDHRec : %1" instead of concatenating into "EDHRec :CardName".

Keep the colon in tr("Token:") and the leading space in tr(" seconds"),
since that separator is language-specific, and attach //: translator
comments so they are no longer shown out of context on Transifex.
2026-11-06 21:12:07 +01:00
DawnFire42
17f583013b
[CardMenu] Free the card menus and submenus rebuilt on each right-click (#7093)
* Parent card menu submenus to the menu that owns them

QMenu::addMenu(QMenu *) does not take ownership of the submenu, and these
submenus were created without a parent. Since CardMenu is rebuilt from
scratch on every right-click, each rebuild leaked the MoveMenu, PtMenu,
card counters and related-cards submenus.

Pass the owning CardMenu as the QWidget parent so Qt's parent-child
mechanism frees them with it.

* Delete the replaced card menu in TabGame to fix menu rebuild leak

PlayerMenu creates a new CardMenu on every right-click and TabGame installs
it with QAction::setMenu(), which does not take ownership, so the previous
menu was never freed. TabGame now deletes the menu it replaces and reuses a
single empty placeholder menu instead of allocating one per update. The
installed menu is also released when the tab is destroyed.
2026-11-06 20:51:00 +01:00
BruebachL
daa8671b4d
[Update] Make the update download atomic and its temp path unpredictable (#7627)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 45 release (push) Blocked by required conditions
Build Desktop / Fedora 45 debug (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
The downloader wrote to a predictable /tmp/<url-basename> path, ignored the
result of write(), and leaked the previous reply when a check was retried. It
also connected no error handler to the reply and had no transfer timeout.

Create the file inside a private QTemporaryDir, write it with QSaveFile and
commit it only after every byte was accepted, report reply errors, give the
request a 30 s transfer timeout, and delete the superseded reply. Ignore a
queued finished()/errorOccurred() from a reply that has already been replaced,
and drop the manual redirect branch because Qt follows redirects by default.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-06 10:47:47 +01:00
BruebachL
06e3d96308
[CI] Size compiler caches for one configuration and split the Fedora leg (#7772)
The bounds in #7667 were set below what one build configuration writes, so
ccache filled to its limit and evicted the entries the next build was about to
ask for. The probe measured what that costs: a 6G cache on one leg served 1059
of 1059 calls and the 700M bound on the same leg served 14%. The quota also
went to legs that finish under the Windows leg, which has no compiler cache.

Debug information is most of an object's size. A measured Arch Debug
configuration is 1.2 GB with the built-in flags and 660 MB at -g1; Debian 13
Release is 1.1 GB and 614 MB. CI therefore builds every non-tag leg at level 1
through a new COCKATRICE_DEBUG_INFO_LEVEL option, which stays empty by default
so local, distro and release builds are unchanged. Level 1 keeps file and line,
which is what a failing test or a sanitizer backtrace reports. Tags and the
nightly legs keep full DWARF for the Sentry symbols.

With one configuration at half the size, the quota goes to fewer legs at a size
that holds one whole configuration, and the legs that duplicate another leg's
coverage give their cache up: Servatrice, Ubuntu 26.04 behind the sanitize leg
of the same distro, macOS 15 and macOS 27 Debug behind macOS 14. Their keys
leave LIVE_CCACHE_KEYS, so prune-caches reclaims the existing entries.

The single Fedora leg built Debug for the suite and Release for the package in
one job (measured on Fedora 44: 30.8 min, the only leg above Windows), so the
package compile queued behind the debug one while one 1.6 GB cache held two
configurations. Fedora 45 now runs as two legs with one cache each, in
parallel.

The option reaches compile.sh inside the container through docker.sh, and
compile.sh turns it into the CMake flag.

Bounds: Arch 850M, Debian 13 800M, Fedora 45 debug 850M and release 800M,
Ubuntu 26.04 sanitize 1200M, macOS 14 800M. That is 5.3 GB of compiler cache
on top of roughly 4.5 GB of buildkit, vcpkg, Qt and CodeQL entries.

Windows still sets the pull-request wall clock: 29.5 min, 23.7 of it compiling
with no compiler cache, because ccache and sccache cannot store the 711 of 1054
translation units that use PCH under MSVC (#7381). That is a separate trial.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-06 10:41:53 +01:00
SlightlyCircuitous
3339cc9d72
Add Fedora 45 build and drop Fedora 43 build (#7774)
* Create Dockerfile

* Delete .ci/Fedora43 directory

* Update release_template.md

* Update desktop-build.yml
2026-11-06 09:04:50 +01:00
Jurriaan
74686d1736
[Client] Added an exclude and color identity filter for the visual database display (#7587)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [Client] Add exclude and color identity filters to the visual database display

* [Filters] Extract the color filter mode derivation into libcockatrice_filters

The mode table and the 'which mode produced this filter set' derivation are
filter semantics, not widget code, so they belong next to CardFilter rather
than in an anonymous namespace in the client. Moving them also makes them
testable: filter_mode_test pins every mode, the Custom fallback and the
'keep the live mode when it still fits' rule.

* [Tests] Cover the negated color identity query

ci! read the camelCase colorIdentity property instead of the lowercase
coloridentity key, so it matched the empty string and rejected every card.
The new case uses a card whose colors and color identity differ, so it fails
on the old lookup and passes on the fixed one.

---------

Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
2026-11-05 12:40:32 +01:00
sentry[bot]
64c7809332
[DlgCreateGame] Heap-allocate create game dialog to prevent crash on parent deletion (#7777)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [DlgCreateGame] Heap-allocate create game dialog to prevent crash on parent deletion

* [Tests] Run the short-write cases without forking a death test

The three cases that force a write failure - CardDatabaseTest.Xml4ShortWriteKeepsInstalledFile,
OracleDatabaseWriter.ShortWriteKeepsPreviouslyInstalledDatabase and
SpoilerFileTest.ShortWriteLeavesExistingFileIntact - all forked a child, lowered
RLIMIT_FSIZE and ignored SIGXFSZ. That child is killed by SIGSEGV on the macOS
runners, so all three have been red on every macOS leg of master since they landed,
while the same cases pass on Linux.

Cap the soft limit in the test process instead: ctest already runs one case per
process, and the guard restores the limit and the SIGXFSZ handler, so a whole binary
keeps working. Same real EFBIG failure, no fork, and macOS keeps its coverage of the
writers.

* [CI] Derive DEBUG_TEST and RUN_TESTS in the nightly job

The shared steps gate on both, but build-linux-nightly never set them, so its
coverage leg skipped "Build debug and test" and then failed collecting a report
from an empty build/ directory on every nightly and tag run. continue-on-error
kept the run green, so the coverage artifact silently never existed.

Derive both exactly as the pull request job does.

---------

Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
2026-11-05 07:49:20 +01:00
BruebachL
df74917cb0
[PrintingSelector] Load the pin badge through the themed icon engine (#7598)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
The badge rendered `theme:icons/pin` through the pixmap generator, which
resolved `currentColor` once and cached the result, so the glyph stayed at its
first palette and was rasterized at DPR 1.

- Render the glyph through `loadThemedIcon`, at the label's device pixel ratio,
  and re-render it on palette and theme changes.
- Compose it onto a rounded backdrop filled with `Window` and outlined with
  `WindowText`. The badge floats on card art rather than on a palette surface,
  so a bare outline vanishes into art of the same lightness; the backdrop keeps
  it readable on every printing, and both roles still follow the palette.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:39:43 +01:00
BruebachL
ada0cccfbf
[Game] Follow the palette in judge message tags (#7597)
* [Settings] Delete update channels at shutdown

SettingsCache owns the stable and beta update channels as raw pointers, and
each owns a QNetworkAccessManager, so the process leaves QtNetwork objects
alive past QApplication teardown.

Delete them from a QCoreApplication::aboutToQuit handler instead of the
destructor: SettingsCache is a Q_GLOBAL_STATIC, so ~SettingsCache() runs
during static teardown, after QApplication is gone, and destroying the
network managers at that point is the post-application window that ASan
reports. The hook is installed on the first instance() call that has an
application object, since the first access may predate it.

* [Game] Follow the palette in judge message tags

Judge tags hardcoded a black prefix and a `theme:icons/scales` image inside rich
text, where `currentColor` cannot resolve and a palette change leaves the tag
unreadable. Render the scales glyph through the themed icon engine, register it
as a document image resource, and repaint the log when the palette or theme
changes.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:39:42 +01:00
BruebachL
e22a75974a
[Icons] Add an icon asset ratchet test (#7596)
* [Icons] Add an icon asset ratchet test

The icon rules were enforced by review only, so the next Inkscape export could
reintroduce a baked fill or a physical size without anyone noticing.

Add a test that rejects non-24 viewBoxes, physical sizes, editor metadata,
hardcoded paints, missing tokens, color-coded filenames, and rasters. Keep the
legacy baseline empty so the ratchet can only tighten.

* [Tests] Drop the empty icon baseline

The legacy icon set is fully paid down, so the ratchet had nothing left to
ratchet: BaselineIsWellFormed required the baseline to be empty, which made
the baseline file, the stale-entry diff and the entry-format regex
unreachable. The rules are enforced directly now.

- AssetsFollowTheRules fails on any violation of collectViolations().
- tests/icon_assets/icon-baseline.txt is gone with its reader.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:39:42 +01:00
BruebachL
627c7a83aa
[Icons] Migrate UI glyphs to palette-token SVGs (#7595)
* [Icons] Migrate UI glyphs to palette-token SVGs

Runtime inversion is not a theming system: it rotates hue, breaks duotone art
and mishandles alpha, and a QPixmap snapshot cannot follow a palette change.
Redraw the top-level UI glyph set on a 24x24 grid with `currentColor` as the
paint token, drop Inkscape/Sodipodi cruft, and delete the icons whose metaphor
was duplicated (cogwheel/gear -> settings, scale_balanced -> scales,
update.png -> reload).

Load the glyphs through `loadThemedIcon` so the engine resolves the token from
the palette on every request, and let `themePixmap` use the same resolution for
one-shot pixmaps. `loadColorAdjustedPixmap` is gone; every caller now keeps a
QIcon, which `SettingsButtonWidget::setButtonIcon` accepts instead of a QPixmap.

Content art (mana, counters, countries) keeps its authored colors.

Add a render test covering aspect ratio, palette flips, icon modes and
high-DPI pixmaps.

* [Tests] Keep the icon render test QApplication alive until exit

Creating it as a function-local static tears it down before Qt's library statics
at exit, and libQt6Network then runs QObject destructors against freed memory —
a SEGV the sanitizer leg reports after the tests have already passed. Leak the
application instead, the usual pattern for a test binary.

* [Icons] Render lock and dropdown glyphs through the icon engine

Both generators rendered the sentinel SVG to a fixed height x height pixmap
at DPR 1. The pre-migration path rendered larger and let QIcon scale it, so
on a 2x display the games-list lock (13px) and the banner dropdown arrow
(24px) came back crisp; the new path hands the widget a DPR-1 bitmap that
gets upscaled and blurred.

- The games list asks the themed engine for `icons/lock` directly, so the
  item view paints it at the device pixel ratio and the lock also follows
  HighlightedText on a selected row.
- The dropdown goes through `loadThemedIcon(...).pixmap(size, dpr)`, which
  keeps the glyph's aspect ratio and renders at the requested ratio.
- LockPixmapGenerator has no consumer left, so it and its cache are gone.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:39:41 +01:00
BruebachL
020d9e8dcb
[Icons] Add palette-aware themed icon engine (#7594)
A QPixmap snapshot cannot follow palette changes, so expose a QIcon whose
engine resolves the `currentColor` sentinel from the live palette on every
pixmap request. Icons stay legible across light/dark switches without
recreating actions or buttons.

- Resolve the asset, its bytes and its intrinsic size once, and drop them on
  ThemeManager::themeChanged/paletteChanged. A repaint does no file I/O and
  no theme.cfg parse; the palette color stays part of the render cache key.
- Centre the glyph on QRectF(rect).center(): QRect::center() floors and uses
  right = left + width - 1, which shifted every 16x16 item-view glyph up and
  left by a logical pixel and clipped its last row and column.
- Take a raster override's intrinsic size from QImageReader instead of the
  24x24 SVG default, so a non-square override reports the box it draws.
- Set the device pixel ratio before inserting into the render cache, so a
  cache hit no longer detaches the pixmap on every paint.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:39:41 +01:00
BruebachL
b04d8b88ac
[Icons] Add SVG currentColor sentinel resolver (#7593)
Qt's SVG renderer has no CSS cascade, so a `currentColor` fill has to be
substituted before the bytes reach QSvgRenderer.

- Match and replace on bytes: both the sentinel and the replacement are
  ASCII, so the document never goes through a UTF-16 round trip, and files
  that are not valid UTF-8 survive unchanged.
- Document that the match is a plain case-insensitive substring, so an
  identifier that merely starts with the sentinel gets rewritten too.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:39:40 +01:00
BruebachL
7b4827dadd
[Replay] Show the history sidebar in replay tabs (#7613)
Add the Game History dock to the left of replay tabs, wire it to the replay timeline, and register it with the View menu and layout reset.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:26:51 +01:00
BruebachL
fc27cb7a59
[Replay] Add a clickable replay history sidebar (#7612)
Render the event index as a scrollable list of event boxes. Selecting an entry seeks the replay to that event and keeps the current event highlighted while playback advances.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:26:50 +01:00
BruebachL
a23f0b43a6
[Replay] Build a compact event history index (#7611)
Turn replay protocol events into short localized summaries with an actor, badge, and time so the history UI can render them without retaining the replay or duplicating the chat log.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:26:50 +01:00
Bryce!
0de92c06f8
[Spoilers] Preserve spoiler files through update and reload failures (#7707)
* Preserve spoiler files and serialize update reloads

* Surface spoiler removal reload failures and allow disabled-state retry

* Address review feedback on spoiler update lifecycle

Only remove the spoiler file when the user unchecks the setting; opening
Settings no longer queues an unrequested reload for a leftover file.
Remove the spoilersUpdatedSuccessfully signal, which had no receiver.

* Fix spoiler settings state after reopen and while disabled

Reopen Settings while an application-owned update is running: the new page
now observes that updater, so the button reads Updating... and re-enables
on completion instead of offering a second update that cancels the first.
Observation does not take ownership: only updaters the page creates are
retired with deleteLater, because the launch check is stack-allocated.

When spoilers are off, the hint now describes what Reload Card Database
actually does, stays readable, and wraps instead of clipping.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 18:24:19 +01:00
Bryce!
3a656c9c4a
Preserve installed card databases when Oracle refresh fails (#7739)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 27 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* Preserve installed card databases when Oracle refresh fails

* Fix the Oracle refresh verdict and cover the parser write path

A background refresh that already installed cards.xml exited 1 when the
token save afterwards failed, so the client kept its old in-memory
database and reported the update as failed. Return the same verdict
main() already computes: a reject after the database was written is a
successful update.

Cover the writer Oracle actually uses: add a short-write death test for
CockatriceXml4Parser::saveToFile, which reverted to QFile would truncate
an installed database without any test noticing.

Route a failed background token download through reject() instead of a
modal box on a hidden wizard, which left the client waiting with no
timeout and blocked later updates. Test the first non-space byte in
place rather than copying the payload, and report an import refusal with
the importer's own message.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 17:33:49 +01:00
Bryce!
e558c0dffa
[Banner] Keep horizontal user banner crops inside the artwork (#7742) 2026-11-04 17:08:54 +01:00
Bryce!
d944273335
[Home] Keep actions bounded and reachable at every window height (#7747) 2026-11-04 17:06:58 +01:00
BruebachL
5ea16c805f
[Server/Client/Protocol] Shuffle or reorder player seats before a game starts (#7398)
* [Protocol] Add shuffle players option and player order command/event

Adds a 'Shuffle players at start' option to game creation plus a local
player id on the game state so clients can reconcile seat changes, and a
SET_PLAYER_ORDER command (1206) with a PLAYER_ORDER_CHANGED event (2023)
so the game host can set or randomize the seating order.

Took 6 minutes

* [Server] Shuffle and reorder player seats before a game starts

Auto-shuffles the seats at game start when enabled (now for games with
two or more players instead of three or more) and handles the new
SET_PLAYER_ORDER command so the game host or a moderator can reorder or
randomize the seats before the game starts. Re-seating re-keys the
player ids while keeping the per-user game/seat routing and persistent
player records in sync, then broadcasts the new order to everyone.

* [Client] Add shuffle players option and turn order dialog

Adds the 'Shuffle players at start' checkbox to game creation and a
host-only 'Set turn order...' action that opens a dialog to reorder or
randomize the seats before the game starts. Seating changes are shown in
the game's message log.

* [Client] Move the turn order dialog strings into retranslateUi

Follows the DlgInviteToGame convention so the dialog can be retranslated
instead of freezing its strings at construction time.

* [Server] Keep player ids stable across seat shuffles

* [Client] Lay out table and turn order dialog in seat order

* [Server] Send game-joined player list in seat order

* [Client] Reword the shuffle option to match its behaviour

* [Client] Address the seat-order review comments

- Give the game setup checkboxes a row of their own so they no longer
  collide in the create-game dialog
- Rename DlgTurnOrder::randomize() to isRandomizeRequested()
- Document what the ids in GameState::seatOrder mean
- Keep "Set turn order..." in sync with the game: hidden for non-hosts
  and once the game has started, re-shown when the game ends or hosting
  moves to the local player (new GameState::hostChanged signal)

* [Client] Do not preview a randomized turn order

Randomizing is a request to the server, not a local shuffle. Shuffling the
list locally meant the dialog showed an order the server then discarded, and
let the host re-open it until a favourable order appeared. The dialog now
switches to a mode that says the server will randomize and toggles back to the
manual list on request.

* [Client] Let moderators set the turn order

The server accepts SET_PLAYER_ORDER from the host or a moderator, but the
client hid the action from everyone except the host, making the moderator
branch unreachable. Visibility and the handler now share canSetTurnOrder().

* [Server] Rely on the player order event instead of a full state resend

cmdSetPlayerOrder re-sent the whole game state after reseating, so every
client reloaded its deck view mid-lobby for no reason. The new seating reaches
clients through the Event_PlayerOrderChanged that reseatPlayers broadcasts.

* [Client] Show random turn order in the game selector

The restrictions column listed "open decklists" but not the new seating
option, so a joining player had no way to see that seats will be randomized
each game. Game info already shows the disabled checkbox.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 15:44:50 +01:00
Bryce!
ca2301bc96
Fix deck export and mana group ordering (#7729) 2026-11-04 15:43:38 +01:00
tooomm
2056043104
[CI] Update macOS runner (keep same compile targets) (#7331)
* update/add eol dates

* use new macOS images

* [CI] Retire the old macOS ccache keys from the live list

prune-caches deletes any ccache-* entry whose key is not in
LIVE_CCACHE_KEYS, so the three keys this PR renames would be dropped on
the next master run and every macOS leg would start cold.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 15:35:45 +01:00
Bryce!
67330ead5a
Embed bundled resources without compiling large C++ arrays [skip ci] (#7730) 2026-11-04 15:27:44 +01:00
BruebachL
7ca697348b
[CI] Give ccache room to hold a configuration (#7667)
* [CI] Re-assert the ccache timestamp sloppiness after configure

CMakeLists.txt sets sloppiness=pch_defines,time_macros at configure
time, and ccache --set-config replaces the whole list rather than
extending it. Setting the four entries before cmake would therefore
leave the build with only two, so the entries CI needs have to be
written after the configure step and before the build.

The mtime and ctime entries cover files created during the build
itself: moc/uic/rcc output and protobuf headers are generated after
ccache starts, so without them every TU that includes one is read as
a race and never cached. Checked-out sources are older than the
compile that follows them and do not trip the check.

* [Build] Keep risky ccache sloppiness out of the global config

USE_CCACHE defaults to ON, so CMakeLists.txt runs on a developer
machine, and ccache --set-config writes the user's global ccache.conf.
The include_file_mtime/include_file_ctime entries turn off the
modified-during-compile check for every project on the machine, not
only this one, so they belong in CI (compile.sh) and not here. The two
entries PCH caching needs stay.

* [CI] Size each compiler cache against the cache quota

The 600M bound sat below what a leg writes: ccache spent each build
deleting the entries the next run asked for, with 2624-3720 cleanup
passes and about 8% of calls served in the runs inspected on
2026-11-04.

Raising it needs room first. The repository's 10 GB quota currently
holds 1.1 GB of CodeQL trap, 1.1 GB of vcpkg, 0.7 GB of Qt and 2.1 GB
of docker buildkit layers. Two of those are now cheap to reclaim:

- docker-release wrote its layer cache with mode=max, but the build
  stage sits behind COPY . ., so its compile layer is invalidated by
  every source change and was never read back. mode=min keeps only the
  final image stage.
- the five nightly-only legs kept their own compiler caches, 600M
  each, that no pull request ever reads. They now keep none, and the
  prune job retires their leftover keys immediately instead of waiting
  for GitHub's whole-entry LRU eviction to pick them.

That funds per-leg bounds: 700M for one configuration, 850M for the
Debian 13 release that also runs the suite, 900M for Fedora 44's
Debug and Release, 800M for the instrumented leg, 400M for the
server-only leg, and 700M for each macOS leg. 6.45G of ccache plus the
3.2G of fixed entries leaves about 350M under the quota.

The prune step also covers the buildkit entries by age now, so the
mode=max generation of layer blobs is reclaimed instead of living
forever: a layer a build needs is downloaded and its date refreshed,
and dropping one that an index still lists only costs docker-release a
rebuilt layer, which gates nothing.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 10:06:56 +01:00
BruebachL
ed119cc940
[Fonts] Polish font editor accessibility and note text (#7542)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:55 +01:00
BruebachL
a791389c6a
[Fonts] Restore DeckList Serif default and live-update all role readers (#7541)
* [Fonts] Restore DeckList Serif default and live-update all role readers

* [Fonts] Keep the DeckList fallback on the application font

DeckList is read by the deck editor headers, the filter tree and the deck
view too, not only by the two surfaces that hardcoded Serif. Resolving the
unset role to Serif in appFont() made every user without a fonts.toml see
Serif outside the deck list.

- appFont(DeckList) falls back to the application font again.
- ThemeManager::deckListFont() resolves the role and keeps the Serif
  fallback for the two historical sites: the deck view zone headers and
  the printed deck list.
- DeckView no longer repaints its viewport on fontsChanged; touching the
  scene items already schedules the repaint in every attached view.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:55 +01:00
BruebachL
13e7a3baaf
[Fonts] Fix font preview leaks and reuse default-font loader (#7540)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:55 +01:00
BruebachL
3bcdfc3793
[Fonts] Migrate DeckList and InterfaceDefault surfaces to theme roles (#7539)
* [Fonts] Migrate DeckList and InterfaceDefault surfaces to theme roles

* [Fonts] Drop unused lambda captures in font-refresh handlers

* [Fonts] Fix pixel sizes and game overlay fonts from review

- Set the sizes the stylesheets carried with setPixelSize() rather than
  setPointSize(): home buttons (34), banner (24), home group box (20),
  the placeholder labels (14), the analytics drag handle (10), the EDHREC
  navigation buttons (24), the user info popup (11/14/11/10/11) and the
  game overlays (14/16/12). Points at 96 DPI were ~33% larger.
- The drag/total counters and tally columns keep the monospace family the
  stylesheets set, so digit widths stop changing as counts tick and
  white-space: pre stays aligned.
- applyDeckListFont() drops the cached tally rows before re-measuring, so
  previewing a larger DeckList font no longer leaves the tally clipped.
- Tally labels and the constructor/fontsChanged pairs in the shared-deck
  preview, quick setup, analytics panel, home box and public decks build
  their font once in a local lambda instead of twice.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:54 +01:00
BruebachL
396196b762
[Fonts] Migrate CardText surfaces to theme font role (#7538)
* [Fonts] Migrate CardText surfaces to theme font role

* [Fonts] Keep CardText call sites on the app font and fix px sizes

- CardText's unset fallback is qApp->font() again: only the three counter
  painters (paintNumberEllipse, GeneralCounter, PlayerCounter) were Serif
  before the role existed, and they now ask for it through
  ThemeManager::cardTextFont(). Card names, player names, counters, the
  overlay, the Archidekt and EDHREC labels and the completer delegate keep the
  app font they used before.
- Use setPixelSize for the EDHREC card name (20px) and commander name (28px)
  and details (16px) sizes: the stylesheets said px, and setPointSize made
  them about a third larger.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:54 +01:00
BruebachL
7a40ed9079
[Fonts] Migrate ChatLog surfaces to theme font role (#7537)
* [Fonts] Migrate ChatLog surfaces to theme font role

* [Fonts] Keep ChatView on the app font and share the report log font

- ChatLog's unset fallback is qApp->font() again: ChatView never set a font
  before the role existed, so an unset role must not switch chat and the game
  log to monospace at the system GeneralFont size.
- Move the monospace fallback the report dialogs used before into
  report_utils::reportLogFont(), which returns the theme's ChatLog role when it
  is set and the old monospace otherwise. tab_report, dlg_my_reports and
  dlg_report_user now share it, so they cannot drift and they honour a themed
  size instead of overriding it.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:53 +01:00
BruebachL
634f8115a5
[Fonts] Add per-purpose font section to palette editor (#7536)
* [Fonts] Add per-purpose font section to palette editor

* [Fonts] Make the editor's fallback and Reset preview-independent

- Add ThemeManager::defaultAppFont(), which resolves an unset role against the
  startup font instead of currentFonts, and use it in FontGridWidget::loadFonts
  so the grid never shows the live preview as the theme's default.
- Reload the live theme in PaletteEditorDialog::onReset before re-reading the
  grid, so Reset restores the saved fonts before loadFonts resolves fallbacks.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:53 +01:00
BruebachL
54797452da
[Fonts] Wire FontConfig into ThemeManager with appFont and preview (#7535)
* [Fonts] Wire FontConfig into ThemeManager with appFont and preview

* [Fonts] Restore the startup font when a config has no InterfaceDefault

- Capture the untouched application font next to the default palette and use
  it in previewFonts' else branch, so a config without InterfaceDefault
  restores the startup font instead of leaving the last preview applied.
- Drop the unpolish/polish loop from previewFonts: QApplication::setFont
  already notifies every widget, and the loop ran on every editor auto-apply.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:52 +01:00
BruebachL
d8700b2947
[Fonts] Add AppFont roles and FontConfig fonts.toml round-trip (#7534)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:52 +01:00