alright, time to move color logic. let's see what it takes for each Renderable to decide its own color -- what state do they need?
- dont forget: search active and lane not a match does yield a color.
- woops, sim Draw{Car,Ped} doesnt implement Renderable. but it'll be nested anyway... we dont want to move stuff in the quadtree constantly. the 'get everything onscreen' routine should do that, interpreting Lanes and Intersections in initial pass of results correctly by querying for more stuff.
- actually, still undecided about color and RenderOptions...
- the whole motivation -- one draw() interface can only return a single color. and dont want to add code to UI for every single object type.
- maybe send in Option<Box<Plugin>> of the current thing, and then it can have a more specific method for each object type? or is that too far the other direction again?
- send in Option<Color>, letting each plugin vote?
- maybe if we only have one or two active plugins, it'll be easier to understand?
- would it be weird to invert and send in all the plugins? the point is
kinda for there to be one place to handle interactions between
plugins -- UI. having a strong concept of one active at a time would probably
_really_ help.
- maybe plugins do have a single color_obj(enum of IDs) -> Option<Color>?
- make it easier to fill out RenderOptions for things generically
- next step: extra things like draw_front_path also take cs, not a specific color -- if it makes sense.
- refactor selection plugin's color_something
- selection plugin currently has this weird case where it can cycle through turns at an intersection. MOVE THAT out.
- more generally, move out all custom logic. make other things know if something is selected and do special stuff if so.
- and make it act generic at last! \o/
OK, so where was I?
- colors are still currently missing for things that need two of them.
** having one active plugin at a time simplifies the color problem and solves a few others, so try that now.
- another refactor to do -- initiate plugins based on current_selection_state in UI or the plugin, but stop mixing so much
- make car and ped also Renderable, for great consistency!
- work on generic quadtree idea
### One active plugin at a time
I wonder if the UI will have a need to reach into plugins beyond event(). Let's find out!
- exceptions
- hider needs to given to finding onscreen stuff
- search can specify colors and OSD lines
- warp can add OSD lines
- show_route can color
- floodfiller can color
- steepness can color
- osm can color
- signal and stop sign editor can color and indicate what icons are onscreen
- road editor can be asked for state to serialize
- sim ctrl can contribute OSD lines (and everything grabs sim from it directly too -- maybe sim should live in UI directly)
- color picker can draw
- turn cycler can draw
- the stuff they take in event() is different. hmm.
- box lil closures
so it feels like we implicitly have a big enum of active plugin, with each of their states kinda hidden inside.
- the simple idea
- UI keeps having a bunch of separate plugins with their real type
- have a list of closures that take UI and do event(). return true if that plugin is active
NOT if something was done with the input
- in event(), go through the list and stop when something becomes
active. remember it's active and just call it directly next time in
event(), until it says its no longer active.
- then figure out the implications for color
tomorrow:
= implement exclusive active plugin thing
= rethink if existing plugins are active or not. maybe dont make event() return if active or not, maybe have a separate method? or just in event, do stuff first, and then have this query at the end.
= tooltips shouldnt be shown while a random plugin is active; move that out to its own plugin! same for debugging. selection plugin should have NO logic.
= refactor the toggleablelayer stuff, then move them to list of plugins too
= clean up selection state... should warp and hider be able to modify it, or just rerun mouseover_something?
= initiate plugins in the plugin's event; stop doing stuff directly in UI
= basically, make UI.event() just the active plugin list thing as much as possible.
- deal with overlapping keys that still kinda happen (sim ctrl, escape game)
- bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow
= make draw car/ped implement renderable.
= move Draw* to editor crate, and have an intermediate thing exposed from sim and do the translation in get_blah_onscreen.
- then rethink colors, with simplified single plugin
= then finally try out a unified quadtree!
= make parcels selectable as well?
- and see how much boilerplate a new type would need, by adding bus stops and water/parks
Alright, replan yet again.
= then rethink colors, with simplified single plugin
= plugin trait, color(id) -> Option<Color>. parallel list of box plugins (or, a fxn that takes the idx)
= refactor to one color_blah method
= handle the two color things... just buildings?
= and see how much boilerplate a new type would need, by adding bus stops and water/parks
- load water/parks and stuff
- deal with overlapping keys that still kinda happen (sim ctrl, escape game)
- and missing keys, like no tooltip for turns, since they're only shown in editors now
- bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow
- consider merging control map into map
- see how hard it is to render textures onto cars or something
= refactor debug and tooltip lines for objects
## Immediate mode GUI
Things organically wound up implementing this pattern. ui.rs is meant to just
be the glue between all the plugins and things, but color logic particularly is
leaking badly into there right now.
## UI plugins
- Things like steepness visualizer used to just be UI-less logic, making it
easy to understand and test. Maybe the sim_ctrl pattern is nicer? A light
adapter to control the thing from the UI? ezgui's textbox and menu are similar
-- no rendering, some input handling.
## GUI refactoring thoughts
- GfxCtx members should be private. make methods for drawing rectangles and such
- should be useful short term. dunno how this will look later with gfx-rs, but dedupes code in the meantime.
- should GfxCtx own Canvas or vice versa?
- Canvas has persistent state, GfxCtx is ephemeral every draw cycle
- dont want to draw outside of render, but may want to readjust camera
- compromise is maybe storing the last known window size in canvas, so we dont have to keep plumbing it between frames anyway.
One UI plugin at a time:
- What can plugins do?
- (rarely) contribute OSD lines (in some order)
- (rarely) do custom drawing (in some order)
- event handling
- mutate themselves or consume+return?
- indicate if the plugin was active and did stuff?
- just quit after handling each plugin? and do panning / some selection stuff earlier
- alright, atfer the current cleanup with short-circuiting... express as a more abstract monadish thing? or since there are side effects sometimes and inconsistent arguments and such, maybe not?
Should be simple -- I want to bundle together map edits as named things, to
prep for A/B tests. But loading a different set of edits could be kind of
tough...
- new control map state has to propagate to intersection editors.
- easy fix: pass them mut ref from control map every tick. then just have to reload control map.
- road edits have to propogate
- theres a way to do that live right now, but it's kind of brittle and funky. probably safer to load from scratch.
- but then have to reload things like steepness visualizer plugin... actually, just that, seemingly.
- er, but also the hider plugin -- it holds onto laneIDs, which may change!
Alright, I think this is the sequence of things to do:
1) make a few plugins less stateful anyway, by taking refs to map/control map stuff instead of caching stuff. thats useful regardless.
- but wait, then road editor kind of cant work, because mut borrow edits from map while holding immutable lane/road refs. theyre really indep things, so cant store together.
2) make it possible to completely reload UI and everything from scratch, from a plugin. rationale: it'd be nice to switch maps from inside the editor anyway. not necessary, but useful.
3) make road edits propogate correctly, and somehow have a strategy for ensuring nothing is forgotten. impl today is VERY incomplete.
- make sure SimMode can handle loading entirely new map or starting an a/btest
- diff needs to be per UI
- are modes exclusive or not? how will tutorial mode work?
- UI takes a single Mode or State or something. not multiple. the blocking vs not behavior happens elsewhere. tutorial mode will use this, mixing in View/Debug/Edit/Sim as relevant. plugins per UI vs map is confusing, but that might actually be lifted into this more abstract ui state thing?
- could SimMode own primary and secondary? UI shouldn't.
- do make one plugin for UI that toggles between the others... at least edit and sim exclusion.
- can that one thing have some state per map and some per UI? sure. what're the only places that touch that? a/b test editor, map editor, sim controls
- just move PluginsPerUI and PluginsPerMap into this one place
- PluginCtx stays the same... (but moves to plugins/mod)
- and the single plugin that UI talks to is a totally new trait with different context. ;)
- UI will need to reach into this single plugin for different stuff.
First simplify UI in smaller ways.
- get_objects_onscreen should return one list of things
- and between drawing and mousing over, dont recalculate all of the DrawCar/Ped stuff!
Tutorial mode needs to wrap the other stuff (and omit DebugMode!) to reach into SimMode and install a callback. Forces us back to this problem again. Radical idea: What state and code in UI is even important to preserve? Should TutorialMode literally be a new UI impl?
- right now diff plugin blocks everything and its mutex, so just make it part of the active_edit_plugin world.
- show score plugin is modal and nonblocking; lets not instantiate it till we need it. list of stackable modal things, just make sure not to create one again.
- sim controls is a little weird; for now, it's always nonblocking and ambient and ever-present.
Look at show_route plugin. Expressing state changes is awkward. Want to make the 'r' key available in Hovering state, but only if we survive the Hovering state.
About to let game own this directly, moving it out of ezgui.
- at first, just do this and keep current impl
- do the same for top menu probably, and context menus?
- but what if we dont constantly reset the active options? right after we do one action, the menu is displayed wrong, because no event causes us to build up possible actions again.
- hack: do a no-op event after just to rebuild the menu. very gross.
- one pass that doesnt DO anything, just builds up all the possible events that do something interesting
- then irrelevant events are immediately dropped
- this is kind of a generalization of the 'unused key_release' thing happening now
- two different styles...
- register callbacks (ew? except it maybe avoids weird problems with accidentally using an event twice)
- or have a no-op phase and an execution phase
The more radical design would get rid of event() entirely, invert the control. The game itself would say "poll for next event".
- When do we draw? When we block for events? Is drawing inlined into this one combined method, or is there still a separate function to redo it from scratch?
- screensaver would immediately hand over control to the menu. no more StillActive case!