chore: revamp theme
This commit is contained in:
@@ -1,35 +1,23 @@
|
||||
use crate::{
|
||||
indicator::Indicator,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
tooltip::Tooltip,
|
||||
Disableable, Icon, Selectable, Sizable, Size, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, relative, AnyElement, App, ClickEvent, Corners, Div,
|
||||
Edges, ElementId, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
|
||||
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, Window,
|
||||
div, prelude::FluentBuilder as _, relative, AnyElement, App, ClickEvent, Div, ElementId, Hsla,
|
||||
InteractiveElement, IntoElement, MouseButton, ParentElement, RenderOnce, SharedString,
|
||||
StatefulInteractiveElement as _, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
indicator::Indicator, tooltip::Tooltip, Disableable, Icon, Selectable, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
pub enum ButtonRounded {
|
||||
None,
|
||||
Small,
|
||||
Medium,
|
||||
Large,
|
||||
Size(Pixels),
|
||||
}
|
||||
|
||||
impl From<Pixels> for ButtonRounded {
|
||||
fn from(px: Pixels) -> Self {
|
||||
ButtonRounded::Size(px)
|
||||
}
|
||||
Normal,
|
||||
Full,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ButtonCustomVariant {
|
||||
color: Hsla,
|
||||
foreground: Hsla,
|
||||
border: Hsla,
|
||||
shadow: bool,
|
||||
hover: Hsla,
|
||||
active: Hsla,
|
||||
}
|
||||
@@ -66,12 +54,10 @@ pub trait ButtonVariants: Sized {
|
||||
impl ButtonCustomVariant {
|
||||
pub fn new(_window: &Window, cx: &App) -> Self {
|
||||
Self {
|
||||
color: cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
foreground: cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
border: cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
hover: cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
active: cx.theme().accent.step(cx, ColorScaleStep::ELEVEN),
|
||||
shadow: true,
|
||||
color: cx.theme().element_background,
|
||||
foreground: cx.theme().element_foreground,
|
||||
hover: cx.theme().element_hover,
|
||||
active: cx.theme().element_active,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +71,6 @@ impl ButtonCustomVariant {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border(mut self, color: Hsla) -> Self {
|
||||
self.border = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn hover(mut self, color: Hsla) -> Self {
|
||||
self.hover = color;
|
||||
self
|
||||
@@ -99,11 +80,6 @@ impl ButtonCustomVariant {
|
||||
self.active = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn shadow(mut self, shadow: bool) -> Self {
|
||||
self.shadow = shadow;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The variant of the Button.
|
||||
@@ -149,12 +125,9 @@ pub struct Button {
|
||||
disabled: bool,
|
||||
variant: ButtonVariant,
|
||||
rounded: ButtonRounded,
|
||||
border_corners: Corners<bool>,
|
||||
border_edges: Edges<bool>,
|
||||
size: Size,
|
||||
reverse: bool,
|
||||
bold: bool,
|
||||
centered: bool,
|
||||
tooltip: Option<SharedString>,
|
||||
on_click: OnClick,
|
||||
loading: bool,
|
||||
@@ -179,9 +152,7 @@ impl Button {
|
||||
disabled: false,
|
||||
selected: false,
|
||||
variant: ButtonVariant::default(),
|
||||
rounded: ButtonRounded::Medium,
|
||||
border_corners: Corners::all(true),
|
||||
border_edges: Edges::all(true),
|
||||
rounded: ButtonRounded::Normal,
|
||||
size: Size::Medium,
|
||||
tooltip: None,
|
||||
on_click: None,
|
||||
@@ -189,7 +160,6 @@ impl Button {
|
||||
loading: false,
|
||||
reverse: false,
|
||||
bold: false,
|
||||
centered: true,
|
||||
children: Vec::new(),
|
||||
loading_icon: None,
|
||||
}
|
||||
@@ -201,18 +171,6 @@ impl Button {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the border corners side of the Button.
|
||||
pub(crate) fn border_corners(mut self, corners: impl Into<Corners<bool>>) -> Self {
|
||||
self.border_corners = corners.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the border edges of the Button.
|
||||
pub(crate) fn border_edges(mut self, edges: impl Into<Edges<bool>>) -> Self {
|
||||
self.border_edges = edges.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set label to the Button, if no label is set, the button will be in Icon Button mode.
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
@@ -243,11 +201,6 @@ impl Button {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn not_centered(mut self) -> Self {
|
||||
self.centered = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bold(mut self) -> Self {
|
||||
self.bold = true;
|
||||
self
|
||||
@@ -335,11 +288,12 @@ impl RenderOnce for Button {
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.items_center()
|
||||
.when(self.centered, |this| this.justify_center())
|
||||
.justify_center()
|
||||
.cursor_pointer()
|
||||
.overflow_hidden()
|
||||
.when(cx.theme().shadow && normal_style.shadow, |this| {
|
||||
this.shadow_sm()
|
||||
.map(|this| match self.rounded {
|
||||
ButtonRounded::Normal => this.rounded(cx.theme().radius),
|
||||
ButtonRounded::Full => this.rounded_full(),
|
||||
})
|
||||
.when(!style.no_padding(), |this| {
|
||||
if self.label.is_none() && self.children.is_empty() {
|
||||
@@ -361,50 +315,20 @@ impl RenderOnce for Button {
|
||||
}
|
||||
}
|
||||
})
|
||||
.when(
|
||||
self.border_corners.top_left && self.border_corners.bottom_left,
|
||||
|this| match self.rounded {
|
||||
ButtonRounded::Small => this.rounded_l(px(cx.theme().radius * 0.5)),
|
||||
ButtonRounded::Medium => this.rounded_l(px(cx.theme().radius)),
|
||||
ButtonRounded::Large => this.rounded_l(px(cx.theme().radius * 1.6)),
|
||||
ButtonRounded::Size(px) => this.rounded_l(px),
|
||||
ButtonRounded::None => this.rounded_none(),
|
||||
},
|
||||
)
|
||||
.when(
|
||||
self.border_corners.top_right && self.border_corners.bottom_right,
|
||||
|this| match self.rounded {
|
||||
ButtonRounded::Small => this.rounded_r(px(cx.theme().radius * 0.5)),
|
||||
ButtonRounded::Medium => this.rounded_r(px(cx.theme().radius)),
|
||||
ButtonRounded::Large => this.rounded_r(px(cx.theme().radius * 1.6)),
|
||||
ButtonRounded::Size(px) => this.rounded_r(px),
|
||||
ButtonRounded::None => this.rounded_none(),
|
||||
},
|
||||
)
|
||||
.when(self.border_edges.left, |this| this.border_l_1())
|
||||
.when(self.border_edges.right, |this| this.border_r_1())
|
||||
.when(self.border_edges.top, |this| this.border_t_1())
|
||||
.when(self.border_edges.bottom, |this| this.border_b_1())
|
||||
.text_color(normal_style.fg)
|
||||
.when(self.selected, |this| {
|
||||
let selected_style = style.selected(window, cx);
|
||||
this.bg(selected_style.bg)
|
||||
.border_color(selected_style.border)
|
||||
.text_color(selected_style.fg)
|
||||
this.bg(selected_style.bg).text_color(selected_style.fg)
|
||||
})
|
||||
.when(!self.disabled && !self.selected, |this| {
|
||||
this.border_color(normal_style.border)
|
||||
.bg(normal_style.bg)
|
||||
this.bg(normal_style.bg)
|
||||
.when(normal_style.underline, |this| this.text_decoration_1())
|
||||
.hover(|this| {
|
||||
let hover_style = style.hovered(window, cx);
|
||||
this.bg(hover_style.bg).border_color(hover_style.border)
|
||||
this.bg(hover_style.bg)
|
||||
})
|
||||
.active(|this| {
|
||||
let active_style = style.active(window, cx);
|
||||
this.bg(active_style.bg)
|
||||
.border_color(active_style.border)
|
||||
.text_color(active_style.fg)
|
||||
this.bg(active_style.bg).text_color(active_style.fg)
|
||||
})
|
||||
})
|
||||
.when_some(
|
||||
@@ -427,9 +351,9 @@ impl RenderOnce for Button {
|
||||
this.cursor_not_allowed()
|
||||
.bg(disabled_style.bg)
|
||||
.text_color(disabled_style.fg)
|
||||
.border_color(disabled_style.border)
|
||||
.shadow_none()
|
||||
})
|
||||
.text_color(normal_style.fg)
|
||||
.child({
|
||||
div()
|
||||
.flex()
|
||||
@@ -474,45 +398,26 @@ impl RenderOnce for Button {
|
||||
|
||||
struct ButtonVariantStyle {
|
||||
bg: Hsla,
|
||||
border: Hsla,
|
||||
fg: Hsla,
|
||||
underline: bool,
|
||||
shadow: bool,
|
||||
}
|
||||
|
||||
impl ButtonVariant {
|
||||
fn bg_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Primary => cx.theme().element_background,
|
||||
ButtonVariant::Custom(colors) => colors.color,
|
||||
_ => cx.theme().transparent,
|
||||
_ => cx.theme().ghost_element_background,
|
||||
}
|
||||
}
|
||||
|
||||
fn text_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
match self {
|
||||
ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() {
|
||||
"Sky" => cx.theme().base.darken(cx),
|
||||
"Mint" => cx.theme().base.darken(cx),
|
||||
"Lime" => cx.theme().base.darken(cx),
|
||||
"Amber" => cx.theme().base.darken(cx),
|
||||
"Yellow" => cx.theme().base.darken(cx),
|
||||
_ => cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
},
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
ButtonVariant::Primary => cx.theme().element_foreground,
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
ButtonVariant::Ghost => cx.theme().text_muted,
|
||||
ButtonVariant::Custom(colors) => colors.foreground,
|
||||
_ => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
}
|
||||
}
|
||||
|
||||
fn border_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Ghost | ButtonVariant::Link | ButtonVariant::Text => {
|
||||
cx.theme().transparent
|
||||
}
|
||||
ButtonVariant::Custom(colors) => colors.border,
|
||||
_ => cx.theme().text,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,142 +425,79 @@ impl ButtonVariant {
|
||||
matches!(self, ButtonVariant::Link)
|
||||
}
|
||||
|
||||
fn shadow(&self, _window: &Window, _cx: &App) -> bool {
|
||||
match self {
|
||||
ButtonVariant::Primary => true,
|
||||
ButtonVariant::Custom(c) => c.shadow,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = self.bg_color(window, cx);
|
||||
let border = self.border_color(window, cx);
|
||||
let fg = self.text_color(window, cx);
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
ButtonVariant::Link => cx.theme().transparent,
|
||||
ButtonVariant::Text => cx.theme().transparent,
|
||||
ButtonVariant::Primary => cx.theme().element_hover,
|
||||
ButtonVariant::Ghost => cx.theme().ghost_element_hover,
|
||||
ButtonVariant::Link => cx.theme().ghost_element_background,
|
||||
ButtonVariant::Text => cx.theme().ghost_element_background,
|
||||
ButtonVariant::Custom(colors) => colors.hover,
|
||||
};
|
||||
let border = self.border_color(window, cx);
|
||||
let fg = match self {
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().text,
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
_ => self.text_color(window, cx),
|
||||
};
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn active(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
ButtonVariant::Link => cx.theme().transparent,
|
||||
ButtonVariant::Text => cx.theme().transparent,
|
||||
ButtonVariant::Primary => cx.theme().element_active,
|
||||
ButtonVariant::Ghost => cx.theme().ghost_element_active,
|
||||
ButtonVariant::Custom(colors) => colors.active,
|
||||
_ => cx.theme().ghost_element_background,
|
||||
};
|
||||
|
||||
let fg = match self {
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Text => cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
ButtonVariant::Text => cx.theme().text,
|
||||
_ => self.text_color(window, cx),
|
||||
};
|
||||
|
||||
let border = self.border_color(window, cx);
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn selected(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
ButtonVariant::Link => cx.theme().transparent,
|
||||
ButtonVariant::Text => cx.theme().transparent,
|
||||
ButtonVariant::Primary => cx.theme().element_selected,
|
||||
ButtonVariant::Ghost => cx.theme().ghost_element_selected,
|
||||
ButtonVariant::Custom(colors) => colors.active,
|
||||
_ => cx.theme().ghost_element_background,
|
||||
};
|
||||
|
||||
let fg = match self {
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Text => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
ButtonVariant::Text => cx.theme().text,
|
||||
_ => self.text_color(window, cx),
|
||||
};
|
||||
|
||||
let border = self.border_color(window, cx);
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn disabled(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Link | ButtonVariant::Ghost | ButtonVariant::Text => {
|
||||
cx.theme().transparent
|
||||
cx.theme().ghost_element_disabled
|
||||
}
|
||||
_ => cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
_ => cx.theme().element_disabled,
|
||||
};
|
||||
|
||||
let fg = match self {
|
||||
ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() {
|
||||
"Sky" => cx.theme().base.darken(cx),
|
||||
"Mint" => cx.theme().base.darken(cx),
|
||||
"Lime" => cx.theme().base.darken(cx),
|
||||
"Amber" => cx.theme().base.darken(cx),
|
||||
"Yellow" => cx.theme().base.darken(cx),
|
||||
_ => cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
},
|
||||
_ => cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
ButtonVariant::Primary => cx.theme().text_muted, // TODO: use a different color?
|
||||
_ => cx.theme().text_muted,
|
||||
};
|
||||
|
||||
let border = bg;
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = false;
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, App, Corners, Div, Edges, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, StatefulInteractiveElement as _, Styled, Window,
|
||||
};
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
button::{Button, ButtonVariant, ButtonVariants},
|
||||
Disableable, Sizable, Size,
|
||||
};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&Vec<usize>, &mut Window, &mut App) + 'static>>;
|
||||
|
||||
/// A ButtonGroup element, to wrap multiple buttons in a group.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ButtonGroup {
|
||||
pub base: Div,
|
||||
id: ElementId,
|
||||
children: Vec<Button>,
|
||||
multiple: bool,
|
||||
disabled: bool,
|
||||
|
||||
// The button props
|
||||
compact: Option<bool>,
|
||||
variant: Option<ButtonVariant>,
|
||||
size: Option<Size>,
|
||||
|
||||
on_click: OnClick,
|
||||
}
|
||||
|
||||
impl Disableable for ButtonGroup {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonGroup {
|
||||
/// Creates a new ButtonGroup.
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
base: div(),
|
||||
children: Vec::new(),
|
||||
id: id.into(),
|
||||
variant: None,
|
||||
size: None,
|
||||
compact: None,
|
||||
multiple: false,
|
||||
disabled: false,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a button as a child to the ButtonGroup.
|
||||
pub fn child(mut self, child: Button) -> Self {
|
||||
self.children.push(child.disabled(self.disabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// With the multiple selection mode.
|
||||
pub fn multiple(mut self, multiple: bool) -> Self {
|
||||
self.multiple = multiple;
|
||||
self
|
||||
}
|
||||
|
||||
/// With the compact mode for the ButtonGroup.
|
||||
pub fn compact(mut self) -> Self {
|
||||
self.compact = Some(true);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the on_click handler for the ButtonGroup.
|
||||
///
|
||||
/// The handler first argument is a vector of the selected button indices.
|
||||
pub fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&Vec<usize>, &mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for ButtonGroup {
|
||||
fn with_size(mut self, size: impl Into<Size>) -> Self {
|
||||
self.size = Some(size.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for ButtonGroup {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonVariants for ButtonGroup {
|
||||
fn with_variant(mut self, variant: ButtonVariant) -> Self {
|
||||
self.variant = Some(variant);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ButtonGroup {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let children_len = self.children.len();
|
||||
let mut selected_ixs: Vec<usize> = Vec::new();
|
||||
let state = Rc::new(Cell::new(None));
|
||||
|
||||
for (ix, child) in self.children.iter().enumerate() {
|
||||
if child.selected {
|
||||
selected_ixs.push(ix);
|
||||
}
|
||||
}
|
||||
|
||||
self.base
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.items_center()
|
||||
.children(
|
||||
self.children
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(child_index, child)| {
|
||||
let state = Rc::clone(&state);
|
||||
|
||||
if children_len == 1 {
|
||||
child
|
||||
} else if child_index == 0 {
|
||||
// First
|
||||
child
|
||||
.border_corners(Corners {
|
||||
top_left: true,
|
||||
top_right: false,
|
||||
bottom_left: true,
|
||||
bottom_right: false,
|
||||
})
|
||||
.border_edges(Edges {
|
||||
left: true,
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
})
|
||||
} else if child_index == children_len - 1 {
|
||||
// Last
|
||||
child
|
||||
.border_edges(Edges {
|
||||
left: false,
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
})
|
||||
.border_corners(Corners {
|
||||
top_left: false,
|
||||
top_right: true,
|
||||
bottom_left: false,
|
||||
bottom_right: true,
|
||||
})
|
||||
} else {
|
||||
// Middle
|
||||
child
|
||||
.border_corners(Corners::all(false))
|
||||
.border_edges(Edges {
|
||||
left: false,
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
})
|
||||
}
|
||||
.stop_propagation(false)
|
||||
.when_some(self.size, |this, size| this.with_size(size))
|
||||
.when_some(self.variant, |this, variant| this.with_variant(variant))
|
||||
.on_click(move |_, _, _| {
|
||||
state.set(Some(child_index));
|
||||
})
|
||||
}),
|
||||
)
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
move |this, on_click| {
|
||||
this.on_click(move |_, window, cx| {
|
||||
let mut selected_ixs = selected_ixs.clone();
|
||||
if let Some(ix) = state.get() {
|
||||
if self.multiple {
|
||||
if let Some(pos) = selected_ixs.iter().position(|&i| i == ix) {
|
||||
selected_ixs.remove(pos);
|
||||
} else {
|
||||
selected_ixs.push(ix);
|
||||
}
|
||||
} else {
|
||||
selected_ixs.clear();
|
||||
selected_ixs.push(ix);
|
||||
}
|
||||
}
|
||||
|
||||
on_click(&selected_ixs, window, cx);
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Disableable, IconName, Selectable,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, relative, svg, App, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, v_flex, Disableable, IconName, Selectable};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>;
|
||||
|
||||
@@ -68,15 +66,9 @@ impl Selectable for Checkbox {
|
||||
impl RenderOnce for Checkbox {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let (color, icon_color) = if self.disabled {
|
||||
(
|
||||
cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
(cx.theme().ghost_element_disabled, cx.theme().text_muted)
|
||||
} else {
|
||||
(
|
||||
cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
)
|
||||
(cx.theme().text_accent, cx.theme().surface_background)
|
||||
};
|
||||
|
||||
h_flex()
|
||||
@@ -93,7 +85,7 @@ impl RenderOnce for Checkbox {
|
||||
.size_4()
|
||||
.flex_shrink_0()
|
||||
.map(|this| match self.checked {
|
||||
false => this.bg(cx.theme().transparent),
|
||||
false => this.bg(cx.theme().ghost_element_background),
|
||||
_ => this.bg(color),
|
||||
})
|
||||
.child(
|
||||
@@ -111,22 +103,21 @@ impl RenderOnce for Checkbox {
|
||||
)
|
||||
.map(|this| {
|
||||
if let Some(label) = self.label {
|
||||
this.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.line_height(relative(1.))
|
||||
.child(label),
|
||||
)
|
||||
this.text_color(cx.theme().text_muted).child(
|
||||
div()
|
||||
.w_full()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.line_height(relative(1.))
|
||||
.child(label),
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
})
|
||||
.when(self.disabled, |this| {
|
||||
this.cursor_not_allowed()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TEN))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
use gpui::{
|
||||
prelude::FluentBuilder, AnyElement, App, ClipboardItem, Element, ElementId, GlobalElementId,
|
||||
IntoElement, LayoutId, ParentElement, SharedString, Styled, Window,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
|
||||
use crate::{
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex, IconName, Sizable as _,
|
||||
};
|
||||
|
||||
type ContentBuilder = Option<Box<dyn Fn(&mut Window, &mut App) -> AnyElement>>;
|
||||
type CopiedCallback = Option<Rc<dyn Fn(SharedString, &mut Window, &mut App)>>;
|
||||
|
||||
pub struct Clipboard {
|
||||
id: ElementId,
|
||||
value: SharedString,
|
||||
content_builder: ContentBuilder,
|
||||
copied_callback: CopiedCallback,
|
||||
}
|
||||
|
||||
impl Clipboard {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
value: "".into(),
|
||||
content_builder: None,
|
||||
copied_callback: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(mut self, value: impl Into<SharedString>) -> Self {
|
||||
self.value = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn content<E, F>(mut self, builder: F) -> Self
|
||||
where
|
||||
E: IntoElement,
|
||||
F: Fn(&mut Window, &mut App) -> E + 'static,
|
||||
{
|
||||
self.content_builder = Some(Box::new(move |window, cx| {
|
||||
builder(window, cx).into_any_element()
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_copied<F>(mut self, handler: F) -> Self
|
||||
where
|
||||
F: Fn(SharedString, &mut Window, &mut App) + 'static,
|
||||
{
|
||||
self.copied_callback = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Clipboard {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClipboardState {
|
||||
copied: Rc<RefCell<bool>>,
|
||||
}
|
||||
|
||||
impl Element for Clipboard {
|
||||
type RequestLayoutState = AnyElement;
|
||||
|
||||
type PrepaintState = ();
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
global_id: Option<&GlobalElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (LayoutId, Self::RequestLayoutState) {
|
||||
window.with_element_state::<ClipboardState, _>(global_id.unwrap(), |state, window| {
|
||||
let state = state.unwrap_or_default();
|
||||
|
||||
let content_element = self
|
||||
.content_builder
|
||||
.as_ref()
|
||||
.map(|builder| builder(window, cx).into_any_element());
|
||||
let value = self.value.clone();
|
||||
let clipboard_id = self.id.clone();
|
||||
let copied_callback = self.copied_callback.as_ref().map(|c| c.clone());
|
||||
let copied = state.copied.clone();
|
||||
let copide_value = *copied.borrow();
|
||||
|
||||
let mut element = h_flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.when_some(content_element, |this, element| this.child(element))
|
||||
.child(
|
||||
Button::new(clipboard_id)
|
||||
.icon(if copide_value {
|
||||
IconName::Check
|
||||
} else {
|
||||
IconName::Copy
|
||||
})
|
||||
.ghost()
|
||||
.xsmall()
|
||||
.when(!copide_value, |this| {
|
||||
this.on_click(move |_, window, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(value.to_string()));
|
||||
*copied.borrow_mut() = true;
|
||||
|
||||
let copied = copied.clone();
|
||||
cx.spawn(async move |cx| {
|
||||
cx.background_executor().timer(Duration::from_secs(2)).await;
|
||||
*copied.borrow_mut() = false;
|
||||
})
|
||||
.detach();
|
||||
|
||||
if let Some(callback) = &copied_callback {
|
||||
callback(value.clone(), window, cx);
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
.into_any_element();
|
||||
|
||||
((element.request_layout(window, cx), element), state)
|
||||
})
|
||||
}
|
||||
|
||||
fn prepaint(
|
||||
&mut self,
|
||||
_: Option<&gpui::GlobalElementId>,
|
||||
_: gpui::Bounds<gpui::Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
element.prepaint(window, cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: Option<&gpui::GlobalElementId>,
|
||||
_: gpui::Bounds<gpui::Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::PrepaintState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
element.paint(window, cx)
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@ use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, Axis, Div, Hsla, IntoElement, ParentElement, RenderOnce,
|
||||
SharedString, Styled,
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
/// A divider that can be either vertical or horizontal.
|
||||
#[derive(IntoElement)]
|
||||
@@ -65,9 +64,7 @@ impl RenderOnce for Divider {
|
||||
Axis::Vertical => this.w(px(2.)).h_full(),
|
||||
Axis::Horizontal => this.h(px(2.)).w_full(),
|
||||
})
|
||||
.bg(self
|
||||
.color
|
||||
.unwrap_or(cx.theme().base.step(cx, ColorScaleStep::FIVE))),
|
||||
.bg(self.color.unwrap_or(cx.theme().border_variant)),
|
||||
)
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Entity,
|
||||
InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels,
|
||||
Point, Render, StatefulInteractiveElement, Style, Styled as _, WeakEntity, Window,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use super::{DockArea, DockItem};
|
||||
use crate::{
|
||||
dock_area::{panel::PanelView, tab_panel::TabPanel},
|
||||
resizable::{HANDLE_PADDING, HANDLE_SIZE, PANEL_MIN_SIZE},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme as _},
|
||||
AxisExt as _, StyledExt,
|
||||
};
|
||||
|
||||
@@ -268,7 +269,7 @@ impl Dock {
|
||||
.child(
|
||||
div()
|
||||
.rounded_full()
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::SIX)))
|
||||
.hover(|this| this.bg(cx.theme().border_variant))
|
||||
.when(axis.is_horizontal(), |this| this.h_full().w(HANDLE_SIZE))
|
||||
.when(axis.is_vertical(), |this| this.w_full().h(HANDLE_SIZE)),
|
||||
)
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength,
|
||||
DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle,
|
||||
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use super::{
|
||||
panel::PanelView, stack_panel::StackPanel, ClosePanel, DockArea, PanelEvent, PanelStyle,
|
||||
ToggleZoom,
|
||||
@@ -8,16 +17,8 @@ use crate::{
|
||||
h_flex,
|
||||
popup_menu::{PopupMenu, PopupMenuExt},
|
||||
tab::{tab_bar::TabBar, Tab},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, AxisExt, IconName, Placement, Selectable, Sizable, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength,
|
||||
DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle,
|
||||
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TabState {
|
||||
@@ -53,11 +54,11 @@ impl Render for DragPanel {
|
||||
.justify_center()
|
||||
.overflow_hidden()
|
||||
.whitespace_nowrap()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.text_xs()
|
||||
.shadow_lg()
|
||||
.bg(cx.theme().background)
|
||||
.text_color(cx.theme().accent.step(cx, ColorScaleStep::TWELVE))
|
||||
.text_color(cx.theme().text_accent)
|
||||
.child(self.panel.title(cx))
|
||||
}
|
||||
}
|
||||
@@ -639,9 +640,7 @@ impl TabPanel {
|
||||
this.rounded_l_none()
|
||||
.border_l_2()
|
||||
.border_r_0()
|
||||
.border_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::FIVE),
|
||||
)
|
||||
.border_color(cx.theme().border)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, drag: &DragPanel, window, cx| {
|
||||
@@ -660,10 +659,10 @@ impl TabPanel {
|
||||
.h_full()
|
||||
.flex_grow()
|
||||
.min_w_16()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.when(state.droppable, |this| {
|
||||
this.drag_over::<DragPanel>(|this, _, _, cx| {
|
||||
this.bg(cx.theme().base.step(cx, ColorScaleStep::TWO))
|
||||
this.bg(cx.theme().surface_background)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, drag: &DragPanel, window, cx| {
|
||||
@@ -718,8 +717,8 @@ impl TabPanel {
|
||||
.size_full()
|
||||
.rounded_lg()
|
||||
.shadow_sm()
|
||||
.when(cx.theme().appearance.is_dark(), |this| this.shadow_lg())
|
||||
.bg(cx.theme().background)
|
||||
.when(cx.theme().mode.is_dark(), |this| this.shadow_lg())
|
||||
.bg(cx.theme().panel_background)
|
||||
.overflow_hidden()
|
||||
.child(
|
||||
active_panel
|
||||
@@ -738,8 +737,8 @@ impl TabPanel {
|
||||
div()
|
||||
.rounded_lg()
|
||||
.border_1()
|
||||
.border_color(cx.theme().accent.step(cx, ColorScaleStep::FOUR))
|
||||
.bg(cx.theme().accent.step_alpha(cx, ColorScaleStep::THREE))
|
||||
.border_color(cx.theme().element_disabled)
|
||||
.bg(cx.theme().drop_target_background)
|
||||
.size_full(),
|
||||
)
|
||||
.map(|this| match self.will_split_placement {
|
||||
|
||||
@@ -5,11 +5,11 @@ use gpui::{
|
||||
Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
h_flex,
|
||||
list::{self, List, ListDelegate, ListItem},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Icon, IconName, Sizable, Size, StyleSized, StyledExt,
|
||||
};
|
||||
|
||||
@@ -216,7 +216,7 @@ where
|
||||
h_flex()
|
||||
.justify_center()
|
||||
.py_6()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(Icon::new(IconName::Inbox).size(px(28.)))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -545,18 +545,15 @@ where
|
||||
.when_some(self.title_prefix.clone(), |this, prefix| this.child(prefix))
|
||||
.child(title.clone())
|
||||
} else {
|
||||
div()
|
||||
.text_color(cx.theme().accent.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(
|
||||
self.placeholder
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Please select".into()),
|
||||
)
|
||||
div().text_color(cx.theme().text_accent).child(
|
||||
self.placeholder
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Please select".into()),
|
||||
)
|
||||
};
|
||||
|
||||
title.when(self.disabled, |this| {
|
||||
this.cursor_not_allowed()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
this.cursor_not_allowed().text_color(cx.theme().text_muted)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -623,9 +620,9 @@ where
|
||||
.justify_between()
|
||||
.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.rounded(px(cx.theme().radius))
|
||||
.when(cx.theme().shadow, |this| this.shadow_sm())
|
||||
.border_color(cx.theme().border)
|
||||
.rounded(cx.theme().radius)
|
||||
.shadow_sm()
|
||||
.map(|this| {
|
||||
if self.disabled {
|
||||
this.cursor_not_allowed()
|
||||
@@ -672,10 +669,8 @@ where
|
||||
Icon::new(icon)
|
||||
.xsmall()
|
||||
.text_color(match self.disabled {
|
||||
true => cx.theme().base.step(cx, ColorScaleStep::TEN),
|
||||
false => {
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN)
|
||||
}
|
||||
true => cx.theme().icon_muted,
|
||||
false => cx.theme().icon,
|
||||
})
|
||||
.when(self.disabled, |this| this.cursor_not_allowed()),
|
||||
)
|
||||
@@ -706,10 +701,8 @@ where
|
||||
.mt_1p5()
|
||||
.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::SEVEN),
|
||||
)
|
||||
.rounded(px(cx.theme().radius))
|
||||
.border_color(cx.theme().border_focused)
|
||||
.rounded(cx.theme().radius)
|
||||
.shadow_md()
|
||||
.on_mouse_down_out(|_, _, cx| {
|
||||
cx.dispatch_action(&Escape);
|
||||
|
||||
@@ -6,12 +6,12 @@ use gpui::{
|
||||
StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
button::{Button, ButtonVariants},
|
||||
input::TextInput,
|
||||
popover::{Popover, PopoverContent},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Icon,
|
||||
};
|
||||
|
||||
@@ -99,11 +99,9 @@ impl RenderOnce for EmojiPicker {
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.child(e.clone())
|
||||
.hover(|this| {
|
||||
this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
})
|
||||
.hover(|this| this.bg(cx.theme().ghost_element_hover))
|
||||
.on_click({
|
||||
let item = e.clone();
|
||||
let input = input.upgrade();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Sizable, Size,
|
||||
};
|
||||
use gpui::{
|
||||
prelude::FluentBuilder as _, svg, AnyElement, App, AppContext, Entity, Hsla, IntoElement,
|
||||
Radians, Render, RenderOnce, SharedString, StyleRefinement, Styled, Svg, Transformation,
|
||||
Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{Sizable, Size};
|
||||
|
||||
#[derive(IntoElement, Clone)]
|
||||
pub enum IconName {
|
||||
@@ -295,9 +294,7 @@ impl Render for Icon {
|
||||
_window: &mut gpui::Window,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let text_color = self
|
||||
.text_color
|
||||
.unwrap_or_else(|| cx.theme().base.step(cx, ColorScaleStep::ELEVEN));
|
||||
let text_color = self.text_color.unwrap_or_else(|| cx.theme().icon);
|
||||
|
||||
svg()
|
||||
.flex_none()
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use super::TextInput;
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use gpui::{
|
||||
fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler,
|
||||
Entity, GlobalElementId, IntoElement, LayoutId, MouseButton, MouseMoveEvent, PaintQuad, Path,
|
||||
Pixels, Point, Style, TextRun, UnderlineStyle, Window, WrappedLine,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use super::TextInput;
|
||||
|
||||
const RIGHT_MARGIN: Pixels = px(5.);
|
||||
const BOTTOM_MARGIN: Pixels = px(20.);
|
||||
@@ -149,7 +150,7 @@ impl TextElement {
|
||||
),
|
||||
size(px(1.), cursor_height),
|
||||
),
|
||||
cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
cx.theme().element_active,
|
||||
))
|
||||
};
|
||||
}
|
||||
@@ -342,17 +343,11 @@ impl Element for TextElement {
|
||||
let mut bounds = bounds;
|
||||
|
||||
let (display_text, text_color) = if text.is_empty() {
|
||||
(
|
||||
placeholder,
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
(placeholder, cx.theme().text_muted)
|
||||
} else if input.masked {
|
||||
(
|
||||
"*".repeat(text.chars().count()).into(),
|
||||
cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
)
|
||||
("*".repeat(text.chars().count()).into(), cx.theme().text)
|
||||
} else {
|
||||
(text, cx.theme().base.step(cx, ColorScaleStep::TWELVE))
|
||||
(text, cx.theme().text)
|
||||
};
|
||||
|
||||
let run = TextRun {
|
||||
@@ -471,7 +466,7 @@ impl Element for TextElement {
|
||||
|
||||
// Paint selections
|
||||
if let Some(path) = prepaint.selection_path.take() {
|
||||
window.paint_path(path, cx.theme().accent.step(cx, ColorScaleStep::FOUR));
|
||||
window.paint_path(path, cx.theme().element_disabled);
|
||||
}
|
||||
|
||||
// Paint multi line text
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::{cell::Cell, ops::Range, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
actions, div, point, prelude::FluentBuilder as _, px, AnyElement, App, AppContext, Bounds,
|
||||
ClipboardItem, Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
|
||||
@@ -6,7 +8,7 @@ use gpui::{
|
||||
ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::Cell, ops::Range, rc::Rc};
|
||||
use theme::ActiveTheme;
|
||||
use unicode_segmentation::*;
|
||||
|
||||
use super::{blink_cursor::BlinkCursor, change::Change, element::TextElement};
|
||||
@@ -14,7 +16,6 @@ use crate::{
|
||||
history::History,
|
||||
indicator::Indicator,
|
||||
scroll::{Scrollbar, ScrollbarAxis, ScrollbarState},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Sizable, Size, StyleSized, StyledExt,
|
||||
};
|
||||
|
||||
@@ -1624,9 +1625,8 @@ impl Render for TextInput {
|
||||
.cursor_text()
|
||||
.when(self.multi_line, |this| this.h_auto())
|
||||
.when(self.appearance, |this| {
|
||||
this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.rounded(px(cx.theme().radius))
|
||||
.when(cx.theme().shadow, |this| this.shadow_sm())
|
||||
this.bg(cx.theme().elevated_surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.when(focused, |this| this.outline(window, cx))
|
||||
.when(prefix.is_none(), |this| this.input_pl(self.size))
|
||||
.when(suffix.is_none(), |this| this.input_pr(self.size))
|
||||
@@ -1642,7 +1642,7 @@ impl Render for TextInput {
|
||||
.child(TextElement::new(cx.entity().clone())),
|
||||
)
|
||||
.when(self.loading, |this| {
|
||||
this.child(Indicator::new().color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)))
|
||||
this.child(Indicator::new().color(cx.theme().text_muted))
|
||||
})
|
||||
.children(suffix)
|
||||
.when(self.is_multi_line(), |this| {
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
pub use event::InteractiveElementExt;
|
||||
pub use focusable::FocusableCycle;
|
||||
pub use icon::*;
|
||||
pub use root::{ContextModal, Root};
|
||||
pub use styled::*;
|
||||
pub use title_bar::*;
|
||||
pub use window_border::{window_border, WindowBorder};
|
||||
|
||||
pub use crate::Disableable;
|
||||
|
||||
mod event;
|
||||
mod focusable;
|
||||
mod icon;
|
||||
mod root;
|
||||
mod styled;
|
||||
mod title_bar;
|
||||
mod window_border;
|
||||
|
||||
pub mod animation;
|
||||
pub mod button;
|
||||
pub mod button_group;
|
||||
pub mod checkbox;
|
||||
pub mod clipboard;
|
||||
pub mod context_menu;
|
||||
pub mod divider;
|
||||
pub mod dock_area;
|
||||
@@ -16,34 +32,14 @@ pub mod modal;
|
||||
pub mod notification;
|
||||
pub mod popover;
|
||||
pub mod popup_menu;
|
||||
pub mod progress;
|
||||
pub mod radio;
|
||||
pub mod resizable;
|
||||
pub mod scroll;
|
||||
pub mod skeleton;
|
||||
pub mod switch;
|
||||
pub mod tab;
|
||||
pub mod text;
|
||||
pub mod theme;
|
||||
pub mod tooltip;
|
||||
|
||||
pub use crate::Disableable;
|
||||
pub use event::InteractiveElementExt;
|
||||
pub use focusable::FocusableCycle;
|
||||
pub use icon::*;
|
||||
pub use root::{ContextModal, Root};
|
||||
pub use styled::*;
|
||||
pub use title_bar::*;
|
||||
pub use window_border::{window_border, WindowBorder};
|
||||
|
||||
mod event;
|
||||
mod focusable;
|
||||
mod icon;
|
||||
mod root;
|
||||
mod styled;
|
||||
mod title_bar;
|
||||
mod window_border;
|
||||
|
||||
/// Initialize the UI module.
|
||||
///
|
||||
/// This must be called before using any of the UI components.
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
use crate::{
|
||||
input::{InputEvent, TextInput},
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Icon, IconName, Size,
|
||||
};
|
||||
use std::{cell::Cell, rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context,
|
||||
Entity, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length,
|
||||
@@ -11,7 +7,13 @@ use gpui::{
|
||||
Subscription, Task, UniformListScrollHandle, Window,
|
||||
};
|
||||
use smol::Timer;
|
||||
use std::{cell::Cell, rc::Rc, time::Duration};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
input::{InputEvent, TextInput},
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
v_flex, Icon, IconName, Size,
|
||||
};
|
||||
|
||||
actions!(list, [Cancel, Confirm, SelectPrev, SelectNext]);
|
||||
|
||||
@@ -122,10 +124,7 @@ where
|
||||
let query_input = cx.new(|cx| {
|
||||
TextInput::new(window, cx)
|
||||
.appearance(false)
|
||||
.prefix(|_window, cx| {
|
||||
Icon::new(IconName::Search)
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
})
|
||||
.prefix(|_window, cx| Icon::new(IconName::Search).text_color(cx.theme().text_muted))
|
||||
.placeholder("Search...")
|
||||
.cleanable()
|
||||
});
|
||||
@@ -379,9 +378,9 @@ where
|
||||
.left(px(0.))
|
||||
.right(px(0.))
|
||||
.bottom(px(0.))
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::SIX))
|
||||
.bg(cx.theme().element_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE)),
|
||||
.border_color(cx.theme().border_selected),
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -394,7 +393,7 @@ where
|
||||
.right(px(0.))
|
||||
.bottom(px(0.))
|
||||
.border_1()
|
||||
.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE)),
|
||||
.border_color(cx.theme().element_active),
|
||||
)
|
||||
})
|
||||
.on_mouse_down(
|
||||
@@ -471,7 +470,7 @@ where
|
||||
_ => this.py_1().px_2(),
|
||||
})
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.border_color(cx.theme().border)
|
||||
.child(input),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Disableable, Icon, IconName, Selectable, Sizable as _,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, AnyElement, App, ClickEvent, Div, ElementId,
|
||||
InteractiveElement, IntoElement, MouseButton, MouseMoveEvent, ParentElement, RenderOnce,
|
||||
Stateful, StatefulInteractiveElement as _, Styled, Window,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, Disableable, Icon, IconName, Selectable, Sizable as _};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>;
|
||||
type OnMouseEnter = Option<Box<dyn Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static>>;
|
||||
@@ -132,7 +130,7 @@ impl RenderOnce for ListItem {
|
||||
let is_active = self.selected || self.confirmed;
|
||||
|
||||
self.base
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TWELVE))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.relative()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
@@ -147,11 +145,9 @@ impl RenderOnce for ListItem {
|
||||
this
|
||||
}
|
||||
})
|
||||
.when(is_active, |this| {
|
||||
this.bg(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
})
|
||||
.when(is_active, |this| this.bg(cx.theme().element_active))
|
||||
.when(!is_active && !self.disabled, |this| {
|
||||
this.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::TWO)))
|
||||
this.hover(|this| this.bg(cx.theme().surface_background))
|
||||
})
|
||||
// Mouse enter
|
||||
.when_some(self.on_mouse_enter, |this, on_mouse_enter| {
|
||||
@@ -169,16 +165,15 @@ impl RenderOnce for ListItem {
|
||||
.gap_x_1()
|
||||
.child(div().w_full().children(self.children))
|
||||
.when_some(self.check_icon, |this, icon| {
|
||||
this.child(div().w_5().items_center().justify_center().when(
|
||||
self.confirmed,
|
||||
|this| {
|
||||
this.child(
|
||||
icon.small().text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
),
|
||||
)
|
||||
},
|
||||
))
|
||||
this.child(
|
||||
div()
|
||||
.w_5()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.when(self.confirmed, |this| {
|
||||
this.child(icon.small().text_color(cx.theme().text_muted))
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(self.suffix, |this, suffix| this.child(suffix(window, cx)))
|
||||
|
||||
@@ -6,11 +6,11 @@ use gpui::{
|
||||
IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString,
|
||||
Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonCustomVariant, ButtonVariants as _},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme as _},
|
||||
v_flex, ContextModal, IconName, StyledExt,
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ impl Modal {
|
||||
let base = v_flex()
|
||||
.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.border_color(cx.theme().border)
|
||||
.rounded_xl()
|
||||
.shadow_md();
|
||||
|
||||
@@ -168,9 +168,7 @@ impl RenderOnce for Modal {
|
||||
.occlude()
|
||||
.w(view_size.width)
|
||||
.h(view_size.height)
|
||||
.when(self.overlay, |this| {
|
||||
this.bg(cx.theme().base.step_alpha(cx, ColorScaleStep::TWO))
|
||||
})
|
||||
.when(self.overlay, |this| this.bg(cx.theme().overlay))
|
||||
.when(self.keyboard, |this| {
|
||||
this.on_mouse_down(MouseButton::Left, {
|
||||
let on_close = self.on_close.clone();
|
||||
@@ -201,7 +199,7 @@ impl RenderOnce for Modal {
|
||||
.items_center()
|
||||
.font_semibold()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.border_color(cx.theme().border)
|
||||
.line_height(relative(1.))
|
||||
.child(title),
|
||||
)
|
||||
@@ -217,13 +215,10 @@ impl RenderOnce for Modal {
|
||||
.right_2()
|
||||
.custom(
|
||||
ButtonCustomVariant::new(window, cx)
|
||||
.foreground(
|
||||
cx.theme().base.step(cx, ColorScaleStep::NINE),
|
||||
)
|
||||
.color(cx.theme().transparent)
|
||||
.hover(cx.theme().transparent)
|
||||
.active(cx.theme().transparent)
|
||||
.border(cx.theme().transparent),
|
||||
.foreground(cx.theme().icon_muted)
|
||||
.color(cx.theme().ghost_element_background)
|
||||
.hover(cx.theme().ghost_element_background)
|
||||
.active(cx.theme().ghost_element_background),
|
||||
)
|
||||
.on_click(
|
||||
move |_, window, cx| {
|
||||
|
||||
@@ -1,21 +1,3 @@
|
||||
use crate::{
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex,
|
||||
theme::{
|
||||
colors::{blue, green, red, yellow},
|
||||
scale::ColorScaleStep,
|
||||
ActiveTheme as _,
|
||||
},
|
||||
v_flex, Icon, IconName, Sizable as _, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, Animation, AnimationExt, App, AppContext, ClickEvent, Context,
|
||||
DismissEvent, ElementId, Entity, EventEmitter, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
||||
Window,
|
||||
};
|
||||
use smol::Timer;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
collections::{HashMap, VecDeque},
|
||||
@@ -23,6 +5,21 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use gpui::{
|
||||
blue, div, green, prelude::FluentBuilder, px, red, yellow, Animation, AnimationExt, App,
|
||||
AppContext, ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter,
|
||||
InteractiveElement as _, IntoElement, ParentElement as _, Render, SharedString,
|
||||
StatefulInteractiveElement, Styled, Subscription, Window,
|
||||
};
|
||||
use smol::Timer;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex, v_flex, Icon, IconName, Sizable as _, StyledExt,
|
||||
};
|
||||
|
||||
pub enum NotificationType {
|
||||
Info,
|
||||
Success,
|
||||
@@ -57,7 +54,7 @@ pub struct Notification {
|
||||
///
|
||||
/// None means the notification will be added to the end of the list.
|
||||
id: NotificationId,
|
||||
type_: NotificationType,
|
||||
kind: NotificationType,
|
||||
title: Option<SharedString>,
|
||||
message: SharedString,
|
||||
icon: Option<Icon>,
|
||||
@@ -110,7 +107,7 @@ impl Notification {
|
||||
id: id.into(),
|
||||
title: None,
|
||||
message: message.into(),
|
||||
type_: NotificationType::Info,
|
||||
kind: NotificationType::Info,
|
||||
icon: None,
|
||||
autohide: true,
|
||||
on_click: None,
|
||||
@@ -169,7 +166,7 @@ impl Notification {
|
||||
|
||||
/// Set the type of the notification, default is NotificationType::Info.
|
||||
pub fn with_type(mut self, type_: NotificationType) -> Self {
|
||||
self.type_ = type_;
|
||||
self.kind = type_;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -217,16 +214,13 @@ impl Render for Notification {
|
||||
let closing = self.closing;
|
||||
let icon = match self.icon.clone() {
|
||||
Some(icon) => icon,
|
||||
None => match self.type_ {
|
||||
NotificationType::Info => {
|
||||
Icon::new(IconName::Info).text_color(blue().step(cx, ColorScaleStep::NINE))
|
||||
None => match self.kind {
|
||||
NotificationType::Info => Icon::new(IconName::Info).text_color(blue()),
|
||||
NotificationType::Error => Icon::new(IconName::CloseCircle).text_color(red()),
|
||||
NotificationType::Success => Icon::new(IconName::CheckCircle).text_color(green()),
|
||||
NotificationType::Warning => {
|
||||
Icon::new(IconName::TriangleAlert).text_color(yellow())
|
||||
}
|
||||
NotificationType::Error => Icon::new(IconName::CloseCircle)
|
||||
.text_color(red().step(cx, ColorScaleStep::NINE)),
|
||||
NotificationType::Success => Icon::new(IconName::CheckCircle)
|
||||
.text_color(green().step(cx, ColorScaleStep::NINE)),
|
||||
NotificationType::Warning => Icon::new(IconName::TriangleAlert)
|
||||
.text_color(yellow().step(cx, ColorScaleStep::NINE)),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -237,9 +231,9 @@ impl Render for Notification {
|
||||
.relative()
|
||||
.w_72()
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE))
|
||||
.bg(cx.theme().background)
|
||||
.rounded(px(cx.theme().radius))
|
||||
.border_color(cx.theme().border)
|
||||
.bg(cx.theme().surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.shadow_md()
|
||||
.p_2()
|
||||
.gap_3()
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
use crate::{
|
||||
button::Button,
|
||||
h_flex,
|
||||
list::ListItem,
|
||||
popover::Popover,
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Icon, IconName, Selectable, Sizable as _, StyledExt,
|
||||
};
|
||||
use std::{cell::Cell, ops::Deref, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
actions, anchored, canvas, div, prelude::FluentBuilder, px, rems, Action, AnyElement, App,
|
||||
AppContext, Bounds, Context, Corner, DismissEvent, Edges, Entity, EventEmitter, FocusHandle,
|
||||
@@ -14,7 +7,16 @@ use gpui::{
|
||||
Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
use std::{cell::Cell, ops::Deref, rc::Rc};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
button::Button,
|
||||
h_flex,
|
||||
list::ListItem,
|
||||
popover::Popover,
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
v_flex, Icon, IconName, Selectable, Sizable as _, StyledExt,
|
||||
};
|
||||
|
||||
actions!(menu, [Confirm, Dismiss, SelectNext, SelectPrev]);
|
||||
|
||||
@@ -456,14 +458,12 @@ impl PopupMenu {
|
||||
) -> Option<impl IntoElement> {
|
||||
if let Some(action) = action {
|
||||
if let Some(keybinding) = window.bindings_for_action(action.deref()).first() {
|
||||
let el = div()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.children(
|
||||
keybinding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|key| key_shortcut(key.clone())),
|
||||
);
|
||||
let el = div().text_color(cx.theme().text_muted).children(
|
||||
keybinding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|key| key_shortcut(key.clone())),
|
||||
);
|
||||
|
||||
return Some(el);
|
||||
}
|
||||
@@ -590,10 +590,7 @@ impl Render for PopupMenu {
|
||||
.h(px(1.))
|
||||
.mx_neg_1()
|
||||
.my_0p5()
|
||||
.bg(cx
|
||||
.theme()
|
||||
.base
|
||||
.step(cx, ColorScaleStep::TWO)),
|
||||
.bg(cx.theme().border_variant),
|
||||
)
|
||||
}
|
||||
PopupMenuItem::ElementItem { render, .. } => this
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, relative, App, IntoElement, ParentElement, RenderOnce, Styled,
|
||||
Window,
|
||||
};
|
||||
|
||||
/// A Progress bar element.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Progress {
|
||||
value: f32,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl Progress {
|
||||
pub fn new() -> Self {
|
||||
Progress {
|
||||
value: Default::default(),
|
||||
height: 8.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(mut self, value: f32) -> Self {
|
||||
self.value = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Progress {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Progress {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let rounded = px(self.height / 2.);
|
||||
let relative_w = relative(match self.value {
|
||||
v if v < 0. => 0.,
|
||||
v if v > 100. => 1.,
|
||||
v => v / 100.,
|
||||
});
|
||||
|
||||
div()
|
||||
.relative()
|
||||
.h(px(self.height))
|
||||
.rounded(rounded)
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::THREE))
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.h_full()
|
||||
.w(relative_w)
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
.map(|this| match self.value {
|
||||
v if v >= 100. => this.rounded(rounded),
|
||||
_ => this.rounded_l(rounded),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
IconName,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, relative, svg, App, ElementId, InteractiveElement, IntoElement,
|
||||
ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window,
|
||||
};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>;
|
||||
|
||||
/// A Radio element.
|
||||
///
|
||||
/// This is not included the Radio group implementation, you can manage the group by yourself.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Radio {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
checked: bool,
|
||||
disabled: bool,
|
||||
on_click: OnClick,
|
||||
}
|
||||
|
||||
impl Radio {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
checked: false,
|
||||
disabled: false,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn checked(mut self, checked: bool) -> Self {
|
||||
self.checked = checked;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Radio {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let color = if self.disabled {
|
||||
cx.theme().accent.step(cx, ColorScaleStep::FIVE)
|
||||
} else {
|
||||
cx.theme().accent.step(cx, ColorScaleStep::NINE)
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.gap_x_2()
|
||||
.items_center()
|
||||
.line_height(relative(1.))
|
||||
.child(
|
||||
div()
|
||||
.relative()
|
||||
.size_4()
|
||||
.flex_shrink_0()
|
||||
.rounded_full()
|
||||
.border_1()
|
||||
.border_color(color)
|
||||
.when(self.checked, |this| this.bg(color))
|
||||
.child(
|
||||
svg()
|
||||
.absolute()
|
||||
.top_px()
|
||||
.left_px()
|
||||
.size_3()
|
||||
.text_color(color)
|
||||
.map(|this| match self.checked {
|
||||
true => this.path(IconName::Check.path()),
|
||||
false => this,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(
|
||||
div()
|
||||
.size_full()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.line_height(relative(1.))
|
||||
.child(label),
|
||||
)
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|this, on_click| {
|
||||
this.on_click(move |_event, window, cx| {
|
||||
on_click(&!self.checked, window, cx);
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
theme::{scale::ColorScaleStep, ActiveTheme as _},
|
||||
AxisExt as _,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, App, Axis, Div, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement as _, Pixels, RenderOnce, Stateful, StatefulInteractiveElement,
|
||||
Styled as _, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::AxisExt as _;
|
||||
|
||||
pub(crate) const HANDLE_PADDING: Pixels = px(8.);
|
||||
pub(crate) const HANDLE_SIZE: Pixels = px(2.);
|
||||
@@ -65,7 +64,7 @@ impl RenderOnce for ResizeHandle {
|
||||
.child(
|
||||
div()
|
||||
.rounded_full()
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::SIX)))
|
||||
.hover(|this| this.bg(cx.theme().border_variant))
|
||||
.when(self.axis.is_horizontal(), |this| {
|
||||
this.h_full().w(HANDLE_SIZE)
|
||||
})
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use crate::{
|
||||
modal::Modal,
|
||||
notification::{Notification, NotificationList},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
window_border,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
div, AnyView, App, AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement,
|
||||
ParentElement as _, Render, Styled, Window,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
modal::Modal,
|
||||
notification::{Notification, NotificationList},
|
||||
window_border,
|
||||
};
|
||||
|
||||
/// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality.
|
||||
pub trait ContextModal: Sized {
|
||||
@@ -230,8 +232,8 @@ impl Render for Root {
|
||||
.relative()
|
||||
.size_full()
|
||||
.font_family(".SystemUIFont")
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::ONE))
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TWELVE))
|
||||
.bg(cx.theme().background)
|
||||
.text_color(cx.theme().text)
|
||||
.child(self.view.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,38 +1,18 @@
|
||||
use gpui::{
|
||||
fill, point, px, relative, App, BorderStyle, Bounds, ContentMask, CursorStyle, Edges, Element,
|
||||
EntityId, Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||
Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, UniformListScrollHandle, Window,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
|
||||
/// Scrollbar show mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Default)]
|
||||
pub enum ScrollbarShow {
|
||||
#[default]
|
||||
Scrolling,
|
||||
Hover,
|
||||
Always,
|
||||
}
|
||||
|
||||
impl ScrollbarShow {
|
||||
fn is_hover(&self) -> bool {
|
||||
matches!(self, Self::Hover)
|
||||
}
|
||||
|
||||
fn is_always(&self) -> bool {
|
||||
matches!(self, Self::Always)
|
||||
}
|
||||
}
|
||||
use gpui::{
|
||||
fill, point, px, relative, App, BorderStyle, Bounds, ContentMask, CursorStyle, Edges, Element,
|
||||
EntityId, Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||
Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, UniformListScrollHandle, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
const WIDTH: Pixels = px(12.);
|
||||
const BORDER_WIDTH: Pixels = px(0.);
|
||||
pub(crate) const WIDTH: Pixels = px(12.);
|
||||
const MIN_THUMB_SIZE: f32 = 80.;
|
||||
const THUMB_RADIUS: Pixels = Pixels(4.0);
|
||||
const THUMB_INSET: Pixels = Pixels(3.);
|
||||
@@ -336,9 +316,9 @@ impl Scrollbar {
|
||||
|
||||
fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
(
|
||||
cx.theme().scrollbar_thumb_hover,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().base.step(cx, ColorScaleStep::SEVEN),
|
||||
cx.theme().scrollbar_thumb_hover_background,
|
||||
cx.theme().scrollbar_thumb_background,
|
||||
cx.theme().scrollbar_thumb_border,
|
||||
THUMB_INSET - px(1.),
|
||||
THUMB_RADIUS,
|
||||
)
|
||||
@@ -346,24 +326,20 @@ impl Scrollbar {
|
||||
|
||||
fn style_for_hovered_thumb(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
(
|
||||
cx.theme().scrollbar_thumb_hover,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().base.step(cx, ColorScaleStep::SIX),
|
||||
cx.theme().scrollbar_thumb_hover_background,
|
||||
cx.theme().scrollbar_thumb_background,
|
||||
cx.theme().scrollbar_thumb_border,
|
||||
THUMB_INSET - px(1.),
|
||||
THUMB_RADIUS,
|
||||
)
|
||||
}
|
||||
|
||||
fn style_for_hovered_bar(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
let (inset, radius) = if cx.theme().scrollbar_show.is_hover() {
|
||||
(THUMB_INSET, THUMB_RADIUS - px(1.))
|
||||
} else {
|
||||
(THUMB_INSET - px(1.), THUMB_RADIUS)
|
||||
};
|
||||
let (inset, radius) = (THUMB_INSET - px(1.), THUMB_RADIUS);
|
||||
|
||||
(
|
||||
cx.theme().scrollbar_thumb,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().scrollbar_thumb_background,
|
||||
cx.theme().scrollbar_thumb_border,
|
||||
gpui::transparent_black(),
|
||||
inset,
|
||||
radius,
|
||||
@@ -514,22 +490,12 @@ impl Element for Scrollbar {
|
||||
};
|
||||
|
||||
let state = self.state.clone();
|
||||
let is_always_to_show = cx.theme().scrollbar_show.is_always();
|
||||
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
|
||||
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
|
||||
let is_hovered_on_thumb = state.get().hovered_on_thumb == Some(axis);
|
||||
|
||||
let (thumb_bg, bar_bg, bar_border, inset, radius) =
|
||||
if state.get().dragged_axis == Some(axis) {
|
||||
Self::style_for_active(cx)
|
||||
} else if (is_hover_to_show || is_always_to_show)
|
||||
&& (is_hovered_on_bar || is_hovered_on_thumb)
|
||||
{
|
||||
if is_hovered_on_thumb {
|
||||
Self::style_for_hovered_thumb(cx)
|
||||
} else {
|
||||
Self::style_for_hovered_bar(cx)
|
||||
}
|
||||
} else {
|
||||
let mut idle_state = Self::style_for_idle(cx);
|
||||
// Delay 2s to fade out the scrollbar thumb (in 1s)
|
||||
@@ -545,11 +511,12 @@ impl Element for Scrollbar {
|
||||
};
|
||||
} else {
|
||||
if elapsed < FADE_OUT_DELAY {
|
||||
idle_state.0 = cx.theme().scrollbar_thumb;
|
||||
idle_state.0 = cx.theme().scrollbar_thumb_background;
|
||||
} else {
|
||||
// opacity = 1 - (x - 2)^10
|
||||
let opacity = 1.0 - (elapsed - FADE_OUT_DELAY).powi(10);
|
||||
idle_state.0 = cx.theme().scrollbar_thumb.opacity(opacity);
|
||||
idle_state.0 =
|
||||
cx.theme().scrollbar_thumb_background.opacity(opacity);
|
||||
};
|
||||
|
||||
window.request_animation_frame();
|
||||
@@ -626,11 +593,10 @@ impl Element for Scrollbar {
|
||||
_: &mut Self::RequestLayoutState,
|
||||
prepaint: &mut Self::PrepaintState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
let hitbox_bounds = prepaint.hitbox.bounds;
|
||||
let is_visible = self.state.get().is_scrollbar_visible();
|
||||
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
|
||||
|
||||
// Update last_scroll_time when offset is changed.
|
||||
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {
|
||||
@@ -711,7 +677,7 @@ impl Element for Scrollbar {
|
||||
|
||||
let safe_range = (-scroll_area_size + container_size)..px(0.);
|
||||
|
||||
if is_hover_to_show || is_visible {
|
||||
if is_visible {
|
||||
window.on_mouse_event({
|
||||
let state = self.state.clone();
|
||||
let view_id = self.view_id;
|
||||
@@ -770,7 +736,7 @@ impl Element for Scrollbar {
|
||||
let mut notify = false;
|
||||
// When is hover to show mode or it was visible,
|
||||
// we need to update the hovered state and increase the last_scroll_time.
|
||||
let need_hover_to_update = is_hover_to_show || is_visible;
|
||||
let need_hover_to_update = is_visible;
|
||||
// Update hovered state for scrollbar
|
||||
if bounds.contains(&event.position) && need_hover_to_update {
|
||||
state.set(state.get().with_hovered(Some(axis)));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use std::time::Duration;
|
||||
|
||||
use gpui::{
|
||||
bounce, div, ease_in_out, Animation, AnimationExt, Div, IntoElement, ParentElement as _,
|
||||
RenderOnce, Styled,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Skeleton {
|
||||
@@ -34,7 +35,7 @@ impl RenderOnce for Skeleton {
|
||||
fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
|
||||
div().child(
|
||||
self.base
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.bg(cx.theme().ghost_element_disabled)
|
||||
.with_animation(
|
||||
"skeleton",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
scroll::{Scrollable, ScrollbarAxis},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use gpui::{div, px, App, Axis, Div, Element, ElementId, EntityId, Pixels, Styled, Window};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::scroll::{Scrollable, ScrollbarAxis};
|
||||
|
||||
/// Returns a `Div` as horizontal flex layout.
|
||||
pub fn h_flex() -> Div {
|
||||
@@ -39,7 +39,7 @@ pub trait StyledExt: Styled + Sized {
|
||||
|
||||
/// Render a border with a width of 1px, color ring color
|
||||
fn outline(self, _window: &Window, cx: &App) -> Self {
|
||||
self.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
self.border_color(cx.theme().ring)
|
||||
}
|
||||
|
||||
/// Wraps the element in a ScrollView.
|
||||
@@ -66,7 +66,7 @@ pub trait StyledExt: Styled + Sized {
|
||||
fn popover_style(self, cx: &mut App) -> Self {
|
||||
self.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.border_color(cx.theme().border)
|
||||
.shadow_lg()
|
||||
.rounded_lg()
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Disableable, Side, Sizable, Size,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, Animation, AnimationExt as _, AnyElement, App, Element,
|
||||
ElementId, GlobalElementId, InteractiveElement, IntoElement, LayoutId, ParentElement as _,
|
||||
SharedString, Styled as _, Window,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, Disableable, Side, Sizable, Size};
|
||||
|
||||
type OnClick = Option<Rc<dyn Fn(&bool, &mut Window, &mut App)>>;
|
||||
|
||||
@@ -110,11 +109,8 @@ impl Element for Switch {
|
||||
let on_click = self.on_click.clone();
|
||||
|
||||
let (bg, toggle_bg) = match self.checked {
|
||||
true => (
|
||||
theme.accent.step(cx, ColorScaleStep::NINE),
|
||||
theme.background,
|
||||
),
|
||||
false => (theme.base.step(cx, ColorScaleStep::THREE), theme.background),
|
||||
true => (theme.icon_accent, theme.background),
|
||||
false => (theme.element_background, theme.background),
|
||||
};
|
||||
|
||||
let (bg, toggle_bg) = match self.disabled {
|
||||
@@ -126,10 +122,12 @@ impl Element for Switch {
|
||||
Size::XSmall | Size::Small => (px(28.), px(16.)),
|
||||
_ => (px(36.), px(20.)),
|
||||
};
|
||||
|
||||
let bar_width = match self.size {
|
||||
Size::XSmall | Size::Small => px(12.),
|
||||
_ => px(16.),
|
||||
};
|
||||
|
||||
let inset = px(2.);
|
||||
|
||||
let mut element = div()
|
||||
@@ -150,7 +148,7 @@ impl Element for Switch {
|
||||
.flex()
|
||||
.items_center()
|
||||
.border(inset)
|
||||
.border_color(theme.transparent)
|
||||
.border_color(theme.border_transparent)
|
||||
.bg(bg)
|
||||
.when(!self.disabled, |this| this.cursor_pointer())
|
||||
.child(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use crate::theme::scale::ColorScaleStep;
|
||||
use crate::theme::ActiveTheme;
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, AnyElement, App, Div, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, Stateful, StatefulInteractiveElement, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::Selectable;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::*;
|
||||
|
||||
pub mod tab_bar;
|
||||
|
||||
@@ -80,25 +82,24 @@ impl RenderOnce for Tab {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let (text_color, bg_color, hover_bg_color) = match (self.selected, self.disabled) {
|
||||
(true, false) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::FIVE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text,
|
||||
cx.theme().tab_active_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
(false, false) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
cx.theme().transparent,
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text_muted,
|
||||
cx.theme().ghost_element_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
// disabled
|
||||
(true, true) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
cx.theme().transparent,
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text_muted,
|
||||
cx.theme().ghost_element_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
(false, true) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
cx.theme().transparent,
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text_muted,
|
||||
cx.theme().ghost_element_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -115,7 +116,7 @@ impl RenderOnce for Tab {
|
||||
.text_ellipsis()
|
||||
.text_color(text_color)
|
||||
.bg(bg_color)
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.hover(|this| this.bg(hover_bg_color))
|
||||
.when_some(self.prefix, |this, prefix| {
|
||||
this.child(prefix).text_color(text_color)
|
||||
|
||||
@@ -8,8 +8,7 @@ use nostr_sdk::prelude::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{collections::HashMap, ops::Range, sync::Arc};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
static NOSTR_URI_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"nostr:(npub|note|nprofile|nevent|naddr)[a-zA-Z0-9]+").unwrap());
|
||||
@@ -78,7 +77,7 @@ impl RichText {
|
||||
}
|
||||
|
||||
pub fn element(&self, id: ElementId, window: &mut Window, cx: &App) -> AnyElement {
|
||||
let link_color = cx.theme().accent.step(cx, ColorScaleStep::ELEVEN);
|
||||
let link_color = cx.theme().text_accent;
|
||||
|
||||
InteractiveText::new(
|
||||
id,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,166 +0,0 @@
|
||||
use crate::scroll::ScrollbarShow;
|
||||
use colors::{default_color_scales, hsl};
|
||||
use gpui::{App, Global, Hsla, SharedString, Window, WindowAppearance};
|
||||
use scale::ColorScaleSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub mod colors;
|
||||
pub mod scale;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
Theme::sync_system_appearance(None, cx)
|
||||
}
|
||||
|
||||
pub trait ActiveTheme {
|
||||
fn theme(&self) -> &Theme;
|
||||
}
|
||||
|
||||
impl ActiveTheme for App {
|
||||
#[inline]
|
||||
fn theme(&self) -> &Theme {
|
||||
Theme::global(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SystemColors {
|
||||
pub background: Hsla,
|
||||
pub transparent: Hsla,
|
||||
pub scrollbar: Hsla,
|
||||
pub scrollbar_thumb: Hsla,
|
||||
pub scrollbar_thumb_hover: Hsla,
|
||||
pub window_border: Hsla,
|
||||
pub danger: Hsla,
|
||||
}
|
||||
|
||||
impl SystemColors {
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
background: hsl(0.0, 0.0, 100.),
|
||||
transparent: Hsla::transparent_black(),
|
||||
window_border: hsl(240.0, 5.9, 78.0),
|
||||
scrollbar: hsl(0., 0., 97.).opacity(0.75),
|
||||
scrollbar_thumb: hsl(0., 0., 69.).opacity(0.9),
|
||||
scrollbar_thumb_hover: hsl(0., 0., 59.),
|
||||
danger: hsl(0.0, 84.2, 60.2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
background: hsl(0.0, 0.0, 8.0),
|
||||
transparent: Hsla::transparent_black(),
|
||||
window_border: hsl(240.0, 3.7, 28.0),
|
||||
scrollbar: hsl(240., 1., 15.).opacity(0.75),
|
||||
scrollbar_thumb: hsl(0., 0., 48.).opacity(0.9),
|
||||
scrollbar_thumb_hover: hsl(0., 0., 68.),
|
||||
danger: hsl(0.0, 62.8, 30.6),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq)]
|
||||
pub enum Appearance {
|
||||
#[default]
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
pub fn is_dark(&self) -> bool {
|
||||
matches!(self, Self::Dark)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Theme {
|
||||
colors: SystemColors,
|
||||
/// Base colors.
|
||||
pub base: ColorScaleSet,
|
||||
/// Accent colors.
|
||||
pub accent: ColorScaleSet,
|
||||
/// Window appearances.
|
||||
pub appearance: Appearance,
|
||||
pub font_family: SharedString,
|
||||
pub font_size: f32,
|
||||
pub radius: f32,
|
||||
pub shadow: bool,
|
||||
/// Show the scrollbar mode, default: Scrolling
|
||||
pub scrollbar_show: ScrollbarShow,
|
||||
}
|
||||
|
||||
impl Deref for Theme {
|
||||
type Target = SystemColors;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Theme {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
||||
impl Theme {
|
||||
/// Returns the global theme reference
|
||||
pub fn global(cx: &App) -> &Theme {
|
||||
cx.global::<Theme>()
|
||||
}
|
||||
|
||||
/// Returns the global theme mutable reference
|
||||
pub fn global_mut(cx: &mut App) -> &mut Theme {
|
||||
cx.global_mut::<Theme>()
|
||||
}
|
||||
|
||||
/// Sync the theme with the system appearance
|
||||
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
|
||||
match cx.window_appearance() {
|
||||
WindowAppearance::Dark | WindowAppearance::VibrantDark => {
|
||||
Self::change(Appearance::Dark, window, cx)
|
||||
}
|
||||
WindowAppearance::Light | WindowAppearance::VibrantLight => {
|
||||
Self::change(Appearance::Light, window, cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change(mode: Appearance, window: Option<&mut Window>, cx: &mut App) {
|
||||
let theme = Theme::new(mode);
|
||||
cx.set_global(theme);
|
||||
|
||||
if let Some(window) = window {
|
||||
window.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
fn new(appearance: Appearance) -> Self {
|
||||
let color_scales = default_color_scales();
|
||||
let colors = match appearance {
|
||||
Appearance::Light => SystemColors::light(),
|
||||
Appearance::Dark => SystemColors::dark(),
|
||||
};
|
||||
|
||||
Theme {
|
||||
base: color_scales.gray,
|
||||
accent: color_scales.yellow,
|
||||
font_size: 15.0,
|
||||
font_family: if cfg!(target_os = "macos") {
|
||||
".SystemUIFont".into()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"Segoe UI".into()
|
||||
} else {
|
||||
"FreeMono".into()
|
||||
},
|
||||
radius: 5.0,
|
||||
shadow: false,
|
||||
scrollbar_show: ScrollbarShow::default(),
|
||||
appearance,
|
||||
colors,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
use crate::theme::{ActiveTheme, Appearance};
|
||||
use gpui::{App, Hsla, SharedString};
|
||||
|
||||
/// A collection of colors that are used to style the UI.
|
||||
///
|
||||
/// Each step has a semantic meaning, and is used to style different parts of the UI.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub struct ColorScaleStep(usize);
|
||||
|
||||
impl ColorScaleStep {
|
||||
pub const ONE: Self = Self(1);
|
||||
pub const TWO: Self = Self(2);
|
||||
pub const THREE: Self = Self(3);
|
||||
pub const FOUR: Self = Self(4);
|
||||
pub const FIVE: Self = Self(5);
|
||||
pub const SIX: Self = Self(6);
|
||||
pub const SEVEN: Self = Self(7);
|
||||
pub const EIGHT: Self = Self(8);
|
||||
pub const NINE: Self = Self(9);
|
||||
pub const TEN: Self = Self(10);
|
||||
pub const ELEVEN: Self = Self(11);
|
||||
pub const TWELVE: Self = Self(12);
|
||||
|
||||
/// All of the steps in a [`ColorScale`].
|
||||
pub const ALL: [ColorScaleStep; 12] = [
|
||||
Self::ONE,
|
||||
Self::TWO,
|
||||
Self::THREE,
|
||||
Self::FOUR,
|
||||
Self::FIVE,
|
||||
Self::SIX,
|
||||
Self::SEVEN,
|
||||
Self::EIGHT,
|
||||
Self::NINE,
|
||||
Self::TEN,
|
||||
Self::ELEVEN,
|
||||
Self::TWELVE,
|
||||
];
|
||||
}
|
||||
|
||||
/// A scale of colors for a given [`ColorScaleSet`].
|
||||
///
|
||||
/// Each [`ColorScale`] contains exactly 12 colors. Refer to
|
||||
/// [`ColorScaleStep`] for a reference of what each step is used for.
|
||||
pub struct ColorScale(Vec<Hsla>);
|
||||
|
||||
impl FromIterator<Hsla> for ColorScale {
|
||||
fn from_iter<T: IntoIterator<Item = Hsla>>(iter: T) -> Self {
|
||||
Self(Vec::from_iter(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorScale {
|
||||
/// Returns the specified step in the [`ColorScale`].
|
||||
#[inline]
|
||||
pub fn step(&self, step: ColorScaleStep) -> Hsla {
|
||||
// Steps are one-based, so we need convert to the zero-based vec index.
|
||||
self.0[step.0 - 1]
|
||||
}
|
||||
|
||||
/// `Step 1` - Used for main application backgrounds.
|
||||
///
|
||||
/// This step provides a neutral base for any overlaying components, ideal for applications' main backdrop or empty spaces such as canvas areas.
|
||||
///
|
||||
#[inline]
|
||||
pub fn step_1(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::ONE)
|
||||
}
|
||||
|
||||
/// `Step 2` - Used for both main application backgrounds and subtle component backgrounds.
|
||||
///
|
||||
/// Like `Step 1`, this step allows variations in background styles, from striped tables, sidebar backgrounds, to card backgrounds.
|
||||
#[inline]
|
||||
pub fn step_2(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::TWO)
|
||||
}
|
||||
|
||||
/// `Step 3` - Used for UI component backgrounds in their normal states.
|
||||
///
|
||||
/// This step maintains accessibility by guaranteeing a contrast ratio of 4.5:1 with steps 11 and 12 for text. It could also suit hover states for transparent components.
|
||||
#[inline]
|
||||
pub fn step_3(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::THREE)
|
||||
}
|
||||
|
||||
/// `Step 4` - Used for UI component backgrounds in their hover states.
|
||||
///
|
||||
/// Also suited for pressed or selected states of components with a transparent background.
|
||||
#[inline]
|
||||
pub fn step_4(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::FOUR)
|
||||
}
|
||||
|
||||
/// `Step 5` - Used for UI component backgrounds in their pressed or selected states.
|
||||
#[inline]
|
||||
pub fn step_5(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::FIVE)
|
||||
}
|
||||
|
||||
/// `Step 6` - Used for subtle borders on non-interactive components.
|
||||
///
|
||||
/// Its usage spans from sidebars' borders, headers' dividers, cards' outlines, to alerts' edges and separators.
|
||||
#[inline]
|
||||
pub fn step_6(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::SIX)
|
||||
}
|
||||
|
||||
/// `Step 7` - Used for subtle borders on interactive components.
|
||||
///
|
||||
/// This step subtly delineates the boundary of elements users interact with.
|
||||
#[inline]
|
||||
pub fn step_7(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::SEVEN)
|
||||
}
|
||||
|
||||
/// `Step 8` - Used for stronger borders on interactive components and focus rings.
|
||||
///
|
||||
/// It strengthens the visibility and accessibility of active elements and their focus states.
|
||||
#[inline]
|
||||
pub fn step_8(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::EIGHT)
|
||||
}
|
||||
|
||||
/// `Step 9` - Used for solid backgrounds.
|
||||
///
|
||||
/// `Step 9` is the most saturated step, having the least mix of white or black.
|
||||
///
|
||||
/// Due to its high chroma, `Step 9` is versatile and particularly useful for semantic colors such as
|
||||
/// error, warning, and success indicators.
|
||||
#[inline]
|
||||
pub fn step_9(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::NINE)
|
||||
}
|
||||
|
||||
/// `Step 10` - Used for hovered or active solid backgrounds, particularly when `Step 9` is their normal state.
|
||||
///
|
||||
/// May also be used for extremely low contrast text. This should be used sparingly, as it may be difficult to read.
|
||||
#[inline]
|
||||
pub fn step_10(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::TEN)
|
||||
}
|
||||
|
||||
/// `Step 11` - Used for text and icons requiring low contrast or less emphasis.
|
||||
#[inline]
|
||||
pub fn step_11(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::ELEVEN)
|
||||
}
|
||||
|
||||
/// `Step 12` - Used for text and icons requiring high contrast or prominence.
|
||||
#[inline]
|
||||
pub fn step_12(&self) -> Hsla {
|
||||
self.step(ColorScaleStep::TWELVE)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ColorScales {
|
||||
pub gray: ColorScaleSet,
|
||||
pub mauve: ColorScaleSet,
|
||||
pub slate: ColorScaleSet,
|
||||
pub sage: ColorScaleSet,
|
||||
pub olive: ColorScaleSet,
|
||||
pub sand: ColorScaleSet,
|
||||
pub gold: ColorScaleSet,
|
||||
pub bronze: ColorScaleSet,
|
||||
pub brown: ColorScaleSet,
|
||||
pub yellow: ColorScaleSet,
|
||||
pub amber: ColorScaleSet,
|
||||
pub orange: ColorScaleSet,
|
||||
pub tomato: ColorScaleSet,
|
||||
pub red: ColorScaleSet,
|
||||
pub ruby: ColorScaleSet,
|
||||
pub crimson: ColorScaleSet,
|
||||
pub pink: ColorScaleSet,
|
||||
pub plum: ColorScaleSet,
|
||||
pub purple: ColorScaleSet,
|
||||
pub violet: ColorScaleSet,
|
||||
pub iris: ColorScaleSet,
|
||||
pub indigo: ColorScaleSet,
|
||||
pub blue: ColorScaleSet,
|
||||
pub cyan: ColorScaleSet,
|
||||
pub teal: ColorScaleSet,
|
||||
pub jade: ColorScaleSet,
|
||||
pub green: ColorScaleSet,
|
||||
pub grass: ColorScaleSet,
|
||||
pub lime: ColorScaleSet,
|
||||
pub mint: ColorScaleSet,
|
||||
pub sky: ColorScaleSet,
|
||||
pub black: ColorScaleSet,
|
||||
pub white: ColorScaleSet,
|
||||
}
|
||||
|
||||
impl IntoIterator for ColorScales {
|
||||
type Item = ColorScaleSet;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
vec![
|
||||
self.gray,
|
||||
self.mauve,
|
||||
self.slate,
|
||||
self.sage,
|
||||
self.olive,
|
||||
self.sand,
|
||||
self.gold,
|
||||
self.bronze,
|
||||
self.brown,
|
||||
self.yellow,
|
||||
self.amber,
|
||||
self.orange,
|
||||
self.tomato,
|
||||
self.red,
|
||||
self.ruby,
|
||||
self.crimson,
|
||||
self.pink,
|
||||
self.plum,
|
||||
self.purple,
|
||||
self.violet,
|
||||
self.iris,
|
||||
self.indigo,
|
||||
self.blue,
|
||||
self.cyan,
|
||||
self.teal,
|
||||
self.jade,
|
||||
self.green,
|
||||
self.grass,
|
||||
self.lime,
|
||||
self.mint,
|
||||
self.sky,
|
||||
self.black,
|
||||
self.white,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides groups of [`ColorScale`]s for light and dark themes, as well as transparent versions of each scale.
|
||||
pub struct ColorScaleSet {
|
||||
name: SharedString,
|
||||
light: ColorScale,
|
||||
dark: ColorScale,
|
||||
light_alpha: ColorScale,
|
||||
dark_alpha: ColorScale,
|
||||
}
|
||||
|
||||
impl ColorScaleSet {
|
||||
pub fn new(
|
||||
name: impl Into<SharedString>,
|
||||
light: ColorScale,
|
||||
light_alpha: ColorScale,
|
||||
dark: ColorScale,
|
||||
dark_alpha: ColorScale,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
light,
|
||||
light_alpha,
|
||||
dark,
|
||||
dark_alpha,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &SharedString {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn light(&self) -> &ColorScale {
|
||||
&self.light
|
||||
}
|
||||
|
||||
pub fn light_alpha(&self) -> &ColorScale {
|
||||
&self.light_alpha
|
||||
}
|
||||
|
||||
pub fn dark(&self) -> &ColorScale {
|
||||
&self.dark
|
||||
}
|
||||
|
||||
pub fn dark_alpha(&self) -> &ColorScale {
|
||||
&self.dark_alpha
|
||||
}
|
||||
|
||||
pub fn step(&self, cx: &App, step: ColorScaleStep) -> Hsla {
|
||||
match cx.theme().appearance {
|
||||
Appearance::Light => self.light().step(step),
|
||||
Appearance::Dark => self.dark().step(step),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step_alpha(&self, cx: &App, step: ColorScaleStep) -> Hsla {
|
||||
match cx.theme().appearance {
|
||||
Appearance::Light => self.light_alpha.step(step),
|
||||
Appearance::Dark => self.dark_alpha.step(step),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn darken(&self, cx: &App) -> Hsla {
|
||||
match cx.theme().appearance {
|
||||
Appearance::Light => self.light.step_12(),
|
||||
Appearance::Dark => self.dark.step_1(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
use crate::{h_flex, theme::ActiveTheme, Icon, IconName, InteractiveElementExt as _, Sizable as _};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
black, div, prelude::FluentBuilder as _, px, relative, white, AnyElement, App, ClickEvent, Div,
|
||||
Element, Hsla, InteractiveElement as _, IntoElement, MouseButton, ParentElement, Pixels,
|
||||
RenderOnce, Rgba, Stateful, StatefulInteractiveElement as _, Style, Styled, Window,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, Icon, IconName, InteractiveElementExt as _, Sizable as _};
|
||||
|
||||
const HEIGHT: Pixels = px(34.);
|
||||
const TITLE_BAR_HEIGHT: Pixels = px(34.);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const TITLE_BAR_LEFT_PADDING: Pixels = px(80.);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
const TITLE_BAR_LEFT_PADDING: Pixels = px(12.);
|
||||
|
||||
@@ -105,7 +110,7 @@ impl Control {
|
||||
}
|
||||
|
||||
fn fg(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
if cx.theme().appearance.is_dark() {
|
||||
if cx.theme().mode.is_dark() {
|
||||
white()
|
||||
} else {
|
||||
black()
|
||||
@@ -113,7 +118,7 @@ impl Control {
|
||||
}
|
||||
|
||||
fn hover_fg(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
if self.is_close() || cx.theme().appearance.is_dark() {
|
||||
if self.is_close() || cx.theme().mode.is_dark() {
|
||||
white()
|
||||
} else {
|
||||
black()
|
||||
@@ -128,7 +133,7 @@ impl Control {
|
||||
b: 32.0 / 255.0,
|
||||
a: 1.0,
|
||||
}
|
||||
} else if cx.theme().appearance.is_dark() {
|
||||
} else if cx.theme().mode.is_dark() {
|
||||
Rgba {
|
||||
r: 0.9,
|
||||
g: 0.9,
|
||||
@@ -247,7 +252,7 @@ impl RenderOnce for TitleBar {
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.h(HEIGHT)
|
||||
.bg(cx.theme().transparent)
|
||||
.bg(cx.theme().title_bar)
|
||||
.when(window.is_fullscreen(), |this| this.pl(px(12.)))
|
||||
.on_double_click(|_, window, _cx| window.zoom_window())
|
||||
.child(
|
||||
|
||||
@@ -2,8 +2,7 @@ use gpui::{
|
||||
div, relative, App, AppContext, Context, Entity, IntoElement, ParentElement, Render,
|
||||
SharedString, Styled, Window,
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
pub struct Tooltip {
|
||||
text: SharedString,
|
||||
@@ -18,18 +17,17 @@ impl Tooltip {
|
||||
impl Render for Tooltip {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().child(
|
||||
// Wrap in a child, to ensure the left margin is applied to the tooltip
|
||||
div()
|
||||
.font_family(".SystemUIFont")
|
||||
.m_3()
|
||||
.p_2()
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::TWO))
|
||||
.border_color(cx.theme().border)
|
||||
.bg(cx.theme().surface_background)
|
||||
.shadow_lg()
|
||||
.rounded_lg()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.line_height(relative(1.25))
|
||||
.child(self.text.clone()),
|
||||
)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use crate::theme::ActiveTheme;
|
||||
use gpui::{
|
||||
canvas, div, point, prelude::FluentBuilder as _, px, AnyElement, App, Bounds, CursorStyle,
|
||||
Decorations, Edges, Hsla, InteractiveElement as _, IntoElement, MouseButton, ParentElement,
|
||||
Pixels, Point, RenderOnce, ResizeEdge, Size, Styled as _, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
pub(crate) const BORDER_SIZE: Pixels = Pixels(1.0);
|
||||
pub(crate) const BORDER_RADIUS: Pixels = Pixels(0.0);
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub(crate) const SHADOW_SIZE: Pixels = Pixels(0.0);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(crate) const SHADOW_SIZE: Pixels = Pixels(12.0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user