diff --git a/Cargo.lock b/Cargo.lock index 8268aee..11701f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1093,7 +1093,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1375,18 +1375,18 @@ dependencies = [ [[package]] name = "ctor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035a5c3c87da8be7a0e5ebd6fbabcccd8651a59c27197096d1dfd74f312aaee5" +checksum = "cdd538fd2dbf2e5932fad5a4f983ff0458891f5ea40973fa2b7d3460ca378914" dependencies = [ "ctor-proc-macro", ] [[package]] name = "ctor-proc-macro" -version = "0.0.2" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107adb396b2d8e7766e79151083ce1cc624b51dfd1c23e0429c50226bba693eb" +checksum = "c426d2ba3e525b39c1f0a9ba41b9fe61878dee11fa4e4a76b6ab440f46c5db5d" [[package]] name = "data-encoding" @@ -1416,7 +1416,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "proc-macro2", "quote", @@ -2135,7 +2135,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2222,7 +2222,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "proc-macro2", "quote", @@ -2237,9 +2237,9 @@ checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -2445,7 +2445,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "anyhow", "bytes", @@ -3132,7 +3132,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "anyhow", "bindgen 0.70.1", @@ -3311,7 +3311,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.39.0" -source = "git+https://github.com/rust-nostr/nostr#a269f7c937cb6c220023795b07686a5d7e337fb0" +source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3" dependencies = [ "aes", "base64", @@ -3335,7 +3335,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.39.0" -source = "git+https://github.com/rust-nostr/nostr#a269f7c937cb6c220023795b07686a5d7e337fb0" +source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3" dependencies = [ "async-utility", "nostr", @@ -3347,7 +3347,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.39.0" -source = "git+https://github.com/rust-nostr/nostr#a269f7c937cb6c220023795b07686a5d7e337fb0" +source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3" dependencies = [ "flatbuffers", "lru", @@ -3358,7 +3358,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.39.0" -source = "git+https://github.com/rust-nostr/nostr#a269f7c937cb6c220023795b07686a5d7e337fb0" +source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3" dependencies = [ "async-utility", "heed", @@ -3371,7 +3371,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.39.0" -source = "git+https://github.com/rust-nostr/nostr#a269f7c937cb6c220023795b07686a5d7e337fb0" +source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3" dependencies = [ "async-utility", "async-wsocket", @@ -3388,7 +3388,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.39.0" -source = "git+https://github.com/rust-nostr/nostr#a269f7c937cb6c220023795b07686a5d7e337fb0" +source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3" dependencies = [ "async-utility", "nostr", @@ -4452,7 +4452,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "derive_refineable", ] @@ -4581,7 +4581,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "anyhow", "bytes", @@ -4961,7 +4961,7 @@ checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "anyhow", "serde", @@ -5285,7 +5285,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "arrayvec", "log", @@ -5733,9 +5733,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4bf6fecd69fcdede0ec680aaf474cdab988f9de6bc73d3758f0160e3b7025a" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", @@ -5892,17 +5892,16 @@ checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" [[package]] name = "tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ - "byteorder", "bytes", "data-encoding", "http", "httparse", "log", - "rand 0.8.5", + "rand 0.9.0", "rustls", "rustls-pki-types", "sha1", @@ -5918,9 +5917,9 @@ checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "uds_windows" @@ -6111,7 +6110,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b4fc127e492924427b6f0afcad03b144b632152b" +source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df" dependencies = [ "anyhow", "async-fs", diff --git a/crates/app/src/views/chat.rs b/crates/app/src/views/chat.rs index 5988f27..c1c912c 100644 --- a/crates/app/src/views/chat.rs +++ b/crates/app/src/views/chat.rs @@ -49,22 +49,39 @@ pub fn init( } #[derive(PartialEq, Eq)] -struct ChatItem { - profile: NostrProfile, +struct ParsedMessage { + avatar: SharedString, + display_name: SharedString, + created_at: SharedString, content: SharedString, - ago: SharedString, +} + +impl ParsedMessage { + pub fn new(profile: &NostrProfile, content: &str, created_at: Timestamp) -> Self { + let avatar = profile.avatar().into(); + let display_name = profile.name().into(); + let content = SharedString::new(content); + let created_at = LastSeen(created_at).human_readable(); + + Self { + avatar, + display_name, + created_at, + content, + } + } } #[derive(PartialEq, Eq)] enum Message { - Item(Box), + User(Box), System(SharedString), Placeholder, } impl Message { - pub fn new(chat_message: ChatItem) -> Self { - Self::Item(Box::new(chat_message)) + pub fn new(message: ParsedMessage) -> Self { + Self::User(Box::new(message)) } pub fn system(content: SharedString) -> Self { @@ -286,12 +303,7 @@ impl Chat { let old_len = self.messages.read(cx).len(); let room = model.read(cx); - let ago = LastSeen(Timestamp::now()).human_readable(); - let message = Message::new(ChatItem { - profile: room.owner.clone(), - content: content.into(), - ago, - }); + let message = Message::new(ParsedMessage::new(&room.owner, &content, Timestamp::now())); // Update message list cx.update_entity(&self.messages, |this, cx| { @@ -336,11 +348,10 @@ impl Chat { room.owner.to_owned() }; - Some(Message::new(ChatItem { - profile: member, - content: ev.content.into(), - ago: LastSeen(ev.created_at).human_readable(), - })) + let message = + Message::new(ParsedMessage::new(&member, &ev.content, ev.created_at)); + + Some(message) } else { None } @@ -378,11 +389,11 @@ impl Chat { .iter() .filter_map(|event| { if let Some(profile) = room.member(&event.pubkey) { - let message = Message::new(ChatItem { - profile, - content: event.content.clone().into(), - ago: LastSeen(event.created_at).human_readable(), - }); + let message = Message::new(ParsedMessage::new( + &profile, + &event.content, + event.created_at, + )); if !old_messages.iter().any(|old| old == &message) { Some(message) @@ -598,7 +609,7 @@ impl Chat { .w_full() .p_2() .map(|this| match message { - Message::Item(item) => this + Message::User(item) => this .hover(|this| this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE))) .child( div() @@ -613,7 +624,7 @@ impl Chat { }), ) .child( - img(item.profile.avatar()) + img(item.avatar.clone()) .size_8() .rounded_full() .flex_shrink_0(), @@ -630,8 +641,10 @@ impl Chat { .items_baseline() .gap_2() .text_xs() - .child(div().font_semibold().child(item.profile.name())) - .child(div().child(item.ago.clone()).text_color( + .child( + div().font_semibold().child(item.display_name.clone()), + ) + .child(div().child(item.created_at.clone()).text_color( cx.theme().base.step(cx, ColorScaleStep::ELEVEN), )), ) @@ -668,7 +681,7 @@ impl Chat { .text_center() .text_xs() .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) - .line_height(relative(1.)) + .line_height(relative(1.2)) .child( svg() .path("brand/coop.svg") diff --git a/crates/ui/src/dock_area/dock.rs b/crates/ui/src/dock_area/dock.rs index f711900..43e7aac 100644 --- a/crates/ui/src/dock_area/dock.rs +++ b/crates/ui/src/dock_area/dock.rs @@ -1,5 +1,5 @@ use gpui::{ - div, prelude::FluentBuilder as _, px, AnyView, App, AppContext, Axis, Context, Element, Entity, + div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Entity, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render, StatefulInteractiveElement, Style, Styled as _, WeakEntity, Window, }; @@ -374,9 +374,7 @@ impl Render for Dock { }) .map(|this| match &self.panel { DockItem::Split { view, .. } => this.child(view.clone()), - DockItem::Tabs { view, .. } => { - this.child(AnyView::from(view.clone()).cached(cache_style)) - } + DockItem::Tabs { view, .. } => this.child(view.clone()), DockItem::Panel { view, .. } => this.child(view.clone().view().cached(cache_style)), }) .child(self.render_resize_handle(window, cx)) @@ -432,14 +430,20 @@ impl Element for DockElement { _: &mut Self::RequestLayoutState, _: &mut Self::PrepaintState, window: &mut gpui::Window, - _: &mut App, + cx: &mut App, ) { window.on_mouse_event({ let view = self.view.clone(); + let is_resizing = view.read(cx).is_resizing; move |e: &MouseMoveEvent, phase, window, cx| { - if phase.bubble() { - view.update(cx, |view, cx| view.resize(e.position, window, cx)) + if !is_resizing { + return; } + if !phase.bubble() { + return; + } + + view.update(cx, |view, cx| view.resize(e.position, window, cx)) } }); diff --git a/crates/ui/src/modal.rs b/crates/ui/src/modal.rs index f84cb43..b13c3c6 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/modal.rs @@ -244,7 +244,7 @@ impl RenderOnce for Modal { .with_easing(cubic_bezier(0.32, 0.72, 0., 1.)), move |this, delta| { let y_offset = px(0.) + delta * px(30.); - this.top(y + y_offset).opacity(delta) + this.top(y + y_offset) }, ), ), diff --git a/crates/ui/src/popup_menu.rs b/crates/ui/src/popup_menu.rs index 048ea0b..79906c1 100644 --- a/crates/ui/src/popup_menu.rs +++ b/crates/ui/src/popup_menu.rs @@ -133,7 +133,6 @@ impl PopupMenu { this.dismiss(&Dismiss, window, cx) }), ]; - let menu = Self { focus_handle, action_focus_handle: None, @@ -150,7 +149,7 @@ impl PopupMenu { scroll_state: Rc::new(Cell::new(ScrollbarState::default())), subscriptions, }; - window.refresh(); + f(menu, window, cx) }) } diff --git a/crates/ui/src/resizable/panel.rs b/crates/ui/src/resizable/panel.rs index 115c0ce..a2af924 100644 --- a/crates/ui/src/resizable/panel.rs +++ b/crates/ui/src/resizable/panel.rs @@ -510,41 +510,38 @@ impl Element for ResizePanelGroupElement { let axis = self.axis; let current_ix = view.read(cx).resizing_panel_ix; move |e: &MouseMoveEvent, phase, window, cx| { - if phase.bubble() { - if let Some(ix) = current_ix { - view.update(cx, |view, cx| { - let panel = view - .panels - .get(ix) - .expect("BUG: invalid panel index") - .read(cx); - - match axis { - Axis::Horizontal => view.resize_panels( - ix, - e.position.x - panel.bounds.left(), - window, - cx, - ), - Axis::Vertical => { - view.resize_panels( - ix, - e.position.y - panel.bounds.top(), - window, - cx, - ); - } - } - }) - } + if !phase.bubble() { + return; } + let Some(ix) = current_ix else { return }; + + view.update(cx, |view, cx| { + let panel = view + .panels + .get(ix) + .expect("BUG: invalid panel index") + .read(cx); + + match axis { + Axis::Horizontal => { + view.resize_panels(ix, e.position.x - panel.bounds.left(), window, cx) + } + Axis::Vertical => { + view.resize_panels(ix, e.position.y - panel.bounds.top(), window, cx); + } + } + }) } }); // When any mouse up, stop dragging window.on_mouse_event({ let view = self.view.clone(); + let current_ix = view.read(cx).resizing_panel_ix; move |_: &MouseUpEvent, phase, window, cx| { + if current_ix.is_none() { + return; + } if phase.bubble() { view.update(cx, |view, cx| view.done_resizing(window, cx)); } diff --git a/crates/ui/src/scroll/scrollbar.rs b/crates/ui/src/scroll/scrollbar.rs index 3e17ecb..6a4c7fb 100644 --- a/crates/ui/src/scroll/scrollbar.rs +++ b/crates/ui/src/scroll/scrollbar.rs @@ -1,6 +1,14 @@ -use gpui::*; +use gpui::{ + fill, point, px, relative, App, Bounds, ContentMask, CursorStyle, Edges, Element, EntityId, + Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, + Point, Position, ScrollHandle, ScrollWheelEvent, UniformListScrollHandle, Window, +}; use serde::{Deserialize, Serialize}; -use std::{cell::Cell, rc::Rc, time::Instant}; +use std::{ + cell::Cell, + rc::Rc, + time::{Duration, Instant}, +}; use crate::theme::{scale::ColorScaleStep, ActiveTheme}; @@ -10,18 +18,24 @@ pub enum ScrollbarShow { #[default] Scrolling, Hover, + Always, } impl ScrollbarShow { fn is_hover(&self) -> bool { matches!(self, Self::Hover) } + + fn is_always(&self) -> bool { + matches!(self, Self::Always) + } } const BORDER_WIDTH: Pixels = px(0.); +pub(crate) const WIDTH: Pixels = px(12.); const MIN_THUMB_SIZE: f32 = 80.; -const THUMB_RADIUS: Pixels = Pixels(3.0); -const THUMB_INSET: Pixels = Pixels(4.); +const THUMB_RADIUS: Pixels = Pixels(4.0); +const THUMB_INSET: Pixels = Pixels(3.); const FADE_OUT_DURATION: f32 = 3.0; const FADE_OUT_DELAY: f32 = 2.0; @@ -65,6 +79,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 { @@ -76,6 +92,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(), } } } @@ -106,8 +123,8 @@ impl ScrollbarState { fn with_hovered(&self, axis: Option) -> Self { let mut state = *self; state.hovered_axis = axis; - if self.is_scrollbar_visible() { - state.last_scroll_time = Some(Instant::now()); + if axis.is_some() { + state.last_scroll_time = Some(std::time::Instant::now()); } state } @@ -115,6 +132,9 @@ impl ScrollbarState { fn with_hovered_on_thumb(&self, axis: Option) -> Self { let mut state = *self; state.hovered_on_thumb = axis; + if axis.is_some() { + state.last_scroll_time = Some(std::time::Instant::now()); + } state } @@ -135,7 +155,18 @@ 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 { + // On drag + if self.dragged_axis.is_some() { + return true; + } + if let Some(last_time) = self.last_scroll_time { let elapsed = Instant::now().duration_since(last_time).as_secs_f32(); elapsed < FADE_OUT_DURATION @@ -178,9 +209,9 @@ impl ScrollbarAxis { match self { Self::Vertical => vec![Self::Vertical], Self::Horizontal => vec![Self::Horizontal], - // This should keep vertical first, vertical is the primary axis - // if vertical not need display, then horizontal will not keep right margin. - Self::Both => vec![Self::Vertical, Self::Horizontal], + // This should keep Horizontal first, Vertical is the primary axis + // if Vertical not need display, then Horizontal will not keep right margin. + Self::Both => vec![Self::Horizontal, Self::Vertical], } } } @@ -189,11 +220,14 @@ impl ScrollbarAxis { pub struct Scrollbar { view_id: EntityId, axis: ScrollbarAxis, - /// When is vertical, this is the height of the scrollbar. - width: Pixels, 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 { @@ -209,8 +243,8 @@ impl Scrollbar { state, axis, scroll_size, - width: px(12.), scroll_handle: Rc::new(Box::new(scroll_handle)), + max_fps: 120, } } @@ -290,11 +324,21 @@ 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, cx.theme().scrollbar, - cx.theme().base.step(cx, ColorScaleStep::THREE), + cx.theme().base.step(cx, ColorScaleStep::SEVEN), THUMB_INSET - px(1.), THUMB_RADIUS, ) @@ -304,7 +348,7 @@ impl Scrollbar { ( cx.theme().scrollbar_thumb_hover, cx.theme().scrollbar, - cx.theme().base.step(cx, ColorScaleStep::THREE), + cx.theme().base.step(cx, ColorScaleStep::SIX), THUMB_INSET - px(1.), THUMB_RADIUS, ) @@ -382,11 +426,11 @@ impl Element for Scrollbar { window: &mut Window, cx: &mut App, ) -> (gpui::LayoutId, Self::RequestLayoutState) { - let style = Style { + let style = gpui::Style { position: Position::Absolute, flex_grow: 1.0, flex_shrink: 1.0, - size: Size { + size: gpui::Size { width: relative(1.).into(), height: relative(1.).into(), }, @@ -409,7 +453,6 @@ impl Element for Scrollbar { }); let mut states = vec![]; - let mut has_both = self.axis.is_both(); for axis in self.axis.all().into_iter() { @@ -430,7 +473,7 @@ impl Element for Scrollbar { // The horizontal scrollbar is set avoid overlapping with the vertical scrollbar, if the vertical scrollbar is visible. let margin_end = if has_both && !is_vertical { - self.width + WIDTH } else { px(0.) }; @@ -449,31 +492,29 @@ impl Element for Scrollbar { let bounds = Bounds { origin: if is_vertical { - point( - hitbox.origin.x + hitbox.size.width - self.width, - hitbox.origin.y, - ) + point(hitbox.origin.x + hitbox.size.width - WIDTH, hitbox.origin.y) } else { point( hitbox.origin.x, - hitbox.origin.y + hitbox.size.height - self.width, + hitbox.origin.y + hitbox.size.height - WIDTH, ) }, size: gpui::Size { width: if is_vertical { - self.width + WIDTH } else { hitbox.size.width }, height: if is_vertical { hitbox.size.height } else { - self.width + WIDTH }, }, }; let state = self.state.clone(); + let is_always_to_show = cx.theme().scrollbar_show.is_always(); let is_hover_to_show = cx.theme().scrollbar_show.is_hover(); let is_hovered_on_bar = state.get().hovered_axis == Some(axis); let is_hovered_on_thumb = state.get().hovered_on_thumb == Some(axis); @@ -481,7 +522,9 @@ impl Element for Scrollbar { let (thumb_bg, bar_bg, bar_border, inset, radius) = if state.get().dragged_axis == Some(axis) { Self::style_for_active(cx) - } else if is_hover_to_show && is_hovered_on_bar { + } else if (is_hover_to_show || is_always_to_show) + && (is_hovered_on_bar || is_hovered_on_thumb) + { if is_hovered_on_thumb { Self::style_for_hovered_thumb(cx) } else { @@ -520,12 +563,12 @@ impl Element for Scrollbar { let thumb_bounds = if is_vertical { Bounds::from_corners( point(bounds.origin.x, bounds.origin.y + thumb_start), - point(bounds.origin.x + self.width, bounds.origin.y + thumb_end), + point(bounds.origin.x + WIDTH, bounds.origin.y + thumb_end), ) } else { Bounds::from_corners( point(bounds.origin.x + thumb_start, bounds.origin.y), - point(bounds.origin.x + thumb_end, bounds.origin.y + self.width), + point(bounds.origin.x + thumb_end, bounds.origin.y + WIDTH), ) }; let thumb_fill_bounds = if is_vertical { @@ -535,7 +578,7 @@ impl Element for Scrollbar { bounds.origin.y + thumb_start + inset, ), point( - bounds.origin.x + self.width - inset, + bounds.origin.x + WIDTH - inset, bounds.origin.y + thumb_end - inset, ), ) @@ -547,7 +590,7 @@ impl Element for Scrollbar { ), point( bounds.origin.x + thumb_end - inset, - bounds.origin.y + self.width - inset, + bounds.origin.y + WIDTH - inset, ), ) }; @@ -589,6 +632,15 @@ impl Element for Scrollbar { let is_visible = self.state.get().is_scrollbar_visible(); let is_hover_to_show = cx.theme().scrollbar_show.is_hover(); + // Update last_scroll_time when offset is changed. + if self.scroll_handle.offset() != self.state.get().last_scroll_offset { + self.state.set( + self.state + .get() + .with_last_scroll(self.scroll_handle.offset(), Some(Instant::now())), + ); + } + window.with_content_mask( Some(ContentMask { bounds: hitbox_bounds, @@ -711,30 +763,36 @@ 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; + // When is hover to show mode or it was visible, + // we need to update the hovered state and increase the last_scroll_time. + let need_hover_to_update = is_hover_to_show || is_visible; // Update hovered state for scrollbar - if bounds.contains(&event.position) { + if bounds.contains(&event.position) && need_hover_to_update { + state.set(state.get().with_hovered(Some(axis))); + 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) && state.get().hovered_axis.is_some() { state.set(state.get().with_hovered(None)); - cx.notify(view_id); + notify = true; } // 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); + 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; } // Move thumb position on dragging @@ -769,10 +827,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/window_border.rs b/crates/ui/src/window_border.rs index 3475d25..5b734d1 100644 --- a/crates/ui/src/window_border.rs +++ b/crates/ui/src/window_border.rs @@ -121,7 +121,6 @@ impl RenderOnce for WindowBorder { .when(!tiling.bottom, |div| div.pb(SHADOW_SIZE)) .when(!tiling.left, |div| div.pl(SHADOW_SIZE)) .when(!tiling.right, |div| div.pr(SHADOW_SIZE)) - .on_mouse_move(|_e, window, _cx| window.refresh()) .on_mouse_down(MouseButton::Left, move |_, window, _cx| { let size = window.window_bounds().get_bounds().size; let pos = window.mouse_position();