Skip to content

Loom: world atlas overlay (Ctrl+M zone picker)#275

Closed
CoreyRDean wants to merge 3 commits into
coreyrdean/loom-xref-navfrom
coreyrdean/loom-world-atlas
Closed

Loom: world atlas overlay (Ctrl+M zone picker)#275
CoreyRDean wants to merge 3 commits into
coreyrdean/loom-xref-navfrom
coreyrdean/loom-world-atlas

Conversation

@CoreyRDean
Copy link
Copy Markdown
Collaborator

Summary

Fourth and final of the Loom-concept PRs. Stacked on top of #274 (on #273 on #271).

Adds a modal overlay invoked by Ctrl+M from any tab that shows every zone in the project as a compact one-line summary:

 *  Hollow's Edge                    3 portals     7 spawns    2 triggers
    Ravensreach                     12 portals    24 spawns    6 triggers
    Sunken Keep                      4 portals    18 spawns   11 triggers
    Misty Plateau                    2 portals    11 spawns    3 triggers

The * pip marks the currently-loaded zone. Clicking a row loads that zone (closing the overlay); Esc also closes.

Why a separate overlay from the command palette

The palette is free-text find-anywhere across every entity type. The atlas is a spatial overview for zones with per-zone metadata at a glance — portal/spawn/trigger counts you can compare side-by-side instead of one at a time. Different jobs, different shapes, different hotkeys.

How it reuses earlier PRs

Clicking a row calls GUE_JumpToEntity(""zone"", Handle(Ar)) — the same primitive PR #271 introduced and PRs #273 / #274 extended. The atlas adds zero new dispatch infrastructure.

Always-current counts

The per-zone counts walk the Area type's existing fixed-size arrays (PortalName$[99], SpawnActor[999], TriggerScript$[149]) on each open. So if you add a portal in the Zones tab and immediately Ctrl+M, the new portal is reflected. No caching, no staleness.

Blast radius

  • New self-contained module src/Modules/WorldAtlas.bb
  • src/GUE.bb gets four hook lines (include, init, per-frame poll, per-event dispatch)
  • Server / Client / Project Manager unaffected
  • All four engine targets compile clean

Future room to grow (out of scope)

  • 2D xy world map layout with portal lines between connected zones
  • ""stub"" / ""draft"" / ""live"" status tags from a completeness heuristic
  • Drag-drop to reorder zone load order

Test plan

  • Open GUE, press Ctrl+M from any tab — atlas opens centered, lists every zone with its counts
  • One row has a * prefix matching the zone currently loaded in the Zones tab
  • Click a different zone — Zones tab opens, that zone loads (save-prompt fires if current zone is dirty)
  • Press Ctrl+M again — atlas reopens; the * now points to the just-loaded zone
  • Press Esc — atlas closes
  • Open atlas, click X button on the window frame — closes; Ctrl+M reopens

Four-PR series wrap-up

Together, #271 + #273 + #274 + this PR port four hero concepts from the Loom redesign brief into the existing F-UI editor:

PR Concept Hotkey/affordance
#271 Command palette Ctrl+K
#273 World-health conscience ribbon top-right of menu bar + Issues... modal
#274 Cross-reference navigation > buttons in actor editor
#275 (this) World atlas Ctrl+M

The series introduces one general-purpose primitive — GUE_JumpToEntity(kind$, refID) — that future work can plug into without revisiting any of these landings. The remaining Loom concepts (visible threads overlay, session timeline scrubber, aesthetic toggle, walk-in playtest, single-workspace shell) either require infrastructure that doesn't exist yet (universal undo, validator framework, server-bridge) or are technically impractical in F-UI's draw model and were correctly left out.

🤖 Generated with Claude Code

Adds a modal overlay invoked by Ctrl+M from any tab that shows every
zone in the project as a compact one-line summary:

   *  Hollow's Edge                    3 portals     7 spawns    2 triggers
      Ravensreach                     12 portals    24 spawns    6 triggers
      Sunken Keep                      4 portals    18 spawns   11 triggers

The "*" pip marks the currently-loaded zone. Clicking a row loads that
zone (via GUE_JumpToEntity, which fires the existing CZone event so the
real zone-load path with save-prompt runs unchanged) and closes the
overlay. Esc also closes.

Why a separate overlay from the command palette: the palette is
free-text find-anywhere; the atlas is a spatial overview for zones with
per-zone metadata at a glance. Different jobs, different shapes,
different hotkeys.

Per-zone counts (portals / spawns / triggers) walk the Area's existing
fixed-size arrays (PortalName$[99], SpawnActor[999], TriggerScript$[149])
on each open -- so the figures are always current, even if the user just
added a portal in the Zones tab and immediately Ctrl+M'd to compare.

