Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use warp_core::ui::Icon;
|
||||
use warp_core::ui::appearance::Appearance;
|
||||
use warpui::{fonts, keymap::Keystroke, prelude::stack};
|
||||
|
||||
use crate::{keyboard_shortcut, tooltip};
|
||||
|
||||
use super::Theme;
|
||||
|
||||
/// The parameters for rendering a button.
|
||||
pub struct Params<'a> {
|
||||
/// The content of the button.
|
||||
pub content: Content,
|
||||
/// The theme to use for the button.
|
||||
pub theme: &'a dyn Theme,
|
||||
/// The options for the button.
|
||||
pub options: Options,
|
||||
}
|
||||
|
||||
impl<'a> crate::Params for Params<'a> {
|
||||
type Options<'b> = Options;
|
||||
}
|
||||
|
||||
pub struct Options {
|
||||
/// Whether or not the button is disabled.
|
||||
///
|
||||
/// Disabled buttons are rendered with a different theme and do not respond
|
||||
/// to mouse events.
|
||||
pub disabled: bool,
|
||||
/// The size of the button.
|
||||
pub size: Size,
|
||||
/// The tooltip to show on hover, if any.
|
||||
pub tooltip: Option<Tooltip>,
|
||||
/// The keystroke to display, if any.
|
||||
pub keystroke: Option<Keystroke>,
|
||||
/// The callback to invoke when the button is clicked, if any.
|
||||
pub on_click: Option<crate::MouseEventHandler>,
|
||||
}
|
||||
|
||||
impl crate::Options for Options {
|
||||
fn default(appearance: &Appearance) -> Self {
|
||||
Self {
|
||||
disabled: false,
|
||||
size: Size::default(appearance),
|
||||
tooltip: None,
|
||||
keystroke: None,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The content of the button.
|
||||
pub enum Content {
|
||||
/// A label-only button.
|
||||
Label(Cow<'static, str>),
|
||||
/// An icon-only button.
|
||||
Icon(Icon),
|
||||
/// A button with both an icon and a label.
|
||||
IconAndLabel(Icon, Cow<'static, str>),
|
||||
}
|
||||
|
||||
/// The size of the button.
|
||||
pub enum Size {
|
||||
Default,
|
||||
Small,
|
||||
Custom(Sizing),
|
||||
}
|
||||
|
||||
impl crate::Options for Size {
|
||||
fn default(_: &Appearance) -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub(super) fn height(&self) -> f32 {
|
||||
self.sizing().height
|
||||
}
|
||||
|
||||
pub(super) fn icon_size(&self) -> f32 {
|
||||
self.sizing().icon_size
|
||||
}
|
||||
|
||||
pub(super) fn font_size(&self) -> f32 {
|
||||
self.sizing().font_size
|
||||
}
|
||||
|
||||
pub(super) fn font_properties(&self) -> fonts::Properties {
|
||||
self.sizing().font_properties
|
||||
}
|
||||
|
||||
pub(super) fn horizontal_padding(&self) -> f32 {
|
||||
self.sizing().horizontal_padding
|
||||
}
|
||||
|
||||
pub(super) fn inner_spacing(&self) -> f32 {
|
||||
self.sizing().inner_spacing
|
||||
}
|
||||
|
||||
pub(super) fn keyboard_shortcut_sizing(&self) -> keyboard_shortcut::Sizing {
|
||||
self.sizing().keyboard_shortcut_sizing
|
||||
}
|
||||
|
||||
fn sizing(&self) -> &Sizing {
|
||||
match self {
|
||||
Size::Default => &DEFAULT_SIZE,
|
||||
Size::Small => &SMALL_SIZE,
|
||||
Size::Custom(custom) => custom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of properties that vary with button size.
|
||||
pub struct Sizing {
|
||||
pub height: f32,
|
||||
pub font_size: f32,
|
||||
pub icon_size: f32,
|
||||
pub font_properties: fonts::Properties,
|
||||
pub horizontal_padding: f32,
|
||||
pub inner_spacing: f32,
|
||||
pub keyboard_shortcut_sizing: keyboard_shortcut::Sizing,
|
||||
}
|
||||
|
||||
/// Sizing for a default-sized button.
|
||||
const DEFAULT_SIZE: Sizing = Sizing {
|
||||
height: 32.,
|
||||
font_size: 14.,
|
||||
icon_size: 16.,
|
||||
font_properties: fonts::Properties {
|
||||
weight: fonts::Weight::Semibold,
|
||||
style: fonts::Style::Normal,
|
||||
},
|
||||
horizontal_padding: 12.,
|
||||
inner_spacing: 4.,
|
||||
keyboard_shortcut_sizing: keyboard_shortcut::Sizing {
|
||||
font_size: 12.,
|
||||
padding: 2.,
|
||||
},
|
||||
};
|
||||
|
||||
/// Sizing for a small-sized button.
|
||||
const SMALL_SIZE: Sizing = Sizing {
|
||||
height: 24.,
|
||||
font_size: 12.,
|
||||
icon_size: 14.,
|
||||
font_properties: fonts::Properties {
|
||||
weight: fonts::Weight::Semibold,
|
||||
style: fonts::Style::Normal,
|
||||
},
|
||||
horizontal_padding: 8.,
|
||||
inner_spacing: 2.,
|
||||
keyboard_shortcut_sizing: keyboard_shortcut::Sizing {
|
||||
font_size: 10.,
|
||||
padding: 2.,
|
||||
},
|
||||
};
|
||||
|
||||
/// The tooltip to show on hover, if any.
|
||||
pub struct Tooltip {
|
||||
pub params: tooltip::Params,
|
||||
pub alignment: TooltipAlignment,
|
||||
}
|
||||
|
||||
/// Alignment options for button tooltips.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum TooltipAlignment {
|
||||
Left,
|
||||
Center,
|
||||
#[default]
|
||||
Right,
|
||||
}
|
||||
|
||||
impl TooltipAlignment {
|
||||
pub fn parent_anchor(&self) -> stack::ParentAnchor {
|
||||
match self {
|
||||
Self::Left => stack::ParentAnchor::TopLeft,
|
||||
Self::Center => stack::ParentAnchor::TopMiddle,
|
||||
Self::Right => stack::ParentAnchor::TopRight,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn child_anchor(&self) -> stack::ChildAnchor {
|
||||
match self {
|
||||
Self::Left => stack::ChildAnchor::BottomLeft,
|
||||
Self::Center => stack::ChildAnchor::BottomMiddle,
|
||||
Self::Right => stack::ChildAnchor::BottomRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
use pathfinder_color::ColorU;
|
||||
use warp_core::ui::{
|
||||
appearance::Appearance, color::coloru_with_opacity, theme::Fill, theme::color::internal_colors,
|
||||
};
|
||||
|
||||
/// Theming delegate for a button.
|
||||
pub trait Theme {
|
||||
/// The background fill for the button.
|
||||
fn background(&self, button_state: super::State, appearance: &Appearance) -> Option<Fill>;
|
||||
|
||||
/// The color to use for text and icons, given the current background color.
|
||||
fn text_color(&self, background: Option<Fill>, appearance: &Appearance) -> ColorU;
|
||||
|
||||
/// The border color for the button, if any.
|
||||
fn border(&self, _: &Appearance) -> Option<ColorU> {
|
||||
None
|
||||
}
|
||||
|
||||
/// The border color for the keyboard shortcut, if any.
|
||||
fn keyboard_shortcut_border(&self, _text_color: ColorU, _: &Appearance) -> Option<ColorU> {
|
||||
None
|
||||
}
|
||||
|
||||
/// The background color for the keyboard shortcut, if any.
|
||||
fn keyboard_shortcut_background(&self, _: &Appearance) -> Option<ColorU> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// "Primary" buttons have a colorful fill.
|
||||
///
|
||||
/// [Figma spec](https://www.figma.com/design/chk9pwt35jTJhf9KnHmZyE/Components?node-id=3628-14344&t=GRYXipD0INVmDupA-0)
|
||||
pub struct Primary;
|
||||
|
||||
impl Theme for Primary {
|
||||
fn background(&self, button_state: super::State, appearance: &Appearance) -> Option<Fill> {
|
||||
match button_state {
|
||||
super::State::Default => Some(appearance.theme().accent()),
|
||||
super::State::Hovered => Some(internal_colors::accent_overlay_4(appearance.theme())),
|
||||
super::State::Pressed => Some(internal_colors::accent_overlay_3(appearance.theme())),
|
||||
}
|
||||
}
|
||||
|
||||
fn text_color(&self, background: Option<Fill>, appearance: &Appearance) -> ColorU {
|
||||
let theme = appearance.theme();
|
||||
let bg = background.unwrap_or_else(|| theme.accent()).into_solid();
|
||||
theme.font_color(bg).into_solid()
|
||||
}
|
||||
|
||||
fn keyboard_shortcut_border(&self, text_color: ColorU, _: &Appearance) -> Option<ColorU> {
|
||||
Some(coloru_with_opacity(text_color, 60))
|
||||
}
|
||||
}
|
||||
|
||||
/// "Secondary" buttons have no fill and a border.
|
||||
///
|
||||
/// [Figma spec](https://www.figma.com/design/chk9pwt35jTJhf9KnHmZyE/Components?node-id=3628-14344&t=L1sS5Nxu1zzpWPYp-0)
|
||||
pub struct Secondary;
|
||||
|
||||
impl Theme for Secondary {
|
||||
fn background(&self, button_state: super::State, appearance: &Appearance) -> Option<Fill> {
|
||||
match button_state {
|
||||
super::State::Default => None,
|
||||
super::State::Hovered => Some(internal_colors::fg_overlay_2(appearance.theme())),
|
||||
super::State::Pressed => Some(internal_colors::fg_overlay_3(appearance.theme())),
|
||||
}
|
||||
}
|
||||
|
||||
fn text_color(&self, _background: Option<Fill>, appearance: &Appearance) -> ColorU {
|
||||
appearance.theme().foreground().into()
|
||||
}
|
||||
|
||||
fn border(&self, appearance: &Appearance) -> Option<ColorU> {
|
||||
Some(internal_colors::neutral_4(appearance.theme()))
|
||||
}
|
||||
|
||||
fn keyboard_shortcut_background(&self, appearance: &Appearance) -> Option<ColorU> {
|
||||
Some(internal_colors::neutral_3(appearance.theme()))
|
||||
}
|
||||
}
|
||||
|
||||
/// "Disabled" buttons have a disabled fill and text color.
|
||||
///
|
||||
/// [Figma spec](https://www.figma.com/design/chk9pwt35jTJhf9KnHmZyE/Components?node-id=3628-14344&t=c27DwGHWevMlisVN-0)
|
||||
pub struct Disabled;
|
||||
|
||||
impl Theme for Disabled {
|
||||
fn background(&self, _button_state: super::State, _: &Appearance) -> Option<Fill> {
|
||||
None
|
||||
}
|
||||
|
||||
fn text_color(&self, _background: Option<Fill>, appearance: &Appearance) -> ColorU {
|
||||
internal_colors::neutral_5(appearance.theme())
|
||||
}
|
||||
|
||||
fn keyboard_shortcut_background(&self, appearance: &Appearance) -> Option<ColorU> {
|
||||
Some(internal_colors::neutral_3(appearance.theme()))
|
||||
}
|
||||
}
|
||||
|
||||
/// "Naked" buttons have no fill or border by default, only their contents.
|
||||
///
|
||||
/// This is typically used for link-like or text-style actions.
|
||||
pub struct Naked;
|
||||
|
||||
impl Theme for Naked {
|
||||
fn background(&self, button_state: super::State, appearance: &Appearance) -> Option<Fill> {
|
||||
match button_state {
|
||||
super::State::Default => None,
|
||||
super::State::Hovered => Some(internal_colors::fg_overlay_2(appearance.theme())),
|
||||
super::State::Pressed => Some(internal_colors::fg_overlay_3(appearance.theme())),
|
||||
}
|
||||
}
|
||||
|
||||
fn text_color(&self, _background: Option<Fill>, appearance: &Appearance) -> ColorU {
|
||||
appearance.theme().foreground().into_solid()
|
||||
}
|
||||
|
||||
fn keyboard_shortcut_background(&self, appearance: &Appearance) -> Option<ColorU> {
|
||||
Some(internal_colors::neutral_3(appearance.theme()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user