mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 20:01:33 +00:00
Toggle contacts popover when clicking on status bar icon
This commit is contained in:
parent
9b8492a3ba
commit
d10f6f60ad
9 changed files with 208 additions and 44 deletions
26
crates/contacts_status_item/src/contacts_popover.rs
Normal file
26
crates/contacts_status_item/src/contacts_popover.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
type Event = ();
|
||||
|
@ -16,15 +34,46 @@ impl View for ContactsStatusItem {
|
|||
Appearance::Light | Appearance::VibrantLight => Color::black(),
|
||||
Appearance::Dark | Appearance::VibrantDark => Color::white(),
|
||||
};
|
||||
Svg::new("icons/zed_22.svg")
|
||||
.with_color(color)
|
||||
.aligned()
|
||||
.boxed()
|
||||
MouseEventHandler::new::<Self, _, _>(0, cx, |_, _| {
|
||||
Svg::new("icons/zed_22.svg")
|
||||
.with_color(color)
|
||||
.aligned()
|
||||
.boxed()
|
||||
})
|
||||
.on_click(MouseButton::Left, |_, cx| {
|
||||
cx.dispatch_action(ToggleContactsPopover);
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl ContactsStatusItem {
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1244,6 +1244,10 @@ impl MutableAppContext {
|
|||
.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> {
|
||||
let window_id = params.window_id;
|
||||
let view_id = params.view_id;
|
||||
|
@ -2032,10 +2036,12 @@ impl MutableAppContext {
|
|||
window_id,
|
||||
}));
|
||||
|
||||
let scene =
|
||||
presenter
|
||||
.borrow_mut()
|
||||
.build_scene(window.size(), window.scale_factor(), false, self);
|
||||
let scene = presenter.borrow_mut().build_scene(
|
||||
window.content_size(),
|
||||
window.scale_factor(),
|
||||
false,
|
||||
self,
|
||||
);
|
||||
window.present_scene(scene);
|
||||
self.presenters_and_platform_windows
|
||||
.insert(window_id, (presenter.clone(), window));
|
||||
|
@ -2411,8 +2417,12 @@ impl MutableAppContext {
|
|||
{
|
||||
let mut presenter = presenter.borrow_mut();
|
||||
presenter.invalidate(&mut invalidation, window.appearance(), self);
|
||||
let scene =
|
||||
presenter.build_scene(window.size(), window.scale_factor(), false, self);
|
||||
let scene = presenter.build_scene(
|
||||
window.content_size(),
|
||||
window.scale_factor(),
|
||||
false,
|
||||
self,
|
||||
);
|
||||
window.present_scene(scene);
|
||||
}
|
||||
self.presenters_and_platform_windows
|
||||
|
@ -2477,7 +2487,8 @@ impl MutableAppContext {
|
|||
window.appearance(),
|
||||
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);
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn window_bounds(&self) -> RectF {
|
||||
self.app.window_bounds(self.window_id)
|
||||
}
|
||||
|
||||
pub fn prompt(
|
||||
&self,
|
||||
level: PromptLevel,
|
||||
|
|
|
@ -128,7 +128,8 @@ pub trait Window {
|
|||
fn zoom(&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 titlebar_height(&self) -> f32;
|
||||
fn present_scene(&mut self, scene: Scene);
|
||||
|
@ -140,6 +141,7 @@ pub trait Window {
|
|||
pub struct WindowOptions<'a> {
|
||||
pub bounds: WindowBounds,
|
||||
pub titlebar: Option<TitlebarOptions<'a>>,
|
||||
pub center: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -273,6 +275,7 @@ impl<'a> Default for WindowOptions<'a> {
|
|||
appears_transparent: Default::default(),
|
||||
traffic_light_position: Default::default(),
|
||||
}),
|
||||
center: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use crate::{
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
platform::{
|
||||
self,
|
||||
mac::{
|
||||
|
@ -10,7 +13,7 @@ use crate::{
|
|||
Event, FontSystem, Scene,
|
||||
};
|
||||
use cocoa::{
|
||||
appkit::{NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},
|
||||
appkit::{NSScreen, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},
|
||||
base::{id, nil, YES},
|
||||
foundation::{NSPoint, NSRect, NSSize, NSString},
|
||||
};
|
||||
|
@ -163,7 +166,7 @@ impl StatusItem {
|
|||
let state = state.borrow();
|
||||
let layer = state.renderer.layer();
|
||||
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_drawable_size(metal::CGSize::new(size.x().into(), size.y().into()));
|
||||
}
|
||||
|
@ -235,8 +238,12 @@ impl platform::Window for StatusItem {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn size(&self) -> Vector2F {
|
||||
self.0.borrow().size()
|
||||
fn bounds(&self) -> RectF {
|
||||
self.0.borrow().bounds()
|
||||
}
|
||||
|
||||
fn content_size(&self) -> Vector2F {
|
||||
self.0.borrow().content_size()
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f32 {
|
||||
|
@ -264,10 +271,28 @@ impl platform::Window for StatusItem {
|
|||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +300,7 @@ impl StatusItemState {
|
|||
fn scale_factor(&self) -> f32 {
|
||||
unsafe {
|
||||
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 {
|
||||
if let Some(state) = get_state(this).upgrade() {
|
||||
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() {
|
||||
drop(state_borrow);
|
||||
callback(event);
|
||||
|
|
|
@ -335,12 +335,6 @@ impl Window {
|
|||
unsafe {
|
||||
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;
|
||||
if let Some(titlebar) = options.titlebar.as_ref() {
|
||||
style_mask = NSWindowStyleMask::NSClosableWindowMask
|
||||
|
@ -357,16 +351,31 @@ impl Window {
|
|||
|
||||
let native_window: id = msg_send![WINDOW_CLASS, alloc];
|
||||
let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
|
||||
frame,
|
||||
RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
|
||||
style_mask,
|
||||
NSBackingStoreBuffered,
|
||||
NO,
|
||||
);
|
||||
assert!(!native_window.is_null());
|
||||
|
||||
if matches!(options.bounds, WindowBounds::Maximized) {
|
||||
let screen = native_window.screen();
|
||||
native_window.setFrame_display_(screen.visibleFrame(), YES);
|
||||
let screen = native_window.screen();
|
||||
match options.bounds {
|
||||
WindowBounds::Maximized => {
|
||||
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];
|
||||
|
@ -438,7 +447,10 @@ impl Window {
|
|||
native_window.setContentView_(native_view.autorelease());
|
||||
native_window.makeFirstResponder_(native_view);
|
||||
|
||||
native_window.center();
|
||||
if options.center {
|
||||
native_window.center();
|
||||
}
|
||||
|
||||
native_window.makeKeyAndOrderFront_(nil);
|
||||
let _: () = msg_send![
|
||||
native_window,
|
||||
|
@ -633,8 +645,12 @@ impl platform::Window for Window {
|
|||
.detach();
|
||||
}
|
||||
|
||||
fn size(&self) -> Vector2F {
|
||||
self.0.as_ref().borrow().size()
|
||||
fn bounds(&self) -> RectF {
|
||||
self.0.as_ref().borrow().bounds()
|
||||
}
|
||||
|
||||
fn content_size(&self) -> Vector2F {
|
||||
self.0.as_ref().borrow().content_size()
|
||||
}
|
||||
|
||||
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, .. } =
|
||||
unsafe { NSView::frame(self.native_window.contentView()) }.size;
|
||||
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 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 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 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 {
|
||||
match &event {
|
||||
Event::MouseMoved(
|
||||
|
@ -1060,7 +1095,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
|
|||
|
||||
unsafe {
|
||||
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 {
|
||||
width: size.x() 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_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use super::{AppVersion, CursorStyle, WindowBounds};
|
||||
use crate::{
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
keymap, Action, ClipboardItem,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
@ -283,7 +286,11 @@ impl super::Window for Window {
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ fn main() {
|
|||
watch_settings_file(default_settings, settings_file, themes.clone(), cx);
|
||||
watch_keymap_file(keymap_file, cx);
|
||||
|
||||
contacts_status_item::init(cx);
|
||||
context_menu::init(cx);
|
||||
project::Project::init(&client);
|
||||
client::Channel::init(&client);
|
||||
|
|
|
@ -335,6 +335,7 @@ pub fn build_window_options() -> WindowOptions<'static> {
|
|||
appears_transparent: true,
|
||||
traffic_light_position: Some(vec2f(8., 8.)),
|
||||
}),
|
||||
center: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue