examples: Add examples folder. (#1378)

This commit is contained in:
Jason Lee 2025-10-15 10:26:13 +08:00 committed by GitHub
parent 6998708b81
commit 4ccd7d99d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 287 additions and 14 deletions

19
Cargo.lock generated
View file

@ -92,6 +92,16 @@ version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "app_assets"
version = "0.2.0"
dependencies = [
"anyhow",
"gpui",
"gpui-component",
"rust-embed",
]
[[package]]
name = "arbitrary"
version = "1.4.1"
@ -8760,6 +8770,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window_title"
version = "0.2.0"
dependencies = [
"anyhow",
"gpui",
"gpui-component",
]
[[package]]
name = "windows"
version = "0.57.0"

View file

@ -1,5 +1,5 @@
[workspace]
members = ["crates/hello_world", "crates/macros", "crates/story", "crates/ui"]
members = ["examples/hello_world", "examples/window_title", "crates/macros", "crates/story", "crates/ui", "examples/window_title", "examples/app_assets", "examples/app_assets"]
default-members = ["crates/story"]
resolver = "2"

18
examples/README.md Normal file
View file

@ -0,0 +1,18 @@
# GPUI Component basic examples
This folder contains basic examples of how to use the GPUI Component library. Each example demonstrates a specific feature or functionality of the library.
Unlike the examples in the `story` folder, these examples focus on 1 example for 1 feature, making it easier to understand and implement specific functionalities in your own projects.
## Contributing
Feel free to contribute more examples to this folder!
If you have a specific use case or feature you'd like to demonstrate, please create a new example file and submit a pull request. We will happy to merge it into the repository.
When creating a new example, please follow these guidelines:
1. Keep 1 example just doing 1 thing for more clarity.
2. Testing the example to ensure it works as expected.
3. Write some comment at some key parts of the code to explain what it does.
4. Following the code style and name style used in the existing examples or in entire of GPUI Component.

View file

@ -0,0 +1,15 @@
[package]
edition = "2021"
name = "app_assets"
description = "Example to load icons or images from assets folder."
publish = false
version = "0.2.0"
[dependencies]
anyhow.workspace = true
gpui.workspace = true
gpui-component = { workspace = true }
rust-embed = { version = "8", features = ["interpolate-folder-path"] }
[lints]
workspace = true

View file

@ -0,0 +1,62 @@
## Icon assets in GPUI Component
The [IconName](https://github.com/longbridge/gpui-component/blob/6998708b817024c2ac0f1ea164d74ddfc024e124/crates/ui/src/icon.rs#L9) is a enum that defined a bunch of icon names, because some internal components in GPUI Component will use them.
You can see, we have a lot of svg icon files in the `assets/icons` folder, but we are not embed all of the icon files in the library by default. This for keep the library size small.
So you must have your own icon files to use the `Icon` component in GPUI Component.
You can download the icon files from [here](https://lucide.dev/) or use your own icon files as you wish, just use the same filename as the icon name (match with the `IconName` defined) you want to use.
For example your assets folder:
```
app_root
assets
icons
close.svg
menu.svg
...
src
main.rs
Cargo.toml
```
You also can just copy the svg files you want from the `assets/icons` folder in GPUI Component repo to your own assets folder.
## How to use
You need define a `Assets` struct with rust-embed to register assets to GPUI application.
```rs
use anyhow::anyhow;
use gpui::*;
use rust_embed::RustEmbed;
use std::borrow::Cow;
#[derive(RustEmbed)]
#[folder = "./assets"]
#[include = "icons/**/*.svg"]
pub struct Assets;
impl AssetSource for Assets {
fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
Self::get(path)
.map(|f| Some(f.data))
.ok_or_else(|| anyhow!("could not find asset at path \"{path}\""))
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
Ok(Self::iter()
.filter_map(|p| p.starts_with(path).then(|| p.into()))
.collect())
}
}
fn main() {
// Call with_assets to register assets
let app = Application::new().with_assets(Assets);
// ...
}
```

View file

@ -0,0 +1,14 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-bot"
><path d="M12 8V4H8" /><rect width="16" height="12" x="4" y="8" rx="2" /><path
d="M2 14h2"
/><path d="M20 14h2" /><path d="M15 13v2" /><path d="M9 13v2" /></svg>

After

Width:  |  Height:  |  Size: 421 B

View file

@ -0,0 +1,14 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-inbox"
><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" /><path
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
/></svg>

After

Width:  |  Height:  |  Size: 443 B

View file

@ -0,0 +1,64 @@
use anyhow::anyhow;
use gpui::*;
use gpui_component::{v_flex, IconName, Root};
use rust_embed::RustEmbed;
use std::borrow::Cow;
/// An asset source that loads assets from the `./assets` folder.
#[derive(RustEmbed)]
#[folder = "./assets"]
#[include = "icons/**/*.svg"]
pub struct Assets;
impl AssetSource for Assets {
fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
if path.is_empty() {
return Ok(None);
}
Self::get(path)
.map(|f| Some(f.data))
.ok_or_else(|| anyhow!("could not find asset at path \"{path}\""))
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
Ok(Self::iter()
.filter_map(|p| p.starts_with(path).then(|| p.into()))
.collect())
}
}
pub struct Example;
impl Render for Example {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_2()
.size_full()
.items_center()
.justify_center()
.text_center()
.child(IconName::Inbox)
.child(IconName::Bot)
}
}
fn main() {
// Register Assets to GPUI application.
let app = Application::new().with_assets(Assets);
app.run(move |cx| {
// We must initialize gpui_component before using it.
gpui_component::init(cx);
cx.spawn(async move |cx| {
cx.open_window(WindowOptions::default(), |window, cx| {
let view = cx.new(|_| Example);
// The first level on the window must be Root.
cx.new(|cx| Root::new(view.into(), window, cx))
})?;
Ok::<_, anyhow::Error>(())
})
.detach();
});
}

View file

@ -29,6 +29,7 @@ fn main() {
let app = Application::new();
app.run(move |cx| {
// We must initialize gpui_component before using it.
gpui_component::init(cx);
cx.activate(true);
@ -57,19 +58,11 @@ fn main() {
..Default::default()
};
let window = cx
.open_window(options, |window, cx| {
let view = cx.new(|_| HelloWorld);
cx.new(|cx| Root::new(view.into(), window, cx))
})
.expect("failed to open window");
window
.update(cx, |_, window, _| {
window.activate_window();
window.set_window_title("Example");
})
.expect("failed to update window");
cx.open_window(options, |window, cx| {
let view = cx.new(|_| HelloWorld);
// The first level on the window must be Root.
cx.new(|cx| Root::new(view.into(), window, cx))
})?;
Ok::<_, anyhow::Error>(())
})

View file

@ -0,0 +1,14 @@
[package]
edition = "2021"
name = "window_title"
description = "An example of using gpui-component to create a window with a custom title bar."
publish = false
version = "0.2.0"
[dependencies]
anyhow.workspace = true
gpui.workspace = true
gpui-component = { workspace = true }
[lints]
workspace = true

View file

@ -0,0 +1,60 @@
use gpui::*;
use gpui_component::{
button::{Button, ButtonVariants},
v_flex, Root, TitleBar,
};
pub struct Example;
impl Render for Example {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.size_full()
.child(
// Render custom title bar on top of Root view.
TitleBar::new()
.justify_between()
.pr_2()
.child("App with Custom title bar")
.child("Right Item"),
)
.child(
div()
.id("window-body")
.p_5()
.size_full()
.items_center()
.justify_center()
.child("Hello, World!")
.child(
Button::new("ok")
.primary()
.label("Let's Go!")
.on_click(|_, _, _| println!("Clicked!")),
),
)
}
}
fn main() {
let app = Application::new();
app.run(move |cx| {
gpui_component::init(cx);
cx.spawn(async move |cx| {
let window_options = WindowOptions {
// Setup GPUI to use custom title bar
titlebar: Some(TitleBar::title_bar_options()),
..Default::default()
};
cx.open_window(window_options, |window, cx| {
let view = cx.new(|_| Example);
cx.new(|cx| Root::new(view.into(), window, cx))
})?;
Ok::<_, anyhow::Error>(())
})
.detach();
});
}