Created
October 27, 2024 23:43
-
-
Save 143mailliw/cc06cfb596b3d7374fba862fb4aa49b8 to your computer and use it in GitHub Desktop.
GPUI List Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use gpui::*; | |
| pub struct ListExample { | |
| state: ListState, | |
| my_data: Model<Vec<SharedString>>, | |
| } | |
| impl ListExample { | |
| /// Creates the list state from our Model. Since we'll need to do this twice (once when the | |
| /// view is created, and many times afterwards when our source model is updated), it should | |
| /// be a function. | |
| /// | |
| /// Note that you *shouldn't* take a reference to self here, since this will need to be done | |
| /// *before* you create your struct containing the ListState. | |
| fn make_state(data: Vec<SharedString>) -> ListState { | |
| ListState::new( | |
| // The amount of entries in our list. In this case, it's the length of our source Vec. | |
| data.len(), | |
| // Whether the list should start from the top or bottom. Usually, you want Top, but if | |
| // you're making an application like Discord where the messages start from the bottom, | |
| // you'll want Bottom. | |
| ListAlignment::Top, | |
| // How many items outside of the rendered area should have their layouts precalculated. | |
| // You likely won't need to change this, unless your scrolling doesn't work properly. | |
| px(30.0), | |
| // This is the important part: the function that renders each item in the list. | |
| // `idx` is the index of the item being currently rendered, and `cx` is a WindowContext | |
| // reference (which we don't use). | |
| move |idx: usize, cx| { | |
| // This can be anything that is Into<AnyElement>, but in our case it will just be | |
| // a simple div with the text in our string. | |
| div().p_1().child(data[idx].clone()).into_any_element() | |
| }, | |
| ) | |
| } | |
| /// Create the view for ListExample. | |
| /// | |
| /// This doesn't have to be a function like this, but I find it most convenient to create Views | |
| /// like this, so that it's impossible to construct ListExample outside of a View. | |
| pub fn new(cx: &mut WindowContext) -> View<Self> { | |
| cx.new_view(move |cx| { | |
| // Create our example model. In the real world, you might want to get this from a | |
| // global, or pass it in as an argument. For now, though, we'll just make an model | |
| // containing a Vec of SharedStrings, which we'll display in our list. | |
| // | |
| // SharedStrings are Strings that can be cloned with a very low cost, making them more | |
| // suitable for our use case. They're built in to GPUI. | |
| // | |
| // If you're unfamiliar with models, read the "Ownership and data flow in GPUI" blog | |
| // post on the Zed blog, which explains the basics (including the functions used here). | |
| // | |
| // You also don't have to use a model! If your data never changes, and it's only used | |
| // in this list, there's no reason to use a model. Models are designed to allow for | |
| // sharing data between parts of your code, so if you aren't doing that here, you don't | |
| // need one. | |
| let my_data = cx.new_model(|_| { | |
| vec![ | |
| SharedString::new_static("One Fish"), | |
| SharedString::new_static("Two Fish"), | |
| SharedString::new_static("Red Fish"), | |
| SharedString::new_static("Blue Fish"), | |
| ] | |
| }); | |
| // First, we'll want to read the current value of our model. Since our `make_state` | |
| // function takes in a Vec instead of a reference to a Vec, we'll have to clone it. | |
| let data = my_data.read(cx).clone(); | |
| // Now we can create our ListState using the `make_state` function. | |
| let state = ListExample::make_state(data); | |
| // Since we're getting the data from a model, we'll probably want to update our state | |
| // when our model gets updated, so our list is always showing the latest data. | |
| cx.observe(&my_data, |this: &mut Self, model, cx| { | |
| // We'll still need to read the data: the `model` argument just passes a copy of | |
| // the model. | |
| let data = model.read(cx).clone(); | |
| // `this` is a reference to the current View's data, so we can change our list | |
| // state: | |
| this.state = ListExample::make_state(data); | |
| // Finally, we'll need to inform the view that it's been updated, which is done | |
| // with cx.notify(). If you skip this, the view won't re-render until something | |
| // else triggers it. | |
| cx.notify(); | |
| }) | |
| // You must call .detatch() on all subscriptions and observations. | |
| .detach(); | |
| // Our setup is finally complete. Now we just need to create the struct for our View! | |
| ListExample { state, my_data } | |
| }) | |
| } | |
| } | |
| // Lastly, we'll need to implement Render, like any struct used in a View. | |
| impl Render for ListExample { | |
| fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement { | |
| // Rendering a list is much easier than making the state: just use the list() element, | |
| // pass in the state, and give it a width and a height. | |
| list(self.state.clone()).w_40().h_24() | |
| // That's it! We're done. | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment