Mainline Icon and IconButton changes (#3022)

This PR mainlines the `Icon` and `IconButton` changes from the
`gpui2-ui` branch.

Release Notes:

- N/A

Co-authored-by: Nate Butler <nate@zed.dev>
This commit is contained in:
Marshall Bowers 2023-09-22 19:14:12 -04:00 committed by GitHub
parent ad62a966a6
commit 895386cfaf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 49 deletions

View file

@ -2,7 +2,7 @@ use gpui2::elements::div;
use gpui2::style::StyleHelpers; use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext}; use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::{breadcrumb, icon_button, theme}; use crate::{breadcrumb, theme, IconAsset, IconButton};
pub struct ToolbarItem {} pub struct ToolbarItem {}
@ -27,9 +27,9 @@ impl Toolbar {
.child( .child(
div() div()
.flex() .flex()
.child(icon_button("icons/inlay_hint.svg")) .child(IconButton::new(IconAsset::InlayHint))
.child(icon_button("icons/magnifying_glass.svg")) .child(IconButton::new(IconAsset::MagnifyingGlass))
.child(icon_button("icons/magic-wand.svg")), .child(IconButton::new(IconAsset::MagicWand)),
) )
} }
} }

View file

@ -1,8 +1,41 @@
use std::sync::Arc;
use crate::theme::theme; use crate::theme::theme;
use crate::Theme;
use gpui2::elements::svg; use gpui2::elements::svg;
use gpui2::style::StyleHelpers; use gpui2::style::StyleHelpers;
use gpui2::IntoElement;
use gpui2::{Element, ViewContext}; use gpui2::{Element, ViewContext};
use gpui2::{Hsla, IntoElement};
#[derive(Default, PartialEq, Copy, Clone)]
pub enum IconColor {
#[default]
Default,
Muted,
Disabled,
Placeholder,
Accent,
Error,
Warning,
Success,
Info,
}
impl IconColor {
pub fn color(self, theme: Arc<Theme>) -> Hsla {
match self {
IconColor::Default => theme.lowest.base.default.foreground,
IconColor::Muted => theme.lowest.variant.default.foreground,
IconColor::Disabled => theme.lowest.base.disabled.foreground,
IconColor::Placeholder => theme.lowest.base.disabled.foreground,
IconColor::Accent => theme.lowest.accent.default.foreground,
IconColor::Error => theme.lowest.negative.default.foreground,
IconColor::Warning => theme.lowest.warning.default.foreground,
IconColor::Success => theme.lowest.positive.default.foreground,
IconColor::Info => theme.lowest.accent.default.foreground,
}
}
}
#[derive(Default, PartialEq, Copy, Clone)] #[derive(Default, PartialEq, Copy, Clone)]
pub enum IconAsset { pub enum IconAsset {
@ -10,21 +43,40 @@ pub enum IconAsset {
ArrowLeft, ArrowLeft,
ArrowRight, ArrowRight,
ArrowUpRight, ArrowUpRight,
AudioOff,
AudioOn,
Bolt, Bolt,
ChevronDown, ChevronDown,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
ChevronUp, ChevronUp,
#[default] Close,
ExclamationTriangle,
File, File,
FileDoc, FileDoc,
FileGit, FileGit,
FileLock, FileLock,
FileRust, FileRust,
FileToml, FileToml,
FileTree,
Folder, Folder,
FolderOpen, FolderOpen,
FolderX,
#[default]
Hash, Hash,
InlayHint,
MagicWand,
MagnifyingGlass,
MessageBubbles,
Mic,
MicMute,
Plus,
Screen,
Split,
Terminal,
XCircle,
Copilot,
Envelope,
} }
impl IconAsset { impl IconAsset {
@ -34,20 +86,39 @@ impl IconAsset {
IconAsset::ArrowLeft => "icons/arrow_left.svg", IconAsset::ArrowLeft => "icons/arrow_left.svg",
IconAsset::ArrowRight => "icons/arrow_right.svg", IconAsset::ArrowRight => "icons/arrow_right.svg",
IconAsset::ArrowUpRight => "icons/arrow_up_right.svg", IconAsset::ArrowUpRight => "icons/arrow_up_right.svg",
IconAsset::AudioOff => "icons/speaker-off.svg",
IconAsset::AudioOn => "icons/speaker-loud.svg",
IconAsset::Bolt => "icons/bolt.svg", IconAsset::Bolt => "icons/bolt.svg",
IconAsset::ChevronDown => "icons/chevron_down.svg", IconAsset::ChevronDown => "icons/chevron_down.svg",
IconAsset::ChevronLeft => "icons/chevron_left.svg", IconAsset::ChevronLeft => "icons/chevron_left.svg",
IconAsset::ChevronRight => "icons/chevron_right.svg", IconAsset::ChevronRight => "icons/chevron_right.svg",
IconAsset::ChevronUp => "icons/chevron_up.svg", IconAsset::ChevronUp => "icons/chevron_up.svg",
IconAsset::Close => "icons/x.svg",
IconAsset::ExclamationTriangle => "icons/warning.svg",
IconAsset::File => "icons/file_icons/file.svg", IconAsset::File => "icons/file_icons/file.svg",
IconAsset::FileDoc => "icons/file_icons/book.svg", IconAsset::FileDoc => "icons/file_icons/book.svg",
IconAsset::FileGit => "icons/file_icons/git.svg", IconAsset::FileGit => "icons/file_icons/git.svg",
IconAsset::FileLock => "icons/file_icons/lock.svg", IconAsset::FileLock => "icons/file_icons/lock.svg",
IconAsset::FileRust => "icons/file_icons/rust.svg", IconAsset::FileRust => "icons/file_icons/rust.svg",
IconAsset::FileToml => "icons/file_icons/toml.svg", IconAsset::FileToml => "icons/file_icons/toml.svg",
IconAsset::FileTree => "icons/project.svg",
IconAsset::Folder => "icons/file_icons/folder.svg", IconAsset::Folder => "icons/file_icons/folder.svg",
IconAsset::FolderOpen => "icons/file_icons/folder_open.svg", IconAsset::FolderOpen => "icons/file_icons/folder_open.svg",
IconAsset::FolderX => "icons/stop_sharing.svg",
IconAsset::Hash => "icons/hash.svg", IconAsset::Hash => "icons/hash.svg",
IconAsset::InlayHint => "icons/inlay_hint.svg",
IconAsset::MagicWand => "icons/magic-wand.svg",
IconAsset::MagnifyingGlass => "icons/magnifying_glass.svg",
IconAsset::MessageBubbles => "icons/conversations.svg",
IconAsset::Mic => "icons/mic.svg",
IconAsset::MicMute => "icons/mic-mute.svg",
IconAsset::Plus => "icons/plus.svg",
IconAsset::Screen => "icons/desktop.svg",
IconAsset::Split => "icons/split.svg",
IconAsset::Terminal => "icons/terminal.svg",
IconAsset::XCircle => "icons/error.svg",
IconAsset::Copilot => "icons/copilot.svg",
IconAsset::Envelope => "icons/feedback.svg",
} }
} }
} }
@ -55,20 +126,30 @@ impl IconAsset {
#[derive(Element, Clone)] #[derive(Element, Clone)]
pub struct Icon { pub struct Icon {
asset: IconAsset, asset: IconAsset,
color: IconColor,
} }
pub fn icon(asset: IconAsset) -> Icon { pub fn icon(asset: IconAsset) -> Icon {
Icon { asset } Icon {
asset,
color: IconColor::default(),
}
} }
impl Icon { impl Icon {
pub fn color(mut self, color: IconColor) -> Self {
self.color = color;
self
}
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> { fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx); let theme = theme(cx);
let fill = self.color.color(theme);
svg() svg()
.flex_none() .flex_none()
.path(self.asset.path()) .path(self.asset.path())
.size_4() .size_4()
.fill(theme.lowest.variant.default.foreground) .fill(fill)
} }
} }

