dock: Add cache to panel view. (#600)

This commit is contained in:
Jason Lee 2025-02-05 17:23:40 +08:00 committed by GitHub
parent 3e16c0d220
commit 9675f1e02b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 206 additions and 189 deletions

View file

@ -33,8 +33,8 @@ impl_internal_actions!(table_story, [ChangeSize, OpenDetail]);
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct Stock { struct Stock {
id: usize, id: usize,
symbol: String, symbol: SharedString,
name: String, name: SharedString,
price: f64, price: f64,
change: f64, change: f64,
change_percent: f64, change_percent: f64,
@ -103,8 +103,8 @@ fn random_stocks(size: usize) -> Vec<Stock> {
(0..size) (0..size)
.map(|id| Stock { .map(|id| Stock {
id, id,
symbol: Faker.fake::<String>(), symbol: Faker.fake::<String>().into(),
name: Faker.fake::<String>(), name: Faker.fake::<String>().into(),
change: (-100.0..100.0).fake(), change: (-100.0..100.0).fake(),
change_percent: (-1.0..1.0).fake(), change_percent: (-1.0..1.0).fake(),
volume: (0.0..1000.0).fake(), volume: (0.0..1000.0).fake(),

View file

@ -3,9 +3,10 @@
use std::sync::Arc; use std::sync::Arc;
use gpui::{ use gpui::{
div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Empty, Entity, div, prelude::FluentBuilder as _, px, AnyView, App, AppContext, Axis, Context, Element, Empty,
InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Entity, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _,
Point, Render, StatefulInteractiveElement, Style, Styled as _, WeakEntity, Window, Pixels, Point, Render, StatefulInteractiveElement, Style, StyleRefinement, Styled as _,
WeakEntity, Window,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -394,6 +395,8 @@ impl Render for Dock {
return div(); return div();
} }
let cache_style = StyleRefinement::default().v_flex().size_full();
div() div()
.relative() .relative()
.overflow_hidden() .overflow_hidden()
@ -408,8 +411,10 @@ impl Render for Dock {
}) })
.map(|this| match &self.panel { .map(|this| match &self.panel {
DockItem::Split { view, .. } => this.child(view.clone()), DockItem::Split { view, .. } => this.child(view.clone()),
DockItem::Tabs { view, .. } => this.child(view.clone()), DockItem::Tabs { view, .. } => {
DockItem::Panel { view, .. } => this.child(view.clone().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 // Not support to render Tiles and Tile into Dock
DockItem::Tiles { .. } => this, DockItem::Tiles { .. } => this,
}) })

View file

@ -4,7 +4,7 @@ use gpui::{
div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength, div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength,
DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable, DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable,
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle, InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle,
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, WeakEntity, Window,
}; };
use rust_i18n::t; use rust_i18n::t;
@ -14,7 +14,7 @@ use crate::{
h_flex, h_flex,
popup_menu::{PopupMenu, PopupMenuExt}, popup_menu::{PopupMenu, PopupMenuExt},
tab::{Tab, TabBar}, tab::{Tab, TabBar},
v_flex, ActiveTheme, AxisExt, IconName, Placement, Selectable, Sizable, v_flex, ActiveTheme, AxisExt, IconName, Placement, Selectable, Sizable, StyledExt as _,
}; };
use super::{ use super::{
@ -767,7 +767,11 @@ impl TabPanel {
.overflow_y_scroll() .overflow_y_scroll()
.overflow_x_hidden() .overflow_x_hidden()
.flex_1() .flex_1()
.child(active_panel.view()) .child(
active_panel
.view()
.cached(StyleRefinement::default().v_flex().size_full()),
)
.when(state.droppable, |this| { .when(state.droppable, |this| {
this.on_drag_move(cx.listener(Self::on_panel_drag_move)) this.on_drag_move(cx.listener(Self::on_panel_drag_move))
.child( .child(

View file

@ -589,201 +589,209 @@ impl Element for Scrollbar {
let is_visible = self.state.get().is_scrollbar_visible(); let is_visible = self.state.get().is_scrollbar_visible();
let is_hover_to_show = cx.theme().scrollbar_show.is_hover(); let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
for state in prepaint.states.iter() { window.with_content_mask(
let axis = state.axis; Some(ContentMask {
let radius = state.radius; bounds: hitbox_bounds,
let bounds = state.bounds; }),
let thumb_bounds = state.thumb_bounds; |window| {
let scroll_area_size = state.scroll_size; for state in prepaint.states.iter() {
let container_size = state.container_size; let axis = state.axis;
let thumb_size = state.thumb_size; let radius = state.radius;
let margin_end = state.margin_end; let bounds = state.bounds;
let is_vertical = axis.is_vertical(); 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.set_cursor_style(CursorStyle::default(), &state.bar_hitbox);
window.paint_layer(hitbox_bounds, |cx| { window.paint_layer(hitbox_bounds, |cx| {
cx.paint_quad(fill(state.bounds, state.bg)); cx.paint_quad(fill(state.bounds, state.bg));
cx.paint_quad(PaintQuad { cx.paint_quad(PaintQuad {
bounds, bounds,
corner_radii: (0.).into(), corner_radii: (0.).into(),
background: gpui::transparent_black().into(), background: gpui::transparent_black().into(),
border_widths: if is_vertical { border_widths: if is_vertical {
Edges { Edges {
top: px(0.), top: px(0.),
right: px(0.), right: px(0.),
bottom: px(0.), bottom: px(0.),
left: BORDER_WIDTH, 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 { } else {
// click on the scrollbar, jump to the position Edges {
// Set the thumb bar center to the click position top: BORDER_WIDTH,
let offset = scroll_handle.offset(); right: px(0.),
let percentage = if is_vertical { bottom: px(0.),
(event.position.y - thumb_size / 2. - bounds.origin.y) 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,
));
}
}
}
}
});
}
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) {
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) / (bounds.size.height - thumb_size)
} else { } else {
(event.position.x - thumb_size / 2. - bounds.origin.x) (event.position.x - drag_pos.x - bounds.origin.x)
/ (bounds.size.width - thumb_size) / (bounds.size.width - thumb_size - margin_end)
} })
.min(1.); .clamp(0., 1.);
if is_vertical { let offset = if is_vertical {
scroll_handle.set_offset(point( point(
offset.x, scroll_handle.offset().x,
(-scroll_area_size * percentage) (-(scroll_area_size - container_size) * percentage)
.clamp(safe_range.start, safe_range.end), .clamp(safe_range.start, safe_range.end),
)); )
} else { } else {
scroll_handle.set_offset(point( point(
(-scroll_area_size * percentage) (-(scroll_area_size - container_size) * percentage)
.clamp(safe_range.start, safe_range.end), .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({ window.on_mouse_event({
let scroll_handle = self.scroll_handle.clone(); let view_id = self.view_id;
let state = self.state.clone(); let state = self.state.clone();
let view_id = self.view_id;
move |event: &MouseMoveEvent, _, _, cx| { move |_event: &MouseUpEvent, phase, _, cx| {
// Update hovered state for scrollbar if phase.bubble() {
if bounds.contains(&event.position) { state.set(state.get().with_unset_drag_pos());
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); 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);
}
}
});
}
} }
} }