Merge pull request #1572 from zed-industries/drag-and-drop-fixes

Misc drag and drop issue fixes
This commit is contained in:
K Simmons 2022-08-26 15:58:16 -07:00 committed by GitHub
commit d59911df26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 97 deletions

View File

@ -4,7 +4,7 @@ use gpui::{
elements::{Container, MouseEventHandler},
geometry::vector::Vector2F,
scene::DragRegionEvent,
Element, ElementBox, EventContext, MouseButton, RenderContext, View, ViewContext,
CursorStyle, Element, ElementBox, EventContext, MouseButton, RenderContext, View, ViewContext,
WeakViewHandle,
};
@ -33,7 +33,6 @@ pub struct DragAndDrop<V: View> {
impl<V: View> DragAndDrop<V> {
pub fn new(parent: WeakViewHandle<V>, cx: &mut ViewContext<V>) -> Self {
// TODO: Figure out if detaching here would result in a memory leak
cx.observe_global::<Self, _>(|cx| {
if let Some(parent) = cx.global::<Self>().parent.upgrade(cx) {
parent.update(cx, |_, cx| cx.notify())
@ -110,6 +109,7 @@ impl<V: View> DragAndDrop<V> {
.left()
.boxed()
})
.with_cursor_style(CursorStyle::Arrow)
.on_up(MouseButton::Left, |_, cx| {
cx.defer(|cx| {
cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());

View File

@ -5103,6 +5103,7 @@ impl<T: Entity> From<WeakModelHandle<T>> for AnyWeakModelHandle {
}
}
#[derive(Debug)]
pub struct WeakViewHandle<T> {
window_id: usize,
view_id: usize,

View File

@ -381,7 +381,12 @@ impl Presenter {
}
}
MouseRegionEvent::Click(e) => {
if e.button == self.clicked_button.unwrap() {
// Only raise click events if the released button is the same as the one stored
if self
.clicked_button
.map(|clicked_button| clicked_button == e.button)
.unwrap_or(false)
{
// Clear clicked regions and clicked button
let clicked_regions =
std::mem::replace(&mut self.clicked_regions, Vec::new());

View File

@ -169,7 +169,7 @@ impl MouseRegionEvent {
match self {
MouseRegionEvent::Move(_) => true,
MouseRegionEvent::Drag(_) => false,
MouseRegionEvent::Hover(_) => true,
MouseRegionEvent::Hover(_) => false,
MouseRegionEvent::Down(_) => true,
MouseRegionEvent::Up(_) => true,
MouseRegionEvent::Click(_) => true,

View File

@ -172,7 +172,7 @@ pub enum Event {
Focused,
ActivateItem { local: bool },
Remove,
RemoveItem,
RemoveItem { item_id: usize },
Split(SplitDirection),
ChangeItemTitle,
}
@ -453,7 +453,7 @@ impl Pane {
item
}
pub fn add_item(
pub(crate) fn add_item(
workspace: &mut Workspace,
pane: &ViewHandle<Pane>,
item: Box<dyn ItemHandle>,
@ -475,6 +475,8 @@ impl Pane {
)
};
item.added_to_pane(workspace, pane.clone(), cx);
// Does the item already exist?
if let Some(existing_item_index) = pane.read(cx).items.iter().position(|existing_item| {
let existing_item_entry_ids = existing_item.project_entry_ids(cx);
@ -516,8 +518,6 @@ impl Pane {
pane.activate_item(insertion_index, activate_pane, focus_item, cx);
});
} else {
// If the item doesn't already exist, add it and activate it
item.added_to_pane(workspace, pane.clone(), cx);
pane.update(cx, |pane, cx| {
cx.reparent(&item);
pane.items.insert(insertion_index, item);
@ -762,7 +762,7 @@ impl Pane {
}
let item = self.items.remove(item_ix);
cx.emit(Event::RemoveItem);
cx.emit(Event::RemoveItem { item_id: item.id() });
if self.items.is_empty() {
item.deactivated(cx);
self.update_toolbar(cx);
@ -1069,7 +1069,7 @@ impl Pane {
let detail = detail.clone();
move |dragged_item, cx: &mut RenderContext<Workspace>| {
let tab_style = &theme.workspace.tab_bar.dragged_tab;
Pane::render_tab(
Self::render_tab(
&dragged_item.item,
dragged_item.pane.clone(),
detail,

View File

@ -535,97 +535,123 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
}
}
let mut pending_autosave = None;
let mut cancel_pending_autosave = oneshot::channel::<()>().0;
let pending_update = Rc::new(RefCell::new(None));
let pending_update_scheduled = Rc::new(AtomicBool::new(false));
let pane = pane.downgrade();
cx.subscribe(self, move |workspace, item, event, cx| {
let pane = if let Some(pane) = pane.upgrade(cx) {
pane
} else {
log::error!("unexpected item event after pane was dropped");
return;
};
if workspace
.panes_by_item
.insert(self.id(), pane.downgrade())
.is_none()
{
let mut pending_autosave = None;
let mut cancel_pending_autosave = oneshot::channel::<()>().0;
let pending_update = Rc::new(RefCell::new(None));
let pending_update_scheduled = Rc::new(AtomicBool::new(false));
if let Some(item) = item.to_followable_item_handle(cx) {
let leader_id = workspace.leader_for_pane(&pane);
let mut event_subscription =
Some(cx.subscribe(self, move |workspace, item, event, cx| {
let pane = if let Some(pane) = workspace
.panes_by_item
.get(&item.id())
.and_then(|pane| pane.upgrade(cx))
{
pane
} else {
log::error!("unexpected item event after pane was dropped");
return;
};
if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
workspace.unfollow(&pane, cx);
}
if let Some(item) = item.to_followable_item_handle(cx) {
let leader_id = workspace.leader_for_pane(&pane);
if item.add_event_to_update_proto(event, &mut *pending_update.borrow_mut(), cx)
&& !pending_update_scheduled.load(SeqCst)
{
pending_update_scheduled.store(true, SeqCst);
cx.after_window_update({
let pending_update = pending_update.clone();
let pending_update_scheduled = pending_update_scheduled.clone();
move |this, cx| {
pending_update_scheduled.store(false, SeqCst);
this.update_followers(
proto::update_followers::Variant::UpdateView(proto::UpdateView {
id: item.id() as u64,
variant: pending_update.borrow_mut().take(),
leader_id: leader_id.map(|id| id.0),
}),
cx,
);
}
});
}
}
if T::should_close_item_on_event(event) {
Pane::close_item(workspace, pane, item.id(), cx).detach_and_log_err(cx);
return;
}
if T::should_update_tab_on_event(event) {
pane.update(cx, |_, cx| {
cx.emit(pane::Event::ChangeItemTitle);
cx.notify();
});
}
if T::is_edit_event(event) {
if let Autosave::AfterDelay { milliseconds } = cx.global::<Settings>().autosave {
let prev_autosave = pending_autosave
.take()
.unwrap_or_else(|| Task::ready(Some(())));
let (cancel_tx, mut cancel_rx) = oneshot::channel::<()>();
let prev_cancel_tx = mem::replace(&mut cancel_pending_autosave, cancel_tx);
let project = workspace.project.downgrade();
let _ = prev_cancel_tx.send(());
pending_autosave = Some(cx.spawn_weak(|_, mut cx| async move {
let mut timer = cx
.background()
.timer(Duration::from_millis(milliseconds))
.fuse();
prev_autosave.await;
futures::select_biased! {
_ = cancel_rx => return None,
_ = timer => {}
if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
workspace.unfollow(&pane, cx);
}
let project = project.upgrade(&cx)?;
cx.update(|cx| Pane::autosave_item(&item, project, cx))
.await
.log_err();
None
}));
}
}
})
.detach();
if item.add_event_to_update_proto(
event,
&mut *pending_update.borrow_mut(),
cx,
) && !pending_update_scheduled.load(SeqCst)
{
pending_update_scheduled.store(true, SeqCst);
cx.after_window_update({
let pending_update = pending_update.clone();
let pending_update_scheduled = pending_update_scheduled.clone();
move |this, cx| {
pending_update_scheduled.store(false, SeqCst);
this.update_followers(
proto::update_followers::Variant::UpdateView(
proto::UpdateView {
id: item.id() as u64,
variant: pending_update.borrow_mut().take(),
leader_id: leader_id.map(|id| id.0),
},
),
cx,
);
}
});
}
}
cx.observe_focus(self, move |workspace, item, focused, cx| {
if !focused && cx.global::<Settings>().autosave == Autosave::OnFocusChange {
Pane::autosave_item(&item, workspace.project.clone(), cx).detach_and_log_err(cx);
}
})
.detach();
if T::should_close_item_on_event(event) {
Pane::close_item(workspace, pane, item.id(), cx).detach_and_log_err(cx);
return;
}
if T::should_update_tab_on_event(event) {
pane.update(cx, |_, cx| {
cx.emit(pane::Event::ChangeItemTitle);
cx.notify();
});
}
if T::is_edit_event(event) {
if let Autosave::AfterDelay { milliseconds } =
cx.global::<Settings>().autosave
{
let prev_autosave = pending_autosave
.take()
.unwrap_or_else(|| Task::ready(Some(())));
let (cancel_tx, mut cancel_rx) = oneshot::channel::<()>();
let prev_cancel_tx =
mem::replace(&mut cancel_pending_autosave, cancel_tx);
let project = workspace.project.downgrade();
let _ = prev_cancel_tx.send(());
pending_autosave = Some(cx.spawn_weak(|_, mut cx| async move {
let mut timer = cx
.background()
.timer(Duration::from_millis(milliseconds))
.fuse();
prev_autosave.await;
futures::select_biased! {
_ = cancel_rx => return None,
_ = timer => {}
}
let project = project.upgrade(&cx)?;
cx.update(|cx| Pane::autosave_item(&item, project, cx))
.await
.log_err();
None
}));
}
}
}));
cx.observe_focus(self, move |workspace, item, focused, cx| {
if !focused && cx.global::<Settings>().autosave == Autosave::OnFocusChange {
Pane::autosave_item(&item, workspace.project.clone(), cx)
.detach_and_log_err(cx);
}
})
.detach();
let item_id = self.id();
cx.observe_release(self, move |workspace, _, _| {
workspace.panes_by_item.remove(&item_id);
event_subscription.take();
})
.detach();
}
}
fn deactivated(&self, cx: &mut MutableAppContext) {
@ -799,6 +825,7 @@ pub struct Workspace {
left_sidebar: ViewHandle<Sidebar>,
right_sidebar: ViewHandle<Sidebar>,
panes: Vec<ViewHandle<Pane>>,
panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
active_pane: ViewHandle<Pane>,
status_bar: ViewHandle<StatusBar>,
notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
@ -910,6 +937,7 @@ impl Workspace {
weak_self,
center: PaneGroup::new(pane.clone()),
panes: vec![pane.clone()],
panes_by_item: Default::default(),
active_pane: pane.clone(),
status_bar,
notifications: Default::default(),
@ -1631,8 +1659,13 @@ impl Workspace {
}
self.update_window_edited(cx);
}
pane::Event::RemoveItem => {
pane::Event::RemoveItem { item_id } => {
self.update_window_edited(cx);
if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) {
if entry.get().id() == pane.id() {
entry.remove();
}
}
}
}
} else {
@ -1663,6 +1696,9 @@ impl Workspace {
cx.focus(self.panes.last().unwrap().clone());
self.unfollow(&pane, cx);
self.last_leaders_by_pane.remove(&pane.downgrade());
for removed_item in pane.read(cx).items() {
self.panes_by_item.remove(&removed_item.id());
}
cx.notify();
} else {
self.active_item_path_changed(cx);

View File

@ -72,7 +72,7 @@ export default function tabBar(theme: Theme) {
return {
height,
background: backgroundColor(theme, 300),
dropTargetOverlayColor: withOpacity(theme.textColor.muted, 0.8),
dropTargetOverlayColor: withOpacity(theme.textColor.muted, 0.6),
border: border(theme, "primary", {
left: true,
bottom: true,