View file

@ -1,26 +1,47 @@
use gpui2::elements::{div, svg}; use gpui2::elements::div;
use gpui2::style::{StyleHelpers, Styleable}; use gpui2::style::{StyleHelpers, Styleable};
use gpui2::{Element, IntoElement, ParentElement, ViewContext}; use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::prelude::*; use crate::{icon, theme, IconColor};
use crate::theme; use crate::{prelude::*, IconAsset};
#[derive(Element)] #[derive(Element)]
pub struct IconButton { pub struct IconButton {
path: &'static str, icon: IconAsset,
color: IconColor,
variant: ButtonVariant, variant: ButtonVariant,
state: InteractionState, state: InteractionState,
} }
pub fn icon_button(path: &'static str) -> IconButton { pub fn icon_button() -> IconButton {
IconButton { IconButton {
path, icon: IconAsset::default(),
color: IconColor::default(),
variant: ButtonVariant::default(), variant: ButtonVariant::default(),
state: InteractionState::default(), state: InteractionState::default(),
} }
} }
impl IconButton { impl IconButton {
pub fn new(icon: IconAsset) -> Self {
Self {
icon,
color: IconColor::default(),
variant: ButtonVariant::default(),
state: InteractionState::default(),
}
}
pub fn icon(mut self, icon: IconAsset) -> Self {
self.icon = icon;
self
}
pub fn color(mut self, color: IconColor) -> Self {
self.color = color;
self
}
pub fn variant(mut self, variant: ButtonVariant) -> Self { pub fn variant(mut self, variant: ButtonVariant) -> Self {
self.variant = variant; self.variant = variant;
self self
@ -34,13 +55,10 @@ impl IconButton {
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> { fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx); let theme = theme(cx);
let icon_color; let icon_color = match (self.state, self.color) {
(InteractionState::Disabled, _) => IconColor::Disabled,
if self.state == InteractionState::Disabled { _ => self.color,
icon_color = theme.highest.base.disabled.foreground; };
} else {
icon_color = theme.highest.base.default.foreground;
}
let mut div = div(); let mut div = div();
if self.variant == ButtonVariant::Filled { if self.variant == ButtonVariant::Filled {
@ -57,6 +75,6 @@ impl IconButton {
.fill(theme.highest.base.hovered.background) .fill(theme.highest.base.hovered.background)
.active() .active()
.fill(theme.highest.base.pressed.background) .fill(theme.highest.base.pressed.background)
.child(svg().path(self.path).w_4().h_4().fill(icon_color)) .child(icon(self.icon).color(icon_color))
} }
} }

View file

@ -1,12 +1,13 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::icon_button;
use crate::theme::theme;
use gpui2::elements::div::ScrollState; use gpui2::elements::div::ScrollState;
use gpui2::style::StyleHelpers; use gpui2::style::StyleHelpers;
use gpui2::{elements::div, IntoElement}; use gpui2::{elements::div, IntoElement};
use gpui2::{Element, ParentElement, ViewContext}; use gpui2::{Element, ParentElement, ViewContext};
use crate::theme::theme;
use crate::{icon_button, IconAsset};
#[derive(Element)] #[derive(Element)]
pub struct ChatPanel<V: 'static> { pub struct ChatPanel<V: 'static> {
view_type: PhantomData<V>, view_type: PhantomData<V>,
@ -57,8 +58,8 @@ impl<V: 'static> ChatPanel<V> {
.flex() .flex()
.items_center() .items_center()
.gap_px() .gap_px()
.child(icon_button("icons/plus.svg")) .child(icon_button().icon(IconAsset::Plus))
.child(icon_button("icons/split.svg")), .child(icon_button().icon(IconAsset::Split)),
), ),
) )
} }

