This commit is contained in:
Nathan Sobo 2023-05-13 14:34:09 -06:00
parent 6c60853842
commit ba50b35de6
9 changed files with 634 additions and 525 deletions

View file

@ -219,7 +219,8 @@
"cmd-shift-g": "search::SelectPrevMatch", "cmd-shift-g": "search::SelectPrevMatch",
"alt-cmd-c": "search::ToggleCaseSensitive", "alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord", "alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-r": "search::ToggleRegex" "alt-cmd-r": "search::ToggleRegex",
"shift-escape": "pane::ToggleZoom"
} }
}, },
// Bindings from VS Code // Bindings from VS Code

View file

@ -3816,6 +3816,12 @@ impl<T> PartialEq for ViewHandle<T> {
} }
} }
impl<T> PartialEq<AnyViewHandle> for ViewHandle<T> {
fn eq(&self, other: &AnyViewHandle) -> bool {
self.window_id == other.window_id && self.view_id == other.view_id
}
}
impl<T> PartialEq<WeakViewHandle<T>> for ViewHandle<T> { impl<T> PartialEq<WeakViewHandle<T>> for ViewHandle<T> {
fn eq(&self, other: &WeakViewHandle<T>) -> bool { fn eq(&self, other: &WeakViewHandle<T>) -> bool {
self.window_id == other.window_id && self.view_id == other.view_id self.window_id == other.window_id && self.view_id == other.view_id

View file

@ -33,8 +33,11 @@ use crate::{
rect::RectF, rect::RectF,
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle, json,
WindowContext, platform::MouseButton,
scene::MouseDown,
Action, EventContext, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
WeakViewHandle, WindowContext,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::HashMap; use collections::HashMap;
@ -198,6 +201,13 @@ pub trait Element<V: View>: 'static {
{ {
Resizable::new(self.into_any(), side, size, on_resize) Resizable::new(self.into_any(), side, size, on_resize)
} }
fn mouse<Tag>(self, region_id: usize) -> MouseEventHandler<Tag, V>
where
Self: Sized,
{
MouseEventHandler::for_child(self.into_any(), region_id)
}
} }
trait AnyElementState<V: View> { trait AnyElementState<V: View> {

View file

@ -32,10 +32,25 @@ pub struct MouseEventHandler<Tag: 'static, V: View> {
/// Element which provides a render_child callback with a MouseState and paints a mouse /// Element which provides a render_child callback with a MouseState and paints a mouse
/// region under (or above) it for easy mouse event handling. /// region under (or above) it for easy mouse event handling.
impl<Tag, V: View> MouseEventHandler<Tag, V> { impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn new<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self pub fn for_child(child: impl Element<V>, region_id: usize) -> Self {
Self {
child: child.into_any(),
region_id,
cursor_style: None,
handlers: Default::default(),
notify_on_hover: false,
notify_on_click: false,
hoverable: false,
above: false,
padding: Default::default(),
_tag: PhantomData,
}
}
pub fn new<E, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
where where
D: Element<V>, E: Element<V>,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D, F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
{ {
let mut mouse_state = cx.mouse_state::<Tag>(region_id); let mut mouse_state = cx.mouse_state::<Tag>(region_id);
let child = render_child(&mut mouse_state, cx).into_any(); let child = render_child(&mut mouse_state, cx).into_any();

View file

@ -68,6 +68,8 @@ pub struct Workspace {
pub breadcrumbs: Interactive<ContainedText>, pub breadcrumbs: Interactive<ContainedText>,
pub disconnected_overlay: ContainedText, pub disconnected_overlay: ContainedText,
pub modal: ContainerStyle, pub modal: ContainerStyle,
pub zoomed_foreground: ContainerStyle,
pub zoomed_background: ContainerStyle,
pub notification: ContainerStyle, pub notification: ContainerStyle,
pub notifications: Notifications, pub notifications: Notifications,
pub joining_project_avatar: ImageStyle, pub joining_project_avatar: ImageStyle,

View file

@ -69,6 +69,7 @@ actions!(
SplitUp, SplitUp,
SplitRight, SplitRight,
SplitDown, SplitDown,
ToggleZoom,
] ]
); );
@ -91,6 +92,7 @@ const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
pub type BackgroundActions = fn() -> &'static [(&'static str, &'static dyn Action)]; pub type BackgroundActions = fn() -> &'static [(&'static str, &'static dyn Action)];
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.add_action(Pane::toggle_zoom);
cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
pane.activate_item(action.0, true, true, cx); pane.activate_item(action.0, true, true, cx);
}); });
@ -132,12 +134,15 @@ pub enum Event {
Split(SplitDirection), Split(SplitDirection),
ChangeItemTitle, ChangeItemTitle,
Focus, Focus,
ZoomIn,
ZoomOut,
} }
pub struct Pane { pub struct Pane {
items: Vec<Box<dyn ItemHandle>>, items: Vec<Box<dyn ItemHandle>>,
activation_history: Vec<usize>, activation_history: Vec<usize>,
is_active: bool, is_active: bool,
zoomed: bool,
active_item_index: usize, active_item_index: usize,
last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>, last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>,
autoscroll: bool, autoscroll: bool,
@ -236,6 +241,7 @@ impl Pane {
items: Vec::new(), items: Vec::new(),
activation_history: Vec::new(), activation_history: Vec::new(),
is_active: true, is_active: true,
zoomed: false,
active_item_index: 0, active_item_index: 0,
last_focused_view_by_item: Default::default(), last_focused_view_by_item: Default::default(),
autoscroll: false, autoscroll: false,
@ -655,6 +661,14 @@ impl Pane {
self.items.iter().position(|i| i.id() == item.id()) self.items.iter().position(|i| i.id() == item.id())
} }
pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
if self.zoomed {
cx.emit(Event::ZoomOut);
} else {
cx.emit(Event::ZoomIn);
}
}
pub fn activate_item( pub fn activate_item(
&mut self, &mut self,
index: usize, index: usize,
@ -1546,6 +1560,15 @@ impl Pane {
.with_background_color(background) .with_background_color(background)
.into_any() .into_any()
} }
pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
self.zoomed = zoomed;
cx.notify();
}
pub fn is_zoomed(&self) -> bool {
self.zoomed
}
} }
impl Entity for Pane { impl Entity for Pane {

View file

@ -142,6 +142,12 @@ impl Member {
match self { match self {
Member::Pane(pane) => { Member::Pane(pane) => {
let pane_element = if pane.read(cx).is_zoomed() {
Empty::new().into_any()
} else {
ChildView::new(pane, cx).into_any()
};
let leader = follower_states let leader = follower_states
.iter() .iter()
.find_map(|(leader_id, follower_states)| { .find_map(|(leader_id, follower_states)| {
@ -258,7 +264,7 @@ impl Member {
}; };
Stack::new() Stack::new()
.with_child(ChildView::new(pane, cx).contained().with_border(border)) .with_child(pane_element.contained().with_border(border))
.with_children(leader_status_box) .with_children(leader_status_box)
.into_any() .into_any()
} }

View file

@ -441,6 +441,7 @@ pub struct Workspace {
weak_self: WeakViewHandle<Self>, weak_self: WeakViewHandle<Self>,
remote_entity_subscription: Option<client::Subscription>, remote_entity_subscription: Option<client::Subscription>,
modal: Option<AnyViewHandle>, modal: Option<AnyViewHandle>,
zoomed: Option<AnyViewHandle>,
center: PaneGroup, center: PaneGroup,
left_dock: ViewHandle<Dock>, left_dock: ViewHandle<Dock>,
bottom_dock: ViewHandle<Dock>, bottom_dock: ViewHandle<Dock>,
@ -627,8 +628,9 @@ impl Workspace {
]; ];
let mut this = Workspace { let mut this = Workspace {
modal: None,
weak_self: weak_handle.clone(), weak_self: weak_handle.clone(),
modal: None,
zoomed: None,
center: PaneGroup::new(center_pane.clone()), center: PaneGroup::new(center_pane.clone()),
panes: vec![center_pane.clone()], panes: vec![center_pane.clone()],
panes_by_item: Default::default(), panes_by_item: Default::default(),
@ -1303,6 +1305,16 @@ impl Workspace {
} }
} }
pub fn zoom_in(&mut self, view: AnyViewHandle, cx: &mut ViewContext<Self>) {
self.zoomed = Some(view);
cx.notify();
}
pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
self.zoomed.take();
cx.notify();
}
pub fn items<'a>( pub fn items<'a>(
&'a self, &'a self,
cx: &'a AppContext, cx: &'a AppContext,
@ -1685,6 +1697,16 @@ impl Workspace {
pane::Event::Focus => { pane::Event::Focus => {
self.handle_pane_focused(pane.clone(), cx); self.handle_pane_focused(pane.clone(), cx);
} }
pane::Event::ZoomIn => {
pane.update(cx, |pane, cx| pane.set_zoomed(true, cx));
self.zoom_in(pane.into_any(), cx);
}
pane::Event::ZoomOut => {
if self.zoomed.as_ref().map_or(false, |zoomed| *zoomed == pane) {
pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
self.zoom_out(cx);
}
}
} }
self.serialize_workspace(cx); self.serialize_workspace(cx);
@ -2735,6 +2757,21 @@ impl View for Workspace {
}) })
.with_child(Overlay::new( .with_child(Overlay::new(
Stack::new() Stack::new()
.with_children(self.zoomed.as_ref().map(|zoomed| {
enum ZoomBackground {}
ChildView::new(zoomed, cx)
.contained()
.with_style(theme.workspace.zoomed_foreground)
.aligned()
.contained()
.with_style(theme.workspace.zoomed_background)
.mouse::<ZoomBackground>(0)
.capture_all()
.on_down(MouseButton::Left, |_, this: &mut Self, cx| {
this.zoom_out(cx);
})
}))
.with_children(self.modal.as_ref().map(|modal| { .with_children(self.modal.as_ref().map(|modal| {
ChildView::new(modal, cx) ChildView::new(modal, cx)
.contained() .contained()

View file

@ -118,6 +118,15 @@ export default function workspace(colorScheme: ColorScheme) {
}, },
cursor: "Arrow", cursor: "Arrow",
}, },
zoomedBackground: {
padding: 10,
cursor: "Arrow",
background: withOpacity(background(colorScheme.lowest), 0.5)
},
zoomedForeground: {
shadow: colorScheme.modalShadow,
border: border(colorScheme.highest, { overlay: true }),
},
dock: { dock: {
initialSize: 240, initialSize: 240,
border: border(layer, { left: true, right: true }), border: border(layer, { left: true, right: true }),