diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 47fe8cbbfb..a396f8728a 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -3,8 +3,8 @@ use futures::StreamExt; use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, - impl_internal_actions, Entity, MouseButton, MutableAppContext, RenderContext, View, - ViewContext, WindowBounds, WindowKind, WindowOptions, + impl_internal_actions, CursorStyle, Entity, MouseButton, MutableAppContext, RenderContext, + View, ViewContext, WindowBounds, WindowKind, WindowOptions, }; use settings::Settings; use util::ResultExt; @@ -24,11 +24,17 @@ pub fn init(cx: &mut MutableAppContext) { } if let Some(incoming_call) = incoming_call { + const PADDING: f32 = 16.; + let screen_size = cx.platform().screen_size(); + let window_size = vec2f(304., 64.); let (window_id, _) = cx.add_window( WindowOptions { - bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + bounds: WindowBounds::Fixed(RectF::new( + vec2f(screen_size.x() - window_size.x() - PADDING, PADDING), + window_size, + )), titlebar: None, - center: true, + center: false, kind: WindowKind::PopUp, is_movable: false, }, @@ -84,22 +90,40 @@ impl IncomingCallNotification { fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { let theme = &cx.global::().theme.incoming_call_notification; Flex::row() - .with_children( - self.call - .caller - .avatar - .clone() - .map(|avatar| Image::new(avatar).with_style(theme.caller_avatar).boxed()), - ) + .with_children(self.call.caller.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.caller_avatar) + .aligned() + .boxed() + })) .with_child( - Label::new( - self.call.caller.github_login.clone(), - theme.caller_username.text.clone(), - ) - .contained() - .with_style(theme.caller_username.container) - .boxed(), + Flex::column() + .with_child( + Label::new( + self.call.caller.github_login.clone(), + theme.caller_username.text.clone(), + ) + .contained() + .with_style(theme.caller_username.container) + .boxed(), + ) + .with_child( + Label::new( + "Incoming Zed call...".into(), + theme.caller_message.text.clone(), + ) + .contained() + .with_style(theme.caller_message.container) + .boxed(), + ) + .contained() + .with_style(theme.caller_metadata) + .aligned() + .boxed(), ) + .contained() + .with_style(theme.caller_container) + .flex(1., true) .boxed() } @@ -107,33 +131,46 @@ impl IncomingCallNotification { enum Accept {} enum Decline {} - Flex::row() + Flex::column() .with_child( MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.incoming_call_notification; Label::new("Accept".to_string(), theme.accept_button.text.clone()) + .aligned() .contained() .with_style(theme.accept_button.container) .boxed() }) + .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(RespondToCall { accept: true }); }) + .flex(1., true) .boxed(), ) .with_child( MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.incoming_call_notification; Label::new("Decline".to_string(), theme.decline_button.text.clone()) + .aligned() .contained() .with_style(theme.decline_button.container) .boxed() }) + .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(RespondToCall { accept: false }); }) + .flex(1., true) .boxed(), ) + .constrained() + .with_width( + cx.global::() + .theme + .incoming_call_notification + .button_width, + ) .boxed() } } @@ -148,9 +185,17 @@ impl View for IncomingCallNotification { } fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { - Flex::column() + let background = cx + .global::() + .theme + .incoming_call_notification + .background; + Flex::row() .with_child(self.render_caller(cx)) .with_child(self.render_buttons(cx)) + .contained() + .with_background_color(background) + .expanded() .boxed() } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index a50698070c..a0e8cedfe9 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -44,6 +44,8 @@ pub trait Platform: Send + Sync { fn unhide_other_apps(&self); fn quit(&self); + fn screen_size(&self) -> Vector2F; + fn open_window( &self, id: usize, diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 7732da2b3e..cf2aeff466 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -2,7 +2,9 @@ use super::{ event::key_to_native, status_item::StatusItem, BoolExt as _, Dispatcher, FontSystem, Window, }; use crate::{ - executor, keymap, + executor, + geometry::vector::{vec2f, Vector2F}, + keymap, platform::{self, CursorStyle}, Action, ClipboardItem, Event, Menu, MenuItem, }; @@ -12,7 +14,7 @@ use cocoa::{ appkit::{ NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard, - NSPasteboardTypeString, NSSavePanel, NSWindow, + NSPasteboardTypeString, NSSavePanel, NSScreen, NSWindow, }, base::{id, nil, selector, YES}, foundation::{ @@ -485,6 +487,14 @@ impl platform::Platform for MacPlatform { } } + fn screen_size(&self) -> Vector2F { + unsafe { + let screen = NSScreen::mainScreen(nil); + let frame = NSScreen::frame(screen); + vec2f(frame.size.width as f32, frame.size.height as f32) + } + } + fn open_window( &self, id: usize, diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 9a458a1dd9..613a2117b9 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -131,6 +131,10 @@ impl super::Platform for Platform { fn quit(&self) {} + fn screen_size(&self) -> Vector2F { + vec2f(1024., 768.) + } + fn open_window( &self, _: usize, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d69720322e..3f9b64ce56 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -479,8 +479,14 @@ pub struct ProjectSharedNotification { #[derive(Deserialize, Default)] pub struct IncomingCallNotification { + #[serde(default)] + pub background: Color, + pub caller_container: ContainerStyle, pub caller_avatar: ImageStyle, + pub caller_metadata: ContainerStyle, pub caller_username: ContainedText, + pub caller_message: ContainedText, + pub button_width: f32, pub accept_button: ContainedText, pub decline_button: ContainedText, } diff --git a/styles/src/styleTree/incomingCallNotification.ts b/styles/src/styleTree/incomingCallNotification.ts index dcae47bff0..d8ea7dbad9 100644 --- a/styles/src/styleTree/incomingCallNotification.ts +++ b/styles/src/styleTree/incomingCallNotification.ts @@ -1,22 +1,38 @@ import Theme from "../themes/common/theme"; -import { text } from "./components"; +import { backgroundColor, borderColor, text } from "./components"; export default function incomingCallNotification(theme: Theme): Object { - const avatarSize = 12; + const avatarSize = 32; return { + background: backgroundColor(theme, 300), + callerContainer: { + padding: 12, + }, callerAvatar: { height: avatarSize, width: avatarSize, - cornerRadius: 6, + cornerRadius: avatarSize / 2, + }, + callerMetadata: { + margin: { left: 10 }, }, callerUsername: { - ...text(theme, "sans", "primary", { size: "xs" }), + ...text(theme, "sans", "active", { size: "sm", weight: "bold" }), + margin: { top: -3 }, }, + callerMessage: { + ...text(theme, "sans", "secondary", { size: "xs" }), + margin: { top: -3 }, + }, + buttonWidth: 96, acceptButton: { - ...text(theme, "sans", "primary", { size: "xs" }) + background: backgroundColor(theme, "ok", "active"), + border: { left: true, bottom: true, width: 1, color: borderColor(theme, "primary") }, + ...text(theme, "sans", "ok", { size: "xs", weight: "extra_bold" }) }, declineButton: { - ...text(theme, "sans", "primary", { size: "xs" }) + border: { left: true, width: 1, color: borderColor(theme, "primary") }, + ...text(theme, "sans", "error", { size: "xs", weight: "extra_bold" }) }, }; }