View file

@ -1,11 +1,12 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::theme::{theme, Theme};
use crate::{icon_button, text_button, tool_divider};
use gpui2::style::StyleHelpers; use gpui2::style::StyleHelpers;
use gpui2::{elements::div, IntoElement}; use gpui2::{elements::div, IntoElement};
use gpui2::{Element, ParentElement, ViewContext}; use gpui2::{Element, ParentElement, ViewContext};
use crate::theme::{theme, Theme};
use crate::{icon_button, text_button, tool_divider, IconAsset};
#[derive(Default, PartialEq)] #[derive(Default, PartialEq)]
pub enum Tool { pub enum Tool {
#[default] #[default]
@ -105,10 +106,10 @@ impl<V: 'static> StatusBar<V> {
.flex() .flex()
.items_center() .items_center()
.gap_1() .gap_1()
.child(icon_button("icons/project.svg")) .child(icon_button().icon(IconAsset::FileTree))
.child(icon_button("icons/hash.svg")) .child(icon_button().icon(IconAsset::Hash))
.child(tool_divider()) .child(tool_divider())
.child(icon_button("icons/error.svg")) .child(icon_button().icon(IconAsset::XCircle))
} }
fn right_tools(&self, theme: &Theme) -> impl Element<V> { fn right_tools(&self, theme: &Theme) -> impl Element<V> {
div() div()
@ -129,8 +130,8 @@ impl<V: 'static> StatusBar<V> {
.flex() .flex()
.items_center() .items_center()
.gap_1() .gap_1()
.child(icon_button("icons/copilot.svg")) .child(icon_button().icon(IconAsset::Copilot))
.child(icon_button("icons/feedback.svg")), .child(icon_button().icon(IconAsset::Envelope)),
) )
.child(tool_divider()) .child(tool_divider())
.child( .child(
@ -138,9 +139,9 @@ impl<V: 'static> StatusBar<V> {
.flex() .flex()
.items_center() .items_center()
.gap_1() .gap_1()
.child(icon_button("icons/terminal.svg")) .child(icon_button().icon(IconAsset::Terminal))
.child(icon_button("icons/conversations.svg")) .child(icon_button().icon(IconAsset::MessageBubbles))
.child(icon_button("icons/ai.svg")), .child(icon_button().icon(IconAsset::Ai)),
) )
} }
} }

View file

@ -1,13 +1,14 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::prelude::InteractionState;
use crate::theme::theme;
use crate::{icon_button, tab};
use gpui2::elements::div::ScrollState; use gpui2::elements::div::ScrollState;
use gpui2::style::StyleHelpers; use gpui2::style::StyleHelpers;
use gpui2::{elements::div, IntoElement}; use gpui2::{elements::div, IntoElement};
use gpui2::{Element, ParentElement, ViewContext}; use gpui2::{Element, ParentElement, ViewContext};
use crate::prelude::InteractionState;
use crate::theme::theme;
use crate::{icon_button, tab, IconAsset};
#[derive(Element)] #[derive(Element)]
pub struct TabBar<V: 'static> { pub struct TabBar<V: 'static> {
view_type: PhantomData<V>, view_type: PhantomData<V>,
@ -43,11 +44,12 @@ impl<V: 'static> TabBar<V> {
.items_center() .items_center()
.gap_px() .gap_px()
.child( .child(
icon_button("icons/arrow_left.svg") icon_button()
.icon(IconAsset::ArrowLeft)
.state(InteractionState::Enabled.if_enabled(can_navigate_back)), .state(InteractionState::Enabled.if_enabled(can_navigate_back)),
) )
.child( .child(
icon_button("icons/arrow_right.svg").state( icon_button().icon(IconAsset::ArrowRight).state(
InteractionState::Enabled.if_enabled(can_navigate_forward), InteractionState::Enabled.if_enabled(can_navigate_forward),
), ),
), ),
@ -83,8 +85,8 @@ impl<V: 'static> TabBar<V> {
.flex() .flex()
.items_center() .items_center()
.gap_px() .gap_px()
.child(icon_button("icons/plus.svg")) .child(icon_button().icon(IconAsset::Plus))
.child(icon_button("icons/split.svg")), .child(icon_button().icon(IconAsset::Split)),
), ),
) )
} }

View file

@ -5,7 +5,10 @@ use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext}; use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::prelude::Shape; use crate::prelude::Shape;
use crate::{avatar, follow_group, icon_button, text_button, theme, tool_divider, traffic_lights}; use crate::{
avatar, follow_group, icon_button, text_button, theme, tool_divider, traffic_lights, IconAsset,
IconColor,
};
#[derive(Element)] #[derive(Element)]
pub struct TitleBar<V: 'static> { pub struct TitleBar<V: 'static> {
@ -65,8 +68,8 @@ impl<V: 'static> TitleBar<V> {
.flex() .flex()
.items_center() .items_center()
.gap_1() .gap_1()
.child(icon_button("icons/stop_sharing.svg")) .child(icon_button().icon(IconAsset::FolderX))
.child(icon_button("icons/exit.svg")), .child(icon_button().icon(IconAsset::Close)),
) )
.child(tool_divider()) .child(tool_divider())
.child( .child(
@ -75,9 +78,13 @@ impl<V: 'static> TitleBar<V> {
.flex() .flex()
.items_center() .items_center()
.gap_1() .gap_1()
.child(icon_button("icons/mic.svg")) .child(icon_button().icon(IconAsset::Mic))
.child(icon_button("icons/speaker-loud.svg")) .child(icon_button().icon(IconAsset::AudioOn))
.child(icon_button("icons/desktop.svg")), .child(
icon_button()
.icon(IconAsset::Screen)
.color(IconColor::Accent),
),
) )
.child( .child(
div().px_2().flex().items_center().child( div().px_2().flex().items_center().child(