SSearchBox and its four subclassesThe model-backed list that filters itself as it is typed into. The list contract under it (
SListWidget/SListBox, lazy row widgets,change(I)) is lists, text and scrolling.
SSearchBox<I, W> — two models, one view| What | Where |
|---|---|
| Every row vs. the rows shown | allitems() (protected abstract) is every row. items() is overridden to answer filtered while searching != null, else allitems(). SListBox builds its row widgets and its scroll range from items(), so a row a search dropped has no widget and no place in the scroll |
| The typing | keydown, with no Ctrl/Meta: a printable character → search(searching + c); Backspace → search(prefix); Enter and Escape → stopsearch() while a search runs; Up/Down → change(items().get(p ± 1)) then display(p). Each consumes the key only where it applies |
search(String) |
public. Empty → stopsearch(). Otherwise walks allitems(), tests each row with searchmatch(item, text) (the fork asks through AddonWidgets.searchmatch, // addon:), fills filtered, sets searching; when the current sel is not among the kept rows it calls change with the first kept row after the old selection (else the first kept, else null) — unless the fork’s quiet (// addon:) is set, which AddonWidgets.search does around a search an addon makes, so that one picks nothing; then display(sel) (a no-op for a row not in items()) and updinfo() |
| What is typed | searching, a public String, null while no search runs. stopsearch() (public) nulls it and filtered, and display(sel)s |
| The counter | updinfo() renders "%s (%d/%d)" — the text, the rows kept, allitems().size() — and draw blits it at the bottom right while a search runs |
| Focus | setcanfocus(true); mousedown calls parent.setfocus(this); lostfocus() calls stopsearch(). A search started by calling search on a list that never had the focus lasts until the list gains and then loses it, or until stopsearch |
searchcallschange, the same method a click on a row reaches — so filtering can pick a row, and on the kin and member listschangeis awdgmsg. Typing into a search list is not read-only.
| Class | Row type | searchmatch tests |
allitems() |
change(row) |
|---|---|---|---|---|
BuddyWnd.BuddyList (private) |
BuddyWnd.Buddy |
name contains the text, lower-cased |
buddies after sortifneeded() — the live list, which uimsg add/rm mutate under synchronized(buddies) |
wdgmsg("ch", id) (or null) on the BuddyWnd; it never writes sel — the server’s i-set does, and opens BuddyInfo beside the list |
Polity.MemberList |
Polity.Member (an inner class) |
m.name() contains the text: rname().text, the roster’s name for id through getparent(GameUI.class).buddies.find(id), unk (???) for a stranger, self (You) for id == null |
mlist, refilled in place (clear, addAll, sort by name then order) from tick whenever Polity.mseq moved |
wdgmsg("sel", id), or a bare "sel", on the Polity |
MapWnd.MarkerList |
MapWnd.ListMarker |
mark.nm contains the text |
markers, a list replaced wholesale when the marker set changes |
change2 writes sel and rebuilds the marker tools, then view.center on the marker: client-side |
GobIcon.SettingsWindow.IconList |
SettingsWindow.ListIcon |
name contains the text, false for a null name |
ordered, rebuilt from conf.settings in tick |
super.change, then the settings box for that icon: client-side |
Every one of them lower-cases both sides and uses indexOf, so the test is “contains, in any case”.
| Trap | Detail |
|---|---|
| A member row is replaced, not updated | Polity.uimsg("add") for an id already present calls parsememb(args, old), which builds a new Member through the copy constructor, and puts it in memb over the old one. A reference to the old row — a selection, a row kept by a caller — is no longer in the list once the server re-sends that member, which is what a group change does |
Member.name() needs the panel attached |
rname() climbs to GameUI through getparent; once the Polity is detached that answers null and the lookup throws |
BuddyList.allitems() is the live roster |
Iterating it off the tree’s monitor races the Loader thread’s add, which runs under synchronized(ui); copy it under that monitor |
The published polity panels subclass Polity |
ui/vlg’s Village and ui/realm’s Realm are server resource code, not in src/. Their rows are Polity.Members (the subclasses their own parsememb builds), so a hook on SSearchBox or on Polity.Member reaches them; the list’s own class name is theirs to choose, so find the list through a row’s parent rather than by that name |