diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs index 0d15a411..d8ef8780 100644 --- a/crates/story/src/table_story.rs +++ b/crates/story/src/table_story.rs @@ -33,8 +33,8 @@ impl_internal_actions!(table_story, [ChangeSize, OpenDetail]); #[derive(Clone, Debug, Default)] struct Stock { id: usize, - symbol: String, - name: String, + symbol: SharedString, + name: SharedString, price: f64, change: f64, change_percent: f64, @@ -103,8 +103,8 @@ fn random_stocks(size: usize) -> Vec { (0..size) .map(|id| Stock { id, - symbol: Faker.fake::(), - name: Faker.fake::(), + symbol: Faker.fake::().into(), + name: Faker.fake::().into(), change: (-100.0..100.0).fake(), change_percent: (-1.0..1.0).fake(), volume: (0.0..1000.0).fake(), diff --git a/crates/ui/src/dock/dock.rs b/crates/ui/src/dock/dock.rs index efd9652f..b1c668e6 100644 --- a/crates/ui/src/dock/dock.rs +++ b/crates/ui/src/dock/dock.rs @@ -3,9 +3,10 @@ use std::sync::Arc; use gpui::{ - div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Empty, Entity, - InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, - Point, Render, StatefulInteractiveElement, Style, Styled as _, WeakEntity, Window, + div, prelude::FluentBuilder as _, px, AnyView, App, AppContext, Axis, Context, Element, Empty, + Entity, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, + Pixels, Point, Render, StatefulInteractiveElement, Style, StyleRefinement, Styled as _, + WeakEntity, Window, }; use serde::{Deserialize, Serialize}; @@ -394,6 +395,8 @@ impl Render for Dock { return div(); } + let cache_style = StyleRefinement::default().v_flex().size_full(); + div() .relative() .overflow_hidden() @@ -408,8 +411,10 @@ impl Render for Dock { }) .map(|this| match &self.panel { DockItem::Split { view, .. } => this.child(view.clone()), - DockItem::Tabs { view, .. } => this.child(view.clone()), - DockItem::Panel { view, .. } => this.child(view.clone().view()), + DockItem::Tabs { view, .. } => { + this.child(AnyView::from(view.clone()).cached(cache_style)) + } + DockItem::Panel { view, .. } => this.child(view.clone().view().cached(cache_style)), // Not support to render Tiles and Tile into Dock DockItem::Tiles { .. } => this, }) diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index d351bc46..8c1d0ca7 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -4,7 +4,7 @@ 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, + SharedString, StatefulInteractiveElement, StyleRefinement, Styled, WeakEntity, Window, }; use rust_i18n::t; @@ -14,7 +14,7 @@ use crate::{ h_flex, popup_menu::{PopupMenu, PopupMenuExt}, tab::{Tab, TabBar}, - v_flex, ActiveTheme, AxisExt, IconName, Placement, Selectable, Sizable, + v_flex, ActiveTheme, AxisExt, IconName, Placement, Selectable, Sizable, StyledExt as _, }; use super::{ @@ -767,7 +767,11 @@ impl TabPanel { .overflow_y_scroll() .overflow_x_hidden() .flex_1() - .child(active_panel.view()) + .child( + active_panel + .view() + .cached(StyleRefinement::default().v_flex().size_full()), + ) .when(state.droppable, |this| { this.on_drag_move(cx.listener(Self::on_panel_drag_move)) .child( diff --git a/crates/ui/src/scroll/scrollbar.rs b/crates/ui/src/scroll/scrollbar.rs index 396d91ae..875c5283 100644 --- a/crates/ui/src/scroll/scrollbar.rs +++ b/crates/ui/src/scroll/scrollbar.rs @@ -589,201 +589,209 @@ impl Element for Scrollbar { let is_visible = self.state.get().is_scrollbar_visible(); let is_hover_to_show = cx.theme().scrollbar_show.is_hover(); - for state in prepaint.states.iter() { - let axis = state.axis; - let radius = state.radius; - let bounds = state.bounds; - let thumb_bounds = state.thumb_bounds; - let scroll_area_size = state.scroll_size; - let container_size = state.container_size; - let thumb_size = state.thumb_size; - let margin_end = state.margin_end; - let is_vertical = axis.is_vertical(); - - window.set_cursor_style(CursorStyle::default(), &state.bar_hitbox); - - window.paint_layer(hitbox_bounds, |cx| { - cx.paint_quad(fill(state.bounds, state.bg)); - - cx.paint_quad(PaintQuad { - bounds, - corner_radii: (0.).into(), - background: gpui::transparent_black().into(), - border_widths: if is_vertical { - Edges { - top: px(0.), - right: px(0.), - bottom: px(0.), - left: BORDER_WIDTH, - } - } else { - Edges { - top: BORDER_WIDTH, - right: px(0.), - bottom: px(0.), - left: px(0.), - } - }, - border_color: state.border, - }); - - cx.paint_quad(fill(state.thumb_fill_bounds, state.thumb_bg).corner_radii(radius)); - }); - - window.on_mouse_event({ - let state = self.state.clone(); - let view_id = self.view_id; - let scroll_handle = self.scroll_handle.clone(); - - move |event: &ScrollWheelEvent, phase, _, cx| { - if phase.bubble() && hitbox_bounds.contains(&event.position) { - if scroll_handle.offset() != state.get().last_scroll_offset { - state.set( - state - .get() - .with_last_scroll(scroll_handle.offset(), Some(Instant::now())), - ); - cx.notify(view_id); + window.with_content_mask( + Some(ContentMask { + bounds: hitbox_bounds, + }), + |window| { + for state in prepaint.states.iter() { + let axis = state.axis; + let radius = state.radius; + let bounds = state.bounds; + let thumb_bounds = state.thumb_bounds; + let scroll_area_size = state.scroll_size; + let container_size = state.container_size; + let thumb_size = state.thumb_size; + let margin_end = state.margin_end; + let is_vertical = axis.is_vertical(); + + window.set_cursor_style(CursorStyle::default(), &state.bar_hitbox); + + window.paint_layer(hitbox_bounds, |cx| { + cx.paint_quad(fill(state.bounds, state.bg)); + + cx.paint_quad(PaintQuad { + bounds, + corner_radii: (0.).into(), + background: gpui::transparent_black().into(), + border_widths: if is_vertical { + Edges { + top: px(0.), + right: px(0.), + bottom: px(0.), + left: BORDER_WIDTH, + } + } else { + Edges { + top: BORDER_WIDTH, + right: px(0.), + bottom: px(0.), + left: px(0.), + } + }, + border_color: state.border, + }); + + cx.paint_quad( + fill(state.thumb_fill_bounds, state.thumb_bg).corner_radii(radius), + ); + }); + + window.on_mouse_event({ + let state = self.state.clone(); + let view_id = self.view_id; + let scroll_handle = self.scroll_handle.clone(); + + move |event: &ScrollWheelEvent, phase, _, cx| { + if phase.bubble() && hitbox_bounds.contains(&event.position) { + if scroll_handle.offset() != state.get().last_scroll_offset { + state.set(state.get().with_last_scroll( + scroll_handle.offset(), + Some(Instant::now()), + )); + cx.notify(view_id); + } + } } + }); + + let safe_range = (-scroll_area_size + container_size)..px(0.); + + if is_hover_to_show || is_visible { + window.on_mouse_event({ + let state = self.state.clone(); + let view_id = self.view_id; + let scroll_handle = self.scroll_handle.clone(); + + move |event: &MouseDownEvent, phase, _, cx| { + if phase.bubble() && bounds.contains(&event.position) { + cx.stop_propagation(); + + if thumb_bounds.contains(&event.position) { + // click on the thumb bar, set the drag position + let pos = event.position - thumb_bounds.origin; + + state.set(state.get().with_drag_pos(axis, pos)); + + cx.notify(view_id); + } else { + // click on the scrollbar, jump to the position + // Set the thumb bar center to the click position + let offset = scroll_handle.offset(); + let percentage = if is_vertical { + (event.position.y - thumb_size / 2. - bounds.origin.y) + / (bounds.size.height - thumb_size) + } else { + (event.position.x - thumb_size / 2. - bounds.origin.x) + / (bounds.size.width - thumb_size) + } + .min(1.); + + if is_vertical { + scroll_handle.set_offset(point( + offset.x, + (-scroll_area_size * percentage) + .clamp(safe_range.start, safe_range.end), + )); + } else { + scroll_handle.set_offset(point( + (-scroll_area_size * percentage) + .clamp(safe_range.start, safe_range.end), + offset.y, + )); + } + } + } + } + }); } - } - }); - let safe_range = (-scroll_area_size + container_size)..px(0.); - - if is_hover_to_show || is_visible { - window.on_mouse_event({ - let state = self.state.clone(); - let view_id = self.view_id; - let scroll_handle = self.scroll_handle.clone(); - - move |event: &MouseDownEvent, phase, _, cx| { - if phase.bubble() && bounds.contains(&event.position) { - cx.stop_propagation(); + window.on_mouse_event({ + let scroll_handle = self.scroll_handle.clone(); + let state = self.state.clone(); + let view_id = self.view_id; + + move |event: &MouseMoveEvent, _, _, cx| { + // Update hovered state for scrollbar + if bounds.contains(&event.position) { + if state.get().hovered_axis != Some(axis) { + state.set(state.get().with_hovered(Some(axis))); + cx.notify(view_id); + } + } else { + if state.get().hovered_axis == Some(axis) { + if state.get().hovered_axis.is_some() { + state.set(state.get().with_hovered(None)); + cx.notify(view_id); + } + } + } + // Update hovered state for scrollbar thumb if thumb_bounds.contains(&event.position) { - // click on the thumb bar, set the drag position - let pos = event.position - thumb_bounds.origin; + if state.get().hovered_on_thumb != Some(axis) { + state.set(state.get().with_hovered_on_thumb(Some(axis))); + cx.notify(view_id); + } + } else { + if state.get().hovered_on_thumb == Some(axis) { + state.set(state.get().with_hovered_on_thumb(None)); + cx.notify(view_id); + } + } - state.set(state.get().with_drag_pos(axis, pos)); + // Move thumb position on dragging + if state.get().dragged_axis == Some(axis) && event.dragging() { + // drag_pos is the position of the mouse down event + // We need to keep the thumb bar still at the origin down position + let drag_pos = state.get().drag_pos; - cx.notify(view_id); - } else { - // click on the scrollbar, jump to the position - // Set the thumb bar center to the click position - let offset = scroll_handle.offset(); - let percentage = if is_vertical { - (event.position.y - thumb_size / 2. - bounds.origin.y) + let percentage = (if is_vertical { + (event.position.y - drag_pos.y - bounds.origin.y) / (bounds.size.height - thumb_size) } else { - (event.position.x - thumb_size / 2. - bounds.origin.x) - / (bounds.size.width - thumb_size) - } - .min(1.); - - if is_vertical { - scroll_handle.set_offset(point( - offset.x, - (-scroll_area_size * percentage) + (event.position.x - drag_pos.x - bounds.origin.x) + / (bounds.size.width - thumb_size - margin_end) + }) + .clamp(0., 1.); + + let offset = if is_vertical { + point( + scroll_handle.offset().x, + (-(scroll_area_size - container_size) * percentage) .clamp(safe_range.start, safe_range.end), - )); + ) } else { - scroll_handle.set_offset(point( - (-scroll_area_size * percentage) + point( + (-(scroll_area_size - container_size) * percentage) .clamp(safe_range.start, safe_range.end), - offset.y, - )); + scroll_handle.offset().y, + ) + }; + + if (scroll_handle.offset().y - offset.y).abs() > px(1.) + || (scroll_handle.offset().x - offset.x).abs() > px(1.) + { + scroll_handle.set_offset(offset); + cx.notify(view_id); } } } - } - }); - } + }); - window.on_mouse_event({ - let scroll_handle = self.scroll_handle.clone(); - let state = self.state.clone(); - let view_id = self.view_id; - - move |event: &MouseMoveEvent, _, _, cx| { - // Update hovered state for scrollbar - if bounds.contains(&event.position) { - if state.get().hovered_axis != Some(axis) { - state.set(state.get().with_hovered(Some(axis))); - cx.notify(view_id); - } - } else { - if state.get().hovered_axis == Some(axis) { - if state.get().hovered_axis.is_some() { - state.set(state.get().with_hovered(None)); + window.on_mouse_event({ + let view_id = self.view_id; + let state = self.state.clone(); + + move |_event: &MouseUpEvent, phase, _, cx| { + if phase.bubble() { + state.set(state.get().with_unset_drag_pos()); cx.notify(view_id); } } - } - - // Update hovered state for scrollbar thumb - if thumb_bounds.contains(&event.position) { - if state.get().hovered_on_thumb != Some(axis) { - state.set(state.get().with_hovered_on_thumb(Some(axis))); - cx.notify(view_id); - } - } else { - if state.get().hovered_on_thumb == Some(axis) { - state.set(state.get().with_hovered_on_thumb(None)); - cx.notify(view_id); - } - } - - // Move thumb position on dragging - if state.get().dragged_axis == Some(axis) && event.dragging() { - // drag_pos is the position of the mouse down event - // We need to keep the thumb bar still at the origin down position - let drag_pos = state.get().drag_pos; - - let percentage = (if is_vertical { - (event.position.y - drag_pos.y - bounds.origin.y) - / (bounds.size.height - thumb_size) - } else { - (event.position.x - drag_pos.x - bounds.origin.x) - / (bounds.size.width - thumb_size - margin_end) - }) - .clamp(0., 1.); - - let offset = if is_vertical { - point( - scroll_handle.offset().x, - (-(scroll_area_size - container_size) * percentage) - .clamp(safe_range.start, safe_range.end), - ) - } else { - point( - (-(scroll_area_size - container_size) * percentage) - .clamp(safe_range.start, safe_range.end), - scroll_handle.offset().y, - ) - }; - - if (scroll_handle.offset().y - offset.y).abs() > px(1.) - || (scroll_handle.offset().x - offset.x).abs() > px(1.) - { - scroll_handle.set_offset(offset); - cx.notify(view_id); - } - } + }); } - }); - - window.on_mouse_event({ - let view_id = self.view_id; - let state = self.state.clone(); - - move |_event: &MouseUpEvent, phase, _, cx| { - if phase.bubble() { - state.set(state.get().with_unset_drag_pos()); - cx.notify(view_id); - } - } - }); - } + }, + ); } }