Branch is stacked on top of #274 (which is stacked on #273 on #271).
Completes the four-PR Loom series.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CoreyRDean CoreyRDean requested a review from a team as a code owner May 26, 2026 13:18
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a83b24bc6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Modules/WorldAtlas.bb Outdated
Comment on lines +206 to +207
If EID = ATL_List
Local sel = FUI_SendMessage(ATL_List, M_GETSELECTED)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sync atlas state when titlebar close button is used

Atlas_HandleEvent only handles ATL_List events, so it ignores the window's own close event; in F-UI, clicking the titlebar X emits a "Closed" event for the window handle (FUI_CreateEvent Handle(gad), "Closed"), but ATL_Open is never cleared in that path. After closing with X, the next Ctrl+M is treated as a close (because ATL_Open is still true), so the atlas requires two keypresses to reopen and contradicts the expected “X closes; Ctrl+M reopens” flow.

Useful? React with 👍 / 👎.

…ment

Three classes of bugs surfaced when actually launching GUE. All four
Loom features (palette, conscience ribbon, xref nav, atlas) were hit
because they share the same primitives. Fixed in one commit on the top
of the stack so the fixes propagate as the underlying PRs merge.

1. F-UI Window gadgets don't respond to M_HIDE / M_SHOW -- only to
   M_CLOSE / M_OPEN. (Confirmed by reading F-UI.bb's per-widget
   Select on Message: every other gadget has Case M_HIDE -> hide, but
   the Window branch has Case M_CLOSE -> hide.) Result: all three
   modals -- palette, findings, atlas -- stayed visible from creation
   instead of starting hidden. That's why the atlas was popping up on
   launch with no zones listed (Atlas_Repopulate is only called from
   Atlas_Open, never from Init, so the auto-shown window had just the
   initial hint and an empty list).

2. F-UI listbox and combobox have M_GETSELECTED / M_GETINDEX SWAPPED:

     ComboBox: M_GETSELECTED -> active item HANDLE
               M_GETINDEX    -> 1-based int

     ListBox:  M_GETSELECTED -> 1-based int
               M_GETINDEX    -> active item HANDLE

   I had matched the combobox shape everywhere, which made every
   listbox click read an integer-as-handle, then M_GETDATA on an int
   returned garbage, then ListAt(..., garbage) returned Null. Result:
   palette click, atlas click, conscience-finding click all silently
   did nothing.

   This same inversion broke the listbox-side of GUE_JumpToEntity --
   the picker-walk for "faction" and "animset" kinds never found its
   target. Which is why the new ">" cross-ref buttons in the actor
   editor (PR #274) clicked but didn't navigate. Fixed by branching on
   a per-kind isList flag in GUE_JumpToEntity and using M_GETINDEX
   (not M_GETSELECTED) on the listbox path.

3. Conscience ribbon widgets were placed at Y=2, inside the menu bar's
   Y=0..20 region. The menu bar's mouse capture / render order made
   the "Issues..." button visually fight the menu strip ("blurry text"
   per the bug report) and the click hit-test never reached the button.
   Plus the button was created with Flags=0 instead of CS_BORDER,
   stripping the visible border. Moved the ribbon to Y=24 (overlays
   the right side of the tab title strip, which has no tab title that
   far right) and restored CS_BORDER.

Also converted the For Each iterators from the
`Local X.Type \n For X = Each Type` shape to the canonical inline
`For X.Type = Each Type` shape that every other GUE module uses. The
pre-declared form happens to work for Items/Spells (the conscience
validator did find one issue at runtime) but the canonical form is
the established pattern; matching it removes a class of "did this
silently fail to bind?" doubt for future debugging.

All four engine targets compile clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CoreyRDean
Copy link
Copy Markdown
Collaborator Author

Pushed 9c937af — fix-up commit on top of this branch addressing four runtime bugs reported during smoke test:

  1. F-UI Window M_HIDE / M_SHOW are no-ops (only M_CLOSE / M_OPEN work). All three Loom modals were popping up on launch instead of starting hidden. This is what made the atlas appear on launch with an empty list — Atlas_Repopulate only runs from Atlas_Open, but the auto-shown window never went through Open.

  2. F-UI ListBox and ComboBox have M_GETSELECTED / M_GETINDEX swapped. I had matched the ComboBox shape everywhere, so every ListBox click read an int-as-handle, then M_GETDATA on an int returned garbage. Fixed in palette activation, conscience-finding click, atlas click, and the listbox path of GUE_JumpToEntity (which was breaking the cross-ref > buttons for faction/animset targets).

  3. Conscience ribbon at Y=2 was inside the menu bar's hit zone — that's why the Issues... button looked blurry and was unclickable. Moved to Y=24 (right side of the tab strip where no titles extend) and restored CS_BORDER on the button.

  4. Defensive sweep: converted For Each iterators to the canonical inline-typed form (For X.Type = Each Type) that every other GUE module uses, instead of the Local X.Type + For X = Each Type shape.

All four engine targets compile clean. Because this branch sits at the top of the stack, the fixes will land alongside the four feature commits when the stack merges (or rebase down into the underlying PRs if you'd prefer them per-PR — say the word and I'll split them).

🤖 Generated with Claude Code

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c937af716

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +122 to 124
FUI_SendMessage(CP_Window, M_OPEN)
FUI_SendMessage(CP_Window, M_BRINGTOFRONT)
CP_Open = True
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Synchronize palette state on titlebar close

CP_Open is only flipped in CmdPalette_Open/CmdPalette_Close, but the window can also be closed via the titlebar X (F-UI emits a "Closed" event for the window handle). In that path CP_Open stays True, so the next Ctrl+K goes through the close branch instead of reopening, requiring two keypresses to bring the palette back. Please handle the window "Closed" event (or query M_VISIBLE) to keep CP_Open in sync with actual window state.

Useful? React with 👍 / 👎.

Two remaining bugs after the first fix-up. Both surfaced clearly from
the user's smoke test:

1. CROSS-REF / PALETTE / ATLAS: "the correct element is selected but
   the tab doesn't switch."

   F-UI's Tab `Case M_SETINDEX` handler does set `Tab\actTabPage`
   internally, but the visible tab does not switch when invoked from
   outside the click path. Suspected cause: the implementation has a
   nested-For-loop iterator with both loops named `tabp`:

       For tabp.TabPage = Each TabPage    ' outer
           If Count = Param1
               Tab\actTabPage = tabp
               For tabp.TabPage = Each TabPage    ' inner, shadows outer
                   ...rebuild every page...
               Next
               Exit

   In non-Strict Blitz, `For X = Each Type` implicitly declares X at
   function scope; the inner re-declaration likely corrupts state in a
   way that nullifies the assignment by the time the next FUI_Update
   renders.

   Bypass M_SETINDEX with `GUE_SetActiveTab(idx)` in CommandPalette.bb
   that does the same work but with distinct iterator names (`probe`
   for the search, `sib` for the rebuild pass), then call it from
   GUE_JumpToEntity in place of `FUI_SendMessage(TabMain, M_SETINDEX,
   tabIdx)`. Still fires the Case TabMain event afterward so the
   existing leave/enter dispatcher runs its hide/show logic.

2. CONSCIENCE RIBBON: Issues button at Y=24 is invisible.

   The tab title strip occupies Y=20..40 (TABPAGE_HEIGHT = 20), so
   Y=24 puts the button behind that strip and behind whatever the
   tabs render. Y=2 (previous attempt) was inside the menu bar's
   click-capture zone. There is no usable top strip in WMain that's
   free of both regions.

   Move to the bottom of WMain instead: rowY = sh - 22. The bottom
   strip is the only horizontal band that has nothing in it on any
   tab. Computed from GraphicsHeight() at Init so it works in any
   window size.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CoreyRDean
Copy link
Copy Markdown
Collaborator Author

Second fix-up (bd77a71) addressing the two bugs left after the first round.

1. Tab doesn't switch (palette / atlas / cross-ref all)

Your "the right entity gets selected but the tab doesn't switch" was the giveaway. The picker walk worked, only the tab visual was failing. F-UI's Tab Case M_SETINDEX handler has a nested For tabp.TabPage = Each TabPage inside an outer For tabp.TabPage = Each TabPage — the inner re-declaration of the same iterator variable corrupts the outer's state in non-Strict Blitz, and the assignment to actTabPage doesn't survive to the next render.

Bypassed it with a tiny GUE_SetActiveTab(idx) helper that does the same work with distinct iterator names (probe for the search, sib for the rebuild). GUE_JumpToEntity now calls that instead of FUI_SendMessage(TabMain, M_SETINDEX, ...).

2. Issues button invisible

Y=2 was inside the menu bar's click zone. Y=24 turned out to be inside the tab title strip (Y=20..40, TABPAGE_HEIGHT=20). No top strip in WMain is free of both. Moved to the bottom of the window — the only horizontal band that has nothing in it on any tab.

🤖 Generated with Claude Code

@CoreyRDean
Copy link
Copy Markdown
Collaborator Author

Closing this PR. Pivoting from "port Loom concepts into the existing GUE" to building a fresh Loom.bb editor as a parallel alpha. The F-UI quirks we hit (window M_HIDE no-ops, listbox/combobox handle inversion, the Tab M_SETINDEX nested-iterator pathology) make incremental retrofit a losing game. New approach will be a clean drop-in editor launched from Project Manager alongside GUE, sharing the on-disk data formats but not the UI shell. Branch will be deleted; nothing landed on develop so no revert needed.

@CoreyRDean CoreyRDean closed this May 26, 2026
@CoreyRDean CoreyRDean deleted the coreyrdean/loom-world-atlas branch May 26, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant