Merge branch 'main' into focus-handlers-on-draw
This commit is contained in:
commit
4be84f3db0
108 changed files with 4470 additions and 3168 deletions
|
@ -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,
|
||||
|
@ -41,7 +41,7 @@ pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
|
|||
}
|
||||
|
||||
pub trait PanelHandle: Send + Sync {
|
||||
fn entity_id(&self) -> EntityId;
|
||||
fn panel_id(&self) -> EntityId;
|
||||
fn persistent_name(&self) -> &'static str;
|
||||
fn position(&self, cx: &WindowContext) -> DockPosition;
|
||||
fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool;
|
||||
|
@ -62,7 +62,7 @@ impl<T> PanelHandle for View<T>
|
|||
where
|
||||
T: Panel,
|
||||
{
|
||||
fn entity_id(&self) -> EntityId {
|
||||
fn panel_id(&self) -> EntityId {
|
||||
Entity::entity_id(self)
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ pub struct Dock {
|
|||
is_open: bool,
|
||||
active_panel_index: usize,
|
||||
focus_handle: FocusHandle,
|
||||
focus_subscription: Subscription,
|
||||
_focus_subscription: Subscription,
|
||||
}
|
||||
|
||||
impl FocusableView for Dock {
|
||||
|
@ -187,7 +187,6 @@ struct PanelEntry {
|
|||
|
||||
pub struct PanelButtons {
|
||||
dock: View<Dock>,
|
||||
workspace: WeakView<Workspace>,
|
||||
}
|
||||
|
||||
impl Dock {
|
||||
|
@ -204,7 +203,7 @@ impl Dock {
|
|||
active_panel_index: 0,
|
||||
is_open: false,
|
||||
focus_handle,
|
||||
focus_subscription,
|
||||
_focus_subscription: focus_subscription,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,7 +260,7 @@ impl Dock {
|
|||
|
||||
pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
|
||||
for entry in &mut self.panel_entries {
|
||||
if entry.panel.entity_id() == panel.entity_id() {
|
||||
if entry.panel.panel_id() == panel.entity_id() {
|
||||
if zoomed != entry.panel.is_zoomed(cx) {
|
||||
entry.panel.set_zoomed(zoomed, cx);
|
||||
}
|
||||
|
@ -309,7 +308,7 @@ impl Dock {
|
|||
|
||||
let was_visible = this.is_open()
|
||||
&& this.visible_panel().map_or(false, |active_panel| {
|
||||
active_panel.entity_id() == Entity::entity_id(&panel)
|
||||
active_panel.panel_id() == Entity::entity_id(&panel)
|
||||
});
|
||||
|
||||
this.remove_panel(&panel, cx);
|
||||
|
@ -351,7 +350,7 @@ impl Dock {
|
|||
if let Some(ix) = this
|
||||
.panel_entries
|
||||
.iter()
|
||||
.position(|entry| entry.panel.entity_id() == Entity::entity_id(&panel))
|
||||
.position(|entry| entry.panel.panel_id() == Entity::entity_id(&panel))
|
||||
{
|
||||
this.set_open(true, cx);
|
||||
this.activate_panel(ix, cx);
|
||||
|
@ -361,7 +360,7 @@ impl Dock {
|
|||
PanelEvent::Close => {
|
||||
if this
|
||||
.visible_panel()
|
||||
.map_or(false, |p| p.entity_id() == Entity::entity_id(&panel))
|
||||
.map_or(false, |p| p.panel_id() == Entity::entity_id(&panel))
|
||||
{
|
||||
this.set_open(false, cx);
|
||||
}
|
||||
|
@ -389,7 +388,7 @@ impl Dock {
|
|||
if let Some(panel_ix) = self
|
||||
.panel_entries
|
||||
.iter()
|
||||
.position(|entry| entry.panel.entity_id() == Entity::entity_id(panel))
|
||||
.position(|entry| entry.panel.panel_id() == Entity::entity_id(panel))
|
||||
{
|
||||
if panel_ix == self.active_panel_index {
|
||||
self.active_panel_index = 0;
|
||||
|
@ -450,7 +449,7 @@ impl Dock {
|
|||
pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
|
||||
self.panel_entries
|
||||
.iter()
|
||||
.find(|entry| entry.panel.entity_id() == panel.entity_id())
|
||||
.find(|entry| entry.panel.panel_id() == panel.panel_id())
|
||||
.map(|entry| entry.panel.size(cx))
|
||||
}
|
||||
|
||||
|
@ -487,62 +486,72 @@ impl Render for Dock {
|
|||
if let Some(entry) = self.visible_entry() {
|
||||
let size = entry.panel.size(cx);
|
||||
|
||||
let mut pre_resize_handle = None;
|
||||
let mut post_resize_handle = None;
|
||||
let position = self.position;
|
||||
let handler = div()
|
||||
let mut handle = 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(DraggedDock(position), |dock, cx| {
|
||||
cx.build_view(|_| dock.clone())
|
||||
})
|
||||
.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)
|
||||
}
|
||||
}));
|
||||
}))
|
||||
.z_index(1);
|
||||
|
||||
const HANDLE_SIZE: Pixels = Pixels(6.);
|
||||
|
||||
match self.position() {
|
||||
DockPosition::Left => {
|
||||
post_resize_handle = Some(handler.w_1().h_full().cursor_col_resize())
|
||||
handle = handle
|
||||
.absolute()
|
||||
.right(px(0.))
|
||||
.top(px(0.))
|
||||
.h_full()
|
||||
.w(HANDLE_SIZE)
|
||||
.cursor_col_resize();
|
||||
}
|
||||
DockPosition::Bottom => {
|
||||
pre_resize_handle = Some(handler.w_full().h_1().cursor_row_resize())
|
||||
handle = handle
|
||||
.absolute()
|
||||
.top(px(0.))
|
||||
.left(px(0.))
|
||||
.w_full()
|
||||
.h(HANDLE_SIZE)
|
||||
.cursor_row_resize();
|
||||
}
|
||||
DockPosition::Right => {
|
||||
pre_resize_handle = Some(handler.w_full().h_1().cursor_col_resize())
|
||||
handle = handle
|
||||
.absolute()
|
||||
.top(px(0.))
|
||||
.left(px(0.))
|
||||
.w_full()
|
||||
.h(HANDLE_SIZE)
|
||||
.cursor_col_resize();
|
||||
}
|
||||
}
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.map(|this| match self.position().axis() {
|
||||
Axis::Horizontal => this.w(px(size)).h_full(),
|
||||
Axis::Vertical => this.h(px(size)).w_full(),
|
||||
Axis::Horizontal => this.w(px(size)).h_full().flex_row(),
|
||||
Axis::Vertical => this.h(px(size)).w_full().flex_col(),
|
||||
})
|
||||
.map(|this| match self.position() {
|
||||
DockPosition::Left => this.border_r(),
|
||||
DockPosition::Right => this.border_l(),
|
||||
DockPosition::Bottom => this.border_t(),
|
||||
})
|
||||
.children(pre_resize_handle)
|
||||
.child(entry.panel.to_any())
|
||||
.children(post_resize_handle)
|
||||
.child(
|
||||
div()
|
||||
.map(|this| match self.position().axis() {
|
||||
Axis::Horizontal => this.min_w(px(size)).h_full(),
|
||||
Axis::Vertical => this.min_h(px(size)).w_full(),
|
||||
})
|
||||
.child(entry.panel.to_any()),
|
||||
)
|
||||
.child(handle)
|
||||
} else {
|
||||
div()
|
||||
}
|
||||
|
@ -550,166 +559,12 @@ impl Render for Dock {
|
|||
}
|
||||
|
||||
impl PanelButtons {
|
||||
pub fn new(
|
||||
dock: View<Dock>,
|
||||
workspace: WeakView<Workspace>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
pub fn new(dock: View<Dock>, cx: &mut ViewContext<Self>) -> Self {
|
||||
cx.observe(&dock, |_, _, cx| cx.notify()).detach();
|
||||
Self { dock, workspace }
|
||||
Self { dock }
|
||||
}
|
||||
}
|
||||
|
||||
// impl Render for PanelButtons {
|
||||
// type Element = ();
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
// todo!("")
|
||||
// }
|
||||
|
||||
// fn ui_name() -> &'static str {
|
||||
// "PanelButtons"
|
||||
// }
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
// let theme = &settings::get::<ThemeSettings>(cx).theme;
|
||||
// let tooltip_style = theme.tooltip.clone();
|
||||
// let theme = &theme.workspace.status_bar.panel_buttons;
|
||||
// let button_style = theme.button.clone();
|
||||
// let dock = self.dock.read(cx);
|
||||
// let active_ix = dock.active_panel_index;
|
||||
// let is_open = dock.is_open;
|
||||
// let dock_position = dock.position;
|
||||
// let group_style = match dock_position {
|
||||
// DockPosition::Left => theme.group_left,
|
||||
// DockPosition::Bottom => theme.group_bottom,
|
||||
// DockPosition::Right => theme.group_right,
|
||||
// };
|
||||
// let menu_corner = match dock_position {
|
||||
// DockPosition::Left => AnchorCorner::BottomLeft,
|
||||
// DockPosition::Bottom | DockPosition::Right => AnchorCorner::BottomRight,
|
||||
// };
|
||||
|
||||
// let panels = dock
|
||||
// .panel_entries
|
||||
// .iter()
|
||||
// .map(|item| (item.panel.clone(), item.context_menu.clone()))
|
||||
// .collect::<Vec<_>>();
|
||||
// Flex::row()
|
||||
// .with_children(panels.into_iter().enumerate().filter_map(
|
||||
// |(panel_ix, (view, context_menu))| {
|
||||
// let icon_path = view.icon_path(cx)?;
|
||||
// let is_active = is_open && panel_ix == active_ix;
|
||||
// let (tooltip, tooltip_action) = if is_active {
|
||||
// (
|
||||
// format!("Close {} dock", dock_position.to_label()),
|
||||
// Some(match dock_position {
|
||||
// DockPosition::Left => crate::ToggleLeftDock.boxed_clone(),
|
||||
// DockPosition::Bottom => crate::ToggleBottomDock.boxed_clone(),
|
||||
// DockPosition::Right => crate::ToggleRightDock.boxed_clone(),
|
||||
// }),
|
||||
// )
|
||||
// } else {
|
||||
// view.icon_tooltip(cx)
|
||||
// };
|
||||
// Some(
|
||||
// Stack::new()
|
||||
// .with_child(
|
||||
// MouseEventHandler::new::<Self, _>(panel_ix, cx, |state, cx| {
|
||||
// let style = button_style.in_state(is_active);
|
||||
|
||||
// let style = style.style_for(state);
|
||||
// Flex::row()
|
||||
// .with_child(
|
||||
// Svg::new(icon_path)
|
||||
// .with_color(style.icon_color)
|
||||
// .constrained()
|
||||
// .with_width(style.icon_size)
|
||||
// .aligned(),
|
||||
// )
|
||||
// .with_children(if let Some(label) = view.icon_label(cx) {
|
||||
// Some(
|
||||
// Label::new(label, style.label.text.clone())
|
||||
// .contained()
|
||||
// .with_style(style.label.container)
|
||||
// .aligned(),
|
||||
// )
|
||||
// } else {
|
||||
// None
|
||||
// })
|
||||
// .constrained()
|
||||
// .with_height(style.icon_size)
|
||||
// .contained()
|
||||
// .with_style(style.container)
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .on_click(MouseButton::Left, {
|
||||
// let tooltip_action =
|
||||
// tooltip_action.as_ref().map(|action| action.boxed_clone());
|
||||
// move |_, this, cx| {
|
||||
// if let Some(tooltip_action) = &tooltip_action {
|
||||
// let window = cx.window();
|
||||
// let view_id = this.workspace.id();
|
||||
// let tooltip_action = tooltip_action.boxed_clone();
|
||||
// cx.spawn(|_, mut cx| async move {
|
||||
// window.dispatch_action(
|
||||
// view_id,
|
||||
// &*tooltip_action,
|
||||
// &mut cx,
|
||||
// );
|
||||
// })
|
||||
// .detach();
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .on_click(MouseButton::Right, {
|
||||
// let view = view.clone();
|
||||
// let menu = context_menu.clone();
|
||||
// move |_, _, cx| {
|
||||
// const POSITIONS: [DockPosition; 3] = [
|
||||
// DockPosition::Left,
|
||||
// DockPosition::Right,
|
||||
// DockPosition::Bottom,
|
||||
// ];
|
||||
|
||||
// menu.update(cx, |menu, cx| {
|
||||
// let items = POSITIONS
|
||||
// .into_iter()
|
||||
// .filter(|position| {
|
||||
// *position != dock_position
|
||||
// && view.position_is_valid(*position, cx)
|
||||
// })
|
||||
// .map(|position| {
|
||||
// let view = view.clone();
|
||||
// ContextMenuItem::handler(
|
||||
// format!("Dock {}", position.to_label()),
|
||||
// move |cx| view.set_position(position, cx),
|
||||
// )
|
||||
// })
|
||||
// .collect();
|
||||
// menu.show(Default::default(), menu_corner, items, cx);
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// .with_tooltip::<Self>(
|
||||
// panel_ix,
|
||||
// tooltip,
|
||||
// tooltip_action,
|
||||
// tooltip_style.clone(),
|
||||
// cx,
|
||||
// ),
|
||||
// )
|
||||
// .with_child(ChildView::new(&context_menu, cx)),
|
||||
// )
|
||||
// },
|
||||
// ))
|
||||
// .contained()
|
||||
// .with_style(group_style)
|
||||
// .into_any()
|
||||
// }
|
||||
// }
|
||||
|
||||
// here be kittens
|
||||
impl Render for PanelButtons {
|
||||
type Element = Div;
|
||||
|
||||
|
|
|
@ -920,7 +920,7 @@ pub mod test {
|
|||
impl EventEmitter<ItemEvent> for TestItem {}
|
||||
|
||||
impl FocusableView for TestItem {
|
||||
fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
|
||||
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
@ -942,8 +942,8 @@ pub mod test {
|
|||
fn tab_content(
|
||||
&self,
|
||||
detail: Option<usize>,
|
||||
selected: bool,
|
||||
cx: &ui::prelude::WindowContext,
|
||||
_selected: bool,
|
||||
_cx: &ui::prelude::WindowContext,
|
||||
) -> AnyElement {
|
||||
self.tab_detail.set(detail);
|
||||
gpui::div().into_any_element()
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{
|
|||
use ui::{h_stack, v_stack};
|
||||
|
||||
pub trait ModalView: ManagedView {
|
||||
fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||
fn on_before_dismiss(&mut self, _: &mut ViewContext<Self>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ impl<V: ModalView> ModalViewHandle for View<V> {
|
|||
|
||||
pub struct ActiveModal {
|
||||
modal: Box<dyn ModalViewHandle>,
|
||||
subscription: Subscription,
|
||||
_subscription: Subscription,
|
||||
previous_focus_handle: Option<FocusHandle>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl ModalLayer {
|
|||
{
|
||||
self.active_modal = Some(ActiveModal {
|
||||
modal: Box::new(new_modal.clone()),
|
||||
subscription: cx.subscribe(&new_modal, |this, modal, _: &DismissEvent, cx| {
|
||||
_subscription: cx.subscribe(&new_modal, |this, _, _: &DismissEvent, cx| {
|
||||
this.hide_modal(cx);
|
||||
}),
|
||||
previous_focus_handle: cx.focused(),
|
||||
|
@ -116,7 +116,7 @@ impl Render for ModalLayer {
|
|||
.size_full()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.z_index(400)
|
||||
.z_index(169)
|
||||
.child(
|
||||
v_stack()
|
||||
.h(px(0.0))
|
||||
|
|
|
@ -104,12 +104,9 @@ impl Workspace {
|
|||
})
|
||||
{
|
||||
let notification = build_notification(cx);
|
||||
cx.subscribe(
|
||||
¬ification,
|
||||
move |this, handle, event: &DismissEvent, cx| {
|
||||
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
|
||||
.push((type_id, id, Box::new(notification)));
|
||||
|
@ -173,21 +170,15 @@ impl Workspace {
|
|||
|
||||
pub mod simple_message_notification {
|
||||
use gpui::{
|
||||
div, AnyElement, AppContext, DismissEvent, Div, EventEmitter, InteractiveElement,
|
||||
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, TextStyle,
|
||||
ViewContext,
|
||||
div, DismissEvent, Div, EventEmitter, InteractiveElement, ParentElement, Render,
|
||||
SharedString, StatefulInteractiveElement, Styled, ViewContext,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use ui::prelude::*;
|
||||
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
|
||||
|
||||
enum NotificationMessage {
|
||||
Text(SharedString),
|
||||
Element(fn(TextStyle, &AppContext) -> AnyElement),
|
||||
}
|
||||
|
||||
pub struct MessageNotification {
|
||||
message: NotificationMessage,
|
||||
message: SharedString,
|
||||
on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>)>>,
|
||||
click_message: Option<SharedString>,
|
||||
}
|
||||
|
@ -200,23 +191,12 @@ pub mod simple_message_notification {
|
|||
S: Into<SharedString>,
|
||||
{
|
||||
Self {
|
||||
message: NotificationMessage::Text(message.into()),
|
||||
message: message.into(),
|
||||
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<SharedString>,
|
||||
|
@ -248,18 +228,13 @@ pub mod simple_message_notification {
|
|||
.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().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, event, cx| this.dismiss(cx))),
|
||||
.on_click(cx.listener(|this, _, cx| this.dismiss(cx))),
|
||||
),
|
||||
)
|
||||
.children(self.click_message.iter().map(|message| {
|
||||
|
|
|
@ -7,10 +7,11 @@ use crate::{
|
|||
use anyhow::Result;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
use gpui::{
|
||||
actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
|
||||
AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
|
||||
FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, Render,
|
||||
ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AppContext,
|
||||
AsyncWindowContext, DismissEvent, Div, DragMoveEvent, EntityId, EventEmitter, FocusHandle,
|
||||
Focusable, FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel,
|
||||
Render, ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakView,
|
||||
WindowContext,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
|
@ -28,8 +29,8 @@ use std::{
|
|||
use theme::ThemeSettings;
|
||||
|
||||
use ui::{
|
||||
h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize,
|
||||
Indicator, Label, Tab, TabBar, TabPosition, Tooltip,
|
||||
prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize, Indicator, Label,
|
||||
Tab, TabBar, TabPosition, Tooltip,
|
||||
};
|
||||
use ui::{v_stack, ContextMenu};
|
||||
use util::{maybe, truncate_and_remove_front, ResultExt};
|
||||
|
@ -164,11 +165,6 @@ impl fmt::Debug for Event {
|
|||
}
|
||||
}
|
||||
|
||||
struct FocusedView {
|
||||
view: AnyWeakView,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
pub struct Pane {
|
||||
focus_handle: FocusHandle,
|
||||
items: Vec<Box<dyn ItemHandle>>,
|
||||
|
@ -184,10 +180,11 @@ pub struct Pane {
|
|||
// tab_context_menu: ViewHandle<ContextMenu>,
|
||||
workspace: WeakView<Workspace>,
|
||||
project: Model<Project>,
|
||||
drag_split_direction: Option<SplitDirection>,
|
||||
// can_drop: Rc<dyn Fn(&DragAndDrop<Workspace>, &WindowContext) -> bool>,
|
||||
can_split: bool,
|
||||
// render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
|
||||
subscriptions: Vec<Subscription>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
tab_bar_scroll_handle: ScrollHandle,
|
||||
}
|
||||
|
||||
|
@ -231,6 +228,7 @@ pub struct NavigationEntry {
|
|||
pub timestamp: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DraggedTab {
|
||||
pub pane: View<Pane>,
|
||||
pub ix: usize,
|
||||
|
@ -365,6 +363,7 @@ impl Pane {
|
|||
new_item_menu: None,
|
||||
split_item_menu: None,
|
||||
tab_bar_scroll_handle: ScrollHandle::new(),
|
||||
drag_split_direction: None,
|
||||
// tab_bar_context_menu: TabBarContextMenu {
|
||||
// kind: TabBarContextMenuKind::New,
|
||||
// handle: context_menu,
|
||||
|
@ -431,14 +430,10 @@ impl Pane {
|
|||
// })
|
||||
// .into_any()
|
||||
// }),
|
||||
subscriptions,
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn workspace(&self) -> &WeakView<Workspace> {
|
||||
&self.workspace
|
||||
}
|
||||
|
||||
pub fn has_focus(&self, cx: &WindowContext) -> bool {
|
||||
// todo!(); // inline this manually
|
||||
self.focus_handle.contains_focused(cx)
|
||||
|
@ -1467,21 +1462,6 @@ impl Pane {
|
|||
let label = item.tab_content(Some(detail), is_active, cx);
|
||||
let close_side = &ItemSettings::get_global(cx).close_position;
|
||||
|
||||
let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index {
|
||||
false => (
|
||||
cx.theme().colors().text_muted,
|
||||
cx.theme().colors().tab_inactive_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
),
|
||||
true => (
|
||||
cx.theme().colors().text,
|
||||
cx.theme().colors().tab_active_background,
|
||||
cx.theme().colors().element_hover,
|
||||
cx.theme().colors().element_active,
|
||||
),
|
||||
};
|
||||
|
||||
let indicator = maybe!({
|
||||
let indicator_color = match (item.has_conflict(cx), item.is_dirty(cx)) {
|
||||
(true, _) => Color::Warning,
|
||||
|
@ -1497,56 +1477,57 @@ impl Pane {
|
|||
let is_last_item = ix == self.items.len() - 1;
|
||||
let position_relative_to_active_item = ix.cmp(&self.active_item_index);
|
||||
|
||||
let tab =
|
||||
Tab::new(ix)
|
||||
.position(if is_first_item {
|
||||
TabPosition::First
|
||||
} else if is_last_item {
|
||||
TabPosition::Last
|
||||
} else {
|
||||
TabPosition::Middle(position_relative_to_active_item)
|
||||
})
|
||||
.close_side(match close_side {
|
||||
ClosePosition::Left => ui::TabCloseSide::Start,
|
||||
ClosePosition::Right => ui::TabCloseSide::End,
|
||||
})
|
||||
.selected(is_active)
|
||||
.on_click(cx.listener(move |pane: &mut Self, event, cx| {
|
||||
pane.activate_item(ix, true, true, cx)
|
||||
}))
|
||||
.on_drag({
|
||||
let pane = cx.view().clone();
|
||||
move |cx| {
|
||||
cx.build_view(|cx| DraggedTab {
|
||||
pane: pane.clone(),
|
||||
detail,
|
||||
item_id,
|
||||
is_active,
|
||||
ix,
|
||||
})
|
||||
}
|
||||
})
|
||||
.drag_over::<DraggedTab>(|tab| tab.bg(cx.theme().colors().tab_active_background))
|
||||
.on_drop(
|
||||
cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
|
||||
this.handle_tab_drop(dragged_tab, ix, cx)
|
||||
}),
|
||||
)
|
||||
.when_some(item.tab_tooltip_text(cx), |tab, text| {
|
||||
tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
|
||||
})
|
||||
.start_slot::<Indicator>(indicator)
|
||||
.end_slot(
|
||||
IconButton::new("close tab", Icon::Close)
|
||||
.icon_color(Color::Muted)
|
||||
.size(ButtonSize::None)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.on_click(cx.listener(move |pane, _, cx| {
|
||||
pane.close_item_by_id(item_id, SaveIntent::Close, cx)
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
)
|
||||
.child(label);
|
||||
let tab = Tab::new(ix)
|
||||
.position(if is_first_item {
|
||||
TabPosition::First
|
||||
} else if is_last_item {
|
||||
TabPosition::Last
|
||||
} else {
|
||||
TabPosition::Middle(position_relative_to_active_item)
|
||||
})
|
||||
.close_side(match close_side {
|
||||
ClosePosition::Left => ui::TabCloseSide::Start,
|
||||
ClosePosition::Right => ui::TabCloseSide::End,
|
||||
})
|
||||
.selected(is_active)
|
||||
.on_click(
|
||||
cx.listener(move |pane: &mut Self, _, cx| pane.activate_item(ix, true, true, cx)),
|
||||
)
|
||||
.on_drag(
|
||||
DraggedTab {
|
||||
pane: cx.view().clone(),
|
||||
detail,
|
||||
item_id,
|
||||
is_active,
|
||||
ix,
|
||||
},
|
||||
|tab, cx| cx.build_view(|_| tab.clone()),
|
||||
)
|
||||
.drag_over::<DraggedTab>(|tab| tab.bg(cx.theme().colors().tab_active_background))
|
||||
.drag_over::<ProjectEntryId>(|tab| tab.bg(gpui::red()))
|
||||
.on_drop(cx.listener(move |this, dragged_tab: &DraggedTab, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_tab_drop(dragged_tab, ix, cx)
|
||||
}))
|
||||
.on_drop(cx.listener(move |this, entry_id: &ProjectEntryId, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_project_entry_drop(entry_id, cx)
|
||||
}))
|
||||
.when_some(item.tab_tooltip_text(cx), |tab, text| {
|
||||
tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
|
||||
})
|
||||
.start_slot::<Indicator>(indicator)
|
||||
.end_slot(
|
||||
IconButton::new("close tab", Icon::Close)
|
||||
.icon_color(Color::Muted)
|
||||
.size(ButtonSize::None)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.on_click(cx.listener(move |pane, _, cx| {
|
||||
pane.close_item_by_id(item_id, SaveIntent::Close, cx)
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
)
|
||||
.child(label);
|
||||
|
||||
let single_entry_to_resolve = {
|
||||
let item_entries = self.items[ix].project_entry_ids(cx);
|
||||
|
@ -1597,7 +1578,8 @@ impl Pane {
|
|||
let view = cx.view().clone();
|
||||
move |_, cx| view.update(cx, Self::navigate_backward)
|
||||
})
|
||||
.disabled(!self.can_navigate_backward()),
|
||||
.disabled(!self.can_navigate_backward())
|
||||
.tooltip(|cx| Tooltip::for_action("Go Back", &GoBack, cx)),
|
||||
)
|
||||
.start_child(
|
||||
IconButton::new("navigate_forward", Icon::ArrowRight)
|
||||
|
@ -1606,7 +1588,8 @@ impl Pane {
|
|||
let view = cx.view().clone();
|
||||
move |_, cx| view.update(cx, Self::navigate_backward)
|
||||
})
|
||||
.disabled(!self.can_navigate_forward()),
|
||||
.disabled(!self.can_navigate_forward())
|
||||
.tooltip(|cx| Tooltip::for_action("Go Forward", &GoForward, cx)),
|
||||
)
|
||||
.end_child(
|
||||
div()
|
||||
|
@ -1614,18 +1597,19 @@ impl Pane {
|
|||
IconButton::new("plus", Icon::Plus)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, _| {
|
||||
menu.action("New File", NewFile.boxed_clone())
|
||||
.action("New Terminal", NewCenterTerminal.boxed_clone())
|
||||
.action("New Search", NewSearch.boxed_clone())
|
||||
});
|
||||
cx.subscribe(&menu, |this, _, event: &DismissEvent, cx| {
|
||||
cx.subscribe(&menu, |this, _, _: &DismissEvent, cx| {
|
||||
this.focus(cx);
|
||||
this.new_item_menu = None;
|
||||
})
|
||||
.detach();
|
||||
this.new_item_menu = Some(menu);
|
||||
})),
|
||||
}))
|
||||
.tooltip(|cx| Tooltip::text("New...", cx)),
|
||||
)
|
||||
.when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
|
||||
el.child(Self::render_menu_overlay(new_item_menu))
|
||||
|
@ -1637,19 +1621,20 @@ impl Pane {
|
|||
IconButton::new("split", Icon::Split)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, _| {
|
||||
menu.action("Split Right", SplitRight.boxed_clone())
|
||||
.action("Split Left", SplitLeft.boxed_clone())
|
||||
.action("Split Up", SplitUp.boxed_clone())
|
||||
.action("Split Down", SplitDown.boxed_clone())
|
||||
});
|
||||
cx.subscribe(&menu, |this, _, event: &DismissEvent, cx| {
|
||||
cx.subscribe(&menu, |this, _, _: &DismissEvent, cx| {
|
||||
this.focus(cx);
|
||||
this.split_item_menu = None;
|
||||
})
|
||||
.detach();
|
||||
this.split_item_menu = Some(menu);
|
||||
})),
|
||||
}))
|
||||
.tooltip(|cx| Tooltip::text("Split Pane", cx)),
|
||||
)
|
||||
.when_some(self.split_item_menu.as_ref(), |el, split_item_menu| {
|
||||
el.child(Self::render_menu_overlay(split_item_menu))
|
||||
|
@ -1664,16 +1649,24 @@ impl Pane {
|
|||
)
|
||||
.child(
|
||||
div()
|
||||
.min_w_6()
|
||||
// HACK: This empty child is currently necessary to force the drop traget to appear
|
||||
// despite us setting a min width above.
|
||||
.child("")
|
||||
.h_full()
|
||||
.flex_grow()
|
||||
.drag_over::<DraggedTab>(|bar| {
|
||||
bar.bg(cx.theme().colors().tab_active_background)
|
||||
})
|
||||
.on_drop(
|
||||
cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
|
||||
this.handle_tab_drop(dragged_tab, this.items.len(), cx)
|
||||
}),
|
||||
),
|
||||
.drag_over::<ProjectEntryId>(|bar| bar.bg(gpui::red()))
|
||||
.on_drop(cx.listener(move |this, dragged_tab: &DraggedTab, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_tab_drop(dragged_tab, this.items.len(), cx)
|
||||
}))
|
||||
.on_drop(cx.listener(move |this, entry_id: &ProjectEntryId, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_project_entry_drop(entry_id, cx)
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1733,24 +1726,75 @@ impl Pane {
|
|||
self.zoomed
|
||||
}
|
||||
|
||||
fn handle_drag_move<T>(&mut self, event: &DragMoveEvent<T>, cx: &mut ViewContext<Self>) {
|
||||
let edge_width = cx.rem_size() * 8;
|
||||
let cursor = event.event.position;
|
||||
let direction = if cursor.x < event.bounds.left() + edge_width {
|
||||
Some(SplitDirection::Left)
|
||||
} else if cursor.x > event.bounds.right() - edge_width {
|
||||
Some(SplitDirection::Right)
|
||||
} else if cursor.y < event.bounds.top() + edge_width {
|
||||
Some(SplitDirection::Up)
|
||||
} else if cursor.y > event.bounds.bottom() - edge_width {
|
||||
Some(SplitDirection::Down)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if direction != self.drag_split_direction {
|
||||
self.drag_split_direction = direction;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_tab_drop(
|
||||
&mut self,
|
||||
dragged_tab: &View<DraggedTab>,
|
||||
dragged_tab: &DraggedTab,
|
||||
ix: usize,
|
||||
cx: &mut ViewContext<'_, Pane>,
|
||||
) {
|
||||
let dragged_tab = dragged_tab.read(cx);
|
||||
let mut to_pane = cx.view().clone();
|
||||
let split_direction = self.drag_split_direction;
|
||||
let item_id = dragged_tab.item_id;
|
||||
let from_pane = dragged_tab.pane.clone();
|
||||
let to_pane = cx.view().clone();
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
.update(cx, |_, cx| {
|
||||
cx.defer(move |workspace, cx| {
|
||||
if let Some(split_direction) = split_direction {
|
||||
to_pane = workspace.split_pane(to_pane, split_direction, cx);
|
||||
}
|
||||
workspace.move_item(from_pane, to_pane, item_id, ix, cx);
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn handle_project_entry_drop(
|
||||
&mut self,
|
||||
project_entry_id: &ProjectEntryId,
|
||||
cx: &mut ViewContext<'_, Pane>,
|
||||
) {
|
||||
let mut to_pane = cx.view().clone();
|
||||
let split_direction = self.drag_split_direction;
|
||||
let project_entry_id = *project_entry_id;
|
||||
self.workspace
|
||||
.update(cx, |_, cx| {
|
||||
cx.defer(move |workspace, cx| {
|
||||
if let Some(path) = workspace
|
||||
.project()
|
||||
.read(cx)
|
||||
.path_for_entry(project_entry_id, cx)
|
||||
{
|
||||
if let Some(split_direction) = split_direction {
|
||||
to_pane = workspace.split_pane(to_pane, split_direction, cx);
|
||||
}
|
||||
workspace
|
||||
.open_path(path, Some(to_pane.downgrade()), true, cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
impl FocusableView for Pane {
|
||||
|
@ -1763,7 +1807,8 @@ impl Render for Pane {
|
|||
type Element = Focusable<Div>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let this = cx.view().downgrade();
|
||||
let mut drag_target_color = cx.theme().colors().text;
|
||||
drag_target_color.a = 0.5;
|
||||
|
||||
v_stack()
|
||||
.key_context("Pane")
|
||||
|
@ -1842,16 +1887,60 @@ impl Render for Pane {
|
|||
}),
|
||||
)
|
||||
.child(self.render_tab_bar(cx))
|
||||
.child(self.toolbar.clone())
|
||||
.child(if let Some(item) = self.active_item() {
|
||||
div().flex().flex_1().child(item.to_any())
|
||||
} else {
|
||||
h_stack()
|
||||
.items_center()
|
||||
.size_full()
|
||||
.justify_center()
|
||||
.child(Label::new("Open a file or project to get started.").color(Color::Muted))
|
||||
})
|
||||
.child(
|
||||
// main content
|
||||
div()
|
||||
.flex_1()
|
||||
.relative()
|
||||
.group("")
|
||||
.on_drag_move::<DraggedTab>(cx.listener(Self::handle_drag_move))
|
||||
.on_drag_move::<ProjectEntryId>(cx.listener(Self::handle_drag_move))
|
||||
.map(|div| {
|
||||
if let Some(item) = self.active_item() {
|
||||
div.flex_col()
|
||||
.child(self.toolbar.clone())
|
||||
.child(item.to_any())
|
||||
} else {
|
||||
div.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.size_full()
|
||||
.justify_center()
|
||||
.child(
|
||||
Label::new("Open a file or project to get started.")
|
||||
.color(Color::Muted),
|
||||
)
|
||||
}
|
||||
})
|
||||
.child(
|
||||
// drag target
|
||||
div()
|
||||
.invisible()
|
||||
.absolute()
|
||||
.bg(drag_target_color)
|
||||
.group_drag_over::<DraggedTab>("", |style| style.visible())
|
||||
.group_drag_over::<ProjectEntryId>("", |style| style.visible())
|
||||
.on_drop(cx.listener(move |this, dragged_tab, cx| {
|
||||
this.handle_tab_drop(dragged_tab, this.active_item_index(), cx)
|
||||
}))
|
||||
.on_drop(cx.listener(move |this, entry_id, cx| {
|
||||
this.handle_project_entry_drop(entry_id, cx)
|
||||
}))
|
||||
.map(|div| match self.drag_split_direction {
|
||||
None => div.top_0().left_0().right_0().bottom_0(),
|
||||
Some(SplitDirection::Up) => div.top_0().left_0().right_0().h_32(),
|
||||
Some(SplitDirection::Down) => {
|
||||
div.left_0().bottom_0().right_0().h_32()
|
||||
}
|
||||
Some(SplitDirection::Left) => {
|
||||
div.top_0().left_0().bottom_0().w_32()
|
||||
}
|
||||
Some(SplitDirection::Right) => {
|
||||
div.top_0().bottom_0().right_0().w_32()
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.on_mouse_down(
|
||||
MouseButton::Navigate(NavigationDirection::Back),
|
||||
cx.listener(|pane, _, cx| {
|
||||
|
|
|
@ -16,7 +16,7 @@ const HANDLE_HITBOX_SIZE: f32 = 10.0; //todo!(change this back to 4)
|
|||
const HORIZONTAL_MIN_SIZE: f32 = 80.;
|
||||
const VERTICAL_MIN_SIZE: f32 = 100.;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone)]
|
||||
pub struct PaneGroup {
|
||||
pub(crate) root: Member,
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ impl PaneGroup {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum Member {
|
||||
Axis(PaneAxis),
|
||||
Pane(View<Pane>),
|
||||
|
@ -426,12 +426,6 @@ pub(crate) struct PaneAxis {
|
|||
pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
|
||||
}
|
||||
|
||||
impl PartialEq for PaneAxis {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PaneAxis {
|
||||
pub fn new(axis: Axis, members: Vec<Member>) -> Self {
|
||||
let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
|
||||
|
@ -816,7 +810,7 @@ mod element {
|
|||
proposed_current_pixel_change -= current_pixel_change;
|
||||
}
|
||||
|
||||
// todo!(reserialize workspace)
|
||||
// todo!(schedule serialize)
|
||||
// workspace.schedule_serialize(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -851,7 +845,7 @@ mod element {
|
|||
|
||||
cx.on_mouse_event({
|
||||
let dragged_handle = dragged_handle.clone();
|
||||
move |e: &MouseDownEvent, phase, cx| {
|
||||
move |e: &MouseDownEvent, phase, _cx| {
|
||||
if phase.bubble() && handle_bounds.contains(&e.position) {
|
||||
dragged_handle.replace(Some(ix));
|
||||
}
|
||||
|
@ -859,7 +853,7 @@ mod element {
|
|||
});
|
||||
cx.on_mouse_event(move |e: &MouseMoveEvent, phase, cx| {
|
||||
let dragged_handle = dragged_handle.borrow();
|
||||
if *dragged_handle == Some(ix) {
|
||||
if phase.bubble() && *dragged_handle == Some(ix) {
|
||||
Self::compute_resize(&flexes, e, ix, axis, axis_bounds, cx)
|
||||
}
|
||||
});
|
||||
|
@ -896,7 +890,7 @@ mod element {
|
|||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
&mut self,
|
||||
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
||||
state: &mut Self::State,
|
||||
cx: &mut ui::prelude::WindowContext,
|
||||
|
@ -912,7 +906,7 @@ mod element {
|
|||
let mut bounding_boxes = self.bounding_boxes.lock();
|
||||
bounding_boxes.clear();
|
||||
|
||||
for (ix, child) in self.children.into_iter().enumerate() {
|
||||
for (ix, child) in self.children.iter_mut().enumerate() {
|
||||
//todo!(active_pane_magnification)
|
||||
// If usign active pane magnification, need to switch to using
|
||||
// 1 for all non-active panes, and then the magnification for the
|
||||
|
@ -949,7 +943,7 @@ mod element {
|
|||
cx.with_z_index(1, |cx| {
|
||||
cx.on_mouse_event({
|
||||
let state = state.clone();
|
||||
move |e: &MouseUpEvent, phase, cx| {
|
||||
move |_: &MouseUpEvent, phase, _cx| {
|
||||
if phase.bubble() {
|
||||
state.replace(None);
|
||||
}
|
||||
|
@ -968,402 +962,4 @@ mod element {
|
|||
fn flex_values_in_bounds(flexes: &[f32]) -> bool {
|
||||
(flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() < 0.001
|
||||
}
|
||||
// // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
|
||||
|
||||
// // use gpui::{
|
||||
// // geometry::{
|
||||
// // rect::Bounds<Pixels>,
|
||||
// // vector::{vec2f, Vector2F},
|
||||
// // },
|
||||
// // json::{self, ToJson},
|
||||
// // platform::{CursorStyle, MouseButton},
|
||||
// // scene::MouseDrag,
|
||||
// // AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, Bounds<Pixels>Ext,
|
||||
// // SizeConstraint, Vector2FExt, ViewContext,
|
||||
// // };
|
||||
|
||||
// use crate::{
|
||||
// pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
|
||||
// Workspace, WorkspaceSettings,
|
||||
// };
|
||||
|
||||
// pub struct PaneAxisElement {
|
||||
// axis: Axis,
|
||||
// basis: usize,
|
||||
// active_pane_ix: Option<usize>,
|
||||
// flexes: Rc<RefCell<Vec<f32>>>,
|
||||
// children: Vec<AnyElement<Workspace>>,
|
||||
// bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
|
||||
// }
|
||||
|
||||
// impl PaneAxisElement {
|
||||
// pub fn new(
|
||||
// axis: Axis,
|
||||
// basis: usize,
|
||||
// flexes: Rc<RefCell<Vec<f32>>>,
|
||||
// bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
|
||||
// ) -> Self {
|
||||
// Self {
|
||||
// axis,
|
||||
// basis,
|
||||
// flexes,
|
||||
// bounding_boxes,
|
||||
// active_pane_ix: None,
|
||||
// children: Default::default(),
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
|
||||
// self.active_pane_ix = active_pane_ix;
|
||||
// }
|
||||
|
||||
// fn layout_children(
|
||||
// &mut self,
|
||||
// active_pane_magnification: f32,
|
||||
// constraint: SizeConstraint,
|
||||
// remaining_space: &mut f32,
|
||||
// remaining_flex: &mut f32,
|
||||
// cross_axis_max: &mut f32,
|
||||
// view: &mut Workspace,
|
||||
// cx: &mut ViewContext<Workspace>,
|
||||
// ) {
|
||||
// let flexes = self.flexes.borrow();
|
||||
// let cross_axis = self.axis.invert();
|
||||
// for (ix, child) in self.children.iter_mut().enumerate() {
|
||||
// let flex = if active_pane_magnification != 1. {
|
||||
// if let Some(active_pane_ix) = self.active_pane_ix {
|
||||
// if ix == active_pane_ix {
|
||||
// active_pane_magnification
|
||||
// } else {
|
||||
// 1.
|
||||
// }
|
||||
// } else {
|
||||
// 1.
|
||||
// }
|
||||
// } else {
|
||||
// flexes[ix]
|
||||
// };
|
||||
|
||||
// let child_size = if *remaining_flex == 0.0 {
|
||||
// *remaining_space
|
||||
// } else {
|
||||
// let space_per_flex = *remaining_space / *remaining_flex;
|
||||
// space_per_flex * flex
|
||||
// };
|
||||
|
||||
// let child_constraint = match self.axis {
|
||||
// Axis::Horizontal => SizeConstraint::new(
|
||||
// vec2f(child_size, constraint.min.y()),
|
||||
// vec2f(child_size, constraint.max.y()),
|
||||
// ),
|
||||
// Axis::Vertical => SizeConstraint::new(
|
||||
// vec2f(constraint.min.x(), child_size),
|
||||
// vec2f(constraint.max.x(), child_size),
|
||||
// ),
|
||||
// };
|
||||
// let child_size = child.layout(child_constraint, view, cx);
|
||||
// *remaining_space -= child_size.along(self.axis);
|
||||
// *remaining_flex -= flex;
|
||||
// *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn handle_resize(
|
||||
// flexes: Rc<RefCell<Vec<f32>>>,
|
||||
// axis: Axis,
|
||||
// preceding_ix: usize,
|
||||
// child_start: Vector2F,
|
||||
// drag_bounds: Bounds<Pixels>,
|
||||
// ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext<Workspace>) {
|
||||
// let size = move |ix, flexes: &[f32]| {
|
||||
// drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32)
|
||||
// };
|
||||
|
||||
// move |drag, workspace: &mut Workspace, cx| {
|
||||
// if drag.end {
|
||||
// // TODO: Clear cascading resize state
|
||||
// return;
|
||||
// }
|
||||
// let min_size = match axis {
|
||||
// Axis::Horizontal => HORIZONTAL_MIN_SIZE,
|
||||
// Axis::Vertical => VERTICAL_MIN_SIZE,
|
||||
// };
|
||||
// let mut flexes = flexes.borrow_mut();
|
||||
|
||||
// // Don't allow resizing to less than the minimum size, if elements are already too small
|
||||
// if min_size - 1. > size(preceding_ix, flexes.as_slice()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// let mut proposed_current_pixel_change = (drag.position - child_start).along(axis)
|
||||
// - size(preceding_ix, flexes.as_slice());
|
||||
|
||||
// let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
|
||||
// let flex_change = pixel_dx / drag_bounds.length_along(axis);
|
||||
// let current_target_flex = flexes[target_ix] + flex_change;
|
||||
// let next_target_flex =
|
||||
// flexes[(target_ix as isize + next) as usize] - flex_change;
|
||||
// (current_target_flex, next_target_flex)
|
||||
// };
|
||||
|
||||
// let mut successors = from_fn({
|
||||
// let forward = proposed_current_pixel_change > 0.;
|
||||
// let mut ix_offset = 0;
|
||||
// let len = flexes.len();
|
||||
// move || {
|
||||
// let result = if forward {
|
||||
// (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset)
|
||||
// } else {
|
||||
// (preceding_ix as isize - ix_offset as isize >= 0)
|
||||
// .then(|| preceding_ix - ix_offset)
|
||||
// };
|
||||
|
||||
// ix_offset += 1;
|
||||
|
||||
// result
|
||||
// }
|
||||
// });
|
||||
|
||||
// while proposed_current_pixel_change.abs() > 0. {
|
||||
// let Some(current_ix) = successors.next() else {
|
||||
// break;
|
||||
// };
|
||||
|
||||
// let next_target_size = f32::max(
|
||||
// size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
|
||||
// min_size,
|
||||
// );
|
||||
|
||||
// let current_target_size = f32::max(
|
||||
// size(current_ix, flexes.as_slice())
|
||||
// + size(current_ix + 1, flexes.as_slice())
|
||||
// - next_target_size,
|
||||
// min_size,
|
||||
// );
|
||||
|
||||
// let current_pixel_change =
|
||||
// current_target_size - size(current_ix, flexes.as_slice());
|
||||
|
||||
// let (current_target_flex, next_target_flex) =
|
||||
// flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
|
||||
|
||||
// flexes[current_ix] = current_target_flex;
|
||||
// flexes[current_ix + 1] = next_target_flex;
|
||||
|
||||
// proposed_current_pixel_change -= current_pixel_change;
|
||||
// }
|
||||
|
||||
// workspace.schedule_serialize(cx);
|
||||
// cx.notify();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Extend<AnyElement<Workspace>> for PaneAxisElement {
|
||||
// fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
|
||||
// self.children.extend(children);
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Element<Workspace> for PaneAxisElement {
|
||||
// type LayoutState = f32;
|
||||
// type PaintState = ();
|
||||
|
||||
// fn layout(
|
||||
// &mut self,
|
||||
// constraint: SizeConstraint,
|
||||
// view: &mut Workspace,
|
||||
// cx: &mut ViewContext<Workspace>,
|
||||
// ) -> (Vector2F, Self::LayoutState) {
|
||||
// debug_assert!(self.children.len() == self.flexes.borrow().len());
|
||||
|
||||
// let active_pane_magnification =
|
||||
// settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
|
||||
|
||||
// let mut remaining_flex = 0.;
|
||||
|
||||
// if active_pane_magnification != 1. {
|
||||
// let active_pane_flex = self
|
||||
// .active_pane_ix
|
||||
// .map(|_| active_pane_magnification)
|
||||
// .unwrap_or(1.);
|
||||
// remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
|
||||
// } else {
|
||||
// for flex in self.flexes.borrow().iter() {
|
||||
// remaining_flex += flex;
|
||||
// }
|
||||
// }
|
||||
|
||||
// let mut cross_axis_max: f32 = 0.0;
|
||||
// let mut remaining_space = constraint.max_along(self.axis);
|
||||
|
||||
// if remaining_space.is_infinite() {
|
||||
// panic!("flex contains flexible children but has an infinite constraint along the flex axis");
|
||||
// }
|
||||
|
||||
// self.layout_children(
|
||||
// active_pane_magnification,
|
||||
// constraint,
|
||||
// &mut remaining_space,
|
||||
// &mut remaining_flex,
|
||||
// &mut cross_axis_max,
|
||||
// view,
|
||||
// cx,
|
||||
// );
|
||||
|
||||
// let mut size = match self.axis {
|
||||
// Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
|
||||
// Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
|
||||
// };
|
||||
|
||||
// if constraint.min.x().is_finite() {
|
||||
// size.set_x(size.x().max(constraint.min.x()));
|
||||
// }
|
||||
// if constraint.min.y().is_finite() {
|
||||
// size.set_y(size.y().max(constraint.min.y()));
|
||||
// }
|
||||
|
||||
// if size.x() > constraint.max.x() {
|
||||
// size.set_x(constraint.max.x());
|
||||
// }
|
||||
// if size.y() > constraint.max.y() {
|
||||
// size.set_y(constraint.max.y());
|
||||
// }
|
||||
|
||||
// (size, remaining_space)
|
||||
// }
|
||||
|
||||
// fn paint(
|
||||
// &mut self,
|
||||
// bounds: Bounds<Pixels>,
|
||||
// visible_bounds: Bounds<Pixels>,
|
||||
// remaining_space: &mut Self::LayoutState,
|
||||
// view: &mut Workspace,
|
||||
// cx: &mut ViewContext<Workspace>,
|
||||
// ) -> Self::PaintState {
|
||||
// let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
|
||||
// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
// let overflowing = *remaining_space < 0.;
|
||||
// if overflowing {
|
||||
// cx.scene().push_layer(Some(visible_bounds));
|
||||
// }
|
||||
|
||||
// let mut child_origin = bounds.origin();
|
||||
|
||||
// let mut bounding_boxes = self.bounding_boxes.borrow_mut();
|
||||
// bounding_boxes.clear();
|
||||
|
||||
// let mut children_iter = self.children.iter_mut().enumerate().peekable();
|
||||
// while let Some((ix, child)) = children_iter.next() {
|
||||
// let child_start = child_origin.clone();
|
||||
// child.paint(child_origin, visible_bounds, view, cx);
|
||||
|
||||
// bounding_boxes.push(Some(Bounds<Pixels>::new(child_origin, child.size())));
|
||||
|
||||
// match self.axis {
|
||||
// Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
|
||||
// Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
|
||||
// }
|
||||
|
||||
// if can_resize && children_iter.peek().is_some() {
|
||||
// cx.scene().push_stacking_context(None, None);
|
||||
|
||||
// let handle_origin = match self.axis {
|
||||
// Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
|
||||
// Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
|
||||
// };
|
||||
|
||||
// let handle_bounds = match self.axis {
|
||||
// Axis::Horizontal => Bounds<Pixels>::new(
|
||||
// handle_origin,
|
||||
// vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
|
||||
// ),
|
||||
// Axis::Vertical => Bounds<Pixels>::new(
|
||||
// handle_origin,
|
||||
// vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
|
||||
// ),
|
||||
// };
|
||||
|
||||
// let style = match self.axis {
|
||||
// Axis::Horizontal => CursorStyle::ResizeLeftRight,
|
||||
// Axis::Vertical => CursorStyle::ResizeUpDown,
|
||||
// };
|
||||
|
||||
// cx.scene().push_cursor_region(CursorRegion {
|
||||
// bounds: handle_bounds,
|
||||
// style,
|
||||
// });
|
||||
|
||||
// enum ResizeHandle {}
|
||||
// let mut mouse_region = MouseRegion::new::<ResizeHandle>(
|
||||
// cx.view_id(),
|
||||
// self.basis + ix,
|
||||
// handle_bounds,
|
||||
// );
|
||||
// mouse_region = mouse_region
|
||||
// .on_drag(
|
||||
// MouseButton::Left,
|
||||
// Self::handle_resize(
|
||||
// self.flexes.clone(),
|
||||
// self.axis,
|
||||
// ix,
|
||||
// child_start,
|
||||
// visible_bounds.clone(),
|
||||
// ),
|
||||
// )
|
||||
// .on_click(MouseButton::Left, {
|
||||
// let flexes = self.flexes.clone();
|
||||
// move |e, v: &mut Workspace, cx| {
|
||||
// if e.click_count >= 2 {
|
||||
// let mut borrow = flexes.borrow_mut();
|
||||
// *borrow = vec![1.; borrow.len()];
|
||||
// v.schedule_serialize(cx);
|
||||
// cx.notify();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// cx.scene().push_mouse_region(mouse_region);
|
||||
|
||||
// cx.scene().pop_stacking_context();
|
||||
// }
|
||||
// }
|
||||
|
||||
// if overflowing {
|
||||
// cx.scene().pop_layer();
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn rect_for_text_range(
|
||||
// &self,
|
||||
// range_utf16: Range<usize>,
|
||||
// _: Bounds<Pixels>,
|
||||
// _: Bounds<Pixels>,
|
||||
// _: &Self::LayoutState,
|
||||
// _: &Self::PaintState,
|
||||
// view: &Workspace,
|
||||
// cx: &ViewContext<Workspace>,
|
||||
// ) -> Option<Bounds<Pixels>> {
|
||||
// self.children
|
||||
// .iter()
|
||||
// .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
|
||||
// }
|
||||
|
||||
// fn debug(
|
||||
// &self,
|
||||
// bounds: Bounds<Pixels>,
|
||||
// _: &Self::LayoutState,
|
||||
// _: &Self::PaintState,
|
||||
// view: &Workspace,
|
||||
// cx: &ViewContext<Workspace>,
|
||||
// ) -> json::Value {
|
||||
// serde_json::json!({
|
||||
// "type": "PaneAxis",
|
||||
// "bounds": bounds.to_json(),
|
||||
// "axis": self.axis.to_json(),
|
||||
// "flexes": *self.flexes.borrow(),
|
||||
// "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
|
|||
cx: &mut WindowContext,
|
||||
) -> Task<Vec<Box<dyn Any + Send>>> {
|
||||
let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
|
||||
cx.spawn(|cx| async {
|
||||
cx.spawn(|_| async {
|
||||
let matches = matches.await;
|
||||
matches
|
||||
.into_iter()
|
||||
|
@ -253,7 +253,7 @@ pub trait WeakSearchableItemHandle: WeakItemHandle {
|
|||
}
|
||||
|
||||
impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn upgrade(&self, _cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.upgrade()?))
|
||||
}
|
||||
|
||||
|
|
|
@ -51,14 +51,14 @@ impl Render for StatusBar {
|
|||
}
|
||||
|
||||
impl StatusBar {
|
||||
fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
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, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
fn render_right_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
h_stack()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
|
|
|
@ -55,6 +55,12 @@ pub struct Toolbar {
|
|||
}
|
||||
|
||||
impl Toolbar {
|
||||
fn has_any_visible_items(&self) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.any(|(_item, location)| *location != ToolbarItemLocation::Hidden)
|
||||
}
|
||||
|
||||
fn left_items(&self) -> impl Iterator<Item = &dyn ToolbarItemViewHandle> {
|
||||
self.items.iter().filter_map(|(item, location)| {
|
||||
if *location == ToolbarItemLocation::PrimaryLeft {
|
||||
|
@ -74,12 +80,28 @@ impl 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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Toolbar {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
if !self.has_any_visible_items() {
|
||||
return div();
|
||||
}
|
||||
|
||||
let secondary_item = self.secondary_items().next().map(|item| item.to_any());
|
||||
|
||||
v_stack()
|
||||
.border_b()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
|
@ -87,8 +109,24 @@ impl Render for Toolbar {
|
|||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.children(self.items.iter().map(|(child, _)| child.to_any())),
|
||||
.when(self.left_items().count() > 0, |this| {
|
||||
this.child(
|
||||
h_stack()
|
||||
.flex_1()
|
||||
.justify_start()
|
||||
.children(self.left_items().map(|item| item.to_any())),
|
||||
)
|
||||
})
|
||||
.when(self.right_items().count() > 0, |this| {
|
||||
this.child(
|
||||
h_stack()
|
||||
.flex_1()
|
||||
.justify_end()
|
||||
.children(self.right_items().map(|item| item.to_any())),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.children(secondary_item)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue