From e71e5a961497405a9f648a5ebc2e0bf731688872 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 11 Aug 2026 06:25:59 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20Campaign=20OP=20slice=20OP5=20?= =?UTF-8?q?=E2=80=94=20the=20Chat=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Binds LayoutDesc 0x2100005C through OP2's template-list mechanism and OP3's per-page OptionPage model: the General Options header + two DualHash-linked opacity sliders (Option_DefaultOpacity_Property 0x10000080 / Option_ActiveOpacity_Property 0x10000081, live-apply on drag through RetailWindowOpacityController, defaults read from the installed DAT's DBProperties collection at DID 0x78000001 via ChatOptionsDatDefaults), and the five per-window text-filter blocks (main window 12 rows minus Gameplay, four floaties 13 rows each — the byte-verified authored order cross-checked against the raw gmChatOptionsUI::InitOptions/AddCheckboxBitfield64Option pseudo-C, not just the research doc's own table) writing AcDream.Core.Chat. ChatWindowState directly, the same state CH6's chat windows already read. AP-195 retired: ported both halves left open at the OP2 re-review — the ALL-set LED media swap (new UiButton.FaceFileOverride, driven by the block-level P0x10000082/P0x10000083 sprites now threaded through ElementInfo/DatWidgetFactory) and the CreateChildren self-sizing tail (UiCheckboxBitfield64.Height grows with its stacked row content; the enclosing ListBox reflows around the block's FINAL height via the new UiTemplateListBox.AddPrebuiltRow, reusing the ListBox's own stacking rather than a third stacking path). AP-187 broadened to cover the main window's own filter (previously only the four floaties) and the new live-editing write path. The main chat window's filter (retail window id 8, ChatWindowState id 0) gains its own settings.json persistence (ChatSettings. ChatWindowMainFilter) alongside the pre-existing floaty 1-4 fields; opacity persistence is now wired on every live slider change, not only through the old dev-scaffold Settings panel. Fixture regeneration (ACDREAM_REGENERATE_UI_FIXTURES=1) picked up the new ElementInfo.LedCheckedSprite/LedUncheckedSprite fields across all 19 committed layout fixtures — purely additive, confirmed against the live installed DAT (0x10000520's own 0x82/0x83 properties resolve to 0x06004D17/0x06004D19 exactly as AP-195 documented). Conformance: FilterRows/FilterBlocks pinned against the byte-verified authored order and ChatWindowState's own default constants; the AP-195 LED swap and self-sizing behavior; the DAT opacity-default extraction against the live installed DAT; live filter/opacity writes reaching ChatWindowState/RetailWindowOpacityController; OnShown re-seed and Reset/Defaults ghosting per the OP4 binding-pattern discipline. Co-Authored-By: Claude Fable 5 --- .../retail-divergence-register.md | 5 +- .../2026-08-11-campaign-op-test-script.md | 148 ++++- .../UI/Layout/ChatOptionsDatDefaults.cs | 92 +++ .../UI/Layout/ChatOptionsPageController.cs | 478 ++++++++++++++ src/AcDream.App/UI/Layout/DatWidgetFactory.cs | 6 +- src/AcDream.App/UI/Layout/ElementReader.cs | 26 + src/AcDream.App/UI/Layout/OptionPageModel.cs | 192 ++++++ src/AcDream.App/UI/RetailUiRuntime.cs | 113 +++- src/AcDream.App/UI/UiButton.cs | 20 +- src/AcDream.App/UI/UiCheckboxBitfield64.cs | 120 +++- src/AcDream.App/UI/UiTemplateListBox.cs | 29 + src/AcDream.Core/Chat/ChatWindowState.cs | 26 +- .../Panels/Settings/ChatSettings.cs | 7 + .../Panels/Settings/SettingsStore.cs | 2 + .../UI/Layout/ChatOptionsDatDefaultsTests.cs | 57 ++ .../Layout/ChatOptionsPageControllerTests.cs | 607 ++++++++++++++++++ .../OptionsPanelLayoutConformanceTests.cs | 190 +++++- .../Layout/fixtures/character_2100002E.json | 268 ++++++++ .../character_info_2100006E_10000183.json | 24 + .../UI/Layout/fixtures/chat_2100006f.json | 78 +++ .../Layout/fixtures/chat_floaty_2100005b.json | 62 ++ .../UI/Layout/fixtures/combat_21000073.json | 200 ++++++ .../component_category_21000033_10000466.json | 2 + .../component_row_21000033_10000467.json | 10 + .../UI/Layout/fixtures/dialogs_2100003C.json | 40 ++ .../fixtures/effects_negative_2100001B.json | 40 ++ .../fixtures/effects_positive_2100001B.json | 40 ++ .../effects_row_2100001B_10000128.json | 8 + .../fixtures/examine_2100006B_100005F2.json | 160 +++++ .../examine_component_2100006B_1000032E.json | 4 + .../examine_row_2100006B_10000166.json | 6 + .../Layout/fixtures/indicators_21000071.json | 60 ++ .../Layout/fixtures/inventory_21000023.json | 118 ++++ .../Layout/fixtures/link_status_2100001D.json | 12 + .../Layout/fixtures/mini_game_2100001E.json | 28 + .../UI/Layout/fixtures/options_2100002B.json | 296 +++++++++ .../fixtures/options_character_21000028.json | 42 ++ .../fixtures/options_chat_2100005C.json | 42 ++ .../fixtures/options_config_21000029.json | 42 ++ .../fixtures/options_gameplay_2100002A.json | 58 ++ .../options_panel_2100006E_1000018D.json | 222 +++++++ .../Layout/fixtures/paperdoll_21000024.json | 60 ++ .../UI/Layout/fixtures/powerbar_21000072.json | 42 ++ .../UI/Layout/fixtures/radar_21000074.json | 18 + .../fixtures/smartbox_fps_2100000F.json | 2 + .../Layout/fixtures/spellbook_21000034.json | 118 ++++ .../UI/Layout/fixtures/toolbar_21000016.json | 100 +++ .../fixtures/vendor_21000012_100000B7.json | 106 +++ .../UI/Layout/fixtures/vitae_21000020.json | 12 + .../UI/Layout/fixtures/vitals_2100006C.json | 106 +++ .../Chat/ChatWindowStateTests.cs | 13 + .../Panels/Settings/SettingsStoreTests.cs | 23 + 52 files changed, 4540 insertions(+), 40 deletions(-) create mode 100644 src/AcDream.App/UI/Layout/ChatOptionsDatDefaults.cs create mode 100644 src/AcDream.App/UI/Layout/ChatOptionsPageController.cs create mode 100644 tests/AcDream.App.Tests/UI/Layout/ChatOptionsDatDefaultsTests.cs create mode 100644 tests/AcDream.App.Tests/UI/Layout/ChatOptionsPageControllerTests.cs diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md index 2e54d388..9257058d 100644 --- a/docs/architecture/retail-divergence-register.md +++ b/docs/architecture/retail-divergence-register.md @@ -175,7 +175,7 @@ readiness/requeue adaptation. See --- -## 3. Documented approximation (AP) — 138 active rows (AP-197 filed 2026-08-11 at the OP4 review-fix round (SF-1/S4) — Display Timestamps hardcodes retail's constructor-default format string instead of the per-character GenericQualitiesData key-1 override the parser reads and discards; AP-196 filed 2026-08-11 at the OP4 review-fix round (MUST-FIX 3 / blast M2) — the Group-C re-point's observable-default changes (ViewCombatTarget true→false) and the GameplaySettings store retirement (AutoTarget/AutoRepeatAttack/ViewCombatTarget deleted, the other five kept as write-behind mirrors); AP-195 filed 2026-08-11 at the Campaign OP OP2 re-review closure — `UIOption_CheckboxBitfield64::Refresh`'s ALL-set LED media swap and the `ResizeTo`/`CalculatePaperSize` self-sizing tail are not ported, and the block's row stacking is a second, divergent implementation of the ListBox mechanism, all due at OP5; AP-194 filed 2026-08-10 at Campaign OP slice OP1 — the GetDefaultOptionValue vs constructor-default disagreement for ConfirmVolatileRareUse/ShowHelm/ShowCloak (see the row below); AP-193 filed 2026-08-10 at Campaign OP slice OP1 — the 0x34 HearPKDeathMessages id/mask mapping is ACE-sourced (see the row below); AP-192 filed 2026-08-10 at the Campaign CH round-5 polish (S2) — authored outline `0x21`/`0x22` now reaches every text-bearing widget, but only at the element's effective-default state; per-STATE outline switching (dialog/character/combat buttons author `0x21` in state `0x3` only) is not ported; AP-191 filed 2026-08-10 at Campaign CH round 4 items 1+2 — the chat transcript's missing tag-colour (`0x1D`, green) and tag-font (`0x1C`) are deferred, needing a per-run tag concept `UiText.Line` does not have yet; AP-184 RETIRED 2026-08-10 at Campaign CH round 4 — the three PARTIAL `/help` group topics (channels/chatting/commands) are now COMPLETE verbatim listings, `ClientCommunicationSystem::HelpStupidChannelHack @0x0056f290` fully decoded (the "vftable slot" operands are the same pooled/mislabeled-data artifact as AP-186's own precedent, not real vtable dispatch — reading the function's own disassembly for the `push imm32` preceding each constructor call resolves them), closing ISSUES.md #364 (full retirement note later in this same list, at its own "AP-184 RETIRED 2026-08-10 at Campaign CH round 4, closing ISSUES.md #364 — filed 2026-08-09..." entry); AP-113 RETIRED 2026-08-10 at the consolidated-review round, SHOULD-FIX 3/1 byproduct — DoLifestone's own bad-args refusal text is now byte-recovered, see its retirement note below; AP-183 and AP-186 RETIRED 2026-08-10 by issue #363's interface-text seam — see their retirement notes below; AP-190 filed 2026-08-10 at Campaign CH slice CH6c — window opacity now fades every RetailWindowManager window on retail's focus-driven Default/Active mechanism, not just ChatInterface-derived ones, and ships gmMainChatUI's 1.0/1.0 default as the ONE shared default across every registered window (fixed from the original 0.5/1.0 base-ChatInterface value, per the row's own REWORDED (2)) instead of applying it only to ChatInterface-derived windows, retiring AP-40 (the prior "opacity is fixed at 0.75, no focus transition" row) in the same commit; AP-189 filed 2026-08-10 at the CH6a/b REJECT-review rework, SHOULD-FIX 5 — acdream's ONE shared 500-entry/200-line-display-tail chat log gives every window a shallower EFFECTIVE per-window scrollback depth than retail's own per-window 10,000-line log, though the accumulate-while-closed and independent-per-window-scroll BEHAVIORS are both correctly reproduced; AP-188 filed 2026-08-10 at Campaign CH slice CH6b — a floating chat window's chat entry always sends on the Say channel because the floaty LayoutDesc authors no talk-focus menu and acdream does not (yet) share the main window's currently-selected channel across all five chat-window instances; AP-187 filed 2026-08-10 at Campaign CH slice CH6b — the four floating chat windows' text-type filters persist in local `settings.json` only (`ChatSettings.ChatWindow1..4Filter`), with no analog to retail's server-side `0x1000008C` GameplayOptions blob, so a character's floaty filter customization does not travel between acdream installs or round-trip to/from a retail client sharing the same character; AP-186 RETIRED 2026-08-10, issue #363's interface-text seam — `ChatVM` now carries an `OnInterfaceText` hook (`Action?`) the App-layer composition wires to `RuntimeCommunicationState.AddText(text, RetailLogTextType.ClientLocal)`, exactly fix shape (a) this row's own filing proposed; `ChatCommandRouter`'s two local-presentation fallbacks (`RetailCommandHelpTable.UnknownCommand` and the degenerate-prefix "Unknown command: {verb}." refusal) now call `ShowInterfaceText` and reach the SpewBox, with a null-fallback into the chat log (still tagged `ClientLocal`) for hosts that never wire the hook (headless has no `ChatVM` at all). Closes ISSUES.md #367; AP-185 filed 2026-08-10 at Campaign CH slice CH6a — the chat window's UiLocked border-art cosmetic swap is unported, see the row for detail; AP-184 RETIRED 2026-08-10 at Campaign CH round 4, closing ISSUES.md #364 — filed 2026-08-09 at Campaign CH user-gate round 2, item 3, recording that three of the seven retail `/help` group-topic listings (channels/chatting/commands) remained PARTIAL because their detail text is built in full or in part by `ClientCommunicationSystem::HelpStupidChannelHack @0x0056f290`, which the filing believed "not decodable with confidence from a static string sweep" because Binary Ninja renders its three internal string operands as dereferences of unrelated vtable slots (`&ClientCommunicationSystem::\`vftable'.RecvNotice_StartBarberNotice` etc.). That belief was WRONG — the same pooled/mislabeled-data artifact this register already documented elsewhere (AP-113's retirement note) applies here too: reading the function's own disassembly for the `push imm32` immediately preceding each `PStringBase::PStringBase` constructor call (rather than trusting BN's line-grouped rendering, which hides the true instruction order) resolves all three operands directly — `"@"` + a one-character tag sliced from a shared wide literal `U"fvpca"`/`U"mh,."` (a wide string read through a narrow `char*` truncates at the first zero high byte, the "hack" retail's own function name calls out) + `" - Sends a broadcast to your "` + `ChannelSystem::GetChannelName`'s own literal switch-table result + `".\n"`. `ChannelsGroupDetail` (entirely 6 such calls), `ChattingGroupDetail` (6 more, plus a `HelpReply@0x00577A50` Summary-branch quirk that unconditionally emits reply+pr+mr together — read directly, not assumed), and `CommandsGroupDetail` (`HelpAllGroup`, a straight-line concatenation of every other group's Detail branch plus a handful of its own short one-liners, including a CONFIRMED retail saveui/loadui duplicate) are now COMPLETE verbatim listings, matching the four (death/status/text/allegiances) the original filing already had. See `RetailCommandHelpTable`'s class remarks and `RetailCommandHelpTableTests` for the full per-line address citations. Round 2 item 2 also deletes `PortalWaitNoticeController` (the dedicated centered-overlay presentation the user reported was the wrong retail surface) and reroutes the portal-space wait-cue notice through the same `AddText`/SpewBox chokepoint every other on-screen interface-text site uses — AP-178's open SpewBox position/extent/font/colour questions now cover this notice too, since its separate controller and consts are gone; no new row was needed for the surface mismatch itself, since it was never separately registered (`PortalWaitNoticeController`'s own doc comment asserted "not a chat message" as an accepted design, not a flagged divergence). AP-150 RETIRED 2026-08-09 at Campaign CH user-gate round 1, item D (#329) — `PortalTunnelPresentation.TickRotation` now emits `"In Portal Space - Please Wait..."` unconditionally on every rotation-segment expiry, exactly matching `gmSmartBoxUI::UseTime`'s `else`-arm at 0x004D6FCD, instead of gating on `_waitCueVisible`, which only ever went true after the invented 5-second `RuntimeWorldTransitState.RetailWaitCueDelay` hold; `RetailWaitCueDelay`/`ObserveWait`/`SetWaitCue` remain as `LocalPlayerTeleportController`'s own hold-delay telemetry (`RuntimePortalSnapshot.WaitCueShown`) but no longer gate the on-screen cue, so they are not a residual of this row — closes issue #329; AP-183 RETIRED 2026-08-10, issue #363 — every named site now routes through the `ChatVM.ShowInterfaceText`/`OnInterfaceText` seam (see AP-186's retirement note) at its correct retail type: `DoStupidChannelHack` ("You must specify the text you wish to say!", newly wired — the six legacy channel verbs previously fell through `ChatInputParser.Parse`'s pure `return null` with no message at all), `DoChannelList`/`On`/`Off` ("Please specify the channel name.", reclassified), `DoAllegiance` ("Please see @help Allegiance...", reclassified), `DoHouseAvailableList` (reclassified AND corrected to retail's own "Please see @help hslist for more information on how to use this command" string, replacing the acdream-synthesized "Usage: /hslist " fallback — verified `acclient_2013_pseudo_c.txt:381481`/`1029383`), and `DoReply` ("Someone must @tell you first!", newly wired for the message-but-no-last-teller branch only — bare `/r` with no message at all is a separate retail branch, deliberately still unported). `DoSpeaker`/`DoEndurance`/`DoTitle` are untouched, confirmed still correct at `0x00`. The generic bad-args fallback (`ChatCommandRouter.Submit`'s catalog dispatch) now resolves `WeenieErrorMessages.Resolve(0x026u, null)` ("That is not a valid command.", the exact port of `DoCommand @0x0057E46D`'s `HandleFailureEvent(0x26)`) instead of synthesizing a `"Usage: {Usage}"` line — cross-checked against five decompiled handlers (`DoDie` plus the four above), all `0x1A`, confirming the uniform routing decision; AP-182 filed 2026-08-09 at Campaign CH slice CH4, corrected at the CH4 REJECT-review (nit 11) — `@title` is wired to a pure no-op (the value is neither stored nor consumed anywhere) and also omits `DoTitle`'s three local failure messages; recount at the CH3 Opus review corrected a pre-existing off-by-one; AP-181 filed 2026-08-09, Campaign CH slice CH3 — the local chat spam throttle (`IsMessageSpam`) has no acdream port. AP-178 NARROWED 2026-08-09 at the CH2 REJECT-review rework NIT 3, wording corrected at the CH2 re-review nits pass (`docs/plans/2026-08-09-chat-parity-campaign.md`, nits 1/2/6) — the original `dats.Portal` pass used an id source that was not Portal's own (`dats.Portal.GetAllIdsOfType()` is empty for this type), so it established nothing about Portal either way; extending a correctly-paired sweep to `dats.Local` FOUND the SpewBox element there; extent (`450×72`) and `MaxConcurrentItems` (`4`, not the code-default `1`) are now AUTHORED, leaving absolute screen position, colour, AND vertical content flow (now TOP-aligned, acdream's own invention pending measurement) open. AP-180 filed 2026-08-09 at the CH2 REJECT-review rework — `RuntimeCommunicationState.AddText`'s `windowId` parameter is accepted but not consumed, so retail's dual-destination echo (a `0x1A` message with a non-zero `windowId` lands in both the SpewBox and its originating chat window) is unimplemented; latent today since every production caller passes `windowId = 0`. AP-177/AP-178/AP-179 filed 2026-08-09, Campaign CH slice CH2 (interface text / SpewBox) — AP-177 records the invented 5-second SpewBox line lifetime (retail's real timeout is keystone-owned and unmeasured); AP-178's original filing recorded the invented SpewBox screen position/extent/font/colour/MaxConcurrentItems after `SpewBoxLayoutDumpDiagnostic`'s Portal-only sweep found zero elements of class 0x10000016 — see the NARROWED note above for the corrected finding; AP-179 is the OnCombatLine half of the RETIRED AP-176 split out to its own row. AP-176 RETIRED the same day — the WeenieErrorMessages full 344-row `HandleFailureEvent` port (`WeenieErrorMessages.Resolve`) replaces the single-stand-in-`LogTextType` approximation that row recorded for `ChatLog.OnWeenieError`. AP-175 filed 2026-08-09, Campaign CH slice CH1 — PopUpString renders as a chat-log line instead of retail's modal dialog; AP-39 updated the same day — chat coloring is now retail's exact 34-value `LogTextType` table, not a synthetic per-`ChatKind` approximation of it. AP-173 and AP-174 filed 2026-08-08, Campaign A slice A2 — AP-173 expresses retail's ±15 dB DirectSound pan as an OpenAL azimuth by inverting the constant-power pan law, since AL exposes no per-channel gain for a mono source; AP-174 records acdream's extra master volume knob on top of retail's three, folded into retail's single master multiply so the −50 dB cutoff and dB quantisation move with it. AP-172 and AP-171 filed 2026-08-08, #354 spell-bar drag-reorder fix — the favorite-bar reorder gesture defers its own list rebuild for the drag's duration so `UiRoot`'s drag-cancel safety net cannot destroy the in-flight cell, compensating the drop-time target index for the resulting stale sibling numbering; final positions and the wire pair are retail-exact, only the mid-drag visual reflow timing differs. AP-170 filed 2026-08-08, grand-gate finding G3 — an out-of-range vendor Use now arms on arrival instead of sending immediately, because the user's local ACE server polls for the player to actually reach use range before opening the shop panel and a too-early Use is silently lost; AP-169 filed 2026-08-08, grand-gate finding G2 — the vendor toolbar split-slider resolver falls back to the packed shop-supply-count field when the item's own `PublicWeenieDesc._stackSize` is absent, because the user's local ACE server never populates the latter for a browse-list item; AP-167/AP-168 filed 2026-08-09 at the Opus review of `92ea3977` (findings F1/F6) — Buy All's container-vs-item slot classification approximates retail's bitfield/capacity test with `ItemType.Container` [AP-168], and SellSingleItem's non-empty-container refusal branch is not ported [AP-167]; AP-164 RETIRED the same review (finding F4) — BF_RETAINED is now checked end to end; AP-162 NARROWED the same review (finding F1) — Buy All's four client-side pre-send guards are now ported, leaving only the single-item TryBuy path without one; AP-161 gains a REVIEW CORRECTIONS paragraph the same review (findings F1-F13) summarizing the rest as bug fixes to already-claimed behavior, not new divergences. AP-164/AP-165/AP-166 filed 2026-08-09 at Slice 6b/6c (staging+sell arc) — InqAcceptability's non-sellable bitfield is unmodeled [AP-164], the Buy-side stackable-removal-amount test substitutes DescStackSize for retail's _maxStackSize [AP-165], and the Buying/Selling tabs' own purse/count text plus the cross-panel pending-sell inventory highlight are unwired [AP-166]; AP-161 NARROWED the same day — the row's last vendor-specific residual (Buying/Selling tabs render but carry no data binding) CLOSES now that both tabs are fully wired (staging, drag-to-sell, InqAcceptability gating, Sell 0x0060, the X-close confirmation), leaving only the two long-standing PRE-EXISTING residuals (dropdown arrow-cap glyph, alt-currency m_last_sale simplification) plus the three new AP-164/165/166 residuals just filed; AP-162 EXTENDED the same day — the same no-client-pre-check omission now also covers the batched "Buy All" path (TryBuyAll), not just the single-item TryBuy. AP-162/AP-163 filed 2026-08-09 at Slice 6.3 (buy arc) — no client-side Buy affordability/capacity pre-check [AP-162] and the shop-item guid-collision skip-not-clobber policy [AP-163]; AP-161 NARROWED the same day — the private-selection and unwired-examine residuals CLOSE at Slice 6.1/6.2, leaving only the dropdown arrow-cap glyph and the alt-currency `m_last_sale` simplification, plus a confirmed-absent-from-retail note on double-click-to-buy. AP-161 REWRITTEN 2026-08-09 at the Slice 5.4 review (findings F1-F8) — the popup-never-rendered, wrong-quantity-price, no-auto-select, dropped-icon-layer, stale-category-on-vendor-switch, and unguarded-Apply-fanout bugs the review found are fixed (`VendorUiController.cs`, `VendorState.cs`, `GameEventWiring.cs`, `RetailUiRuntime.cs`); the row now records only the four consciously-deferred residuals it still owns (private per-panel selection vs. retail's global `ACCWeenieObject::selectedID`, the unwired shop-item examine route, the dropdown button-face arrow-cap glyph, and the alt-currency held-amount's `m_last_sale`-free simplification). AP-110's "retail-correct per-unit prices" phrasing is corrected the same day to "quantity-correct pricing" — the OLD phrase mischaracterized what retail even shows (a `GetObjectSplitSize`-quantity price, not literally one unit) independent of whether the code was buggy. AP-161 filed 2026-08-09 at Slice 5.4 (vendor browse panel) — the authored "Buying"/"Selling" tabs render and switch pages but carry no data binding, per contract decision 8's required successor to AP-110's narrowing; AP-110 NARROWED the same day — "vendor" is retired from its absent-panels list now that the "Items" browse tab is user-reachable. AP-160 filed 2026-08-07 at Slice 5.3 — the client-local vendor-panel distance watcher closes on plain 3D center distance instead of retail/ACE's cylinder-gap distance, because Runtime has no per-entity collision radius/height source outside the App-layer's Setup-cylinder resolver. AP-158 RETIRED 2026-08-06 by the #333 fix, closing #337 — the `maxReach` distance pre-filter is DELETED rather than re-centred, because retail has none: `CObjCell::find_obj_collisions` @0x0052b750 walks the cell's shadow list and calls `CPhysicsObj::FindObjCollisions` unconditionally. The row's predicted symptom was observed live at Neftet before it was fixed — a tall prop AP-156 had just placed correctly still not blocking, plus jumps sinking into the mesh and corpses falling through. Perf measured, not assumed: at the live-maximum 38 in-cell candidates 10.61 µs → 16.68 µs per resolve. AP-159 filed 2026-08-06 at the #334 fix — the INDOOR half of AP-156’s traversal residual is all that remains of it; the outdoor half is CLOSED by the `find_bbox_cell_list` port, and AP-156’s RISK COLUMN IS CORRECTED at the same commit: it recorded the residual as “extra broadphase candidates, never a missed one”, which generalised the indoor direction to the whole row and is exactly why #334 — a MISSED one, and a user-observed loss of collision on landblock-spanning formations — sat inside it unnoticed. AP-158 filed 2026-08-06 at the AP-156 fix review — the shadow broadphase's `maxReach` distance pre-filter is acdream's own invention with NO retail counterpart, and it measures from the part origin, so it can discard a genuine contact for exactly the off-centre parts AP-156 just placed correctly; issue #333. AP-156 CORRECTED at the same review: its population was understated — 172 is AP-152's DISPATCH population, not AP-156's CONTAINMENT population. AP-155 NARROWED and AP-156/AP-157 filed 2026-08-06 at the AP-152 retail-conformance review. AP-155 bundled two divergences with different code paths, populations and gates under one id; its flood half is now AP-156, **with its direction corrected**. AP-155(b) recorded the BSP flood approximation as OVER-inclusive and used that direction as the reason the residual was safe to defer; measured over the installed DAT it was UNDER-inclusive for 428 of the 530 BSP-bearing Setups (the AP-156 fix review corrected the originally-recorded '170 of 172'), because `BuildFloodSpheres` carried each physics-BSP part's root bounding-sphere RADIUS while discarding that sphere's own ORIGIN and centring it on the part origin. That is the #98/#168 class, and for 43 Setups the post-AP-152 flood was strictly smaller than the pre-AP-152 one. AP-156 records the correction and the fix — `ShadowShape.BoundsCenter`, filled from the same resolver that supplies the radius, plus the retirement of the 10-sphere clamp on a branch where retail has none — and keeps open only the sphere-vs-portal TRAVERSAL approximation. AP-157 is the previously unregistered third-branch substitution: retail floods from one `CPartArray::GetSortingSphere` where acdream floods from every Sphere shape, and acdream's cylinder flood ignores `CylHeight`. AP-152 RETIRED 2026-08-06, one day after it was filed: `ShadowShapeBuilder.FromSetup` now dispatches BSP-first instead of unioning, and `ShadowObjectRegistry.BuildFloodSpheres` now applies `calc_cross_cells`' own BSP → cylsphere → sorting-sphere order. Four statements in the row were false and are corrected in its retirement text — most importantly its predicted symptom, "catching on a doorway sill", which could not have been occurring: `Transition.BspOnlyDispatch` had already made the extra primitive inert at collision-query time since 2026-05-25. The live half was CELL MEMBERSHIP, the #98/#168 symptom class, which had no such guard. AP-153/AP-154/AP-155 filed at that retirement — retail's dispatch flag is cached once at part-array construction where acdream's gate is live [AP-153]; acdream's query-time guard takes a CLIENT-DERIVED flag off the WIRE and never derives it, an undeclared dependency on ACE reading the same DAT bit [AP-154]; and the static publication paths emit a Setup Sphere as a height-capped Cylinder while `BuildFloodSpheres` approximates retail's bounding BOX with bounding SPHERES [AP-155, whose flood-priority half is closed by the same commit]. AP-152 filed 2026-08-06 at the AP-22 retirement — the LIVE collision path emits Setup primitives and per-part physics-BSP shapes additively where retail's `CPhysicsObj::FindObjCollisions` dispatches exclusively; 172 of 5,935 installed Setups are affected, including BSP doors, so it needs its own visual gate and was deliberately not folded into the AP-22 commit; the count is unchanged because AP-22 retired in the same commit. AP-22 RETIRED 2026-08-06 — retail synthesizes no shape for a shapeless object (`CPhysicsObj::FindObjCollisions` 0x0050f050 exits at `0x0050f22f je 0x50f31b` returning the seeded OK_TS, and `CPartArray::GetRadius`/`GetHeight` are absent from its whole call set), so the invented `setup.Radius` cylinder was deleted rather than re-derived; the row's site list named one file that never contained the fallback and omitted the two that did, one of them the headless-only copy, and its "rare decorative props" risk described an unreachable branch — 0 of 5,935 installed Setups can satisfy the guard. AP-150/AP-151 filed 2026-08-06 at the #280 dual review — the wait cue's five-second arming is acdream's own and not retail's trigger [AP-150], and the reveal gate is materially stricter than retail's DAT-residency prefetch predicate on the mesh-build/GPU-upload axis [AP-151], the opposite asymmetry from AP-149; AP-149 filed 2026-08-05 at the #280 portal-prefetch fix — the reveal gate's outer ring accepts terrain-only publication where retail requires LandBlockInfo and every building EnvCell; the fix closes the reveal-window/visible-window ratio, not this residual; AP-148 filed 2026-08-05 at the C5b closeout — acdream's local-player Gate A requires the wire TELEPORT_TS to be EQUAL where retail requires only that it not be OLDER, verified by disassembly against the PDB-paired binary after two review rounds read the Binary Ninja tautology and missed it; AP-147 filed 2026-08-05 at the C5b architecture review, finding D3 — the accepted-Position delta stream's cardinality change and its torn intermediate; AP-138 amended at the same review — C5b staled its route-2 first-submit `CurrentCellId` measurement; AP-131 RETIRED 2026-08-05, C5b, closing #275 — the steady-state merge's `installPlacementFrame: true, clearParent: true` literals no longer exist; `InboundPhysicsStateController.TryApplyPosition` now computes both flags PRE-MERGE from `(disposition, hasAnimations(old))`, which is exactly `RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition`'s own `ApplyPlacementFrameBeforeRouting`/`UnparentBeforeRouting` rows (false/false on the Gate A force row, `!HasAnimations`/true on every accepted non-force route). Retail decides both writes BEFORE `MoveOrTeleport` is consulted — Gate A @0x0045400C returns @0x0045409D ahead of `unset_parent` @0x00454129 and the `HasAnims` `SetPlacementFrame` gate @0x00454137 — so the flags need no route, no player distance and no signature change. The row's predicted symptoms are gone: an animated entity's ordinary Position no longer installs a placement frame retail skips, and a ForcePosition no longer unparents. Evidence: `InboundPhysicsStateControllerTests` — `ApplyOnAnimatedEntity_NeverInstallsTheWirePlacementFrame`, `ApplyOnNonAnimatedEntity_InstallsTheWirePlacementFrame`, `ForcePositionOnParentedLocalPlayer_RetainsTheParentAttachment`, and the 12-row `MergedPrePlacementFieldsMatchTheClassifiedRouteFlags` matrix which uses the production classifier as its oracle rather than re-encoding the table; all four sabotage-verified in both directions. The row's "the legacy caller is deleted at the production cutover" framing was overtaken: the caller was CORRECTED, not deleted, and remains the only production Position wire caller; AP-145 RETIRED 2026-08-05, C5a commit 1, closing #318 — `TryPublishPlace` now publishes the local player's Place through `LocalPlayerShadowSynchronizer.SyncPose`, the same publisher ordinary per-tick movement uses, instead of a direct `LocalPlayerShadowState.Set` that never touched `PhysicsEngine.ShadowObjects`; AP-1 RETIRED 2026-08-05, C5a deletion sweep — `PhysicsEngine.Resolve`/`ResolvePlacement`/`HasCellSurface` deleted outright, zero production callers, so "production zero-delta routes remain on the legacy resolver" is now structurally false; AP-146 filed 2026-08-05, #319 fix — the local player's canonical cell is written only at login/inbound-Position/teleport, not per ordinary-movement tick as retail's SetPositionInternal does; #319's fix makes a player-parented child inherit exactly this coarseness, stale-but-equal to the parent, not a new staleness class; follow-up filed as issue #320; AP-144 filed 2026-08-05, C4 route 3 round 3 (R7) — the portal-arrival movement-event send reuses `UsePositionFromServer` (`autonomy_level != 2`) where retail's actual gate, `SendMovementEvent`, is `autonomy_level != 0`; the two agree everywhere except level 1, which no production caller can reach today; AP-142/AP-143 filed 2026-08-04, C4 route 7 — the parented-child single-field cell model (id/pointer collapse, zero-not-stale removal propagation, same-cell tick-loop subsumption) and the headless parent-realize drive's skipped holding-location validation; AP-141 filed 2026-08-04, C4 route 5, NARROWED 2026-08-04 at the round-2 delta review — the far-branch StopInterpolating clause was wrong for the adopted-body case (it is now ported there) and the row's language now distinguishes "never armed" from "never re-anchored"; CORRECTED 2026-08-04 at the round-3 delta review — the risk column's "would drag the body toward a stale anchor" claim was itself wrong (the leash anchor is write-only; `ConstraintManager::adjust_offset` only brakes, never pulls) and is retracted; every half remains test-gated only, since ACE never sends a missile UpdatePosition; AP-140 filed AND RETIRED 2026-08-04 — filed at the Bug B Opus review because the two accepted-Position routing gates read the client `Airborne` flag, i.e. walkability, where retail's free-flight predicate is CONTACT, and Bug B had just turned "in contact, not on walkable ground" from unreachable into ordinary; retired the same day by pointing both gates at `PhysicsBody.InContact`, retail's literal `transient_state & 1` test at `InterpolationManager::adjust_offset` @0x00555D52 (bit 0 = `CONTACT_TS`, acclient.h:3690), while leaving `Airborne` and all five of its `!Body.OnWalkable` writers untouched — the narrow shape the row itself pinned. A remote sliding on a steep face now interpolates as retail does instead of snapping at UpdatePosition cadence; AP-139 filed 2026-08-04, Bug B remote steep-contact slide — the interpolation-queue clear on the landing edge, carried over from the deleted hand-rolled remote landing block; AP-81 narrowed the same day by that fix, which retired its whole GRAVITY half; AP-87 annotated the same day — its predicted symptom was observed live and then fixed at the source, with the row's own thresholds and conditions deliberately unchanged; AP-138 filed 2026-08-04, C4 route 4b-2 dual Opus review, parts (1) and (2) rewritten the same day at the DELTA review — the far snap's refusable-placement residual: store_position only on the outcomes that never reached the engine, the two quiescence parks made restorable at the source, with the rollback gated on the cell it actually restores into, rather than refused by a pre-flight that structurally cannot see them, and the leash not armed through a superseded incarnation; AP-137 filed 2026-08-04, C4 route 4b-2 and rewritten the same day at that review, `teleport_hook`'s call list completed at the delta review — the acdream-only null/rejected/cell-less leftover arm, what the deleted duplicated 96 m/4 m constant pairs actually computed, and the vacuous headless satisfaction; AP-136 filed 2026-08-04, C4 route 4b-1 review, NARROWED 2026-08-04 at the C4 route 4b-2 delta review and AMENDED 2026-08-04 by the cancelled-park presentation rollback (the row's "restored visible" claim covered only the CANONICAL half; the presentation half was never rolled back, which left a parked-then-cancelled remote that stops moving invisible in the world AND absent from the radar for the rest of the session — a defect, now fixed by the `WithdrawalRestored` receipt, with the selection residual filed as AD-63) — a cancelled lost-cell park re-shows the entity where retail keeps it hidden until cell load, and the rollback's scope now covers the two placement-side quiescence parks whenever the cell it restores into is not itself quiescing — round 4 (2026-08-04) applies that same test a second time at RESTORE time, because a retained park's rollback lands a packet later; AP-135 filed 2026-08-03, C4 route 4a — the airborne no-op's retained acdream bookkeeping; the stated total was 2 rows stale before that filing and is now a literal count of this section; AP-130/AP-131/AP-132 filed 2026-08-02, continuation-executor slice; AP-5 retired 2026-07-31 at Campaign P Slice 2A — every successful `step_down` now performs retail's final `PLACEMENT_INSERT`; AP-3/AP-4 retired 2026-07-31 at Campaign P Slice 1B — `transitional_insert` and `edge_slide` now preserve retail's valid-contact early return and Branch-1-first order; AP-127 retired 2026-07-31 by #268 — the complete augmentation chain is shared by character UI and Runtime movement; AP-30 retired 2026-07-30 by the movement parity audit — retail Frame::is_equal genuinely uses the 0.0002 epsilon [byte-confirmed], so the row recorded a NON-divergence; acdream already matches; AP-129 narrowed 2026-07-30 at the P4 Opus review fix — `CanMoveInto`/`RestrictionDB::IsAllowedIn` are now ported and fed end-to-end (CreateObject HouseOwner/HouseRestrictions/Monarch tail fields + live `House_UpdateRestrictions 0x0248`, resolved through `PhysicsEngine.Objects`), retiring the original "CanMoveInto entirely unmodeled, unconditional fail-closed" gap the row described — the review was triggered by `RestrictionObjPrevalenceInspectionTests` showing 103,766 of 729,888 installed EnvCells (the whole housing estate) carry a baked `RestrictionObj`, so the unconditional fail-closed default would have locked every house for every player including its own owner; AP-10 retired 2026-07-30 at Campaign P Slice P4 — restored retail's 0.1 m dry-corner water sink-in, full suite green proving the sticky-bit no-regression argument; AP-71 retired same slice — `check_entry_restrictions` ported at the head of the indoor `FindEnvCollisions` branch, `CellPhysics.RestrictionObj` wired from the DAT-baked `EnvCell` field in both the dev and production caching paths; AP-128 filed 2026-07-30 at the P3 Opus review — PK-timer clock basis; AP-25 retired 2026-07-30 at Campaign P Slice P1 — the vitae/enchantment-aware run/jump skill chain; AP-7 retired 2026-07-30 at Campaign P Slice P2 — `calc_friction`'s threshold ported to retail's confirmed 0.25f; its still-open cos(10°)-vs-0.99999536f Sledding constant question moved to AD-55) +## 3. Documented approximation (AP) — 137 active rows (AP-197 filed 2026-08-11 at the OP4 review-fix round (SF-1/S4) — Display Timestamps hardcodes retail's constructor-default format string instead of the per-character GenericQualitiesData key-1 override the parser reads and discards; AP-196 filed 2026-08-11 at the OP4 review-fix round (MUST-FIX 3 / blast M2) — the Group-C re-point's observable-default changes (ViewCombatTarget true→false) and the GameplaySettings store retirement (AutoTarget/AutoRepeatAttack/ViewCombatTarget deleted, the other five kept as write-behind mirrors); AP-195 RETIRED 2026-08-11 at Campaign OP slice OP5 — ported both halves left open at OP2 re-review closure: the ALL-set LED media swap (`UiButton.FaceFileOverride`, driven by the block-level `P0x10000082`/`P0x10000083` sprites now threaded through `ElementInfo`/`DatWidgetFactory`) and the `CreateChildren` self-sizing tail (`UiCheckboxBitfield64.Height` grows with `_contentHeight` per row; the ENCLOSING page ListBox reflows around the block's FINAL size via the new `UiTemplateListBox.AddPrebuiltRow`, reusing the ListBox's own stacking exactly as the row's own disposition menu allowed, rather than a third stacking path); AP-194 filed 2026-08-10 at Campaign OP slice OP1 — the GetDefaultOptionValue vs constructor-default disagreement for ConfirmVolatileRareUse/ShowHelm/ShowCloak (see the row below); AP-193 filed 2026-08-10 at Campaign OP slice OP1 — the 0x34 HearPKDeathMessages id/mask mapping is ACE-sourced (see the row below); AP-192 filed 2026-08-10 at the Campaign CH round-5 polish (S2) — authored outline `0x21`/`0x22` now reaches every text-bearing widget, but only at the element's effective-default state; per-STATE outline switching (dialog/character/combat buttons author `0x21` in state `0x3` only) is not ported; AP-191 filed 2026-08-10 at Campaign CH round 4 items 1+2 — the chat transcript's missing tag-colour (`0x1D`, green) and tag-font (`0x1C`) are deferred, needing a per-run tag concept `UiText.Line` does not have yet; AP-184 RETIRED 2026-08-10 at Campaign CH round 4 — the three PARTIAL `/help` group topics (channels/chatting/commands) are now COMPLETE verbatim listings, `ClientCommunicationSystem::HelpStupidChannelHack @0x0056f290` fully decoded (the "vftable slot" operands are the same pooled/mislabeled-data artifact as AP-186's own precedent, not real vtable dispatch — reading the function's own disassembly for the `push imm32` preceding each constructor call resolves them), closing ISSUES.md #364 (full retirement note later in this same list, at its own "AP-184 RETIRED 2026-08-10 at Campaign CH round 4, closing ISSUES.md #364 — filed 2026-08-09..." entry); AP-113 RETIRED 2026-08-10 at the consolidated-review round, SHOULD-FIX 3/1 byproduct — DoLifestone's own bad-args refusal text is now byte-recovered, see its retirement note below; AP-183 and AP-186 RETIRED 2026-08-10 by issue #363's interface-text seam — see their retirement notes below; AP-190 filed 2026-08-10 at Campaign CH slice CH6c — window opacity now fades every RetailWindowManager window on retail's focus-driven Default/Active mechanism, not just ChatInterface-derived ones, and ships gmMainChatUI's 1.0/1.0 default as the ONE shared default across every registered window (fixed from the original 0.5/1.0 base-ChatInterface value, per the row's own REWORDED (2)) instead of applying it only to ChatInterface-derived windows, retiring AP-40 (the prior "opacity is fixed at 0.75, no focus transition" row) in the same commit; AP-189 filed 2026-08-10 at the CH6a/b REJECT-review rework, SHOULD-FIX 5 — acdream's ONE shared 500-entry/200-line-display-tail chat log gives every window a shallower EFFECTIVE per-window scrollback depth than retail's own per-window 10,000-line log, though the accumulate-while-closed and independent-per-window-scroll BEHAVIORS are both correctly reproduced; AP-188 filed 2026-08-10 at Campaign CH slice CH6b — a floating chat window's chat entry always sends on the Say channel because the floaty LayoutDesc authors no talk-focus menu and acdream does not (yet) share the main window's currently-selected channel across all five chat-window instances; AP-187 filed 2026-08-10 at Campaign CH slice CH6b — the four floating chat windows' text-type filters persist in local `settings.json` only (`ChatSettings.ChatWindow1..4Filter`), with no analog to retail's server-side `0x1000008C` GameplayOptions blob, so a character's floaty filter customization does not travel between acdream installs or round-trip to/from a retail client sharing the same character; AP-186 RETIRED 2026-08-10, issue #363's interface-text seam — `ChatVM` now carries an `OnInterfaceText` hook (`Action?`) the App-layer composition wires to `RuntimeCommunicationState.AddText(text, RetailLogTextType.ClientLocal)`, exactly fix shape (a) this row's own filing proposed; `ChatCommandRouter`'s two local-presentation fallbacks (`RetailCommandHelpTable.UnknownCommand` and the degenerate-prefix "Unknown command: {verb}." refusal) now call `ShowInterfaceText` and reach the SpewBox, with a null-fallback into the chat log (still tagged `ClientLocal`) for hosts that never wire the hook (headless has no `ChatVM` at all). Closes ISSUES.md #367; AP-185 filed 2026-08-10 at Campaign CH slice CH6a — the chat window's UiLocked border-art cosmetic swap is unported, see the row for detail; AP-184 RETIRED 2026-08-10 at Campaign CH round 4, closing ISSUES.md #364 — filed 2026-08-09 at Campaign CH user-gate round 2, item 3, recording that three of the seven retail `/help` group-topic listings (channels/chatting/commands) remained PARTIAL because their detail text is built in full or in part by `ClientCommunicationSystem::HelpStupidChannelHack @0x0056f290`, which the filing believed "not decodable with confidence from a static string sweep" because Binary Ninja renders its three internal string operands as dereferences of unrelated vtable slots (`&ClientCommunicationSystem::\`vftable'.RecvNotice_StartBarberNotice` etc.). That belief was WRONG — the same pooled/mislabeled-data artifact this register already documented elsewhere (AP-113's retirement note) applies here too: reading the function's own disassembly for the `push imm32` immediately preceding each `PStringBase::PStringBase` constructor call (rather than trusting BN's line-grouped rendering, which hides the true instruction order) resolves all three operands directly — `"@"` + a one-character tag sliced from a shared wide literal `U"fvpca"`/`U"mh,."` (a wide string read through a narrow `char*` truncates at the first zero high byte, the "hack" retail's own function name calls out) + `" - Sends a broadcast to your "` + `ChannelSystem::GetChannelName`'s own literal switch-table result + `".\n"`. `ChannelsGroupDetail` (entirely 6 such calls), `ChattingGroupDetail` (6 more, plus a `HelpReply@0x00577A50` Summary-branch quirk that unconditionally emits reply+pr+mr together — read directly, not assumed), and `CommandsGroupDetail` (`HelpAllGroup`, a straight-line concatenation of every other group's Detail branch plus a handful of its own short one-liners, including a CONFIRMED retail saveui/loadui duplicate) are now COMPLETE verbatim listings, matching the four (death/status/text/allegiances) the original filing already had. See `RetailCommandHelpTable`'s class remarks and `RetailCommandHelpTableTests` for the full per-line address citations. Round 2 item 2 also deletes `PortalWaitNoticeController` (the dedicated centered-overlay presentation the user reported was the wrong retail surface) and reroutes the portal-space wait-cue notice through the same `AddText`/SpewBox chokepoint every other on-screen interface-text site uses — AP-178's open SpewBox position/extent/font/colour questions now cover this notice too, since its separate controller and consts are gone; no new row was needed for the surface mismatch itself, since it was never separately registered (`PortalWaitNoticeController`'s own doc comment asserted "not a chat message" as an accepted design, not a flagged divergence). AP-150 RETIRED 2026-08-09 at Campaign CH user-gate round 1, item D (#329) — `PortalTunnelPresentation.TickRotation` now emits `"In Portal Space - Please Wait..."` unconditionally on every rotation-segment expiry, exactly matching `gmSmartBoxUI::UseTime`'s `else`-arm at 0x004D6FCD, instead of gating on `_waitCueVisible`, which only ever went true after the invented 5-second `RuntimeWorldTransitState.RetailWaitCueDelay` hold; `RetailWaitCueDelay`/`ObserveWait`/`SetWaitCue` remain as `LocalPlayerTeleportController`'s own hold-delay telemetry (`RuntimePortalSnapshot.WaitCueShown`) but no longer gate the on-screen cue, so they are not a residual of this row — closes issue #329; AP-183 RETIRED 2026-08-10, issue #363 — every named site now routes through the `ChatVM.ShowInterfaceText`/`OnInterfaceText` seam (see AP-186's retirement note) at its correct retail type: `DoStupidChannelHack` ("You must specify the text you wish to say!", newly wired — the six legacy channel verbs previously fell through `ChatInputParser.Parse`'s pure `return null` with no message at all), `DoChannelList`/`On`/`Off` ("Please specify the channel name.", reclassified), `DoAllegiance` ("Please see @help Allegiance...", reclassified), `DoHouseAvailableList` (reclassified AND corrected to retail's own "Please see @help hslist for more information on how to use this command" string, replacing the acdream-synthesized "Usage: /hslist " fallback — verified `acclient_2013_pseudo_c.txt:381481`/`1029383`), and `DoReply` ("Someone must @tell you first!", newly wired for the message-but-no-last-teller branch only — bare `/r` with no message at all is a separate retail branch, deliberately still unported). `DoSpeaker`/`DoEndurance`/`DoTitle` are untouched, confirmed still correct at `0x00`. The generic bad-args fallback (`ChatCommandRouter.Submit`'s catalog dispatch) now resolves `WeenieErrorMessages.Resolve(0x026u, null)` ("That is not a valid command.", the exact port of `DoCommand @0x0057E46D`'s `HandleFailureEvent(0x26)`) instead of synthesizing a `"Usage: {Usage}"` line — cross-checked against five decompiled handlers (`DoDie` plus the four above), all `0x1A`, confirming the uniform routing decision; AP-182 filed 2026-08-09 at Campaign CH slice CH4, corrected at the CH4 REJECT-review (nit 11) — `@title` is wired to a pure no-op (the value is neither stored nor consumed anywhere) and also omits `DoTitle`'s three local failure messages; recount at the CH3 Opus review corrected a pre-existing off-by-one; AP-181 filed 2026-08-09, Campaign CH slice CH3 — the local chat spam throttle (`IsMessageSpam`) has no acdream port. AP-178 NARROWED 2026-08-09 at the CH2 REJECT-review rework NIT 3, wording corrected at the CH2 re-review nits pass (`docs/plans/2026-08-09-chat-parity-campaign.md`, nits 1/2/6) — the original `dats.Portal` pass used an id source that was not Portal's own (`dats.Portal.GetAllIdsOfType()` is empty for this type), so it established nothing about Portal either way; extending a correctly-paired sweep to `dats.Local` FOUND the SpewBox element there; extent (`450×72`) and `MaxConcurrentItems` (`4`, not the code-default `1`) are now AUTHORED, leaving absolute screen position, colour, AND vertical content flow (now TOP-aligned, acdream's own invention pending measurement) open. AP-180 filed 2026-08-09 at the CH2 REJECT-review rework — `RuntimeCommunicationState.AddText`'s `windowId` parameter is accepted but not consumed, so retail's dual-destination echo (a `0x1A` message with a non-zero `windowId` lands in both the SpewBox and its originating chat window) is unimplemented; latent today since every production caller passes `windowId = 0`. AP-177/AP-178/AP-179 filed 2026-08-09, Campaign CH slice CH2 (interface text / SpewBox) — AP-177 records the invented 5-second SpewBox line lifetime (retail's real timeout is keystone-owned and unmeasured); AP-178's original filing recorded the invented SpewBox screen position/extent/font/colour/MaxConcurrentItems after `SpewBoxLayoutDumpDiagnostic`'s Portal-only sweep found zero elements of class 0x10000016 — see the NARROWED note above for the corrected finding; AP-179 is the OnCombatLine half of the RETIRED AP-176 split out to its own row. AP-176 RETIRED the same day — the WeenieErrorMessages full 344-row `HandleFailureEvent` port (`WeenieErrorMessages.Resolve`) replaces the single-stand-in-`LogTextType` approximation that row recorded for `ChatLog.OnWeenieError`. AP-175 filed 2026-08-09, Campaign CH slice CH1 — PopUpString renders as a chat-log line instead of retail's modal dialog; AP-39 updated the same day — chat coloring is now retail's exact 34-value `LogTextType` table, not a synthetic per-`ChatKind` approximation of it. AP-173 and AP-174 filed 2026-08-08, Campaign A slice A2 — AP-173 expresses retail's ±15 dB DirectSound pan as an OpenAL azimuth by inverting the constant-power pan law, since AL exposes no per-channel gain for a mono source; AP-174 records acdream's extra master volume knob on top of retail's three, folded into retail's single master multiply so the −50 dB cutoff and dB quantisation move with it. AP-172 and AP-171 filed 2026-08-08, #354 spell-bar drag-reorder fix — the favorite-bar reorder gesture defers its own list rebuild for the drag's duration so `UiRoot`'s drag-cancel safety net cannot destroy the in-flight cell, compensating the drop-time target index for the resulting stale sibling numbering; final positions and the wire pair are retail-exact, only the mid-drag visual reflow timing differs. AP-170 filed 2026-08-08, grand-gate finding G3 — an out-of-range vendor Use now arms on arrival instead of sending immediately, because the user's local ACE server polls for the player to actually reach use range before opening the shop panel and a too-early Use is silently lost; AP-169 filed 2026-08-08, grand-gate finding G2 — the vendor toolbar split-slider resolver falls back to the packed shop-supply-count field when the item's own `PublicWeenieDesc._stackSize` is absent, because the user's local ACE server never populates the latter for a browse-list item; AP-167/AP-168 filed 2026-08-09 at the Opus review of `92ea3977` (findings F1/F6) — Buy All's container-vs-item slot classification approximates retail's bitfield/capacity test with `ItemType.Container` [AP-168], and SellSingleItem's non-empty-container refusal branch is not ported [AP-167]; AP-164 RETIRED the same review (finding F4) — BF_RETAINED is now checked end to end; AP-162 NARROWED the same review (finding F1) — Buy All's four client-side pre-send guards are now ported, leaving only the single-item TryBuy path without one; AP-161 gains a REVIEW CORRECTIONS paragraph the same review (findings F1-F13) summarizing the rest as bug fixes to already-claimed behavior, not new divergences. AP-164/AP-165/AP-166 filed 2026-08-09 at Slice 6b/6c (staging+sell arc) — InqAcceptability's non-sellable bitfield is unmodeled [AP-164], the Buy-side stackable-removal-amount test substitutes DescStackSize for retail's _maxStackSize [AP-165], and the Buying/Selling tabs' own purse/count text plus the cross-panel pending-sell inventory highlight are unwired [AP-166]; AP-161 NARROWED the same day — the row's last vendor-specific residual (Buying/Selling tabs render but carry no data binding) CLOSES now that both tabs are fully wired (staging, drag-to-sell, InqAcceptability gating, Sell 0x0060, the X-close confirmation), leaving only the two long-standing PRE-EXISTING residuals (dropdown arrow-cap glyph, alt-currency m_last_sale simplification) plus the three new AP-164/165/166 residuals just filed; AP-162 EXTENDED the same day — the same no-client-pre-check omission now also covers the batched "Buy All" path (TryBuyAll), not just the single-item TryBuy. AP-162/AP-163 filed 2026-08-09 at Slice 6.3 (buy arc) — no client-side Buy affordability/capacity pre-check [AP-162] and the shop-item guid-collision skip-not-clobber policy [AP-163]; AP-161 NARROWED the same day — the private-selection and unwired-examine residuals CLOSE at Slice 6.1/6.2, leaving only the dropdown arrow-cap glyph and the alt-currency `m_last_sale` simplification, plus a confirmed-absent-from-retail note on double-click-to-buy. AP-161 REWRITTEN 2026-08-09 at the Slice 5.4 review (findings F1-F8) — the popup-never-rendered, wrong-quantity-price, no-auto-select, dropped-icon-layer, stale-category-on-vendor-switch, and unguarded-Apply-fanout bugs the review found are fixed (`VendorUiController.cs`, `VendorState.cs`, `GameEventWiring.cs`, `RetailUiRuntime.cs`); the row now records only the four consciously-deferred residuals it still owns (private per-panel selection vs. retail's global `ACCWeenieObject::selectedID`, the unwired shop-item examine route, the dropdown button-face arrow-cap glyph, and the alt-currency held-amount's `m_last_sale`-free simplification). AP-110's "retail-correct per-unit prices" phrasing is corrected the same day to "quantity-correct pricing" — the OLD phrase mischaracterized what retail even shows (a `GetObjectSplitSize`-quantity price, not literally one unit) independent of whether the code was buggy. AP-161 filed 2026-08-09 at Slice 5.4 (vendor browse panel) — the authored "Buying"/"Selling" tabs render and switch pages but carry no data binding, per contract decision 8's required successor to AP-110's narrowing; AP-110 NARROWED the same day — "vendor" is retired from its absent-panels list now that the "Items" browse tab is user-reachable. AP-160 filed 2026-08-07 at Slice 5.3 — the client-local vendor-panel distance watcher closes on plain 3D center distance instead of retail/ACE's cylinder-gap distance, because Runtime has no per-entity collision radius/height source outside the App-layer's Setup-cylinder resolver. AP-158 RETIRED 2026-08-06 by the #333 fix, closing #337 — the `maxReach` distance pre-filter is DELETED rather than re-centred, because retail has none: `CObjCell::find_obj_collisions` @0x0052b750 walks the cell's shadow list and calls `CPhysicsObj::FindObjCollisions` unconditionally. The row's predicted symptom was observed live at Neftet before it was fixed — a tall prop AP-156 had just placed correctly still not blocking, plus jumps sinking into the mesh and corpses falling through. Perf measured, not assumed: at the live-maximum 38 in-cell candidates 10.61 µs → 16.68 µs per resolve. AP-159 filed 2026-08-06 at the #334 fix — the INDOOR half of AP-156’s traversal residual is all that remains of it; the outdoor half is CLOSED by the `find_bbox_cell_list` port, and AP-156’s RISK COLUMN IS CORRECTED at the same commit: it recorded the residual as “extra broadphase candidates, never a missed one”, which generalised the indoor direction to the whole row and is exactly why #334 — a MISSED one, and a user-observed loss of collision on landblock-spanning formations — sat inside it unnoticed. AP-158 filed 2026-08-06 at the AP-156 fix review — the shadow broadphase's `maxReach` distance pre-filter is acdream's own invention with NO retail counterpart, and it measures from the part origin, so it can discard a genuine contact for exactly the off-centre parts AP-156 just placed correctly; issue #333. AP-156 CORRECTED at the same review: its population was understated — 172 is AP-152's DISPATCH population, not AP-156's CONTAINMENT population. AP-155 NARROWED and AP-156/AP-157 filed 2026-08-06 at the AP-152 retail-conformance review. AP-155 bundled two divergences with different code paths, populations and gates under one id; its flood half is now AP-156, **with its direction corrected**. AP-155(b) recorded the BSP flood approximation as OVER-inclusive and used that direction as the reason the residual was safe to defer; measured over the installed DAT it was UNDER-inclusive for 428 of the 530 BSP-bearing Setups (the AP-156 fix review corrected the originally-recorded '170 of 172'), because `BuildFloodSpheres` carried each physics-BSP part's root bounding-sphere RADIUS while discarding that sphere's own ORIGIN and centring it on the part origin. That is the #98/#168 class, and for 43 Setups the post-AP-152 flood was strictly smaller than the pre-AP-152 one. AP-156 records the correction and the fix — `ShadowShape.BoundsCenter`, filled from the same resolver that supplies the radius, plus the retirement of the 10-sphere clamp on a branch where retail has none — and keeps open only the sphere-vs-portal TRAVERSAL approximation. AP-157 is the previously unregistered third-branch substitution: retail floods from one `CPartArray::GetSortingSphere` where acdream floods from every Sphere shape, and acdream's cylinder flood ignores `CylHeight`. AP-152 RETIRED 2026-08-06, one day after it was filed: `ShadowShapeBuilder.FromSetup` now dispatches BSP-first instead of unioning, and `ShadowObjectRegistry.BuildFloodSpheres` now applies `calc_cross_cells`' own BSP → cylsphere → sorting-sphere order. Four statements in the row were false and are corrected in its retirement text — most importantly its predicted symptom, "catching on a doorway sill", which could not have been occurring: `Transition.BspOnlyDispatch` had already made the extra primitive inert at collision-query time since 2026-05-25. The live half was CELL MEMBERSHIP, the #98/#168 symptom class, which had no such guard. AP-153/AP-154/AP-155 filed at that retirement — retail's dispatch flag is cached once at part-array construction where acdream's gate is live [AP-153]; acdream's query-time guard takes a CLIENT-DERIVED flag off the WIRE and never derives it, an undeclared dependency on ACE reading the same DAT bit [AP-154]; and the static publication paths emit a Setup Sphere as a height-capped Cylinder while `BuildFloodSpheres` approximates retail's bounding BOX with bounding SPHERES [AP-155, whose flood-priority half is closed by the same commit]. AP-152 filed 2026-08-06 at the AP-22 retirement — the LIVE collision path emits Setup primitives and per-part physics-BSP shapes additively where retail's `CPhysicsObj::FindObjCollisions` dispatches exclusively; 172 of 5,935 installed Setups are affected, including BSP doors, so it needs its own visual gate and was deliberately not folded into the AP-22 commit; the count is unchanged because AP-22 retired in the same commit. AP-22 RETIRED 2026-08-06 — retail synthesizes no shape for a shapeless object (`CPhysicsObj::FindObjCollisions` 0x0050f050 exits at `0x0050f22f je 0x50f31b` returning the seeded OK_TS, and `CPartArray::GetRadius`/`GetHeight` are absent from its whole call set), so the invented `setup.Radius` cylinder was deleted rather than re-derived; the row's site list named one file that never contained the fallback and omitted the two that did, one of them the headless-only copy, and its "rare decorative props" risk described an unreachable branch — 0 of 5,935 installed Setups can satisfy the guard. AP-150/AP-151 filed 2026-08-06 at the #280 dual review — the wait cue's five-second arming is acdream's own and not retail's trigger [AP-150], and the reveal gate is materially stricter than retail's DAT-residency prefetch predicate on the mesh-build/GPU-upload axis [AP-151], the opposite asymmetry from AP-149; AP-149 filed 2026-08-05 at the #280 portal-prefetch fix — the reveal gate's outer ring accepts terrain-only publication where retail requires LandBlockInfo and every building EnvCell; the fix closes the reveal-window/visible-window ratio, not this residual; AP-148 filed 2026-08-05 at the C5b closeout — acdream's local-player Gate A requires the wire TELEPORT_TS to be EQUAL where retail requires only that it not be OLDER, verified by disassembly against the PDB-paired binary after two review rounds read the Binary Ninja tautology and missed it; AP-147 filed 2026-08-05 at the C5b architecture review, finding D3 — the accepted-Position delta stream's cardinality change and its torn intermediate; AP-138 amended at the same review — C5b staled its route-2 first-submit `CurrentCellId` measurement; AP-131 RETIRED 2026-08-05, C5b, closing #275 — the steady-state merge's `installPlacementFrame: true, clearParent: true` literals no longer exist; `InboundPhysicsStateController.TryApplyPosition` now computes both flags PRE-MERGE from `(disposition, hasAnimations(old))`, which is exactly `RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition`'s own `ApplyPlacementFrameBeforeRouting`/`UnparentBeforeRouting` rows (false/false on the Gate A force row, `!HasAnimations`/true on every accepted non-force route). Retail decides both writes BEFORE `MoveOrTeleport` is consulted — Gate A @0x0045400C returns @0x0045409D ahead of `unset_parent` @0x00454129 and the `HasAnims` `SetPlacementFrame` gate @0x00454137 — so the flags need no route, no player distance and no signature change. The row's predicted symptoms are gone: an animated entity's ordinary Position no longer installs a placement frame retail skips, and a ForcePosition no longer unparents. Evidence: `InboundPhysicsStateControllerTests` — `ApplyOnAnimatedEntity_NeverInstallsTheWirePlacementFrame`, `ApplyOnNonAnimatedEntity_InstallsTheWirePlacementFrame`, `ForcePositionOnParentedLocalPlayer_RetainsTheParentAttachment`, and the 12-row `MergedPrePlacementFieldsMatchTheClassifiedRouteFlags` matrix which uses the production classifier as its oracle rather than re-encoding the table; all four sabotage-verified in both directions. The row's "the legacy caller is deleted at the production cutover" framing was overtaken: the caller was CORRECTED, not deleted, and remains the only production Position wire caller; AP-145 RETIRED 2026-08-05, C5a commit 1, closing #318 — `TryPublishPlace` now publishes the local player's Place through `LocalPlayerShadowSynchronizer.SyncPose`, the same publisher ordinary per-tick movement uses, instead of a direct `LocalPlayerShadowState.Set` that never touched `PhysicsEngine.ShadowObjects`; AP-1 RETIRED 2026-08-05, C5a deletion sweep — `PhysicsEngine.Resolve`/`ResolvePlacement`/`HasCellSurface` deleted outright, zero production callers, so "production zero-delta routes remain on the legacy resolver" is now structurally false; AP-146 filed 2026-08-05, #319 fix — the local player's canonical cell is written only at login/inbound-Position/teleport, not per ordinary-movement tick as retail's SetPositionInternal does; #319's fix makes a player-parented child inherit exactly this coarseness, stale-but-equal to the parent, not a new staleness class; follow-up filed as issue #320; AP-144 filed 2026-08-05, C4 route 3 round 3 (R7) — the portal-arrival movement-event send reuses `UsePositionFromServer` (`autonomy_level != 2`) where retail's actual gate, `SendMovementEvent`, is `autonomy_level != 0`; the two agree everywhere except level 1, which no production caller can reach today; AP-142/AP-143 filed 2026-08-04, C4 route 7 — the parented-child single-field cell model (id/pointer collapse, zero-not-stale removal propagation, same-cell tick-loop subsumption) and the headless parent-realize drive's skipped holding-location validation; AP-141 filed 2026-08-04, C4 route 5, NARROWED 2026-08-04 at the round-2 delta review — the far-branch StopInterpolating clause was wrong for the adopted-body case (it is now ported there) and the row's language now distinguishes "never armed" from "never re-anchored"; CORRECTED 2026-08-04 at the round-3 delta review — the risk column's "would drag the body toward a stale anchor" claim was itself wrong (the leash anchor is write-only; `ConstraintManager::adjust_offset` only brakes, never pulls) and is retracted; every half remains test-gated only, since ACE never sends a missile UpdatePosition; AP-140 filed AND RETIRED 2026-08-04 — filed at the Bug B Opus review because the two accepted-Position routing gates read the client `Airborne` flag, i.e. walkability, where retail's free-flight predicate is CONTACT, and Bug B had just turned "in contact, not on walkable ground" from unreachable into ordinary; retired the same day by pointing both gates at `PhysicsBody.InContact`, retail's literal `transient_state & 1` test at `InterpolationManager::adjust_offset` @0x00555D52 (bit 0 = `CONTACT_TS`, acclient.h:3690), while leaving `Airborne` and all five of its `!Body.OnWalkable` writers untouched — the narrow shape the row itself pinned. A remote sliding on a steep face now interpolates as retail does instead of snapping at UpdatePosition cadence; AP-139 filed 2026-08-04, Bug B remote steep-contact slide — the interpolation-queue clear on the landing edge, carried over from the deleted hand-rolled remote landing block; AP-81 narrowed the same day by that fix, which retired its whole GRAVITY half; AP-87 annotated the same day — its predicted symptom was observed live and then fixed at the source, with the row's own thresholds and conditions deliberately unchanged; AP-138 filed 2026-08-04, C4 route 4b-2 dual Opus review, parts (1) and (2) rewritten the same day at the DELTA review — the far snap's refusable-placement residual: store_position only on the outcomes that never reached the engine, the two quiescence parks made restorable at the source, with the rollback gated on the cell it actually restores into, rather than refused by a pre-flight that structurally cannot see them, and the leash not armed through a superseded incarnation; AP-137 filed 2026-08-04, C4 route 4b-2 and rewritten the same day at that review, `teleport_hook`'s call list completed at the delta review — the acdream-only null/rejected/cell-less leftover arm, what the deleted duplicated 96 m/4 m constant pairs actually computed, and the vacuous headless satisfaction; AP-136 filed 2026-08-04, C4 route 4b-1 review, NARROWED 2026-08-04 at the C4 route 4b-2 delta review and AMENDED 2026-08-04 by the cancelled-park presentation rollback (the row's "restored visible" claim covered only the CANONICAL half; the presentation half was never rolled back, which left a parked-then-cancelled remote that stops moving invisible in the world AND absent from the radar for the rest of the session — a defect, now fixed by the `WithdrawalRestored` receipt, with the selection residual filed as AD-63) — a cancelled lost-cell park re-shows the entity where retail keeps it hidden until cell load, and the rollback's scope now covers the two placement-side quiescence parks whenever the cell it restores into is not itself quiescing — round 4 (2026-08-04) applies that same test a second time at RESTORE time, because a retained park's rollback lands a packet later; AP-135 filed 2026-08-03, C4 route 4a — the airborne no-op's retained acdream bookkeeping; the stated total was 2 rows stale before that filing and is now a literal count of this section; AP-130/AP-131/AP-132 filed 2026-08-02, continuation-executor slice; AP-5 retired 2026-07-31 at Campaign P Slice 2A — every successful `step_down` now performs retail's final `PLACEMENT_INSERT`; AP-3/AP-4 retired 2026-07-31 at Campaign P Slice 1B — `transitional_insert` and `edge_slide` now preserve retail's valid-contact early return and Branch-1-first order; AP-127 retired 2026-07-31 by #268 — the complete augmentation chain is shared by character UI and Runtime movement; AP-30 retired 2026-07-30 by the movement parity audit — retail Frame::is_equal genuinely uses the 0.0002 epsilon [byte-confirmed], so the row recorded a NON-divergence; acdream already matches; AP-129 narrowed 2026-07-30 at the P4 Opus review fix — `CanMoveInto`/`RestrictionDB::IsAllowedIn` are now ported and fed end-to-end (CreateObject HouseOwner/HouseRestrictions/Monarch tail fields + live `House_UpdateRestrictions 0x0248`, resolved through `PhysicsEngine.Objects`), retiring the original "CanMoveInto entirely unmodeled, unconditional fail-closed" gap the row described — the review was triggered by `RestrictionObjPrevalenceInspectionTests` showing 103,766 of 729,888 installed EnvCells (the whole housing estate) carry a baked `RestrictionObj`, so the unconditional fail-closed default would have locked every house for every player including its own owner; AP-10 retired 2026-07-30 at Campaign P Slice P4 — restored retail's 0.1 m dry-corner water sink-in, full suite green proving the sticky-bit no-regression argument; AP-71 retired same slice — `check_entry_restrictions` ported at the head of the indoor `FindEnvCollisions` branch, `CellPhysics.RestrictionObj` wired from the DAT-baked `EnvCell` field in both the dev and production caching paths; AP-128 filed 2026-07-30 at the P3 Opus review — PK-timer clock basis; AP-25 retired 2026-07-30 at Campaign P Slice P1 — the vitae/enchantment-aware run/jump skill chain; AP-7 retired 2026-07-30 at Campaign P Slice P2 — `calc_friction`'s threshold ported to retail's confirmed 0.25f; its still-open cos(10°)-vs-0.99999536f Sledding constant question moved to AD-55) Wave-0 UI ledger repair (2026-07-10) retired stale AP-38, resolved the AP-84 collision, restored overwritten paperdoll rows as AP-92/AP-93, and registered @@ -184,7 +184,6 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps. | # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle | |---|---|---|---|---|---| | AP-194 | `CharacterOptionTable`'s `ClientDefault` column (what the Character tab's Defaults button restores) disagrees with the raw constructor default word for three ids: `ConfirmVolatileRareUse` (`0x2D`), `ShowHelm` (`0x2F`), and `ShowCloak` (`0x32`) are all ON in retail's constructor default `CharacterOptions2 = 0x00948700` (`PlayerModule::PlayerModule @0x005D51F0`, byte-verified literal write) but report default-OFF via `PlayerModule::GetDefaultOptionValue @0x005D2A30`, whose own per-option table stops at id `0x2A` and returns `false` for everything past it. This is retail's OWN behavior, reproduced deliberately — the Defaults button does not reproduce a fresh `PlayerModule`. **CONFIRMED 2026-08-11 at Campaign OP slice OP4**: `CharacterOptionsPageController` seeds every `BoolOptionRow`'s default directly from this column (`EveryRow_DefaultValue_MatchesCharacterOptionTableClientDefault`, `tests/AcDream.App.Tests/UI/Layout/CharacterOptionsPageControllerTests.cs`); the directive below was followed, not re-litigated. OP4 also independently traced retail's OWN mechanism for the Character tab specifically — `UIOption_Checkbox::SetPlayerOption @0x00486e80` (pseudo-C line 147375) sets `m_default` directly from `GetDefaultOptionValue`, confirming this column (not the separate `DBPropertyCollection`/`InqDefaultGameplayOptionProperty` mechanism that governs the Chat/Config tabs' `m_propName`-bound rows) is the correct and ONLY source for this tab. | `src/AcDream.Runtime/Gameplay/CharacterOptionTable.cs` (`ClientDefault` column; see the type's XML doc); `src/AcDream.App/UI/Layout/CharacterOptionsPageController.cs` | Byte-verified at both addresses (wire research §2.5 for the constructor literals, §8.2 for `GetDefaultOptionValue`'s own table and bounds check) — this is not a guess, it is retail's documented quirk. "Fixing" it to match the constructor default would make acdream's Defaults button MORE correct than retail's own, which is the opposite of this project's goal. | A future OP-campaign slice (OP4, the Character tab's Defaults button) must consult THIS column, not the constructor default word, or a future reader may "fix" this back and silently diverge from retail. | `PlayerModule::GetDefaultOptionValue @0x005D2A30`; `UIOption_Checkbox::SetPlayerOption @0x00486e80` (N-4 anchor-column correction, OP4 review-fix round 2026-08-11 — was mislabeled `PlayerModule::SetPlayerOption`, same address, wrong class); `PlayerModule::PlayerModule @0x005D51F0`; `docs/research/2026-08-10-set-character-options-wire.md` §8.2 | -| AP-195 | **Filed 2026-08-11 (Campaign OP OP2 re-review closure; supersedes the ported half of retired TS-72).** `UIOption_CheckboxBitfield64` ports HALF of retail's `Refresh @0x004859C0`: the ANY-set predicate driving each row's checkbox bool is exact, but the same retail pass also computes the ALL-set predicate to swap each row's LED media between the two surfaces authored on template consumer `0x10000520` (`P0x10000082 = 0x06004D17` checked-art / `P0x10000083 = 0x06004D19` unchecked-art) — acdream reads neither property anywhere. Retail's `CreateChildren` tail also self-sizes the block (`ResizeTo(GetWidth(), CalculatePaperSize(0, -1))`) because `UIOption_CheckboxBitfield64` IS a `UIElement_ListBox` (its `PostInit` tail-calls `UIElement_Scrollable::PostInit`); acdream's block instead keeps its authored 272×100 extent while stacking rows itself — a second, divergent implementation of the row-stacking mechanism beside `UiTemplateListBox` (which stacks into a lazy `UiScrollablePanel`). The row-index attribute stamp (`SetAttribute_Int(cb, 0x10000084, i)` @`0x00485E3E`) is deliberately replaced by a typed mask closure — equivalent click routing without the attribute round-trip; NOT part of this row's gap. `src/AcDream.App/UI/UiCheckboxBitfield64.cs` (`IsSet`/`AddChild`) | The checkbox bool — the half every wire-visible behavior flows through — is exact and conformance-tested (multi-bit discriminating test); the LED media swap and self-sizing are presentation-only, invisible until the Chat tab actually mounts a bitfield block, and porting them properly wants the ListBox unification (reuse `UiTemplateListBox`'s stacking or record why not) rather than a third stacking path bolted on in a closure commit | The Chat tab's five filter blocks (the only authored bitfield consumers, 12/13 rows each) would render rows whose LED art never switches between checked/unchecked surfaces and whose block height stays the authored 272×100 instead of growing to fit 13 rows (~260 px + chrome) — visibly wrong the moment OP5 mounts them; both gaps MUST close (or this row be consciously re-scoped) in OP5 before its connected gate | `UIOption_CheckboxBitfield64::Refresh @0x004859C0`; `CreateChildren @0x00485DF0`; `ListenToElementMessage @0x00485AE0` (`BitUtils::SetBitsOnOrOff`); `docs/research/2026-08-11-op2-rereview-mechanism.md` §2.1–§2.3 | | AP-193 | Character option id `0x34` (`ListenToPKDeathMessages` / "Listen to PK death messages") is mapped to `CharacterOptions2` bit `0x02000000` and modeled as a batched (non-auto-save) option purely on ACE's own enum — the id does not exist in the 2013 EoR PDB (`PlayerOption` there terminates at `TotalNumberOfPlayerOptions_PlayerOption = 0x34`), so neither the mask nor its `IsAutoSaveOption`/`GetDefaultOptionValue` classification is byte-verifiable against our binary. | `src/AcDream.Runtime/Gameplay/CharacterOptionTable.cs` (`HearPkDeathMessages` row) | The user's retail memory (and ACE's own `CharacterOption` enum) both carry this option; shipping wire+store coverage for it is strictly better than omitting the row the Character tab's screenshots show, and ACE never actually reads the bit server-side (`PlayerFactory.cs:659-660` — "possibly was added to Defaults post PDB we have"), so a wrong id/mask/auto-save guess here has zero server-observable consequence either way. | If the final EoR client's real id/mask/auto-save classification ever surfaces (a later PDB, or a byte-level trace against a 2015+ binary), this row's values may be wrong and need correcting — until then treat them as ACE-sourced, not retail-verified. | ACE `PlayerFactory.cs:659-660`, `CharacterOptions2.cs` (`ListenToPKDeathMessages = 0x02000000`); `named-retail/acclient.h:4162-4218` (2013 `PlayerOption` terminates at `0x34`); `docs/research/2026-08-10-set-character-options-wire.md` §8.1 | | AP-196 | **Filed 2026-08-11 at the OP4 review-fix round (MUST-FIX 3 / blast M2).** OP4's Group-C re-point (`AutoTarget`/`AutoRepeatAttack`/`ViewCombatTarget`/`VividTargetingIndicator`/`CoordinatesOnRadar`/`LockUI`/`AcceptLootPermits`/`ToggleRun`) moved these options' EFFECTIVE default source from the client-local `GameplaySettings.Default` record (or a persisted `settings.json`) to the retail constructor word, read live through `RuntimeCharacterOptionsState`/`CharacterOptionTable.ClientDefault` — a fresh install's observable default for `ViewCombatTarget` FLIPS from `true` (the old `GameplaySettings.Default`) to `false` (retail's real `CharacterOptions1` default — bit `0x80` is clear in `0x50C4A54A`). This fix round additionally DELETED the three now-fully-orphaned `GameplaySettings` fields (`AutoTarget`/`AutoRepeatAttack`/`ViewCombatTarget`) and their `RuntimeSettingsController` mirror properties/`SetCombatGameplay` method outright — the Combat panel's own three LEDs (`CombatUiController`) now read/write the SAME `RuntimeCharacterOptionsState` seam the Character tab's rows use, closing the "two writable copies" divergence the blast review found. The other five re-pointed options remain present in `GameplaySettings` as WRITE-BEHIND persistence/draft mirrors (`settings.json` still records the last-known value for restart continuity and Settings-panel draft consistency) — but the AUTHORITATIVE read for gameplay behavior is always the live server bit. | `src/AcDream.UI.Abstractions/Panels/Settings/GameplaySettings.cs`; `src/AcDream.App/Settings/RuntimeSettingsController.cs`; `src/AcDream.App/UI/Layout/CombatUiController.cs`; `src/AcDream.App/Combat/LiveCombatAttackOperations.cs` (`CharacterOptionCombatSettingsSource`) | Server-authoritative reads matching CH3's established precedent are strictly more correct than a client-local snapshot that can silently diverge from the character's real server state; the three deleted fields had zero remaining production consumers once the Combat panel's LEDs were re-pointed, so deleting rather than deprecating avoids a permanently-dead second store. | A fresh install (or a character whose `settings.json` predates this change) shows `ViewCombatTarget` OFF by default where it previously showed ON — a deliberate correctness fix, not a regression, if a future report cites "combat camera doesn't track by default." Any future code that still expects `GameplaySettings` to carry `AutoTarget`/`AutoRepeatAttack`/`ViewCombatTarget` fails to compile — a forcing function, not a silent gap. | `CharacterOptions1.Default = 0x50C4A54A` (`PlayerModule::PlayerModule @0x005D51F0`); `CharacterOptionTable.cs`'s `ClientDefault` column; `docs/research/2026-08-11-op4-review-blast.md` §S3 | | AP-197 | **Filed 2026-08-11 at the OP4 review-fix round (SF-1/S4).** "Display Timestamps" hardcodes retail's `PlayerModule` constructor-default format string `"%#H:%M:%S "` rather than reading the PER-CHARACTER override `GenericQualitiesData::InqString(m_pPlayerOptionsData, 1, &m_TimeStampFormat)` carries when the wire's `GenericQualitiesData` string-key `1` is populated — acdream's `PlayerDescription` parser reads and discards that field (wire research doc: "timestamp string (`0x80`) \| read, discarded \| ❌ \| never sent"). | `src/AcDream.Core/Chat/ChatLog.cs` (`FormatTimestampPrefix`); parser site cited at `docs/research/2026-08-10-set-character-options-wire.md:647` | The 2013 client's own constructor default is the only format any fresh/default character would ever show — retail ships no options-panel control that authors a custom one — so hardcoding the one value every real player sees is a safe, honest approximation until a consumer needs the per-character override. | A character whose account somehow carries a non-default persisted timestamp format (a modded/legacy server, or a hypothetical later retail patch exposing a UI for it) sees acdream render the DEFAULT format instead of their stored one — cosmetic only (still a valid H:MM:SS-shaped timestamp), never a wire or data-loss risk. | `PlayerModule::PlayerModule @0x005D51F0` (ctor default literal); `GenericQualitiesData::InqString` call site (wire doc §3.3); `docs/research/2026-08-10-set-character-options-wire.md` U6 | @@ -347,7 +346,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps. | AP-181 | **Filed 2026-08-09 (Campaign CH slice CH3, side-channel gate); corrected 2026-08-09 at the CH3 Opus review (S6) — the original text named only the spam throttle and wrongly credited `RouteLegacyChannel` with porting gates it has no code for.** Retail's `SendTurbineChat @0x0057db10` runs TWO local pre-send refusals acdream has no port for, in this order: `IsMessageSafe(text)` first (a silent drop — no wire send, no local text at all), then, only if that passes, the per-account spam throttle `IsMessageSpam()` (→ "You must wait %ds before communicating again!"). acdream's `TurbineChatMembershipGate`/`RouteTurbineChat` port the Turbine-unavailable and Hear-option gates that run BEFORE both checks in retail's own function (§4.2) and stop there — neither `IsMessageSafe` nor `IsMessageSpam` exists anywhere in acdream. `RouteLegacyChannel` is the unrelated legacy 0x0147 `ChatChannel` pipeline and has no equivalent of either check in retail OR acdream — it was never the site these two gates belonged to. | `src/AcDream.Runtime/Gameplay/TurbineChatMembershipGate.cs`; `src/AcDream.App/Net/LiveSessionCommandRouter.cs` (`RouteTurbineChat`) | The user's target server (local ACE) leaves `chat_requires_account_15days`/`chat_requires_player_level` etc. at their disabled defaults (research doc §3.6) and has no observed rate-limit or unsafe-content complaint; porting a client-side throttle/safety check with no server-side counterpart to validate against risks inventing a threshold retail didn't use. | A future connected gate against a server that DOES rate-limit chat, or a deliberately unsafe test string, would see every send attempted rather than refused after the first — cosmetic only, since ACE's own server-side handling (if any) still governs what actually reaches other players. | `ClientCommunicationSystem::SendTurbineChat @0x0057db10` (`IsMessageSafe`/`IsMessageSpam` branches); research doc `docs/research/2026-08-09-chat-side-channels-vs-ace.md` §4.2 | | AP-182 | **Filed 2026-08-09 (Campaign CH slice CH4); corrected 2026-08-09 at the CH4 REJECT-review (nit 11).** `@title ` is wired to a pure no-op — `LiveSessionRuntimeFactory`'s `SetChatTitle` binding is `_ => { }`; the requested title is neither stored nor consumed anywhere (the original filing's "stores the value locally" claim was false). This matches retail's own silent success (no confirmation text was recovered at the `DoTitle` success site, so a no-visible-effect accept is exactly as faithful as a stored-but-unread value would be). Also omitted: `DoTitle`'s three local failure messages — no title given, "You must provide a new title for the window."; length over 99 characters, "Window title length cannot exceed 100 characters."; and wrong source window (`m_idCurrentCommandSource` 1 or 8), "This command must be issued from a popup chat window." — acdream's catalog validator (`ClientCommandId.SetChatTitle`, `AnyArguments`) accepts any argument shape and never raises any of the three. `src/AcDream.App/Net/LiveSessionRuntimeFactory.cs` (`SetChatTitle`) | Retail's chat window presumably re-renders its title bar text; acdream's chat window has no title bar at all under the current retained-UI import, so there is nothing to visually diverge from yet | Once a titled chat-window chrome is built, `@title` needs to be re-wired to it — today it is a pure no-op, and the three failure messages above are silently absent | `ClientCommunicationSystem::DoTitle @ 0x0057A640` | | AP-185 | **Filed 2026-08-10 (Campaign CH slice CH6a — retail chat-window layout + 8-grip resize).** The main chat window's 8 cosmetic `_Locked` border-art twins (`0x10000693`-`0x1000069A`) are retail's `PlayerModule::LockUI`-driven alternate skin — `gmFloatyMainChatUI::UpdateLockedStatus @0x004D23D0` swaps them in for the 8 live Resizebar/Dragbar grips (`0x1000069B`-`0x100006A2`) when the UI is locked, and swaps them back out when unlocked. `ChatWindowController.Bind` always hides the twins and always shows the live set — i.e. it renders only retail's UNLOCKED skin, regardless of `UiRoot.UiLocked`. `src/AcDream.App/UI/Layout/ChatWindowController.cs` (`LockedTwinIds`) | `UiRoot.UiLocked` already gates the underlying move/resize INTERACTION generically and correctly in both states (locked ⇒ no move, no resize, regardless of which border art is drawn); the two art sets occupy identical rects, so always showing the interactive-grip skin is a cosmetic simplification, not a functional one, and the default matches `UiLocked`'s own `false` default | A user who locks the UI (`PlayerModule::LockUI`) sees the interactive-grip chat-window border art unchanged instead of retail's inert locked variant — cosmetic only; the window still correctly refuses to move or resize while locked | `gmFloatyMainChatUI::UpdateLockedStatus @0x004D23D0`; `PlayerModule::LockUI`; `docs/research/2026-08-09-chat-retail-window-shell.md` §1.6 | -| AP-187 | **Filed 2026-08-10 (Campaign CH slice CH6b — floating chat windows).** The four floating chat windows' text-type filters (`AcDream.Core.Chat.ChatWindowState`, retail's `0x1000007F` per-window option) persist only in local `settings.json` (`ChatSettings.ChatWindow1Filter`..`ChatWindow4Filter`, `SettingsStore.LoadChat`/`SaveChat`). Retail's authoritative store for this same data is the per-window option array (`0x1000008C`) packed inside the character-scoped `GameplayOptions` blob, which ACE stores and echoes as opaque bytes without parsing (window-shell research doc §4.1/§4.4); acdream has no reader or writer for that blob (CH3 already deleted one malformed attempt at the outbound `SetCharacterOptions 0x01A1` builder — `SocialActions.cs`). Geometry and open/visible state for these same four windows do NOT need a row of their own: they persist through the pre-existing generic `RetailWindowLayoutPersistence` path (X/Y/W/H/visible/collapsed/maximized per window name), which is retail's OWN local-file mechanism too (`gmGamePlayUI::SaveScreenLayout`/`LoadScreenLayout`, window-shell research doc §4.3) — only the filter mask lacks any such local-file precedent in retail and is acdream's own addition to make the feature usable before CH6f lands. `src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs`; `src/AcDream.UI.Abstractions/Panels/Settings/SettingsStore.cs` (`LoadChat`/`SaveChat`/`BuildChatObject`); `src/AcDream.App/UI/RetailUiRuntime.cs` (`MountFloatingChatWindows`, `SaveChatWindowFilters`) | CH6a/CH6b's own port-shape recommendation (window-shell research doc §6.1) explicitly chose local persistence first and deferred the `0x1000008B`/`0x1000008C` wire to a dedicated CH6f slice, citing CH3's deleted malformed builder as the reason not to rush it | A character's floating-window filter customization does not travel to a different acdream install, and would not round-trip through a retail client sharing the same character (retail would see acdream's local-only values as unset, falling back to its own `PostInit` defaults) — cosmetic/preference-only, no gameplay effect | `PlayerModule::GetChatOptionStructure @0x005D5300`; `PlayerModule::InqChatWindowOption/SetChatWindowOption @0x005D5540/:70`; `docs/research/2026-08-09-chat-retail-window-shell.md` §4.1/§4.4/§6.1; `docs/plans/2026-08-09-chat-parity-campaign.md` (CH6f row) | +| AP-187 | **Filed 2026-08-10 (Campaign CH slice CH6b — floating chat windows). BROADENED 2026-08-11 at Campaign OP slice OP5 (Chat tab): the divergence now covers the MAIN chat window's filter too (`ChatSettings.ChatWindowMainFilter`), and the write path is no longer mount-time-seed-only — the retail Options panel's Chat tab (`ChatOptionsPageController`, five `UiCheckboxBitfield64` blocks) is now a LIVE editing surface for all five windows' filters, writing `ChatWindowState.SetFilter` directly and persisting on every change via `RetailUiRuntime.SaveChatWindowFilters`, closing that method's own former "worth tightening to auto-save-on-change once a live settings surface exists" note.** The five chat windows' (main + four floating) text-type filters (`AcDream.Core.Chat.ChatWindowState`, retail's `0x1000007F` per-window option) persist only in local `settings.json` (`ChatSettings.ChatWindowMainFilter`/`ChatWindow1Filter`..`ChatWindow4Filter`, `SettingsStore.LoadChat`/`SaveChat`). Retail's authoritative store for this same data is the per-window option array (`0x1000008C`) packed inside the character-scoped `GameplayOptions` blob, which ACE stores and echoes as opaque bytes without parsing (window-shell research doc §4.1/§4.4); acdream has no reader or writer for that blob (CH3 already deleted one malformed attempt at the outbound `SetCharacterOptions 0x01A1` builder — `SocialActions.cs`). Geometry and open/visible state for these same windows do NOT need a row of their own: they persist through the pre-existing generic `RetailWindowLayoutPersistence` path (X/Y/W/H/visible/collapsed/maximized per window name), which is retail's OWN local-file mechanism too (`gmGamePlayUI::SaveScreenLayout`/`LoadScreenLayout`, window-shell research doc §4.3) — only the filter mask lacks any such local-file precedent in retail and is acdream's own addition to make the feature usable before a `0x1000008C` wire slice lands. `src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs`; `src/AcDream.UI.Abstractions/Panels/Settings/SettingsStore.cs` (`LoadChat`/`SaveChat`/`BuildChatObject`); `src/AcDream.App/UI/RetailUiRuntime.cs` (`MountChat`, `MountFloatingChatWindows`, `SaveChatWindowFilters`); `src/AcDream.App/UI/Layout/ChatOptionsPageController.cs` | CH6a/CH6b's own port-shape recommendation (window-shell research doc §6.1) explicitly chose local persistence first and deferred the `0x1000008B`/`0x1000008C` wire to a dedicated CH6f slice, citing CH3's deleted malformed builder as the reason not to rush it | A character's floating-window filter customization does not travel to a different acdream install, and would not round-trip through a retail client sharing the same character (retail would see acdream's local-only values as unset, falling back to its own `PostInit` defaults) — cosmetic/preference-only, no gameplay effect | `PlayerModule::GetChatOptionStructure @0x005D5300`; `PlayerModule::InqChatWindowOption/SetChatWindowOption @0x005D5540/:70`; `docs/research/2026-08-09-chat-retail-window-shell.md` §4.1/§4.4/§6.1; `docs/plans/2026-08-09-chat-parity-campaign.md` (CH6f row) | | AP-188 | **Filed 2026-08-10 (Campaign CH slice CH6b — floating chat windows).** A floating chat window's chat entry always sends on the `Say` channel (`FloatingChatWindowController.Bind`'s `OnSubmit` hardcodes `ChatChannelKind.Say`). The floaty LayoutDesc (`0x2100005B`) authors no talk-focus menu (window-shell research doc §2.2 — only the main window's layout has one, element `0x10000014`), so there is no visible channel picker on a floaty window either way, matching retail's authored UI exactly. What is UNVERIFIED is whether retail's actual SEND path for a floaty window's typed message reads a per-window channel or the single globally-current talk-focus channel/target the main window's menu (or the last-selected/last-speakable-target state `gmMainChatUI::UseTime @0x004CDB20` tracks) last set — if the latter, a real retail floaty window would send on whatever channel the player most recently picked from the MAIN window, not always `Say`. Confirming this requires tracing `gmCCommunicationSystem`'s send-command path from a floaty `ChatInterface` instance, not yet done. Filed as ISSUES.md #369. `src/AcDream.App/UI/Layout/FloatingChatWindowController.cs` (`Bind`, the `OnSubmit` wiring) | Building genuine cross-window shared-channel state (reading `ChatWindowController`'s private `_activeChannel` from four independent sibling controllers, or promoting it to a shared owner) is a real design decision outside this slice's explicit scope (task items 1-6 do not ask for cross-window channel sharing); `Say` is retail's own default channel and the safest fixed value absent confirmation | If retail's actual mechanism is "send on the currently-selected global channel," a user who selects e.g. Fellowship from the main window's talk-focus menu and then types into a floaty window would see it sent as Fellowship in retail but as Say in acdream — no data loss (the message still sends), only channel-selection mismatch | `gmMainChatUI::InitTalkFocusMenu @0x004CDC50`; `gmMainChatUI::UseTime @0x004CDB20`; `docs/research/2026-08-09-chat-retail-window-shell.md` §2.2 | | AP-189 | **Filed 2026-08-10 at the CH6a/b REJECT-review rework (SHOULD-FIX 5, `docs/research/2026-08-10-ch6ab-review-findings.md`).** Retail keeps a PER-`ChatInterface` `m_chatLog`, truncated at 10,000 lines (`ChatInterface::RecvNotice_DisplayFinalStringInfo @0x004F4711` → `TruncateChatLog`) — each of the five windows (main + 4 floaty) owns its OWN 10,000-line backlog, and a closed window keeps accumulating into its own log because `gmFloatyMainChatUI::SetVisible @0x004CE9B0` never unregisters the handler. acdream instead shares ONE canonical `ChatLog` capped at 500 entries (`RuntimeCommunicationState`'s ctor, `maximumChatEntries: 500`) with a 200-line display tail every window filters from (`InteractionRetainedUiComposition.cs:564`'s `displayLimit: 200` feeding `ChatVM.RecentLinesDetailed`; `ChatWindowState.ShouldDisplay` does the per-window filtering). The accumulate-while-closed and independent-per-window-scroll BEHAVIORS both fall out correctly from this shared-log shape, but the EFFECTIVE per-window scrollback DEPTH differs from retail's: a window whose filter accepts only a rare message type (e.g. a Fellowship-only floaty) sees only the fellowship lines that happen to still be inside the shared log's last 200-of-500 lines, not up to 10,000 like retail's own per-window log. `src/AcDream.Core/Chat/ChatLog.cs` (`_maxEntries`); `src/AcDream.App/Composition/InteractionRetainedUiComposition.cs:564` (`displayLimit: 200`); `src/AcDream.App/UI/Layout/ChatWindowController.cs`/`FloatingChatWindowController.cs` (`GetTranscriptLines`) | A single shared canonical log matches acdream's Slice-J "one canonical transcript, many filtered presentations" pattern and keeps memory bounded regardless of how many windows are open; 500 shared entries covers many minutes of typical mixed-channel play, and both retail-observable BEHAVIORS this row could have broken (closed-window accumulation, independent per-window scroll position) are reproduced correctly — only the numeric DEPTH ceiling differs | In a busy mixed-channel session (heavy General/Trade traffic), a rarely-used channel (Fellowship, a Turbine room) can scroll out of the shared 500-entry window long before a floaty window filtered to just that channel would have neared retail's 10,000-line depth — a user who opens that floaty window after a long session sees a much shorter backlog than retail would show for the same play session | `ChatInterface::RecvNotice_DisplayFinalStringInfo @0x004F4640`/`TruncateChatLog @0x004F4711`; `gmFloatyMainChatUI::SetVisible @0x004CE9B0`; `docs/research/2026-08-09-chat-retail-window-shell.md` §1.2 | | AP-190 | **Filed 2026-08-10 (Campaign CH slice CH6c — window opacity + transparency setting; retires AP-40). AMENDED 2026-08-10 at the CH6c review-fix round: reworded (2), added (3)/(4).** Four divergences from retail's focus-driven window opacity, all decomp-verified (`docs/research/2026-08-09-chat-retail-window-shell.md` §3). (1) SCOPE: retail's `ChatInterface::SetOpacity`/`SetDefaultOpacity`/`SetActiveOpacity` only ever run on `ChatInterface`-derived windows (the main chat window + the four floaties) — every other retail window (vitals, toolbar, inventory, ...) has no opacity fade at all. acdream's `RetailWindowOpacityController` subscribes to `RetailWindowManager.WindowRegistered` and applies the SAME focus-driven fade to every window the manager ever registers, so the one Settings → Chat tab transparency slider pair affects the whole retained UI. (2) DEFAULT VALUE — REWORDED at the review-fix round: retail's shipped defaults are PER WINDOW CLASS — the base `ChatInterface` ctor (`0x004F4550`) sets DefaultOpacity=0.5/ActiveOpacity=1.0, but `gmMainChatUI`'s own ctor (`0x004CD0F0`, called after the base ctor) overrides DefaultOpacity to 1.0 (the main window is ALWAYS fully opaque in both states); `gmFloatyChatUI::Create` (`0x004CE2C0`) calls the base ctor directly with no override, so only the four floating windows keep 0.5/1.0. acdream originally shipped the base ChatInterface value (0.5/1.0) as ONE shared global default applied to EVERY registered window — combined with (1)'s scope extension this faded the WHOLE registered UI (radar, vitals, toolbar, main chat, ...) to 50% opacity out of the box, including several windows that can never take keyboard focus at all and so were PERMANENTLY stuck at 0.5. Fixed at the review round to `gmMainChatUI`'s 1.0/1.0 override as the shared default instead: this reduces the remaining divergence to acdream's four floating chat windows shipping OPAQUE where retail's floaties ship 0.5-while-idle — user-settable via the same Settings → Chat opacity slider pair, so it is now a default-VALUE divergence only, not a missing mechanism. (3) EASING (new, filed at the review-fix round): retail's `ChatInterface::ListenToGlobalMessage @0x004F3840` — armed on the focus element-messages `0x1A`/`0x1E`/`0x28`/`0x29`/`0x2E` at `0x004F5275` via `UIListener::RegisterForGlobalMessage(this, 3)` — eases the live opacity toward its target by 5% of the target-delta per tick, unregistering from the global tick once within FP-epsilon of the target. acdream's `RetailWindowOpacityController.Apply` snaps to the target opacity immediately on every focus-change event; porting the per-tick lerp needs a UI frame-tick hook the controller does not have today, so it is deferred rather than implemented this round. (4) FOCUS PREDICATE (new, filed at the review-fix round): retail's `ChatInterface::IsTextEntryFocused @0x004F30A0` tests specifically whether `GetFocusDescendant(rootElement) == this->m_chatEntry` — the chat ENTRY FIELD, not the window generally. acdream's `RetailWindowHandle.DescendantFocusChanged` fires whenever ANY focusable descendant of the window gains focus, a strictly broader predicate for any window with more than one focusable child. The linked active>=default invariant itself (`SetDefaultOpacity`/`SetActiveOpacity`'s mutual-correction bodies) IS ported exactly — `ChatOpacityLink` in `AcDream.UI.Abstractions`. | `src/AcDream.App/UI/RetailWindowOpacityController.cs`; `src/AcDream.App/UI/RetailWindowManager.cs` (`WindowRegistered`); `src/AcDream.UI.Abstractions/Panels/Settings/ChatOpacityLink.cs`; `src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs` (`DefaultOpacity`/`ActiveOpacity`) | Extending the fade to every window is the shape the user's requested "transparency setting" actually wants (a general UI preference, not a chat-only one); shipping the shared default at 1.0 keeps the out-of-box render retail-identical for the 11 non-chat windows AND the main chat window (the windows retail keeps opaque, several of which can never take focus at all), while the Settings → Chat transparency slider remains fully user-settable for anyone who wants the four floaties' retail translucence back. (3) and (4) are both presentation-only refinements — the fade direction and the linked-invariant math stay retail-exact, only the transition curve (snap vs. 5%-per-tick ease) and the focus predicate's granularity (any descendant vs. the text-entry specifically) diverge — so recording them without implementing the frame-tick hook (3) or narrowing the focus event (4) is the correct scope for a review-fix round rather than opening new implementation work | A user who compares acdream's default install against retail side-by-side now sees the 11 non-chat windows AND the main chat window matching (opaque); only the four floating chat windows still diverge (opaque vs. retail's 50%-while-idle) until the slider is dragged. (3) is visible as the opacity change happening in a single frame instead of retail's ~20-tick fade — low severity, since the START and END states are both retail-exact, only the transition is instant instead of eased. (4) is visible on any window with more than one distinct focusable descendant (e.g. a settings panel with several controls): acdream stays at ActiveOpacity while ANY of them holds focus, where retail would already have faded back to DefaultOpacity once focus left the specific text-entry element — for single-focusable-child windows (most of the retained UI today) the two predicates coincide and there is no observable difference | `ChatInterface::ChatInterface @0x004F4550`; `gmMainChatUI::gmMainChatUI @0x004CD0F0`; `gmFloatyChatUI::Create @0x004CE2C0`; `ChatInterface::SetDefaultOpacity @0x004F3BC0`/`SetActiveOpacity @0x004F3C40`; `ChatInterface::ListenToGlobalMessage @0x004F3840`; `ChatInterface::IsTextEntryFocused @0x004F30A0`; global-message arming switch @0x004F5275 (`UIListener::RegisterForGlobalMessage(this, 3)` on element messages `0x1A`/`0x1E`/`0x28`/`0x29`/`0x2E`) | diff --git a/docs/research/2026-08-11-campaign-op-test-script.md b/docs/research/2026-08-11-campaign-op-test-script.md index 342b517d..0ed0ff4b 100644 --- a/docs/research/2026-08-11-campaign-op-test-script.md +++ b/docs/research/2026-08-11-campaign-op-test-script.md @@ -1,6 +1,6 @@ # Campaign OP connected-gate test script -**Status:** OP3, OP4, and OP7 sections. Later slices (OP5-6, OP8) append +**Status:** OP3, OP4, OP5, and OP7 sections. Later slices (OP6, OP8) append their own sections here as they land; the campaign's OP9 closeout gate is this document complete plus every slice code-complete. @@ -363,6 +363,152 @@ TrySetOption` seam every other Options-panel consumer already uses. --- +## OP5 — the Chat tab + +The Chat tab (`0x2100005C`) has six sections: **General Options** (the two +linked window-opacity sliders) and five per-window text-filter blocks — the +**Main Chat Window** (12 checkboxes, no "Gameplay" row) and four **Floaty +Chat Window 1-4** blocks (13 checkboxes each). All five blocks write +`AcDream.Core.Chat.ChatWindowState` directly — the SAME state CH6's floating +chat windows already read when deciding which lines to show. + +### Opening the tab + +1. **Open the Options panel (F11) and click the Chat tab.** Six group + headers appear top-to-bottom: **General Options**, **Main Chat Window**, + **Floaty Chat Window 1**, **Floaty Chat Window 2**, **Floaty Chat Window + 3**, **Floaty Chat Window 4**. Under General Options, two horizontal + sliders; the second one has "Transparent"/"Opaque" range captions at its + ends. Under each window section, a block of checkboxes with labels + (Gameplay, Combat, Magic, Area Speech, Tells, Allegiance, Fellowship, + General, Trade, LFG, Roleplay, Society, Error) — the Main Chat Window + block is missing the **Gameplay** row (12 rows, not 13); every floaty + block has all 13. +2. **Scroll the list.** All six sections are reachable; the block heights + visibly differ from a flat 100px box — each block is exactly tall enough + to show all of its own rows with no clipping and no dead space below the + last row (AP-195's self-sizing). +3. **Look closely at a row whose mask covers MULTIPLE underlying message + types** (Gameplay, Combat, Allegiance, or Fellowship — the composite-mask + rows per the research doc) versus a single-bit row (e.g. "Error" or + "General"). If your character's chat history has caused a PARTIAL match + on one of those composite rows (some but not all of its underlying types + currently enabled), the checkbox's own LED art should look visibly + different (dimmer/"ghosted") from a row that is FULLY on — this is + AP-195's LED media swap. On a fresh character every row is likely either + fully on or fully off, so this may not be observable without deliberately + creating a partial-match state via repeated single clicks inside a + composite row's underlying bits (not directly clickable per-bit through + this UI — a visual nice-to-have to note, not a blocking check). + +### Opacity sliders — live drag, linked, never clamping + +4. **Drag the FIRST slider (Default Opacity) most of the way to the right** + while the Options panel itself is NOT focused/hovered by your mouse (so + you can see another registered window, e.g. the toolbar or a floating + chat window, at its UNFOCUSED opacity). Watch that OTHER window's + transparency change LIVE, continuously, as you drag — not just on + release. This is retail's `SetCurrentValue -> Apply(1)` immediate-apply + semantic. +5. **Drag the first slider (Default) ABOVE the second slider's (Active) + current position.** Retail's link drags the SECOND slider's thumb UP to + match — watch it move on its own as you drag the first. Neither slider + should ever let Default end up above Active. +6. **Drag the second slider (Active) BELOW the first slider's (Default) + current position.** The first slider's thumb should get dragged DOWN to + match, symmetric to step 5. +7. **Focus a chat window (click into its text entry) and watch its opacity + change to the Active value; click away and watch it ease back toward + Default.** (The easing itself is AP-190's existing behavior, not new to + this slice — this step just confirms the sliders are actually driving + the SAME live opacity controller every other window already uses.) + +### Filter checkboxes — live routing, per-window independence + +8. **Open a floating chat window (Alt+1 through Alt+4, or the toolbar + indicator buttons) and uncheck "Combat" in that SAME window's filter + block on the Chat tab** (e.g. Floaty Chat Window 1's block if you opened + window 1). Trigger a combat message (attack a monster, or have someone + attack you) — it should STOP appearing in that floating window while + still appearing in the MAIN chat window (whose own filter is untouched). + Re-check the box — combat messages resume in that window. +9. **Uncheck a filter row in the MAIN CHAT WINDOW's own block** (e.g. + "Trade") and generate a matching message (a `/trade` broadcast, or + whatever is easiest to trigger). It should stop appearing in the main + window specifically, while a floating window with Trade still checked + keeps showing it — proving the main window's filter is genuinely + consulted now (CH6a/b's own fixed bug: broadcast lines used to + short-circuit true for the main window regardless of its filter). +10. **Verify the five blocks are independent**: toggling a row in Floaty + Chat Window 2's block must not affect Floaty Chat Window 1, 3, 4, or the + Main window's own same-named row. + +### Apply/Reset/Defaults + +11. **Toggle a couple of filter checkboxes AND drag both opacity sliders, + then click Reset.** Every row you touched (checkboxes, both sliders) + reverts to its value from the last Apply/OnShown — filter writes revert + live (you should see the reverted checkbox state immediately, and the + corresponding message routing revert too). +12. **Click Defaults.** The two opacity sliders jump to their DAT-extracted + values (0.5 default / 1.0 active — the same values you'd see on a + NEVER-touched install) LIVE; every filter block reverts to its OWN + window's retail default (Main: 0xFBFFFFFF's rows — everything checked + except Error; each floaty: its own narrower default set, per the + research doc's table) — Apply/Reset light up afterward if anything + actually changed (Defaults never commits by itself). +13. **Switch to another tab (e.g. Gameplay) without clicking Apply after an + edit.** Uncommitted slider/checkbox edits silently revert — switching + tabs is a Reset, not a save. Switch back to Chat and confirm the + reverted state (both the slider positions AND the filter checkboxes). + +### Persistence — local-only, survives relog AND relaunch + +14. **Set a distinctive combination** (e.g. Default Opacity ~30%, Active + Opacity ~90%, Main window's "LFG" row OFF, Floaty 3's "Fellowship" row + OFF) and click Apply (or just leave the panel open — filter writes and + slider drags apply live and persist to `settings.json` on every change, + not just on Apply). +15. **Fully relaunch the client** (close the window, start a new process; + a simple relog is not required since this is LOCAL-only state, unlike + the Character tab's server-authoritative rows). Reopen the Options + panel's Chat tab — the two sliders should read back at your set values, + and the Main/Floaty-3 checkboxes should read back unchecked for the + rows you turned off. **This is expected to be LOCAL-only persistence**: + retail's own wire mechanism for this data (the `0x1000008C` + GameplayOptions blob) is explicitly out of scope for this campaign + (register row AP-187, broadened at this slice — the blob stays + unsent); acdream persists through `settings.json` instead, which is + why a relaunch (not a server round trip) is the correct way to prove + it survived. + +### What to report + +- Any section/row/label missing, or the Main window showing a Gameplay row + (or a floaty missing one). +- A slider that does not apply live while dragging, or that lets Default + end up above Active (or vice versa) after a drag. +- A filter checkbox whose toggle does not change live message routing in + the window it belongs to, or that leaks into a DIFFERENT window. +- Reset/Defaults not behaving as described in 11-12. +- Any block whose height looks clipped or has a large empty gap below its + last row (a self-sizing regression). +- Any setting that reverts to default after a full relaunch (a persistence + regression) — remember this is local-only, so a SERVER-side relog is not + the right test here (see item 15's note). + +### Explicitly NOT in scope for this gate + +- Config tab content — OP6. +- The retail `0x1000008C` wire format for this same data — explicitly + deferred (see the plan's "What is explicitly OUT of scope" section and + this slice's own register citation). +- The composite-mask LED "partial vs full" visual distinction (step 3) is a + nice-to-have confirmation, not a blocking pass/fail item — it requires a + specific partial-match state that a fresh character is unlikely to be in. + +--- + ## OP7 — headless `characterOptions` Unlike OP3-OP6, this is not a graphical-client gate: no window is launched. diff --git a/src/AcDream.App/UI/Layout/ChatOptionsDatDefaults.cs b/src/AcDream.App/UI/Layout/ChatOptionsDatDefaults.cs new file mode 100644 index 00000000..3fd097a9 --- /dev/null +++ b/src/AcDream.App/UI/Layout/ChatOptionsDatDefaults.cs @@ -0,0 +1,92 @@ +using AcDream.Content; +using DatReaderWriter.DBObjs; +using DatReaderWriter.Types; + +namespace AcDream.App.UI.Layout; + +/// +/// Campaign OP slice OP5: reads the Chat tab's two opacity-slider defaults from the +/// installed DAT's DBProperties collection — retail +/// UIOption::InqDefaultGameplayOptionProperty @0x004ef8d0's +/// m_propName-bound path, the ONE genuine consumer of that mechanism (the +/// Character tab's 49/50 checkboxes are PlayerOption-bound, not +/// m_propName-bound — see 's own +/// U1 trace for why THAT tab does not use this seam). +/// +/// +/// Byte-verified recipe (DBCache::GetDIDFromEnum @0x00413940): the outer call +/// site is GetDIDFromEnumStatic(&ret, 0x16, 2) — outer arg2 (0x16) +/// feeds the inner function's arg3 (the SUB-MAP lookup key), outer arg3 (2) +/// feeds arg4 (the MASTER-MAP lookup key FIRST). So: +/// master.ClientEnumToID[2] → subMapDid; subMap.ClientEnumToID[0x16] → DID — +/// i.e. 's own (enumValue, enumCategory) +/// parameter order is (0x16, 2), matching +/// RetailUiRuntime.cs's existing Resolve(dats, 2u, 5u) convention +/// (enumCategory selects the master-map submap FIRST, enumValue indexes it SECOND). +/// Empirically confirmed against the installed DATs: resolves to DID +/// 0x78000001, a 3-entry DBProperties — +/// 0x1000007F (Bitfield64, the Chat filter blocks' shared +/// m_propName — NOT a per-window default; the five blocks' actual defaults are +/// the literal constants gmChatOptionsUI::InitOptions authors per window, +/// ported verbatim in ChatWindowState/), +/// 0x10000080 (Float, 0.5Option_DefaultOpacity_Property), +/// 0x10000081 (Float, 1.0Option_ActiveOpacity_Property). +/// +/// +public static class ChatOptionsDatDefaults +{ + /// DAT enum-category key for the FIRST (master-map) lookup — see the + /// class doc's byte-verified recipe. + private const uint EnumCategory = 2u; + + /// DAT enum-value key for the SECOND (sub-map) lookup. + private const uint EnumValue = 0x16u; + + /// Option_DefaultOpacity_Property — the unfocused-window opacity + /// slider's m_propName. + public const uint DefaultOpacityPropertyId = 0x10000080u; + + /// Option_ActiveOpacity_Property — the focused-window opacity + /// slider's m_propName. + public const uint ActiveOpacityPropertyId = 0x10000081u; + + /// Retail base-constructor fallback (ChatInterface::ChatInterface + /// @0x004F4550) — used only when the DAT read fails (missing DAT entry, + /// corrupt install); the live installed DAT resolves to these exact values today. + public const float FallbackDefaultOpacity = 0.5f; + + public const float FallbackActiveOpacity = 1.0f; + + /// + /// Resolves the two slider defaults from the live DAT. Returns + /// (with both out params set to the retail-constructor + /// fallback) if the DID does not resolve, the DBProperties object is + /// missing, or either property is absent/not a Float — matching this + /// codebase's "degrade to a documented fallback, never invent a value" discipline. + /// + public static bool TryRead( + IDatReaderWriter dats, out float defaultOpacity, out float activeOpacity) + { + defaultOpacity = FallbackDefaultOpacity; + activeOpacity = FallbackActiveOpacity; + + uint did = RetailDataIdResolver.Resolve(dats, EnumValue, EnumCategory); + if (did == 0u || !dats.Portal.TryGet(did, out DBProperties? props) || props is null) + return false; + + bool ok = true; + if (props.Properties.TryGetValue(DefaultOpacityPropertyId, out BaseProperty? d) + && d is FloatBaseProperty defaultProp) + defaultOpacity = defaultProp.Value; + else + ok = false; + + if (props.Properties.TryGetValue(ActiveOpacityPropertyId, out BaseProperty? a) + && a is FloatBaseProperty activeProp) + activeOpacity = activeProp.Value; + else + ok = false; + + return ok; + } +} diff --git a/src/AcDream.App/UI/Layout/ChatOptionsPageController.cs b/src/AcDream.App/UI/Layout/ChatOptionsPageController.cs new file mode 100644 index 00000000..27731ec4 --- /dev/null +++ b/src/AcDream.App/UI/Layout/ChatOptionsPageController.cs @@ -0,0 +1,478 @@ +using System; +using AcDream.App.UI; +using AcDream.Core.Chat; + +namespace AcDream.App.UI.Layout; + +/// +/// Campaign OP slice OP5 (2026-08-11): binds the Chat tab (LayoutDesc +/// 0x2100005C, root 0x1000050A, ListBox 0x1000050D) — retail's +/// gmChatOptionsUI::InitOptions @0x0049FC60 — through OP2's template-list +/// mechanism and OP3's per-page model, exactly like +/// . Six authored sections, byte-verified +/// end-to-end against docs/research/2026-08-10-options-panel-structure.md §5 +/// AND the raw gmChatOptionsUI::InitOptions/AddCheckboxBitfield64Option +/// pseudo-C (research doc's own row table was cross-checked against BOTH the inline +/// main/floaty4 call sites and the shared helper's switch — every mask, header +/// key, separator, and the 12-vs-13-row Gameplay-row gap matched exactly): +/// +/// +/// General Options — header, then TWO LINKED opacity sliders +/// (Option_DefaultOpacity_Property 0x10000080 / Option_ActiveOpacity_Property +/// 0x10000081), then a separator. +/// Five per-window filter sections (main window id 8, floaties +/// 2-5) — header, one block, separator. Main gets +/// 12 rows (no "Gameplay" row); every floaty gets 13. +/// +/// +/// +/// The one 64-bit register, not 128 bits. Retail's own +/// UIOption_CheckboxBitfield64::AddChild(this, uint64_t mask, uint32_t labelId, +/// uint32_t tooltipId) signature (byte-verified from the pseudo-C function +/// declaration, not just call-site operands) takes exactly ONE 64-bit mask — matching +/// ChatWindowState's own single-ulong-per-window model (its committed +/// defaults, e.g. main 0xFBFFFFFFu, are byte-identical to the research doc's +/// "(high:low)" row/window tables once "high:low" is read as bits[63:32]:bits[31:0] of +/// THAT one register — a BN push-immediate artifact for the 64-bit argument, not a +/// genuine 128-bit two-register value). 's own +/// CurrentLow/CurrentHigh pair is exactly that split, so every mask this +/// controller feeds it is produced by from ONE canonical +/// — never independently derived low/high literals. +/// +/// +/// +/// Retail window id vs acdream compact id. Retail's SetUserData argument +/// (8 main, 2-5 floaties) is the id PlayerModule::SetChatWindowOption dispatches +/// on; uses its OWN compact 0-4 range +/// (Campaign CH slice CH6b). carries the +/// mapping so nothing downstream needs to know retail's raw numbering. +/// +/// +public static class ChatOptionsPageController +{ + /// Chat page root — gmChatOptionsUI — the STANDALONE + /// 0x2100005C layout's own root id. Only present as its own distinct node + /// when 0x2100005C is imported directly (e.g. + /// FixtureLoader.LoadOptionsChat()); when mounted through the tab host + /// (0x2100006E/0x2100002B, what actually + /// operates on), ElementReader.Merge's "derived id wins" rule means the + /// PAGE SLOT keeps its own id () instead — use + /// that one for any host-tree lookup. + public const uint RootElementId = 0x1000050Au; + + /// The Chat page's SLOT element within the tab host + /// (OptionsPanelController's own private ChatPageId) — the id that + /// actually survives base-merge in the host-mounted tree + /// operates on. Used to SCOPE the scrollbar lookup below. + private const uint PageSlotElementId = 0x1000050Cu; + + /// The row ListBox (dat Type 5) — m_pOptionBox. + public const uint ListBoxElementId = 0x1000050Du; + + /// The ListBox's linked scrollbar — shared with the Config tab (research + /// doc §10.1: both tabs author the SAME scrollbar element id). + public const uint ScrollbarElementId = 0x10000201u; + + private const int HeaderTemplateIndex = 0; + private const int SeparatorTemplateIndex = 1; + private const int UnlabelledSliderTemplateIndex = 3; + private const int LabelledSliderTemplateIndex = 6; + private const int BitfieldTemplateIndex = 8; + + private const uint StringTableId = 0x23000003u; + + /// The slider leaf inside either slider row template's subtree. + private const uint SliderElementId = 0x1000021Cu; + + /// Labelled slider template's low/high range-caption children + /// (research doc §1.5's template array; present only on template idx 6). + private const uint SliderRangeMinElementId = 0x1000021Eu; + private const uint SliderRangeMaxElementId = 0x1000021Fu; + + /// One authored filter row: its combined 64-bit mask (bits[63:32] = + /// research doc's "high", bits[31:0] = "low") and retail's own + /// ID_ChatOption_TextFilter_* symbol name (used verbatim as the hash key — + /// "Allegience" is retail's OWN misspelling in the symbol; the STRING TABLE value + /// it resolves to is properly spelled "Allegiance", per structure doc §10.4 item 8 + /// and §5.2's own footnote). + public readonly record struct FilterRowSpec(ulong Mask, string RetailLabelKey); + + /// + /// The 13-row authored order, byte-verified against BOTH + /// AddCheckboxBitfield64Option's shared tail (Combat..Error, always present) + /// and the two inline blocks' own Gameplay-row prefix (main SKIPS it; window 5 + /// INCLUDES it) — research doc §5.2. Row 0 (Gameplay) is the one + /// gates per block. + /// + public static readonly FilterRowSpec[] FilterRows = + { + new(0x0000000083912021ul, "ID_ChatOption_TextFilter_Gameplay"), + new(0x0000000000600040ul, "ID_ChatOption_TextFilter_Combat"), + new(0x0000000000020080ul, "ID_ChatOption_TextFilter_Magic"), + new(0x0000000000001004ul, "ID_ChatOption_TextFilter_AreaSpeech"), + new(0x0000000000000018ul, "ID_ChatOption_TextFilter_Tells"), + new(0x0000000000040C00ul, "ID_ChatOption_TextFilter_Allegience"), // retail's own misspelling + new(0x0000000000080000ul, "ID_ChatOption_TextFilter_Fellowship"), + new(0x0000000008000000ul, "ID_ChatOption_TextFilter_General"), + new(0x0000000010000000ul, "ID_ChatOption_TextFilter_Trade"), + new(0x0000000020000000ul, "ID_ChatOption_TextFilter_LFG"), + new(0x0000000040000000ul, "ID_ChatOption_TextFilter_Roleplay"), + new(0x0000000100000000ul, "ID_ChatOption_TextFilter_Society"), + new(0x0000000004000000ul, "ID_ChatOption_TextFilter_Error"), + }; + + /// One authored per-window filter block/section. + public readonly record struct FilterBlockSpec( + int RetailWindowId, + int CompactWindowId, + string HeaderKey, + ulong DefaultFilter, + bool IncludesGameplayRow); + + /// + /// The five window sections in authored order — research doc §5/§5.2, byte- + /// verified defaults matching 's own committed + /// constants exactly (both sides derive from the SAME retail literals; this table + /// reuses 's named constants rather than re-declaring + /// them, so the two can never drift). + /// + public static readonly FilterBlockSpec[] FilterBlocks = + { + new(8, ChatWindowState.MainWindowId, "ID_ChatOption_MainChatWindow_Section", + ChatWindowState.MainWindowDefaultFilter, IncludesGameplayRow: false), + new(2, 1, "ID_ChatOption_FloatyChatWindow1_Section", + ChatWindowState.Floaty1DefaultFilter, IncludesGameplayRow: true), + new(3, 2, "ID_ChatOption_FloatyChatWindow2_Section", + ChatWindowState.Floaty2DefaultFilter, IncludesGameplayRow: true), + new(4, 3, "ID_ChatOption_FloatyChatWindow3_Section", + ChatWindowState.Floaty3DefaultFilter, IncludesGameplayRow: true), + new(5, 4, "ID_ChatOption_FloatyChatWindow4_Section", + ChatWindowState.Floaty4DefaultFilter, IncludesGameplayRow: true), + }; + + /// The live read/write seam every row on this page writes/reads + /// through. Opacity routes to RetailWindowOpacityController (AP-190's + /// live per-window fade); filters route to the shared + /// CH6 already consumes. The two DAT-extracted defaults are resolved ONCE by the + /// caller (RetailUiRuntime) — — since + /// they never change within a session. + public sealed record Bindings( + Func CurrentDefaultOpacity, + Func CurrentActiveOpacity, + Action SetDefaultOpacity, + Action SetActiveOpacity, + float DefaultOpacityDatDefault, + float ActiveOpacityDatDefault, + Func CurrentFilter, + Action SetFilter); + + /// + /// Builds the General Options header + 2 sliders + 5 filter sections (header + + /// block each) into 's Chat ListBox, links its + /// scrollbar, seeds every row's current/default state, and registers each row + /// into . + /// + /// The already-built Options panel tree — the Chat page's + /// ListBox is a descendant of it via the tab host's page-slot base-merge (same + /// import used). + /// The Chat tab's model + /// (). + /// Builds one row template's subtree — wired onto + /// the ListBox's AND called + /// directly for the five bitfield blocks (bypassing + /// 's own resolve step — + /// see for why). + /// Resolves ONE ID_ChatOption_* string by + /// (table id, hash) — never hard-coded English. + /// The live read/write seam for every row. + /// if the Chat page's ListBox did not resolve as + /// a . + public static bool Bind( + ImportedLayout layout, + OptionPage page, + Func templateResolver, + Func resolveString, + Bindings bindings) + { + ArgumentNullException.ThrowIfNull(layout); + ArgumentNullException.ThrowIfNull(page); + ArgumentNullException.ThrowIfNull(templateResolver); + ArgumentNullException.ThrowIfNull(resolveString); + ArgumentNullException.ThrowIfNull(bindings); + + if (layout.FindElement(ListBoxElementId) is not UiTemplateListBox listBox) + { + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: ListBox 0x{ListBoxElementId:X8} " + + "not found (or not a UiTemplateListBox) in the built Options panel tree — " + + "the Chat tab will have no rows."); + return false; + } + + listBox.TemplateResolver = templateResolver; + + // ScrollbarElementId (0x10000201) is SHARED with the Config tab (research + // doc §10.1 — both tabs author the same scrollbar element id). ImportedLayout's + // FindElement is a flat id->widget dictionary (last-build-wins on a + // collision), so a plain layout.FindElement(ScrollbarElementId) here can + // silently wire THIS scroll model onto Config's own scrollbar instance + // instead of Chat's — scope the search to the Chat page's own subtree + // instead (same "scoped search for a shared id" shape + // OptionsPanelController uses for the Apply/Reset/Defaults buttons). Scoped + // from PageSlotElementId, NOT RootElementId — see that field's own doc for + // why the standalone layout's root id does not survive base-merge here. + UiElement? chatPageSlot = layout.FindElement(PageSlotElementId); + UiElement? scrollbarElement = chatPageSlot is null + ? null + : UiElement.FindDescendant(chatPageSlot, ScrollbarElementId); + if (scrollbarElement is UiScrollbar scrollbar) + scrollbar.Model = listBox.Scroll; + else + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: scrollbar 0x{ScrollbarElementId:X8} " + + $"not found under Chat page slot 0x{PageSlotElementId:X8} — the Chat " + + "tab's row list will not scroll."); + + BuildHeaderRow(listBox, "ID_ChatOption_GeneralOptions_Section", resolveString); + BuildOpacitySliders(listBox, page, resolveString, bindings); + BuildSeparatorRow(listBox); + + foreach (FilterBlockSpec spec in FilterBlocks) + { + BuildHeaderRow(listBox, spec.HeaderKey, resolveString); + BuildFilterBlock(listBox, spec, page, templateResolver, resolveString, bindings); + BuildSeparatorRow(listBox); + } + + return true; + } + + private static void BuildHeaderRow( + UiTemplateListBox listBox, string headerKey, Func resolveString) + { + if (listBox.AddItemFromTemplateList(HeaderTemplateIndex) is not UiText header) + { + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: header template did not build as " + + $"UiText for '{headerKey}'."); + return; + } + + string? label = resolveString(StringTableId, DatStringResolver.ComputeHash(headerKey)); + if (label is null) + { + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: header string '{headerKey}' did not " + + "resolve from the DAT string table — the row renders with no text rather " + + "than an invented label."); + return; + } + + header.LinesProvider = () => new[] { new UiText.Line(label, header.DefaultColor) }; + } + + private static void BuildSeparatorRow(UiTemplateListBox listBox) + { + if (listBox.AddItemFromTemplateList(SeparatorTemplateIndex) is null) + Console.WriteLine("[D.2b] ChatOptionsPageController: separator template did not build."); + } + + /// + /// Slider 1 (template idx 3, unlabelled — Option_DefaultOpacity_Property) + /// and slider 2 (template idx 6, labelled with the + /// ID_UI_Value_Transparent/ID_UI_Value_Opaque range captions — + /// Option_ActiveOpacity_Property). Retail's DualHash slider link + /// (research doc §5.1) is ported through : + /// dragging one applies through (which itself runs + /// ChatOpacityLink and re-applies live), then pushes whatever the OTHER + /// value became onto its own row/slider — raising one drags the other, never + /// clamps. + /// + private static void BuildOpacitySliders( + UiTemplateListBox listBox, + OptionPage page, + Func resolveString, + Bindings bindings) + { + UiElement? row1 = listBox.AddItemFromTemplateList(UnlabelledSliderTemplateIndex); + UiScrollbar? slider1 = row1 is null ? null : UiElement.FindDescendant(row1, SliderElementId) as UiScrollbar; + if (slider1 is null) + Console.WriteLine( + "[D.2b] ChatOptionsPageController: default-opacity slider template did not " + + "build a UiScrollbar leaf."); + + UiElement? row2 = listBox.AddItemFromTemplateList(LabelledSliderTemplateIndex); + UiScrollbar? slider2 = row2 is null ? null : UiElement.FindDescendant(row2, SliderElementId) as UiScrollbar; + if (slider2 is null) + Console.WriteLine( + "[D.2b] ChatOptionsPageController: active-opacity slider template did not " + + "build a UiScrollbar leaf."); + + if (row2 is not null) + { + SetRangeLabel(row2, SliderRangeMinElementId, "ID_UI_Value_Transparent", resolveString); + SetRangeLabel(row2, SliderRangeMaxElementId, "ID_UI_Value_Opaque", resolveString); + } + + if (slider1 is null || slider2 is null) + return; + + float initialDefault = bindings.CurrentDefaultOpacity(); + float initialActive = bindings.CurrentActiveOpacity(); + slider1.SetScalarPosition(initialDefault); + slider2.SetScalarPosition(initialActive); + + FloatOptionRow? defaultRow = null; + FloatOptionRow? activeRow = null; + + defaultRow = new FloatOptionRow( + initialDefault, + bindings.DefaultOpacityDatDefault, + apply: value => + { + bindings.SetDefaultOpacity(value); + activeRow!.RefreshFromLink(bindings.CurrentActiveOpacity()); + }, + read: bindings.CurrentDefaultOpacity, + refresh: value => slider1.SetScalarPosition(value)); + + activeRow = new FloatOptionRow( + initialActive, + bindings.ActiveOpacityDatDefault, + apply: value => + { + bindings.SetActiveOpacity(value); + defaultRow!.RefreshFromLink(bindings.CurrentDefaultOpacity()); + }, + read: bindings.CurrentActiveOpacity, + refresh: value => slider2.SetScalarPosition(value)); + + slider1.ScalarChanged = value => defaultRow.SetCurrentValue(value); + slider2.ScalarChanged = value => activeRow.SetCurrentValue(value); + + page.Register(defaultRow); + page.Register(activeRow); + } + + private static void SetRangeLabel( + UiElement row, uint elementId, string labelKey, Func resolveString) + { + if (UiElement.FindDescendant(row, elementId) is not UiText text) + return; + string? label = resolveString(StringTableId, DatStringResolver.ComputeHash(labelKey)); + if (label is null) + { + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: range label '{labelKey}' did not " + + "resolve — rendered with no text rather than invented English."); + return; + } + text.LinesProvider = () => new[] { new UiText.Line(label, text.DefaultColor) }; + } + + /// + /// One per-window filter block: resolves the block template DIRECTLY through + /// (NOT via + /// — the block must be + /// fully populated, growing to its final self-sized height (AP-195), BEFORE it is + /// stacked into 's viewport), seeds it with retail's + /// authored window default, adds every authored row (skipping "Gameplay" on the + /// main window per ), THEN inserts + /// it via . + /// + private static void BuildFilterBlock( + UiTemplateListBox listBox, + FilterBlockSpec spec, + OptionPage page, + Func templateResolver, + Func resolveString, + Bindings bindings) + { + if (BitfieldTemplateIndex >= listBox.Templates.Count) + { + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: bitfield template index " + + $"{BitfieldTemplateIndex} missing from the Chat ListBox's authored " + + $"template list — window {spec.RetailWindowId}'s filter block was not built."); + return; + } + + UiTemplateListEntry entry = listBox.Templates[BitfieldTemplateIndex]; + UiElement? built = templateResolver(entry.TemplateLayoutId, entry.TemplateElementId); + if (built is not UiCheckboxBitfield64 block) + { + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: filter block template did not build as " + + $"UiCheckboxBitfield64 for window {spec.RetailWindowId}."); + return; + } + + // The block resolved fresh through templateResolver above — it needs its + // OWN TemplateResolver wired (a SEPARATE property from the outer + // listBox.TemplateResolver) before AddChild can resolve its per-row + // template (0x10000521), exactly like the outer ListBox's own wiring a + // few lines up in Bind(). + block.TemplateResolver = templateResolver; + + (ulong defaultLow, ulong defaultHigh) = SplitMask(spec.DefaultFilter); + block.SetDefaultValue(defaultLow, defaultHigh); + + for (int i = 0; i < FilterRows.Length; i++) + { + if (i == 0 && !spec.IncludesGameplayRow) + continue; // research doc §5.2: the main window has no Gameplay row. + + FilterRowSpec rowSpec = FilterRows[i]; + string? label = resolveString(StringTableId, DatStringResolver.ComputeHash(rowSpec.RetailLabelKey)); + string? tooltip = resolveString( + StringTableId, DatStringResolver.ComputeHash(rowSpec.RetailLabelKey + "_Desc")); + if (label is null) + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: filter row label " + + $"'{rowSpec.RetailLabelKey}' did not resolve — row renders with no " + + "caption rather than invented English."); + + (ulong low, ulong high) = SplitMask(rowSpec.Mask); + if (block.AddChild(low, high, label ?? string.Empty, tooltip) is null) + Console.WriteLine( + $"[D.2b] ChatOptionsPageController: filter row '{rowSpec.RetailLabelKey}' " + + $"did not build for window {spec.RetailWindowId}."); + } + + listBox.AddPrebuiltRow(block); + + ulong initial = bindings.CurrentFilter(spec.CompactWindowId); + (ulong initialLow, ulong initialHigh) = SplitMask(initial); + block.SetCurrentValue(initialLow, initialHigh); // live seed, not the window default + + var row = new BitfieldOptionRow( + initial, + spec.DefaultFilter, + apply: value => + { + (ulong low, ulong high) = SplitMask(value); + block.SetCurrentValue(low, high); + bindings.SetFilter(spec.CompactWindowId, value); + }, + read: () => bindings.CurrentFilter(spec.CompactWindowId), + refresh: value => + { + (ulong low, ulong high) = SplitMask(value); + block.SetCurrentValue(low, high); + }); + + block.ValueChanged = (low, high) => row.SetCurrentValue(CombineMask(low, high)); + + page.Register(row); + } + + /// Splits ONE 64-bit filter value into (bits[31:0], bits[63:32]) — + /// the same split 's Low/High pair encodes (see + /// this class's own doc for why that is not a genuine 128-bit value). + private static (ulong Low, ulong High) SplitMask(ulong combined) + => (combined & 0xFFFFFFFFul, (combined >> 32) & 0xFFFFFFFFul); + + /// Inverse of . + private static ulong CombineMask(ulong low, ulong high) + => ((high & 0xFFFFFFFFul) << 32) | (low & 0xFFFFFFFFul); +} diff --git a/src/AcDream.App/UI/Layout/DatWidgetFactory.cs b/src/AcDream.App/UI/Layout/DatWidgetFactory.cs index 25ad06be..008cef17 100644 --- a/src/AcDream.App/UI/Layout/DatWidgetFactory.cs +++ b/src/AcDream.App/UI/Layout/DatWidgetFactory.cs @@ -159,7 +159,11 @@ public static class DatWidgetFactory // gmChatOptionsUI::AddCheckboxBitfield64Option call pattern (research doc // §5.2). AddChild(lowMask, highMask, label, tooltip) resolves that SAME // template per row instead of synthesizing a fake ElementInfo. - 0x10000044u => new UiCheckboxBitfield64(info.TemplateList), + // AP-195 (OP5): the block ALSO authors its own all/partial-set LED media + // (dat properties 0x10000082/0x10000083) directly on this element — thread + // them through so Refresh's ported LED swap has real sprites to apply. + 0x10000044u => new UiCheckboxBitfield64( + info.TemplateList, info.LedCheckedSprite, info.LedUncheckedSprite), _ => new UiDatElement(info, resolve), // generic fallback (incl. Type 3 chrome/containers) }; diff --git a/src/AcDream.App/UI/Layout/ElementReader.cs b/src/AcDream.App/UI/Layout/ElementReader.cs index de4171a2..085d5d2b 100644 --- a/src/AcDream.App/UI/Layout/ElementReader.cs +++ b/src/AcDream.App/UI/Layout/ElementReader.cs @@ -198,6 +198,25 @@ public sealed class ElementInfo /// public List TemplateList = new(); + /// + /// AP-195 (Campaign OP slice OP5): UIOption_CheckboxBitfield64's block-level + /// "all mask bits set" LED media (dat property 0x10000082, + /// P0x10000082 = 0x06004D17 on the authored template consumer + /// 0x10000520) — retail Refresh @0x004859C0 swaps a row's LED to THIS + /// sprite when every bit of that row's mask is currently set. 0 when absent. + /// Populated the same way as . + /// + public uint LedCheckedSprite; + + /// + /// AP-195 counterpart of : the "some but not all mask + /// bits set" LED media (dat property 0x10000083, + /// P0x10000083 = 0x06004D19). Retail applies this ONLY while the row is + /// checked (any-set) but not fully checked (all-set) — see + /// UiCheckboxBitfield64's own doc for the full any/all-set decode. + /// + public uint LedUncheckedSprite; + /// /// Element id of this ListBox's linked scrollbar, read from dat property /// 0x72 (e.g. the Character page's ListBox 0x100001FA names @@ -503,6 +522,13 @@ public static class ElementReader // (same doc §10.1 — e.g. Character ListBox 0x100001FA names scrollbar // 0x100001FB). info.ScrollbarElementId = ReadReferencedElementId(info, 0x72u); + + // AP-195: the CheckboxBitfield64 block's own all/partial-set LED media DIDs + // (0x10000082/0x10000083 — DataId-kind properties, verified against the + // committed options_2100002B.json fixture at element 0x10000520: Kind=2 + // (DataId), UnsignedValue 100683031/100683033 == 0x06004D17/0x06004D19). + info.LedCheckedSprite = ReadReferencedElementId(info, 0x10000082u); + info.LedUncheckedSprite = ReadReferencedElementId(info, 0x10000083u); } private static List ReadTabTable(ElementInfo info) diff --git a/src/AcDream.App/UI/Layout/OptionPageModel.cs b/src/AcDream.App/UI/Layout/OptionPageModel.cs index 817e5f02..07b75858 100644 --- a/src/AcDream.App/UI/Layout/OptionPageModel.cs +++ b/src/AcDream.App/UI/Layout/OptionPageModel.cs @@ -168,6 +168,198 @@ public sealed class BoolOptionRow : IOptionRow } } +/// +/// Campaign OP slice OP5: the Chat tab's two linked opacity sliders' leaf — +/// UIOption_Slider's current/saved/default triple over a , +/// same shape as (research doc §3.3's leaf semantics apply +/// identically; retail's UIOption_Slider shares the same base UIOption +/// verbs as UIOption_Checkbox, just over a float value). +/// is what dragging the slider thumb runs — retail's SetCurrentValue → Apply(1) +/// applies LIVE, immediately, on every drag tick (not just mouse-up), which is what +/// makes the two Chat-tab sliders visibly fade windows WHILE dragging rather than only +/// on release. +/// +public sealed class FloatOptionRow : IOptionRow +{ + private readonly Action? _apply; + private readonly Func? _read; + private readonly Action? _refresh; + private Action? _notifyPageOptionChanged; + private float _current; + private float _saved; + private float _default; + + /// Re-reads the LIVE value on + /// (OnShown/Apply) — same MUST-FIX 1 discipline as 's + /// own read parameter. + /// Pushes a re-read/linked value onto the slider widget + /// WITHOUT invoking — used by both the OnShown re-seed + /// AND by (the linked slider's own drag moving THIS + /// row's value as a side effect, per ChatOpacityLink's never-clamp-always- + /// drag-the-other invariant). + public FloatOptionRow( + float initial, + float defaultValue, + Action? apply = null, + Func? read = null, + Action? refresh = null) + { + _current = initial; + _saved = initial; + _default = defaultValue; + _apply = apply; + _read = read; + _refresh = refresh; + } + + /// The live value — what the slider thumb currently shows. + public float Current => _current; + + /// The committed baseline Reset reverts to. + public float Saved => _saved; + + /// The value Defaults restores (the DAT DBProperties-extracted + /// slider default for the Chat tab's two sliders — U1's + /// InqDefaultGameplayOptionProperty mechanism, this tab's genuine + /// consumer). + public float DefaultValue => _default; + + public bool Changed => _saved != _current; + + public void SetDefaultValue(float value) => _default = value; + + /// Retail SetCurrentValue — the drag-tick entry point. Writes + /// m_current and applies it live IMMEDIATELY; does not touch + /// . + public void SetCurrentValue(float value) + { + _current = value; + _apply?.Invoke(value); + _notifyPageOptionChanged?.Invoke(); + } + + /// + /// The LINKED slider's own drag already ran ChatOpacityLink's math and + /// applied the new pair live (a single RetailWindowOpacityController call + /// covers both values) — this pushes the resulting value onto THIS row/widget + /// without re-applying (that would double-fire the live opacity write) but still + /// notifies the page, since the OTHER slider's Changed baseline may now + /// differ too (retail's DualHash link moves both sliders' own + /// m_current). + /// + public void RefreshFromLink(float value) + { + _current = value; + _refresh?.Invoke(value); + _notifyPageOptionChanged?.Invoke(); + } + + public void AttachPageNotify(Action notify) => _notifyPageOptionChanged = notify; + + public void SaveCurrentValue() + { + if (_read is not null) + { + _current = _read(); + _refresh?.Invoke(_current); + } + _saved = _current; + } + + public void RestoreSavedValue() + { + _current = _saved; + _apply?.Invoke(_current); + } + + public void RestoreDefaultValue() + { + _current = _default; + _apply?.Invoke(_current); + } +} + +/// +/// Campaign OP slice OP5: one of the Chat tab's five per-window text-filter +/// UIOption_CheckboxBitfield64 blocks — the current/saved/default triple over +/// the block's combined 64-bit filter value (retail's genuine one-register +/// m_llTextTypeFilter shape; see UiCheckboxBitfield64's own class doc for +/// why the widget's CurrentLow/CurrentHigh pair is just that ONE 64-bit +/// value split at the 32-bit boundary, not a 128-bit value). Unlike +/// /, the WIDGET already IS the +/// live UI for N individual checkboxes and fires +/// on any row toggle — this row wraps that composite value the same way a page needs, +/// translating widget-originated edits into / +/// bindings.SetFilter and page-originated reverts (Reset/Defaults/OnShown) into +/// pushes. +/// +public sealed class BitfieldOptionRow : IOptionRow +{ + private readonly Action? _apply; + private readonly Func? _read; + private readonly Action? _refresh; + private Action? _notifyPageOptionChanged; + private ulong _current; + private ulong _saved; + private ulong _default; + + public BitfieldOptionRow( + ulong initial, + ulong defaultValue, + Action? apply = null, + Func? read = null, + Action? refresh = null) + { + _current = initial; + _saved = initial; + _default = defaultValue; + _apply = apply; + _read = read; + _refresh = refresh; + } + + public ulong Current => _current; + public ulong Saved => _saved; + public ulong DefaultValue => _default; + public bool Changed => _saved != _current; + + public void SetDefaultValue(ulong value) => _default = value; + + /// The widget-originated entry point — wired to + /// , fired the instant a row + /// checkbox toggles (retail's own per-checkbox Apply(1)). + public void SetCurrentValue(ulong value) + { + _current = value; + _apply?.Invoke(value); + _notifyPageOptionChanged?.Invoke(); + } + + public void AttachPageNotify(Action notify) => _notifyPageOptionChanged = notify; + + public void SaveCurrentValue() + { + if (_read is not null) + { + _current = _read(); + _refresh?.Invoke(_current); + } + _saved = _current; + } + + public void RestoreSavedValue() + { + _current = _saved; + _apply?.Invoke(_current); + } + + public void RestoreDefaultValue() + { + _current = _default; + _apply?.Invoke(_current); + } +} + /// /// Retail OptionPage/PlayerOptionPage: a page's registered-option /// array plus the four verbs (Apply/Reset/Defaults/visibility) with retail's diff --git a/src/AcDream.App/UI/RetailUiRuntime.cs b/src/AcDream.App/UI/RetailUiRuntime.cs index 7a60e5a5..daec1da5 100644 --- a/src/AcDream.App/UI/RetailUiRuntime.cs +++ b/src/AcDream.App/UI/RetailUiRuntime.cs @@ -657,19 +657,18 @@ public sealed class RetailUiRuntime : IDisposable }; /// - /// Capture the four floating windows' current text-type filters into - /// the local settings store (research doc §4.4/§6.1 — local-only until - /// CH6f's wire format). No-op when no store was wired. + /// Capture the main window's and four floating windows' current text-type + /// filters into the local settings store (research doc §4.4/§6.1 — local-only + /// until a future retail 0x1000008C wire format). No-op when no store was + /// wired. /// /// - /// CH6a/b REJECT-review NIT 1: unlike window geometry/visibility (which - /// auto-saves on every change), a filter - /// change is only captured here — i.e. only when - /// runs, which today is exclusively the explicit /saveautoui - /// command. Harmless in practice (nothing currently mutates a window's - /// filter live — no options UI exists yet), but worth tightening to - /// auto-save-on-change once CH6e/CH6f gives filters a live settings - /// surface a user can actually edit mid-session. + /// CH6a/b REJECT-review NIT 1 — CLOSED by Campaign OP slice OP5: filters are now + /// ALSO captured on every live edit through the Chat tab's five filter blocks + /// (each block's BitfieldOptionRow.apply closure calls this directly), not + /// only when runs via the explicit /saveautoui + /// command — OP5 is the "live settings surface a user can actually edit + /// mid-session" this NIT was waiting on. /// /// private void SaveChatWindowFilters() @@ -679,6 +678,7 @@ public sealed class RetailUiRuntime : IDisposable ChatSettings current = store.LoadChat(); store.SaveChat(current with { + ChatWindowMainFilter = windows.GetFilter(ChatWindowState.MainWindowId), ChatWindow1Filter = windows.GetFilter(1), ChatWindow2Filter = windows.GetFilter(2), ChatWindow3Filter = windows.GetFilter(3), @@ -686,6 +686,25 @@ public sealed class RetailUiRuntime : IDisposable }); } + /// + /// Campaign OP slice OP5: persists the Chat tab's two opacity-slider values — + /// the "worth tightening to auto-save-on-change" gap + /// 's own doc flagged before a live options UI + /// existed to edit them. Called from the Chat tab's slider apply closures, so + /// every drag tick is captured (matching the filter blocks' own auto-save-on- + /// change wiring below, not the old "only on /saveautoui" schedule). + /// + private void SaveChatOpacity() + { + if (_bindings.Chat.Store is not { } store) return; + ChatSettings current = store.LoadChat(); + store.SaveChat(current with + { + DefaultOpacity = WindowOpacity.DefaultOpacity, + ActiveOpacity = WindowOpacity.ActiveOpacity, + }); + } + public void CloseWindow(string name) { if (RetailPanelCatalog.TryGetPanelId(name, out uint panelId)) @@ -898,6 +917,16 @@ public sealed class RetailUiRuntime : IDisposable return; } + // Campaign OP slice OP5: the main window's own filter (id 0) is now + // user-settable through the Chat tab's main-window filter block + // (research doc §5.2) — seed it from the local store here, the same + // "local-only persistence" leg MountFloatingChatWindows already runs + // for windows 1-4 (research doc §4.4/§6.1 — the retail 0x1000008C + // gameplay-options wire remains explicitly deferred). + if (_bindings.Chat.Store is { } chatStore) + _bindings.Chat.Windows.SetFilter( + ChatWindowState.MainWindowId, chatStore.LoadChat().ChatWindowMainFilter); + ChatWindowController? controller = ChatWindowController.Bind( info, layout, @@ -2040,6 +2069,68 @@ public sealed class RetailUiRuntime : IDisposable Console.WriteLine("[UI] options panel: Character tab rows did not bind."); } + // Campaign OP slice OP5 (2026-08-11): the Chat tab's 6 headers, 2 linked + // opacity sliders, and 5 per-window filter blocks. Same "runs before + // ActivateTabs, own dat-lock scope" shape as the Character-tab block above. + lock (_bindings.Assets.DatLock) + { + var strings = new DatStringResolver(_bindings.Assets.Dats); + + // U1's Chat-tab-specific consumer (this class's own doc trace): the + // two sliders' Defaults values come from the DAT DBProperties + // collection, resolved ONCE — the installed DAT does not change + // within a session. + if (!Layout.ChatOptionsDatDefaults.TryRead( + _bindings.Assets.Dats, out float datDefaultOpacity, out float datActiveOpacity)) + { + Console.WriteLine( + "[UI] options panel: Chat tab opacity DAT defaults did not resolve " + + "(DID 0x78000001) — falling back to retail's ChatInterface base-" + + "constructor values (0.5/1.0)."); + } + + bool chatBound = Layout.ChatOptionsPageController.Bind( + layout, + controller.ChatPage, + templateResolver: (templateLayoutId, templateElementId) => + { + ElementInfo? info = LayoutImporter.ImportInfos( + _bindings.Assets.Dats, templateLayoutId, templateElementId); + return info is null + ? null + : LayoutImporter.Build( + info, + _bindings.Assets.ResolveSprite, + _bindings.Assets.DefaultFont, + _bindings.Assets.ResolveFont, + strings.Resolve).Root; + }, + resolveString: (tableId, stringId) => strings.Resolve(tableId, stringId), + new Layout.ChatOptionsPageController.Bindings( + CurrentDefaultOpacity: () => WindowOpacity.DefaultOpacity, + CurrentActiveOpacity: () => WindowOpacity.ActiveOpacity, + SetDefaultOpacity: value => + { + WindowOpacity.SetDefaultOpacity(value); + SaveChatOpacity(); + }, + SetActiveOpacity: value => + { + WindowOpacity.SetActiveOpacity(value); + SaveChatOpacity(); + }, + DefaultOpacityDatDefault: datDefaultOpacity, + ActiveOpacityDatDefault: datActiveOpacity, + CurrentFilter: windowId => _bindings.Chat.Windows.GetFilter(windowId), + SetFilter: (windowId, value) => + { + _bindings.Chat.Windows.SetFilter(windowId, value); + SaveChatWindowFilters(); + })); + if (!chatBound) + Console.WriteLine("[UI] options panel: Chat tab rows did not bind."); + } + controller.ActivateTabs(); RetailWindowHandle handle = RetailWindowFrame.Mount( diff --git a/src/AcDream.App/UI/UiButton.cs b/src/AcDream.App/UI/UiButton.cs index ff189f0d..305dfbd0 100644 --- a/src/AcDream.App/UI/UiButton.cs +++ b/src/AcDream.App/UI/UiButton.cs @@ -116,6 +116,24 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful public float FaceWidth { get; set; } public float FaceHeight { get; set; } + /// + /// AP-195 (Campaign OP slice OP5): when non-null, drawn as the face media INSTEAD + /// of 's resolved sprite — a dat RenderSurface id, not a + /// pre-resolved texture handle (resolved through the same _resolve callback + /// as the normal path, so tiling/UV math stays identical). Ports retail + /// UIOption_CheckboxBitfield64::Refresh @0x004859C0's + /// SetMediaImageForState(indicatorChild, dataId, 1, 6): the Chat tab's + /// per-window filter rows force their 5-state LED indicator to ONE fixed image + /// (the block-level all/partial-set sprite) regardless of hover/press state, + /// bypassing the checkbox's own per-state media entirely. Null (default) restores + /// the ordinary -driven lookup — every other button + /// instance in the codebase is unaffected. Same shape as + /// , deliberately a DID here + /// (not a texture handle) because the checkbox draw path already resolves its + /// file id through _resolve for correct native-size UV tiling. + /// + public uint? FaceFileOverride { get; set; } + /// Additional left inset for left-aligned labels. public float LabelOffsetX { get; set; } = 3f; @@ -326,7 +344,7 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful } else { - uint file = ActiveFile(_mediaInfo); + uint file = FaceFileOverride ?? ActiveFile(_mediaInfo); if (file != 0) { var (tex, tw, th) = _resolve(file); diff --git a/src/AcDream.App/UI/UiCheckboxBitfield64.cs b/src/AcDream.App/UI/UiCheckboxBitfield64.cs index b981805c..67e70e15 100644 --- a/src/AcDream.App/UI/UiCheckboxBitfield64.cs +++ b/src/AcDream.App/UI/UiCheckboxBitfield64.cs @@ -70,6 +70,19 @@ public sealed class UiCheckboxBitfield64 : UiPanel /// public IReadOnlyList Templates { get; } + /// + /// AP-195 (Campaign OP slice OP5): block-level "all mask bits set" LED media + /// (dat property 0x10000082 on THIS element, 0x06004D17 on the + /// installed DAT). 0 when the authoring template carries none — the LED override + /// is then simply never applied (rows keep their per-row authored art). + /// + public uint CheckedLedSprite { get; } + + /// AP-195 counterpart of : block-level + /// "checked but not every mask bit set" LED media (dat property + /// 0x10000083, 0x06004D19). + public uint UncheckedLedSprite { get; } + /// Same seam as — a page /// controller wires this with real DAT access (Campaign OP slice OP5+). Left null /// by DatWidgetFactory itself. @@ -84,12 +97,21 @@ public sealed class UiCheckboxBitfield64 : UiPanel // SpriteResolve (forwarded to each row's built subtree) is inherited from UiPanel — // same resolver shape, no need to redeclare it. - /// Fired with the new (low, high) value after any row toggles. + /// Fired with the new (low, high) value after any row toggles — a LIVE + /// user edit only (retail's Apply(1) path). NEVER fired by + /// (the OnShown/Reset/Defaults re-seed path — pushing + /// an externally-sourced value must not loop back into "the user changed this"). + /// public Action? ValueChanged { get; set; } - public UiCheckboxBitfield64(IReadOnlyList templates) + public UiCheckboxBitfield64( + IReadOnlyList templates, + uint checkedLedSprite = 0u, + uint uncheckedLedSprite = 0u) { Templates = templates; + CheckedLedSprite = checkedLedSprite; + UncheckedLedSprite = uncheckedLedSprite; BackgroundColor = Vector4.Zero; BorderColor = Vector4.Zero; } @@ -121,6 +143,24 @@ public sealed class UiCheckboxBitfield64 : UiPanel ValueChanged?.Invoke(CurrentLow, CurrentHigh); } + /// + /// Pushes an externally-sourced (low, high) value onto the widget — the page-model + /// re-seed path (retail's Refresh re-run after + /// OptionPage::RestoreSavedValues/RestoreDefaultValues/the OnShown + /// SaveCurrentValue re-read). Updates / + /// and every row's checked state + AP-195 LED media, but + /// deliberately does NOT invoke — the caller (an + /// IOptionRow wrapper) already owns whatever notification it needs, and + /// firing here would loop a re-seed back into "the user changed this" (the same + /// live/refresh distinction BoolOptionRow's refresh delegate makes). + /// + public void SetCurrentValue(ulong low, ulong high) + { + CurrentLow = low; + CurrentHigh = high; + RefreshRowVisuals(); + } + /// /// Retail UIOption_CheckboxBitfield64::AddChild(lowMask, highMask, labelId, /// tooltipId)CreateChildren's per-row loop: instantiates the authored @@ -174,7 +214,6 @@ public sealed class UiCheckboxBitfield64 : UiPanel // replaced by the typed mask closure below: equivalent routing, // without a stringly attribute round-trip. checkbox.TooltipText = tooltip; - checkbox.Selected = IsSet(lowMask, highMask); checkbox.OnClick = () => ToggleRow(lowMask, highMask, checkbox); row.Left = 0f; @@ -182,7 +221,24 @@ public sealed class UiCheckboxBitfield64 : UiPanel _contentHeight += row.Height; base.AddChild(row); - _rows.Add(new Row(lowMask, highMask, label, tooltip, row, checkbox)); + // AP-195 self-sizing tail: retail's own CreateChildren ends with + // ResizeTo(GetWidth(), CalculatePaperSize(0, -1)) because + // UIOption_CheckboxBitfield64 IS a UIElement_ListBox (its PostInit + // tail-calls UIElement_Scrollable::PostInit) — the block grows to fit its + // rows instead of keeping its authored 272x100 extent. Width is untouched + // (GetWidth() — the ListBox's own authored column width); only Height + // grows, exactly matching the stacked _contentHeight this class already + // tracks per row. A dedicated ListBox-unification pass (reusing + // UiTemplateListBox's own viewport) remains future work per AP-195's own + // disposition menu; this direct resize is the recorded-rationale half of + // that menu — see UiTemplateListBox.AddPrebuiltRow for how a page + // controller reflows the ENCLOSING ListBox around this widget's now-final + // height instead of the pre-build authored one. + Height = _contentHeight; + + var newRow = new Row(lowMask, highMask, label, tooltip, row, checkbox); + _rows.Add(newRow); + ApplyRowVisuals(newRow); return checkbox; } @@ -199,18 +255,26 @@ public sealed class UiCheckboxBitfield64 : UiPanel } /// - /// Retail UIOption_CheckboxBitfield64::Refresh @0x004859C0: a row is checked - /// when ANY bit of its mask is set in the current value — NOT when every bit is - /// set. lowHit = current.low & mask.low; highHit = current.high & - /// mask.high; checked = (lowHit | highHit) != 0. This is HALF of Refresh: - /// the same pass also computes the ALL-set predicate to swap each row's LED - /// media between the two authored surfaces (P0x10000082 = 0x06004D17 / - /// P0x10000083 = 0x06004D19) — not ported yet, register row AP-195, - /// due with the Chat tab (OP5) alongside the ListBox self-sizing tail. + /// Retail UIOption_CheckboxBitfield64::Refresh @0x004859C0's ANY-set half: + /// a row is checked when ANY bit of its mask is set in the current value — NOT + /// when every bit is set. lowHit = current.low & mask.low; highHit = + /// current.high & mask.high; checked = (lowHit | highHit) != 0. /// - private bool IsSet(ulong lowMask, ulong highMask) + private bool IsAnySet(ulong lowMask, ulong highMask) => (CurrentLow & lowMask) != 0 || (CurrentHigh & highMask) != 0; + /// + /// AP-195: retail Refresh's ALL-set half, computed by the SAME pass as + /// (current.low & mask.low) == mask.low && + /// (current.high & mask.high) == mask.high. Drives which of + /// / a checked row's + /// LED shows: composite masks (e.g. the Gameplay row's 0x83912021, five + /// bits) can be PARTIALLY satisfied — checked (any-set) but not fully (all-set) — + /// which single-bit rows (e.g. "General") can never observably distinguish. + /// + private bool IsAllSet(ulong lowMask, ulong highMask) + => (CurrentLow & lowMask) == lowMask && (CurrentHigh & highMask) == highMask; + /// /// Retail UIOption_CheckboxBitfield64::ListenToElementMessage @0x00485AE0: /// BitUtils::SetBitsOnOrOff(&m_current, mask, onOff) — decomp-confirmed @@ -219,7 +283,7 @@ public sealed class UiCheckboxBitfield64 : UiPanel /// private void ToggleRow(ulong lowMask, ulong highMask, UiButton toggle) { - bool turnOn = !IsSet(lowMask, highMask); + bool turnOn = !IsAnySet(lowMask, highMask); if (turnOn) { CurrentLow |= lowMask; @@ -230,13 +294,37 @@ public sealed class UiCheckboxBitfield64 : UiPanel CurrentLow &= ~lowMask; CurrentHigh &= ~highMask; } - toggle.Selected = turnOn; + ApplyRowVisualsForMask(lowMask, highMask, toggle); ValueChanged?.Invoke(CurrentLow, CurrentHigh); } private void RefreshRowVisuals() { foreach (Row row in _rows) - row.Toggle.Selected = IsSet(row.LowMask, row.HighMask); + ApplyRowVisuals(row); + } + + private void ApplyRowVisuals(Row row) => ApplyRowVisualsForMask(row.LowMask, row.HighMask, row.Toggle); + + /// + /// One row's complete Refresh visual: the ANY-set checked bool + /// (, always) plus AP-195's ALL-set-driven LED + /// media swap () — retail's own gate: the + /// swap ONLY applies while ANY-set is true; a fully-unchecked row keeps its + /// authored per-row art untouched (Refresh @0x004859C0's + /// if (ebx == 0) { ... media swap ... } branch, where ebx == 0 is + /// the ANY-set case per the byte trace in this class's own research citations). + /// + private void ApplyRowVisualsForMask(ulong lowMask, ulong highMask, UiButton toggle) + { + bool anySet = IsAnySet(lowMask, highMask); + toggle.Selected = anySet; + if (!anySet) + { + toggle.FaceFileOverride = null; + return; + } + uint overrideSprite = IsAllSet(lowMask, highMask) ? CheckedLedSprite : UncheckedLedSprite; + toggle.FaceFileOverride = overrideSprite != 0u ? overrideSprite : null; } } diff --git a/src/AcDream.App/UI/UiTemplateListBox.cs b/src/AcDream.App/UI/UiTemplateListBox.cs index 56dc351f..4091d143 100644 --- a/src/AcDream.App/UI/UiTemplateListBox.cs +++ b/src/AcDream.App/UI/UiTemplateListBox.cs @@ -179,6 +179,35 @@ public sealed class UiTemplateListBox : UiDatElement UiElement? row = resolver(entry.TemplateLayoutId, entry.TemplateElementId); if (row is null) return null; + return AddPrebuiltRow(row); + } + + /// + /// AP-195 (Campaign OP slice OP5): stacks an ALREADY-BUILT row using the exact + /// same "below the previous row, then grow ContentHeight" placement + /// uses internally, WITHOUT resolving it + /// through first. For a row whose own final size + /// is not known until AFTER it is fully populated — the Chat tab's five + /// filter blocks, which self-size to fit + /// their N rows (retail's own CreateChildren tail, + /// docs/architecture/retail-divergence-register.md AP-195) — a page + /// controller resolves the block directly through the SAME + /// delegate (bypassing + /// 's own resolve step), calls + /// SetDefaultValue + N AddChild on it (growing its + /// to its final, fully-populated extent), THEN + /// calls this method to insert it. Because the row is placed at its FINAL height + /// instead of its authored pre-build extent, this viewport's own + /// — and every row placed AFTER + /// it — accounts for the real size from the start; no retroactive reflow of + /// already-placed siblings is needed (this class has no such reflow mechanism — + /// each row's is fixed at insertion time). This is + /// literally "reuse UiTemplateListBox's stacking," the disposition AP-195 itself + /// names as an acceptable alternative to a full ListBox-unification pass. + /// + public UiElement AddPrebuiltRow(UiElement row) + { + ArgumentNullException.ThrowIfNull(row); UiScrollablePanel viewport = Viewport; row.Left = 0f; row.Top = viewport.ContentHeight; diff --git a/src/AcDream.Core/Chat/ChatWindowState.cs b/src/AcDream.Core/Chat/ChatWindowState.cs index 9706eae1..2267d946 100644 --- a/src/AcDream.Core/Chat/ChatWindowState.cs +++ b/src/AcDream.Core/Chat/ChatWindowState.cs @@ -69,6 +69,22 @@ public sealed class ChatWindowState public const int MinFloatingWindowId = 1; public const int MaxFloatingWindowId = 4; + /// + /// Retail's authored per-window text-type filter defaults + /// (gmChatOptionsUI::InitOptions @0x0049FC60 / + /// AddCheckboxBitfield64Option @0x0049EDA0's SetDefaultValue calls, + /// docs/research/2026-08-10-options-panel-structure.md §5.2). Named here + /// (rather than only as the private literals in ) + /// because Campaign OP slice OP5's Chat-tab Defaults button needs the SAME + /// values seeds — one source, not two independently + /// literal tables that could drift. + /// + public const ulong MainWindowDefaultFilter = 0xFBFFFFFFul; + public const ulong Floaty1DefaultFilter = 0x0000101Cul; + public const ulong Floaty2DefaultFilter = 0x00040C00ul; + public const ulong Floaty3DefaultFilter = 0x00080000ul; + public const ulong Floaty4DefaultFilter = 0x78000000ul; + /// /// Sentinel targetWindowId meaning "broadcast to every window, /// subject to each window's own filter" — the acdream-internal analogue @@ -108,15 +124,15 @@ public sealed class ChatWindowState // "everything 0x00-0x1F except 0x1A" (m_oldState 1/8) — genuinely // consulted for broadcast lines now that BroadcastTargetWindow is // distinct from MainWindowId (see class doc). - _filters[0] = 0xFBFFFFFFu; + _filters[0] = MainWindowDefaultFilter; // Speech, Tell, Speech_Direct_Send, Emote (m_oldState 2). - _filters[1] = 0x0000101Cu; + _filters[1] = Floaty1DefaultFilter; // Social, Social_Send, Allegiance (m_oldState 3). - _filters[2] = 0x00040C00u; + _filters[2] = Floaty2DefaultFilter; // Fellowship (m_oldState 4). - _filters[3] = 0x00080000u; + _filters[3] = Floaty3DefaultFilter; // Turbine General/Trade/LFG/Roleplay (m_oldState 5). - _filters[4] = 0x78000000u; + _filters[4] = Floaty4DefaultFilter; _open[0] = true; for (int i = MinFloatingWindowId; i <= MaxFloatingWindowId; i++) diff --git a/src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs b/src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs index 41b8d373..3a27f6b4 100644 --- a/src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs +++ b/src/AcDream.UI.Abstractions/Panels/Settings/ChatSettings.cs @@ -62,6 +62,13 @@ public sealed record ChatSettings( ulong ChatWindow2Filter = 0x00040C00u, // Social, Social_Send, Allegiance ulong ChatWindow3Filter = 0x00080000u, // Fellowship ulong ChatWindow4Filter = 0x78000000u, // Turbine General/Trade/LFG/Roleplay + // Campaign OP slice OP5: the MAIN chat window's own filter — CH6a/b's + // ChatWindowState.SetFilter already accepted window id 0 as a real write + // (its class doc: "Retail's main window IS user-settable through the + // options page; only the settings UI to drive this is future work") but + // nothing persisted it until the Chat tab's main-window filter block + // shipped. Default matches ChatWindowState.MainWindowDefaultFilter. + ulong ChatWindowMainFilter = 0xFBFFFFFFu, // Campaign CH slice CH6c: retail's two GLOBAL window-opacity options // (Option_DefaultOpacity_Property 0x10000080 / Option_ActiveOpacity_Property // 0x10000081, docs/research/2026-08-09-chat-retail-window-shell.md §3). diff --git a/src/AcDream.UI.Abstractions/Panels/Settings/SettingsStore.cs b/src/AcDream.UI.Abstractions/Panels/Settings/SettingsStore.cs index d8f38fd1..e725c223 100644 --- a/src/AcDream.UI.Abstractions/Panels/Settings/SettingsStore.cs +++ b/src/AcDream.UI.Abstractions/Panels/Settings/SettingsStore.cs @@ -197,6 +197,7 @@ public sealed class SettingsStore ChatWindow2Filter: ReadULong(chat, "chatWindow2Filter", d.ChatWindow2Filter), ChatWindow3Filter: ReadULong(chat, "chatWindow3Filter", d.ChatWindow3Filter), ChatWindow4Filter: ReadULong(chat, "chatWindow4Filter", d.ChatWindow4Filter), + ChatWindowMainFilter: ReadULong(chat, "chatWindowMainFilter", d.ChatWindowMainFilter), DefaultOpacity: ReadFloat(chat, "defaultOpacity", d.DefaultOpacity), ActiveOpacity: ReadFloat(chat, "activeOpacity", d.ActiveOpacity)); } @@ -583,6 +584,7 @@ public sealed class SettingsStore ["chatWindow2Filter"] = c.ChatWindow2Filter, ["chatWindow3Filter"] = c.ChatWindow3Filter, ["chatWindow4Filter"] = c.ChatWindow4Filter, + ["chatWindowMainFilter"] = c.ChatWindowMainFilter, ["defaultOpacity"] = c.DefaultOpacity, ["filterProfanity"] = c.FilterProfanity, ["fontSize"] = c.FontSize, diff --git a/tests/AcDream.App.Tests/UI/Layout/ChatOptionsDatDefaultsTests.cs b/tests/AcDream.App.Tests/UI/Layout/ChatOptionsDatDefaultsTests.cs new file mode 100644 index 00000000..a3638377 --- /dev/null +++ b/tests/AcDream.App.Tests/UI/Layout/ChatOptionsDatDefaultsTests.cs @@ -0,0 +1,57 @@ +using AcDream.App.UI.Layout; +using DatReaderWriter; +using DatReaderWriter.Options; +using Xunit.Sdk; +using SysEnv = System.Environment; + +namespace AcDream.App.Tests.UI.Layout; + +/// +/// Campaign OP slice OP5: live-DAT proof that +/// resolves the Chat tab's two opacity-slider defaults from the installed DAT — +/// same pattern as RetailSelectionAssetTests (runs against the real +/// installed client_portal.dat, skips gracefully when none is present, no +/// env-var gate needed since it never writes anything). +/// +public sealed class ChatOptionsDatDefaultsTests +{ + [Fact] + public void InstalledDat_ResolvesTheByteVerifiedSliderDefaults() + { + string datDir = ResolveDatDir(); + using var dats = new DatCollection(datDir, DatAccessType.Read); + + bool resolved = ChatOptionsDatDefaults.TryRead( + dats, out float defaultOpacity, out float activeOpacity); + + // Empirically confirmed against the installed DAT (this slice's own research + // trace): DID 0x78000001, Option_DefaultOpacity_Property = 0.5, + // Option_ActiveOpacity_Property = 1.0 — matching retail's ChatInterface base- + // constructor fallback exactly (a coincidence the fallback documents, not + // something this test relies on to pass). + Assert.True(resolved); + Assert.Equal(0.5f, defaultOpacity); + Assert.Equal(1.0f, activeOpacity); + } + + private static string ResolveDatDir() + { + string? configured = SysEnv.GetEnvironmentVariable("ACDREAM_DAT_DIR"); + if (!string.IsNullOrWhiteSpace(configured) + && File.Exists(Path.Combine(configured, "client_portal.dat"))) + return configured; + + const string installed = @"C:\Turbine\Asheron's Call"; + if (File.Exists(Path.Combine(installed, "client_portal.dat"))) + return installed; + + string conventional = Path.Combine( + SysEnv.GetFolderPath(SysEnv.SpecialFolder.UserProfile), + "Documents", + "Asheron's Call"); + if (File.Exists(Path.Combine(conventional, "client_portal.dat"))) + return conventional; + + throw SkipException.ForSkip("Installed client_portal.dat is required."); + } +} diff --git a/tests/AcDream.App.Tests/UI/Layout/ChatOptionsPageControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/ChatOptionsPageControllerTests.cs new file mode 100644 index 00000000..853690fe --- /dev/null +++ b/tests/AcDream.App.Tests/UI/Layout/ChatOptionsPageControllerTests.cs @@ -0,0 +1,607 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AcDream.App.UI; +using AcDream.App.UI.Layout; +using AcDream.Core.Chat; + +namespace AcDream.App.Tests.UI.Layout; + +/// +/// Campaign OP slice OP5 (2026-08-11) conformance + behavior tests for +/// — the same hermetic pattern +/// CharacterOptionsPageControllerTests established: pure-data conformance +/// against the byte-verified research doc/decomp tables, then behavioral tests +/// against the committed options_panel_2100006E_1000018D.json + +/// options_2100002B.json fixtures — no live DAT access. +/// +public sealed class ChatOptionsPageControllerTests +{ + // ── Pure data conformance ──────────────────────────────────────────────── + + [Fact] + public void FilterRows_Has13EntriesInByteVerifiedAuthoredOrder() + { + // docs/research/2026-08-10-options-panel-structure.md §5.2, cross-checked + // against the raw gmChatOptionsUI::InitOptions / AddCheckboxBitfield64Option + // pseudo-C AddChild call sequence (this class's own doc trace). + (ulong Mask, string Key)[] expected = + { + (0x0000000083912021ul, "ID_ChatOption_TextFilter_Gameplay"), + (0x0000000000600040ul, "ID_ChatOption_TextFilter_Combat"), + (0x0000000000020080ul, "ID_ChatOption_TextFilter_Magic"), + (0x0000000000001004ul, "ID_ChatOption_TextFilter_AreaSpeech"), + (0x0000000000000018ul, "ID_ChatOption_TextFilter_Tells"), + (0x0000000000040C00ul, "ID_ChatOption_TextFilter_Allegience"), + (0x0000000000080000ul, "ID_ChatOption_TextFilter_Fellowship"), + (0x0000000008000000ul, "ID_ChatOption_TextFilter_General"), + (0x0000000010000000ul, "ID_ChatOption_TextFilter_Trade"), + (0x0000000020000000ul, "ID_ChatOption_TextFilter_LFG"), + (0x0000000040000000ul, "ID_ChatOption_TextFilter_Roleplay"), + (0x0000000100000000ul, "ID_ChatOption_TextFilter_Society"), + (0x0000000004000000ul, "ID_ChatOption_TextFilter_Error"), + }; + + Assert.Equal(13, ChatOptionsPageController.FilterRows.Length); + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i].Mask, ChatOptionsPageController.FilterRows[i].Mask); + Assert.Equal(expected[i].Key, ChatOptionsPageController.FilterRows[i].RetailLabelKey); + } + } + + [Fact] + public void FilterRows_EveryMaskIsPairwiseDistinct_AndNonZero() + { + var masks = ChatOptionsPageController.FilterRows.Select(static r => r.Mask).ToList(); + Assert.Equal(masks.Count, masks.Distinct().Count()); + Assert.All(masks, m => Assert.NotEqual(0ul, m)); + } + + [Fact] + public void FilterBlocks_Has5EntriesInAuthoredOrder_WithRetailWindowIds() + { + // Research doc §5: main(8), floaty1(2), floaty2(3), floaty3(4), floaty4(5) — + // authored top-to-bottom order in gmChatOptionsUI::InitOptions. + (int RetailId, int CompactId, string HeaderKey, ulong Default, bool IncludesGameplay)[] expected = + { + (8, ChatWindowState.MainWindowId, "ID_ChatOption_MainChatWindow_Section", + 0xFBFFFFFFul, false), + (2, 1, "ID_ChatOption_FloatyChatWindow1_Section", 0x0000101Cul, true), + (3, 2, "ID_ChatOption_FloatyChatWindow2_Section", 0x00040C00ul, true), + (4, 3, "ID_ChatOption_FloatyChatWindow3_Section", 0x00080000ul, true), + (5, 4, "ID_ChatOption_FloatyChatWindow4_Section", 0x78000000ul, true), + }; + + Assert.Equal(5, ChatOptionsPageController.FilterBlocks.Length); + for (int i = 0; i < expected.Length; i++) + { + ChatOptionsPageController.FilterBlockSpec spec = ChatOptionsPageController.FilterBlocks[i]; + Assert.Equal(expected[i].RetailId, spec.RetailWindowId); + Assert.Equal(expected[i].CompactId, spec.CompactWindowId); + Assert.Equal(expected[i].HeaderKey, spec.HeaderKey); + Assert.Equal(expected[i].Default, spec.DefaultFilter); + Assert.Equal(expected[i].IncludesGameplay, spec.IncludesGameplayRow); + } + } + + [Fact] + public void FilterBlocks_DefaultsMatchChatWindowStateNamedConstants() + { + // These MUST be the exact same source (ChatWindowState's own constants), + // not two independently-typed literal tables that could drift apart. + Assert.Equal(ChatWindowState.MainWindowDefaultFilter, ChatOptionsPageController.FilterBlocks[0].DefaultFilter); + Assert.Equal(ChatWindowState.Floaty1DefaultFilter, ChatOptionsPageController.FilterBlocks[1].DefaultFilter); + Assert.Equal(ChatWindowState.Floaty2DefaultFilter, ChatOptionsPageController.FilterBlocks[2].DefaultFilter); + Assert.Equal(ChatWindowState.Floaty3DefaultFilter, ChatOptionsPageController.FilterBlocks[3].DefaultFilter); + Assert.Equal(ChatWindowState.Floaty4DefaultFilter, ChatOptionsPageController.FilterBlocks[4].DefaultFilter); + } + + // ── Row/header string-hash conformance ─────────────────────────────────── + + [Theory] + [InlineData("ID_ChatOption_TextFilter_Gameplay")] + [InlineData("ID_ChatOption_TextFilter_Combat")] + [InlineData("ID_ChatOption_TextFilter_Magic")] + [InlineData("ID_ChatOption_TextFilter_AreaSpeech")] + [InlineData("ID_ChatOption_TextFilter_Tells")] + [InlineData("ID_ChatOption_TextFilter_Allegience")] + [InlineData("ID_ChatOption_TextFilter_Fellowship")] + [InlineData("ID_ChatOption_TextFilter_General")] + [InlineData("ID_ChatOption_TextFilter_Trade")] + [InlineData("ID_ChatOption_TextFilter_LFG")] + [InlineData("ID_ChatOption_TextFilter_Roleplay")] + [InlineData("ID_ChatOption_TextFilter_Society")] + [InlineData("ID_ChatOption_TextFilter_Error")] + public void EveryFilterRowKey_IsAuthoredOnTheFilterRowsTable(string key) + { + Assert.Contains(ChatOptionsPageController.FilterRows, r => r.RetailLabelKey == key); + } + + // ── Behavioral: built against the committed fixtures ───────────────────── + + private static (uint, int, int) NoTex(uint _) => (0, 0, 0); + + private static ElementInfo? Find(ElementInfo n, uint id) + { + if (n.Id == id) return n; + foreach (ElementInfo c in n.Children) + { + ElementInfo? f = Find(c, id); + if (f is not null) return f; + } + return null; + } + + /// The same "resolve a row template from the standalone 0x2100002B + /// fixture" resolver CharacterOptionsPageControllerTests uses. + private static Func MakeTemplateResolver() + { + ElementInfo panelRoot = FixtureLoader.LoadOptionsPanelInfos(); + return (layoutId, elementId) => + { + if (layoutId != 0x2100002Bu) return null; + ElementInfo? templateInfo = Find(panelRoot, elementId); + return templateInfo is null ? null : LayoutImporter.Build(templateInfo, NoTex, null).Root; + }; + } + + private sealed class FakeBindings + { + public float DefaultOpacity = 0.5f; + public float ActiveOpacity = 1.0f; + public float DefaultOpacityDatDefault = 0.5f; + public float ActiveOpacityDatDefault = 1.0f; + public List DefaultOpacitySets { get; } = new(); + public List ActiveOpacitySets { get; } = new(); + + public Dictionary Filters { get; } = new() + { + [ChatWindowState.MainWindowId] = ChatWindowState.MainWindowDefaultFilter, + [1] = ChatWindowState.Floaty1DefaultFilter, + [2] = ChatWindowState.Floaty2DefaultFilter, + [3] = ChatWindowState.Floaty3DefaultFilter, + [4] = ChatWindowState.Floaty4DefaultFilter, + }; + public List<(int WindowId, ulong Value)> FilterSets { get; } = new(); + + public ChatOptionsPageController.Bindings ToBindings() => new( + CurrentDefaultOpacity: () => DefaultOpacity, + CurrentActiveOpacity: () => ActiveOpacity, + SetDefaultOpacity: value => + { + (DefaultOpacity, ActiveOpacity) = ChatOpacityLinkFor(ActiveOpacity, value, isDefault: true); + DefaultOpacitySets.Add(value); + }, + SetActiveOpacity: value => + { + (DefaultOpacity, ActiveOpacity) = ChatOpacityLinkFor(DefaultOpacity, value, isDefault: false); + ActiveOpacitySets.Add(value); + }, + DefaultOpacityDatDefault: DefaultOpacityDatDefault, + ActiveOpacityDatDefault: ActiveOpacityDatDefault, + CurrentFilter: windowId => Filters[windowId], + SetFilter: (windowId, value) => + { + Filters[windowId] = value; + FilterSets.Add((windowId, value)); + }); + + // Mirrors AcDream.UI.Abstractions.Panels.Settings.ChatOpacityLink's own + // math exactly (the fake stands in for RetailWindowOpacityController, + // which itself just calls that link) — kept local so this test file has + // no cross-project dependency for a two-line clamp/drag rule. + private static (float Default, float Active) ChatOpacityLinkFor( + float other, float value, bool isDefault) + { + value = Math.Clamp(value, 0f, 1f); + if (isDefault) + return (value, other < value ? value : other); + return (other > value ? value : other, value); + } + } + + private static (OptionsPanelController Panel, FakeBindings Bindings, bool Bound) BindReal( + Func? resolveString = null) + { + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + + var fakeBindings = new FakeBindings(); + bool bound = ChatOptionsPageController.Bind( + layout, + controller.ChatPage, + MakeTemplateResolver(), + resolveString ?? ((_, _) => null), + fakeBindings.ToBindings()); + + return (controller, fakeBindings, bound); + } + + [Fact] + public void Bind_Succeeds_AndRegistersExactlySevenRows() + { + // 2 opacity sliders + 5 filter blocks = 7 IOptionRow registrations + // (each block is ONE composite row, not one row per checkbox). + (OptionsPanelController controller, _, bool bound) = BindReal(); + + Assert.True(bound); + Assert.Equal(7, controller.ChatPage.Rows.Count); + } + + [Fact] + public void Bind_MainWindowBlock_Has12Rows_FloatyBlocks_Have13Rows() + { + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + var fakeBindings = new FakeBindings(); + bool bound = ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + Assert.True(bound); + + var listBox = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ListBoxElementId)); + List blocks = CollectBlocks(listBox); + + Assert.Equal(5, blocks.Count); + Assert.Equal(12, blocks[0].Rows.Count); // main window — no Gameplay row + for (int i = 1; i < 5; i++) + Assert.Equal(13, blocks[i].Rows.Count); // the four floaties + } + + [Fact] + public void Bind_FilterBlocks_ResolveTheAP195LedSprites() + { + // AP-195 end-to-end: the regenerated fixture now carries dat properties + // 0x10000082/0x10000083 on template consumer 0x10000520 + // (0x06004D17/0x06004D19), and DatWidgetFactory threads them into the + // constructed UiCheckboxBitfield64 — every one of the five blocks shares + // the SAME authored template, so every block resolves the same sprites. + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + var fakeBindings = new FakeBindings(); + ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + + var listBox = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ListBoxElementId)); + List blocks = CollectBlocks(listBox); + + Assert.Equal(5, blocks.Count); + Assert.All(blocks, b => + { + Assert.Equal(0x06004D17u, b.CheckedLedSprite); + Assert.Equal(0x06004D19u, b.UncheckedLedSprite); + }); + } + + [Fact] + public void Bind_FilterBlocks_SelfSizeToFitTheirRows() + { + // AP-195's self-sizing tail — the block grows past its authored 272x100 + // extent to fit N rows (13 floaty rows * 20px template height > 100). + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + var fakeBindings = new FakeBindings(); + ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + + var listBox = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ListBoxElementId)); + List blocks = CollectBlocks(listBox); + + Assert.All(blocks, b => Assert.True(b.Height > 100f, $"block Height={b.Height}")); + Assert.Equal(272f, blocks[0].Width); // width (GetWidth()) untouched + } + + private static List CollectBlocks(UiElement root) + { + var found = new List(); + Walk(root, found); + return found; + + static void Walk(UiElement node, List acc) + { + if (node is UiCheckboxBitfield64 block) acc.Add(block); + foreach (UiElement child in node.Children) Walk(child, acc); + } + } + + [Fact] + public void Bind_SeedsSlidersFromCurrentOpacity_NotTheDatDefault() + { + var fakeBindings = new FakeBindings { DefaultOpacity = 0.25f, ActiveOpacity = 0.75f }; + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + bool bound = ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + Assert.True(bound); + + var defaultRow = Assert.IsType(controller.ChatPage.Rows[0]); + var activeRow = Assert.IsType(controller.ChatPage.Rows[1]); + Assert.Equal(0.25f, defaultRow.Current); + Assert.Equal(0.75f, activeRow.Current); + // Never wrote back just from seeding. + Assert.Empty(fakeBindings.DefaultOpacitySets); + Assert.Empty(fakeBindings.ActiveOpacitySets); + } + + [Fact] + public void DraggingDefaultSlider_AppliesLive_AndDragsActiveUp_NeverClamping() + { + var fakeBindings = new FakeBindings { DefaultOpacity = 0.3f, ActiveOpacity = 0.3f }; + (OptionsPanelController controller, FakeBindings bindings, bool bound) = + BindRealWith(fakeBindings); + Assert.True(bound); + var defaultRow = Assert.IsType(controller.ChatPage.Rows[0]); + var activeRow = Assert.IsType(controller.ChatPage.Rows[1]); + + // Raise Default ABOVE the current Active value — retail's link drags + // Active UP to match, never clamps Default down. + defaultRow.SetCurrentValue(0.9f); + + Assert.Equal(0.9f, bindings.DefaultOpacity); + Assert.Equal(0.9f, bindings.ActiveOpacity); // dragged up + Assert.Equal(0.9f, defaultRow.Current); + Assert.Equal(0.9f, activeRow.Current); // the OTHER row's own value moved too + Assert.Single(bindings.DefaultOpacitySets); + // Live-apply: Changed reflects immediately (retail's Apply(1) shape). + Assert.True(controller.ChatPage.Changed); + } + + [Fact] + public void DraggingActiveSliderBelowDefault_DragsDefaultDown() + { + var fakeBindings = new FakeBindings { DefaultOpacity = 0.6f, ActiveOpacity = 0.6f }; + (OptionsPanelController controller, FakeBindings bindings, _) = BindRealWith(fakeBindings); + var defaultRow = Assert.IsType(controller.ChatPage.Rows[0]); + var activeRow = Assert.IsType(controller.ChatPage.Rows[1]); + + activeRow.SetCurrentValue(0.1f); + + Assert.Equal(0.1f, bindings.ActiveOpacity); + Assert.Equal(0.1f, bindings.DefaultOpacity); // dragged down + Assert.Equal(0.1f, defaultRow.Current); + Assert.Equal(0.1f, activeRow.Current); + } + + [Fact] + public void OnShown_ReSeedsBothSliders_FromLiveOpacity() + { + var fakeBindings = new FakeBindings { DefaultOpacity = 0.5f, ActiveOpacity = 1.0f }; + (OptionsPanelController controller, FakeBindings bindings, _) = BindRealWith(fakeBindings); + + bindings.DefaultOpacity = 0.2f; + bindings.ActiveOpacity = 0.9f; + + controller.ChatPage.OnShown(); + + var defaultRow = Assert.IsType(controller.ChatPage.Rows[0]); + var activeRow = Assert.IsType(controller.ChatPage.Rows[1]); + Assert.Equal(0.2f, defaultRow.Current); + Assert.Equal(0.2f, defaultRow.Saved); + Assert.Equal(0.9f, activeRow.Current); + Assert.Equal(0.9f, activeRow.Saved); + Assert.False(controller.ChatPage.Changed); + } + + [Fact] + public void Defaults_RestoresDatExtractedSliderDefaults() + { + var fakeBindings = new FakeBindings + { + DefaultOpacity = 0.1f, + ActiveOpacity = 0.1f, + DefaultOpacityDatDefault = 0.5f, + ActiveOpacityDatDefault = 1.0f, + }; + (OptionsPanelController controller, FakeBindings bindings, _) = BindRealWith(fakeBindings); + + controller.ChatPage.Defaults(); + + var defaultRow = Assert.IsType(controller.ChatPage.Rows[0]); + var activeRow = Assert.IsType(controller.ChatPage.Rows[1]); + Assert.Equal(0.5f, defaultRow.Current); + Assert.Equal(1.0f, activeRow.Current); + Assert.Equal(0.5f, bindings.DefaultOpacity); + Assert.Equal(1.0f, bindings.ActiveOpacity); + } + + [Fact] + public void CheckingAFilterRow_PublishesSetFilter_ForItsOwnCompactWindowId() + { + var fakeBindings = new FakeBindings(); + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + bool bound = ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + Assert.True(bound); + + var listBox = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ListBoxElementId)); + List blocks = CollectBlocks(listBox); + UiCheckboxBitfield64 floaty1Block = blocks[1]; // compact window id 1 + + UiCheckboxBitfield64.Row row = floaty1Block.Rows[0]; // Gameplay (floaty has it) + row.Toggle.OnClick!.Invoke(); + + var (windowId, value) = Assert.Single(fakeBindings.FilterSets); + Assert.Equal(1, windowId); + Assert.Equal(fakeBindings.Filters[1], value); + } + + [Fact] + public void MainWindowBlock_HasNoGameplayRow() + { + var fakeBindings = new FakeBindings(); + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + + var listBox = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ListBoxElementId)); + UiCheckboxBitfield64 mainBlock = CollectBlocks(listBox)[0]; + + Assert.All(mainBlock.Rows, r => Assert.NotEqual("Gameplay", r.Label)); + Assert.DoesNotContain(mainBlock.Rows, r => r.LowMask == 0x83912021ul); + } + + [Fact] + public void OnShown_ReSeedsFilterBlock_FromLiveChatWindowState() + { + var fakeBindings = new FakeBindings(); + (OptionsPanelController controller, FakeBindings bindings, _) = BindRealWith(fakeBindings); + + // Simulate the filter having changed behind the panel's back (e.g. a + // future second write path) between mount and the next tab-show. + bindings.Filters[ChatWindowState.MainWindowId] = 0x1ul; + + controller.ChatPage.OnShown(); + + var mainRow = Assert.IsType(controller.ChatPage.Rows[2]); // 2 sliders + main block + Assert.Equal(0x1ul, mainRow.Current); + Assert.Equal(0x1ul, mainRow.Saved); + Assert.False(controller.ChatPage.Changed); + } + + [Fact] + public void Reset_RevertsAFilterBlock_ToItsSavedBaseline() + { + var fakeBindings = new FakeBindings(); + (OptionsPanelController controller, FakeBindings bindings, _) = BindRealWith(fakeBindings); + var mainRow = Assert.IsType(controller.ChatPage.Rows[2]); + ulong initial = mainRow.Current; + bindings.FilterSets.Clear(); + + mainRow.SetCurrentValue(0x0ul); + Assert.True(controller.ChatPage.Changed); + + controller.ChatPage.Reset(); + + Assert.Equal(initial, mainRow.Current); + Assert.Contains(bindings.FilterSets, s => s.WindowId == ChatWindowState.MainWindowId && s.Value == initial); + } + + [Fact] + public void LabelResolutionFailure_LeavesRowsBuilt_NeverInventsEnglish() + { + (OptionsPanelController controller, _, bool bound) = BindReal(resolveString: (_, _) => null); + + Assert.True(bound); + Assert.Equal(7, controller.ChatPage.Rows.Count); + } + + [Fact] + public void Bind_MissingListBox_ReturnsFalse_AndDoesNotThrow() + { + var emptyRoot = new ElementInfo { Id = 0, Type = 3 }; + ImportedLayout emptyLayout = LayoutImporter.Build(emptyRoot, NoTex, null); + var page = new OptionPage(); + + bool bound = ChatOptionsPageController.Bind( + emptyLayout, page, MakeTemplateResolver(), (_, _) => null, + new FakeBindings().ToBindings()); + + Assert.False(bound); + Assert.Empty(page.Rows); + } + + [Fact] + public void ScrollbarLinkage_ModelPointsAtTheChatListBoxScroll() + { + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + var fakeBindings = new FakeBindings(); + ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + + var listBox = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ListBoxElementId)); + var scrollbar = Assert.IsType( + layout.FindElement(ChatOptionsPageController.ScrollbarElementId)); + + Assert.Same(listBox.Scroll, scrollbar.Model); + } + + private static (OptionsPanelController Panel, FakeBindings Bindings, bool Bound) BindRealWith( + FakeBindings fakeBindings) + { + ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost(); + OptionsPanelController controller = OptionsPanelController.Bind( + layout, + new OptionsPanelController.Callbacks( + Toggle: () => { }, + RequestExitToCharacterSelection: () => { }, + ExitGame: () => { }, + UseMouseTurningSettings: () => { }, + DisplaySystemMessage: _ => { }))!; + bool bound = ChatOptionsPageController.Bind( + layout, controller.ChatPage, MakeTemplateResolver(), (_, _) => null, + fakeBindings.ToBindings()); + return (controller, fakeBindings, bound); + } +} diff --git a/tests/AcDream.App.Tests/UI/Layout/OptionsPanelLayoutConformanceTests.cs b/tests/AcDream.App.Tests/UI/Layout/OptionsPanelLayoutConformanceTests.cs index f941786d..24b5b7e4 100644 --- a/tests/AcDream.App.Tests/UI/Layout/OptionsPanelLayoutConformanceTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/OptionsPanelLayoutConformanceTests.cs @@ -270,10 +270,11 @@ public class OptionsPanelLayoutConformanceTests /// containing ONLY the checkbox retail's own CreateChildren looks for /// () — proves the /// widget resolves and stamps that exact id rather than assuming row shape. - private static UiCheckboxBitfield64 NewBitfieldWithStubTemplate() + private static UiCheckboxBitfield64 NewBitfieldWithStubTemplate( + uint checkedLedSprite = 0u, uint uncheckedLedSprite = 0u) { var templates = new[] { new UiTemplateListEntry(0x2100002Bu, 0x10000521u) }; - return new UiCheckboxBitfield64(templates) + return new UiCheckboxBitfield64(templates, checkedLedSprite, uncheckedLedSprite) { Width = 272f, TemplateResolver = (layoutId, elementId) => @@ -359,6 +360,191 @@ public class OptionsPanelLayoutConformanceTests Assert.Empty(bitfield.Rows); } + // ── AP-195: LED media swap + self-sizing (Campaign OP slice OP5) ──────── + + private const uint CheckedLedDid = 0x06004D17u; + private const uint UncheckedLedDid = 0x06004D19u; + + [Fact] + public void AddChild_AllMaskBitsSet_AppliesCheckedLedSprite() + { + var bitfield = NewBitfieldWithStubTemplate(CheckedLedDid, UncheckedLedDid); + const ulong combatMask = 0x00000000_00600040ul; // two bits + bitfield.SetDefaultValue(combatMask, 0ul); // BOTH bits already set + + UiButton? row = bitfield.AddChild(combatMask, 0ul, "Combat"); + + Assert.NotNull(row); + Assert.True(row!.Selected); + Assert.Equal(CheckedLedDid, row.FaceFileOverride); + } + + [Fact] + public void AddChild_PartialMaskBitsSet_AppliesUncheckedLedSprite() + { + var bitfield = NewBitfieldWithStubTemplate(CheckedLedDid, UncheckedLedDid); + const ulong combatMask = 0x00000000_00600040ul; // two bits + const ulong onlyOneBitOfMask = 0x00000000_00000040ul; + bitfield.SetDefaultValue(onlyOneBitOfMask, 0ul); // checked (any-set) but not all-set + + UiButton? row = bitfield.AddChild(combatMask, 0ul, "Combat"); + + Assert.NotNull(row); + Assert.True(row!.Selected); // any-set half unaffected by AP-195 + Assert.Equal(UncheckedLedDid, row.FaceFileOverride); + } + + [Fact] + public void AddChild_NoMaskBitsSet_LeavesLedOverrideUnset() + { + var bitfield = NewBitfieldWithStubTemplate(CheckedLedDid, UncheckedLedDid); + bitfield.SetDefaultValue(0ul, 0ul); + + UiButton? row = bitfield.AddChild(0x00000000_00600040ul, 0ul, "Combat"); + + Assert.NotNull(row); + Assert.False(row!.Selected); + Assert.Null(row.FaceFileOverride); // retail's own gate: swap only applies while checked + } + + [Fact] + public void ToggleRow_TogglingOff_ClearsLedOverride() + { + var bitfield = NewBitfieldWithStubTemplate(CheckedLedDid, UncheckedLedDid); + const ulong singleBitMask = 0x00000000_00000040ul; + bitfield.SetDefaultValue(singleBitMask, 0ul); // fully set -> checked LED + + UiButton row = bitfield.AddChild(singleBitMask, 0ul, "Combat")!; + Assert.Equal(CheckedLedDid, row.FaceFileOverride); + + row.OnClick!.Invoke(); // turns OFF (was fully set) + + Assert.False(row.Selected); + Assert.Null(row.FaceFileOverride); + } + + [Fact] + public void AddChild_MissingLedSprites_LeavesOverrideNull_EvenWhileChecked() + { + // Constructed WITHOUT checkedLedSprite/uncheckedLedSprite (0/0 default) — + // e.g. an authoring gap or a fixture that predates AP-195's properties. + var bitfield = NewBitfieldWithStubTemplate(); + const ulong mask = 0x1ul; + bitfield.SetDefaultValue(mask, 0ul); + + UiButton? row = bitfield.AddChild(mask, 0ul, "Row"); + + Assert.NotNull(row); + Assert.True(row!.Selected); + Assert.Null(row.FaceFileOverride); + } + + [Fact] + public void AddChild_MultipleRows_SelfSizesHeightToStackedContent() + { + // AP-195's self-sizing tail: retail's ResizeTo(GetWidth(), CalculatePaperSize) + // — the block grows to fit N rows instead of keeping its authored 272x100 + // extent. Each stub row template is 14px tall (NewBitfieldWithStubTemplate). + var bitfield = NewBitfieldWithStubTemplate(); + bitfield.SetDefaultValue(0ul, 0ul); + Assert.Equal(0f, bitfield.Height); + + bitfield.AddChild(0x1ul, 0ul, "Row1"); + Assert.Equal(14f, bitfield.Height); + + bitfield.AddChild(0x2ul, 0ul, "Row2"); + bitfield.AddChild(0x4ul, 0ul, "Row3"); + + Assert.Equal(42f, bitfield.Height); // 3 * 14 + Assert.Equal(272f, bitfield.Width); // Width (GetWidth()) is untouched + } + + [Fact] + public void SetCurrentValue_PushesValueAndVisuals_WithoutFiringValueChanged() + { + var bitfield = NewBitfieldWithStubTemplate(CheckedLedDid, UncheckedLedDid); + const ulong mask = 0x1ul; + bitfield.SetDefaultValue(0ul, 0ul); // starts unset + UiButton row = bitfield.AddChild(mask, 0ul, "Row")!; + Assert.False(row.Selected); + + bool valueChangedFired = false; + bitfield.ValueChanged += (_, _) => valueChangedFired = true; + + bitfield.SetCurrentValue(mask, 0ul); // the OnShown/Reset/Defaults re-seed path + + Assert.Equal(mask, bitfield.CurrentLow); + Assert.True(row.Selected); + Assert.Equal(CheckedLedDid, row.FaceFileOverride); + Assert.False(valueChangedFired); // a re-seed must never loop back as "user edited this" + } + + // ── UiTemplateListBox.AddPrebuiltRow (AP-195's "reuse the ListBox's own + // stacking" disposition) ────────────────────────────────────────────── + + [Fact] + public void AddPrebuiltRow_StacksBelowPreviousContent_UsingTheRowsFinalHeight() + { + ElementInfo panelRoot = FixtureLoader.LoadOptionsPanelInfos(); + ElementInfo characterListBoxInfo = Find(FixtureLoader.LoadOptionsCharacterInfos(), 0x100001FAu)!; + var listBox = new UiTemplateListBox( + characterListBoxInfo, NoTex, + characterListBoxInfo.TemplateList, characterListBoxInfo.ScrollbarElementId) + { + TemplateResolver = (layoutId, elementId) => + { + if (layoutId != 0x2100002Bu) return null; + ElementInfo? templateInfo = Find(panelRoot, elementId); + return templateInfo is null ? null : LayoutImporter.Build(templateInfo, NoTex, null).Root; + }, + }; + + // A first ordinary row via the normal template path (header, 22px). + UiElement? header = listBox.AddItemFromTemplateList(0); + Assert.NotNull(header); + Assert.Equal(22, listBox.ContentHeight); + + // A pre-sized row built OUTSIDE the ListBox's own resolve step (the shape a + // self-sizing UiCheckboxBitfield64 block uses) — its height is whatever the + // caller already grew it to BEFORE calling AddPrebuiltRow. + var prebuilt = new UiPanel { Width = 272f, Height = 260f }; + + UiElement returned = listBox.AddPrebuiltRow(prebuilt); + + Assert.Same(prebuilt, returned); + Assert.Equal(22f, prebuilt.Top); // stacked directly below the header row + Assert.Equal(22 + 260, listBox.ContentHeight); // the FINAL 260px height, not a stale one + Assert.Contains(prebuilt, listBox.Children.OfType().Single().Children); + } + + [Fact] + public void AddPrebuiltRow_SubsequentTemplateRow_StacksAfterThePrebuiltRowsFinalHeight() + { + ElementInfo characterListBoxInfo = Find(FixtureLoader.LoadOptionsCharacterInfos(), 0x100001FAu)!; + var listBox = new UiTemplateListBox( + characterListBoxInfo, NoTex, + characterListBoxInfo.TemplateList, characterListBoxInfo.ScrollbarElementId) + { + TemplateResolver = (layoutId, elementId) => + { + if (layoutId != 0x2100002Bu) return null; + ElementInfo? templateInfo = Find(FixtureLoader.LoadOptionsPanelInfos(), elementId); + return templateInfo is null ? null : LayoutImporter.Build(templateInfo, NoTex, null).Root; + }, + }; + + var prebuilt = new UiPanel { Width = 272f, Height = 260f }; + listBox.AddPrebuiltRow(prebuilt); + Assert.Equal(260, listBox.ContentHeight); + + // A separator row added AFTER the prebuilt block must stack below its FINAL + // 260px height, not the block's pre-build authored 100px extent — this is + // exactly the reflow problem AddPrebuiltRow exists to avoid. + UiElement? separator = listBox.AddItemFromTemplateList(1); // separator template + Assert.NotNull(separator); + Assert.Equal(260f, separator!.Top); + } + // ── The template mechanism, exercised end-to-end ──────────────────────── /// diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/character_2100002E.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/character_2100002E.json index c283b741..1f80c8f9 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/character_2100002E.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/character_2100002E.json @@ -1037,6 +1037,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1118,6 +1120,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1199,11 +1203,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1765,6 +1773,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1846,6 +1856,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1927,11 +1939,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2043,6 +2059,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2604,6 +2622,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2685,6 +2705,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2766,11 +2788,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3262,6 +3288,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3791,6 +3819,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3843,6 +3873,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4227,6 +4259,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4490,6 +4524,8 @@ "TemplateElementId": 268436790 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268436787 }, { @@ -4915,6 +4951,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5014,6 +5052,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5113,11 +5153,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5334,6 +5378,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5550,11 +5596,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5607,6 +5657,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6252,6 +6304,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6369,6 +6423,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6486,16 +6542,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7227,6 +7289,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7582,6 +7646,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8024,6 +8090,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8379,6 +8447,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8734,6 +8804,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8815,6 +8887,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9003,11 +9077,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9089,11 +9167,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9489,6 +9571,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9844,6 +9928,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10344,6 +10430,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10873,6 +10961,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11228,6 +11318,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11612,6 +11704,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11967,6 +12061,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12155,6 +12251,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12539,6 +12637,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12894,11 +12994,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12951,6 +13055,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13335,6 +13441,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13719,11 +13827,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13776,6 +13888,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14370,6 +14484,8 @@ "TemplateElementId": 268436044 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268436030 }, { @@ -14795,6 +14911,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14894,6 +15012,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14993,11 +15113,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15214,6 +15338,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15430,11 +15556,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15487,6 +15617,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16005,6 +16137,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16360,6 +16494,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16802,6 +16938,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17157,6 +17295,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17512,11 +17652,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18035,6 +18179,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18390,6 +18536,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18832,6 +18980,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19187,6 +19337,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19542,6 +19694,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19623,6 +19777,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19704,21 +19860,29 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20450,6 +20614,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20805,6 +20971,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21247,6 +21415,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21602,6 +21772,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21957,6 +22129,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22038,6 +22212,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22226,11 +22402,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22312,11 +22492,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22712,6 +22896,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23067,6 +23253,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23567,6 +23755,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24096,6 +24286,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24451,6 +24643,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24835,6 +25029,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -25190,6 +25386,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -25378,6 +25576,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -25762,6 +25962,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26117,11 +26319,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26174,6 +26380,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26558,6 +26766,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26942,11 +27152,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26999,6 +27213,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -27593,6 +27809,8 @@ "TemplateElementId": 268436044 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268436030 }, { @@ -28018,6 +28236,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28117,6 +28337,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28216,11 +28438,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28437,6 +28663,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28653,11 +28881,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28710,6 +28942,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -29228,6 +29462,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -29583,6 +29819,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -30025,6 +30263,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -30380,6 +30620,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -30735,11 +30977,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -31258,6 +31504,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -31613,6 +31861,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -32055,6 +32305,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -32410,6 +32662,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -32765,6 +33019,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -32846,6 +33102,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -32927,21 +33185,29 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], @@ -32963,5 +33229,7 @@ } ], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/character_info_2100006E_10000183.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/character_info_2100006E_10000183.json index 2cea163a..c9a99105 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/character_info_2100006E_10000183.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/character_info_2100006E_10000183.json @@ -585,6 +585,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435742 }, { @@ -1010,6 +1012,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1109,6 +1113,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1208,11 +1214,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1429,6 +1439,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1645,16 +1657,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1766,6 +1784,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2158,10 +2178,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_2100006f.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_2100006f.json index 91bbfe71..6cb6a756 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_2100006f.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_2100006f.json @@ -623,6 +623,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -705,6 +707,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1124,6 +1128,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1206,6 +1212,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1288,6 +1296,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1380,6 +1390,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1462,6 +1474,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1544,6 +1558,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1626,6 +1642,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1708,6 +1726,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1831,6 +1851,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1895,6 +1917,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2018,6 +2042,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2112,6 +2138,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3311,11 +3339,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435474 }, { @@ -3741,6 +3773,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3840,6 +3874,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3939,11 +3975,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4160,6 +4200,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4376,16 +4418,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4509,6 +4557,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4603,6 +4653,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4726,6 +4778,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5469,11 +5523,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6525,6 +6583,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6588,11 +6648,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6961,11 +7025,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7060,6 +7128,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7479,6 +7549,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7898,6 +7970,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7980,10 +8054,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_floaty_2100005b.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_floaty_2100005b.json index 804821ed..3246d81d 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_floaty_2100005b.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/chat_floaty_2100005b.json @@ -205,6 +205,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -261,6 +263,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -342,6 +346,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -465,6 +471,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -588,6 +596,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -682,6 +692,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -776,6 +788,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1975,16 +1989,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435474 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2079,6 +2099,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2202,6 +2224,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2296,6 +2320,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2750,6 +2776,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2849,6 +2877,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2948,11 +2978,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3169,6 +3203,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3385,11 +3421,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3513,6 +3553,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3876,6 +3918,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5125,6 +5169,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5177,6 +5223,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5229,6 +5277,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5281,16 +5331,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5615,15 +5671,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/combat_21000073.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/combat_21000073.json index b3f9c2ea..b8adb063 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/combat_21000073.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/combat_21000073.json @@ -446,6 +446,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -490,6 +492,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -572,11 +576,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1309,6 +1317,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1426,6 +1436,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1543,11 +1555,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2097,11 +2113,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2154,6 +2174,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3689,6 +3711,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4217,6 +4241,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4745,6 +4771,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5273,6 +5301,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5744,6 +5774,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5960,11 +5992,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6532,11 +6568,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7008,6 +7048,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7224,11 +7266,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7796,11 +7842,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8272,6 +8322,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8488,11 +8540,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9060,11 +9116,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9536,6 +9596,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9752,11 +9814,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10324,11 +10390,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10800,6 +10870,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11016,11 +11088,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11588,11 +11664,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12064,6 +12144,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12280,11 +12362,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12852,11 +12938,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13328,6 +13418,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13544,11 +13636,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14116,11 +14212,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14649,6 +14749,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15177,6 +15279,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15648,6 +15752,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15864,11 +15970,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16436,11 +16546,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16969,6 +17083,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17497,6 +17613,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], @@ -17543,11 +17661,15 @@ } ], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17630,6 +17752,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17712,6 +17836,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17835,6 +17961,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17899,6 +18027,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17993,6 +18123,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18087,6 +18219,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18210,6 +18344,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18304,6 +18440,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18427,6 +18565,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18521,6 +18661,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18603,6 +18745,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18747,6 +18891,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18799,6 +18945,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18851,6 +18999,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19126,6 +19276,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19178,6 +19330,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19358,6 +19512,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19410,16 +19566,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19804,6 +19966,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20299,6 +20463,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20885,11 +21051,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21476,11 +21646,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22096,11 +22270,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22851,6 +23029,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23502,6 +23682,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24153,16 +24335,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24245,6 +24433,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24327,6 +24517,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24409,6 +24601,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24491,6 +24685,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24573,10 +24769,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/component_category_21000033_10000466.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/component_category_21000033_10000466.json index 42b40b5e..365d55cc 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/component_category_21000033_10000466.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/component_category_21000033_10000466.json @@ -359,5 +359,7 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/component_row_21000033_10000467.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/component_row_21000033_10000467.json index df440263..882f1a68 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/component_row_21000033_10000467.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/component_row_21000033_10000467.json @@ -106,6 +106,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -461,6 +463,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -845,6 +849,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1411,10 +1417,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/dialogs_2100003C.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/dialogs_2100003C.json index 0baca67b..34fca5ef 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/dialogs_2100003C.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/dialogs_2100003C.json @@ -341,6 +341,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -393,6 +395,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -445,6 +449,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -497,6 +503,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -549,6 +557,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -601,6 +611,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -653,6 +665,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -705,6 +719,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1390,6 +1406,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1507,6 +1525,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1624,11 +1644,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2274,6 +2298,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2391,6 +2417,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2508,16 +2536,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3409,15 +3443,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_negative_2100001B.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_negative_2100001B.json index a7dfd054..6ba55066 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_negative_2100001B.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_negative_2100001B.json @@ -516,6 +516,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -713,6 +715,8 @@ "TemplateElementId": 268435752 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435748 }, { @@ -1138,6 +1142,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1237,6 +1243,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1336,11 +1344,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1557,6 +1569,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1773,11 +1787,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2210,6 +2228,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435751 }, { @@ -2635,6 +2655,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2734,6 +2756,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2833,11 +2857,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3054,6 +3082,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3270,16 +3300,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3391,10 +3427,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_positive_2100001B.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_positive_2100001B.json index a40132b2..b8991c8c 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_positive_2100001B.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_positive_2100001B.json @@ -545,6 +545,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -742,6 +744,8 @@ "TemplateElementId": 268435752 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435748 }, { @@ -1167,6 +1171,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1266,6 +1272,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1365,11 +1373,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1586,6 +1598,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1802,11 +1816,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2239,6 +2257,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435751 }, { @@ -2664,6 +2684,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2763,6 +2785,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2862,11 +2886,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3083,6 +3111,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3299,16 +3329,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3420,10 +3456,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_row_2100001B_10000128.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_row_2100001B_10000128.json index 66ca36b3..2b8cfe9b 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_row_2100001B_10000128.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/effects_row_2100001B_10000128.json @@ -117,6 +117,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -617,6 +619,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -972,10 +976,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_2100006B_100005F2.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_2100006B_100005F2.json index f0ca9801..78815bb3 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_2100006B_100005F2.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_2100006B_100005F2.json @@ -234,6 +234,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -357,6 +359,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -451,6 +455,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -574,6 +580,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -668,6 +676,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -720,6 +730,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -843,6 +855,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -887,6 +901,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -981,6 +997,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1376,6 +1394,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1428,6 +1448,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1783,6 +1805,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2067,6 +2091,8 @@ "TemplateElementId": 268436270 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2422,6 +2448,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2777,6 +2805,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3634,6 +3664,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435812 }, { @@ -4059,6 +4091,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4158,6 +4192,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4257,11 +4293,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4478,6 +4518,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4694,11 +4736,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5083,6 +5129,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5165,6 +5213,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5217,6 +5267,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5269,6 +5321,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5321,11 +5375,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5408,6 +5466,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5490,6 +5550,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5572,6 +5634,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5686,6 +5750,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5854,6 +5920,8 @@ "TemplateElementId": 268435814 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6238,6 +6306,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6622,6 +6692,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6977,6 +7049,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7372,11 +7446,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7772,6 +7850,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8127,6 +8207,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8482,11 +8564,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8713,6 +8799,8 @@ "TemplateElementId": 268436279 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8765,6 +8853,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8847,6 +8937,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8899,6 +8991,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9399,6 +9493,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9451,11 +9547,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9538,6 +9638,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9620,6 +9722,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9702,6 +9806,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9784,6 +9890,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10321,6 +10429,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10403,6 +10513,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10906,6 +11018,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11005,6 +11119,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11104,11 +11220,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11325,6 +11445,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11541,11 +11663,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11628,6 +11754,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11680,6 +11808,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11732,6 +11862,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12647,6 +12779,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435773 }, { @@ -13101,6 +13235,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13200,6 +13336,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13299,11 +13437,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13520,6 +13662,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13736,11 +13880,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14813,6 +14961,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268436590 }, { @@ -15786,11 +15936,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15914,6 +16068,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16008,10 +16164,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_component_2100006B_1000032E.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_component_2100006B_1000032E.json index 7349f117..97c538ca 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_component_2100006B_1000032E.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_component_2100006B_1000032E.json @@ -118,10 +118,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_row_2100006B_10000166.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_row_2100006B_10000166.json index e70f2416..736d2492 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_row_2100006B_10000166.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/examine_row_2100006B_10000166.json @@ -486,6 +486,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1015,10 +1017,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/indicators_21000071.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/indicators_21000071.json index 3a139ae7..8b300dd7 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/indicators_21000071.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/indicators_21000071.json @@ -247,6 +247,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -311,6 +313,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -405,6 +409,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -499,6 +505,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -687,11 +695,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -880,11 +892,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1102,11 +1118,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1324,11 +1344,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1411,6 +1435,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1621,11 +1647,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1708,6 +1738,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1936,11 +1968,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2023,6 +2059,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2105,6 +2143,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2186,6 +2226,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2268,6 +2310,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2350,6 +2394,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2432,6 +2478,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2514,6 +2562,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2608,6 +2658,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2672,6 +2724,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2766,6 +2820,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2860,10 +2916,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/inventory_21000023.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/inventory_21000023.json index fd915593..867f11b6 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/inventory_21000023.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/inventory_21000023.json @@ -147,6 +147,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -265,6 +267,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -802,6 +806,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1264,6 +1270,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1686,6 +1694,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2108,6 +2118,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2530,6 +2542,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2952,6 +2966,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3374,6 +3390,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3448,6 +3466,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3870,6 +3890,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4337,11 +4359,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4764,6 +4790,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5186,6 +5214,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5608,6 +5638,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6030,6 +6062,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6452,6 +6486,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6874,6 +6910,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7296,6 +7334,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7718,6 +7758,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8140,6 +8182,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8562,6 +8606,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9206,11 +9252,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9633,6 +9683,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10055,6 +10107,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10477,6 +10531,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10899,6 +10955,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11321,6 +11379,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11743,6 +11803,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12165,11 +12227,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12594,6 +12660,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12949,6 +13017,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13137,11 +13207,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13564,6 +13638,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14015,6 +14091,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435915 }, { @@ -14440,6 +14518,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14539,6 +14619,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14638,11 +14720,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14859,6 +14945,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15075,16 +15163,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15625,6 +15719,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16192,6 +16288,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435911 }, { @@ -16646,6 +16744,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16745,6 +16845,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16844,11 +16946,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17065,6 +17171,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17281,16 +17389,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17343,10 +17457,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json index 6ea4a87a..12bfb344 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json @@ -527,11 +527,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -643,6 +647,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1075,15 +1081,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json index ecca6c83..c68a507c 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json @@ -235,6 +235,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -667,11 +669,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1346,6 +1352,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1972,6 +1980,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2598,6 +2608,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2690,6 +2702,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2742,6 +2756,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2794,6 +2810,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2846,6 +2864,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3458,20 +3478,28 @@ "TemplateElementId": 268435832 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_2100002B.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_2100002B.json index c9c2d247..a7c5ea8f 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_2100002B.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_2100002B.json @@ -308,6 +308,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -360,6 +362,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -986,16 +990,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1262,6 +1272,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1624,16 +1636,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1900,6 +1918,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2262,11 +2282,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2493,6 +2517,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2719,11 +2745,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3373,6 +3403,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3934,6 +3966,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4015,6 +4049,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4096,11 +4132,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4662,6 +4702,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4743,6 +4785,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4824,11 +4868,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5390,6 +5438,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5471,6 +5521,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5552,11 +5604,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5668,6 +5724,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6156,6 +6214,8 @@ "TemplateElementId": 268435992 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435963 }, { @@ -6581,6 +6641,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6680,6 +6742,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6779,11 +6843,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7000,6 +7068,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7216,11 +7286,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7866,6 +7940,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7983,6 +8059,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8100,11 +8178,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8750,6 +8832,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8867,6 +8951,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8984,11 +9070,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9634,6 +9724,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9751,6 +9843,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9868,16 +9962,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10630,6 +10730,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10747,6 +10849,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10864,11 +10968,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11543,6 +11651,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11660,6 +11770,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11777,11 +11889,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12456,6 +12572,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12573,6 +12691,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12690,11 +12810,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13340,6 +13464,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13457,6 +13583,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13574,11 +13702,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14224,6 +14356,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14341,6 +14475,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14458,11 +14594,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15108,6 +15248,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15225,6 +15367,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15342,11 +15486,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15992,6 +16140,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16109,6 +16259,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16226,16 +16378,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17184,6 +17342,8 @@ "TemplateElementId": 268436001 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435969 }, { @@ -17609,6 +17769,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17708,6 +17870,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17807,11 +17971,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18028,6 +18196,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18244,11 +18414,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18894,6 +19068,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19011,6 +19187,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19128,11 +19306,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19778,6 +19960,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19895,6 +20079,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20012,11 +20198,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20662,6 +20852,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20779,6 +20971,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20896,16 +21090,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21467,6 +21667,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21548,6 +21750,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21629,11 +21833,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22674,6 +22882,8 @@ "TemplateElementId": 268436768 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435969 }, { @@ -23099,6 +23309,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23198,6 +23410,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23297,11 +23511,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23518,6 +23736,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23734,11 +23954,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24384,6 +24608,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24501,6 +24727,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -24618,11 +24846,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -25268,6 +25500,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -25385,6 +25619,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -25502,11 +25738,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26152,6 +26392,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26269,6 +26511,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -26386,16 +26630,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], @@ -26422,6 +26672,8 @@ } ], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -27048,11 +27300,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -27415,16 +27671,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28051,11 +28313,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28418,11 +28684,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28649,6 +28919,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -28875,11 +29147,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -29146,6 +29422,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -29788,6 +30066,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -29905,16 +30185,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -30141,6 +30427,8 @@ "TemplateElementId": 268436769 } ], + "LedCheckedSprite": 100683031, + "LedUncheckedSprite": 100683033, "ScrollbarElementId": 0 }, { @@ -30767,20 +31055,28 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_character_21000028.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_character_21000028.json index 50cd9a00..b7ba24ff 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_character_21000028.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_character_21000028.json @@ -476,6 +476,8 @@ "TemplateElementId": 268435992 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435963 }, { @@ -901,6 +903,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1000,6 +1004,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1099,11 +1105,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1320,6 +1330,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1536,11 +1548,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2186,6 +2202,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2303,6 +2321,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2420,11 +2440,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3070,6 +3094,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3187,6 +3213,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3304,11 +3332,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3954,6 +3986,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4071,6 +4105,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4188,15 +4224,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_chat_2100005C.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_chat_2100005C.json index 2d24260d..4836a9c4 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_chat_2100005C.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_chat_2100005C.json @@ -1028,6 +1028,8 @@ "TemplateElementId": 268436768 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435969 }, { @@ -1453,6 +1455,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1552,6 +1556,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1651,11 +1657,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1872,6 +1882,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2088,11 +2100,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2738,6 +2754,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2855,6 +2873,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2972,11 +2992,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3622,6 +3646,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3739,6 +3765,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3856,11 +3884,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4506,6 +4538,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4623,6 +4657,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4740,15 +4776,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_config_21000029.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_config_21000029.json index af504849..7560e8fb 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_config_21000029.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_config_21000029.json @@ -936,6 +936,8 @@ "TemplateElementId": 268436001 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435969 }, { @@ -1361,6 +1363,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1460,6 +1464,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1559,11 +1565,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1780,6 +1790,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1996,11 +2008,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2646,6 +2662,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2763,6 +2781,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2880,11 +2900,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3530,6 +3554,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3647,6 +3673,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3764,11 +3792,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4414,6 +4446,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4531,6 +4565,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4648,15 +4684,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_gameplay_2100002A.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_gameplay_2100002A.json index d4549fdd..ad3b7c58 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_gameplay_2100002A.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_gameplay_2100002A.json @@ -740,6 +740,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -857,6 +859,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -974,11 +978,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1653,6 +1661,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1770,6 +1780,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1887,11 +1899,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2566,6 +2582,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2683,6 +2701,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2800,11 +2820,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3450,6 +3474,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3567,6 +3593,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3684,11 +3712,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4334,6 +4366,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4451,6 +4485,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4568,11 +4604,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5218,6 +5258,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5335,6 +5377,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5452,11 +5496,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6102,6 +6150,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6219,6 +6269,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6336,15 +6388,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_panel_2100006E_1000018D.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_panel_2100006E_1000018D.json index 83f15364..8a227423 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/options_panel_2100006E_1000018D.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/options_panel_2100006E_1000018D.json @@ -674,6 +674,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1235,6 +1237,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1316,6 +1320,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1397,11 +1403,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1963,6 +1973,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2044,6 +2056,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2125,11 +2139,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2691,6 +2709,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2772,6 +2792,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2853,11 +2875,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2969,6 +2995,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3457,6 +3485,8 @@ "TemplateElementId": 268435992 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435963 }, { @@ -3882,6 +3912,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3981,6 +4013,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4080,11 +4114,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4301,6 +4339,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4517,11 +4557,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5167,6 +5211,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5284,6 +5330,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5401,11 +5449,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6051,6 +6103,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6168,6 +6222,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6285,11 +6341,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6935,6 +6995,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7052,6 +7114,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7169,16 +7233,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7931,6 +8001,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8048,6 +8120,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8165,11 +8239,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8844,6 +8922,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8961,6 +9041,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9078,11 +9160,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9757,6 +9843,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9874,6 +9962,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9991,11 +10081,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10641,6 +10735,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10758,6 +10854,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10875,11 +10973,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11525,6 +11627,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11642,6 +11746,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11759,11 +11865,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12409,6 +12519,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12526,6 +12638,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12643,11 +12757,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13293,6 +13411,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13410,6 +13530,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13527,16 +13649,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14485,6 +14613,8 @@ "TemplateElementId": 268436001 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435969 }, { @@ -14910,6 +15040,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15009,6 +15141,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15108,11 +15242,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15329,6 +15467,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15545,11 +15685,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16195,6 +16339,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16312,6 +16458,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16429,11 +16577,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17079,6 +17231,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17196,6 +17350,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17313,11 +17469,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -17963,6 +18123,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18080,6 +18242,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18197,16 +18361,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18768,6 +18938,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18849,6 +19021,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -18930,11 +19104,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -19975,6 +20153,8 @@ "TemplateElementId": 268436768 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268435969 }, { @@ -20400,6 +20580,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20499,6 +20681,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20598,11 +20782,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -20819,6 +21007,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21035,11 +21225,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21685,6 +21879,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21802,6 +21998,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -21919,11 +22117,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22569,6 +22771,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22686,6 +22890,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -22803,11 +23009,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23453,6 +23663,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23570,6 +23782,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -23687,16 +23901,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], @@ -23723,5 +23943,7 @@ } ], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/paperdoll_21000024.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/paperdoll_21000024.json index 062b438d..60049365 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/paperdoll_21000024.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/paperdoll_21000024.json @@ -458,6 +458,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -880,6 +882,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1302,6 +1306,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1724,6 +1730,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2146,6 +2154,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2568,6 +2578,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2642,6 +2654,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3064,6 +3078,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3531,11 +3547,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3958,6 +3978,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4380,6 +4402,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4802,6 +4826,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5224,6 +5250,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5646,6 +5674,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6068,6 +6098,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6490,6 +6522,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6912,6 +6946,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7334,6 +7370,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7756,6 +7794,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8400,11 +8440,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8827,6 +8871,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9249,6 +9295,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9671,6 +9719,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10093,6 +10143,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10515,6 +10567,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10937,6 +10991,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11359,10 +11415,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/powerbar_21000072.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/powerbar_21000072.json index 324ff0bc..787bcb2b 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/powerbar_21000072.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/powerbar_21000072.json @@ -1041,6 +1041,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1158,6 +1160,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1210,11 +1214,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1297,6 +1305,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1379,6 +1389,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1461,6 +1473,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1543,6 +1557,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1625,6 +1641,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1707,6 +1725,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1789,6 +1809,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1871,6 +1893,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1965,6 +1989,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2029,6 +2055,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2123,6 +2151,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2217,6 +2247,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2311,6 +2343,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2375,6 +2409,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2469,6 +2505,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2563,10 +2601,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/radar_21000074.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/radar_21000074.json index e41c835e..5fe52392 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/radar_21000074.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/radar_21000074.json @@ -829,6 +829,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -881,6 +883,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -933,6 +937,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -985,6 +991,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1037,6 +1045,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1101,6 +1111,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1193,6 +1205,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1245,10 +1259,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/smartbox_fps_2100000F.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/smartbox_fps_2100000F.json index 08549eda..0b8f9264 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/smartbox_fps_2100000F.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/smartbox_fps_2100000F.json @@ -396,5 +396,7 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/spellbook_21000034.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/spellbook_21000034.json index a4375bab..ad7467d5 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/spellbook_21000034.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/spellbook_21000034.json @@ -920,6 +920,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1001,6 +1003,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1082,11 +1086,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1648,6 +1656,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1729,6 +1739,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1810,11 +1822,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1926,6 +1942,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2484,6 +2502,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268436118 }, { @@ -2909,6 +2929,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3008,6 +3030,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3107,11 +3131,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3328,6 +3356,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3544,11 +3574,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4125,11 +4159,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4658,11 +4696,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5191,11 +5233,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5580,6 +5626,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5964,6 +6012,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6425,11 +6475,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6958,11 +7012,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7491,11 +7549,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8024,11 +8086,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8557,11 +8623,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9090,11 +9160,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9623,11 +9697,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10156,11 +10234,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10689,11 +10771,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11222,11 +11308,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11755,21 +11845,29 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12311,6 +12409,8 @@ "TemplateElementId": 268436583 } ], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 268436581 }, { @@ -12736,6 +12836,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12835,6 +12937,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12934,11 +13038,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13155,6 +13263,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13371,16 +13481,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], @@ -13397,5 +13513,7 @@ } ], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/toolbar_21000016.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/toolbar_21000016.json index b76cb7cd..aa91c0a9 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/toolbar_21000016.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/toolbar_21000016.json @@ -117,6 +117,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -169,6 +171,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -591,6 +595,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1013,6 +1019,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1435,6 +1443,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1516,6 +1526,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1938,6 +1950,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2049,6 +2063,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2471,6 +2487,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2651,6 +2669,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3030,6 +3050,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3452,6 +3474,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3563,6 +3587,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3985,6 +4011,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4037,6 +4065,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4089,6 +4119,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4511,6 +4543,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4691,6 +4725,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5113,6 +5149,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5535,6 +5573,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5715,6 +5755,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6137,6 +6179,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6189,6 +6233,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6369,6 +6415,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6834,11 +6882,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7261,6 +7313,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7441,6 +7495,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7863,6 +7919,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8043,6 +8101,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8465,6 +8525,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8517,6 +8579,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8939,6 +9003,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9038,6 +9104,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9460,6 +9528,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10078,6 +10148,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10229,6 +10301,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10281,11 +10355,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11042,6 +11120,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11123,6 +11203,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11340,11 +11422,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11562,16 +11648,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11994,6 +12086,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12416,6 +12510,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12468,10 +12564,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/vendor_21000012_100000B7.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/vendor_21000012_100000B7.json index 65230c73..305f51a2 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/vendor_21000012_100000B7.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/vendor_21000012_100000B7.json @@ -117,6 +117,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1098,6 +1100,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1552,6 +1556,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1651,6 +1657,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1750,11 +1758,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1971,6 +1983,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2187,11 +2201,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2547,6 +2565,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2902,6 +2922,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3528,6 +3550,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4154,6 +4178,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4780,6 +4806,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -5406,11 +5434,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6174,6 +6206,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -6800,6 +6834,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7251,6 +7287,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7705,6 +7743,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7804,6 +7844,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -7903,11 +7945,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8124,6 +8170,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8340,11 +8388,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -8700,6 +8752,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9055,6 +9109,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -9681,6 +9737,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10307,11 +10365,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -10782,6 +10844,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11252,6 +11316,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -11722,6 +11788,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12281,6 +12349,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12735,6 +12805,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12834,6 +12906,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -12933,11 +13007,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13154,6 +13232,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -13370,11 +13450,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14048,6 +14132,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14165,11 +14251,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14525,6 +14615,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -14880,6 +14972,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -15506,6 +15600,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16132,11 +16228,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], @@ -16158,6 +16258,8 @@ } ], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -16210,10 +16312,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json index 284e776c..b59a5402 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json @@ -527,11 +527,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -643,6 +647,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1075,15 +1081,21 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/vitals_2100006C.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/vitals_2100006C.json index edadf770..41c85bc1 100644 --- a/tests/AcDream.App.Tests/UI/Layout/fixtures/vitals_2100006C.json +++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/vitals_2100006C.json @@ -257,6 +257,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -756,6 +758,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -892,6 +896,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -944,6 +950,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -996,6 +1004,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1048,11 +1058,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1189,6 +1203,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1241,6 +1257,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1293,6 +1311,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1345,16 +1365,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1437,6 +1463,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1519,6 +1547,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1717,6 +1747,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1769,6 +1801,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1821,6 +1855,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -1873,11 +1909,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2315,6 +2355,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2451,6 +2493,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2503,6 +2547,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2555,6 +2601,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2607,16 +2655,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2711,6 +2765,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2775,6 +2831,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2869,6 +2927,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -2963,6 +3023,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3057,6 +3119,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3121,6 +3185,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3215,6 +3281,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3309,6 +3377,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3808,6 +3878,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3944,6 +4016,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -3996,6 +4070,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4048,6 +4124,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4100,11 +4178,15 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4241,6 +4323,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4293,6 +4377,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4345,6 +4431,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4397,16 +4485,22 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4489,6 +4583,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4571,6 +4667,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4653,6 +4751,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4735,6 +4835,8 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 }, { @@ -4817,10 +4919,14 @@ "Children": [], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } ], "TabTable": [], "TemplateList": [], + "LedCheckedSprite": 0, + "LedUncheckedSprite": 0, "ScrollbarElementId": 0 } \ No newline at end of file diff --git a/tests/AcDream.Core.Tests/Chat/ChatWindowStateTests.cs b/tests/AcDream.Core.Tests/Chat/ChatWindowStateTests.cs index f84d738f..80f2ce2b 100644 --- a/tests/AcDream.Core.Tests/Chat/ChatWindowStateTests.cs +++ b/tests/AcDream.Core.Tests/Chat/ChatWindowStateTests.cs @@ -25,6 +25,19 @@ public sealed class ChatWindowStateTests Assert.Equal(expectedFilter, state.GetFilter(windowId)); } + [Fact] + public void NamedDefaultConstants_MatchTheSameTable() + { + // Campaign OP slice OP5: the named constants ResetToDefaults() itself now + // uses — and that the Chat tab's Defaults button reuses directly — must + // never drift from this table. + Assert.Equal(0xFBFFFFFFu, ChatWindowState.MainWindowDefaultFilter); + Assert.Equal(0x0000101Cu, ChatWindowState.Floaty1DefaultFilter); + Assert.Equal(0x00040C00u, ChatWindowState.Floaty2DefaultFilter); + Assert.Equal(0x00080000u, ChatWindowState.Floaty3DefaultFilter); + Assert.Equal(0x78000000u, ChatWindowState.Floaty4DefaultFilter); + } + [Fact] public void Defaults_MainWindowIsAlwaysOpen_FloatingWindowsStartClosed() { diff --git a/tests/AcDream.UI.Abstractions.Tests/Panels/Settings/SettingsStoreTests.cs b/tests/AcDream.UI.Abstractions.Tests/Panels/Settings/SettingsStoreTests.cs index c88989af..190bf6db 100644 --- a/tests/AcDream.UI.Abstractions.Tests/Panels/Settings/SettingsStoreTests.cs +++ b/tests/AcDream.UI.Abstractions.Tests/Panels/Settings/SettingsStoreTests.cs @@ -329,6 +329,29 @@ public sealed class SettingsStoreTests : System.IDisposable Assert.Equal(original.ChatWindow4Filter, loaded.ChatWindow4Filter); } + // -- Campaign OP slice OP5: main chat window filter round-trip --------- + + [Fact] + public void LoadChat_returns_retail_main_window_filter_default_when_file_is_missing() + { + var store = new SettingsStore(_tempPath); + ChatSettings loaded = store.LoadChat(); + + Assert.Equal(0xFBFFFFFFu, loaded.ChatWindowMainFilter); + } + + [Fact] + public void SaveChat_then_LoadChat_round_trips_main_window_filter() + { + var store = new SettingsStore(_tempPath); + var original = ChatSettings.Default with { ChatWindowMainFilter = 0x1ul }; + + store.SaveChat(original); + ChatSettings loaded = store.LoadChat(); + + Assert.Equal(original.ChatWindowMainFilter, loaded.ChatWindowMainFilter); + } + // -- Campaign CH slice CH6c: window opacity round-trip ----------------- [Fact]