feat: add spinner component
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<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"
|
||||
>
|
||||
<path d="M12 2v4" />
|
||||
<path d="m16.2 7.8 2.9-2.9" />
|
||||
<path d="M18 12h4" />
|
||||
<path d="m16.2 16.2 2.9 2.9" />
|
||||
<path d="M12 18v4" />
|
||||
<path d="m4.9 19.1 2.9-2.9" />
|
||||
<path d="M2 12h4" />
|
||||
<path d="m4.9 4.9 2.9 2.9" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 434 B |
+32
-13
@@ -4,7 +4,7 @@
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
AssetSource, Hsla, IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString,
|
||||
Styled as _, Window, div, px, svg,
|
||||
Styled as _, Transformation, Window, div, px, svg,
|
||||
};
|
||||
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
@@ -34,6 +34,7 @@ pub enum IconRole {
|
||||
WindowMinimize,
|
||||
WindowMaximize,
|
||||
WindowRestore,
|
||||
Loader,
|
||||
}
|
||||
|
||||
impl IconRole {
|
||||
@@ -58,6 +59,7 @@ impl IconRole {
|
||||
Self::WindowMinimize,
|
||||
Self::WindowMaximize,
|
||||
Self::WindowRestore,
|
||||
Self::Loader,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -82,6 +84,7 @@ impl IconRole {
|
||||
Self::WindowMinimize => "icons/minus.svg",
|
||||
Self::WindowMaximize => "icons/square.svg",
|
||||
Self::WindowRestore => "icons/copy.svg",
|
||||
Self::Loader => "icons/loader.svg",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +109,7 @@ impl IconRole {
|
||||
Self::WindowMinimize => "window-minimize",
|
||||
Self::WindowMaximize => "window-maximize",
|
||||
Self::WindowRestore => "window-restore",
|
||||
Self::Loader => "loader",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +184,7 @@ impl IconRole {
|
||||
Self::WindowMinimize => &["window-minimize-symbolic", "window-minimize"],
|
||||
Self::WindowMaximize => &["window-maximize-symbolic", "window-maximize"],
|
||||
Self::WindowRestore => &["window-restore-symbolic", "window-restore"],
|
||||
Self::Loader => &["process-working-symbolic", "process-working"],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +195,7 @@ pub struct Icon {
|
||||
role: IconRole,
|
||||
size: Pixels,
|
||||
color: Option<Hsla>,
|
||||
transformation: Option<Transformation>,
|
||||
}
|
||||
|
||||
/// Creates an icon for the role at the default size of 16px.
|
||||
@@ -198,6 +204,7 @@ pub fn icon(role: IconRole) -> Icon {
|
||||
role,
|
||||
size: px(16.0),
|
||||
color: None,
|
||||
transformation: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +219,12 @@ impl Icon {
|
||||
self
|
||||
}
|
||||
|
||||
/// An extra SVG transformation, e.g. a spinner's rotation.
|
||||
pub fn with_transformation(mut self, transformation: Transformation) -> Self {
|
||||
self.transformation = Some(transformation);
|
||||
self
|
||||
}
|
||||
|
||||
/// The asset key a resolved freedesktop file is served under.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn freedesktop_asset_path(&self) -> Option<SharedString> {
|
||||
@@ -232,27 +245,29 @@ impl RenderOnce for Icon {
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
/// Windows: the Segoe Fluent codepoint drawn as text in the system font.
|
||||
/// Windows: the Segoe Fluent codepoint drawn as text in the system font;
|
||||
/// roles without a codepoint fall back to the bundled SVG.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn render_glyph(self) -> impl IntoElement {
|
||||
div()
|
||||
.flex_none()
|
||||
.size(self.size)
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.when_some(segoe::glyph(self.role), |el, glyph| {
|
||||
el.child(
|
||||
match segoe::glyph(self.role) {
|
||||
Some(glyph) => div()
|
||||
.flex_none()
|
||||
.size(self.size)
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.child(
|
||||
div()
|
||||
.font_family(segoe::FONT_FAMILY)
|
||||
.text_size(self.size)
|
||||
.when_some(self.color, |el, color| el.text_color(color))
|
||||
.child(glyph.to_string()),
|
||||
)
|
||||
})
|
||||
.into_any_element(),
|
||||
None => self.render_svg().into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn render_svg(self) -> impl IntoElement {
|
||||
#[cfg(target_os = "linux")]
|
||||
let path: SharedString = self
|
||||
@@ -266,7 +281,10 @@ impl Icon {
|
||||
svg()
|
||||
.size_full()
|
||||
.path(path)
|
||||
.when_some(self.color, |el, color| el.text_color(color)),
|
||||
.when_some(self.color, |el, color| el.text_color(color))
|
||||
.when_some(self.transformation, |el, transformation| {
|
||||
el.with_transformation(transformation)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -294,6 +312,7 @@ fn asset_bytes(path: &str) -> Option<&'static [u8]> {
|
||||
"icons/eye-off.svg" => Some(include_bytes!("../assets/eye-off.svg")),
|
||||
"icons/panel-left.svg" => Some(include_bytes!("../assets/panel-left.svg")),
|
||||
"icons/panel-right.svg" => Some(include_bytes!("../assets/panel-right.svg")),
|
||||
"icons/loader.svg" => Some(include_bytes!("../assets/loader.svg")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ use crate::IconRole;
|
||||
/// The Segoe Fluent Icons codepoint for the role.
|
||||
pub(crate) fn glyph(role: IconRole) -> Option<char> {
|
||||
let codepoint = match role {
|
||||
// No Segoe spinner glyph; the icon falls back to the bundled SVG,
|
||||
// which the spinner rotates.
|
||||
IconRole::Loader => return None,
|
||||
IconRole::Check => 0xE73E, // CheckMark
|
||||
IconRole::Close => 0xE8BB, // ChromeClose
|
||||
IconRole::ChevronDown => 0xE70D, // ChevronDown
|
||||
@@ -37,9 +40,12 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn every_role_maps_to_a_codepoint() {
|
||||
fn every_role_maps_a_codepoint_or_falls_back_to_the_bundled_svg() {
|
||||
for role in crate::IconRole::all() {
|
||||
assert!(glyph(*role).is_some(), "missing codepoint for {role:?}");
|
||||
assert!(
|
||||
glyph(*role).is_some() || crate::asset_bytes(role.asset_path()).is_some(),
|
||||
"no Segoe codepoint or bundled asset for {role:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user