Add hover behaviour to tabs

Co-Authored-By: Marshall <marshall@zed.dev>
Co-Authored-By: Nathan <nathan@zed.dev>
This commit is contained in:
Conrad Irwin 2023-11-03 10:54:26 -06:00
parent d3b02c4de4
commit c604a2e34e
6 changed files with 71 additions and 5 deletions

View file

@ -212,6 +212,19 @@ pub trait Component<V> {
{ {
self.map(|this| if condition { then(this) } else { this }) self.map(|this| if condition { then(this) } else { this })
} }
fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
where
Self: Sized,
{
self.map(|this| {
if let Some(value) = option {
then(this, value)
} else {
this
}
})
}
} }
impl<V> Component<V> for AnyElement<V> { impl<V> Component<V> for AnyElement<V> {

View file

@ -3,7 +3,7 @@ use crate::{
ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable, ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement, GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement,
Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction, Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, Visibility,
}; };
use refineable::Refineable; use refineable::Refineable;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -249,11 +249,15 @@ where
cx: &mut ViewContext<V>, cx: &mut ViewContext<V>,
) { ) {
self.with_element_id(cx, |this, _global_id, cx| { self.with_element_id(cx, |this, _global_id, cx| {
let style = this.compute_style(bounds, element_state, cx);
if style.visibility == Visibility::Hidden {
return;
}
if let Some(group) = this.group.clone() { if let Some(group) = this.group.clone() {
GroupBounds::push(group, bounds, cx); GroupBounds::push(group, bounds, cx);
} }
let style = this.compute_style(bounds, element_state, cx);
let z_index = style.z_index.unwrap_or(0); let z_index = style.z_index.unwrap_or(0);
let mut child_min = point(Pixels::MAX, Pixels::MAX); let mut child_min = point(Pixels::MAX, Pixels::MAX);

View file

@ -19,6 +19,9 @@ pub struct Style {
/// What layout strategy should be used? /// What layout strategy should be used?
pub display: Display, pub display: Display,
/// Should the element be painted on screen?
pub visibility: Visibility,
// Overflow properties // Overflow properties
/// How children overflowing their container should affect layout /// How children overflowing their container should affect layout
#[refineable] #[refineable]
@ -107,6 +110,13 @@ impl Styled for StyleRefinement {
} }
} }
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
pub enum Visibility {
#[default]
Visible,
Hidden,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BoxShadow { pub struct BoxShadow {
pub color: Hsla, pub color: Hsla,
@ -297,6 +307,7 @@ impl Default for Style {
fn default() -> Self { fn default() -> Self {
Style { Style {
display: Display::Block, display: Display::Block,
visibility: Visibility::Visible,
overflow: Point { overflow: Point {
x: Overflow::Visible, x: Overflow::Visible,
y: Overflow::Visible, y: Overflow::Visible,

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
self as gpui2, hsla, point, px, relative, rems, AlignItems, DefiniteLength, Display, Fill, self as gpui2, hsla, point, px, relative, rems, AlignItems, DefiniteLength, Display, Fill,
FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, StyleRefinement, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, StyleRefinement,
Visibility,
}; };
use crate::{BoxShadow, TextStyleRefinement}; use crate::{BoxShadow, TextStyleRefinement};
use smallvec::smallvec; use smallvec::smallvec;
@ -60,6 +61,26 @@ pub trait Styled {
self self
} }
/// Sets the visibility of the element to `visible`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn visible(mut self) -> Self
where
Self: Sized,
{
self.style().visibility = Some(Visibility::Visible);
self
}
/// Sets the visibility of the element to `hidden`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn invisible(mut self) -> Self
where
Self: Sized,
{
self.style().visibility = Some(Visibility::Hidden);
self
}
/// Sets the flex direction of the element to `column`. /// Sets the flex direction of the element to `column`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#column) /// [Docs](https://tailwindcss.com/docs/flex-direction#column)
fn flex_col(mut self) -> Self fn flex_col(mut self) -> Self

View file

@ -159,6 +159,7 @@ impl Icon {
pub struct IconElement { pub struct IconElement {
icon: Icon, icon: Icon,
color: IconColor, color: IconColor,
hover_color: Option<IconColor>,
size: IconSize, size: IconSize,
} }
@ -167,6 +168,7 @@ impl IconElement {
Self { Self {
icon, icon,
color: IconColor::default(), color: IconColor::default(),
hover_color: None,
size: IconSize::default(), size: IconSize::default(),
} }
} }
@ -176,13 +178,17 @@ impl IconElement {
self self
} }
pub fn hover_color(mut self, hover_color: impl Into<Option<IconColor>>) -> Self {
self.hover_color = hover_color.into();
self
}
pub fn size(mut self, size: IconSize) -> Self { pub fn size(mut self, size: IconSize) -> Self {
self.size = size; self.size = size;
self self
} }
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
let fill = self.color.color(cx);
let svg_size = match self.size { let svg_size = match self.size {
IconSize::Small => rems(0.75), IconSize::Small => rems(0.75),
IconSize::Medium => rems(0.9375), IconSize::Medium => rems(0.9375),
@ -192,7 +198,10 @@ impl IconElement {
.size(svg_size) .size(svg_size)
.flex_none() .flex_none()
.path(self.icon.path()) .path(self.icon.path())
.text_color(fill) .text_color(self.color.color(cx))
.when_some(self.hover_color, |this, hover_color| {
this.hover(|style| style.text_color(hover_color.color(cx)))
})
} }
} }

View file

@ -1361,9 +1361,16 @@ impl Pane {
let label = item.tab_content(Some(detail), cx); let label = item.tab_content(Some(detail), cx);
let close_icon = || { let close_icon = || {
let id = item.id(); let id = item.id();
div() div()
.id(item.id()) .id(item.id())
.child(IconElement::new(Icon::Close).color(IconColor::Muted)) .invisible()
.group_hover("", |style| style.visible())
.child(
IconElement::new(Icon::Close)
.color(IconColor::Muted)
.hover_color(IconColor::Accent),
)
.on_click(move |pane: &mut Self, _, cx| { .on_click(move |pane: &mut Self, _, cx| {
pane.close_item_by_id(id, SaveIntent::Close, cx) pane.close_item_by_id(id, SaveIntent::Close, cx)
.detach_and_log_err(cx); .detach_and_log_err(cx);
@ -1388,6 +1395,7 @@ impl Pane {
let close_right = ItemSettings::get_global(cx).close_position.right(); let close_right = ItemSettings::get_global(cx).close_position.right();
div() div()
.group("")
.id(item.id()) .id(item.id())
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target)) // .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))