Toggle contacts popover when clicking on status bar icon

This commit is contained in:
Antonio Scandurra 2022-09-14 15:43:51 +02:00
parent 9b8492a3ba
commit d10f6f60ad
9 changed files with 208 additions and 44 deletions

View file

@ -0,0 +1,26 @@
use gpui::{color::Color, elements::*, Entity, RenderContext, View};
pub struct ContactsPopover;
impl Entity for ContactsPopover {
type Event = ();
}
impl View for ContactsPopover {
fn ui_name() -> &'static str {
"ContactsPopover"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
Empty::new()
.contained()
.with_background_color(Color::red())
.boxed()
}
}
impl ContactsPopover {
pub fn new() -> Self {
Self
}
}

View file

@ -1,6 +1,24 @@
use gpui::{color::Color, elements::*, Appearance, Entity, RenderContext, View}; mod contacts_popover;
pub struct ContactsStatusItem; use contacts_popover::ContactsPopover;
use gpui::{
actions,
color::Color,
elements::*,
geometry::{rect::RectF, vector::vec2f},
Appearance, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext,
ViewHandle,
};
actions!(contacts_status_item, [ToggleContactsPopover]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ContactsStatusItem::toggle_contacts_popover);
}
pub struct ContactsStatusItem {
popover: Option<ViewHandle<ContactsPopover>>,
}
impl Entity for ContactsStatusItem { impl Entity for ContactsStatusItem {
type Event = (); type Event = ();
@ -16,15 +34,46 @@ impl View for ContactsStatusItem {
Appearance::Light | Appearance::VibrantLight => Color::black(), Appearance::Light | Appearance::VibrantLight => Color::black(),
Appearance::Dark | Appearance::VibrantDark => Color::white(), Appearance::Dark | Appearance::VibrantDark => Color::white(),
}; };
MouseEventHandler::new::<Self, _, _>(0, cx, |_, _| {
Svg::new("icons/zed_22.svg") Svg::new("icons/zed_22.svg")
.with_color(color) .with_color(color)
.aligned() .aligned()
.boxed() .boxed()
})
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(ToggleContactsPopover);
})
.boxed()
} }
} }
impl ContactsStatusItem { impl ContactsStatusItem {
pub fn new() -> Self { pub fn new() -> Self {
Self Self { popover: None }
}
fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext<Self>) {
match self.popover.take() {
Some(popover) => {
cx.remove_window(popover.window_id());
}
None => {
let window_bounds = cx.window_bounds();
let size = vec2f(360., 460.);
let origin = window_bounds.lower_left()
+ vec2f(window_bounds.width() / 2. - size.x() / 2., 0.);
self.popover = Some(
cx.add_window(
gpui::WindowOptions {
bounds: gpui::WindowBounds::Fixed(RectF::new(origin, size)),
titlebar: None,
center: false,
},
|_| ContactsPopover::new(),
)
.1,
);
}
}
} }
} }

View file

@ -1244,6 +1244,10 @@ impl MutableAppContext {
.map_or(false, |window| window.is_fullscreen) .map_or(false, |window| window.is_fullscreen)
} }
pub fn window_bounds(&self, window_id: usize) -> RectF {
self.presenters_and_platform_windows[&window_id].1.bounds()
}
pub fn render_view(&mut self, params: RenderParams) -> Result<ElementBox> { pub fn render_view(&mut self, params: RenderParams) -> Result<ElementBox> {
let window_id = params.window_id; let window_id = params.window_id;
let view_id = params.view_id; let view_id = params.view_id;
@ -2032,10 +2036,12 @@ impl MutableAppContext {
window_id, window_id,
})); }));
let scene = let scene = presenter.borrow_mut().build_scene(
presenter window.content_size(),
.borrow_mut() window.scale_factor(),
.build_scene(window.size(), window.scale_factor(), false, self); false,
self,
);
window.present_scene(scene); window.present_scene(scene);
self.presenters_and_platform_windows self.presenters_and_platform_windows
.insert(window_id, (presenter.clone(), window)); .insert(window_id, (presenter.clone(), window));
@ -2411,8 +2417,12 @@ impl MutableAppContext {
{ {
let mut presenter = presenter.borrow_mut(); let mut presenter = presenter.borrow_mut();
presenter.invalidate(&mut invalidation, window.appearance(), self); presenter.invalidate(&mut invalidation, window.appearance(), self);
let scene = let scene = presenter.build_scene(
presenter.build_scene(window.size(), window.scale_factor(), false, self); window.content_size(),
window.scale_factor(),
false,
self,
);
window.present_scene(scene); window.present_scene(scene);
} }
self.presenters_and_platform_windows self.presenters_and_platform_windows
@ -2477,7 +2487,8 @@ impl MutableAppContext {
window.appearance(), window.appearance(),
self, self,
); );
let scene = presenter.build_scene(window.size(), window.scale_factor(), true, self); let scene =
presenter.build_scene(window.content_size(), window.scale_factor(), true, self);
window.present_scene(scene); window.present_scene(scene);
} }
self.presenters_and_platform_windows = presenters; self.presenters_and_platform_windows = presenters;
@ -3749,6 +3760,10 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.toggle_window_full_screen(self.window_id) self.app.toggle_window_full_screen(self.window_id)
} }
pub fn window_bounds(&self) -> RectF {
self.app.window_bounds(self.window_id)
}
pub fn prompt( pub fn prompt(
&self, &self,
level: PromptLevel, level: PromptLevel,

View file

@ -128,7 +128,8 @@ pub trait Window {
fn zoom(&self); fn zoom(&self);
fn toggle_full_screen(&self); fn toggle_full_screen(&self);
fn size(&self) -> Vector2F; fn bounds(&self) -> RectF;
fn content_size(&self) -> Vector2F;
fn scale_factor(&self) -> f32; fn scale_factor(&self) -> f32;
fn titlebar_height(&self) -> f32; fn titlebar_height(&self) -> f32;
fn present_scene(&mut self, scene: Scene); fn present_scene(&mut self, scene: Scene);
@ -140,6 +141,7 @@ pub trait Window {
pub struct WindowOptions<'a> { pub struct WindowOptions<'a> {
pub bounds: WindowBounds, pub bounds: WindowBounds,
pub titlebar: Option<TitlebarOptions<'a>>, pub titlebar: Option<TitlebarOptions<'a>>,
pub center: bool,
} }
#[derive(Debug)] #[derive(Debug)]
@ -273,6 +275,7 @@ impl<'a> Default for WindowOptions<'a> {
appears_transparent: Default::default(), appears_transparent: Default::default(),
traffic_light_position: Default::default(), traffic_light_position: Default::default(),
}), }),
center: false,
} }
} }
} }

View file

