mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 18:41:56 +03:00
Add new drag API
This commit is contained in:
parent
bfbbec0b01
commit
a807e798ec
@ -1138,6 +1138,10 @@ impl AppContext {
|
||||
pub fn has_active_drag(&self) -> bool {
|
||||
self.active_drag.is_some()
|
||||
}
|
||||
|
||||
pub fn active_drag(&self) -> Option<AnyView> {
|
||||
self.active_drag.as_ref().map(|drag| drag.view.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for AppContext {
|
||||
|
@ -482,48 +482,31 @@ impl IntoElement for AnyElement {
|
||||
}
|
||||
}
|
||||
|
||||
// impl<V, E, F> Element for Option<F>
|
||||
// where
|
||||
// V: 'static,
|
||||
// E: Element,
|
||||
// F: FnOnce(&mut V, &mut WindowContext<'_, V>) -> E + 'static,
|
||||
// {
|
||||
// type State = Option<AnyElement>;
|
||||
/// The empty element, which renders nothing.
|
||||
pub type Empty = ();
|
||||
|
||||
// fn element_id(&self) -> Option<ElementId> {
|
||||
// None
|
||||
// }
|
||||
impl IntoElement for () {
|
||||
type Element = Self;
|
||||
|
||||
// fn layout(
|
||||
// &mut self,
|
||||
// _: Option<Self::State>,
|
||||
// cx: &mut WindowContext,
|
||||
// ) -> (LayoutId, Self::State) {
|
||||
// let render = self.take().unwrap();
|
||||
// let mut element = (render)(view_state, cx).into_any();
|
||||
// let layout_id = element.layout(view_state, cx);
|
||||
// (layout_id, Some(element))
|
||||
// }
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
// fn paint(
|
||||
// self,
|
||||
// _bounds: Bounds<Pixels>,
|
||||
// rendered_element: &mut Self::State,
|
||||
// cx: &mut WindowContext,
|
||||
// ) {
|
||||
// rendered_element.take().unwrap().paint(view_state, cx);
|
||||
// }
|
||||
// }
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// impl<V, E, F> RenderOnce for Option<F>
|
||||
// where
|
||||
// V: 'static,
|
||||
// E: Element,
|
||||
// F: FnOnce(&mut V, &mut WindowContext) -> E + 'static,
|
||||
// {
|
||||
// type Element = Self;
|
||||
impl Element for () {
|
||||
type State = ();
|
||||
|
||||
// fn render(self) -> Self::Element {
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
fn layout(
|
||||
&mut self,
|
||||
_state: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
(cx.request_layout(&crate::Style::default(), None), ())
|
||||
}
|
||||
|
||||
fn paint(self, _bounds: Bounds<Pixels>, _state: &mut Self::State, _cx: &mut WindowContext) {}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use refineable::Refineable as _;
|
||||
|
||||
use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
|
||||
|
||||
pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext)) -> Canvas {
|
||||
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
|
||||
Canvas {
|
||||
paint_callback: Box::new(callback),
|
||||
style: StyleRefinement::default(),
|
||||
@ -10,7 +10,7 @@ pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext
|
||||
}
|
||||
|
||||
pub struct Canvas {
|
||||
paint_callback: Box<dyn FnOnce(Bounds<Pixels>, &mut WindowContext)>,
|
||||
paint_callback: Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ impl Element for Canvas {
|
||||
}
|
||||
|
||||
fn paint(self, bounds: Bounds<Pixels>, _: &mut (), cx: &mut WindowContext) {
|
||||
(self.paint_callback)(bounds, cx)
|
||||
(self.paint_callback)(&bounds, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,11 @@ pub struct GroupStyle {
|
||||
pub style: Box<StyleRefinement>,
|
||||
}
|
||||
|
||||
pub struct DragMoveEvent<W: Render> {
|
||||
pub event: MouseMoveEvent,
|
||||
pub drag: View<W>,
|
||||
}
|
||||
|
||||
pub trait InteractiveElement: Sized {
|
||||
fn interactivity(&mut self) -> &mut Interactivity;
|
||||
|
||||
@ -192,6 +197,34 @@ pub trait InteractiveElement: Sized {
|
||||
self
|
||||
}
|
||||
|
||||
fn on_drag_move<W>(
|
||||
mut self,
|
||||
listener: impl Fn(&DragMoveEvent<W>, &mut WindowContext) + 'static,
|
||||
) -> Self
|
||||
where
|
||||
W: Render,
|
||||
{
|
||||
self.interactivity().mouse_move_listeners.push(Box::new(
|
||||
move |event, bounds, phase, cx| {
|
||||
if phase == DispatchPhase::Capture
|
||||
&& bounds.drag_target_contains(&event.position, cx)
|
||||
{
|
||||
if let Some(view) = cx.active_drag().and_then(|view| view.downcast::<W>().ok())
|
||||
{
|
||||
(listener)(
|
||||
&DragMoveEvent {
|
||||
event: event.clone(),
|
||||
drag: view,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_scroll_wheel(
|
||||
mut self,
|
||||
listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
|
||||
@ -403,7 +436,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
|
||||
self
|
||||
}
|
||||
|
||||
fn on_drag<W>(mut self, listener: impl Fn(&mut WindowContext) -> View<W> + 'static) -> Self
|
||||
fn on_drag<W>(mut self, constructor: impl Fn(&mut WindowContext) -> View<W> + 'static) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
W: 'static + Render,
|
||||
@ -413,7 +446,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
|
||||
"calling on_drag more than once on the same element is not supported"
|
||||
);
|
||||
self.interactivity().drag_listener = Some(Box::new(move |cursor_offset, cx| AnyDrag {
|
||||
view: listener(cx).into(),
|
||||
view: constructor(cx).into(),
|
||||
cursor_offset,
|
||||
}));
|
||||
self
|
||||
|
23
crates/gpui2_macros/src/derive_render.rs
Normal file
23
crates/gpui2_macros/src/derive_render.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
pub fn derive_render(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let type_name = &ast.ident;
|
||||
let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
|
||||
|
||||
let gen = quote! {
|
||||
impl #impl_generics gpui::Render for #type_name #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Element = ();
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
mod derive_into_element;
|
||||
mod derive_render;
|
||||
mod register_action;
|
||||
mod style_helpers;
|
||||
mod test;
|
||||
@ -15,6 +16,11 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
|
||||
derive_into_element::derive_into_element(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Render)]
|
||||
pub fn derive_render(input: TokenStream) -> TokenStream {
|
||||
derive_render::derive_render(input)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn style_helpers(input: TokenStream) -> TokenStream {
|
||||
style_helpers::style_helpers(input)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::DraggedDock;
|
||||
use crate::{status_bar::StatusItemView, Workspace};
|
||||
use crate::{DockClickReset, DockDragState};
|
||||
use gpui::{
|
||||
div, px, Action, AnchorCorner, AnyView, AppContext, Axis, ClickEvent, Div, Entity, EntityId,
|
||||
EventEmitter, FocusHandle, FocusableView, IntoElement, MouseButton, ParentElement, Render,
|
||||
@ -493,27 +493,10 @@ impl Render for Dock {
|
||||
let handler = div()
|
||||
.id("resize-handle")
|
||||
.bg(cx.theme().colors().border)
|
||||
.on_mouse_down(gpui::MouseButton::Left, move |_, cx| {
|
||||
cx.update_global(|drag: &mut DockDragState, cx| drag.0 = Some(position))
|
||||
})
|
||||
.on_drag(move |cx| cx.build_view(|_| DraggedDock(position)))
|
||||
.on_click(cx.listener(|v, e: &ClickEvent, cx| {
|
||||
if e.down.button == MouseButton::Left {
|
||||
cx.update_global(|state: &mut DockClickReset, cx| {
|
||||
if state.0.is_some() {
|
||||
state.0 = None;
|
||||
v.resize_active_panel(None, cx)
|
||||
} else {
|
||||
let double_click = cx.double_click_interval();
|
||||
let timer = cx.background_executor().timer(double_click);
|
||||
state.0 = Some(cx.spawn(|_, mut cx| async move {
|
||||
timer.await;
|
||||
cx.update_global(|state: &mut DockClickReset, cx| {
|
||||
state.0 = None;
|
||||
})
|
||||
.ok();
|
||||
}));
|
||||
}
|
||||
})
|
||||
if e.down.button == MouseButton::Left && e.down.click_count == 2 {
|
||||
v.resize_active_panel(None, cx)
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -30,11 +30,11 @@ use futures::{
|
||||
};
|
||||
use gpui::{
|
||||
actions, canvas, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
|
||||
AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity,
|
||||
EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveElement,
|
||||
KeyContext, ManagedView, Model, ModelContext, MouseMoveEvent, ParentElement, PathPromptOptions,
|
||||
Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext,
|
||||
VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
|
||||
AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div,
|
||||
DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels,
|
||||
InteractiveElement, KeyContext, ManagedView, Model, ModelContext, ParentElement,
|
||||
PathPromptOptions, Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View,
|
||||
ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
|
||||
};
|
||||
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
|
||||
use itertools::Itertools;
|
||||
@ -227,9 +227,6 @@ pub fn init_settings(cx: &mut AppContext) {
|
||||
}
|
||||
|
||||
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||
cx.default_global::<DockDragState>();
|
||||
cx.default_global::<DockClickReset>();
|
||||
|
||||
init_settings(cx);
|
||||
notifications::init(cx);
|
||||
|
||||
@ -466,6 +463,7 @@ pub struct Workspace {
|
||||
_observe_current_user: Task<Result<()>>,
|
||||
_schedule_serialize: Option<Task<()>>,
|
||||
pane_history_timestamp: Arc<AtomicUsize>,
|
||||
bounds: Bounds<Pixels>,
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for Workspace {}
|
||||
@ -708,6 +706,8 @@ impl Workspace {
|
||||
subscriptions,
|
||||
pane_history_timestamp,
|
||||
workspace_actions: Default::default(),
|
||||
// This data will be incorrect, but it will be overwritten by the time it needs to be used.
|
||||
bounds: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -3580,13 +3580,8 @@ impl FocusableView for Workspace {
|
||||
|
||||
struct WorkspaceBounds(Bounds<Pixels>);
|
||||
|
||||
//todo!("remove this when better drag APIs are in GPUI2")
|
||||
#[derive(Default)]
|
||||
struct DockDragState(Option<DockPosition>);
|
||||
|
||||
//todo!("remove this when better double APIs are in GPUI2")
|
||||
#[derive(Default)]
|
||||
struct DockClickReset(Option<Task<()>>);
|
||||
#[derive(Render)]
|
||||
struct DraggedDock(DockPosition);
|
||||
|
||||
impl Render for Workspace {
|
||||
type Element = Div;
|
||||
@ -3632,37 +3627,33 @@ impl Render for Workspace {
|
||||
.border_t()
|
||||
.border_b()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.on_mouse_up(gpui::MouseButton::Left, |_, cx| {
|
||||
cx.update_global(|drag: &mut DockDragState, cx| {
|
||||
drag.0 = None;
|
||||
})
|
||||
})
|
||||
.on_mouse_move(cx.listener(|workspace, e: &MouseMoveEvent, cx| {
|
||||
if let Some(types) = &cx.global::<DockDragState>().0 {
|
||||
let workspace_bounds = cx.global::<WorkspaceBounds>().0;
|
||||
match types {
|
||||
.child(canvas(
|
||||
cx.listener(|workspace, bounds, cx| workspace.bounds = *bounds),
|
||||
))
|
||||
.on_drag_move(
|
||||
cx.listener(|workspace, e: &DragMoveEvent<DraggedDock>, cx| {
|
||||
match e.drag.read(cx).0 {
|
||||
DockPosition::Left => {
|
||||
let size = e.position.x;
|
||||
let size = e.event.position.x;
|
||||
workspace.left_dock.update(cx, |left_dock, cx| {
|
||||
left_dock.resize_active_panel(Some(size.0), cx);
|
||||
});
|
||||
}
|
||||
DockPosition::Right => {
|
||||
let size = workspace_bounds.size.width - e.position.x;
|
||||
let size = workspace.bounds.size.width - e.event.position.x;
|
||||
workspace.right_dock.update(cx, |right_dock, cx| {
|
||||
right_dock.resize_active_panel(Some(size.0), cx);
|
||||
});
|
||||
}
|
||||
DockPosition::Bottom => {
|
||||
let size = workspace_bounds.size.height - e.position.y;
|
||||
let size = workspace.bounds.size.height - e.event.position.y;
|
||||
workspace.bottom_dock.update(cx, |bottom_dock, cx| {
|
||||
bottom_dock.resize_active_panel(Some(size.0), cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.child(canvas(|bounds, cx| cx.set_global(WorkspaceBounds(bounds))))
|
||||
}),
|
||||
)
|
||||
.child(self.modal_layer.clone())
|
||||
.child(
|
||||
div()
|
||||
|
Loading…
Reference in New Issue
Block a user