Merge branch 'main' into project_search2
This commit is contained in:
commit
ddbd5cf2cb
145 changed files with 16798 additions and 12900 deletions
|
@ -13,6 +13,7 @@ mod popover;
|
|||
mod popover_menu;
|
||||
mod right_click_menu;
|
||||
mod stack;
|
||||
mod tab;
|
||||
mod tooltip;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
|
@ -33,6 +34,7 @@ pub use popover::*;
|
|||
pub use popover_menu::*;
|
||||
pub use right_click_menu::*;
|
||||
pub use stack::*;
|
||||
pub use tab::*;
|
||||
pub use tooltip::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::prelude::*;
|
||||
use gpui::{img, rems, Div, ImageData, ImageSource, IntoElement, Styled};
|
||||
use gpui::{img, Div, Hsla, ImageData, ImageSource, Img, IntoElement, Styled};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub enum Shape {
|
||||
|
@ -12,35 +11,39 @@ pub enum Shape {
|
|||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Avatar {
|
||||
src: ImageSource,
|
||||
image: Img,
|
||||
border_color: Option<Hsla>,
|
||||
is_available: Option<bool>,
|
||||
shape: Shape,
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let mut img = img(self.src);
|
||||
|
||||
if self.shape == Shape::Circle {
|
||||
img = img.rounded_full();
|
||||
} else {
|
||||
img = img.rounded_md();
|
||||
fn render(mut self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
if self.image.style().corner_radii.top_left.is_none() {
|
||||
self = self.shape(Shape::Circle);
|
||||
}
|
||||
|
||||
let size = rems(1.0);
|
||||
let size = cx.rem_size();
|
||||
|
||||
div()
|
||||
.size(size)
|
||||
.size(size + px(2.))
|
||||
.map(|mut div| {
|
||||
div.style().corner_radii = self.image.style().corner_radii.clone();
|
||||
div
|
||||
})
|
||||
.when_some(self.border_color, |this, color| {
|
||||
this.border().border_color(color)
|
||||
})
|
||||
.child(
|
||||
img.size(size)
|
||||
self.image
|
||||
.size(size)
|
||||
// todo!(Pull the avatar fallback background from the theme.)
|
||||
.bg(gpui::red()),
|
||||
)
|
||||
.children(self.is_available.map(|is_free| {
|
||||
// HACK: non-integer sizes result in oval indicators.
|
||||
let indicator_size = (size.0 * cx.rem_size() * 0.4).round();
|
||||
let indicator_size = (size * 0.4).round();
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
|
@ -56,31 +59,39 @@ impl RenderOnce for Avatar {
|
|||
|
||||
impl Avatar {
|
||||
pub fn uri(src: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
src: src.into().into(),
|
||||
shape: Shape::Circle,
|
||||
is_available: None,
|
||||
}
|
||||
Self::source(src.into().into())
|
||||
}
|
||||
|
||||
pub fn data(src: Arc<ImageData>) -> Self {
|
||||
Self {
|
||||
src: src.into(),
|
||||
shape: Shape::Circle,
|
||||
is_available: None,
|
||||
}
|
||||
Self::source(src.into())
|
||||
}
|
||||
|
||||
pub fn source(src: ImageSource) -> Self {
|
||||
Self {
|
||||
src,
|
||||
shape: Shape::Circle,
|
||||
image: img(src),
|
||||
is_available: None,
|
||||
border_color: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape(mut self, shape: Shape) -> Self {
|
||||
self.shape = shape;
|
||||
self.image = match shape {
|
||||
Shape::Circle => self.image.rounded_full(),
|
||||
Shape::RoundedRectangle => self.image.rounded_md(),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||
self.image = self.image.grayscale(grayscale);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||
self.border_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn availability_indicator(mut self, is_available: impl Into<Option<bool>>) -> Self {
|
||||
self.is_available = is_available.into();
|
||||
self
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use gpui::{AnyView, DefiniteLength};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{prelude::*, IconPosition};
|
||||
use crate::{
|
||||
ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize, Label, LineHeightStyle,
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ pub struct Button {
|
|||
label_color: Option<Color>,
|
||||
selected_label: Option<SharedString>,
|
||||
icon: Option<Icon>,
|
||||
icon_position: Option<IconPosition>,
|
||||
icon_size: Option<IconSize>,
|
||||
icon_color: Option<Color>,
|
||||
selected_icon: Option<Icon>,
|
||||
|
@ -27,6 +28,7 @@ impl Button {
|
|||
label_color: None,
|
||||
selected_label: None,
|
||||
icon: None,
|
||||
icon_position: None,
|
||||
icon_size: None,
|
||||
icon_color: None,
|
||||
selected_icon: None,
|
||||
|
@ -48,6 +50,11 @@ impl Button {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn icon_position(mut self, icon_position: impl Into<Option<IconPosition>>) -> Self {
|
||||
self.icon_position = icon_position.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
|
||||
self.icon_size = icon_size.into();
|
||||
self
|
||||
|
@ -141,19 +148,29 @@ impl RenderOnce for Button {
|
|||
self.label_color.unwrap_or_default()
|
||||
};
|
||||
|
||||
self.base
|
||||
.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
.child(
|
||||
Label::new(label)
|
||||
.color(label_color)
|
||||
.line_height_style(LineHeightStyle::UILabel),
|
||||
)
|
||||
self.base.child(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.map(|this| {
|
||||
if self.icon_position == Some(IconPosition::End) {
|
||||
this.flex_row_reverse()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
})
|
||||
.child(
|
||||
Label::new(label)
|
||||
.color(label_color)
|
||||
.line_height_style(LineHeightStyle::UILabel),
|
||||
)
|
||||
.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,13 @@ pub trait ButtonCommon: Clickable + Disableable {
|
|||
fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum IconPosition {
|
||||
#[default]
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum ButtonStyle {
|
||||
/// A filled button with a solid background color. Provides emphasis versus
|
||||
|
@ -342,8 +349,12 @@ impl RenderOnce for ButtonLike {
|
|||
.when_some(self.width, |this, width| this.w(width))
|
||||
.rounded_md()
|
||||
.gap_1()
|
||||
.px_1()
|
||||
.map(|this| match self.size {
|
||||
ButtonSize::Default | ButtonSize::Compact => this.px_1(),
|
||||
ButtonSize::None => this,
|
||||
})
|
||||
.bg(self.style.enabled(cx).background)
|
||||
.when(self.disabled, |this| this.cursor_not_allowed())
|
||||
.when(!self.disabled, |this| {
|
||||
this.cursor_pointer()
|
||||
.hover(|hover| hover.bg(self.style.hovered(cx).background))
|
||||
|
|
|
@ -239,7 +239,6 @@ impl Render for ContextMenu {
|
|||
action,
|
||||
} => {
|
||||
let handler = handler.clone();
|
||||
let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent));
|
||||
|
||||
let label_element = if let Some(icon) = icon {
|
||||
h_stack()
|
||||
|
@ -263,10 +262,7 @@ impl Render for ContextMenu {
|
|||
})),
|
||||
)
|
||||
.selected(Some(ix) == self.selected_index)
|
||||
.on_click(move |event, cx| {
|
||||
handler(cx);
|
||||
dismiss(event, cx)
|
||||
})
|
||||
.on_click(move |_, cx| handler(cx))
|
||||
.into_any_element()
|
||||
}
|
||||
},
|
||||
|
|
|
@ -51,6 +51,7 @@ pub enum Icon {
|
|||
CopilotDisabled,
|
||||
Dash,
|
||||
Envelope,
|
||||
ExternalLink,
|
||||
ExclamationTriangle,
|
||||
Exit,
|
||||
File,
|
||||
|
@ -124,13 +125,13 @@ impl Icon {
|
|||
Icon::Close => "icons/x.svg",
|
||||
Icon::Collab => "icons/user_group_16.svg",
|
||||
Icon::Copilot => "icons/copilot.svg",
|
||||
|
||||
Icon::CopilotInit => "icons/copilot_init.svg",
|
||||
Icon::CopilotError => "icons/copilot_error.svg",
|
||||
Icon::CopilotDisabled => "icons/copilot_disabled.svg",
|
||||
Icon::Dash => "icons/dash.svg",
|
||||
Icon::Envelope => "icons/feedback.svg",
|
||||
Icon::ExclamationTriangle => "icons/warning.svg",
|
||||
Icon::ExternalLink => "icons/external_link.svg",
|
||||
Icon::Exit => "icons/exit.svg",
|
||||
Icon::File => "icons/file.svg",
|
||||
Icon::FileDoc => "icons/file_icons/book.svg",
|
||||
|
|
|
@ -98,11 +98,13 @@ impl RenderOnce for Key {
|
|||
|
||||
div()
|
||||
.py_0()
|
||||
.when_else(
|
||||
single_char,
|
||||
|el| el.w(rems(14. / 16.)).flex().flex_none().justify_center(),
|
||||
|el| el.px_0p5(),
|
||||
)
|
||||
.map(|this| {
|
||||
if single_char {
|
||||
this.w(rems(14. / 16.)).flex().flex_none().justify_center()
|
||||
} else {
|
||||
this.px_0p5()
|
||||
}
|
||||
})
|
||||
.h(rems(14. / 16.))
|
||||
.text_ui()
|
||||
.line_height(relative(1.))
|
||||
|
|
|
@ -10,6 +10,7 @@ mod label;
|
|||
mod list;
|
||||
mod list_header;
|
||||
mod list_item;
|
||||
mod tab;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use button::*;
|
||||
|
@ -23,3 +24,4 @@ pub use label::*;
|
|||
pub use list::*;
|
||||
pub use list_header::*;
|
||||
pub use list_item::*;
|
||||
pub use tab::*;
|
||||
|
|
|
@ -4,7 +4,7 @@ use story::Story;
|
|||
use crate::prelude::*;
|
||||
use crate::{right_click_menu, ContextMenu, Label};
|
||||
|
||||
actions!(PrintCurrentDate, PrintBestFood);
|
||||
actions!(context_menu, [PrintCurrentDate, PrintBestFood]);
|
||||
|
||||
fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
|
||||
ContextMenu::build(cx, |menu, _| {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use gpui::{actions, Div, Render};
|
||||
use gpui::NoAction;
|
||||
use gpui::{Div, Render};
|
||||
use itertools::Itertools;
|
||||
use story::Story;
|
||||
|
||||
|
@ -7,8 +8,6 @@ use crate::KeyBinding;
|
|||
|
||||
pub struct KeybindingStory;
|
||||
|
||||
actions!(NoAction);
|
||||
|
||||
pub fn binding(key: &str) -> gpui::KeyBinding {
|
||||
gpui::KeyBinding::new(key, NoAction {}, None)
|
||||
}
|
||||
|
|
114
crates/ui2/src/components/stories/tab.rs
Normal file
114
crates/ui2/src/components/stories/tab.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use gpui::{Div, Render};
|
||||
use story::Story;
|
||||
|
||||
use crate::{prelude::*, TabPosition};
|
||||
use crate::{Indicator, Tab};
|
||||
|
||||
pub struct TabStory;
|
||||
|
||||
impl Render for TabStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container()
|
||||
.child(Story::title_for::<Tab>())
|
||||
.child(Story::label("Default"))
|
||||
.child(h_stack().child(Tab::new("tab_1").child("Tab 1")))
|
||||
.child(Story::label("With indicator"))
|
||||
.child(
|
||||
h_stack().child(
|
||||
Tab::new("tab_1")
|
||||
.start_slot(Indicator::dot().color(Color::Warning))
|
||||
.child("Tab 1"),
|
||||
),
|
||||
)
|
||||
.child(Story::label("With close button"))
|
||||
.child(
|
||||
h_stack().child(
|
||||
Tab::new("tab_1")
|
||||
.end_slot(
|
||||
IconButton::new("close_button", Icon::Close)
|
||||
.icon_color(Color::Muted)
|
||||
.size(ButtonSize::None)
|
||||
.icon_size(IconSize::XSmall),
|
||||
)
|
||||
.child("Tab 1"),
|
||||
),
|
||||
)
|
||||
.child(Story::label("List of tabs"))
|
||||
.child(
|
||||
h_stack()
|
||||
.child(Tab::new("tab_1").child("Tab 1"))
|
||||
.child(Tab::new("tab_2").child("Tab 2")),
|
||||
)
|
||||
.child(Story::label("List of tabs with first tab selected"))
|
||||
.child(
|
||||
h_stack()
|
||||
.child(
|
||||
Tab::new("tab_1")
|
||||
.selected(true)
|
||||
.position(TabPosition::First)
|
||||
.child("Tab 1"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_2")
|
||||
.position(TabPosition::Middle(Ordering::Greater))
|
||||
.child("Tab 2"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_3")
|
||||
.position(TabPosition::Middle(Ordering::Greater))
|
||||
.child("Tab 3"),
|
||||
)
|
||||
.child(Tab::new("tab_4").position(TabPosition::Last).child("Tab 4")),
|
||||
)
|
||||
.child(Story::label("List of tabs with last tab selected"))
|
||||
.child(
|
||||
h_stack()
|
||||
.child(
|
||||
Tab::new("tab_1")
|
||||
.position(TabPosition::First)
|
||||
.child("Tab 1"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_2")
|
||||
.position(TabPosition::Middle(Ordering::Less))
|
||||
.child("Tab 2"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_3")
|
||||
.position(TabPosition::Middle(Ordering::Less))
|
||||
.child("Tab 3"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_4")
|
||||
.position(TabPosition::Last)
|
||||
.selected(true)
|
||||
.child("Tab 4"),
|
||||
),
|
||||
)
|
||||
.child(Story::label("List of tabs with second tab selected"))
|
||||
.child(
|
||||
h_stack()
|
||||
.child(
|
||||
Tab::new("tab_1")
|
||||
.position(TabPosition::First)
|
||||
.child("Tab 1"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_2")
|
||||
.position(TabPosition::Middle(Ordering::Equal))
|
||||
.selected(true)
|
||||
.child("Tab 2"),
|
||||
)
|
||||
.child(
|
||||
Tab::new("tab_3")
|
||||
.position(TabPosition::Middle(Ordering::Greater))
|
||||
.child("Tab 3"),
|
||||
)
|
||||
.child(Tab::new("tab_4").position(TabPosition::Last).child("Tab 4")),
|
||||
)
|
||||
}
|
||||
}
|
198
crates/ui2/src/components/tab.rs
Normal file
198
crates/ui2/src/components/tab.rs
Normal file
|
@ -0,0 +1,198 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{AnyElement, AnyView, ClickEvent, IntoElement, MouseButton};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The position of a [`Tab`] within a list of tabs.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TabPosition {
|
||||
/// The tab is first in the list.
|
||||
First,
|
||||
|
||||
/// The tab is in the middle of the list (i.e., it is not the first or last tab).
|
||||
///
|
||||
/// The [`Ordering`] is where this tab is positioned with respect to the selected tab.
|
||||
Middle(Ordering),
|
||||
|
||||
/// The tab is last in the list.
|
||||
Last,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TabCloseSide {
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Tab {
|
||||
id: ElementId,
|
||||
selected: bool,
|
||||
position: TabPosition,
|
||||
close_side: TabCloseSide,
|
||||
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
|
||||
start_slot: Option<AnyElement>,
|
||||
end_slot: Option<AnyElement>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
selected: false,
|
||||
position: TabPosition::First,
|
||||
close_side: TabCloseSide::End,
|
||||
on_click: None,
|
||||
tooltip: None,
|
||||
start_slot: None,
|
||||
end_slot: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(mut self, position: TabPosition) -> Self {
|
||||
self.position = position;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn close_side(mut self, close_side: TabCloseSide) -> Self {
|
||||
self.close_side = close_side;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||
self.on_click = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn start_slot<E: IntoElement>(mut self, element: impl Into<Option<E>>) -> Self {
|
||||
self.start_slot = element.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn end_slot<E: IntoElement>(mut self, element: impl Into<Option<E>>) -> Self {
|
||||
self.end_slot = element.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for Tab {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Tab {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Tab {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
const HEIGHT_IN_REMS: f32 = 30. / 16.;
|
||||
|
||||
let (text_color, tab_bg, _tab_hover_bg, _tab_active_bg) = match self.selected {
|
||||
false => (
|
||||
cx.theme().colors().text_muted,
|
||||
cx.theme().colors().tab_inactive_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
),
|
||||
true => (
|
||||
cx.theme().colors().text,
|
||||
cx.theme().colors().tab_active_background,
|
||||
cx.theme().colors().element_hover,
|
||||
cx.theme().colors().element_active,
|
||||
),
|
||||
};
|
||||
|
||||
div()
|
||||
.h(rems(HEIGHT_IN_REMS))
|
||||
.bg(tab_bg)
|
||||
.border_color(cx.theme().colors().border)
|
||||
.map(|this| match self.position {
|
||||
TabPosition::First => {
|
||||
if self.selected {
|
||||
this.pl_px().border_r().pb_px()
|
||||
} else {
|
||||
this.pl_px().pr_px().border_b()
|
||||
}
|
||||
}
|
||||
TabPosition::Last => {
|
||||
if self.selected {
|
||||
this.border_l().border_r().pb_px()
|
||||
} else {
|
||||
this.pr_px().pl_px().border_b()
|
||||
}
|
||||
}
|
||||
TabPosition::Middle(Ordering::Equal) => this.border_l().border_r().pb_px(),
|
||||
TabPosition::Middle(Ordering::Less) => this.border_l().pr_px().border_b(),
|
||||
TabPosition::Middle(Ordering::Greater) => this.border_r().pl_px().border_b(),
|
||||
})
|
||||
.child(
|
||||
h_stack()
|
||||
.group("")
|
||||
.id(self.id)
|
||||
.relative()
|
||||
.h_full()
|
||||
.px_5()
|
||||
.gap_1()
|
||||
.text_color(text_color)
|
||||
// .hover(|style| style.bg(tab_hover_bg))
|
||||
// .active(|style| style.bg(tab_active_bg))
|
||||
.when_some(self.on_click, |tab, on_click| {
|
||||
tab.cursor_pointer().on_click(move |event, cx| {
|
||||
// HACK: GPUI currently fires `on_click` with any mouse button,
|
||||
// but we only care about the left button.
|
||||
if event.down.button == MouseButton::Left {
|
||||
(on_click)(event, cx)
|
||||
}
|
||||
})
|
||||
})
|
||||
.when_some(self.tooltip, |tab, tooltip| {
|
||||
tab.tooltip(move |cx| tooltip(cx))
|
||||
})
|
||||
.child(
|
||||
h_stack()
|
||||
.w_3()
|
||||
.h_3()
|
||||
.justify_center()
|
||||
.absolute()
|
||||
.map(|this| match self.close_side {
|
||||
TabCloseSide::Start => this.right_1(),
|
||||
TabCloseSide::End => this.left_1(),
|
||||
})
|
||||
.children(self.start_slot),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.invisible()
|
||||
.w_3()
|
||||
.h_3()
|
||||
.justify_center()
|
||||
.absolute()
|
||||
.map(|this| match self.close_side {
|
||||
TabCloseSide::Start => this.left_1(),
|
||||
TabCloseSide::End => this.right_1(),
|
||||
})
|
||||
.group_hover("", |style| style.visible())
|
||||
.children(self.end_slot),
|
||||
)
|
||||
.children(self.children),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -12,6 +12,6 @@ pub use crate::selectable::*;
|
|||
pub use crate::{h_stack, v_stack};
|
||||
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton};
|
||||
pub use crate::{ButtonCommon, Color, StyledExt};
|
||||
pub use crate::{Icon, IconElement, IconSize};
|
||||
pub use crate::{Icon, IconElement, IconPosition, IconSize};
|
||||
pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle};
|
||||
pub use theme::ActiveTheme;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue