Remove 2 suffix for workspace
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
492805af9c
commit
789ce8dd75
66 changed files with 4758 additions and 19021 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
135
crates/workspace/src/modal_layer.rs
Normal file
135
crates/workspace/src/modal_layer.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use gpui::{
|
||||
div, prelude::*, px, AnyView, DismissEvent, FocusHandle, ManagedView, Render, Subscription,
|
||||
View, ViewContext, WindowContext,
|
||||
};
|
||||
use ui::{h_stack, v_stack};
|
||||
|
||||
pub trait ModalView: ManagedView {
|
||||
fn on_before_dismiss(&mut self, _: &mut ViewContext<Self>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
trait ModalViewHandle {
|
||||
fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> bool;
|
||||
fn view(&self) -> AnyView;
|
||||
}
|
||||
|
||||
impl<V: ModalView> ModalViewHandle for View<V> {
|
||||
fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> bool {
|
||||
self.update(cx, |this, cx| this.on_before_dismiss(cx))
|
||||
}
|
||||
|
||||
fn view(&self) -> AnyView {
|
||||
self.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ActiveModal {
|
||||
modal: Box<dyn ModalViewHandle>,
|
||||
_subscription: Subscription,
|
||||
previous_focus_handle: Option<FocusHandle>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
pub struct ModalLayer {
|
||||
active_modal: Option<ActiveModal>,
|
||||
}
|
||||
|
||||
impl ModalLayer {
|
||||
pub fn new() -> Self {
|
||||
Self { active_modal: None }
|
||||
}
|
||||
|
||||
pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B)
|
||||
where
|
||||
V: ModalView,
|
||||
B: FnOnce(&mut ViewContext<V>) -> V,
|
||||
{
|
||||
if let Some(active_modal) = &self.active_modal {
|
||||
let is_close = active_modal.modal.view().downcast::<V>().is_ok();
|
||||
let did_close = self.hide_modal(cx);
|
||||
if is_close || !did_close {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let new_modal = cx.new_view(build_view);
|
||||
self.show_modal(new_modal, cx);
|
||||
}
|
||||
|
||||
fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
V: ModalView,
|
||||
{
|
||||
self.active_modal = Some(ActiveModal {
|
||||
modal: Box::new(new_modal.clone()),
|
||||
_subscription: cx.subscribe(&new_modal, |this, _, _: &DismissEvent, cx| {
|
||||
this.hide_modal(cx);
|
||||
}),
|
||||
previous_focus_handle: cx.focused(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
});
|
||||
cx.focus_view(&new_modal);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn hide_modal(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||
let Some(active_modal) = self.active_modal.as_mut() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let dismiss = active_modal.modal.on_before_dismiss(cx);
|
||||
if !dismiss {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(active_modal) = self.active_modal.take() {
|
||||
if let Some(previous_focus) = active_modal.previous_focus_handle {
|
||||
if active_modal.focus_handle.contains_focused(cx) {
|
||||
previous_focus.focus(cx);
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn active_modal<V>(&self) -> Option<View<V>>
|
||||
where
|
||||
V: 'static,
|
||||
{
|
||||
let active_modal = self.active_modal.as_ref()?;
|
||||
active_modal.modal.view().downcast::<V>().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ModalLayer {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let Some(active_modal) = &self.active_modal else {
|
||||
return div();
|
||||
};
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.size_full()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.z_index(169)
|
||||
.child(
|
||||
v_stack()
|
||||
.h(px(0.0))
|
||||
.top_20()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.items_center()
|
||||
.track_focus(&active_modal.focus_handle)
|
||||
.child(
|
||||
h_stack()
|
||||
.on_mouse_down_out(cx.listener(|this, _, cx| {
|
||||
this.hide_modal(cx);
|
||||
}))
|
||||
.child(active_modal.modal.view()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,35 +1,39 @@
|
|||
use crate::{Toast, Workspace};
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle};
|
||||
use gpui::{
|
||||
AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, Render,
|
||||
View, ViewContext, VisualContext,
|
||||
};
|
||||
use std::{any::TypeId, ops::DerefMut};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.set_global(NotificationTracker::new());
|
||||
simple_message_notification::init(cx);
|
||||
// todo!()
|
||||
// simple_message_notification::init(cx);
|
||||
}
|
||||
|
||||
pub trait Notification: View {
|
||||
fn should_dismiss_notification_on_event(&self, event: &<Self as Entity>::Event) -> bool;
|
||||
pub trait Notification: EventEmitter<DismissEvent> + Render {}
|
||||
|
||||
impl<V: EventEmitter<DismissEvent> + Render> Notification for V {}
|
||||
|
||||
pub trait NotificationHandle: Send {
|
||||
fn id(&self) -> EntityId;
|
||||
fn to_any(&self) -> AnyView;
|
||||
}
|
||||
|
||||
pub trait NotificationHandle {
|
||||
fn id(&self) -> usize;
|
||||
fn as_any(&self) -> &AnyViewHandle;
|
||||
}
|
||||
|
||||
impl<T: Notification> NotificationHandle for ViewHandle<T> {
|
||||
fn id(&self) -> usize {
|
||||
self.id()
|
||||
impl<T: Notification> NotificationHandle for View<T> {
|
||||
fn id(&self) -> EntityId {
|
||||
self.entity_id()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &AnyViewHandle {
|
||||
self
|
||||
fn to_any(&self) -> AnyView {
|
||||
self.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dyn NotificationHandle> for AnyViewHandle {
|
||||
impl From<&dyn NotificationHandle> for AnyView {
|
||||
fn from(val: &dyn NotificationHandle) -> Self {
|
||||
val.as_any().clone()
|
||||
val.to_any()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,14 +79,12 @@ impl Workspace {
|
|||
&mut self,
|
||||
id: usize,
|
||||
cx: &mut ViewContext<Self>,
|
||||
build_notification: impl FnOnce(&mut ViewContext<Self>) -> ViewHandle<V>,
|
||||
build_notification: impl FnOnce(&mut ViewContext<Self>) -> View<V>,
|
||||
) {
|
||||
if !self.has_shown_notification_once::<V>(id, cx) {
|
||||
cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
|
||||
let entry = tracker.entry(TypeId::of::<V>()).or_default();
|
||||
entry.push(id);
|
||||
});
|
||||
|
||||
let tracker = cx.global_mut::<NotificationTracker>();
|
||||
let entry = tracker.entry(TypeId::of::<V>()).or_default();
|
||||
entry.push(id);
|
||||
self.show_notification::<V>(id, cx, build_notification)
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +93,7 @@ impl Workspace {
|
|||
&mut self,
|
||||
id: usize,
|
||||
cx: &mut ViewContext<Self>,
|
||||
build_notification: impl FnOnce(&mut ViewContext<Self>) -> ViewHandle<V>,
|
||||
build_notification: impl FnOnce(&mut ViewContext<Self>) -> View<V>,
|
||||
) {
|
||||
let type_id = TypeId::of::<V>();
|
||||
if self
|
||||
|
@ -102,10 +104,8 @@ impl Workspace {
|
|||
})
|
||||
{
|
||||
let notification = build_notification(cx);
|
||||
cx.subscribe(¬ification, move |this, handle, event, cx| {
|
||||
if handle.read(cx).should_dismiss_notification_on_event(event) {
|
||||
this.dismiss_notification_internal(type_id, id, cx);
|
||||
}
|
||||
cx.subscribe(¬ification, move |this, _, _: &DismissEvent, cx| {
|
||||
this.dismiss_notification_internal(type_id, id, cx);
|
||||
})
|
||||
.detach();
|
||||
self.notifications
|
||||
|
@ -114,6 +114,17 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn show_error<E>(&mut self, err: &E, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
E: std::fmt::Debug,
|
||||
{
|
||||
self.show_notification(0, cx, |cx| {
|
||||
cx.new_view(|_cx| {
|
||||
simple_message_notification::MessageNotification::new(format!("Error: {err:?}"))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn dismiss_notification<V: Notification>(&mut self, id: usize, cx: &mut ViewContext<Self>) {
|
||||
let type_id = TypeId::of::<V>();
|
||||
|
||||
|
@ -123,7 +134,7 @@ impl Workspace {
|
|||
pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext<Self>) {
|
||||
self.dismiss_notification::<simple_message_notification::MessageNotification>(toast.id, cx);
|
||||
self.show_notification(toast.id, cx, |cx| {
|
||||
cx.add_view(|_cx| match toast.on_click.as_ref() {
|
||||
cx.new_view(|_cx| match toast.on_click.as_ref() {
|
||||
Some((click_msg, on_click)) => {
|
||||
let on_click = on_click.clone();
|
||||
simple_message_notification::MessageNotification::new(toast.msg.clone())
|
||||
|
@ -158,78 +169,29 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub mod simple_message_notification {
|
||||
use super::Notification;
|
||||
use crate::Workspace;
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text},
|
||||
fonts::TextStyle,
|
||||
impl_actions,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AnyElement, AppContext, Element, Entity, View, ViewContext,
|
||||
div, DismissEvent, EventEmitter, InteractiveElement, ParentElement, Render, SharedString,
|
||||
StatefulInteractiveElement, Styled, ViewContext,
|
||||
};
|
||||
use menu::Cancel;
|
||||
use serde::Deserialize;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
actions!(message_notifications, [CancelMessageNotification]);
|
||||
|
||||
#[derive(Clone, Default, Deserialize, PartialEq)]
|
||||
pub struct OsOpen(pub Cow<'static, str>);
|
||||
|
||||
impl OsOpen {
|
||||
pub fn new<I: Into<Cow<'static, str>>>(url: I) -> Self {
|
||||
OsOpen(url.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl_actions!(message_notifications, [OsOpen]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.add_action(MessageNotification::dismiss);
|
||||
cx.add_action(
|
||||
|_workspace: &mut Workspace, open_action: &OsOpen, cx: &mut ViewContext<Workspace>| {
|
||||
cx.platform().open_url(open_action.0.as_ref());
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
enum NotificationMessage {
|
||||
Text(Cow<'static, str>),
|
||||
Element(fn(TextStyle, &AppContext) -> AnyElement<MessageNotification>),
|
||||
}
|
||||
use std::sync::Arc;
|
||||
use ui::prelude::*;
|
||||
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
|
||||
|
||||
pub struct MessageNotification {
|
||||
message: NotificationMessage,
|
||||
message: SharedString,
|
||||
on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>)>>,
|
||||
click_message: Option<Cow<'static, str>>,
|
||||
click_message: Option<SharedString>,
|
||||
}
|
||||
|
||||
pub enum MessageNotificationEvent {
|
||||
Dismiss,
|
||||
}
|
||||
|
||||
impl Entity for MessageNotification {
|
||||
type Event = MessageNotificationEvent;
|
||||
}
|
||||
impl EventEmitter<DismissEvent> for MessageNotification {}
|
||||
|
||||
impl MessageNotification {
|
||||
pub fn new<S>(message: S) -> MessageNotification
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
S: Into<SharedString>,
|
||||
{
|
||||
Self {
|
||||
message: NotificationMessage::Text(message.into()),
|
||||
on_click: None,
|
||||
click_message: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_element(
|
||||
message: fn(TextStyle, &AppContext) -> AnyElement<MessageNotification>,
|
||||
) -> MessageNotification {
|
||||
Self {
|
||||
message: NotificationMessage::Element(message),
|
||||
message: message.into(),
|
||||
on_click: None,
|
||||
click_message: None,
|
||||
}
|
||||
|
@ -237,7 +199,7 @@ pub mod simple_message_notification {
|
|||
|
||||
pub fn with_click_message<S>(mut self, message: S) -> Self
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
S: Into<SharedString>,
|
||||
{
|
||||
self.click_message = Some(message.into());
|
||||
self
|
||||
|
@ -251,117 +213,139 @@ pub mod simple_message_notification {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn dismiss(&mut self, _: &CancelMessageNotification, cx: &mut ViewContext<Self>) {
|
||||
cx.emit(MessageNotificationEvent::Dismiss);
|
||||
pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
|
||||
impl View for MessageNotification {
|
||||
fn ui_name() -> &'static str {
|
||||
"MessageNotification"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> gpui::AnyElement<Self> {
|
||||
let theme = theme::current(cx).clone();
|
||||
let theme = &theme.simple_message_notification;
|
||||
|
||||
enum MessageNotificationTag {}
|
||||
|
||||
let click_message = self.click_message.clone();
|
||||
let message = match &self.message {
|
||||
NotificationMessage::Text(text) => {
|
||||
Text::new(text.to_owned(), theme.message.text.clone()).into_any()
|
||||
}
|
||||
NotificationMessage::Element(e) => e(theme.message.text.clone(), cx),
|
||||
};
|
||||
let on_click = self.on_click.clone();
|
||||
let has_click_action = on_click.is_some();
|
||||
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
message
|
||||
.contained()
|
||||
.with_style(theme.message.container)
|
||||
.aligned()
|
||||
.top()
|
||||
.left()
|
||||
.flex(1., true),
|
||||
)
|
||||
.with_child(
|
||||
MouseEventHandler::new::<Cancel, _>(0, cx, |state, _| {
|
||||
let style = theme.dismiss_button.style_for(state);
|
||||
Svg::new("icons/x.svg")
|
||||
.with_color(style.color)
|
||||
.constrained()
|
||||
.with_width(style.icon_width)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.constrained()
|
||||
.with_width(style.button_width)
|
||||
.with_height(style.button_width)
|
||||
})
|
||||
.with_padding(Padding::uniform(5.))
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
this.dismiss(&Default::default(), cx);
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.aligned()
|
||||
.constrained()
|
||||
.with_height(cx.font_cache().line_height(theme.message.text.font_size))
|
||||
.aligned()
|
||||
.top()
|
||||
.flex_float(),
|
||||
impl Render for MessageNotification {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
v_stack()
|
||||
.elevation_3(cx)
|
||||
.p_4()
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.child(div().max_w_80().child(Label::new(self.message.clone())))
|
||||
.child(
|
||||
div()
|
||||
.id("cancel")
|
||||
.child(IconElement::new(Icon::Close))
|
||||
.cursor_pointer()
|
||||
.on_click(cx.listener(|this, _, cx| this.dismiss(cx))),
|
||||
),
|
||||
)
|
||||
.with_children({
|
||||
click_message
|
||||
.map(|click_message| {
|
||||
MouseEventHandler::new::<MessageNotificationTag, _>(
|
||||
0,
|
||||
cx,
|
||||
|state, _| {
|
||||
let style = theme.action_message.style_for(state);
|
||||
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Text::new(click_message, style.text.clone())
|
||||
.contained()
|
||||
.with_style(style.container),
|
||||
)
|
||||
.contained()
|
||||
},
|
||||
)
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
if let Some(on_click) = on_click.as_ref() {
|
||||
on_click(cx);
|
||||
this.dismiss(&Default::default(), cx);
|
||||
}
|
||||
})
|
||||
// Since we're not using a proper overlay, we have to capture these extra events
|
||||
.on_down(MouseButton::Left, |_, _, _| {})
|
||||
.on_up(MouseButton::Left, |_, _, _| {})
|
||||
.with_cursor_style(if has_click_action {
|
||||
CursorStyle::PointingHand
|
||||
} else {
|
||||
CursorStyle::Arrow
|
||||
})
|
||||
})
|
||||
.into_iter()
|
||||
})
|
||||
.into_any()
|
||||
.children(self.click_message.iter().map(|message| {
|
||||
Button::new(message.clone(), message.clone()).on_click(cx.listener(
|
||||
|this, _, cx| {
|
||||
if let Some(on_click) = this.on_click.as_ref() {
|
||||
(on_click)(cx)
|
||||
};
|
||||
this.dismiss(cx)
|
||||
},
|
||||
))
|
||||
}))
|
||||
}
|
||||
}
|
||||
// todo!()
|
||||
// impl View for MessageNotification {
|
||||
// fn ui_name() -> &'static str {
|
||||
// "MessageNotification"
|
||||
// }
|
||||
|
||||
impl Notification for MessageNotification {
|
||||
fn should_dismiss_notification_on_event(&self, event: &<Self as Entity>::Event) -> bool {
|
||||
match event {
|
||||
MessageNotificationEvent::Dismiss => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
// fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> gpui::AnyElement<Self> {
|
||||
// let theme = theme2::current(cx).clone();
|
||||
// let theme = &theme.simple_message_notification;
|
||||
|
||||
// enum MessageNotificationTag {}
|
||||
|
||||
// let click_message = self.click_message.clone();
|
||||
// let message = match &self.message {
|
||||
// NotificationMessage::Text(text) => {
|
||||
// Text::new(text.to_owned(), theme.message.text.clone()).into_any()
|
||||
// }
|
||||
// NotificationMessage::Element(e) => e(theme.message.text.clone(), cx),
|
||||
// };
|
||||
// let on_click = self.on_click.clone();
|
||||
// let has_click_action = on_click.is_some();
|
||||
|
||||
// Flex::column()
|
||||
// .with_child(
|
||||
// Flex::row()
|
||||
// .with_child(
|
||||
// message
|
||||
// .contained()
|
||||
// .with_style(theme.message.container)
|
||||
// .aligned()
|
||||
// .top()
|
||||
// .left()
|
||||
// .flex(1., true),
|
||||
// )
|
||||
// .with_child(
|
||||
// MouseEventHandler::new::<Cancel, _>(0, cx, |state, _| {
|
||||
// let style = theme.dismiss_button.style_for(state);
|
||||
// Svg::new("icons/x.svg")
|
||||
// .with_color(style.color)
|
||||
// .constrained()
|
||||
// .with_width(style.icon_width)
|
||||
// .aligned()
|
||||
// .contained()
|
||||
// .with_style(style.container)
|
||||
// .constrained()
|
||||
// .with_width(style.button_width)
|
||||
// .with_height(style.button_width)
|
||||
// })
|
||||
// .with_padding(Padding::uniform(5.))
|
||||
// .on_click(MouseButton::Left, move |_, this, cx| {
|
||||
// this.dismiss(&Default::default(), cx);
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .aligned()
|
||||
// .constrained()
|
||||
// .with_height(cx.font_cache().line_height(theme.message.text.font_size))
|
||||
// .aligned()
|
||||
// .top()
|
||||
// .flex_float(),
|
||||
// ),
|
||||
// )
|
||||
// .with_children({
|
||||
// click_message
|
||||
// .map(|click_message| {
|
||||
// MouseEventHandler::new::<MessageNotificationTag, _>(
|
||||
// 0,
|
||||
// cx,
|
||||
// |state, _| {
|
||||
// let style = theme.action_message.style_for(state);
|
||||
|
||||
// Flex::row()
|
||||
// .with_child(
|
||||
// Text::new(click_message, style.text.clone())
|
||||
// .contained()
|
||||
// .with_style(style.container),
|
||||
// )
|
||||
// .contained()
|
||||
// },
|
||||
// )
|
||||
// .on_click(MouseButton::Left, move |_, this, cx| {
|
||||
// if let Some(on_click) = on_click.as_ref() {
|
||||
// on_click(cx);
|
||||
// this.dismiss(&Default::default(), cx);
|
||||
// }
|
||||
// })
|
||||
// // Since we're not using a proper overlay, we have to capture these extra events
|
||||
// .on_down(MouseButton::Left, |_, _, _| {})
|
||||
// .on_up(MouseButton::Left, |_, _, _| {})
|
||||
// .with_cursor_style(if has_click_action {
|
||||
// CursorStyle::PointingHand
|
||||
// } else {
|
||||
// CursorStyle::Arrow
|
||||
// })
|
||||
// })
|
||||
// .into_iter()
|
||||
// })
|
||||
// .into_any()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub trait NotifyResultExt {
|
||||
|
@ -372,6 +356,8 @@ pub trait NotifyResultExt {
|
|||
workspace: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<Self::Ok>;
|
||||
|
||||
fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<Self::Ok>;
|
||||
}
|
||||
|
||||
impl<T, E> NotifyResultExt for Result<T, E>
|
||||
|
@ -384,15 +370,24 @@ where
|
|||
match self {
|
||||
Ok(value) => Some(value),
|
||||
Err(err) => {
|
||||
workspace.show_notification(0, cx, |cx| {
|
||||
cx.add_view(|_cx| {
|
||||
simple_message_notification::MessageNotification::new(format!(
|
||||
"Error: {:?}",
|
||||
err,
|
||||
))
|
||||
})
|
||||
});
|
||||
log::error!("TODO {err:?}");
|
||||
workspace.show_error(&err, cx);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<T> {
|
||||
match self {
|
||||
Ok(value) => Some(value),
|
||||
Err(err) => {
|
||||
log::error!("TODO {err:?}");
|
||||
cx.update(|view, cx| {
|
||||
if let Ok(workspace) = view.downcast::<Workspace>() {
|
||||
workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,15 +1,14 @@
|
|||
use super::DraggedItem;
|
||||
use crate::{Pane, SplitDirection, Workspace};
|
||||
use drag_and_drop::DragAndDrop;
|
||||
use gpui::{
|
||||
color::Color,
|
||||
elements::{Canvas, MouseEventHandler, ParentElement, Stack},
|
||||
elements::{Canvas, MouseEventHandler, ParentComponent, Stack},
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
platform::MouseButton,
|
||||
scene::MouseUp,
|
||||
AppContext, Element, EventContext, MouseState, Quad, ViewContext, WeakViewHandle,
|
||||
};
|
||||
use project::ProjectEntryId;
|
||||
use project2::ProjectEntryId;
|
||||
|
||||
pub fn dragged_item_receiver<Tag, D, F>(
|
||||
pane: &Pane,
|
||||
|
@ -236,5 +235,5 @@ fn drop_split_direction(
|
|||
}
|
||||
|
||||
fn overlay_color(cx: &AppContext) -> Color {
|
||||
theme::current(cx).workspace.drop_target_overlay_color
|
||||
theme2::current(cx).workspace.drop_target_overlay_color
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
#![allow(dead_code)]
|
||||
//#![allow(dead_code)]
|
||||
|
||||
pub mod model;
|
||||
|
||||
|
@ -6,7 +6,7 @@ use std::path::Path;
|
|||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
|
||||
use gpui::{platform::WindowBounds, Axis};
|
||||
use gpui::{Axis, WindowBounds};
|
||||
|
||||
use util::{unzip_option, ResultExt};
|
||||
use uuid::Uuid;
|
||||
|
@ -403,7 +403,7 @@ impl WorkspaceDb {
|
|||
.map(|(group_id, axis, pane_id, active, flexes)| {
|
||||
if let Some((group_id, axis)) = group_id.zip(axis) {
|
||||
let flexes = flexes
|
||||
.map(|flexes| serde_json::from_str::<Vec<f32>>(&flexes))
|
||||
.map(|flexes: String| serde_json::from_str::<Vec<f32>>(&flexes))
|
||||
.transpose()?;
|
||||
|
||||
Ok(SerializedPaneGroup::Group {
|
||||
|
@ -553,6 +553,7 @@ impl WorkspaceDb {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use db::open_test_db;
|
||||
use gpui;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_next_id_stability() {
|
||||
|
@ -612,13 +613,13 @@ mod tests {
|
|||
conn.migrate(
|
||||
"test_table",
|
||||
&[sql!(
|
||||
CREATE TABLE test_table(
|
||||
text TEXT,
|
||||
workspace_id INTEGER,
|
||||
FOREIGN KEY(workspace_id)
|
||||
REFERENCES workspaces(workspace_id)
|
||||
ON DELETE CASCADE
|
||||
) STRICT;)],
|
||||
CREATE TABLE test_table(
|
||||
text TEXT,
|
||||
workspace_id INTEGER,
|
||||
FOREIGN KEY(workspace_id)
|
||||
REFERENCES workspaces(workspace_id)
|
||||
ON DELETE CASCADE
|
||||
) STRICT;)],
|
||||
)
|
||||
})
|
||||
.await
|
||||
|
@ -680,7 +681,7 @@ mod tests {
|
|||
assert_eq!(test_text_1, "test-text-1");
|
||||
}
|
||||
|
||||
fn group(axis: gpui::Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
|
||||
fn group(axis: Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
|
||||
SerializedPaneGroup::Group {
|
||||
axis,
|
||||
flexes: None,
|
||||
|
@ -700,10 +701,10 @@ mod tests {
|
|||
// | 3,4 | |
|
||||
// -----------------
|
||||
let center_group = group(
|
||||
gpui::Axis::Horizontal,
|
||||
Axis::Horizontal,
|
||||
vec![
|
||||
group(
|
||||
gpui::Axis::Vertical,
|
||||
Axis::Vertical,
|
||||
vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
|
@ -859,10 +860,10 @@ mod tests {
|
|||
// | 3,4 | |
|
||||
// -----------------
|
||||
let center_pane = group(
|
||||
gpui::Axis::Horizontal,
|
||||
Axis::Horizontal,
|
||||
vec![
|
||||
group(
|
||||
gpui::Axis::Vertical,
|
||||
Axis::Vertical,
|
||||
vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
|
@ -906,10 +907,10 @@ mod tests {
|
|||
let db = WorkspaceDb(open_test_db("test_cleanup_panes").await);
|
||||
|
||||
let center_pane = group(
|
||||
gpui::Axis::Horizontal,
|
||||
Axis::Horizontal,
|
||||
vec![
|
||||
group(
|
||||
gpui::Axis::Vertical,
|
||||
Axis::Vertical,
|
||||
vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
|
@ -944,7 +945,7 @@ mod tests {
|
|||
db.save_workspace(workspace.clone()).await;
|
||||
|
||||
workspace.center_group = group(
|
||||
gpui::Axis::Vertical,
|
||||
Axis::Vertical,
|
||||
vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
|
|
|
@ -5,9 +5,7 @@ use db::sqlez::{
|
|||
bindable::{Bind, Column, StaticColumnCount},
|
||||
statement::Statement,
|
||||
};
|
||||
use gpui::{
|
||||
platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView, WindowBounds};
|
||||
use project::Project;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
@ -151,15 +149,11 @@ impl SerializedPaneGroup {
|
|||
#[async_recursion(?Send)]
|
||||
pub(crate) async fn deserialize(
|
||||
self,
|
||||
project: &ModelHandle<Project>,
|
||||
project: &Model<Project>,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace: &WeakViewHandle<Workspace>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Option<(
|
||||
Member,
|
||||
Option<ViewHandle<Pane>>,
|
||||
Vec<Option<Box<dyn ItemHandle>>>,
|
||||
)> {
|
||||
workspace: WeakView<Workspace>,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
|
||||
match self {
|
||||
SerializedPaneGroup::Group {
|
||||
axis,
|
||||
|
@ -171,7 +165,7 @@ impl SerializedPaneGroup {
|
|||
let mut items = Vec::new();
|
||||
for child in children {
|
||||
if let Some((new_member, active_pane, new_items)) = child
|
||||
.deserialize(project, workspace_id, workspace, cx)
|
||||
.deserialize(project, workspace_id, workspace.clone(), cx)
|
||||
.await
|
||||
{
|
||||
members.push(new_member);
|
||||
|
@ -200,18 +194,15 @@ impl SerializedPaneGroup {
|
|||
.log_err()?;
|
||||
let active = serialized_pane.active;
|
||||
let new_items = serialized_pane
|
||||
.deserialize_to(project, &pane, workspace_id, workspace, cx)
|
||||
.deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
|
||||
.await
|
||||
.log_err()?;
|
||||
|
||||
if pane
|
||||
.read_with(cx, |pane, _| pane.items_len() != 0)
|
||||
.log_err()?
|
||||
{
|
||||
let pane = pane.upgrade(cx)?;
|
||||
if pane.update(cx, |pane, _| pane.items_len() != 0).log_err()? {
|
||||
let pane = pane.upgrade()?;
|
||||
Some((Member::Pane(pane.clone()), active.then(|| pane), new_items))
|
||||
} else {
|
||||
let pane = pane.upgrade(cx)?;
|
||||
let pane = pane.upgrade()?;
|
||||
workspace
|
||||
.update(cx, |workspace, cx| workspace.force_remove_pane(&pane, cx))
|
||||
.log_err()?;
|
||||
|
@ -235,11 +226,11 @@ impl SerializedPane {
|
|||
|
||||
pub async fn deserialize_to(
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
pane: &WeakViewHandle<Pane>,
|
||||
project: &Model<Project>,
|
||||
pane: &WeakView<Pane>,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace: &WeakViewHandle<Workspace>,
|
||||
cx: &mut AsyncAppContext,
|
||||
workspace: WeakView<Workspace>,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
|
||||
let mut items = Vec::new();
|
||||
let mut active_item_index = None;
|
||||
|
@ -284,7 +275,7 @@ impl SerializedPane {
|
|||
|
||||
pub type GroupId = i64;
|
||||
pub type PaneId = i64;
|
||||
pub type ItemId = usize;
|
||||
pub type ItemId = u64;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct SerializedItem {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use gpui::{
|
||||
AnyViewHandle, AnyWeakViewHandle, AppContext, Subscription, Task, ViewContext, ViewHandle,
|
||||
WeakViewHandle, WindowContext,
|
||||
AnyView, AppContext, EventEmitter, Subscription, Task, View, ViewContext, WeakView,
|
||||
WindowContext,
|
||||
};
|
||||
use project::search::SearchQuery;
|
||||
|
||||
use crate::{item::WeakItemHandle, Item, ItemHandle};
|
||||
use crate::{
|
||||
item::{Item, WeakItemHandle},
|
||||
ItemHandle,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SearchEvent {
|
||||
|
@ -29,7 +32,7 @@ pub struct SearchOptions {
|
|||
pub replacement: bool,
|
||||
}
|
||||
|
||||
pub trait SearchableItem: Item {
|
||||
pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
||||
type Match: Any + Sync + Send + Clone;
|
||||
|
||||
fn supported_options() -> SearchOptions {
|
||||
|
@ -40,11 +43,7 @@ pub trait SearchableItem: Item {
|
|||
replacement: true,
|
||||
}
|
||||
}
|
||||
fn to_search_event(
|
||||
&mut self,
|
||||
event: &Self::Event,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<SearchEvent>;
|
||||
|
||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
|
||||
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
|
||||
|
@ -95,7 +94,7 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
fn subscribe_to_search_events(
|
||||
&self,
|
||||
cx: &mut WindowContext,
|
||||
handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
|
||||
handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
|
||||
) -> Subscription;
|
||||
fn clear_matches(&self, cx: &mut WindowContext);
|
||||
fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
|
||||
|
@ -128,7 +127,8 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
) -> Option<usize>;
|
||||
}
|
||||
|
||||
impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
||||
// todo!("here is where we need to use AnyWeakView");
|
||||
impl<T: SearchableItem> SearchableItemHandle for View<T> {
|
||||
fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
|
||||
Box::new(self.downgrade())
|
||||
}
|
||||
|
@ -144,14 +144,9 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
|||
fn subscribe_to_search_events(
|
||||
&self,
|
||||
cx: &mut WindowContext,
|
||||
handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
|
||||
handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
|
||||
) -> Subscription {
|
||||
cx.subscribe(self, move |handle, event, cx| {
|
||||
let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
|
||||
if let Some(search_event) = search_event {
|
||||
handler(search_event, cx)
|
||||
}
|
||||
})
|
||||
cx.subscribe(self, move |_, event: &SearchEvent, cx| handler(event, cx))
|
||||
}
|
||||
|
||||
fn clear_matches(&self, cx: &mut WindowContext) {
|
||||
|
@ -198,7 +193,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
|||
cx: &mut WindowContext,
|
||||
) -> Task<Vec<Box<dyn Any + Send>>> {
|
||||
let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
|
||||
cx.foreground().spawn(async {
|
||||
cx.spawn(|_| async {
|
||||
let matches = matches.await;
|
||||
matches
|
||||
.into_iter()
|
||||
|
@ -231,21 +226,21 @@ fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T
|
|||
)
|
||||
}
|
||||
|
||||
impl From<Box<dyn SearchableItemHandle>> for AnyViewHandle {
|
||||
impl From<Box<dyn SearchableItemHandle>> for AnyView {
|
||||
fn from(this: Box<dyn SearchableItemHandle>) -> Self {
|
||||
this.as_any().clone()
|
||||
this.to_any().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Box<dyn SearchableItemHandle>> for AnyViewHandle {
|
||||
impl From<&Box<dyn SearchableItemHandle>> for AnyView {
|
||||
fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
|
||||
this.as_any().clone()
|
||||
this.to_any().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Box<dyn SearchableItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id() && self.window() == other.window()
|
||||
self.item_id() == other.item_id()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,22 +249,22 @@ impl Eq for Box<dyn SearchableItemHandle> {}
|
|||
pub trait WeakSearchableItemHandle: WeakItemHandle {
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
|
||||
|
||||
fn into_any(self) -> AnyWeakViewHandle;
|
||||
// fn into_any(self) -> AnyWeakView;
|
||||
}
|
||||
|
||||
impl<T: SearchableItem> WeakSearchableItemHandle for WeakViewHandle<T> {
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.upgrade(cx)?))
|
||||
impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
|
||||
fn upgrade(&self, _cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.upgrade()?))
|
||||
}
|
||||
|
||||
fn into_any(self) -> AnyWeakViewHandle {
|
||||
self.into_any()
|
||||
}
|
||||
// fn into_any(self) -> AnyView {
|
||||
// self.into_any()
|
||||
// }
|
||||
}
|
||||
|
||||
impl PartialEq for Box<dyn WeakSearchableItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id() && self.window() == other.window()
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,6 +272,6 @@ impl Eq for Box<dyn WeakSearchableItemHandle> {}
|
|||
|
||||
impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self.id(), self.window().id()).hash(state)
|
||||
self.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,16 +7,12 @@ use call::participant::{Frame, RemoteVideoTrack};
|
|||
use client::{proto::PeerId, User};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::MouseButton,
|
||||
AppContext, Entity, Task, View, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
sync::{Arc, Weak},
|
||||
div, img, AppContext, Element, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
|
||||
ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use ui::{h_stack, prelude::*, Icon, IconElement, Label};
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
|
@ -29,6 +25,7 @@ pub struct SharedScreen {
|
|||
user: Arc<User>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
_maintain_frame: Task<Result<()>>,
|
||||
focus: FocusHandle,
|
||||
}
|
||||
|
||||
impl SharedScreen {
|
||||
|
@ -38,6 +35,7 @@ impl SharedScreen {
|
|||
user: Arc<User>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
cx.focus_handle();
|
||||
let mut frames = track.frames();
|
||||
Self {
|
||||
track: Arc::downgrade(track),
|
||||
|
@ -55,77 +53,56 @@ impl SharedScreen {
|
|||
this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
|
||||
Ok(())
|
||||
}),
|
||||
focus: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for SharedScreen {
|
||||
type Event = Event;
|
||||
}
|
||||
impl EventEmitter<Event> for SharedScreen {}
|
||||
|
||||
impl View for SharedScreen {
|
||||
fn ui_name() -> &'static str {
|
||||
"SharedScreen"
|
||||
impl FocusableView for SharedScreen {
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
self.focus.clone()
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
enum Focus {}
|
||||
|
||||
let frame = self.frame.clone();
|
||||
MouseEventHandler::new::<Focus, _>(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 Render for SharedScreen {
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div().track_focus(&self.focus).size_full().children(
|
||||
self.frame
|
||||
.as_ref()
|
||||
.map(|frame| img(frame.image()).size_full()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
|
||||
type Event = Event;
|
||||
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_content<V: 'static>(
|
||||
fn tab_content(
|
||||
&self,
|
||||
_: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
_: &AppContext,
|
||||
) -> gpui::AnyElement<V> {
|
||||
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(),
|
||||
selected: bool,
|
||||
_: &WindowContext<'_>,
|
||||
) -> gpui::AnyElement {
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.child(IconElement::new(Icon::Screen))
|
||||
.child(
|
||||
Label::new(format!("{}'s screen", self.user.github_login)).color(if selected {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
}),
|
||||
)
|
||||
.into_any()
|
||||
}
|
||||
|
@ -138,14 +115,14 @@ impl Item for SharedScreen {
|
|||
&self,
|
||||
_workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self> {
|
||||
) -> Option<View<Self>> {
|
||||
let track = self.track.upgrade()?;
|
||||
Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
|
||||
Some(cx.new_view(|cx| Self::new(&track, self.peer_id, self.user.clone(), cx)))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
|
||||
Event::Close => f(ItemEvent::CloseItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::{ItemHandle, Pane};
|
||||
use gpui::{
|
||||
elements::*,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
AnyElement, AnyViewHandle, Entity, SizeConstraint, Subscription, View, ViewContext, ViewHandle,
|
||||
div, AnyView, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
|
||||
WindowContext,
|
||||
};
|
||||
use std::any::TypeId;
|
||||
use ui::{h_stack, prelude::*};
|
||||
use util::ResultExt;
|
||||
|
||||
pub trait StatusItemView: View {
|
||||
pub trait StatusItemView: Render {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn crate::ItemHandle>,
|
||||
|
@ -20,63 +15,57 @@ pub trait StatusItemView: View {
|
|||
);
|
||||
}
|
||||
|
||||
trait StatusItemViewHandle {
|
||||
fn as_any(&self) -> &AnyViewHandle;
|
||||
trait StatusItemViewHandle: Send {
|
||||
fn to_any(&self) -> AnyView;
|
||||
fn set_active_pane_item(
|
||||
&self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
cx: &mut WindowContext,
|
||||
);
|
||||
fn ui_name(&self) -> &'static str;
|
||||
fn item_type(&self) -> TypeId;
|
||||
}
|
||||
|
||||
pub struct StatusBar {
|
||||
left_items: Vec<Box<dyn StatusItemViewHandle>>,
|
||||
right_items: Vec<Box<dyn StatusItemViewHandle>>,
|
||||
active_pane: ViewHandle<Pane>,
|
||||
active_pane: View<Pane>,
|
||||
_observe_active_pane: Subscription,
|
||||
}
|
||||
|
||||
impl Entity for StatusBar {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for StatusBar {
|
||||
fn ui_name() -> &'static str {
|
||||
"StatusBar"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
let theme = &theme::current(cx).workspace.status_bar;
|
||||
|
||||
StatusBarElement {
|
||||
left: Flex::row()
|
||||
.with_children(self.left_items.iter().map(|i| {
|
||||
ChildView::new(i.as_any(), cx)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_margin_right(theme.item_spacing)
|
||||
}))
|
||||
.into_any(),
|
||||
right: Flex::row()
|
||||
.with_children(self.right_items.iter().rev().map(|i| {
|
||||
ChildView::new(i.as_any(), cx)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_margin_left(theme.item_spacing)
|
||||
}))
|
||||
.into_any(),
|
||||
}
|
||||
.contained()
|
||||
.with_style(theme.container)
|
||||
.constrained()
|
||||
.with_height(theme.height)
|
||||
.into_any()
|
||||
impl Render for StatusBar {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.w_full()
|
||||
.h_8()
|
||||
.bg(cx.theme().colors().status_bar_background)
|
||||
.child(self.render_left_tools(cx))
|
||||
.child(self.render_right_tools(cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusBar {
|
||||
pub fn new(active_pane: &ViewHandle<Pane>, cx: &mut ViewContext<Self>) -> Self {
|
||||
fn render_left_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
h_stack()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.children(self.left_items.iter().map(|item| item.to_any()))
|
||||
}
|
||||
|
||||
fn render_right_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
h_stack()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.children(self.right_items.iter().rev().map(|item| item.to_any()))
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusBar {
|
||||
pub fn new(active_pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Self {
|
||||
let mut this = Self {
|
||||
left_items: Default::default(),
|
||||
right_items: Default::default(),
|
||||
|
@ -88,19 +77,22 @@ impl StatusBar {
|
|||
this
|
||||
}
|
||||
|
||||
pub fn add_left_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
|
||||
pub fn add_left_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
T: 'static + StatusItemView,
|
||||
{
|
||||
let active_pane_item = self.active_pane.read(cx).active_item();
|
||||
item.set_active_pane_item(active_pane_item.as_deref(), cx);
|
||||
|
||||
self.left_items.push(Box::new(item));
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn item_of_type<T: StatusItemView>(&self) -> Option<ViewHandle<T>> {
|
||||
pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
|
||||
self.left_items
|
||||
.iter()
|
||||
.chain(self.right_items.iter())
|
||||
.find_map(|item| item.as_any().clone().downcast())
|
||||
.find_map(|item| item.to_any().clone().downcast().log_err())
|
||||
}
|
||||
|
||||
pub fn position_of_item<T>(&self) -> Option<usize>
|
||||
|
@ -108,12 +100,12 @@ impl StatusBar {
|
|||
T: StatusItemView,
|
||||
{
|
||||
for (index, item) in self.left_items.iter().enumerate() {
|
||||
if item.as_ref().ui_name() == T::ui_name() {
|
||||
if item.item_type() == TypeId::of::<T>() {
|
||||
return Some(index);
|
||||
}
|
||||
}
|
||||
for (index, item) in self.right_items.iter().enumerate() {
|
||||
if item.as_ref().ui_name() == T::ui_name() {
|
||||
if item.item_type() == TypeId::of::<T>() {
|
||||
return Some(index + self.left_items.len());
|
||||
}
|
||||
}
|
||||
|
@ -123,11 +115,14 @@ impl StatusBar {
|
|||
pub fn insert_item_after<T>(
|
||||
&mut self,
|
||||
position: usize,
|
||||
item: ViewHandle<T>,
|
||||
item: View<T>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
T: 'static + StatusItemView,
|
||||
{
|
||||
let active_pane_item = self.active_pane.read(cx).active_item();
|
||||
item.set_active_pane_item(active_pane_item.as_deref(), cx);
|
||||
|
||||
if position < self.left_items.len() {
|
||||
self.left_items.insert(position + 1, Box::new(item))
|
||||
} else {
|
||||
|
@ -146,15 +141,18 @@ impl StatusBar {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn add_right_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
|
||||
pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
T: 'static + StatusItemView,
|
||||
{
|
||||
let active_pane_item = self.active_pane.read(cx).active_item();
|
||||
item.set_active_pane_item(active_pane_item.as_deref(), cx);
|
||||
|
||||
self.right_items.push(Box::new(item));
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn set_active_pane(&mut self, active_pane: &ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
|
||||
pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
|
||||
self.active_pane = active_pane.clone();
|
||||
self._observe_active_pane =
|
||||
cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
|
||||
|
@ -169,9 +167,9 @@ impl StatusBar {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: StatusItemView> StatusItemViewHandle for ViewHandle<T> {
|
||||
fn as_any(&self) -> &AnyViewHandle {
|
||||
self
|
||||
impl<T: StatusItemView> StatusItemViewHandle for View<T> {
|
||||
fn to_any(&self) -> AnyView {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
fn set_active_pane_item(
|
||||
|
@ -184,88 +182,13 @@ impl<T: StatusItemView> StatusItemViewHandle for ViewHandle<T> {
|
|||
});
|
||||
}
|
||||
|
||||
fn ui_name(&self) -> &'static str {
|
||||
T::ui_name()
|
||||
fn item_type(&self) -> TypeId {
|
||||
TypeId::of::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dyn StatusItemViewHandle> for AnyViewHandle {
|
||||
impl From<&dyn StatusItemViewHandle> for AnyView {
|
||||
fn from(val: &dyn StatusItemViewHandle) -> Self {
|
||||
val.as_any().clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct StatusBarElement {
|
||||
left: AnyElement<StatusBar>,
|
||||
right: AnyElement<StatusBar>,
|
||||
}
|
||||
|
||||
impl Element<StatusBar> for StatusBarElement {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
mut constraint: SizeConstraint,
|
||||
view: &mut StatusBar,
|
||||
cx: &mut ViewContext<StatusBar>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let max_width = constraint.max.x();
|
||||
constraint.min = vec2f(0., constraint.min.y());
|
||||
|
||||
let right_size = self.right.layout(constraint, view, cx);
|
||||
let constraint = SizeConstraint::new(
|
||||
vec2f(0., constraint.min.y()),
|
||||
vec2f(max_width - right_size.x(), constraint.max.y()),
|
||||
);
|
||||
|
||||
self.left.layout(constraint, view, cx);
|
||||
|
||||
(vec2f(max_width, right_size.y()), ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut StatusBar,
|
||||
cx: &mut ViewContext<StatusBar>,
|
||||
) -> Self::PaintState {
|
||||
let origin_y = bounds.upper_right().y();
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
let left_origin = vec2f(bounds.lower_left().x(), origin_y);
|
||||
self.left.paint(left_origin, visible_bounds, view, cx);
|
||||
|
||||
let right_origin = vec2f(bounds.upper_right().x() - self.right.size().x(), origin_y);
|
||||
self.right.paint(right_origin, visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
_: Range<usize>,
|
||||
_: RectF,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &StatusBar,
|
||||
_: &ViewContext<StatusBar>,
|
||||
) -> Option<RectF> {
|
||||
None
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &StatusBar,
|
||||
_: &ViewContext<StatusBar>,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "StatusBarElement",
|
||||
"bounds": bounds.to_json()
|
||||
})
|
||||
val.to_any().clone()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,35 @@
|
|||
use crate::ItemHandle;
|
||||
use gpui::{
|
||||
elements::*, AnyElement, AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle,
|
||||
AnyView, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View, ViewContext,
|
||||
WindowContext,
|
||||
};
|
||||
use ui::prelude::*;
|
||||
use ui::{h_stack, v_stack};
|
||||
|
||||
pub trait ToolbarItemView: View {
|
||||
pub enum ToolbarItemEvent {
|
||||
ChangeLocation(ToolbarItemLocation),
|
||||
}
|
||||
|
||||
pub trait ToolbarItemView: Render + EventEmitter<ToolbarItemEvent> {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn crate::ItemHandle>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> ToolbarItemLocation;
|
||||
|
||||
fn location_for_event(
|
||||
&self,
|
||||
_event: &Self::Event,
|
||||
current_location: ToolbarItemLocation,
|
||||
_cx: &AppContext,
|
||||
) -> ToolbarItemLocation {
|
||||
current_location
|
||||
}
|
||||
|
||||
fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
|
||||
|
||||
/// Number of times toolbar's height will be repeated to get the effective height.
|
||||
/// Useful when multiple rows one under each other are needed.
|
||||
/// The rows have the same width and act as a whole when reacting to resizes and similar events.
|
||||
fn row_count(&self, _cx: &ViewContext<Self>) -> usize {
|
||||
fn row_count(&self, _cx: &WindowContext) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
trait ToolbarItemViewHandle {
|
||||
fn id(&self) -> usize;
|
||||
fn as_any(&self) -> &AnyViewHandle;
|
||||
trait ToolbarItemViewHandle: Send {
|
||||
fn id(&self) -> EntityId;
|
||||
fn to_any(&self) -> AnyView;
|
||||
fn set_active_pane_item(
|
||||
&self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
|
@ -45,8 +42,8 @@ trait ToolbarItemViewHandle {
|
|||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ToolbarItemLocation {
|
||||
Hidden,
|
||||
PrimaryLeft { flex: Option<(f32, bool)> },
|
||||
PrimaryRight { flex: Option<(f32, bool)> },
|
||||
PrimaryLeft,
|
||||
PrimaryRight,
|
||||
Secondary,
|
||||
}
|
||||
|
||||
|
@ -57,140 +54,161 @@ pub struct Toolbar {
|
|||
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
|
||||
}
|
||||
|
||||
impl Entity for Toolbar {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for Toolbar {
|
||||
fn ui_name() -> &'static str {
|
||||
"Toolbar"
|
||||
impl Toolbar {
|
||||
fn has_any_visible_items(&self) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.any(|(_item, location)| *location != ToolbarItemLocation::Hidden)
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
let theme = &theme::current(cx).workspace.toolbar;
|
||||
|
||||
let mut primary_left_items = Vec::new();
|
||||
let mut primary_right_items = Vec::new();
|
||||
let mut secondary_item = None;
|
||||
let spacing = theme.item_spacing;
|
||||
let mut primary_items_row_count = 1;
|
||||
|
||||
for (item, position) in &self.items {
|
||||
match *position {
|
||||
ToolbarItemLocation::Hidden => {}
|
||||
|
||||
ToolbarItemLocation::PrimaryLeft { flex } => {
|
||||
primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
|
||||
let left_item = ChildView::new(item.as_any(), cx).aligned();
|
||||
if let Some((flex, expanded)) = flex {
|
||||
primary_left_items.push(left_item.flex(flex, expanded).into_any());
|
||||
} else {
|
||||
primary_left_items.push(left_item.into_any());
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItemLocation::PrimaryRight { flex } => {
|
||||
primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
|
||||
let right_item = ChildView::new(item.as_any(), cx).aligned().flex_float();
|
||||
if let Some((flex, expanded)) = flex {
|
||||
primary_right_items.push(right_item.flex(flex, expanded).into_any());
|
||||
} else {
|
||||
primary_right_items.push(right_item.into_any());
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItemLocation::Secondary => {
|
||||
secondary_item = Some(
|
||||
ChildView::new(item.as_any(), cx)
|
||||
.constrained()
|
||||
.with_height(theme.height * item.row_count(cx) as f32)
|
||||
.into_any(),
|
||||
);
|
||||
}
|
||||
fn left_items(&self) -> impl Iterator<Item = &dyn ToolbarItemViewHandle> {
|
||||
self.items.iter().filter_map(|(item, location)| {
|
||||
if *location == ToolbarItemLocation::PrimaryLeft {
|
||||
Some(item.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let container_style = theme.container;
|
||||
let height = theme.height * primary_items_row_count as f32;
|
||||
fn right_items(&self) -> impl Iterator<Item = &dyn ToolbarItemViewHandle> {
|
||||
self.items.iter().filter_map(|(item, location)| {
|
||||
if *location == ToolbarItemLocation::PrimaryRight {
|
||||
Some(item.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let mut primary_items = Flex::row().with_spacing(spacing);
|
||||
primary_items.extend(primary_left_items);
|
||||
primary_items.extend(primary_right_items);
|
||||
|
||||
let mut toolbar = Flex::column();
|
||||
if !primary_items.is_empty() {
|
||||
toolbar.add_child(primary_items.constrained().with_height(height));
|
||||
}
|
||||
if let Some(secondary_item) = secondary_item {
|
||||
toolbar.add_child(secondary_item);
|
||||
}
|
||||
|
||||
if toolbar.is_empty() {
|
||||
toolbar.into_any_named("toolbar")
|
||||
} else {
|
||||
toolbar
|
||||
.contained()
|
||||
.with_style(container_style)
|
||||
.into_any_named("toolbar")
|
||||
}
|
||||
fn secondary_items(&self) -> impl Iterator<Item = &dyn ToolbarItemViewHandle> {
|
||||
self.items.iter().filter_map(|(item, location)| {
|
||||
if *location == ToolbarItemLocation::Secondary {
|
||||
Some(item.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// <<<<<<< HEAD
|
||||
// =======
|
||||
// #[allow(clippy::too_many_arguments)]
|
||||
// fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>)>(
|
||||
// svg_path: &'static str,
|
||||
// style: theme::Interactive<theme::IconButton>,
|
||||
// nav_button_height: f32,
|
||||
// tooltip_style: TooltipStyle,
|
||||
// enabled: bool,
|
||||
// spacing: f32,
|
||||
// on_click: F,
|
||||
// tooltip_action: A,
|
||||
// action_name: &'static str,
|
||||
// cx: &mut ViewContext<Toolbar>,
|
||||
// ) -> AnyElement<Toolbar> {
|
||||
// MouseEventHandler::new::<A, _>(0, cx, |state, _| {
|
||||
// let style = if enabled {
|
||||
// style.style_for(state)
|
||||
impl Render for Toolbar {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
if !self.has_any_visible_items() {
|
||||
return div();
|
||||
}
|
||||
|
||||
let secondary_item = self.secondary_items().next().map(|item| item.to_any());
|
||||
|
||||
let has_left_items = self.left_items().count() > 0;
|
||||
let has_right_items = self.right_items().count() > 0;
|
||||
|
||||
v_stack()
|
||||
.p_2()
|
||||
.when(has_left_items || has_right_items, |this| this.gap_2())
|
||||
.border_b()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.bg(cx.theme().colors().toolbar_background)
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.when(has_left_items, |this| {
|
||||
this.child(
|
||||
h_stack()
|
||||
.flex_1()
|
||||
.justify_start()
|
||||
.children(self.left_items().map(|item| item.to_any())),
|
||||
)
|
||||
})
|
||||
.when(has_right_items, |this| {
|
||||
this.child(
|
||||
h_stack()
|
||||
.flex_1()
|
||||
.justify_end()
|
||||
.children(self.right_items().map(|item| item.to_any())),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.children(secondary_item)
|
||||
}
|
||||
}
|
||||
|
||||
// todo!()
|
||||
// impl View for Toolbar {
|
||||
// fn ui_name() -> &'static str {
|
||||
// "Toolbar"
|
||||
// }
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
// let theme = &theme::current(cx).workspace.toolbar;
|
||||
|
||||
// let mut primary_left_items = Vec::new();
|
||||
// let mut primary_right_items = Vec::new();
|
||||
// let mut secondary_item = None;
|
||||
// let spacing = theme.item_spacing;
|
||||
// let mut primary_items_row_count = 1;
|
||||
|
||||
// for (item, position) in &self.items {
|
||||
// match *position {
|
||||
// ToolbarItemLocation::Hidden => {}
|
||||
|
||||
// ToolbarItemLocation::PrimaryLeft { flex } => {
|
||||
// primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
|
||||
// let left_item = ChildView::new(item.as_any(), cx).aligned();
|
||||
// if let Some((flex, expanded)) = flex {
|
||||
// primary_left_items.push(left_item.flex(flex, expanded).into_any());
|
||||
// } else {
|
||||
// primary_left_items.push(left_item.into_any());
|
||||
// }
|
||||
// }
|
||||
|
||||
// ToolbarItemLocation::PrimaryRight { flex } => {
|
||||
// primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
|
||||
// let right_item = ChildView::new(item.as_any(), cx).aligned().flex_float();
|
||||
// if let Some((flex, expanded)) = flex {
|
||||
// primary_right_items.push(right_item.flex(flex, expanded).into_any());
|
||||
// } else {
|
||||
// primary_right_items.push(right_item.into_any());
|
||||
// }
|
||||
// }
|
||||
|
||||
// ToolbarItemLocation::Secondary => {
|
||||
// secondary_item = Some(
|
||||
// ChildView::new(item.as_any(), cx)
|
||||
// .constrained()
|
||||
// .with_height(theme.height * item.row_count(cx) as f32)
|
||||
// .into_any(),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// let container_style = theme.container;
|
||||
// let height = theme.height * primary_items_row_count as f32;
|
||||
|
||||
// let mut primary_items = Flex::row().with_spacing(spacing);
|
||||
// primary_items.extend(primary_left_items);
|
||||
// primary_items.extend(primary_right_items);
|
||||
|
||||
// let mut toolbar = Flex::column();
|
||||
// if !primary_items.is_empty() {
|
||||
// toolbar.add_child(primary_items.constrained().with_height(height));
|
||||
// }
|
||||
// if let Some(secondary_item) = secondary_item {
|
||||
// toolbar.add_child(secondary_item);
|
||||
// }
|
||||
|
||||
// if toolbar.is_empty() {
|
||||
// toolbar.into_any_named("toolbar")
|
||||
// } else {
|
||||
// style.disabled_style()
|
||||
// };
|
||||
// Svg::new(svg_path)
|
||||
// .with_color(style.color)
|
||||
// .constrained()
|
||||
// .with_width(style.icon_width)
|
||||
// .aligned()
|
||||
// .contained()
|
||||
// .with_style(style.container)
|
||||
// .constrained()
|
||||
// .with_width(style.button_width)
|
||||
// .with_height(nav_button_height)
|
||||
// .aligned()
|
||||
// .top()
|
||||
// })
|
||||
// .with_cursor_style(if enabled {
|
||||
// CursorStyle::PointingHand
|
||||
// } else {
|
||||
// CursorStyle::default()
|
||||
// })
|
||||
// .on_click(MouseButton::Left, move |_, toolbar, cx| {
|
||||
// on_click(toolbar, cx)
|
||||
// })
|
||||
// .with_tooltip::<A>(
|
||||
// 0,
|
||||
// action_name,
|
||||
// Some(Box::new(tooltip_action)),
|
||||
// tooltip_style,
|
||||
// cx,
|
||||
// )
|
||||
// .contained()
|
||||
// .with_margin_right(spacing)
|
||||
// .into_any_named("nav button")
|
||||
// toolbar
|
||||
// .contained()
|
||||
// .with_style(container_style)
|
||||
// .into_any_named("toolbar")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// >>>>>>> 139cbbfd3aebd0863a7d51b0c12d748764cf0b2e
|
||||
impl Toolbar {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
@ -206,7 +224,7 @@ impl Toolbar {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn add_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
|
||||
pub fn add_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
T: 'static + ToolbarItemView,
|
||||
{
|
||||
|
@ -215,12 +233,13 @@ impl Toolbar {
|
|||
if let Some((_, current_location)) =
|
||||
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
|
||||
{
|
||||
let new_location = item
|
||||
.read(cx)
|
||||
.location_for_event(event, *current_location, cx);
|
||||
if new_location != *current_location {
|
||||
*current_location = new_location;
|
||||
cx.notify();
|
||||
match event {
|
||||
ToolbarItemEvent::ChangeLocation(new_location) => {
|
||||
if new_location != current_location {
|
||||
*current_location = *new_location;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -252,10 +271,10 @@ impl Toolbar {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
|
||||
pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<View<T>> {
|
||||
self.items
|
||||
.iter()
|
||||
.find_map(|(item, _)| item.as_any().clone().downcast())
|
||||
.find_map(|(item, _)| item.to_any().downcast().ok())
|
||||
}
|
||||
|
||||
pub fn hidden(&self) -> bool {
|
||||
|
@ -263,13 +282,13 @@ impl Toolbar {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
|
||||
fn id(&self) -> usize {
|
||||
self.id()
|
||||
impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
|
||||
fn id(&self) -> EntityId {
|
||||
self.entity_id()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &AnyViewHandle {
|
||||
self
|
||||
fn to_any(&self) -> AnyView {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
fn set_active_pane_item(
|
||||
|
@ -290,12 +309,13 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
|
|||
}
|
||||
|
||||
fn row_count(&self, cx: &WindowContext) -> usize {
|
||||
self.read_with(cx, |this, cx| this.row_count(cx))
|
||||
self.read(cx).row_count(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {
|
||||
fn from(val: &dyn ToolbarItemViewHandle) -> Self {
|
||||
val.as_any().clone()
|
||||
}
|
||||
}
|
||||
// todo!()
|
||||
// impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {
|
||||
// fn from(val: &dyn ToolbarItemViewHandle) -> Self {
|
||||
// val.as_any().clone()
|
||||
// }
|
||||
// }
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
use settings::Settings;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WorkspaceSettings {
|
||||
|
@ -41,7 +41,7 @@ pub enum GitGutterSetting {
|
|||
Hide,
|
||||
}
|
||||
|
||||
impl Setting for WorkspaceSettings {
|
||||
impl Settings for WorkspaceSettings {
|
||||
const KEY: Option<&'static str> = None;
|
||||
|
||||
type FileContent = WorkspaceSettingsContent;
|
||||
|
@ -49,7 +49,7 @@ impl Setting for WorkspaceSettings {
|
|||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &gpui::AppContext,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue