scrollbar: Add max_fps to limit drag scroll update rate for reduce CPU usage. (#604)
And set Table to use 60 FPS.
This commit is contained in:
parent
abb3de013e
commit
2e3f3dd669
2 changed files with 54 additions and 12 deletions
|
|
@ -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 crate::ActiveTheme;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
@ -69,6 +73,8 @@ pub struct ScrollbarState {
|
||||||
drag_pos: Point<Pixels>,
|
drag_pos: Point<Pixels>,
|
||||||
last_scroll_offset: Point<Pixels>,
|
last_scroll_offset: Point<Pixels>,
|
||||||
last_scroll_time: Option<Instant>,
|
last_scroll_time: Option<Instant>,
|
||||||
|
// Last update offset
|
||||||
|
last_update: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScrollbarState {
|
impl Default for ScrollbarState {
|
||||||
|
|
@ -80,6 +86,7 @@ impl Default for ScrollbarState {
|
||||||
drag_pos: point(px(0.), px(0.)),
|
drag_pos: point(px(0.), px(0.)),
|
||||||
last_scroll_offset: point(px(0.), px(0.)),
|
last_scroll_offset: point(px(0.), px(0.)),
|
||||||
last_scroll_time: None,
|
last_scroll_time: None,
|
||||||
|
last_update: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +146,12 @@ impl ScrollbarState {
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_last_update(&self, t: Instant) -> Self {
|
||||||
|
let mut state = *self;
|
||||||
|
state.last_update = t;
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
fn is_scrollbar_visible(&self) -> bool {
|
fn is_scrollbar_visible(&self) -> bool {
|
||||||
if let Some(last_time) = self.last_scroll_time {
|
if let Some(last_time) = self.last_scroll_time {
|
||||||
let elapsed = Instant::now().duration_since(last_time).as_secs_f32();
|
let elapsed = Instant::now().duration_since(last_time).as_secs_f32();
|
||||||
|
|
@ -198,6 +211,11 @@ pub struct Scrollbar {
|
||||||
scroll_handle: Rc<Box<dyn ScrollHandleOffsetable>>,
|
scroll_handle: Rc<Box<dyn ScrollHandleOffsetable>>,
|
||||||
scroll_size: gpui::Size<Pixels>,
|
scroll_size: gpui::Size<Pixels>,
|
||||||
state: Rc<Cell<ScrollbarState>>,
|
state: Rc<Cell<ScrollbarState>>,
|
||||||
|
/// 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 {
|
impl Scrollbar {
|
||||||
|
|
@ -215,6 +233,7 @@ impl Scrollbar {
|
||||||
scroll_size,
|
scroll_size,
|
||||||
width: px(12.),
|
width: px(12.),
|
||||||
scroll_handle: Rc::new(Box::new(scroll_handle)),
|
scroll_handle: Rc::new(Box::new(scroll_handle)),
|
||||||
|
max_fps: 120,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,6 +313,16 @@ impl Scrollbar {
|
||||||
self
|
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) {
|
fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||||
(
|
(
|
||||||
cx.theme().scrollbar_thumb_hover,
|
cx.theme().scrollbar_thumb_hover,
|
||||||
|
|
@ -710,19 +739,21 @@ impl Element for Scrollbar {
|
||||||
let scroll_handle = self.scroll_handle.clone();
|
let scroll_handle = self.scroll_handle.clone();
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let view_id = self.view_id;
|
let view_id = self.view_id;
|
||||||
|
let max_fps_duration = Duration::from_millis((1000 / self.max_fps) as u64);
|
||||||
|
|
||||||
move |event: &MouseMoveEvent, _, _, cx| {
|
move |event: &MouseMoveEvent, _, _, cx| {
|
||||||
|
let mut notify = false;
|
||||||
// Update hovered state for scrollbar
|
// Update hovered state for scrollbar
|
||||||
if bounds.contains(&event.position) {
|
if bounds.contains(&event.position) {
|
||||||
if state.get().hovered_axis != Some(axis) {
|
if state.get().hovered_axis != Some(axis) {
|
||||||
state.set(state.get().with_hovered(Some(axis)));
|
state.set(state.get().with_hovered(Some(axis)));
|
||||||
cx.notify(view_id);
|
notify = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if state.get().hovered_axis == Some(axis) {
|
if state.get().hovered_axis == Some(axis) {
|
||||||
if state.get().hovered_axis.is_some() {
|
if state.get().hovered_axis.is_some() {
|
||||||
state.set(state.get().with_hovered(None));
|
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 thumb_bounds.contains(&event.position) {
|
||||||
if state.get().hovered_on_thumb != Some(axis) {
|
if state.get().hovered_on_thumb != Some(axis) {
|
||||||
state.set(state.get().with_hovered_on_thumb(Some(axis)));
|
state.set(state.get().with_hovered_on_thumb(Some(axis)));
|
||||||
cx.notify(view_id);
|
notify = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if state.get().hovered_on_thumb == Some(axis) {
|
if state.get().hovered_on_thumb == Some(axis) {
|
||||||
state.set(state.get().with_hovered_on_thumb(None));
|
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.)
|
if (scroll_handle.offset().y - offset.y).abs() > px(1.)
|
||||||
|| (scroll_handle.offset().x - offset.x).abs() > px(1.)
|
|| (scroll_handle.offset().x - offset.x).abs() > px(1.)
|
||||||
{
|
{
|
||||||
scroll_handle.set_offset(offset);
|
// Limit update rate
|
||||||
cx.notify(view_id);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -866,11 +866,14 @@ where
|
||||||
.left_0()
|
.left_0()
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(Scrollbar::uniform_scroll(
|
.child(
|
||||||
cx.entity().entity_id(),
|
Scrollbar::uniform_scroll(
|
||||||
state,
|
cx.entity().entity_id(),
|
||||||
self.vertical_scroll_handle.clone(),
|
state,
|
||||||
)),
|
self.vertical_scroll_handle.clone(),
|
||||||
|
)
|
||||||
|
.max_fps(60),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue