icon: Rework icons to make use of the IconNamed trait (#1640)
This makes use of the `IconNamed` trait and a blanked implementation to
convert anything that implements this to an `Icon`.
This allows for easily defined custom versions of `IconName`, while
minimally changing existing code (essentially only if you previously
made use of the `.path()` method on the `IconName` enum; this now
requires an import of the `IconNamed` trait).
# Example
```rust
use gpui_component::IconNamed;
pub enum IconName {
Encounters,
Monsters,
Spells,
}
impl IconNamed for IconName {
fn path(self) -> gpui::SharedString {
match self {
IconName::Encounters => "icons/encounters.svg",
IconName::Monsters => "icons/monsters.svg",
IconName::Spells => "icons/spells.svg",
}
.into()
}
}
// this allows for the following interactions (works with anything that has the `.icon(icon)` method
Button::new("my-button").icon(IconName::Spells);
Icon::new(IconName::Monsters);
```
If you want to directly "render" a custom `IconName` you must implement
the `RenderOnce` trait and derive `IntoElement` on the `IconName`.
```rust
use gpui::{IntoElement, RenderOnce};
use gpui_component::IconNamed;
#[derive(IntoElement)]
pub enum IconName {
// The same as before
}
impl IconNamed for IconName {
// The same as before
}
impl RenderOnce for IconName {
fn render(self, _: &mut gpui::Window, _: &mut gpui::App) -> impl gpui::IntoElement {
gpui_component::Icon::empty().path(self.path())
}
}
// this allows for the following interaction
div()
.child(IconName::Monsters)
```
Overall I think is an improvement to the existing way to do custom
`IconName` implementations.
I am unsure if this change should also be reflected in the documentation
on the section with "Icons & Assets", though I personally think it would
make sense to highlight this way to do custom versions of `IconName` as
it is considerably less involved than the current approach.
Closes #1627.
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
parent
2e3387a76f
commit
d251a9b08b
3 changed files with 62 additions and 60 deletions
|
|
@ -1,8 +1,8 @@
|
|||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
use crate::{
|
||||
text::Text, v_flex, ActiveTheme, Disableable, FocusableExt, IconName, Selectable, Sizable,
|
||||
Size, StyledExt as _,
|
||||
icon::IconNamed, text::Text, v_flex, ActiveTheme, Disableable, FocusableExt, IconName,
|
||||
Selectable, Sizable, Size, StyledExt as _,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, relative, rems, svg, Animation, AnimationExt, AnyElement,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,21 @@ use gpui::{
|
|||
Transformation, Window,
|
||||
};
|
||||
|
||||
/// Types implementing this trait can automatically be converted to [`Icon`].
|
||||
///
|
||||
/// This allows you to implement a custom version of [`IconName`] that functions as a drop-in
|
||||
/// replacement for other UI components.
|
||||
pub trait IconNamed {
|
||||
/// Returns the embedded path of the icon.
|
||||
fn path(self) -> SharedString;
|
||||
}
|
||||
|
||||
impl<T: IconNamed> From<T> for Icon {
|
||||
fn from(value: T) -> Self {
|
||||
Icon::build(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of an icon in the asset bundle.
|
||||
#[derive(IntoElement, Clone)]
|
||||
pub enum IconName {
|
||||
|
|
@ -93,7 +108,14 @@ pub enum IconName {
|
|||
}
|
||||
|
||||
impl IconName {
|
||||
pub fn path(self) -> SharedString {
|
||||
/// Return the icon as a Entity<Icon>
|
||||
pub fn view(self, cx: &mut App) -> Entity<Icon> {
|
||||
Icon::build(self).view(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl IconNamed for IconName {
|
||||
fn path(self) -> SharedString {
|
||||
match self {
|
||||
Self::ALargeSmall => "icons/a-large-small.svg",
|
||||
Self::ArrowDown => "icons/arrow-down.svg",
|
||||
|
|
@ -180,17 +202,6 @@ impl IconName {
|
|||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Return the icon as a Entity<Icon>
|
||||
pub fn view(self, cx: &mut App) -> Entity<Icon> {
|
||||
Icon::build(self).view(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IconName> for Icon {
|
||||
fn from(val: IconName) -> Self {
|
||||
Icon::build(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IconName> for AnyElement {
|
||||
|
|
@ -239,16 +250,12 @@ impl Clone for Icon {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait IconNamed {
|
||||
fn path(&self) -> SharedString;
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn new(icon: impl Into<Icon>) -> Self {
|
||||
icon.into()
|
||||
}
|
||||
|
||||
fn build(name: IconName) -> Self {
|
||||
fn build(name: impl IconNamed) -> Self {
|
||||
Self::default().path(name.path())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ description: Display SVG icons with various sizes, colors, and transformations.
|
|||
|
||||
A flexible icon component that renders SVG icons from the built-in icon library. Icons are based on Lucide.dev and support customization of size, color, and rotation. The component requires SVG files to be provided by the user in the assets bundle.
|
||||
|
||||
Before you start, please make sure you have read: [Icons & Assets](../assets.md) to understand how use SVG in GPUI & GPUI Component application.
|
||||
|
||||
## Import
|
||||
|
||||
```rust
|
||||
|
|
@ -136,55 +138,48 @@ The Icon component supports several predefined sizes:
|
|||
| Large | `.large()` | `size_6()` | 24px |
|
||||
| Custom | `.with_size(px(n))` | - | n px |
|
||||
|
||||
## SVG Asset Requirements
|
||||
## Build you own `IconName`.
|
||||
|
||||
**Important:** SVG files must be provided by the user in the assets bundle. The component expects SVG files to be located in the `icons/` directory relative to your assets root.
|
||||
You can define your own `IconName` to have more specific icons for your application. We have `IconNamed` trait for you to implement for your.
|
||||
|
||||
For example, if you want to use `IconName::Heart`, you need to provide:
|
||||
```rust
|
||||
use gpui_component::IconNamed;
|
||||
|
||||
- `icons/heart.svg` in your assets bundle
|
||||
pub enum IconName {
|
||||
Encounters,
|
||||
Monsters,
|
||||
Spells,
|
||||
}
|
||||
|
||||
The component automatically maps icon names to their corresponding SVG file paths:
|
||||
impl IconNamed for IconName {
|
||||
fn path(self) -> gpui::SharedString {
|
||||
match self {
|
||||
IconName::Encounters => "icons/encounters.svg",
|
||||
IconName::Monsters => "icons/monsters.svg",
|
||||
IconName::Spells => "icons/spells.svg",
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
- `IconName::ArrowUp` → `icons/arrow-up.svg`
|
||||
- `IconName::GitHub` → `icons/github.svg`
|
||||
- `IconName::CircleCheck` → `icons/circle-check.svg`
|
||||
// This allows for the following interactions (works with anything that has the `.icon(icon)` method.
|
||||
Button::new("my-button").icon(IconName::Spells);
|
||||
Icon::new(IconName::Monsters);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
If you want to directly `render` a custom `IconName` you must implement the `RenderOnce` trait and derive `IntoElement` on the `IconName`.
|
||||
|
||||
### Icon
|
||||
```rust
|
||||
impl RenderOnce for IconName {
|
||||
fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
|
||||
Icon::empty().path(self.path())
|
||||
}
|
||||
}
|
||||
|
||||
| Method | Description |
|
||||
| --------------------------- | --------------------------------------------------- |
|
||||
| `new(icon)` | Create a new icon from `IconName` or another `Icon` |
|
||||
| `path(path)` | Set custom SVG file path |
|
||||
| `view(cx)` | Create a view entity for the icon |
|
||||
| `rotate(radians)` | Rotate the icon by specified radians |
|
||||
| `transform(transformation)` | Apply custom transformation |
|
||||
| `empty()` | Create an empty icon (for custom paths) |
|
||||
|
||||
### IconName Methods
|
||||
|
||||
| Method | Description |
|
||||
| ---------- | ------------------------------------------------ |
|
||||
| `path()` | Get the SVG file path for this icon |
|
||||
| `view(cx)` | Create a view entity directly from the icon name |
|
||||
|
||||
### Styling (via `Styled` trait)
|
||||
|
||||
| Method | Description |
|
||||
| ------------------- | ------------------ |
|
||||
| `text_color(color)` | Set the icon color |
|
||||
|
||||
### Sizing (via `Sizable` trait)
|
||||
|
||||
| Method | Description |
|
||||
| ----------------- | ----------------------------------- |
|
||||
| `xsmall()` | Set size to extra small (12px) |
|
||||
| `small()` | Set size to small (14px) |
|
||||
| `medium()` | Set size to medium (16px) - default |
|
||||
| `large()` | Set size to large (24px) |
|
||||
| `with_size(size)` | Set custom size in pixels |
|
||||
// Now you can use it directly in your element tree:
|
||||
div()
|
||||
.child(IconName::Monsters)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue