Commit graph

205 commits

Author SHA1 Message Date
BruebachL
6a8835f136
Merge 8432dae653 into dd2018a2d3 2026-11-09 12:54:15 +01:00
sentry[bot]
9793a0df4a
[Crash] Add lifecycle breadcrumbs for unattributable native crashes (#7813)
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
* [Crash] Add lifecycle breadcrumbs for unattributable native crashes

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

* Restore the native split host button on non-macOS styles

The merged quick-host control composed a QPushButton and a separate autoRaise arrow QToolButton, which Fusion draws as two adjacent controls. Version one of the quick-host branch used a single QToolButton with MenuButtonPopup, which every style draws as one native split button.

Use that split button on every style that can draw one, and keep the plain primary + settings button pair as the native macOS fallback.

* Enable hover zoom and the hover panel by default

Both hover triggers now default to Hover, with the existing 400 ms zoom delay and 200 ms panel delay. The Off trigger remains available for users who want the old behavior.

---------

Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
2026-11-08 23:06:21 +01:00
BruebachL
40ebd22549
[Lobby] Add instant quick host to the game room (#7802)
* Add instant quick host to the game lobby

* Use a native pull-down on macOS and a split button elsewhere

- Gate on the active application style instead of the platform, so
  choosing Fusion on macOS still gets the split button: QMacStyle ignores
  autoRaise/flat and draws a QToolButton menu as a separate square.
- Rebuild the host control on QEvent::StyleChange, because the appearance
  settings switch styles while the room is open.
- Keep the review action, its arrow and the host button disabled while a
  host or join command is still in flight.
- Announce inline quick-host failures to screen readers.

* Address review findings

* Replace the macOS pull-down with a sibling settings button

QMacStyle has no split-button rendering: a menu-bearing QToolButton is drawn as a second square bezel. Keep the primary Host game button as the one-click quick host on every platform, and show a plain Game settings button as the secondary action on macOS instead; other styles keep the split arrow.

Rename the secondary action from "Review settings first..." to "Game settings..." so the label says what it opens, and update the shortcuts page label to match.
2026-11-08 22:12:29 +01:00
BruebachL
b1c8f156dc
[Game] Add hover zoom and a tabbed card panel to the board (#7700)
* [Settings] Add hover zoom/panel triggers, delays, and double-confirm

* [Game] Smooth board hover-zoom with fade helpers

* [Game] Add hover card panel with tabbed actions

* [Game] Wire hover zoom/panel triggers through the scene

* [Game] Repaint cached zones when the hover zoom moves

`TableZone` cuts the hovered card out of its own paint so zone washes,
indicators, dimming and the land divider never draw over the zoom. It is a
`DeviceCoordinateCache` item, so the card changing geometry never dirtied it
and the stale hole stayed transparent, uncovering the black viewport as soon
as the zoom crossed a zone edge.

- Emit `hoverFootprintChanged(old, new)` from `setCardScale()` and once from
  `setHovered(false)`.
- Repaint every overlapping `TableZone` on that signal.
- Clip with the card's painted outline instead of its bounding rectangle, and
  treat a zoom covering the whole zone as "paint nothing".

* [Interface] Keep the hover panel P/T controls out of the way

The P/T set/inc/dec rows were always visible and sat under the cursor, so
clicking a card to select it could mutate its P/T instead.

- Collapse the controls to a pill on the printed P/T box, anchored on the
  drawn artwork so letterboxed and sideways cards stay aligned.
- Open the rows only from the pill and grow them upward, and re-measure the
  pill on every value so three-digit P/T does not clip.
- Close the panel when another card is hovered, and repaint the area it
  covered so no patch is left behind.

* [Interface] Address hover panel review findings

* [Game] Fix include order flagged by the format gate

The card hover widget header was included before theme_manager.h, which
clang-format rejects; the branch's format CI job failed on it.

* [Interface] Load the hover panel icons through the themed icon engine

The panel still named pre-#7595 assets (arrow_*_green, arrow_right_blue,
cogwheel) and built QIcon snapshots from themePixmap(), so the deleted names
rendered blank and every other glyph froze one palette.

Load every glyph through loadThemedIcon() with the emphasis axis the adoption
plan calls for: the category tabs take the primary level as panel wayfinding,
the action grid the quiet level because its glyphs label the button text.
Semantic tints keep their family and follow the same quiet level; Clone keeps
its sticker treatment.

* [Icons] Add an Informational semantic color family

Face-down play and the exile zone are states that are neither a gain nor a
loss, and the pre-#7595 art painted them blue (arrow_right_blue). #7595 dropped
that hue, and the icon engine has had only Positive and Negative since, so both
states fall back to neutral ink.

Add the family with the same three-level ladder the other semantic roles use:
authored roles per theme, scheme defaults otherwise, an emphasis mapping, the
palette-editor rows and the generator seed. The defaults clear the 4.5:1 label
floor on both schemes and the ladder's saturation ordering; the existing
emphasis, hue and preview tests now cover the new family. Preview sheets that
name roles resolve an unauthored one through the same default the client
paints.

* [Interface] Split the hover panel icons by emphasis and semantics

Category tabs only navigate between pages, so they take the quiet level; the
action grid is the surface the player works in and takes the primary level. Both
stay line glyphs: a filled treatment reads as a second state on every cell.

Hue marks an outcome where the glyph cannot. Play, clone and to hand are
Positive; reduce life and graveyard Negative; play face down and exile
Informational. Direction-only moves (top and bottom of library) stay neutral
because the arrow already carries the meaning.

* [Icons] Add Hidden and Marked semantic color families

Two game concepts had no hue of their own. Hidden information (a face-down
play, peeking at a card face) now has violet, and the marks a player leaves on a
card (annotation, draw arrow) now share amber instead of splitting blue and red
for the same job.

Both families follow the existing three-level ladder: authored roles per theme,
scheme defaults otherwise, an emphasis mapping, palette-editor rows and generator
seeding. The defaults clear the 4.5:1 floor on the representative platform
surfaces and keep the saturation ordering the emphasis tests assert; the hue
tests pin violet's blue-red-green dominance and amber's red-green-blue one, and
every preview sheet now renders the two families.

The hover panel grid preview is added alongside: it renders the real pages
(section headers, two-column cells, glyph over label) so the whole map can be
judged at once instead of glyph by glyph.

* [Icons] Add a Movement semantic color family

A card changing position inside the library is neither a gain, a loss nor
information, so it had no hue. Movement names it with teal and covers the three
library placements (top, bottom, X from top).

It follows the existing ladder: authored roles per theme, scheme defaults
otherwise, an emphasis mapping, palette-editor rows and generator seeding. The
defaults clear the 4.5:1 floor on the representative platform surfaces, keep
strong > base > soft saturation, and keep green > blue > red so teal cannot be
mistaken for the green gain family.

* [Interface] Give every hover panel action a semantic color

The quick actions are the panel's whole point, so a neutral glyph made the
most-used buttons the hardest to scan. Every action now names a game fact with
its hue: gain, loss, information, hidden information, a mark on the card or a
move. The line glyph stays identical, so the colour is the only difference
between neighbours.

The grid is also ordered by family so a row reads as one idea: the two state
toggles, the two gains, the two marks, the two hidden-information actions and
the two losses each share a row. Conditional actions (Peek, Unattach) sit at the
end of their pair, so a missing one leaves a hole at the section edge instead of
splitting a row. In a zone, Play and Play Face Down stay adjacent on purpose:
the same action, one of them hiding information. The move tab leads with the
three library placements (Movement), then the destinations.

actionGridIcon is removed; semanticActionIcon is now the only grid ink. The
preview renders the real pages so the whole map can be judged at once.

* [Icons] Use quiet emphasis for glyphs that label rather than act

The search and info glyphs inside the search fields, and the combo-box
chevron, label or afford an action rather than performing one. At the
default secondary weight they compete with the actual controls next to
them. Paint them at quiet emphasis so they stay legible without claiming
attention.

Sites: deck editor, visual database display, visual deck storage, set
manager, settings search, in-game zone view, and the dropdown chevron.

* [Icons] Restore sticker treatment to the add, remove and delete actions

The pre-#7595 add and delete glyphs were filled shapes with an outline.
After the line migration they read as ordinary toolbar glyphs, losing the
weight their verbs had. Give the add and delete family the sticker
treatment again.

- Deck editor Add card: primary positive sticker. It is the one action
  the database panel exists for.
- Visual database Clear filters: negative sticker, matching the delete
  family.
- Filter dock Clear all: negative sticker, matching Clear one.

* [Icons] Give sharing and pinning their semantic families

Sharing is an informational act: the app already tints reveal and
make-public with the informational family, so the deck storage share
action, the replay share-code action and the visual deck storage share
button follow it.

The printing-selector pin badge marks a card as pinned. Paint it with the
marked family, the same one used for a mark left on a card, instead of
leaving it neutral.

* [Game] Dismiss the hover panel before opening a modal dialog

The hover panel is a Qt::ToolTip window, i.e. a popup: it is always on
top and holds the input grab. Actions on the panel that open a modal
dialog (Set power/toughness, Set annotation, Set counter value, token
creation) therefore opened the dialog behind the panel, where it could
not be seen or activated.

Hide the panel synchronously and defer the action to the next event-loop
turn, so the popup grab is released before the dialog's event loop
starts. The panel asks the scene to dismiss it through a new
dismissRequested signal, keeping hover timers and the covered-card fade
on the scene's side.

Reported from the P/T overlay SET button; the same path covers the
counter tab and the token tab.

* [Icons] Refine VDE and VDD emphasis and semantics

The visual database filter section and the syntax help are information
aids, so paint them with the informational family at the default
emphasis: the filter section glyph and the search field's help glyph.
The help glyph leaves quiet emphasis because it is a clickable action,
not a field label.

The visual deck editor's display-type toggle is the one control that
changes the whole view, so it takes primary emphasis.

The search glyphs stay quiet; only the help action changes.

* [Icons] Raise the visual database filter controls to primary

The six quick-filter buttons are the controls the filter section exists
for, so paint them at primary emphasis instead of the default weight.

The sort glyph carries the same informational family as the filter
glyph, in both the visual database toolbar label and the visual deck
editor's sort-criteria button.

* [Icons] Keep the display options sort glyph neutral

The sort button beside the display-mode toggle is not part of the
informational family. Only the visual database toolbar's sort glyph
carries informational; the display options button returns to the
default neutral weight.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 21:51:31 +01:00
BruebachL
e81ee02c24
[Icons] Tint semantic glyphs and add badge and sticker treatments (#7775)
* [Icons] Keep semantic red and green on themed glyphs

The one-glyph cutover recolored every icon from a single palette role, so a
disabled deck, a not-ready player and a remove button all lost the red or green
that carried their meaning.

Add Positive and Negative application colors beside the accents, ship
scheme-appropriate defaults for every theme, and resolve them through
ThemeManager. Icons now take an IconTint: a palette role for structural glyphs,
or a semantic role for states and add/remove actions. A disabled semantic icon
falls back to the disabled foreground; on a selected row a semantic tint keeps
its color only while it stays legible on the highlight, and a palette tint keeps
the existing WindowText -> HighlightedText swap.

* [Icons] Render a semantic-tint sheet for every shipped theme

The PR showed two sheets for one theme, which cannot show that the shipped
reds and greens stay legible on every palette. Each shipped theme and scheme
now gets its own sheet: both families, three glyphs, at 16 and 24 px, on the
window and button surfaces the contrast is measured against, with the
disabled tone beside each family.

The generator lives in its own test file so the preview work does not have to
move the branches above it.

* [Icons] Add a badge icon treatment

Restore the filled-disc look the ready/not-ready icons had before the
one-glyph cutover, without bringing back palette-locked art.

IconStyle::Badge composes the existing sentinel SVG at paint time: a
gradient disc, a dark ring, and the glyph re-weighted and inked for
contrast against the tone. Line stays the default and is unchanged.

Only the two state icons opt in. The add/remove icons keep the line
treatment because their old art was a filled glyph with an outline and
no disc, so badging them would invent a look the UI never had. Disabled
badges fall back to the line glyph so an unavailable control still reads
as unavailable.

* [Icons] Add a sticker icon treatment

The pre-#7595 add, remove and delete icons were filled shapes with a black
outline and no disc. A tint cannot bring that back, and the badge disc is a
different icon language, so add a third value to the existing style axis.

IconStyle::Sticker draws the sentinel glyph twice: a near-black rim at the
band width plus one rim on each side, then the tone pass clipped to the glyph
and filled with a vertical gradient. Both treatments keep the disabled
fallback to the line glyph.

The fill rule moves into theme_icon_fill: the semantic tones are tuned for
text, so a filled shape gives its tone a saturation floor, and a dark-scheme
tone is pulled down rather than up. IconTint::resolve() applies the same floor
to line glyphs, and the badge disc uses the same fill, so a line, a badge and
a sticker next to each other carry the same green or red instead of a pale
line beside a saturated glyph.

The badge's glyph ink follows the scheme: white on light-scheme tones, as in
the pre-#7595 art, and the saturated tone darkened to an outline-dark ink on
dark-scheme tones, where the shared saturated disc is light enough that white
would sit near 2.7:1. The new test pins both cases so the glyph cannot
silently flip.

resolveSvgStrokeWidth() moves the stroke-width rewrite into
libcockatrice_utility so the badge and the sticker share it; the badge's local
copy is gone.

The sticker is opted into at 17 call sites: increment, decrement and
remove_row in the deck editor, settings pages, token dialog, server dialogs
and replay/deck storage tabs. The visual database's clear-filter button keeps
its trash can (icons/delete) in the line treatment: that glyph is new in
#7595, but it is the clearer metaphor, and a sticker rim closes its lid gap
at toolbar sizes.

The preview generators also gain a semantic-tint sheet for #7775 and a
line/badge/sticker family strip, so the three PR descriptions can show the
same rendered colors.

* [Icons] Render authored theme SVG art without the SVG image plugin

`QPixmap("theme:file.svg")` needs Qt's SVG *image-format* plugin. Debian
builds that as the separate `qt6-svg-plugins` package, which is only a
Recommends of `qt6-svg-dev`, so a `--no-install-recommends` install (CI,
minimal user installs) does not have it. Authored theme art then loaded as
a null pixmap: the two authored-art render tests failed on Debian 13 and on
the Ubuntu sanitize leg while passing on a developer machine whose Qt
bundles the format.

Render authored SVG assets through QSvgRenderer instead, which is already
linked for the sentinel path and needs no plugin. Raster assets keep the
QPixmap loader. The test suite now hides the plugin directories, so the
dependency cannot come back unnoticed on platforms that ship the plugin.

* [Icons] Render a badge/sticker sheet for every shipped theme

The PR showed one theme's badge and sticker sheets, which cannot show that the
filled treatments read on every palette. Each shipped theme and scheme now
gets its own sheet: both semantic families, three glyphs, as line glyphs,
stickers and badges, at 16 and 24 px, on the window and button surfaces the
contrast is measured against, with the disabled fallback under each family.

The generator lives in its own test file so the preview work does not have to
move the branches above it.

* [Icons] Show the trash can with the sticker treatment again

The sticker sheet kept the delete glyph out of the sticker rows and explained
why: at toolbar sizes the rim closed the lid gap. The icon is used with the
sticker treatment again for destructive actions, so the sheet now shows it and
the exception note is gone.

* [Icons] Apply the CI clang-format output to the treatment preview

clang-format 18 wraps the family label and the glyph x offset differently from
the local clang-format 23. The preview test takes the CI output so the bottom
of the stack passes the style job on its own.

* [Icons] Rewrite the icon role docs in plain language

Address review: drop the AI-cadence prose from the IconStyle/IconTint and treatment headers, use ///< for the enum member docs, and move theme_icon.cpp's anonymous namespace above the IconTint definitions. No behavior change.

* Address review findings

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 19:09:01 +01:00
BruebachL
2b4019ccaf
[Client] Active pill for alts table, dismissed pill for report queue (#7519)
* [Client] Active pill for alts table, dismissed pill for report queue

* Apply suggestion from @ZeldaZach

Co-authored-by: Zach H <zahalpern+github@gmail.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: Zach H <zahalpern+github@gmail.com>
2026-11-08 09:01:11 +01:00
BruebachL
53f69c9216
[Alts] Server-side search controls, IP counts, adjustable window (#7517)
* [Alts] Server-side search controls, IP counts, adjustable window

Plumbs the match-type chips through to the server: include_email/include_clientid/include_ip skip disabled identifier types in both matching and frontier expansion, so excluded types (e.g. throwaway domains) never enter the graph at any depth. Session lookback becomes adjustable (session_months, clamped 1-24, default 6). Each alt row carries ip_count, the distinct recent session IP count shown in a new IPs column. All proto additions are optional with old defaults, so old clients/servers interoperate.

* Fix remaining review threads on the alts search controls

- Disable keyboard tracking on the depth and window spin boxes so typing a
  two-digit value sends one search, not one per digit.
- Reload the user under investigation on a filter change instead of reading
  the search box, which could switch to a name never submitted.
- Keep a chip's last match count while it is unchecked; the server omits
  unchecked match types, so the label used to drop to "(0)".

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:59:51 +01:00
BruebachL
2fba13753b
[Client] Alt badges, breadcrumb trail, investigate button (#7516)
* [Client] Alt badges, breadcrumb trail, investigate button

AltTableDelegate paints palette-tinted match badges with paint-through selection, style warning/critical icons on nonzero counts, and font/elision handling. TabAlts gains a clickable breadcrumb trail, an Investigate-this-user button, and bold usernames. Async alts responses are guarded against stale investigations.

* [Client] Fix alt badge and trail review findings

- Drop the dead VPN warning branch: rowVisible() already hides IP rows
  above the threshold, so setVpnThreshold(), its member and the spin-box
  connect never took effect.
- Dim IP rows at shared_count > 2 instead of > 1: the count already
  includes the investigated account, so every IP row was dimmed.
- Truncate backStack when a breadcrumb is clicked instead of pushing the
  current user again, so the trail keeps a single path and Back works.
- Word-wrap the trail label so a long chain cannot widen the tab.
- Extract the status/match pill painting into widgets/utility/pill_painter
  and use it from both delegates instead of duplicating it.

* [Client] Route investigate() through the trail helper

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:59:50 +01:00
BruebachL
18f9b26a3d
[Client] Report queue dashboard and status pills (#7515)
* [Client] Moderation and alts dashboards

Turn the two staff tables into dashboards without behavior changes: TabModeration gets an identity header (display-size name, status, registered/last-login subline), Reports/Warnings/Bans/Alts count cards with an Open-graph deep link, full-width admin notes, and Alts/Sessions/Staff tabbed sections with tab memory. TabAlts gets toggle chips with match counts, bold usernames, palette-token dimming of shared-IP rows, and a resizable side card for the selected-user timeline with selection restore. Row activation works via Enter as well as double-click; async responses are guarded against stale investigations and retranslation re-applies cached dashboard data.

* [Client] Report queue dashboard and status pills

TabReport becomes a dashboard: selected-report identity header (display-size user, bold status, category/time/room subline), Description/Chat/Comments/Reported-user tabbed sections with tab memory, always-visible user context with empty cues, and bold reported users. ReportStatusDelegate paints palette-tinted status pills with paint-through selection, translated status strings, and font/elision handling. report_utils gains formatReportStatus plus an opt-out status-color flag so the delegate owns color in the mod queue while the user dialog keeps its colors. Async responses stay guarded; retranslation re-applies header and comment states.

* Fix remaining review threads on the report queue dashboard

- Read the raw report status from Qt::UserRole in DlgMyReports, so regular
  users can comment on their own open reports again.
- Exclude the server's depth-0 seed row from the related-alt count.
- Dim only IP matches shared by more than the pair, not every IP match.
- Activate alts rows on double-click or Enter instead of `activated`, which
  fires on a single click on single-click platforms.
- Fold the reported-user bolding pass into the fill loop.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:59:50 +01:00
BruebachL
e603257e08
[Client] Alt tab filters and unified user timeline (#7513)
TabAlts gains client-side match filters (Email / Client ID / IP toggles plus a hide-IPs-shared-by-more-than-N-users threshold for VPN noise) with a shown/hidden status count, and a Selected user panel that loads the row account's counts, admin notes, warning history, and ban history in one place. Detail requests are guarded against stale selections.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:18:01 +01:00
BruebachL
9fda5c3c3f
[Client] Alt investigation entry points and in-tab navigation (#7512)
TabModeration gains an Open alt graph button plus alts-table double-click, both routing to the User Alts tab. UserContextMenu gains an Investigate alts action. TabAlts gains row double-click navigation with a Back history stack so moderators can walk the alt graph without retyping names.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:15:38 +01:00
BruebachL
88de1ff39b
[Client] Transient User Alts investigation tab (#7511)
* [Client] Transient User Alts investigation tab

New TabAlts tab with user search, depth selector (1-3), and an alt table showing per-row match evidence (Via, Depth, Shared fan-out) alongside the account data. Sends Command_GetUserAlts with the requested depth and degrades gracefully against old servers (missing evidence fields render as dashes). Wired as a transient single-reuse tab via TabSupervisor::openTabAlts with no menu action or persisted setting; closed on disconnect like the other staff tabs.

* Fix remaining review threads on the user alts tab

- Switch to the User Alts tab when it is created, so the first investigate
  click does not open it in the background.
- Treat an empty RespOk as "user not found" and use a generic message for
  error codes, matching what the server actually returns.
- Exclude the depth-0 seed row from the related-account count.
- Guard the reply with the requested user name so a stale response cannot
  overwrite a newer one.
- Move the duplicated formatEpoch body into widgets/utility/epoch_format.h.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:15:12 +01:00
Lukas Brübach
cc89c32191 [DeckSync] Pass the sync summary through the signal and tidy the manager API
- Emit SyncSummary from syncFinished() instead of four positional ints, and
  build it from a single currentSummary() helper, so the counts cannot drift
  between the status message and the signal.
- Use a designated initializer for LocalFile, like the other records.
- Drop setClient() and setColorIdentityProvider(): both fields are already
  constructor parameters and the setters had no callers. setProfile() and
  setDeckPath() stay, since the tab re-reads them before every run.
- Fix the include paths in tab_deck_storage.h and the narrowing conversion of
  the tree item id, so the branch builds again.
2026-11-07 23:49:18 +01:00
BruebachL
78ed356ba8 [Format] Fix include order in tab_deck_storage.h 2026-11-07 23:49:18 +01:00
BruebachL
2db9760170 [DeckSync] Prefer ctor params for set-once client/provider per review 2026-11-07 23:48:56 +01:00
BruebachL
f597e9bd9f [DeckSync] Move SyncSummary into DeckSyncManager per review 2026-11-07 23:24:52 +01:00
Lukas Brübach
53c15b1298 [Client] Manual deck collection sync with Sync Now action 2026-11-07 23:24:52 +01:00
RickyRister
eab217d1bf
[DlgCreateGame] Revert dlg to use exec instead of open (#7795) 2026-11-07 13:44:40 +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
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
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
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
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
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
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
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
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
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!
7768416091
Place current deck-editor docks before restoring saved layouts [skip ci] (#7734) 2026-11-04 04:37:25 +01:00
Jurriaan
bfb53c5def
[Client] Visual Deck Editor: Added more mouse click modifiers to add/decrement cards to specific zones (#7589)
* Make visual database mouse click modifiers properly add/decrement different card zones

* Use event->modifiers(), refactor

* Removed QApplication include

* Added visual database display documentation page

* Minor fixes

* Added documentation for the mouse interaction of the table view
2026-11-03 10:35:58 +01:00
BruebachL
94e6281d87
[Logs] Browse room history without filters with paginated infinite scroll (#7531)
* [Logs] Browse room history without filters with paginated infinite scroll

* [Logs] Continue pagination when switching log tabs

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:36:49 +01:00
BruebachL
345b0e8ba9
[Refactor] Move game logic into libcockatrice_game (#7437)
* [Refactor] Move game logic into libcockatrice_game

* [Refactor] Include card_state.h in player_actions.h

* [Game] Inject settings providers into libcockatrice_game, drop Widgets

* [Game] Follow base rename to createCardRequested after rebase

* [Game] Drop unused CardItem forward declaration

* [Game] Fix includes after moving game logic into libcockatrice_game

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-31 18:44:15 +01:00
BruebachL
6f0c5dca12
[Refactor] Move LoadedDeck and DeckFileFormat data types into libcockatrice_deck_list (#7424)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-31 18:44:11 +01:00
BruebachL
73d5a2a0ab
[Refactor] Emit phase toolbar intent instead of protobuf commands (#7419)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-31 18:44:10 +01:00
sentry[bot]
aa42cfbccc
[VisualDeckEditor] Create menus before central frame to prevent Windows crash in setParent_sys (#7673)
Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
2026-10-27 09:12:37 +01:00
BruebachL
93309e01a2
[Server/Client/Protocol] Let moderators clear banner cards (#7546)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-25 13:19:20 +01:00
Neel Jogani
a4c4c67674
Fix tab retranslation (#7580)
* Retranslate all open tabs on language change

* Address review: deeper Visual DB retranslation, index-based Archidekt size logic

In Qt 6, setItemText() on the current item emits currentTextChanged but not
currentIndexChanged, so connecting the Archidekt combos to currentTextChanged
fired a search on every language change. Both combos now use currentIndexChanged,
and buildSearchUrl() compares the size-logic combo by index so translated labels
cannot drop sizeLogic.

* Derive display mode button text in one place
2026-10-25 12:30:17 +01:00
BruebachL
518c7e6125
[Network] Add a configured QNetworkAccessManager factory (#7628)
* [Network] Add a configured QNetworkAccessManager factory

Every HTTP caller built its own QNetworkAccessManager, so the system proxy
and a transfer timeout were easy to omit. Add NetworkManagerFactory::create()
as the single place that applies the process-wide proxy configuration and a
transfer timeout, and use it at all 14 construction sites.

Per-site settings such as disk caches and manual redirect policies stay with
their callers.

* [Tests] Link libcockatrice_network into the tests that compile its headers

release_channel.cpp and card_picture_loader_worker.cpp now include
libcockatrice/network/network_manager.h, but the test targets that compile
them only linked libcockatrice_settings, so the include directory never
reached them and every platform failed with "file not found":
loader_local_matching_test and picture_loader_benchmark_test.

libcockatrice_network is an INTERFACE library, so linking it costs these
targets nothing but hands over the include path.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-25 00:33:43 +02:00
BruebachL
11e2bb0d2f
[QtUtils] Adopt window fade, path, bold font and confirmation helpers (#7622)
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 / 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
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-24 21:43:50 +02:00
BruebachL
e806085681
[QtUtils] Use makeLayout() for zero-margin layouts (#7620)
* [QtUtils] Add parent lookup, layout clearing, scroll area and monospace helpers

* [QtUtils] Adopt shared Qt helpers across the client

* [QtUtils] Add layout creation and relayout helpers

* Swap parameter order (spacing before margin) and remove static from relayout()

* [QtUtils] Use relayout() after rebuilding dynamic layouts

* [QtUtils] Use makeLayout() for zero-margin layouts

* Consolidate setSpacing() calls into makeLayout() using new parameter order

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-24 19:06:18 +02:00
Neel Jogani
6e770eb7b2
Retranslate Home tab when the language changes (#7579)
* Retranslate Home tab when the language changes

TabSupervisor::retranslateUi() never included the Home tab, and
HomeWidget set its button text only once, when the buttons were
created. Switching languages left the Home tab in the old language
until it was reopened.

Store the Home buttons as members, add HomeWidget::retranslateUi()
to set their text, and call it from TabHome and TabSupervisor. The
connect button text still only changes for the Connecting,
Disconnected and LoggedIn statuses, as before.

* Address review: plain ClientStatus, drop optional and placeholder
2026-10-24 08:35:31 +02:00
BruebachL
ab13a6748f
[QtUtils] Adopt shared Qt helpers across the client (#7618)
* [QtUtils] Add parent lookup, layout clearing, scroll area and monospace helpers

* [QtUtils] Adopt shared Qt helpers across the client

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-24 08:26:21 +02:00
RickyRister
b849a57dab
[TabRoom] Fix compile failure due to unused lambda capture (#7577) 2026-10-18 11:10:26 +02:00
BruebachL
ce51e7147d
[Servatrice] Log room channel messages in audit log (#7479)
* [Servatrice] Log room channel messages and bump schema to 38

* Fixup: show room_channel audit rows and keep migration nullable

The log history query returns room_channel rows, but the log tab
dropped them. Render them in the room table with an extra Channel
column. Also drop the NOT NULL on target_type from the 37->38
migration so migrated schemas match the nullable servatrice.sql
definition (and strict-mode ALTERs cannot fail on legacy NULL rows).
Update the channel tests to the flag-based privilege model: the admin
fixture now carries IsModerator alongside IsAdmin, as the database
layer assigns.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-16 22:46:39 +02:00
BruebachL
c99113ab0e
[Client] Tabbed room channels with unread states and collapsible chat (#7478)
* [Client] Add room channel selector with per-channel chat views

* [Client] Tabbed room channels with unread states, collapse, read-only, banners

Reworks the room chat channel display: channels move out of the combobox
into a tab bar below the message input showing every channel available
to the user.

- Each tab shows its unread message count and is tinted when it holds
  unreads or a mention; both colors are user-configurable chat settings
  (channelUnreadColor, channelMentionColor) with a settings-page section.
- Clicking the active tab collapses the chat: message view and input
  hide and the splitter section minimizes to the tab strip; expanding
  restores the previous splitter sizes. Viewing a channel clears its
  unread state.
- Channels the user may read but not write (per write_access_level)
  show with the message input disabled and a read-only placeholder;
  sends are guarded locally, the server still enforces.
- Channel banner_message strings render at the top of their channel
  view on join, like the room join message on Main.
- Replayed channel history no longer inflates unread counts; moderation
  redaction now applies across all channel views.

* Fixup: keep chat tab bar visible when chat is collapsed

QSplitter children are collapsible by default, so setSizes({1, 0})
hid the whole chat group box including the channel tab bar.
Mark the chat widget non-collapsible so collapsing clamps to its
minimum size (title + tab bar) instead.

* [Room] Explicitly initialize MAIN_CHANNEL per review

* [Room] Address channel tab review follow-ups

- gate @all mentions on moderator/admin sender to match ChatView,
  whole-word matching for own name and highlight words
- hide single-channel tab bar so old servers do not show a lone Main
  tab that collapses chat on misclick
- capture target ChatView in sendMessage so errors land in the
  channel the message was sent from
- share ignore/unregistered filtering between RoomSay and
  RoomChannelSay via isRoomMessageIgnored

* [Client] Fix tab_room.cpp formatting per clang-format-18

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: BruebachL <bruebachl@cockatrice.example>
2026-10-16 22:46:39 +02:00
BruebachL
4c5a5e3e55
[Game] Invalidate view when hiding replaced card menu (#7453)
* [Game] Invalidate view when hiding replaced card menu

Hide any visible card menu before replacing and invalidate viewport.

* [Game] Fix viewport update compile error in setCardMenu

QWidget::update has no QRectF overload, and sceneRect is in scene
coordinates rather than viewport coordinates anyway, so invalidate the
whole viewport instead.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-11 15:36:11 +02:00