Skip to content

Instantly share code, notes, and snippets.

@143mailliw
Created October 27, 2024 23:43
Show Gist options
  • Select an option

  • Save 143mailliw/cc06cfb596b3d7374fba862fb4aa49b8 to your computer and use it in GitHub Desktop.

Select an option

Save 143mailliw/cc06cfb596b3d7374fba862fb4aa49b8 to your computer and use it in GitHub Desktop.
GPUI List Example
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