diff --git a/gpui/src/app.rs b/gpui/src/app.rs index d403af3615d5e9321bdeac6ffa1f12e66d04ec3b..ffe695519409a4c3beff6d10dfb5a20708ad5b8d 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -9,7 +9,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use keymap::MatchResult; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use pathfinder_geometry::{rect::RectF, vector::vec2f}; use platform::Event; use postage::{sink::Sink as _, stream::Stream as _}; @@ -900,7 +900,7 @@ impl MutableAppContext { } } - let mut values = self.ctx.values.lock(); + let mut values = self.ctx.values.write(); for key in dropped_values { values.remove(&key); } @@ -1322,7 +1322,7 @@ impl AsRef for MutableAppContext { pub struct AppContext { models: HashMap>, windows: HashMap, - values: Mutex>>, + values: RwLock>>, background: Arc, ref_counts: Arc>, thread_pool: scoped_pool::Pool, @@ -1376,7 +1376,7 @@ impl AppContext { pub fn value(&self, id: usize) -> ValueHandle { let key = (TypeId::of::(), id); - let mut values = self.values.lock(); + let mut values = self.values.write(); values.entry(key).or_insert_with(|| Box::new(T::default())); ValueHandle::new(TypeId::of::(), id, &self.ref_counts) } @@ -2387,10 +2387,20 @@ impl ValueHandle { } } - pub fn map(&self, ctx: &AppContext, f: impl FnOnce(&mut T) -> R) -> R { + pub fn read(&self, ctx: &AppContext, f: impl FnOnce(&T) -> R) -> R { f(ctx .values - .lock() + .read() + .get(&(self.tag_type_id, self.id)) + .unwrap() + .downcast_ref() + .unwrap()) + } + + pub fn update(&self, ctx: &AppContext, f: impl FnOnce(&mut T) -> R) -> R { + f(ctx + .values + .write() .get_mut(&(self.tag_type_id, self.id)) .unwrap() .downcast_mut() diff --git a/gpui/src/elements/mouse_event_handler.rs b/gpui/src/elements/mouse_event_handler.rs index 6d15a9709ef639ade7f8e5013a8df5f087034cec..89f7ba87e63d722a33afdd827ec52942ebb903e8 100644 --- a/gpui/src/elements/mouse_event_handler.rs +++ b/gpui/src/elements/mouse_event_handler.rs @@ -22,9 +22,13 @@ impl MouseEventHandler { Tag: 'static, F: FnOnce(MouseState) -> ElementBox, { - let state = ctx.value::(id); - let child = state.map(ctx, |state| render_child(*state)); - Self { state, child } + let state_handle = ctx.value::(id); + let state = state_handle.read(ctx, |state| *state); + let child = render_child(state); + Self { + state: state_handle, + child, + } } } @@ -68,7 +72,7 @@ impl Element for MouseEventHandler { ) -> bool { let handled_in_child = self.child.dispatch_event(event, ctx); - self.state.map(ctx.app, |state| match event { + self.state.update(ctx.app, |state| match event { Event::MouseMoved { position } => { let mouse_in = bounds.contains_point(*position); if state.hovered != mouse_in { diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index 52296c69c3adc6703f1606c0e462a287aeb60e59..bbe361a9c96389e4d6c20e1a7bb52cf851ef0557 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -510,7 +510,7 @@ impl Renderer { ); // Snap sprite to pixel grid. - let origin = (icon.bounds.origin() * scene.scale_factor()).floor(); + let origin = (icon.bounds.origin() * scene.scale_factor()); //.floor(); sprites_by_atlas .entry(sprite.atlas_id) .or_insert_with(Vec::new) diff --git a/gpui/src/platform/mac/shaders/shaders.h b/gpui/src/platform/mac/shaders/shaders.h index 2241a25c2a4fd07ec6c19265b29317f60e2b398a..5f49bfca64004ec67abc9c58c641b631cfd58375 100644 --- a/gpui/src/platform/mac/shaders/shaders.h +++ b/gpui/src/platform/mac/shaders/shaders.h @@ -49,7 +49,8 @@ typedef enum { typedef struct { vector_float2 origin; - vector_float2 size; + vector_float2 target_size; + vector_float2 source_size; vector_float2 atlas_origin; vector_uchar4 color; uint8_t compute_winding; diff --git a/gpui/src/platform/mac/shaders/shaders.metal b/gpui/src/platform/mac/shaders/shaders.metal index 8250a8783602644c2bed0bdac6b0de834994d966..91e5ea129577d9443ee0f395c0e01df72f37b702 100644 --- a/gpui/src/platform/mac/shaders/shaders.metal +++ b/gpui/src/platform/mac/shaders/shaders.metal @@ -186,9 +186,9 @@ vertex SpriteFragmentInput sprite_vertex( ) { float2 unit_vertex = unit_vertices[unit_vertex_id]; GPUISprite sprite = sprites[sprite_id]; - float2 position = unit_vertex * sprite.size + sprite.origin; + float2 position = unit_vertex * sprite.target_size + sprite.origin; float4 device_position = to_device_position(position, *viewport_size); - float2 atlas_position = (unit_vertex * sprite.size + sprite.atlas_origin) / *atlas_size; + float2 atlas_position = (unit_vertex * sprite.source_size + sprite.atlas_origin) / *atlas_size; return SpriteFragmentInput { device_position, diff --git a/gpui/src/platform/mac/sprite_cache.rs b/gpui/src/platform/mac/sprite_cache.rs index bfb1c1f6013e2c6f64aca1c253974a7c320a1a07..c78928deca334b966f06118937c4c05bb455fb53 100644 --- a/gpui/src/platform/mac/sprite_cache.rs +++ b/gpui/src/platform/mac/sprite_cache.rs @@ -140,8 +140,23 @@ impl SpriteCache { size: Vector2F, path: Cow<'static, str>, svg: usvg::Tree, + target_position: Vector2F, scale_factor: f32, ) -> IconSprite { + const SUBPIXEL_VARIANTS: u8 = 4; + + let target_position = target_position * scale_factor; + let subpixel_variant = ( + (target_position.x().fract() * SUBPIXEL_VARIANTS as f32).round() as u8 + % SUBPIXEL_VARIANTS, + (target_position.y().fract() * SUBPIXEL_VARIANTS as f32).round() as u8 + % SUBPIXEL_VARIANTS, + ); + let subpixel_shift = vec2f( + subpixel_variant.0 as f32 / SUBPIXEL_VARIANTS as f32, + subpixel_variant.1 as f32 / SUBPIXEL_VARIANTS as f32, + ); + let atlases = &mut self.atlases; let atlas_size = self.atlas_size; let device = &self.device; diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index d0f26aa7373da4b8b46602f594723fdc8873f2f4..a09c3ea556c1b01106eda81240319937959f01ed 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -215,9 +215,11 @@ impl Pane { ) .with_child( Align::new(Self::render_tab_icon( + item.id(), line_height - 2., mouse_state.hovered, item.is_dirty(ctx), + ctx, )) .right() .boxed(), @@ -281,14 +283,33 @@ impl Pane { .named("tabs") } - fn render_tab_icon(close_icon_size: f32, tab_hovered: bool, is_modified: bool) -> ElementBox { + fn render_tab_icon( + item_id: usize, + close_icon_size: f32, + tab_hovered: bool, + is_modified: bool, + ctx: &AppContext, + ) -> ElementBox { + enum TabCloseButton {} + let modified_color = ColorU::from_u32(0x556de8ff); let icon = if tab_hovered { let mut icon = Svg::new("icons/x.svg"); - if is_modified { - icon = icon.with_color(modified_color); - } - icon.named("close-tab-icon") + + MouseEventHandler::new::(item_id, ctx, |mouse_state| { + if mouse_state.hovered { + Container::new(icon.with_color(ColorU::white()).boxed()) + .with_background_color(modified_color) + .with_corner_radius(close_icon_size / 2.) + .boxed() + } else { + if is_modified { + icon = icon.with_color(modified_color); + } + icon.boxed() + } + }) + .named("close-tab-icon") } else { let diameter = 8.; ConstrainedBox::new(