Merge branch 'main' into completions-styles

This commit is contained in:
Conrad Irwin
2023-11-27 11:49:39 -07:00
15 changed files with 1213 additions and 1150 deletions
@@ -1,12 +1,13 @@
use gpui::{div, Div, EventEmitter, ParentElement, Render, SemanticVersion, ViewContext};
use gpui::{
div, DismissEvent, Div, EventEmitter, ParentElement, Render, SemanticVersion, ViewContext,
};
use menu::Cancel;
use workspace::notifications::NotificationEvent;
pub struct UpdateNotification {
_version: SemanticVersion,
}
impl EventEmitter<NotificationEvent> for UpdateNotification {}
impl EventEmitter<DismissEvent> for UpdateNotification {}
impl Render for UpdateNotification {
type Element = Div;
@@ -82,6 +83,6 @@ impl UpdateNotification {
}
pub fn _dismiss(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
cx.emit(NotificationEvent::Dismiss);
cx.emit(DismissEvent::Dismiss);
}
}
@@ -1,8 +1,9 @@
use collections::{CommandPaletteFilter, HashMap};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, div, prelude::*, Action, AppContext, Div, EventEmitter, FocusHandle, FocusableView,
Keystroke, Manager, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
actions, div, prelude::*, Action, AppContext, DismissEvent, Div, EventEmitter, FocusHandle,
FocusableView, Keystroke, ParentElement, Render, Styled, View, ViewContext, VisualContext,
WeakView,
};
use picker::{Picker, PickerDelegate};
use std::{
@@ -68,7 +69,7 @@ impl CommandPalette {
}
}
impl EventEmitter<Manager> for CommandPalette {}
impl EventEmitter<DismissEvent> for CommandPalette {}
impl FocusableView for CommandPalette {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
@@ -268,7 +269,7 @@ impl PickerDelegate for CommandPaletteDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.command_palette
.update(cx, |_, cx| cx.emit(Manager::Dismiss))
.update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
.log_err();
}
-2
View File
@@ -531,8 +531,6 @@ pub fn init(cx: &mut AppContext) {
// cx.register_action_type(Editor::context_menu_next);
// cx.register_action_type(Editor::context_menu_last);
hover_popover::init(cx);
workspace::register_project_item::<Editor>(cx);
workspace::register_followable_item::<Editor>(cx);
workspace::register_deserializable_item::<Editor>(cx);
+82 -99
View File
@@ -5,7 +5,9 @@ use crate::{
},
editor_settings::ShowScrollbar,
git::{diff_hunk_to_display, DisplayDiffHunk},
hover_popover::hover_at,
hover_popover::{
self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
},
link_go_to_definition::{
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
update_inlay_link_and_hover_points, GoToDefinitionTrigger,
@@ -257,6 +259,7 @@ impl EditorElement {
// on_action(cx, Editor::open_excerpts); todo!()
register_action(view, cx, Editor::toggle_soft_wrap);
register_action(view, cx, Editor::toggle_inlay_hints);
register_action(view, cx, hover_popover::hover);
register_action(view, cx, Editor::reveal_in_finder);
register_action(view, cx, Editor::copy_path);
register_action(view, cx, Editor::copy_relative_path);
@@ -1024,8 +1027,8 @@ impl EditorElement {
}
});
if let Some((position, mut context_menu)) = layout.context_menu.take() {
cx.with_z_index(1, |cx| {
cx.with_z_index(1, |cx| {
if let Some((position, mut context_menu)) = layout.context_menu.take() {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
let context_menu_size = context_menu.measure(available_space, cx);
@@ -1052,81 +1055,74 @@ impl EditorElement {
list_origin.y -= layout.position_map.line_height + list_height;
}
context_menu.draw(list_origin, available_space, cx);
})
}
cx.break_content_mask(|cx| {
context_menu.draw(list_origin, available_space, cx)
});
}
// if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
// cx.scene().push_stacking_context(None, None);
if let Some((position, mut hover_popovers)) = layout.hover_popovers.take() {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
// // This is safe because we check on layout whether the required row is available
// let hovered_row_layout =
// &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
// This is safe because we check on layout whether the required row is available
let hovered_row_layout = &layout.position_map.line_layouts
[(position.row() - start_row) as usize]
.line;
// // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
// // height. This is the size we will use to decide whether to render popovers above or below
// // the hovered line.
// let first_size = hover_popovers[0].size();
// let height_to_reserve = first_size.y
// + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height;
// Minimum required size: Take the first popover, and add 1.5 times the minimum popover
// height. This is the size we will use to decide whether to render popovers above or below
// the hovered line.
let first_size = hover_popovers[0].measure(available_space, cx);
let height_to_reserve = first_size.height
+ 1.5 * MIN_POPOVER_LINE_HEIGHT * layout.position_map.line_height;
// // Compute Hovered Point
// let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
// let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
// let hovered_point = content_origin + point(x, y);
// Compute Hovered Point
let x = hovered_row_layout.x_for_index(position.column() as usize)
- layout.position_map.scroll_position.x;
let y = position.row() as f32 * layout.position_map.line_height
- layout.position_map.scroll_position.y;
let hovered_point = content_origin + point(x, y);
// if hovered_point.y - height_to_reserve > 0.0 {
// // There is enough space above. Render popovers above the hovered point
// let mut current_y = hovered_point.y;
// for hover_popover in hover_popovers {
// let size = hover_popover.size();
// let mut popover_origin = point(hovered_point.x, current_y - size.y);
if hovered_point.y - height_to_reserve > Pixels::ZERO {
// There is enough space above. Render popovers above the hovered point
let mut current_y = hovered_point.y;
for mut hover_popover in hover_popovers {
let size = hover_popover.measure(available_space, cx);
let mut popover_origin =
point(hovered_point.x, current_y - size.height);
// let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
// if x_out_of_bounds < 0.0 {
// popover_origin.set_x(popover_origin.x + x_out_of_bounds);
// }
let x_out_of_bounds =
text_bounds.upper_right().x - (popover_origin.x + size.width);
if x_out_of_bounds < Pixels::ZERO {
popover_origin.x = popover_origin.x + x_out_of_bounds;
}
// hover_popover.paint(
// popover_origin,
// Bounds::<Pixels>::from_points(
// gpui::Point::<Pixels>::zero(),
// point(f32::MAX, f32::MAX),
// ), // Let content bleed outside of editor
// editor,
// cx,
// );
cx.break_content_mask(|cx| {
hover_popover.draw(popover_origin, available_space, cx)
});
// current_y = popover_origin.y - HOVER_POPOVER_GAP;
// }
// } else {
// // There is not enough space above. Render popovers below the hovered point
// let mut current_y = hovered_point.y + layout.position_map.line_height;
// for hover_popover in hover_popovers {
// let size = hover_popover.size();
// let mut popover_origin = point(hovered_point.x, current_y);
current_y = popover_origin.y - HOVER_POPOVER_GAP;
}
} else {
// There is not enough space above. Render popovers below the hovered point
let mut current_y = hovered_point.y + layout.position_map.line_height;
for mut hover_popover in hover_popovers {
let size = hover_popover.measure(available_space, cx);
let mut popover_origin = point(hovered_point.x, current_y);
// let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
// if x_out_of_bounds < 0.0 {
// popover_origin.set_x(popover_origin.x + x_out_of_bounds);
// }
let x_out_of_bounds =
text_bounds.upper_right().x - (popover_origin.x + size.width);
if x_out_of_bounds < Pixels::ZERO {
popover_origin.x = popover_origin.x + x_out_of_bounds;
}
// hover_popover.paint(
// popover_origin,
// Bounds::<Pixels>::from_points(
// gpui::Point::<Pixels>::zero(),
// point(f32::MAX, f32::MAX),
// ), // Let content bleed outside of editor
// editor,
// cx,
// );
hover_popover.draw(popover_origin, available_space, cx);
// current_y = popover_origin.y + size.y + HOVER_POPOVER_GAP;
// }
// }
// cx.scene().pop_stacking_context();
// }
current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP;
}
}
}
})
},
)
}
@@ -1992,15 +1988,23 @@ impl EditorElement {
}
let visible_rows = start_row..start_row + line_layouts.len() as u32;
// todo!("hover")
// let mut hover = editor.hover_state.render(
// &snapshot,
// &style,
// visible_rows,
// editor.workspace.as_ref().map(|(w, _)| w.clone()),
// cx,
// );
// let mode = editor.mode;
let max_size = size(
(120. * em_width) // Default size
.min(bounds.size.width / 2.) // Shrink to half of the editor width
.max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
(16. * line_height) // Default size
.min(bounds.size.height / 2.) // Shrink to half of the editor height
.max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
);
let mut hover = editor.hover_state.render(
&snapshot,
&style,
visible_rows,
max_size,
editor.workspace.as_ref().map(|(w, _)| w.clone()),
cx,
);
let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
editor.render_fold_indicators(
@@ -2013,27 +2017,6 @@ impl EditorElement {
)
});
// todo!("hover popovers")
// if let Some((_, hover_popovers)) = hover.as_mut() {
// for hover_popover in hover_popovers.iter_mut() {
// hover_popover.layout(
// SizeConstraint {
// min: gpui::Point::<Pixels>::zero(),
// max: point(
// (120. * em_width) // Default size
// .min(size.x / 2.) // Shrink to half of the editor width
// .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
// (16. * line_height) // Default size
// .min(size.y / 2.) // Shrink to half of the editor height
// .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
// ),
// },
// editor,
// cx,
// );
// }
// }
let invisible_symbol_font_size = font_size / 2.;
let tab_invisible = cx
.text_system()
@@ -2102,7 +2085,7 @@ impl EditorElement {
fold_indicators,
tab_invisible,
space_invisible,
// hover_popovers: hover,
hover_popovers: hover,
}
})
}
@@ -3287,7 +3270,7 @@ pub struct LayoutState {
max_row: u32,
context_menu: Option<(DisplayPoint, AnyElement)>,
code_actions_indicator: Option<CodeActionsIndicator>,
// hover_popovers: Option<(DisplayPoint, Vec<AnyElement>)>,
hover_popovers: Option<(DisplayPoint, Vec<AnyElement>)>,
fold_indicators: Vec<Option<IconButton>>,
tab_invisible: ShapedLine,
space_invisible: ShapedLine,
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -2,8 +2,8 @@ use collections::HashMap;
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{
actions, div, AppContext, Div, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
IntoElement, Manager, Model, ParentElement, Render, Styled, Task, View, ViewContext,
actions, div, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, Task, View, ViewContext,
VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
@@ -111,7 +111,7 @@ impl FileFinder {
}
}
impl EventEmitter<Manager> for FileFinder {}
impl EventEmitter<DismissEvent> for FileFinder {}
impl FocusableView for FileFinder {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.picker.focus_handle(cx)
@@ -690,7 +690,7 @@ impl PickerDelegate for FileFinderDelegate {
}
}
finder
.update(&mut cx, |_, cx| cx.emit(Manager::Dismiss))
.update(&mut cx, |_, cx| cx.emit(DismissEvent::Dismiss))
.ok()?;
Some(())
@@ -702,7 +702,7 @@ impl PickerDelegate for FileFinderDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
self.file_finder
.update(cx, |_, cx| cx.emit(Manager::Dismiss))
.update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
.log_err();
}
+7 -6
View File
@@ -1,7 +1,8 @@
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
use gpui::{
actions, div, prelude::*, AppContext, Div, EventEmitter, FocusHandle, FocusableView, Manager,
Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
actions, div, prelude::*, AppContext, DismissEvent, Div, EventEmitter, FocusHandle,
FocusableView, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
WindowContext,
};
use text::{Bias, Point};
use theme::ActiveTheme;
@@ -28,7 +29,7 @@ impl FocusableView for GoToLine {
self.active_editor.focus_handle(cx)
}
}
impl EventEmitter<Manager> for GoToLine {}
impl EventEmitter<DismissEvent> for GoToLine {}
impl GoToLine {
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
@@ -88,7 +89,7 @@ impl GoToLine {
) {
match event {
// todo!() this isn't working...
editor::EditorEvent::Blurred => cx.emit(Manager::Dismiss),
editor::EditorEvent::Blurred => cx.emit(DismissEvent::Dismiss),
editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx),
_ => {}
}
@@ -123,7 +124,7 @@ impl GoToLine {
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
cx.emit(Manager::Dismiss);
cx.emit(DismissEvent::Dismiss);
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
@@ -140,7 +141,7 @@ impl GoToLine {
self.prev_scroll_position.take();
}
cx.emit(Manager::Dismiss);
cx.emit(DismissEvent::Dismiss);
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, FocusableView,
ForegroundExecutor, Manager, Model, ModelContext, Render, Result, Task, View, ViewContext,
VisualContext, WindowContext, WindowHandle,
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent,
FocusableView, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View,
ViewContext, VisualContext, WindowContext, WindowHandle,
};
use anyhow::{anyhow, Context as _};
use derive_more::{Deref, DerefMut};
@@ -326,7 +326,7 @@ impl VisualContext for AsyncWindowContext {
V: crate::ManagedView,
{
self.window.update(self, |_, cx| {
view.update(cx, |_, cx| cx.emit(Manager::Dismiss))
view.update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
})
}
}
+1 -1
View File
@@ -611,7 +611,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
{
self.window
.update(self.cx, |_, cx| {
view.update(cx, |_, cx| cx.emit(crate::Manager::Dismiss))
view.update(cx, |_, cx| cx.emit(crate::DismissEvent::Dismiss))
})
.unwrap()
}
+5 -3
View File
@@ -145,9 +145,11 @@ impl Element for Overlay {
}
cx.with_element_offset(desired.origin - bounds.origin, |cx| {
for child in self.children {
child.paint(cx);
}
cx.break_content_mask(|cx| {
for child in self.children {
child.paint(cx);
}
})
})
}
}
+23 -5
View File
@@ -193,11 +193,11 @@ pub trait FocusableView: 'static + Render {
/// ManagedView is a view (like a Modal, Popover, Menu, etc.)
/// where the lifecycle of the view is handled by another view.
pub trait ManagedView: FocusableView + EventEmitter<Manager> {}
pub trait ManagedView: FocusableView + EventEmitter<DismissEvent> {}
impl<M: FocusableView + EventEmitter<Manager>> ManagedView for M {}
impl<M: FocusableView + EventEmitter<DismissEvent>> ManagedView for M {}
pub enum Manager {
pub enum DismissEvent {
Dismiss,
}
@@ -1663,7 +1663,7 @@ impl VisualContext for WindowContext<'_> {
where
V: ManagedView,
{
self.update_view(view, |_, cx| cx.emit(Manager::Dismiss))
self.update_view(view, |_, cx| cx.emit(DismissEvent::Dismiss))
}
}
@@ -1752,6 +1752,24 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
}
}
/// Invoke the given function with the content mask reset to that
/// of the window.
fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let mask = ContentMask {
bounds: Bounds {
origin: Point::default(),
size: self.window().viewport_size,
},
};
self.window_mut()
.current_frame
.content_mask_stack
.push(mask);
let result = f(self);
self.window_mut().current_frame.content_mask_stack.pop();
result
}
/// Update the global element offset relative to the current offset. This is used to implement
/// scrolling.
fn with_element_offset<R>(
@@ -2349,7 +2367,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
where
V: ManagedView,
{
self.defer(|_, cx| cx.emit(Manager::Dismiss))
self.defer(|_, cx| cx.emit(DismissEvent::Dismiss))
}
pub fn listener<E>(
+8 -8
View File
@@ -4,9 +4,9 @@ use std::rc::Rc;
use crate::{prelude::*, v_stack, Label, List};
use crate::{ListItem, ListSeparator, ListSubHeader};
use gpui::{
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DispatchPhase,
Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId, ManagedView, Manager,
MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DismissEvent,
DispatchPhase, Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId,
ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
};
pub enum ContextMenuItem {
@@ -26,7 +26,7 @@ impl FocusableView for ContextMenu {
}
}
impl EventEmitter<Manager> for ContextMenu {}
impl EventEmitter<DismissEvent> for ContextMenu {}
impl ContextMenu {
pub fn build(
@@ -74,11 +74,11 @@ impl ContextMenu {
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
// todo!()
cx.emit(Manager::Dismiss);
cx.emit(DismissEvent::Dismiss);
}
pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
cx.emit(Manager::Dismiss);
cx.emit(DismissEvent::Dismiss);
}
}
@@ -111,7 +111,7 @@ impl Render for ContextMenu {
}
ContextMenuItem::Entry(entry, callback) => {
let callback = callback.clone();
let dismiss = cx.listener(|_, _, cx| cx.emit(Manager::Dismiss));
let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent::Dismiss));
ListItem::new(entry.clone())
.child(Label::new(entry.clone()))
@@ -265,7 +265,7 @@ impl<M: ManagedView> Element for MenuHandle<M> {
let new_menu = (builder)(cx);
let menu2 = menu.clone();
cx.subscribe(&new_menu, move |modal, e, cx| match e {
&Manager::Dismiss => {
&DismissEvent::Dismiss => {
*menu2.borrow_mut() = None;
cx.notify();
}
+18 -2
View File
@@ -63,7 +63,7 @@ use crate::{
};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
use lazy_static::lazy_static;
use notifications::{NotificationHandle, NotifyResultExt};
use notifications::{simple_message_notification, NotificationHandle, NotifyResultExt};
pub use pane::*;
pub use pane_group::*;
use persistence::{model::SerializedItem, DB};
@@ -776,7 +776,23 @@ impl Workspace {
}),
];
cx.defer(|this, cx| this.update_window_title(cx));
cx.defer(|this, cx| {
this.update_window_title(cx);
this.show_notification(0, cx, |cx| {
cx.add_view(|_cx| {
simple_message_notification::MessageNotification::new(format!(
"Error: what happens if this message is very very very very very long "
))
.with_click_message("Click here because!")
})
});
this.show_notification(1, cx, |cx| {
cx.add_view(|_cx| {
simple_message_notification::MessageNotification::new(format!("Nope"))
})
});
});
Workspace {
weak_self: weak_handle.clone(),
modal: None,
+90 -43
View File
@@ -1,6 +1,9 @@
use crate::{Toast, Workspace};
use collections::HashMap;
use gpui::{AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext};
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) {
@@ -9,13 +12,9 @@ pub fn init(cx: &mut AppContext) {
// simple_message_notification::init(cx);
}
pub enum NotificationEvent {
Dismiss,
}
pub trait Notification: EventEmitter<DismissEvent> + Render {}
pub trait Notification: EventEmitter<NotificationEvent> + Render {}
impl<V: EventEmitter<NotificationEvent> + Render> Notification for V {}
impl<V: EventEmitter<DismissEvent> + Render> Notification for V {}
pub trait NotificationHandle: Send {
fn id(&self) -> EntityId;
@@ -107,8 +106,8 @@ impl Workspace {
let notification = build_notification(cx);
cx.subscribe(
&notification,
move |this, handle, event: &NotificationEvent, cx| match event {
NotificationEvent::Dismiss => {
move |this, handle, event: &DismissEvent, cx| match event {
DismissEvent::Dismiss => {
this.dismiss_notification_internal(type_id, id, cx);
}
},
@@ -120,6 +119,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.build_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>();
@@ -166,13 +176,14 @@ impl Workspace {
}
pub mod simple_message_notification {
use super::NotificationEvent;
use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
use gpui::{
div, AnyElement, AppContext, DismissEvent, Div, EventEmitter, InteractiveElement,
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, TextStyle,
ViewContext,
};
use serde::Deserialize;
use std::{borrow::Cow, sync::Arc};
// todo!()
// actions!(message_notifications, [CancelMessageNotification]);
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
#[derive(Clone, Default, Deserialize, PartialEq)]
pub struct OsOpen(pub Cow<'static, str>);
@@ -197,22 +208,22 @@ pub mod simple_message_notification {
// }
enum NotificationMessage {
Text(Cow<'static, str>),
Text(SharedString),
Element(fn(TextStyle, &AppContext) -> AnyElement),
}
pub struct MessageNotification {
message: NotificationMessage,
on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>) + Send + Sync>>,
click_message: Option<Cow<'static, str>>,
click_message: Option<SharedString>,
}
impl EventEmitter<NotificationMessage> for MessageNotification {}
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()),
@@ -221,19 +232,20 @@ pub mod simple_message_notification {
}
}
pub fn new_element(
message: fn(TextStyle, &AppContext) -> AnyElement,
) -> MessageNotification {
Self {
message: NotificationMessage::Element(message),
on_click: None,
click_message: None,
}
}
// not needed I think (only for the "new panel" toast, which is outdated now)
// pub fn new_element(
// message: fn(TextStyle, &AppContext) -> AnyElement,
// ) -> MessageNotification {
// Self {
// message: NotificationMessage::Element(message),
// on_click: None,
// click_message: None,
// }
// }
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
@@ -247,17 +259,43 @@ pub mod simple_message_notification {
self
}
// todo!()
// 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::Dismiss);
}
}
impl Render for MessageNotification {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()
v_stack()
.elevation_3(cx)
.p_4()
.child(
h_stack()
.justify_between()
.child(div().max_w_80().child(match &self.message {
NotificationMessage::Text(text) => Label::new(text.clone()),
NotificationMessage::Element(element) => {
todo!()
}
}))
.child(
div()
.id("cancel")
.child(IconElement::new(Icon::Close))
.cursor_pointer()
.on_click(cx.listener(|this, event, cx| this.dismiss(cx))),
),
)
.children(self.click_message.iter().map(|message| {
Button::new(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!()
@@ -359,8 +397,6 @@ pub mod simple_message_notification {
// .into_any()
// }
// }
impl EventEmitter<NotificationEvent> for MessageNotification {}
}
pub trait NotifyResultExt {
@@ -371,6 +407,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,14 +422,23 @@ where
Ok(value) => Some(value),
Err(err) => {
log::error!("TODO {err:?}");
// todo!()
// workspace.show_notification(0, cx, |cx| {
// cx.add_view(|_cx| {
// simple_message_notification::MessageNotification::new(format!(
// "Error: {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
}
}
+42 -29
View File
@@ -683,7 +683,21 @@ impl Workspace {
}),
];
cx.defer(|this, cx| this.update_window_title(cx));
cx.defer(|this, cx| {
this.update_window_title(cx);
// todo! @nate - these are useful for testing notifications
// this.show_error(
// &anyhow::anyhow!("what happens if this message is very very very very very long"),
// cx,
// );
// this.show_notification(1, cx, |cx| {
// cx.build_view(|_cx| {
// simple_message_notification::MessageNotification::new(format!("Error:"))
// .with_click_message("click here because!")
// })
// });
});
Workspace {
window_self: window_handle,
weak_self: weak_handle.clone(),
@@ -2566,32 +2580,31 @@ impl Workspace {
// }
// }
// fn render_notifications(
// &self,
// theme: &theme::Workspace,
// cx: &AppContext,
// ) -> Option<AnyElement<Workspace>> {
// if self.notifications.is_empty() {
// None
// } else {
// Some(
// Flex::column()
// .with_children(self.notifications.iter().map(|(_, _, notification)| {
// ChildView::new(notification.as_any(), cx)
// .contained()
// .with_style(theme.notification)
// }))
// .constrained()
// .with_width(theme.notifications.width)
// .contained()
// .with_style(theme.notifications.container)
// .aligned()
// .bottom()
// .right()
// .into_any(),
// )
// }
// }
fn render_notifications(&self, cx: &ViewContext<Self>) -> Option<Div> {
if self.notifications.is_empty() {
None
} else {
Some(
div()
.absolute()
.z_index(100)
.right_3()
.bottom_3()
.w_96()
.h_full()
.flex()
.flex_col()
.justify_end()
.gap_2()
.children(self.notifications.iter().map(|(_, _, notification)| {
div()
.on_any_mouse_down(|_, cx| cx.stop_propagation())
.on_any_mouse_up(|_, cx| cx.stop_propagation())
.child(notification.to_any())
})),
)
}
}
// // RPC handlers
@@ -3653,7 +3666,6 @@ impl Render for Workspace {
.bg(cx.theme().colors().background)
.children(self.titlebar_item.clone())
.child(
// todo! should this be a component a view?
div()
.id("workspace")
.relative()
@@ -3703,7 +3715,8 @@ impl Render for Workspace {
.overflow_hidden()
.child(self.right_dock.clone()),
),
),
)
.children(self.render_notifications(cx)),
)
.child(self.status_bar.clone())
}