From 600b564bbfb31d12240921dcab62fd7be16212ad Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 28 Nov 2023 16:40:43 +0100 Subject: [PATCH 1/6] Introduce surface rendering Co-Authored-By: Julia --- crates/call2/src/shared_screen.rs | 18 +-- crates/gpui2/build.rs | 2 + crates/gpui2/src/elements/img.rs | 77 ++++++---- .../gpui2/src/platform/mac/metal_renderer.rs | 138 +++++++++++++++++- crates/gpui2/src/platform/mac/shaders.metal | 52 +++++++ crates/gpui2/src/scene.rs | 66 +++++++++ crates/gpui2/src/window.rs | 22 ++- 7 files changed, 332 insertions(+), 43 deletions(-) diff --git a/crates/call2/src/shared_screen.rs b/crates/call2/src/shared_screen.rs index 7b7cd7d9df33045759ae9bda2f16e016f959c9d6..14b997af3b4f7221ff6a2acded9af516d7773310 100644 --- a/crates/call2/src/shared_screen.rs +++ b/crates/call2/src/shared_screen.rs @@ -3,8 +3,8 @@ use anyhow::Result; use client::{proto::PeerId, User}; use futures::StreamExt; use gpui::{ - div, AppContext, Div, Element, EventEmitter, FocusHandle, FocusableView, ParentElement, Render, - SharedString, Task, View, ViewContext, VisualContext, WindowContext, + div, img, AppContext, Div, Element, EventEmitter, FocusHandle, FocusableView, ParentElement, + Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WindowContext, }; use std::sync::{Arc, Weak}; use workspace::{item::Item, ItemNavHistory, WorkspaceId}; @@ -68,15 +68,11 @@ impl Render for SharedScreen { type Element = Div; fn render(&mut self, _: &mut ViewContext) -> Self::Element { let frame = self.frame.clone(); - let frame_id = self.current_frame_id; - self.current_frame_id = self.current_frame_id.wrapping_add(1); - div().children(frame.map(|_| { - ui::Label::new(frame_id.to_string()).color(ui::Color::Error) - // img().data(Arc::new(ImageData::new(image::ImageBuffer::new( - // frame.width() as u32, - // frame.height() as u32, - // )))) - })) + // let frame_id = self.current_frame_id; + // self.current_frame_id = self.current_frame_id.wrapping_add(1); + div() + .size_full() + .children(frame.map(|frame| img().size_full().surface(frame.image()))) } } // impl View for SharedScreen { diff --git a/crates/gpui2/build.rs b/crates/gpui2/build.rs index 6e8a0868b969c7a85627fd974f0f1bde793eb587..24e493cb812d6e7478b266cc621eea7cbc77b051 100644 --- a/crates/gpui2/build.rs +++ b/crates/gpui2/build.rs @@ -65,6 +65,8 @@ fn generate_shader_bindings() -> PathBuf { "MonochromeSprite".into(), "PolychromeSprite".into(), "PathSprite".into(), + "SurfaceInputIndex".into(), + "SurfaceBounds".into(), ]); config.no_includes = true; config.enumeration.prefix_with_name = true; diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 2aece17b4707f4c9fa473e7d7388e9ffdc8093f8..1c1fbd0961fb0a86a1c407d803245d11ce1c72d6 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -5,6 +5,7 @@ use crate::{ IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext, }; use futures::FutureExt; +use media::core_video::CVImageBuffer; use util::ResultExt; #[derive(Clone, Debug)] @@ -12,6 +13,7 @@ pub enum ImageSource { /// Image content will be loaded from provided URI at render time. Uri(SharedString), Data(Arc), + Surface(CVImageBuffer), } impl From for ImageSource { @@ -26,6 +28,12 @@ impl From> for ImageSource { } } +impl From for ImageSource { + fn from(value: CVImageBuffer) -> Self { + Self::Surface(value) + } +} + pub struct Img { interactivity: Interactivity, source: Option, @@ -45,11 +53,17 @@ impl Img { self.source = Some(ImageSource::from(uri.into())); self } + pub fn data(mut self, data: Arc) -> Self { self.source = Some(ImageSource::from(data)); self } + pub fn surface(mut self, data: CVImageBuffer) -> Self { + self.source = Some(ImageSource::from(data)); + self + } + pub fn source(mut self, source: impl Into) -> Self { self.source = Some(source.into()); self @@ -85,36 +99,41 @@ impl Element for Img { element_state, cx, |style, _scroll_offset, cx| { - let corner_radii = style.corner_radii; - - if let Some(source) = self.source { - let image = match source { - ImageSource::Uri(uri) => { - let image_future = cx.image_cache.get(uri.clone()); - if let Some(data) = image_future - .clone() - .now_or_never() - .and_then(|result| result.ok()) - { - data - } else { - cx.spawn(|mut cx| async move { - if image_future.await.ok().is_some() { - cx.on_next_frame(|cx| cx.notify()); - } - }) - .detach(); - return; + let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size()); + cx.with_z_index(1, |cx| { + if let Some(source) = self.source { + match source { + ImageSource::Uri(uri) => { + let image_future = cx.image_cache.get(uri.clone()); + if let Some(data) = image_future + .clone() + .now_or_never() + .and_then(|result| result.ok()) + { + cx.paint_image(bounds, corner_radii, data, self.grayscale) + .log_err(); + } else { + cx.spawn(|mut cx| async move { + if image_future.await.ok().is_some() { + cx.on_next_frame(|cx| cx.notify()); + } + }) + .detach(); + } + } + + ImageSource::Data(image) => { + cx.paint_image(bounds, corner_radii, image, self.grayscale) + .log_err(); + } + + ImageSource::Surface(surface) => { + // TODO: Add support for corner_radii and grayscale. + cx.paint_surface(bounds, surface); } - } - ImageSource::Data(image) => image, - }; - let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size()); - cx.with_z_index(1, |cx| { - cx.paint_image(bounds, corner_radii, image, self.grayscale) - .log_err() - }); - } + }; + } + }); }, ) } diff --git a/crates/gpui2/src/platform/mac/metal_renderer.rs b/crates/gpui2/src/platform/mac/metal_renderer.rs index 0631c75de5e222e43f780f29fb9844f5e96c099c..19afb503324907a0b84bcac7722fc71ead05451d 100644 --- a/crates/gpui2/src/platform/mac/metal_renderer.rs +++ b/crates/gpui2/src/platform/mac/metal_renderer.rs @@ -1,7 +1,7 @@ use crate::{ point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels, Hsla, MetalAtlas, MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch, - Quad, ScaledPixels, Scene, Shadow, Size, Underline, + Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline, }; use cocoa::{ base::{NO, YES}, @@ -9,6 +9,9 @@ use cocoa::{ quartzcore::AutoresizingMask, }; use collections::HashMap; +use core_foundation::base::TCFType; +use foreign_types::ForeignType; +use media::core_video::CVMetalTextureCache; use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange}; use objc::{self, msg_send, sel, sel_impl}; use smallvec::SmallVec; @@ -27,9 +30,11 @@ pub(crate) struct MetalRenderer { underlines_pipeline_state: metal::RenderPipelineState, monochrome_sprites_pipeline_state: metal::RenderPipelineState, polychrome_sprites_pipeline_state: metal::RenderPipelineState, + surfaces_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, sprite_atlas: Arc, + core_video_texture_cache: CVMetalTextureCache, } impl MetalRenderer { @@ -143,6 +148,14 @@ impl MetalRenderer { "polychrome_sprite_fragment", MTLPixelFormat::BGRA8Unorm, ); + let surfaces_pipeline_state = build_pipeline_state( + &device, + &library, + "surfaces", + "surface_vertex", + "surface_fragment", + MTLPixelFormat::BGRA8Unorm, + ); let command_queue = device.new_command_queue(); let sprite_atlas = Arc::new(MetalAtlas::new(device.clone())); @@ -157,9 +170,11 @@ impl MetalRenderer { underlines_pipeline_state, monochrome_sprites_pipeline_state, polychrome_sprites_pipeline_state, + surfaces_pipeline_state, unit_vertices, instances, sprite_atlas, + core_video_texture_cache: CVMetalTextureCache::new(device.as_ptr()).unwrap(), } } @@ -268,6 +283,14 @@ impl MetalRenderer { command_encoder, ); } + PrimitiveBatch::Surfaces(surfaces) => { + self.draw_surfaces( + surfaces, + &mut instance_offset, + viewport_size, + command_encoder, + ); + } } } @@ -793,6 +816,102 @@ impl MetalRenderer { ); *offset = next_offset; } + + fn draw_surfaces( + &mut self, + surfaces: &[Surface], + offset: &mut usize, + viewport_size: Size, + command_encoder: &metal::RenderCommandEncoderRef, + ) { + command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state); + command_encoder.set_vertex_buffer( + SurfaceInputIndex::Vertices as u64, + Some(&self.unit_vertices), + 0, + ); + command_encoder.set_vertex_bytes( + SurfaceInputIndex::ViewportSize as u64, + mem::size_of_val(&viewport_size) as u64, + &viewport_size as *const Size as *const _, + ); + + for surface in surfaces { + let texture_size = size( + DevicePixels::from(surface.image_buffer.width() as i32), + DevicePixels::from(surface.image_buffer.height() as i32), + ); + + assert_eq!( + surface.image_buffer.pixel_format_type(), + media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + ); + + let y_texture = self + .core_video_texture_cache + .create_texture_from_image( + surface.image_buffer.as_concrete_TypeRef(), + ptr::null(), + MTLPixelFormat::R8Unorm, + surface.image_buffer.plane_width(0), + surface.image_buffer.plane_height(0), + 0, + ) + .unwrap(); + let cb_cr_texture = self + .core_video_texture_cache + .create_texture_from_image( + surface.image_buffer.as_concrete_TypeRef(), + ptr::null(), + MTLPixelFormat::RG8Unorm, + surface.image_buffer.plane_width(1), + surface.image_buffer.plane_height(1), + 1, + ) + .unwrap(); + + align_offset(offset); + let next_offset = *offset + mem::size_of::(); + assert!( + next_offset <= INSTANCE_BUFFER_SIZE, + "instance buffer exhausted" + ); + + command_encoder.set_vertex_buffer( + SurfaceInputIndex::Surfaces as u64, + Some(&self.instances), + *offset as u64, + ); + command_encoder.set_vertex_bytes( + SurfaceInputIndex::TextureSize as u64, + mem::size_of_val(&texture_size) as u64, + &texture_size as *const Size as *const _, + ); + command_encoder.set_fragment_texture( + SurfaceInputIndex::YTexture as u64, + Some(y_texture.as_texture_ref()), + ); + command_encoder.set_fragment_texture( + SurfaceInputIndex::CbCrTexture as u64, + Some(cb_cr_texture.as_texture_ref()), + ); + + unsafe { + let buffer_contents = + (self.instances.contents() as *mut u8).add(*offset) as *mut SurfaceBounds; + ptr::write( + buffer_contents, + SurfaceBounds { + bounds: surface.bounds, + content_mask: surface.content_mask.clone(), + }, + ); + } + + command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6); + *offset = next_offset; + } + } } fn build_pipeline_state( @@ -898,6 +1017,16 @@ enum SpriteInputIndex { AtlasTexture = 4, } +#[repr(C)] +enum SurfaceInputIndex { + Vertices = 0, + Surfaces = 1, + ViewportSize = 2, + TextureSize = 3, + YTexture = 4, + CbCrTexture = 5, +} + #[repr(C)] enum PathRasterizationInputIndex { Vertices = 0, @@ -911,3 +1040,10 @@ pub struct PathSprite { pub color: Hsla, pub tile: AtlasTile, } + +#[derive(Clone, Debug, Eq, PartialEq)] +#[repr(C)] +pub struct SurfaceBounds { + pub bounds: Bounds, + pub content_mask: ContentMask, +} diff --git a/crates/gpui2/src/platform/mac/shaders.metal b/crates/gpui2/src/platform/mac/shaders.metal index 4def1c33b85a430d051376322dca47de93b9e70c..aba01b9d5b059da1c1df55c0a01120d8be10775b 100644 --- a/crates/gpui2/src/platform/mac/shaders.metal +++ b/crates/gpui2/src/platform/mac/shaders.metal @@ -469,6 +469,58 @@ fragment float4 path_sprite_fragment( return color; } +struct SurfaceVertexOutput { + float4 position [[position]]; + float2 texture_position; + float clip_distance [[clip_distance]][4]; +}; + +struct SurfaceFragmentInput { + float4 position [[position]]; + float2 texture_position; +}; + +vertex SurfaceVertexOutput surface_vertex( + uint unit_vertex_id [[vertex_id]], uint surface_id [[instance_id]], + constant float2 *unit_vertices [[buffer(SurfaceInputIndex_Vertices)]], + constant SurfaceBounds *surfaces [[buffer(SurfaceInputIndex_Surfaces)]], + constant Size_DevicePixels *viewport_size + [[buffer(SurfaceInputIndex_ViewportSize)]], + constant Size_DevicePixels *texture_size + [[buffer(SurfaceInputIndex_TextureSize)]]) { + float2 unit_vertex = unit_vertices[unit_vertex_id]; + SurfaceBounds surface = surfaces[surface_id]; + float4 device_position = + to_device_position(unit_vertex, surface.bounds, viewport_size); + float4 clip_distance = distance_from_clip_rect(unit_vertex, surface.bounds, + surface.content_mask.bounds); + // We are going to copy the whole texture, so the texture position corresponds + // to the current vertex of the unit triangle. + float2 texture_position = unit_vertex; + return SurfaceVertexOutput{ + device_position, + texture_position, + {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}}; +} + +fragment float4 surface_fragment(SurfaceFragmentInput input [[stage_in]], + texture2d y_texture + [[texture(SurfaceInputIndex_YTexture)]], + texture2d cb_cr_texture + [[texture(SurfaceInputIndex_CbCrTexture)]]) { + constexpr sampler texture_sampler(mag_filter::linear, min_filter::linear); + const float4x4 ycbcrToRGBTransform = + float4x4(float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f), + float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f), + float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f), + float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)); + float4 ycbcr = float4( + y_texture.sample(texture_sampler, input.texture_position).r, + cb_cr_texture.sample(texture_sampler, input.texture_position).rg, 1.0); + + return ycbcrToRGBTransform * ycbcr; +} + float4 hsla_to_rgba(Hsla hsla) { float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range float s = hsla.s; diff --git a/crates/gpui2/src/scene.rs b/crates/gpui2/src/scene.rs index 87e89adfa0e15d18512d2a1a748ea5a212a48489..549260560236ffb43179caf6cb8fce5340c10b7a 100644 --- a/crates/gpui2/src/scene.rs +++ b/crates/gpui2/src/scene.rs @@ -25,6 +25,7 @@ pub(crate) struct SceneBuilder { underlines: Vec, monochrome_sprites: Vec, polychrome_sprites: Vec, + surfaces: Vec, } impl Default for SceneBuilder { @@ -38,6 +39,7 @@ impl Default for SceneBuilder { underlines: Vec::new(), monochrome_sprites: Vec::new(), polychrome_sprites: Vec::new(), + surfaces: Vec::new(), } } } @@ -120,6 +122,7 @@ impl SceneBuilder { (PrimitiveKind::PolychromeSprite, ix) => { self.polychrome_sprites[ix].order = draw_order as DrawOrder } + (PrimitiveKind::Surface, ix) => self.surfaces[ix].order = draw_order as DrawOrder, } } @@ -129,6 +132,7 @@ impl SceneBuilder { self.underlines.sort_unstable(); self.monochrome_sprites.sort_unstable(); self.polychrome_sprites.sort_unstable(); + self.surfaces.sort_unstable(); Scene { shadows: mem::take(&mut self.shadows), @@ -137,6 +141,7 @@ impl SceneBuilder { underlines: mem::take(&mut self.underlines), monochrome_sprites: mem::take(&mut self.monochrome_sprites), polychrome_sprites: mem::take(&mut self.polychrome_sprites), + surfaces: mem::take(&mut self.surfaces), } } @@ -185,6 +190,10 @@ impl SceneBuilder { sprite.order = layer_id; self.polychrome_sprites.push(sprite); } + Primitive::Surface(mut surface) => { + surface.order = layer_id; + self.surfaces.push(surface); + } } } } @@ -196,6 +205,7 @@ pub(crate) struct Scene { pub underlines: Vec, pub monochrome_sprites: Vec, pub polychrome_sprites: Vec, + pub surfaces: Vec, } impl Scene { @@ -224,6 +234,9 @@ impl Scene { polychrome_sprites: &self.polychrome_sprites, polychrome_sprites_start: 0, polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(), + surfaces: &self.surfaces, + surfaces_start: 0, + surfaces_iter: self.surfaces.iter().peekable(), } } } @@ -247,6 +260,9 @@ struct BatchIterator<'a> { polychrome_sprites: &'a [PolychromeSprite], polychrome_sprites_start: usize, polychrome_sprites_iter: Peekable>, + surfaces: &'a [Surface], + surfaces_start: usize, + surfaces_iter: Peekable>, } impl<'a> Iterator for BatchIterator<'a> { @@ -272,6 +288,10 @@ impl<'a> Iterator for BatchIterator<'a> { self.polychrome_sprites_iter.peek().map(|s| s.order), PrimitiveKind::PolychromeSprite, ), + ( + self.surfaces_iter.peek().map(|s| s.order), + PrimitiveKind::Surface, + ), ]; orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind)); @@ -378,6 +398,21 @@ impl<'a> Iterator for BatchIterator<'a> { sprites: &self.polychrome_sprites[sprites_start..sprites_end], }) } + PrimitiveKind::Surface => { + let surfaces_start = self.surfaces_start; + let mut surfaces_end = surfaces_start; + while self + .surfaces_iter + .next_if(|surface| surface.order <= max_order) + .is_some() + { + surfaces_end += 1; + } + self.surfaces_start = surfaces_end; + Some(PrimitiveBatch::Surfaces( + &self.surfaces[surfaces_start..surfaces_end], + )) + } } } } @@ -391,6 +426,7 @@ pub enum PrimitiveKind { Underline, MonochromeSprite, PolychromeSprite, + Surface, } pub enum Primitive { @@ -400,6 +436,7 @@ pub enum Primitive { Underline(Underline), MonochromeSprite(MonochromeSprite), PolychromeSprite(PolychromeSprite), + Surface(Surface), } impl Primitive { @@ -411,6 +448,7 @@ impl Primitive { Primitive::Underline(underline) => &underline.bounds, Primitive::MonochromeSprite(sprite) => &sprite.bounds, Primitive::PolychromeSprite(sprite) => &sprite.bounds, + Primitive::Surface(surface) => &surface.bounds, } } @@ -422,6 +460,7 @@ impl Primitive { Primitive::Underline(underline) => &underline.content_mask, Primitive::MonochromeSprite(sprite) => &sprite.content_mask, Primitive::PolychromeSprite(sprite) => &sprite.content_mask, + Primitive::Surface(surface) => &surface.content_mask, } } } @@ -440,6 +479,7 @@ pub(crate) enum PrimitiveBatch<'a> { texture_id: AtlasTextureId, sprites: &'a [PolychromeSprite], }, + Surfaces(&'a [Surface]), } #[derive(Default, Debug, Clone, Eq, PartialEq)] @@ -593,6 +633,32 @@ impl From for Primitive { } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Surface { + pub order: u32, + pub bounds: Bounds, + pub content_mask: ContentMask, + pub image_buffer: media::core_video::CVImageBuffer, +} + +impl Ord for Surface { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.order.cmp(&other.order) + } +} + +impl PartialOrd for Surface { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From for Primitive { + fn from(surface: Surface) -> Self { + Primitive::Surface(surface) + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub(crate) struct PathId(pub(crate) usize); diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 20561c544368b6b9c41124194c07ada33145c968..1f98860f09c40eed3a427225dc9aa22fb7494986 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -8,8 +8,8 @@ use crate::{ MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, - VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, + Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, + UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Context as _, Result}; use collections::HashMap; @@ -18,6 +18,7 @@ use futures::{ channel::{mpsc, oneshot}, StreamExt, }; +use media::core_video::CVImageBuffer; use parking_lot::RwLock; use slotmap::SlotMap; use smallvec::SmallVec; @@ -1090,6 +1091,23 @@ impl<'a> WindowContext<'a> { Ok(()) } + /// Paint a surface into the scene for the current frame at the current z-index. + pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVImageBuffer) { + let scale_factor = self.scale_factor(); + let bounds = bounds.scale(scale_factor); + let content_mask = self.content_mask().scale(scale_factor); + let window = &mut *self.window; + window.current_frame.scene_builder.insert( + &window.current_frame.z_index_stack, + Surface { + order: 0, + bounds, + content_mask, + image_buffer, + }, + ); + } + /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) { let root_view = self.window.root_view.take().unwrap(); From 3ac545088ac41e7b27ed855de84ba9cfafdceaf9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 28 Nov 2023 17:32:27 +0100 Subject: [PATCH 2/6] WIP: preserve aspect ratio of images --- crates/call2/src/shared_screen.rs | 2 +- crates/gpui2/src/elements/img.rs | 134 ++++++++++++++++------------ crates/gpui2/src/geometry.rs | 6 ++ crates/ui2/src/components/avatar.rs | 5 +- 4 files changed, 88 insertions(+), 59 deletions(-) diff --git a/crates/call2/src/shared_screen.rs b/crates/call2/src/shared_screen.rs index 14b997af3b4f7221ff6a2acded9af516d7773310..58d8445f8f0ef242ba563eeca76d2c3da5d78030 100644 --- a/crates/call2/src/shared_screen.rs +++ b/crates/call2/src/shared_screen.rs @@ -72,7 +72,7 @@ impl Render for SharedScreen { // self.current_frame_id = self.current_frame_id.wrapping_add(1); div() .size_full() - .children(frame.map(|frame| img().size_full().surface(frame.image()))) + .children(frame.map(|frame| img(frame.image()).w_full())) } } // impl View for SharedScreen { diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 1c1fbd0961fb0a86a1c407d803245d11ce1c72d6..87a7cda07fe11d9f9da0fee9c33a76551b3b8347 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use crate::{ - Bounds, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity, - IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext, + size, Bounds, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity, + IntoElement, LayoutId, Pixels, SharedString, Size, StyleRefinement, Styled, WindowContext, }; use futures::FutureExt; use media::core_video::CVImageBuffer; @@ -36,38 +36,19 @@ impl From for ImageSource { pub struct Img { interactivity: Interactivity, - source: Option, + source: ImageSource, grayscale: bool, } -pub fn img() -> Img { +pub fn img(source: impl Into) -> Img { Img { interactivity: Interactivity::default(), - source: None, + source: source.into(), grayscale: false, } } impl Img { - pub fn uri(mut self, uri: impl Into) -> Self { - self.source = Some(ImageSource::from(uri.into())); - self - } - - pub fn data(mut self, data: Arc) -> Self { - self.source = Some(ImageSource::from(data)); - self - } - - pub fn surface(mut self, data: CVImageBuffer) -> Self { - self.source = Some(ImageSource::from(data)); - self - } - - pub fn source(mut self, source: impl Into) -> Self { - self.source = Some(source.into()); - self - } pub fn grayscale(mut self, grayscale: bool) -> Self { self.grayscale = grayscale; self @@ -83,7 +64,52 @@ impl Element for Img { cx: &mut WindowContext, ) -> (LayoutId, Self::State) { self.interactivity.layout(element_state, cx, |style, cx| { - cx.request_layout(&style, None) + let image_size = match &self.source { + ImageSource::Uri(uri) => { + let image_future = cx.image_cache.get(uri.clone()); + if let Some(data) = image_future + .clone() + .now_or_never() + .and_then(|result| result.ok()) + { + data.size().map(|pixels| Pixels::from(u32::from(pixels))) + } else { + Size::default() + } + } + + ImageSource::Data(data) => { + data.size().map(|pixels| Pixels::from(u32::from(pixels))) + } + + ImageSource::Surface(surface) => { + size(surface.width().into(), surface.height().into()) + } + }; + dbg!(image_size); + + cx.request_measured_layout( + style, + cx.rem_size(), + move |known_dimensions, available_space| match dbg!( + known_dimensions.width, + known_dimensions.height, + ) { + (None, None) => image_size, + + (None, Some(height)) => { + let aspect_ratio = height / image_size.height; + size(image_size.width * aspect_ratio, height) + } + + (Some(width), None) => { + let aspect_ratio = width / image_size.width; + size(width, image_size.height * aspect_ratio) + } + + (Some(width), Some(height)) => size(width, height), + }, + ) }) } @@ -101,38 +127,36 @@ impl Element for Img { |style, _scroll_offset, cx| { let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size()); cx.with_z_index(1, |cx| { - if let Some(source) = self.source { - match source { - ImageSource::Uri(uri) => { - let image_future = cx.image_cache.get(uri.clone()); - if let Some(data) = image_future - .clone() - .now_or_never() - .and_then(|result| result.ok()) - { - cx.paint_image(bounds, corner_radii, data, self.grayscale) - .log_err(); - } else { - cx.spawn(|mut cx| async move { - if image_future.await.ok().is_some() { - cx.on_next_frame(|cx| cx.notify()); - } - }) - .detach(); - } - } - - ImageSource::Data(image) => { - cx.paint_image(bounds, corner_radii, image, self.grayscale) + match self.source { + ImageSource::Uri(uri) => { + let image_future = cx.image_cache.get(uri.clone()); + if let Some(data) = image_future + .clone() + .now_or_never() + .and_then(|result| result.ok()) + { + cx.paint_image(bounds, corner_radii, data, self.grayscale) .log_err(); + } else { + cx.spawn(|mut cx| async move { + if image_future.await.ok().is_some() { + cx.on_next_frame(|cx| cx.notify()); + } + }) + .detach(); } - - ImageSource::Surface(surface) => { - // TODO: Add support for corner_radii and grayscale. - cx.paint_surface(bounds, surface); - } - }; - } + } + + ImageSource::Data(image) => { + cx.paint_image(bounds, corner_radii, image, self.grayscale) + .log_err(); + } + + ImageSource::Surface(surface) => { + // TODO: Add support for corner_radii and grayscale. + cx.paint_surface(bounds, surface); + } + }; }); }, ) diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index e1f039e309466bcd07e867551b2e082b27c6a186..5ade68663c6ea4a379663044027bd0dbef9061ae 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -905,6 +905,12 @@ impl From for usize { } } +impl From for Pixels { + fn from(pixels: usize) -> Self { + Pixels(pixels as f32) + } +} + #[derive( Add, AddAssign, Clone, Copy, Default, Div, Eq, Hash, Ord, PartialEq, PartialOrd, Sub, SubAssign, )] diff --git a/crates/ui2/src/components/avatar.rs b/crates/ui2/src/components/avatar.rs index d358b221da9287f488bd68dd95bcff659191b5a5..f74a7d02eadfa5a076ce07c219983f4e60853551 100644 --- a/crates/ui2/src/components/avatar.rs +++ b/crates/ui2/src/components/avatar.rs @@ -20,7 +20,7 @@ impl RenderOnce for Avatar { type Rendered = Img; fn render(self, _: &mut WindowContext) -> Self::Rendered { - let mut img = img(); + let mut img = img(self.src); if self.shape == Shape::Circle { img = img.rounded_full(); @@ -28,8 +28,7 @@ impl RenderOnce for Avatar { img = img.rounded_md(); } - img.source(self.src.clone()) - .size_4() + img.size_4() // todo!(Pull the avatar fallback background from the theme.) .bg(gpui::red()) } From a4035f15afe527e4e35fc6f6eaaeecbc95cf41d9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 29 Nov 2023 18:18:15 +0100 Subject: [PATCH 3/6] WIP --- crates/gpui2/src/elements/img.rs | 85 ++++++++++++-------------------- crates/gpui2/src/geometry.rs | 12 +++++ 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 87a7cda07fe11d9f9da0fee9c33a76551b3b8347..dd59ddb17c9365c4868839a62202f8c1d671b2fc 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use crate::{ - size, Bounds, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity, - IntoElement, LayoutId, Pixels, SharedString, Size, StyleRefinement, Styled, WindowContext, + point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement, + InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedString, Size, + StyleRefinement, Styled, WindowContext, }; use futures::FutureExt; use media::core_video::CVImageBuffer; @@ -63,54 +64,8 @@ impl Element for Img { element_state: Option, cx: &mut WindowContext, ) -> (LayoutId, Self::State) { - self.interactivity.layout(element_state, cx, |style, cx| { - let image_size = match &self.source { - ImageSource::Uri(uri) => { - let image_future = cx.image_cache.get(uri.clone()); - if let Some(data) = image_future - .clone() - .now_or_never() - .and_then(|result| result.ok()) - { - data.size().map(|pixels| Pixels::from(u32::from(pixels))) - } else { - Size::default() - } - } - - ImageSource::Data(data) => { - data.size().map(|pixels| Pixels::from(u32::from(pixels))) - } - - ImageSource::Surface(surface) => { - size(surface.width().into(), surface.height().into()) - } - }; - dbg!(image_size); - - cx.request_measured_layout( - style, - cx.rem_size(), - move |known_dimensions, available_space| match dbg!( - known_dimensions.width, - known_dimensions.height, - ) { - (None, None) => image_size, - - (None, Some(height)) => { - let aspect_ratio = height / image_size.height; - size(image_size.width * aspect_ratio, height) - } - - (Some(width), None) => { - let aspect_ratio = width / image_size.width; - size(width, image_size.height * aspect_ratio) - } - - (Some(width), Some(height)) => size(width, height), - }, - ) - }) + self.interactivity + .layout(element_state, cx, |style, cx| cx.request_layout(&style, [])) } fn paint( @@ -135,7 +90,8 @@ impl Element for Img { .now_or_never() .and_then(|result| result.ok()) { - cx.paint_image(bounds, corner_radii, data, self.grayscale) + let new_bounds = preserve_aspect_ratio(bounds, data.size()); + cx.paint_image(new_bounds, corner_radii, data, self.grayscale) .log_err(); } else { cx.spawn(|mut cx| async move { @@ -147,14 +103,17 @@ impl Element for Img { } } - ImageSource::Data(image) => { - cx.paint_image(bounds, corner_radii, image, self.grayscale) + ImageSource::Data(data) => { + let new_bounds = preserve_aspect_ratio(bounds, data.size()); + cx.paint_image(new_bounds, corner_radii, data, self.grayscale) .log_err(); } ImageSource::Surface(surface) => { + let size = size(surface.width().into(), surface.height().into()); + let new_bounds = preserve_aspect_ratio(bounds, size); // TODO: Add support for corner_radii and grayscale. - cx.paint_surface(bounds, surface); + cx.paint_surface(new_bounds, surface); } }; }); @@ -186,3 +145,21 @@ impl InteractiveElement for Img { &mut self.interactivity } } + +fn preserve_aspect_ratio(bounds: Bounds, image_size: Size) -> Bounds { + let new_size = if bounds.size.width > bounds.size.height { + let ratio = u32::from(image_size.height) as f32 / u32::from(image_size.width) as f32; + size(bounds.size.width, bounds.size.width * ratio) + } else { + let ratio = u32::from(image_size.width) as f32 / u32::from(image_size.height) as f32; + size(bounds.size.width * ratio, bounds.size.height) + }; + + Bounds { + origin: point( + bounds.origin.x + (bounds.size.width - new_size.width) / 2.0, + bounds.origin.y + (bounds.size.height - new_size.height) / 2.0, + ), + size: new_size, + } +} diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index 5ade68663c6ea4a379663044027bd0dbef9061ae..bc927a17ba366b4bd9f4df3fbdefc6bfa24dd751 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -965,6 +965,18 @@ impl From for DevicePixels { } } +impl From for usize { + fn from(device_pixels: DevicePixels) -> Self { + device_pixels.0 as usize + } +} + +impl From for DevicePixels { + fn from(device_pixels: usize) -> Self { + DevicePixels(device_pixels as i32) + } +} + #[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)] #[repr(transparent)] pub struct ScaledPixels(pub(crate) f32); From 624ea58f2e3e26b184aca2fb7c9791b3b5032100 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 30 Nov 2023 11:22:01 +0100 Subject: [PATCH 4/6] Polish screen sharing --- crates/call2/src/call2.rs | 21 ++------ crates/call2/src/shared_screen.rs | 80 +++++++---------------------- crates/gpui2/src/elements/img.rs | 18 +++++-- crates/workspace2/src/workspace2.rs | 24 ++++++--- 4 files changed, 54 insertions(+), 89 deletions(-) diff --git a/crates/call2/src/call2.rs b/crates/call2/src/call2.rs index df7dd847cf3413a410ce17b87f1c89efa2e2905e..a93305772312cab3624f995e0dd49751554867e1 100644 --- a/crates/call2/src/call2.rs +++ b/crates/call2/src/call2.rs @@ -15,7 +15,7 @@ use collections::HashSet; use futures::{channel::oneshot, future::Shared, Future, FutureExt}; use gpui::{ AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, PromptLevel, - Subscription, Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowHandle, + Subscription, Task, View, ViewContext, VisualContext, WeakModel, WindowHandle, }; pub use participant::ParticipantLocation; use postage::watch; @@ -557,24 +557,17 @@ pub fn report_call_event_for_channel( pub struct Call { active_call: Option<(Model, Vec)>, - parent_workspace: WeakView, } impl Call { - pub fn new( - parent_workspace: WeakView, - cx: &mut ViewContext<'_, Workspace>, - ) -> Box { + pub fn new(cx: &mut ViewContext<'_, Workspace>) -> Box { let mut active_call = None; if cx.has_global::>() { let call = cx.global::>().clone(); let subscriptions = vec![cx.subscribe(&call, Self::on_active_call_event)]; active_call = Some((call, subscriptions)); } - Box::new(Self { - active_call, - parent_workspace, - }) + Box::new(Self { active_call }) } fn on_active_call_event( workspace: &mut Workspace, @@ -597,6 +590,7 @@ impl CallHandler for Call { fn peer_state( &mut self, leader_id: PeerId, + project: &Model, cx: &mut ViewContext, ) -> Option<(bool, bool)> { let (call, _) = self.active_call.as_ref()?; @@ -608,12 +602,7 @@ impl CallHandler for Call { match participant.location { ParticipantLocation::SharedProject { project_id } => { leader_in_this_app = true; - leader_in_this_project = Some(project_id) - == self - .parent_workspace - .update(cx, |this, cx| this.project().read(cx).remote_id()) - .log_err() - .flatten(); + leader_in_this_project = Some(project_id) == project.read(cx).remote_id(); } ParticipantLocation::UnsharedProject => { leader_in_this_app = true; diff --git a/crates/call2/src/shared_screen.rs b/crates/call2/src/shared_screen.rs index 58d8445f8f0ef242ba563eeca76d2c3da5d78030..c38ebeac021d59c810fc27ff528ddc773f9642f4 100644 --- a/crates/call2/src/shared_screen.rs +++ b/crates/call2/src/shared_screen.rs @@ -3,10 +3,12 @@ use anyhow::Result; use client::{proto::PeerId, User}; use futures::StreamExt; use gpui::{ - div, img, AppContext, Div, Element, EventEmitter, FocusHandle, FocusableView, ParentElement, - Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WindowContext, + div, img, AppContext, Div, Element, EventEmitter, FocusHandle, Focusable, FocusableView, + InteractiveElement, ParentElement, Render, SharedString, Styled, Task, View, ViewContext, + VisualContext, WindowContext, }; use std::sync::{Arc, Weak}; +use ui::{h_stack, Icon, IconElement}; use workspace::{item::Item, ItemNavHistory, WorkspaceId}; pub enum Event { @@ -16,8 +18,6 @@ pub enum Event { pub struct SharedScreen { track: Weak, frame: Option, - // temporary addition just to render something interactive. - current_frame_id: usize, pub peer_id: PeerId, user: Arc, nav_history: Option, @@ -51,7 +51,6 @@ impl SharedScreen { Ok(()) }), focus: cx.focus_handle(), - current_frame_id: 0, } } } @@ -65,46 +64,16 @@ impl FocusableView for SharedScreen { } } impl Render for SharedScreen { - type Element = Div; + type Element = Focusable
; + fn render(&mut self, _: &mut ViewContext) -> Self::Element { - let frame = self.frame.clone(); - // let frame_id = self.current_frame_id; - // self.current_frame_id = self.current_frame_id.wrapping_add(1); - div() - .size_full() - .children(frame.map(|frame| img(frame.image()).w_full())) + div().track_focus(&self.focus).size_full().children( + self.frame + .as_ref() + .map(|frame| img(frame.image()).size_full()), + ) } } -// impl View for SharedScreen { -// fn ui_name() -> &'static str { -// "SharedScreen" -// } - -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// enum Focus {} - -// let frame = self.frame.clone(); -// MouseEventHandler::new::(0, cx, |_, cx| { -// Canvas::new(move |bounds, _, _, cx| { -// if let Some(frame) = frame.clone() { -// let size = constrain_size_preserving_aspect_ratio( -// bounds.size(), -// vec2f(frame.width() as f32, frame.height() as f32), -// ); -// let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.; -// cx.scene().push_surface(gpui::platform::mac::Surface { -// bounds: RectF::new(origin, size), -// image_buffer: frame.image(), -// }); -// } -// }) -// .contained() -// .with_style(theme::current(cx).shared_screen) -// }) -// .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent()) -// .into_any() -// } -// } impl Item for SharedScreen { fn tab_tooltip_text(&self, _: &AppContext) -> Option { @@ -117,25 +86,14 @@ impl Item for SharedScreen { } fn tab_content(&self, _: Option, _: &WindowContext<'_>) -> gpui::AnyElement { - div().child("Shared screen").into_any() - // Flex::row() - // .with_child( - // Svg::new("icons/desktop.svg") - // .with_color(style.label.text.color) - // .constrained() - // .with_width(style.type_icon_width) - // .aligned() - // .contained() - // .with_margin_right(style.spacing), - // ) - // .with_child( - // Label::new( - // format!("{}'s screen", self.user.github_login), - // style.label.clone(), - // ) - // .aligned(), - // ) - // .into_any() + h_stack() + .gap_1() + .child(IconElement::new(Icon::Screen)) + .child(SharedString::from(format!( + "{}'s screen", + self.user.github_login + ))) + .into_any() } fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext) { diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index dd59ddb17c9365c4868839a62202f8c1d671b2fc..e519a93e64d5205bd70b2c5f08458ab5a5180c28 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -147,12 +147,20 @@ impl InteractiveElement for Img { } fn preserve_aspect_ratio(bounds: Bounds, image_size: Size) -> Bounds { - let new_size = if bounds.size.width > bounds.size.height { - let ratio = u32::from(image_size.height) as f32 / u32::from(image_size.width) as f32; - size(bounds.size.width, bounds.size.width * ratio) + let image_size = image_size.map(|dimension| Pixels::from(u32::from(dimension))); + let image_ratio = image_size.width / image_size.height; + let bounds_ratio = bounds.size.width / bounds.size.height; + + let new_size = if bounds_ratio > image_ratio { + size( + image_size.width * (bounds.size.height / image_size.height), + bounds.size.height, + ) } else { - let ratio = u32::from(image_size.width) as f32 / u32::from(image_size.height) as f32; - size(bounds.size.width * ratio, bounds.size.height) + size( + bounds.size.width, + image_size.height * (bounds.size.width / image_size.width), + ) }; Bounds { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 23907f8dae882f0780e5a8ad351a96f74129c363..cbd3e4309cd9a3e3aa38a1dd6645cc3b22a05505 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -326,7 +326,12 @@ pub struct TestCallHandler; #[cfg(any(test, feature = "test-support"))] impl CallHandler for TestCallHandler { - fn peer_state(&mut self, id: PeerId, cx: &mut ViewContext) -> Option<(bool, bool)> { + fn peer_state( + &mut self, + id: PeerId, + project: &Model, + cx: &mut ViewContext, + ) -> Option<(bool, bool)> { None } @@ -409,7 +414,7 @@ impl AppState { workspace_store, node_runtime: FakeNodeRuntime::new(), build_window_options: |_, _, _| Default::default(), - call_factory: |_, _| Box::new(TestCallHandler), + call_factory: |_| Box::new(TestCallHandler), }) } } @@ -468,7 +473,12 @@ pub enum Event { #[async_trait(?Send)] pub trait CallHandler { - fn peer_state(&mut self, id: PeerId, cx: &mut ViewContext) -> Option<(bool, bool)>; + fn peer_state( + &mut self, + id: PeerId, + project: &Model, + cx: &mut ViewContext, + ) -> Option<(bool, bool)>; fn shared_screen_for_peer( &self, peer_id: PeerId, @@ -546,7 +556,7 @@ struct FollowerState { enum WorkspaceBounds {} -type CallFactory = fn(WeakView, &mut ViewContext) -> Box; +type CallFactory = fn(&mut ViewContext) -> Box; impl Workspace { pub fn new( workspace_id: WorkspaceId, @@ -760,7 +770,7 @@ impl Workspace { last_leaders_by_pane: Default::default(), window_edited: false, - call_handler: (app_state.call_factory)(weak_handle.clone(), cx), + call_handler: (app_state.call_factory)(cx), database_id: workspace_id, app_state, _observe_current_user, @@ -2884,7 +2894,7 @@ impl Workspace { cx.notify(); let (leader_in_this_project, leader_in_this_app) = - self.call_handler.peer_state(leader_id, cx)?; + self.call_handler.peer_state(leader_id, &self.project, cx)?; let mut items_to_activate = Vec::new(); for (pane, state) in &self.follower_states { if state.leader_id != leader_id { @@ -3385,7 +3395,7 @@ impl Workspace { fs: project.read(cx).fs().clone(), build_window_options: |_, _, _| Default::default(), node_runtime: FakeNodeRuntime::new(), - call_factory: |_, _| Box::new(TestCallHandler), + call_factory: |_| Box::new(TestCallHandler), }); let workspace = Self::new(0, project, app_state, cx); workspace.active_pane.update(cx, |pane, cx| pane.focus(cx)); From d2c1897385badde8b276c981f2c0da495b7fa3ef Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 30 Nov 2023 11:45:10 +0100 Subject: [PATCH 5/6] Fix error in tests --- crates/collab2/src/tests/test_server.rs | 2 +- crates/editor2/src/test.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/collab2/src/tests/test_server.rs b/crates/collab2/src/tests/test_server.rs index 969869599b33f4d9e4ec19d8df14fda875bc9ddf..5f95f00d6fcd5c74d81d90f9b4b455ab531862d5 100644 --- a/crates/collab2/src/tests/test_server.rs +++ b/crates/collab2/src/tests/test_server.rs @@ -221,7 +221,7 @@ impl TestServer { fs: fs.clone(), build_window_options: |_, _, _| Default::default(), node_runtime: FakeNodeRuntime::new(), - call_factory: |_, _| Box::new(workspace::TestCallHandler), + call_factory: |_| Box::new(workspace::TestCallHandler), }); cx.update(|cx| { diff --git a/crates/editor2/src/test.rs b/crates/editor2/src/test.rs index ec37c57f2c9656a978e289cbb33f8580004f98f9..4f6e157e4e0cbf27831b578aeeb25860cbc8e632 100644 --- a/crates/editor2/src/test.rs +++ b/crates/editor2/src/test.rs @@ -27,7 +27,7 @@ pub fn marked_display_snapshot( let (unmarked_text, markers) = marked_text_offsets(text); let font = cx.text_style().font(); - let font_size: Pixels = 14.into(); + let font_size: Pixels = 14usize.into(); let buffer = MultiBuffer::build_simple(&unmarked_text, cx); let display_map = cx.build_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx)); From fac029b80867a65bf2c45fbf6025b2780b7a91e3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 30 Nov 2023 12:08:00 +0100 Subject: [PATCH 6/6] Fix errors in theme2 --- crates/gpui2/src/elements/img.rs | 12 +++ crates/theme2/src/styles/stories/players.rs | 92 ++++++++++++--------- 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index e519a93e64d5205bd70b2c5f08458ab5a5180c28..f6aae2de66aebb7bc894bd63a8d9a85a6e74e089 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -23,6 +23,18 @@ impl From for ImageSource { } } +impl From<&'static str> for ImageSource { + fn from(uri: &'static str) -> Self { + Self::Uri(uri.into()) + } +} + +impl From for ImageSource { + fn from(uri: String) -> Self { + Self::Uri(uri.into()) + } +} + impl From> for ImageSource { fn from(value: Arc) -> Self { Self::Data(value) diff --git a/crates/theme2/src/styles/stories/players.rs b/crates/theme2/src/styles/stories/players.rs index d189d3bfb07bb0efc4691ca14abac4d94948d620..237f2f10817872ea07c0f7fafa5745ee8722cf13 100644 --- a/crates/theme2/src/styles/stories/players.rs +++ b/crates/theme2/src/styles/stories/players.rs @@ -55,9 +55,8 @@ impl Render for PlayerStory { .border_2() .border_color(player.cursor) .child( - img() + img("https://avatars.githubusercontent.com/u/1714999?v=4") .rounded_full() - .uri("https://avatars.githubusercontent.com/u/1714999?v=4") .size_6() .bg(gpui::red()), ) @@ -67,51 +66,62 @@ impl Render for PlayerStory { .child(div().flex().gap_1().children( cx.theme().players().0.clone().iter_mut().map(|player| { div() - .my_1() - .rounded_xl() - .flex() - .items_center() - .h_8() - .py_0p5() - .px_1p5() - .bg(player.background) - .child( - div().relative().neg_mx_1().rounded_full().z_index(3) + .my_1() + .rounded_xl() + .flex() + .items_center() + .h_8() + .py_0p5() + .px_1p5() + .bg(player.background) + .child( + div() + .relative() + .neg_mx_1() + .rounded_full() + .z_index(3) .border_2() .border_color(player.background) .size(px(28.)) .child( - img() - .rounded_full() - .uri("https://avatars.githubusercontent.com/u/1714999?v=4") - .size(px(24.)) - .bg(gpui::red()), - ), - ).child( - div().relative().neg_mx_1().rounded_full().z_index(2) - .border_2() - .border_color(player.background) - .size(px(28.)) - .child( - img() + img("https://avatars.githubusercontent.com/u/1714999?v=4") + .rounded_full() + .size(px(24.)) + .bg(gpui::red()), + ), + ) + .child( + div() + .relative() + .neg_mx_1() .rounded_full() - .uri("https://avatars.githubusercontent.com/u/1714999?v=4") - .size(px(24.)) - .bg(gpui::red()), - ), - ).child( - div().relative().neg_mx_1().rounded_full().z_index(1) - .border_2() - .border_color(player.background) - .size(px(28.)) + .z_index(2) + .border_2() + .border_color(player.background) + .size(px(28.)) + .child( + img("https://avatars.githubusercontent.com/u/1714999?v=4") + .rounded_full() + .size(px(24.)) + .bg(gpui::red()), + ), + ) .child( - img() - .rounded_full() - .uri("https://avatars.githubusercontent.com/u/1714999?v=4") - .size(px(24.)) - .bg(gpui::red()), - ), - ) + div() + .relative() + .neg_mx_1() + .rounded_full() + .z_index(1) + .border_2() + .border_color(player.background) + .size(px(28.)) + .child( + img("https://avatars.githubusercontent.com/u/1714999?v=4") + .rounded_full() + .size(px(24.)) + .bg(gpui::red()), + ), + ) }), )) .child(Story::label("Player Selections"))