Collab ui2 (#3357)
* Clickable context menus & movable panels – what will they think of next?! Release Notes: - N/A
This commit is contained in:
commit
624bd0a05a
12 changed files with 302 additions and 150 deletions
|
@ -71,11 +71,12 @@ impl EntityMap {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn lease<'a, T>(&mut self, model: &'a Model<T>) -> Lease<'a, T> {
|
pub fn lease<'a, T>(&mut self, model: &'a Model<T>) -> Lease<'a, T> {
|
||||||
self.assert_valid_context(model);
|
self.assert_valid_context(model);
|
||||||
let entity = Some(
|
let entity = Some(self.entities.remove(model.entity_id).unwrap_or_else(|| {
|
||||||
self.entities
|
panic!(
|
||||||
.remove(model.entity_id)
|
"Circular entity lease of {}. Is it already being updated?",
|
||||||
.expect("Circular entity lease. Is the entity already being updated?"),
|
std::any::type_name::<T>()
|
||||||
);
|
)
|
||||||
|
}));
|
||||||
Lease {
|
Lease {
|
||||||
model,
|
model,
|
||||||
entity,
|
entity,
|
||||||
|
|
|
@ -1124,9 +1124,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if self.hover_style.is_some() {
|
||||||
if bounds.contains_point(&mouse_position) {
|
if bounds.contains_point(&mouse_position) {
|
||||||
|
// eprintln!("div hovered {bounds:?} {mouse_position:?}");
|
||||||
style.refine(&self.hover_style);
|
style.refine(&self.hover_style);
|
||||||
|
} else {
|
||||||
|
// eprintln!("div NOT hovered {bounds:?} {mouse_position:?}");
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
if let Some(drag) = cx.active_drag.take() {
|
if let Some(drag) = cx.active_drag.take() {
|
||||||
for (state_type, group_drag_style) in &self.group_drag_over_styles {
|
for (state_type, group_drag_style) in &self.group_drag_over_styles {
|
||||||
|
|
|
@ -1205,10 +1205,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||||
|
|
||||||
InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
|
InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
|
||||||
|
|
||||||
InputEvent::MouseUp(MouseUpEvent {
|
InputEvent::MouseUp(MouseUpEvent { .. }) => {
|
||||||
button: MouseButton::Left,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
lock.synthetic_drag_counter += 1;
|
lock.synthetic_drag_counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,10 @@ impl AnyView {
|
||||||
self.model.entity_type
|
self.model.entity_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn entity_id(&self) -> EntityId {
|
||||||
|
self.model.entity_id()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn draw(
|
pub(crate) fn draw(
|
||||||
&self,
|
&self,
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod file_associations;
|
pub mod file_associations;
|
||||||
mod project_panel_settings;
|
mod project_panel_settings;
|
||||||
use settings::Settings;
|
use settings::{Settings, SettingsStore};
|
||||||
|
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor};
|
use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor};
|
||||||
|
@ -34,7 +34,7 @@ use ui::{h_stack, v_stack, IconElement, Label};
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
use util::{maybe, ResultExt, TryFutureExt};
|
use util::{maybe, ResultExt, TryFutureExt};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
dock::{DockPosition, PanelEvent},
|
dock::{DockPosition, Panel, PanelEvent},
|
||||||
Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,7 +148,6 @@ pub enum Event {
|
||||||
SplitEntry {
|
SplitEntry {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
},
|
},
|
||||||
DockPositionChanged,
|
|
||||||
Focus,
|
Focus,
|
||||||
NewSearchInDirectory {
|
NewSearchInDirectory {
|
||||||
dir_entry: Entry,
|
dir_entry: Entry,
|
||||||
|
@ -244,16 +243,17 @@ impl ProjectPanel {
|
||||||
this.update_visible_entries(None, cx);
|
this.update_visible_entries(None, cx);
|
||||||
|
|
||||||
// Update the dock position when the setting changes.
|
// Update the dock position when the setting changes.
|
||||||
// todo!()
|
let mut old_dock_position = this.position(cx);
|
||||||
// let mut old_dock_position = this.position(cx);
|
ProjectPanelSettings::register(cx);
|
||||||
// cx.observe_global::<SettingsStore, _>(move |this, cx| {
|
cx.observe_global::<SettingsStore>(move |this, cx| {
|
||||||
// let new_dock_position = this.position(cx);
|
dbg!("OLA!");
|
||||||
// if new_dock_position != old_dock_position {
|
let new_dock_position = this.position(cx);
|
||||||
// old_dock_position = new_dock_position;
|
if new_dock_position != old_dock_position {
|
||||||
// cx.emit(Event::DockPositionChanged);
|
old_dock_position = new_dock_position;
|
||||||
// }
|
cx.emit(PanelEvent::ChangePosition);
|
||||||
// })
|
}
|
||||||
// .detach();
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
this
|
this
|
||||||
});
|
});
|
||||||
|
@ -1485,7 +1485,7 @@ impl EventEmitter<Event> for ProjectPanel {}
|
||||||
|
|
||||||
impl EventEmitter<PanelEvent> for ProjectPanel {}
|
impl EventEmitter<PanelEvent> for ProjectPanel {}
|
||||||
|
|
||||||
impl workspace::dock::Panel for ProjectPanel {
|
impl Panel for ProjectPanel {
|
||||||
fn position(&self, cx: &WindowContext) -> DockPosition {
|
fn position(&self, cx: &WindowContext) -> DockPosition {
|
||||||
match ProjectPanelSettings::get_global(cx).dock {
|
match ProjectPanelSettings::get_global(cx).dock {
|
||||||
ProjectPanelDockPosition::Left => DockPosition::Left,
|
ProjectPanelDockPosition::Left => DockPosition::Left,
|
||||||
|
|
|
@ -77,6 +77,7 @@ pub fn handle_settings_file_changes(
|
||||||
});
|
});
|
||||||
cx.spawn(move |mut cx| async move {
|
cx.spawn(move |mut cx| async move {
|
||||||
while let Some(user_settings_content) = user_settings_file_rx.next().await {
|
while let Some(user_settings_content) = user_settings_file_rx.next().await {
|
||||||
|
eprintln!("settings file changed");
|
||||||
let result = cx.update_global(|store: &mut SettingsStore, cx| {
|
let result = cx.update_global(|store: &mut SettingsStore, cx| {
|
||||||
store
|
store
|
||||||
.set_user_settings(&user_settings_content, cx)
|
.set_user_settings(&user_settings_content, cx)
|
||||||
|
|
|
@ -32,7 +32,7 @@ use workspace::{
|
||||||
notifications::NotifyResultExt,
|
notifications::NotifyResultExt,
|
||||||
register_deserializable_item,
|
register_deserializable_item,
|
||||||
searchable::{SearchEvent, SearchOptions, SearchableItem},
|
searchable::{SearchEvent, SearchOptions, SearchableItem},
|
||||||
ui::{ContextMenu, Label},
|
ui::{ContextMenu, Label, ListEntry},
|
||||||
CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
|
CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ pub struct TerminalView {
|
||||||
has_new_content: bool,
|
has_new_content: bool,
|
||||||
//Currently using iTerm bell, show bell emoji in tab until input is received
|
//Currently using iTerm bell, show bell emoji in tab until input is received
|
||||||
has_bell: bool,
|
has_bell: bool,
|
||||||
context_menu: Option<View<ContextMenu>>,
|
context_menu: Option<View<ContextMenu<Self>>>,
|
||||||
blink_state: bool,
|
blink_state: bool,
|
||||||
blinking_on: bool,
|
blinking_on: bool,
|
||||||
blinking_paused: bool,
|
blinking_paused: bool,
|
||||||
|
@ -300,11 +300,10 @@ impl TerminalView {
|
||||||
position: gpui::Point<Pixels>,
|
position: gpui::Point<Pixels>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.context_menu = Some(cx.build_view(|cx| {
|
self.context_menu = Some(ContextMenu::build(cx, |menu, _| {
|
||||||
ContextMenu::new(cx)
|
menu.action(ListEntry::new(Label::new("Clear")), Box::new(Clear))
|
||||||
.entry(Label::new("Clear"), Box::new(Clear))
|
.action(
|
||||||
.entry(
|
ListEntry::new(Label::new("Close")),
|
||||||
Label::new("Close"),
|
|
||||||
Box::new(CloseActiveItem { save_intent: None }),
|
Box::new(CloseActiveItem { save_intent: None }),
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -6,42 +6,74 @@ use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHea
|
||||||
use gpui::{
|
use gpui::{
|
||||||
overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div,
|
overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div,
|
||||||
FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
|
FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
|
||||||
|
VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ContextMenu {
|
pub enum ContextMenuItem<V> {
|
||||||
items: Vec<ListItem>,
|
Separator(ListSeparator),
|
||||||
focus_handle: FocusHandle,
|
Header(ListSubHeader),
|
||||||
|
Entry(
|
||||||
|
ListEntry<ContextMenu<V>>,
|
||||||
|
Rc<dyn Fn(&mut V, &mut ViewContext<V>)>,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManagedView for ContextMenu {
|
pub struct ContextMenu<V> {
|
||||||
|
items: Vec<ContextMenuItem<V>>,
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
handle: WeakView<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: Render> ManagedView for ContextMenu<V> {
|
||||||
fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
|
fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
|
||||||
self.focus_handle.clone()
|
self.focus_handle.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenu {
|
impl<V: Render> ContextMenu<V> {
|
||||||
pub fn new(cx: &mut WindowContext) -> Self {
|
pub fn build(
|
||||||
|
cx: &mut ViewContext<V>,
|
||||||
|
f: impl FnOnce(Self, &mut ViewContext<Self>) -> Self,
|
||||||
|
) -> View<Self> {
|
||||||
|
let handle = cx.view().downgrade();
|
||||||
|
cx.build_view(|cx| {
|
||||||
|
f(
|
||||||
Self {
|
Self {
|
||||||
|
handle,
|
||||||
items: Default::default(),
|
items: Default::default(),
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header(mut self, title: impl Into<SharedString>) -> Self {
|
pub fn header(mut self, title: impl Into<SharedString>) -> Self {
|
||||||
self.items.push(ListItem::Header(ListSubHeader::new(title)));
|
self.items
|
||||||
|
.push(ContextMenuItem::Header(ListSubHeader::new(title)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn separator(mut self) -> Self {
|
pub fn separator(mut self) -> Self {
|
||||||
self.items.push(ListItem::Separator(ListSeparator));
|
self.items.push(ContextMenuItem::Separator(ListSeparator));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry(mut self, label: Label, action: Box<dyn Action>) -> Self {
|
pub fn entry(
|
||||||
self.items.push(ListEntry::new(label).action(action).into());
|
mut self,
|
||||||
|
view: ListEntry<Self>,
|
||||||
|
on_click: impl Fn(&mut V, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.items
|
||||||
|
.push(ContextMenuItem::Entry(view, Rc::new(on_click)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn action(self, view: ListEntry<Self>, action: Box<dyn Action>) -> Self {
|
||||||
|
// todo: add the keybindings to the list entry
|
||||||
|
self.entry(view, move |_, cx| cx.dispatch_action(action.boxed_clone()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
||||||
// todo!()
|
// todo!()
|
||||||
cx.emit(Dismiss);
|
cx.emit(Dismiss);
|
||||||
|
@ -52,9 +84,9 @@ impl ContextMenu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ContextMenu {
|
impl<V: Render> Render for ContextMenu<V> {
|
||||||
type Element = Div<Self>;
|
type Element = Div<Self>;
|
||||||
// todo!()
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
div().elevation_2(cx).flex().flex_row().child(
|
div().elevation_2(cx).flex().flex_row().child(
|
||||||
v_stack()
|
v_stack()
|
||||||
|
@ -71,7 +103,25 @@ impl Render for ContextMenu {
|
||||||
// .bg(cx.theme().colors().elevated_surface_background)
|
// .bg(cx.theme().colors().elevated_surface_background)
|
||||||
// .border()
|
// .border()
|
||||||
// .border_color(cx.theme().colors().border)
|
// .border_color(cx.theme().colors().border)
|
||||||
.child(List::new(self.items.clone())),
|
.child(List::new(
|
||||||
|
self.items
|
||||||
|
.iter()
|
||||||
|
.map(|item| match item {
|
||||||
|
ContextMenuItem::Separator(separator) => {
|
||||||
|
ListItem::Separator(separator.clone())
|
||||||
|
}
|
||||||
|
ContextMenuItem::Header(header) => ListItem::Header(header.clone()),
|
||||||
|
ContextMenuItem::Entry(entry, callback) => {
|
||||||
|
let callback = callback.clone();
|
||||||
|
let handle = self.handle.clone();
|
||||||
|
ListItem::Entry(entry.clone().on_click(move |this, cx| {
|
||||||
|
handle.update(cx, |view, cx| callback(view, cx)).ok();
|
||||||
|
cx.emit(Dismiss);
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,6 +282,7 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
cx.focus_view(&new_menu);
|
||||||
*menu.borrow_mut() = Some(new_menu);
|
*menu.borrow_mut() = Some(new_menu);
|
||||||
|
|
||||||
*position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() {
|
*position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() {
|
||||||
|
@ -260,16 +311,25 @@ pub use stories::*;
|
||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::story::Story;
|
use crate::story::Story;
|
||||||
use gpui::{actions, Div, Render, VisualContext};
|
use gpui::{actions, Div, Render};
|
||||||
|
|
||||||
actions!(PrintCurrentDate);
|
actions!(PrintCurrentDate, PrintBestFood);
|
||||||
|
|
||||||
fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
|
fn build_menu<V: Render>(
|
||||||
cx.build_view(|cx| {
|
cx: &mut ViewContext<V>,
|
||||||
ContextMenu::new(cx).header(header).separator().entry(
|
header: impl Into<SharedString>,
|
||||||
Label::new("Print current time"),
|
) -> View<ContextMenu<V>> {
|
||||||
PrintCurrentDate.boxed_clone(),
|
let handle = cx.view().clone();
|
||||||
)
|
ContextMenu::build(cx, |menu, _| {
|
||||||
|
menu.header(header)
|
||||||
|
.separator()
|
||||||
|
.entry(ListEntry::new(Label::new("Print current time")), |v, cx| {
|
||||||
|
println!("dispatching PrintCurrentTime action");
|
||||||
|
cx.dispatch_action(PrintCurrentDate.boxed_clone())
|
||||||
|
})
|
||||||
|
.entry(ListEntry::new(Label::new("Print best food")), |v, cx| {
|
||||||
|
cx.dispatch_action(PrintBestFood.boxed_clone())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,10 +341,14 @@ mod stories {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
Story::container(cx)
|
Story::container(cx)
|
||||||
.on_action(|_, _: &PrintCurrentDate, _| {
|
.on_action(|_, _: &PrintCurrentDate, _| {
|
||||||
|
println!("printing unix time!");
|
||||||
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
|
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
|
||||||
println!("Current Unix time is {:?}", unix_time.as_secs());
|
println!("Current Unix time is {:?}", unix_time.as_secs());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.on_action(|_, _: &PrintBestFood, _| {
|
||||||
|
println!("burrito");
|
||||||
|
})
|
||||||
.flex()
|
.flex()
|
||||||
.flex_row()
|
.flex_row()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use gpui::{div, Action};
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use gpui::{div, Div, Stateful, StatefulInteractiveComponent};
|
||||||
|
|
||||||
use crate::settings::user_settings;
|
use crate::settings::user_settings;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -172,35 +174,35 @@ pub enum ListEntrySize {
|
||||||
Medium,
|
Medium,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ListItem {
|
pub enum ListItem<V: 'static> {
|
||||||
Entry(ListEntry),
|
Entry(ListEntry<V>),
|
||||||
Separator(ListSeparator),
|
Separator(ListSeparator),
|
||||||
Header(ListSubHeader),
|
Header(ListSubHeader),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ListEntry> for ListItem {
|
impl<V: 'static> From<ListEntry<V>> for ListItem<V> {
|
||||||
fn from(entry: ListEntry) -> Self {
|
fn from(entry: ListEntry<V>) -> Self {
|
||||||
Self::Entry(entry)
|
Self::Entry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ListSeparator> for ListItem {
|
impl<V: 'static> From<ListSeparator> for ListItem<V> {
|
||||||
fn from(entry: ListSeparator) -> Self {
|
fn from(entry: ListSeparator) -> Self {
|
||||||
Self::Separator(entry)
|
Self::Separator(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ListSubHeader> for ListItem {
|
impl<V: 'static> From<ListSubHeader> for ListItem<V> {
|
||||||
fn from(entry: ListSubHeader) -> Self {
|
fn from(entry: ListSubHeader) -> Self {
|
||||||
Self::Header(entry)
|
Self::Header(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListItem {
|
impl<V: 'static> ListItem<V> {
|
||||||
fn render<V: 'static>(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render(self, view: &mut V, ix: usize, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
match self {
|
match self {
|
||||||
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
|
ListItem::Entry(entry) => div().child(entry.render(ix, cx)),
|
||||||
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
||||||
ListItem::Header(header) => div().child(header.render(view, cx)),
|
ListItem::Header(header) => div().child(header.render(view, cx)),
|
||||||
}
|
}
|
||||||
|
@ -210,7 +212,7 @@ impl ListItem {
|
||||||
Self::Entry(ListEntry::new(label))
|
Self::Entry(ListEntry::new(label))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_entry(&mut self) -> Option<&mut ListEntry> {
|
pub fn as_entry(&mut self) -> Option<&mut ListEntry<V>> {
|
||||||
if let Self::Entry(entry) = self {
|
if let Self::Entry(entry) = self {
|
||||||
Some(entry)
|
Some(entry)
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,8 +221,7 @@ impl ListItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
pub struct ListEntry<V> {
|
||||||
pub struct ListEntry {
|
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
// TODO: Reintroduce this
|
// TODO: Reintroduce this
|
||||||
// disclosure_control_style: DisclosureControlVisibility,
|
// disclosure_control_style: DisclosureControlVisibility,
|
||||||
|
@ -231,15 +232,13 @@ pub struct ListEntry {
|
||||||
size: ListEntrySize,
|
size: ListEntrySize,
|
||||||
toggle: Toggle,
|
toggle: Toggle,
|
||||||
variant: ListItemVariant,
|
variant: ListItemVariant,
|
||||||
on_click: Option<Box<dyn Action>>,
|
on_click: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) + 'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for ListEntry {
|
impl<V> Clone for ListEntry<V> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
disabled: self.disabled,
|
disabled: self.disabled,
|
||||||
// TODO: Reintroduce this
|
|
||||||
// disclosure_control_style: DisclosureControlVisibility,
|
|
||||||
indent_level: self.indent_level,
|
indent_level: self.indent_level,
|
||||||
label: self.label.clone(),
|
label: self.label.clone(),
|
||||||
left_slot: self.left_slot.clone(),
|
left_slot: self.left_slot.clone(),
|
||||||
|
@ -247,12 +246,12 @@ impl Clone for ListEntry {
|
||||||
size: self.size,
|
size: self.size,
|
||||||
toggle: self.toggle,
|
toggle: self.toggle,
|
||||||
variant: self.variant,
|
variant: self.variant,
|
||||||
on_click: self.on_click.as_ref().map(|opt| opt.boxed_clone()),
|
on_click: self.on_click.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListEntry {
|
impl<V: 'static> ListEntry<V> {
|
||||||
pub fn new(label: Label) -> Self {
|
pub fn new(label: Label) -> Self {
|
||||||
Self {
|
Self {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
@ -267,8 +266,8 @@ impl ListEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(mut self, action: impl Into<Box<dyn Action>>) -> Self {
|
pub fn on_click(mut self, handler: impl Fn(&mut V, &mut ViewContext<V>) + 'static) -> Self {
|
||||||
self.on_click = Some(action.into());
|
self.on_click = Some(Rc::new(handler));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +306,7 @@ impl ListEntry {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render(self, ix: usize, cx: &mut ViewContext<V>) -> Stateful<V, Div<V>> {
|
||||||
let settings = user_settings(cx);
|
let settings = user_settings(cx);
|
||||||
|
|
||||||
let left_content = match self.left_slot.clone() {
|
let left_content = match self.left_slot.clone() {
|
||||||
|
@ -328,21 +327,21 @@ impl ListEntry {
|
||||||
ListEntrySize::Medium => div().h_7(),
|
ListEntrySize::Medium => div().h_7(),
|
||||||
};
|
};
|
||||||
div()
|
div()
|
||||||
|
.id(ix)
|
||||||
.relative()
|
.relative()
|
||||||
.hover(|mut style| {
|
.hover(|mut style| {
|
||||||
style.background = Some(cx.theme().colors().editor_background.into());
|
style.background = Some(cx.theme().colors().editor_background.into());
|
||||||
style
|
style
|
||||||
})
|
})
|
||||||
.on_mouse_down(gpui::MouseButton::Left, {
|
.on_click({
|
||||||
let action = self.on_click.map(|action| action.boxed_clone());
|
let on_click = self.on_click.clone();
|
||||||
|
|
||||||
move |entry: &mut V, event, cx| {
|
move |view: &mut V, event, cx| {
|
||||||
if let Some(action) = action.as_ref() {
|
if let Some(on_click) = &on_click {
|
||||||
cx.dispatch_action(action.boxed_clone());
|
(on_click)(view, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.group("")
|
|
||||||
.bg(cx.theme().colors().surface_background)
|
.bg(cx.theme().colors().surface_background)
|
||||||
// TODO: Add focus state
|
// TODO: Add focus state
|
||||||
// .when(self.state == InteractionState::Focused, |this| {
|
// .when(self.state == InteractionState::Focused, |this| {
|
||||||
|
@ -391,8 +390,8 @@ impl ListSeparator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct List {
|
pub struct List<V: 'static> {
|
||||||
items: Vec<ListItem>,
|
items: Vec<ListItem<V>>,
|
||||||
/// Message to display when the list is empty
|
/// Message to display when the list is empty
|
||||||
/// Defaults to "No items"
|
/// Defaults to "No items"
|
||||||
empty_message: SharedString,
|
empty_message: SharedString,
|
||||||
|
@ -400,8 +399,8 @@ pub struct List {
|
||||||
toggle: Toggle,
|
toggle: Toggle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl List {
|
impl<V: 'static> List<V> {
|
||||||
pub fn new(items: Vec<ListItem>) -> Self {
|
pub fn new(items: Vec<ListItem<V>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
items,
|
items,
|
||||||
empty_message: "No items".into(),
|
empty_message: "No items".into(),
|
||||||
|
@ -425,9 +424,14 @@ impl List {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
let list_content = match (self.items.is_empty(), self.toggle) {
|
let list_content = match (self.items.is_empty(), self.toggle) {
|
||||||
(false, _) => div().children(self.items),
|
(false, _) => div().children(
|
||||||
|
self.items
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(ix, item)| item.render(view, ix, cx)),
|
||||||
|
),
|
||||||
(true, Toggle::Toggled(false)) => div(),
|
(true, Toggle::Toggled(false)) => div(),
|
||||||
(true, _) => {
|
(true, _) => {
|
||||||
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
|
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
|
||||||
|
|
|
@ -478,7 +478,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
pub fn static_project_panel_project_items<V>() -> Vec<ListItem<V>> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("zed"))
|
ListEntry::new(Label::new("zed"))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
|
@ -605,7 +605,7 @@ pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
pub fn static_project_panel_single_items<V>() -> Vec<ListItem<V>> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("todo.md"))
|
ListEntry::new(Label::new("todo.md"))
|
||||||
.left_icon(Icon::FileDoc.into())
|
.left_icon(Icon::FileDoc.into())
|
||||||
|
@ -622,7 +622,7 @@ pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_collab_panel_current_call() -> Vec<ListItem> {
|
pub fn static_collab_panel_current_call<V>() -> Vec<ListItem<V>> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
||||||
ListEntry::new(Label::new("nathansobo"))
|
ListEntry::new(Label::new("nathansobo"))
|
||||||
|
@ -635,7 +635,7 @@ pub fn static_collab_panel_current_call() -> Vec<ListItem> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_collab_panel_channels() -> Vec<ListItem> {
|
pub fn static_collab_panel_channels<V>() -> Vec<ListItem<V>> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("zed"))
|
ListEntry::new(Label::new("zed"))
|
||||||
.left_icon(Icon::Hash.into())
|
.left_icon(Icon::Hash.into())
|
||||||
|
|
|
@ -8,7 +8,9 @@ use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme2::ActiveTheme;
|
use theme2::ActiveTheme;
|
||||||
use ui::{h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Tooltip};
|
use ui::{
|
||||||
|
h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Label, ListEntry, Tooltip,
|
||||||
|
};
|
||||||
|
|
||||||
pub enum PanelEvent {
|
pub enum PanelEvent {
|
||||||
ChangePosition,
|
ChangePosition,
|
||||||
|
@ -40,7 +42,7 @@ pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PanelHandle: Send + Sync {
|
pub trait PanelHandle: Send + Sync {
|
||||||
fn id(&self) -> EntityId;
|
fn entity_id(&self) -> EntityId;
|
||||||
fn persistent_name(&self) -> &'static str;
|
fn persistent_name(&self) -> &'static str;
|
||||||
fn position(&self, cx: &WindowContext) -> DockPosition;
|
fn position(&self, cx: &WindowContext) -> DockPosition;
|
||||||
fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool;
|
fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool;
|
||||||
|
@ -62,8 +64,8 @@ impl<T> PanelHandle for View<T>
|
||||||
where
|
where
|
||||||
T: Panel,
|
T: Panel,
|
||||||
{
|
{
|
||||||
fn id(&self) -> EntityId {
|
fn entity_id(&self) -> EntityId {
|
||||||
self.entity_id()
|
Entity::entity_id(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn persistent_name(&self) -> &'static str {
|
fn persistent_name(&self) -> &'static str {
|
||||||
|
@ -254,20 +256,19 @@ impl Dock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo!()
|
pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
|
||||||
// pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
|
for entry in &mut self.panel_entries {
|
||||||
// for entry in &mut self.panel_entries {
|
if entry.panel.entity_id() == panel.entity_id() {
|
||||||
// if entry.panel.as_any() == panel {
|
if zoomed != entry.panel.is_zoomed(cx) {
|
||||||
// if zoomed != entry.panel.is_zoomed(cx) {
|
entry.panel.set_zoomed(zoomed, cx);
|
||||||
// entry.panel.set_zoomed(zoomed, cx);
|
}
|
||||||
// }
|
} else if entry.panel.is_zoomed(cx) {
|
||||||
// } else if entry.panel.is_zoomed(cx) {
|
entry.panel.set_zoomed(false, cx);
|
||||||
// entry.panel.set_zoomed(false, cx);
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// cx.notify();
|
cx.notify();
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
|
pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
for entry in &mut self.panel_entries {
|
for entry in &mut self.panel_entries {
|
||||||
|
@ -277,42 +278,91 @@ impl Dock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
|
pub(crate) fn add_panel<T: Panel>(
|
||||||
|
&mut self,
|
||||||
|
panel: View<T>,
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
let subscriptions = [
|
let subscriptions = [
|
||||||
cx.observe(&panel, |_, _, cx| cx.notify()),
|
cx.observe(&panel, |_, _, cx| cx.notify()),
|
||||||
cx.subscribe(&panel, |this, panel, event, cx| {
|
cx.subscribe(&panel, move |this, panel, event, cx| match event {
|
||||||
match event {
|
|
||||||
PanelEvent::ChangePosition => {
|
PanelEvent::ChangePosition => {
|
||||||
//todo!()
|
let new_position = panel.read(cx).position(cx);
|
||||||
// see: Workspace::add_panel_with_extra_event_handler
|
|
||||||
|
let Ok(new_dock) = workspace.update(cx, |workspace, cx| {
|
||||||
|
if panel.is_zoomed(cx) {
|
||||||
|
workspace.zoomed_position = Some(new_position);
|
||||||
|
}
|
||||||
|
match new_position {
|
||||||
|
DockPosition::Left => &workspace.left_dock,
|
||||||
|
DockPosition::Bottom => &workspace.bottom_dock,
|
||||||
|
DockPosition::Right => &workspace.right_dock,
|
||||||
|
}
|
||||||
|
.clone()
|
||||||
|
}) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let was_visible = this.is_open()
|
||||||
|
&& this.visible_panel().map_or(false, |active_panel| {
|
||||||
|
active_panel.entity_id() == Entity::entity_id(&panel)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.remove_panel(&panel, cx);
|
||||||
|
|
||||||
|
new_dock.update(cx, |new_dock, cx| {
|
||||||
|
new_dock.add_panel(panel.clone(), workspace.clone(), cx);
|
||||||
|
if was_visible {
|
||||||
|
new_dock.set_open(true, cx);
|
||||||
|
new_dock.activate_panel(this.panels_len() - 1, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
PanelEvent::ZoomIn => {
|
PanelEvent::ZoomIn => {
|
||||||
//todo!()
|
this.set_panel_zoomed(&panel.to_any(), true, cx);
|
||||||
// see: Workspace::add_panel_with_extra_event_handler
|
if !panel.has_focus(cx) {
|
||||||
|
cx.focus_view(&panel);
|
||||||
|
}
|
||||||
|
workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
workspace.zoomed = Some(panel.downgrade().into());
|
||||||
|
workspace.zoomed_position = Some(panel.read(cx).position(cx));
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
PanelEvent::ZoomOut => {
|
PanelEvent::ZoomOut => {
|
||||||
// todo!()
|
this.set_panel_zoomed(&panel.to_any(), false, cx);
|
||||||
// // see: Workspace::add_panel_with_extra_event_handler
|
workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
if workspace.zoomed_position == Some(this.position) {
|
||||||
|
workspace.zoomed = None;
|
||||||
|
workspace.zoomed_position = None;
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
PanelEvent::Activate => {
|
PanelEvent::Activate => {
|
||||||
if let Some(ix) = this
|
if let Some(ix) = this
|
||||||
.panel_entries
|
.panel_entries
|
||||||
.iter()
|
.iter()
|
||||||
.position(|entry| entry.panel.id() == panel.id())
|
.position(|entry| entry.panel.entity_id() == Entity::entity_id(&panel))
|
||||||
{
|
{
|
||||||
this.set_open(true, cx);
|
this.set_open(true, cx);
|
||||||
this.activate_panel(ix, cx);
|
this.activate_panel(ix, cx);
|
||||||
//` todo!()
|
cx.focus_view(&panel);
|
||||||
// cx.focus(&panel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PanelEvent::Close => {
|
PanelEvent::Close => {
|
||||||
if this.visible_panel().map_or(false, |p| p.id() == panel.id()) {
|
if this
|
||||||
|
.visible_panel()
|
||||||
|
.map_or(false, |p| p.entity_id() == Entity::entity_id(&panel))
|
||||||
|
{
|
||||||
this.set_open(false, cx);
|
this.set_open(false, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PanelEvent::Focus => todo!(),
|
PanelEvent::Focus => todo!(),
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -335,7 +385,7 @@ impl Dock {
|
||||||
if let Some(panel_ix) = self
|
if let Some(panel_ix) = self
|
||||||
.panel_entries
|
.panel_entries
|
||||||
.iter()
|
.iter()
|
||||||
.position(|entry| entry.panel.id() == panel.id())
|
.position(|entry| entry.panel.entity_id() == Entity::entity_id(panel))
|
||||||
{
|
{
|
||||||
if panel_ix == self.active_panel_index {
|
if panel_ix == self.active_panel_index {
|
||||||
self.active_panel_index = 0;
|
self.active_panel_index = 0;
|
||||||
|
@ -396,7 +446,7 @@ impl Dock {
|
||||||
pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
|
pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
|
||||||
self.panel_entries
|
self.panel_entries
|
||||||
.iter()
|
.iter()
|
||||||
.find(|entry| entry.panel.id() == panel.id())
|
.find(|entry| entry.panel.entity_id() == panel.entity_id())
|
||||||
.map(|entry| entry.panel.size(cx))
|
.map(|entry| entry.panel.size(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,6 +670,7 @@ impl Render for PanelButtons {
|
||||||
let dock = self.dock.read(cx);
|
let dock = self.dock.read(cx);
|
||||||
let active_index = dock.active_panel_index;
|
let active_index = dock.active_panel_index;
|
||||||
let is_open = dock.is_open;
|
let is_open = dock.is_open;
|
||||||
|
let dock_position = dock.position;
|
||||||
|
|
||||||
let (menu_anchor, menu_attach) = match dock.position {
|
let (menu_anchor, menu_attach) = match dock.position {
|
||||||
DockPosition::Left => (AnchorCorner::BottomLeft, AnchorCorner::TopLeft),
|
DockPosition::Left => (AnchorCorner::BottomLeft, AnchorCorner::TopLeft),
|
||||||
|
@ -632,9 +683,10 @@ impl Render for PanelButtons {
|
||||||
.panel_entries
|
.panel_entries
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, panel)| {
|
.filter_map(|(i, entry)| {
|
||||||
let icon = panel.panel.icon(cx)?;
|
let icon = entry.panel.icon(cx)?;
|
||||||
let name = panel.panel.persistent_name();
|
let name = entry.panel.persistent_name();
|
||||||
|
let panel = entry.panel.clone();
|
||||||
|
|
||||||
let mut button: IconButton<Self> = if i == active_index && is_open {
|
let mut button: IconButton<Self> = if i == active_index && is_open {
|
||||||
let action = dock.toggle_action();
|
let action = dock.toggle_action();
|
||||||
|
@ -645,7 +697,7 @@ impl Render for PanelButtons {
|
||||||
.action(action.boxed_clone())
|
.action(action.boxed_clone())
|
||||||
.tooltip(move |_, cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
|
.tooltip(move |_, cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
|
||||||
} else {
|
} else {
|
||||||
let action = panel.panel.toggle_action(cx);
|
let action = entry.panel.toggle_action(cx);
|
||||||
|
|
||||||
IconButton::new(name, icon)
|
IconButton::new(name, icon)
|
||||||
.action(action.boxed_clone())
|
.action(action.boxed_clone())
|
||||||
|
@ -656,7 +708,30 @@ impl Render for PanelButtons {
|
||||||
menu_handle()
|
menu_handle()
|
||||||
.id(name)
|
.id(name)
|
||||||
.menu(move |_, cx| {
|
.menu(move |_, cx| {
|
||||||
cx.build_view(|cx| ContextMenu::new(cx).header("SECTION"))
|
const POSITIONS: [DockPosition; 3] = [
|
||||||
|
DockPosition::Left,
|
||||||
|
DockPosition::Right,
|
||||||
|
DockPosition::Bottom,
|
||||||
|
];
|
||||||
|
ContextMenu::build(cx, |mut menu, cx| {
|
||||||
|
for position in POSITIONS {
|
||||||
|
if position != dock_position
|
||||||
|
&& panel.position_is_valid(position, cx)
|
||||||
|
{
|
||||||
|
let panel = panel.clone();
|
||||||
|
menu = menu.entry(
|
||||||
|
ListEntry::new(Label::new(format!(
|
||||||
|
"Dock {}",
|
||||||
|
position.to_label()
|
||||||
|
))),
|
||||||
|
move |_, cx| {
|
||||||
|
panel.set_position(position, cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.anchor(menu_anchor)
|
.anchor(menu_anchor)
|
||||||
.attach(menu_attach)
|
.attach(menu_attach)
|
||||||
|
|
|
@ -813,7 +813,9 @@ impl Workspace {
|
||||||
DockPosition::Right => &self.right_dock,
|
DockPosition::Right => &self.right_dock,
|
||||||
};
|
};
|
||||||
|
|
||||||
dock.update(cx, |dock, cx| dock.add_panel(panel, cx));
|
dock.update(cx, |dock, cx| {
|
||||||
|
dock.add_panel(panel, self.weak_self.clone(), cx)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status_bar(&self) -> &View<StatusBar> {
|
pub fn status_bar(&self) -> &View<StatusBar> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue