Commit graph

6609 commits

Author SHA1 Message Date
tooomm
6476e27a7b
Merge f14c2481be into df74917cb0 2026-11-04 20:50:40 +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
BruebachL
140fbb4484
[CI] Cover the legacy distros and the slow macOS runner in a nightly (#7668)
* [CI] Cover the legacy distros and the slow macOS runner in a nightly

Every pull request built ten Linux legs and five macOS/Windows legs, including
the previous release of each distro and macOS 13 on the Intel runner. Those
legs exist to catch a regression for users on an older distribution, which a
change being reviewed almost never does, and macOS 13 Intel is the slowest leg
of the matrix - 28 minutes against 19 for the rest.

They now run in a nightly build of the default branch and on tag builds, which
is when a change actually ships. Pull requests keep the current release of each
distro, the sanitizer leg and the coverage leg.

* [CI] Run the nightly legs from their own jobs

jobs.<id>.if cannot read matrix, so the previous filter never validated and
no leg of this workflow ran. matrix.exclude cannot express it either: GitHub
processes exclude before include, and these matrices are include-only.

Split the regular and nightly legs into two jobs per platform, sharing the
step list through a YAML anchor and gating the jobs on the event instead of
on matrix. The nightly run now covers only the legs a push leaves out, and
the event name is part of the concurrency group so a nightly and a master
push do not cancel each other.

* [CI] Run the coverage leg in the nightly

gcovr.cfg sets no fail_under, so the leg cannot fail a build, and nothing
consumes coverage-report: it was an artifact produced on every pull request and
read by no one. In the nightly it becomes a trend over the default branch
instead, next to the legs a change being reviewed does not wait for.

The leg itself is unchanged - same distro, version and variant, so the image
name and ccache key still match the warm cache it built. It is marked
allow-failure so a reporting leg cannot redden the nightly run that
vcpkg-cache-import requires to conclude in success.

The release-test comment loses its per-leg parenthetical: it claimed the
sanitizer leg packages, which #7666 stopped being true, and the coverage leg is
no longer in this job.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:42:35 +01:00
BruebachL
3f7aa4ca82
[CI] Split the sanitizer leg from the Ubuntu packaging leg (#7666)
The sanitize leg re-ran the Release packaging build after the instrumented
one, so 18 minutes of Release compile queued behind the sanitizer run in a
single job. Give sanitize its own leg with its own image and ccache, and
keep the plain Ubuntu leg publishing the DEB.

- The coverage leg's packaging comment now points at the leg that actually
  publishes the DEB.
- The -DENABLE_SANITIZERS=OFF comment no longer describes a debug-sanitize
  then release-package sequence that no job runs.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:24:24 +01:00
BruebachL
75beb7f302
[CI] Run the Linux test suite against the release build (#7665)
* [CI] Run the Linux test suite against the release build

A packaging leg compiled the tree twice: once in Debug to run the suite, once
in Release to package it. The suite is 655 tests that finish in four seconds,
so the Debug build bought nothing the release build cannot provide - macOS and
Windows have always tested their release build.

The sanitizer and coverage legs keep their Debug build, because that is the
configuration their instrumentation is for. Every other leg runs ctest against
the release configuration, ahead of the package target, so a failing suite
still stops the leg before anything is built into an installer. A leg that only
tests, Arch, builds Release without the crash reporting SDK the packaging legs
ship with.

Debian 13 and Fedora 44 each drop a 15 minute compile from a job that gates a
pull request.

* [CI] Keep Fedora 44 and Arch on the Debug build

Only the Debug configuration passes -Wall -Wextra -Werror, so moving
those two legs to the release build would have stopped the newest distro
toolchains from failing on a new warning. They keep the Debug compile and
test master had; the other Linux legs still test the release build.

Addresses review thread PRRT_kwDOACxbl86x2tOP (PR #7665).

* [CI] Decide the release test run once per leg

The step's if: and its shell both recomputed "does this leg test the
release build", and the two could drift into a leg that builds a release
it never tests. RUN_TESTS now holds that decision and both read it.

It lives at job level because a step's own env is not visible to that
step's if:.

Addresses review thread PRRT_kwDOACxbl86x2tOV (PR #7665).

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:22:43 +01:00
BruebachL
99422c2bf0
[Client] Stop sending the saved login when its checkbox is unticked (#7713)
Unticking 'Save password' only disabled auto-connect: the dialog still handed
the stored verifier to the connection, so a password typed at the same time was
ignored, and a server that still has the legacy hash refused the login before it
could upgrade the account.

Clear the verifier in effect on untick, restore it on re-tick unless a password
has been typed, and drop the stale 'Password saved' hint. The downgrade abort
can now point at unticking the box and logging in with the password, without the
extra reconnect round-trip.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:06:35 +01:00
BruebachL
96f8c3a423
[Tests] Assert settings navigation through the event player (#7698)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:45 +01:00
BruebachL
3fa8d78786
[Tests] Play card interactions through a Cockatrice event player (#7697)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:45 +01:00
BruebachL
34201122ec
[Tests] Name the tab, home and settings widgets the GUI tests address (#7696)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:44 +01:00
BruebachL
d2abd645e8
[Tests] Name the menus and dialogs the shell GUI tests drive (#7695)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:44 +01:00
BruebachL
ee57d740c2
[Tests] Replay XML test files through a headless runner (#7694)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:43 +01:00
BruebachL
2e0a718cd7
[Tests] Vendor QtTesting behind an opt-in WITH_GUI_TEST flag (#7693)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:43 +01:00
Bryce!
28597ef716
Fix Deckstats and TappedOut request lifetime and result handling (#7705)
* Fix deck analysis request lifetime, result URLs and form encoding [skip ci]

* Preserve unknown cards in deck analysis exports

* Share deck analysis URL validation and drop the unused request latch

Both analysis interfaces carried a copy of normalizeAnalysisUrl; move it to one
header next to them. They are constructed per analysis and analyzeDeck is called
once, so the analysisStarted latch was unreachable; remove it from both.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:22:29 +01:00
Bryce!
75d7764643
[Client] Keep Archidekt navigation tied to its active request (#7723)
* [Client] Keep Archidekt navigation tied to its active request [skip ci]

* [Client] Keep Archidekt listing pagination after a failed navigation

Only update list mode and the next-page link when a listing or deck
response is accepted, so a failed deck navigation leaves the existing
listing scrollable. Retry still re-issues the failed request.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 05:59:17 +01:00
BruebachL
81a0b97240
[Replay] Seek to the exact time of an event (#7610)
Timeline seeks only expose whole timer intervals, so jumping to an event could leave that event unprocessed until the next tick. Add an event-time seek that also applies events at the target timestamp.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 05:56:32 +01:00
Bryce!
624163916f
[Client] Keep Commander Spellbook requests bound to the active deck (#7722)
* [Client] Keep Commander Spellbook requests bound to the active deck [skip ci]

* [Client] Restore the bracket info tooltip when idle

retranslateUi() used to install the static "Why this bracket?" tooltip, but it now calls displayEstimate(), which returned early while no estimate existed. That left the info button without its idle tooltip.

Set the fallback where displayEstimate() owns the button's tooltip. An existing estimate still wins, and a language change refreshes the text.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 05:49:31 +01:00
Bryce!
94f7de42f2
Fix user card games for offline users and stale requests (#7738) 2026-11-04 05:29:51 +01:00
Bryce!
7768416091
Place current deck-editor docks before restoring saved layouts [skip ci] (#7734) 2026-11-04 04:37:25 +01:00
BruebachL
c72192fd81
[Format] Small fixup (#7768)
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 / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (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 / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (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 / 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
* [Format] Small fixup.

* [CardDB] Stop leaking the loader's mutexes.

Both mutexes were heap-allocated through default member initializers and
never freed, so every CardDatabase destruction leaked 16 bytes. LeakSanitizer
flags this while discovering tests that create the database in main.

Hold them by value instead, as the loader is a QObject and therefore neither
copyable nor movable.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-03 15:59:32 +01:00
Bryce!
9588e1960d
Preserve saved deck formats through database reload and view refresh (#7745) 2026-11-03 11:10:50 +01:00
Bryce!
fd2a99e897
Respect nonempty Qt hints and explain discovery failures [skip ci] (#7733) 2026-11-03 10:41:47 +01:00
Bryce!
dc4aa19d23
Import Moxfield signature spells into the deck [skip ci] (#7751)
Co-authored-by: Codex <codex@openai.com>
2026-11-03 10:40:28 +01:00
Bryce!
ada8f399f1
Log server identification and connection lifecycle (#7731)
* Log remote server identification and connection lifecycle [skip ci]

* Update libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

---------

Co-authored-by: BruebachL <44814898+BruebachL@users.noreply.github.com>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2026-11-03 10:38:54 +01:00
Jurriaan
bc09ec6afe
[Client] Make DeckStateManager::decrementCard remove tokens from the token zone if the token is not in the given zone (#7588)
* Fixed decrementCard for tokens not using the token zone

* Removed dead function actDecrementCard

Updated documentation

* Fixed no-op, added documentation

* Removed stray blank line

* Allow finding card nodes even if no database entry exists for the given card

* Added DeckStateManager tests

* Added tokens on mainboard test

* Added if checks and sorted sources

Added if checks

* Library include fix

* Renamed zone to targetZone

* Revert "Allow finding card nodes even if no database entry exists for the given card"

This reverts commit 88578cd0d9.

* Combined loader_local_matching_test and deck_state_manager_test settings sources

* Added settings sandbox, refactor

* Format

* Renamed return variable

* Flattened if statement

* Style fix
2026-11-03 10:37:17 +01:00