@ -1,5 +1,8 @@
use crate::{ use crate::{
geometry::vector::{vec2f, Vector2F}, geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
platform::{ platform::{
self, self,
mac::{ mac::{
@ -10,7 +13,7 @@ use crate::{
Event, FontSystem, Scene, Event, FontSystem, Scene,
}; };
use cocoa::{ use cocoa::{
appkit::{NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow}, appkit::{NSScreen, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},
base::{id, nil, YES}, base::{id, nil, YES},
foundation::{NSPoint, NSRect, NSSize, NSString}, foundation::{NSPoint, NSRect, NSSize, NSString},
}; };
@ -163,7 +166,7 @@ impl StatusItem {
let state = state.borrow(); let state = state.borrow();
let layer = state.renderer.layer(); let layer = state.renderer.layer();
let scale_factor = state.scale_factor(); let scale_factor = state.scale_factor();
let size = state.size() * scale_factor; let size = state.content_size() * scale_factor;
layer.set_contents_scale(scale_factor.into()); layer.set_contents_scale(scale_factor.into());
layer.set_drawable_size(metal::CGSize::new(size.x().into(), size.y().into())); layer.set_drawable_size(metal::CGSize::new(size.x().into(), size.y().into()));
} }
@ -235,8 +238,12 @@ impl platform::Window for StatusItem {
unimplemented!() unimplemented!()
} }
fn size(&self) -> Vector2F { fn bounds(&self) -> RectF {
self.0.borrow().size() self.0.borrow().bounds()
}
fn content_size(&self) -> Vector2F {
self.0.borrow().content_size()
} }
fn scale_factor(&self) -> f32 { fn scale_factor(&self) -> f32 {
@ -264,10 +271,28 @@ impl platform::Window for StatusItem {
} }
impl StatusItemState { impl StatusItemState {
fn size(&self) -> Vector2F { fn bounds(&self) -> RectF {
unsafe {
let window: id = msg_send![self.native_item.button(), window];
let screen_frame = window.screen().visibleFrame();
let window_frame = NSWindow::frame(window);
let origin = vec2f(
window_frame.origin.x as f32,
(window_frame.origin.y - screen_frame.size.height - window_frame.size.height)
as f32,
);
let size = vec2f(
window_frame.size.width as f32,
window_frame.size.height as f32,
);
RectF::new(origin, size)
}
}
fn content_size(&self) -> Vector2F {
unsafe { unsafe {
let NSSize { width, height, .. } = let NSSize { width, height, .. } =
NSWindow::frame(self.native_item.button().superview().superview()).size; NSView::frame(self.native_item.button().superview().superview()).size;
vec2f(width as f32, height as f32) vec2f(width as f32, height as f32)
} }
} }
@ -275,7 +300,7 @@ impl StatusItemState {
fn scale_factor(&self) -> f32 { fn scale_factor(&self) -> f32 {
unsafe { unsafe {
let window: id = msg_send![self.native_item.button(), window]; let window: id = msg_send![self.native_item.button(), window];
window.screen().backingScaleFactor() as f32 NSScreen::backingScaleFactor(window.screen()) as f32
} }
} }
} }
@ -292,7 +317,9 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
unsafe { unsafe {
if let Some(state) = get_state(this).upgrade() { if let Some(state) = get_state(this).upgrade() {
let mut state_borrow = state.as_ref().borrow_mut(); let mut state_borrow = state.as_ref().borrow_mut();
if let Some(event) = Event::from_native(native_event, Some(state_borrow.size().y())) { if let Some(event) =
Event::from_native(native_event, Some(state_borrow.content_size().y()))
{
if let Some(mut callback) = state_borrow.event_callback.take() { if let Some(mut callback) = state_borrow.event_callback.take() {
drop(state_borrow); drop(state_borrow);
callback(event); callback(event);

View file

@ -335,12 +335,6 @@ impl Window {
unsafe { unsafe {
let pool = NSAutoreleasePool::new(nil); let pool = NSAutoreleasePool::new(nil);
let frame = match options.bounds {
WindowBounds::Maximized => RectF::new(Default::default(), vec2f(1024., 768.)),
WindowBounds::Fixed(rect) => rect,
}
.to_ns_rect();
let mut style_mask; let mut style_mask;
if let Some(titlebar) = options.titlebar.as_ref() { if let Some(titlebar) = options.titlebar.as_ref() {
style_mask = NSWindowStyleMask::NSClosableWindowMask style_mask = NSWindowStyleMask::NSClosableWindowMask
@ -357,17 +351,32 @@ impl Window {
let native_window: id = msg_send![WINDOW_CLASS, alloc]; let native_window: id = msg_send![WINDOW_CLASS, alloc];
let native_window = native_window.initWithContentRect_styleMask_backing_defer_( let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
frame, RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
style_mask, style_mask,
NSBackingStoreBuffered, NSBackingStoreBuffered,
NO, NO,
); );
assert!(!native_window.is_null()); assert!(!native_window.is_null());
if matches!(options.bounds, WindowBounds::Maximized) {
let screen = native_window.screen(); let screen = native_window.screen();
match options.bounds {
WindowBounds::Maximized => {
native_window.setFrame_display_(screen.visibleFrame(), YES); native_window.setFrame_display_(screen.visibleFrame(), YES);
} }
WindowBounds::Fixed(top_left_bounds) => {
let frame = screen.visibleFrame();
let bottom_left_bounds = RectF::new(
vec2f(
top_left_bounds.origin_x(),
frame.size.height as f32
- top_left_bounds.origin_y()
- top_left_bounds.height(),
),
top_left_bounds.size(),
);
native_window.setFrame_display_(bottom_left_bounds.to_ns_rect(), YES);
}
}
let native_view: id = msg_send![VIEW_CLASS, alloc]; let native_view: id = msg_send![VIEW_CLASS, alloc];
let native_view = NSView::init(native_view); let native_view = NSView::init(native_view);
@ -438,7 +447,10 @@ impl Window {
native_window.setContentView_(native_view.autorelease()); native_window.setContentView_(native_view.autorelease());
native_window.makeFirstResponder_(native_view); native_window.makeFirstResponder_(native_view);
if options.center {
native_window.center(); native_window.center();
}
native_window.makeKeyAndOrderFront_(nil); native_window.makeKeyAndOrderFront_(nil);
let _: () = msg_send![ let _: () = msg_send![
native_window, native_window,
@ -633,8 +645,12 @@ impl platform::Window for Window {
.detach(); .detach();
} }
fn size(&self) -> Vector2F { fn bounds(&self) -> RectF {
self.0.as_ref().borrow().size() self.0.as_ref().borrow().bounds()
}
fn content_size(&self) -> Vector2F {
self.0.as_ref().borrow().content_size()
} }
fn scale_factor(&self) -> f32 { fn scale_factor(&self) -> f32 {
@ -706,7 +722,24 @@ impl WindowState {
} }
} }
fn size(&self) -> Vector2F { fn bounds(&self) -> RectF {
unsafe {
let screen_frame = self.native_window.screen().visibleFrame();
let window_frame = NSWindow::frame(self.native_window);
let origin = vec2f(
window_frame.origin.x as f32,
(window_frame.origin.y - screen_frame.size.height - window_frame.size.height)
as f32,
);
let size = vec2f(
window_frame.size.width as f32,
window_frame.size.height as f32,
);
RectF::new(origin, size)
}
}
fn content_size(&self) -> Vector2F {
let NSSize { width, height, .. } = let NSSize { width, height, .. } =
unsafe { NSView::frame(self.native_window.contentView()) }.size; unsafe { NSView::frame(self.native_window.contentView()) }.size;
vec2f(width as f32, height as f32) vec2f(width as f32, height as f32)
@ -783,7 +816,8 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
let mut window_state_borrow = window_state.as_ref().borrow_mut(); let mut window_state_borrow = window_state.as_ref().borrow_mut();
let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) }; let event =
unsafe { Event::from_native(native_event, Some(window_state_borrow.content_size().y())) };
if let Some(event) = event { if let Some(event) = event {
if key_equivalent { if key_equivalent {
@ -875,7 +909,8 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
let weak_window_state = Rc::downgrade(&window_state); let weak_window_state = Rc::downgrade(&window_state);
let mut window_state_borrow = window_state.as_ref().borrow_mut(); let mut window_state_borrow = window_state.as_ref().borrow_mut();
let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) }; let event =
unsafe { Event::from_native(native_event, Some(window_state_borrow.content_size().y())) };
if let Some(event) = event { if let Some(event) = event {
match &event { match &event {
Event::MouseMoved( Event::MouseMoved(
@ -1060,7 +1095,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
unsafe { unsafe {
let scale_factor = window_state_borrow.scale_factor() as f64; let scale_factor = window_state_borrow.scale_factor() as f64;
let size = window_state_borrow.size(); let size = window_state_borrow.content_size();
let drawable_size: NSSize = NSSize { let drawable_size: NSSize = NSSize {
width: size.x() as f64 * scale_factor, width: size.x() as f64 * scale_factor,
height: size.y() as f64 * scale_factor, height: size.y() as f64 * scale_factor,
@ -1087,7 +1122,7 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
let window_state = unsafe { get_window_state(this) }; let window_state = unsafe { get_window_state(this) };
let window_state_borrow = window_state.as_ref().borrow(); let window_state_borrow = window_state.as_ref().borrow();
if window_state_borrow.size() == vec2f(size.width as f32, size.height as f32) { if window_state_borrow.content_size() == vec2f(size.width as f32, size.height as f32) {
return; return;
} }

View file

@ -1,6 +1,9 @@
use super::{AppVersion, CursorStyle, WindowBounds}; use super::{AppVersion, CursorStyle, WindowBounds};
use crate::{ use crate::{
geometry::vector::{vec2f, Vector2F}, geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
keymap, Action, ClipboardItem, keymap, Action, ClipboardItem,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -283,7 +286,11 @@ impl super::Window for Window {
fn toggle_full_screen(&self) {} fn toggle_full_screen(&self) {}
fn size(&self) -> Vector2F { fn bounds(&self) -> RectF {
RectF::new(Default::default(), self.size)
}
fn content_size(&self) -> Vector2F {
self.size self.size
} }

View file

@ -105,6 +105,7 @@ fn main() {
watch_settings_file(default_settings, settings_file, themes.clone(), cx); watch_settings_file(default_settings, settings_file, themes.clone(), cx);
watch_keymap_file(keymap_file, cx); watch_keymap_file(keymap_file, cx);
contacts_status_item::init(cx);
context_menu::init(cx); context_menu::init(cx);
project::Project::init(&client); project::Project::init(&client);
client::Channel::init(&client); client::Channel::init(&client);

View file

@ -335,6 +335,7 @@ pub fn build_window_options() -> WindowOptions<'static> {
appears_transparent: true, appears_transparent: true,
traffic_light_position: Some(vec2f(8., 8.)), traffic_light_position: Some(vec2f(8., 8.)),
}), }),
center: false,
} }
} }