Open dabreegster opened 1 year ago
Big :+1: to this such a great idea.
If this is issue still needs someone, I would like to try my hands on this issue.
Hi @Cerabbite, it's very much up for grabs, yes! I have not given thought to how to implement this yet. https://github.com/a-b-street/abstreet/blob/main/apps/ltn/src/design_ltn.rs handles the part of the UI that the tutorial covers. I would almost guess it could be easier to start a new module + State
to do the tutorial, and pull in / refactor components to share. Another possible inspiration is https://github.com/a-b-street/abstreet/blob/main/apps/game/src/sandbox/gameplay/tutorial.rs, code for the traffic simulator's tutorial mode. This was written long ago and I would probably try things very differently today, though.
If you want any guidance or to talk through how to tackle this, I'm happy to help!
@dabreegster do we want to initiate the tutorial from the command line just like we do with the other tutorial?
It'd be useful, but not necessary. A button somewhere maybe in the current top bar of the tool, maybe?
On Wed, Feb 22, 2023, 7:18 PM Cerabbite @.***> wrote:
@dabreegster https://github.com/dabreegster do we want to initiate the tutorial from the command line just like we do with the other tutorial?
— Reply to this email directly, view it on GitHub https://github.com/a-b-street/abstreet/issues/1033#issuecomment-1440660574, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAMWLFZWW2ZTMJ4BSIP72YDWYZRA5ANCNFSM6AAAAAASYDQGSU . You are receiving this because you were mentioned.Message ID: @.***>
Is the tutorial supposed to automatically move on once the user has changed text size or is the only way to move the tutorial forward the "next" button?
Hmm, good question. I think in this case, "next" should be the only way to advance. If we advanced when someone changed the text size, it'd be kind of annoying in many cases, because the default of 1 is probably fine. What do you think?
I think that it can be useful to be able to interact with the components during the tutorial, however I agree with you that it can be annoying in some cases. One possibility could be to have a pop-up at the end of the tutorial to ask if they want to keep or discard all the changes they made during the tutorial. Although I don't know how hard that would be to implement.
Offering to revert is a nice idea! It shouldn't be too tough to stash initial settings before the tutorial somewhere.
You might be finding that overlaying extra dialogs on top of the existing UI (and hiding / preventing interactions there) is tough, or the same with injecting something in the Design LTN mode that can figure out when the user has done something (and update hints / instructions / "good job!" panels accordingly). If you've done any other UI programming before or have any ideas about how it should work, I'd be very open to refactoring the LTN UI to make tutorial mode easier to implement. I've been separately working on Svelte web apps lately, and had a very experimental attempt to organize existing code into something more component-like.
Yeah, working with the current UI is indeed quite difficult, and I still have not gotten used to it. However, the reorganization of the LTN code looks great I have not yet read all the code, but from what I have currently read it is all a lot clearer. I will try to build a simple MVP tutorial system on top of this refactored code to see how easy it is.
The refactoring is very incomplete (it doesn't compile). I can try and work through finishing it if you like the direction it's taking.
In the meantime, I guess thinking about strategies for the tutorial... maybe it could just be a new state on top of DesignLTN
. If you look at places using DrawBaselayer::PreviousState
, there are ways to draw extra stuff on top of deactivated and greyed out things. But that might not make it easy to point to the correct part of the screen for parts of the tutorial instruction. So maybe adding in some kind of extra component in DesignLTN
would be easier. Maybe we need a way to disable lots of the controls, before the user gets to the right part of the tutorial.
I had not yet tried to compile, so thanks for letting me know.
Yeah, the main two things I was thinking about was drawing a gray (partially transparent) layer on top of the current screen state. To then draw the pop-up boxes on top of that, and to then either mask the part of the UI the user needs to access. Or redraw only that specific part back on top of the gray layer. One of the problems with this approach is that it does not disable keyboard shortcuts. So I do in fact think we need to disable all controls before going to the tutorial and then turning them back on when we need them.
Is there anyway I can help in refactoring the UI code?
drawing a gray (partially transparent) layer on top of the current screen state
map_gui::tools::grey_out_map
or similar can help. Allowing clicks only in certain parts of the screen is an interesting idea I haven't explored much before; if that looks like a pattern we'll need, let me know if you need a guided tour through the widgetry
guts.
So I do in fact think we need to disable all controls before going to the tutorial and then turning them back on when we need them.
Looking at Madison's tutorial design more closely... the first bit is non-interactive; looks like the controls aren't supposed to actually be active, just the prev/next/skip tour stuff. I think DrawBaselayer::PreviousState
plus greying out parts of the screen should work here.
Looking at the rest, it seems like the Design LTN page and rest of the app is fully usable in the normal way; things aren't disabled. That suggests to me that we want to add some kind of optional tutorial controller state to DesignLTN
struct and inject extra behavior there. That's roughly how the traffic sim tutorial worked; GameplayMode::Tutorial
specializes some behavior in SandboxMode
. I don't know if this is the nicest approach, curious to see what you find.
Is there anyway I can help in refactoring the UI code?
I merged #1068. If you have feedback or other ideas what to do, feel free to discuss those changes or make them. Getting a fresh pair of eyes on this is helpful, and you'll be butting heads with any problems with the current structure.
After I pulled from the main branch to be up-to-date I ran into a strange bug, where the binary will compile, but then I get this screen
setup widgetry...
setup app...
[2023-02-26T18:15:35Z ERROR map_model::make] 0 total intersections have some connectivity problem
[2023-02-26T18:15:35Z INFO map_model::make::buildings] Discarded 1 buildings that weren't close enough to a sidewalk
[2023-02-26T18:15:35Z INFO map_model::make::parking_lots] Discarded 0 parking lots that weren't close enough to a sidewalk
[2023-02-26T18:15:35Z INFO map_model::pathfind::engine] Contraction hierarchy input graph has 2 nodes
[2023-02-26T18:15:35Z INFO map_model::pathfind::engine] Contraction hierarchy input graph has 2 nodes
[2023-02-26T18:15:35Z INFO map_model::pathfind::engine] Contraction hierarchy input graph has 4 nodes
[2023-02-26T18:15:35Z INFO map_model::pathfind::engine] Contraction hierarchy input graph has 6 nodes
[2023-02-26T18:15:35Z INFO map_gui::render::map] static DrawMap consumes 0 MB on the GPU
setup app took 0.1750s
setup widgetry... plus 0.2028s
setup widgetry took 0.3777s
[2023-02-26T18:15:35Z INFO abstutil::time] - setup widgetry took 0.3777s
[2023-02-26T18:15:35Z INFO abstutil::time] - setup app took 0.1750s
[2023-02-26T18:15:35Z INFO abstutil::time] - ... plus 0.2028s
load data/system/us/seattle/maps/montlake.bin...
load data/system/us/seattle/maps/montlake.bin took 0.0086s
[2023-02-26T18:15:35Z INFO abstutil::time] - load data/system/us/seattle/maps/montlake.bin took 0.0086s
[2023-02-26T18:15:35Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:36Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
[2023-02-26T18:15:37Z WARN widgetry::app_state] A state requested DrawBaselayer::PreviousState, but it's the only state on the stack!
And these warnings kept coming for a while. After this I went back to my own branches but it stayed there. Even after completely removing the abstreet folder from my computer and then cloning my own branch from before the reorganization suddenly had this same bug. Any idea on what this could be and what I can do about it?
Ah, you need to download new binary map data. There's a binary incompatibility introduced recently. If cargo run --bin updater -- download
doesn't fix, let me know
Yep that fixed it thanks, i'll take a look at all the updates and new features of #1068 tomorrow
if you need a guided tour through the widgetry guts.
I don't know if an entire tour is necessary, but getting to know some of the ins and outs of the widgetry might be useful
I think DrawBaselayer::PreviousState plus greying out parts of the screen should work here.
Thanks, I'll look into it.
i'll take a look at all the updates and new features of #1068 tomorrow
The code definitely looks a lot cleaner and easier to work with.
I added a CLI option for tutorial, so now you can use cargo run --bin ltn -- --tutorial true
to start the tutorial (although the state of the tutorial is not there yet). Although it would be nice to just be able to run cargo run --bin ltn -- --tutorial
normally, you would use an enum for that, but all the other CLI arguments are in a struct. Any suggestions?
normally, you would use an enum for that, but all the other CLI arguments are in a struct.
I added tutorial: bool
to Args
in ltn/src/lib.rs
and it seems to work fine for me
#[derive(StructOpt)]
struct Args {
#[structopt(long)]
tutorial: bool,
/// Load a previously saved proposal with this name. Note this takes a name, not a full path.
/// Or `remote/<ID>`.
#[structopt(long)]
proposal: Option<String>,
/// Lock the user into one fixed neighbourhood, and remove many controls
#[structopt(long)]
consultation: Option<String>,
#[structopt(flatten)]
app_args: map_gui::SimpleAppArgs,
}
Looks like --tutorial=false
still counts as true. I guess if structopt sees anything involving that flag, the value doesn't matter.
I added tutorial: bool to Args in ltn/src/lib.rs and it seems to work fine for me
I used tutorial: Option<bool>
, I think that that is why it did not work for me. Thanks
Madison designed an interactive tutorial mode for the LTN tool, but I haven't found time to implement it yet. If someone's not afraid of the slightly odd UI library A/B Street uses, it could be a fun medium-sized starter project. The design can be found at https://www.figma.com/file/HxSNM2146bcb0P0bNlW0x1/LTN-Working-Station-V2?node-id=5369%3A5464&t=cE5ZYUOuSsUMKwl2-0. Some samples: Embedding a second view of the map canvas is a bit advanced, but something we've figured out before. If anyone's interested in implementing, I can help flesh out smaller tasks and figure out how to structure the code.
CC @matthieu-foucault as an option I forgot from the other list