From 2e3f3dd669ed6a80e9c235ad607fad02c0105474 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 7 Feb 2025 10:07:47 +0800 Subject: [PATCH] scrollbar: Add max_fps to limit drag scroll update rate for reduce CPU usage. (#604) And set Table to use 60 FPS. --- crates/ui/src/scroll/scrollbar.rs | 53 +++++++++++++++++++++++++++---- crates/ui/src/table.rs | 13 +++++--- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/crates/ui/src/scroll/scrollbar.rs b/crates/ui/src/scroll/scrollbar.rs index 875c5283..b2e16081 100644 --- a/crates/ui/src/scroll/scrollbar.rs +++ b/crates/ui/src/scroll/scrollbar.rs @@ -1,4 +1,8 @@ -use std::{cell::Cell, rc::Rc, time::Instant}; +use std::{ + cell::Cell, + rc::Rc, + time::{Duration, Instant}, +}; use crate::ActiveTheme; use gpui::{ @@ -69,6 +73,8 @@ pub struct ScrollbarState { drag_pos: Point, last_scroll_offset: Point, last_scroll_time: Option, + // Last update offset + last_update: Instant, } impl Default for ScrollbarState { @@ -80,6 +86,7 @@ impl Default for ScrollbarState { drag_pos: point(px(0.), px(0.)), last_scroll_offset: point(px(0.), px(0.)), last_scroll_time: None, + last_update: Instant::now(), } } } @@ -139,6 +146,12 @@ impl ScrollbarState { state } + fn with_last_update(&self, t: Instant) -> Self { + let mut state = *self; + state.last_update = t; + state + } + fn is_scrollbar_visible(&self) -> bool { if let Some(last_time) = self.last_scroll_time { let elapsed = Instant::now().duration_since(last_time).as_secs_f32(); @@ -198,6 +211,11 @@ pub struct Scrollbar { scroll_handle: Rc>, scroll_size: gpui::Size, state: Rc>, + /// Maximum frames per second for scrolling by drag. Default is 120 FPS. + /// + /// This is used to limit the update rate of the scrollbar when it is + /// being dragged for some complex interactions for reducing CPU usage. + max_fps: usize, } impl Scrollbar { @@ -215,6 +233,7 @@ impl Scrollbar { scroll_size, width: px(12.), scroll_handle: Rc::new(Box::new(scroll_handle)), + max_fps: 120, } } @@ -294,6 +313,16 @@ impl Scrollbar { self } + /// Set maximum frames per second for scrolling by drag. Default is 120 FPS. + /// + /// If you have very high CPU usage, consider reducing this value to improve performance. + /// + /// Available values: 30..120 + pub fn max_fps(mut self, max_fps: usize) -> Self { + self.max_fps = max_fps.clamp(30, 120); + self + } + fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) { ( cx.theme().scrollbar_thumb_hover, @@ -710,19 +739,21 @@ impl Element for Scrollbar { let scroll_handle = self.scroll_handle.clone(); let state = self.state.clone(); let view_id = self.view_id; + let max_fps_duration = Duration::from_millis((1000 / self.max_fps) as u64); move |event: &MouseMoveEvent, _, _, cx| { + let mut notify = false; // 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); + notify = true; } } 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); + notify = true; } } } @@ -731,12 +762,12 @@ impl Element for Scrollbar { 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); + notify = true; } } else { if state.get().hovered_on_thumb == Some(axis) { state.set(state.get().with_hovered_on_thumb(None)); - cx.notify(view_id); + notify = true; } } @@ -772,10 +803,18 @@ impl Element for Scrollbar { 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); + // Limit update rate + if state.get().last_update.elapsed() > max_fps_duration { + scroll_handle.set_offset(offset); + state.set(state.get().with_last_update(Instant::now())); + notify = true; + } } } + + if notify { + cx.notify(view_id); + } } }); diff --git a/crates/ui/src/table.rs b/crates/ui/src/table.rs index a75ab4da..62f3e607 100644 --- a/crates/ui/src/table.rs +++ b/crates/ui/src/table.rs @@ -866,11 +866,14 @@ where .left_0() .right_0() .bottom_0() - .child(Scrollbar::uniform_scroll( - cx.entity().entity_id(), - state, - self.vertical_scroll_handle.clone(), - )), + .child( + Scrollbar::uniform_scroll( + cx.entity().entity_id(), + state, + self.vertical_scroll_handle.clone(), + ) + .max_fps(60), + ), ) }