collab ui2 (#3341)

Panel switching

* Also: Tooltip::text("whatever", cx);
* And: Tooltip::for_action("whatever", &collab_panel::Toggle, cx);
* And `overlay()` in Gpui2 (similar to `Overlay` in gpui).

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin
2023-11-15 23:10:52 -07:00
committed by GitHub
20 changed files with 3499 additions and 3179 deletions
File diff suppressed because it is too large Load Diff
@@ -37,7 +37,7 @@ use gpui::{
};
use project::Project;
use theme::ActiveTheme;
use ui::{h_stack, Button, ButtonVariant, KeyBinding, Label, TextColor, TextTooltip};
use ui::{h_stack, Button, ButtonVariant, KeyBinding, Label, TextColor, Tooltip};
use workspace::Workspace;
// const MAX_PROJECT_NAME_LENGTH: usize = 40;
@@ -111,18 +111,14 @@ impl Render for CollabTitlebarItem {
.variant(ButtonVariant::Ghost)
.color(Some(TextColor::Player(0))),
)
.tooltip(move |_, cx| {
cx.build_view(|_| TextTooltip::new("Toggle following"))
}),
.tooltip(move |_, cx| Tooltip::text("Toggle following", cx)),
)
// TODO - Add project menu
.child(
div()
.id("titlebar_project_menu_button")
.child(Button::new("project_name").variant(ButtonVariant::Ghost))
.tooltip(move |_, cx| {
cx.build_view(|_| TextTooltip::new("Recent Projects"))
}),
.tooltip(move |_, cx| Tooltip::text("Recent Projects", cx)),
)
// TODO - Add git menu
.child(
@@ -137,9 +133,8 @@ impl Render for CollabTitlebarItem {
// todo!() Replace with real action.
#[gpui::action]
struct NoAction {}
cx.build_view(|_| {
TextTooltip::new("Recent Branches")
Tooltip::new("Recent Branches")
.key_binding(KeyBinding::new(gpui::KeyBinding::new(
"cmd-b",
NoAction {},
@@ -147,6 +142,7 @@ impl Render for CollabTitlebarItem {
)))
.meta("Only local branches shown")
})
.into()
}),
),
) // self.titlebar_item
+2 -1
View File
@@ -9,6 +9,7 @@ mod panel_settings;
use std::sync::Arc;
pub use collab_panel::CollabPanel;
pub use collab_titlebar_item::CollabTitlebarItem;
use gpui::AppContext;
pub use panel_settings::{
@@ -29,7 +30,7 @@ pub fn init(_app_state: &Arc<AppState>, cx: &mut AppContext) {
// vcs_menu::init(cx);
collab_titlebar_item::init(cx);
// collab_panel::init(cx);
collab_panel::init(cx);
// chat_panel::init(cx);
// notifications::init(&app_state, cx);
+2 -2
View File
@@ -97,7 +97,7 @@ use text::{OffsetUtf16, Rope};
use theme::{
ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
};
use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, TextTooltip};
use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, Tooltip};
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::{
item::{ItemEvent, ItemHandle},
@@ -9985,7 +9985,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
.on_click(move |_, _, cx| {
cx.write_to_clipboard(ClipboardItem::new(message.clone()));
})
.tooltip(|_, cx| cx.build_view(|cx| TextTooltip::new("Copy diagnostic message")))
.tooltip(|_, cx| Tooltip::text("Copy diagnostic message", cx))
.render()
})
}
+6 -4
View File
@@ -12,8 +12,8 @@ use crate::{
},
scroll::scroll_amount::ScrollAmount,
CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
HalfPageDown, HalfPageUp, LineDown, LineUp, MoveDown, PageDown, PageUp, Point, SelectPhase,
Selection, SoftWrap, ToPoint, MAX_LINE_LEN,
HalfPageDown, HalfPageUp, LineDown, LineUp, MoveDown, OpenExcerpts, PageDown, PageUp, Point,
SelectPhase, Selection, SoftWrap, ToPoint, MAX_LINE_LEN,
};
use anyhow::Result;
use collections::{BTreeMap, HashMap};
@@ -45,7 +45,7 @@ use std::{
};
use sum_tree::Bias;
use theme::{ActiveTheme, PlayerColor};
use ui::{h_stack, IconButton};
use ui::{h_stack, IconButton, Tooltip};
use util::ResultExt;
use workspace::item::Item;
@@ -2036,7 +2036,9 @@ impl EditorElement {
.on_click(move |editor: &mut Editor, cx| {
editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
})
.tooltip("Jump to Buffer") // todo!(pass an action as well to show key binding)
.tooltip(move |_, cx| {
Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx)
})
});
let element = if *starts_new_buffer {
+5 -8
View File
@@ -22,7 +22,6 @@ use util::ResultExt;
const DRAG_THRESHOLD: f64 = 2.;
const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
pub struct GroupStyle {
pub group: SharedString,
@@ -408,21 +407,19 @@ pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveCo
self
}
fn tooltip<W>(
fn tooltip(
mut self,
build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
) -> Self
where
Self: Sized,
W: 'static + Render,
{
debug_assert!(
self.interactivity().tooltip_builder.is_none(),
"calling tooltip more than once on the same element is not supported"
);
self.interactivity().tooltip_builder = Some(Rc::new(move |view_state, cx| {
build_tooltip(view_state, cx).into()
}));
self.interactivity().tooltip_builder =
Some(Rc::new(move |view_state, cx| build_tooltip(view_state, cx)));
self
}
@@ -966,7 +963,7 @@ where
waiting: None,
tooltip: Some(AnyTooltip {
view: tooltip_builder(view_state, cx),
cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
cursor_offset: cx.mouse_position(),
}),
});
cx.notify();
+2
View File
@@ -1,11 +1,13 @@
mod div;
mod img;
mod overlay;
mod svg;
mod text;
mod uniform_list;
pub use div::*;
pub use img::*;
pub use overlay::*;
pub use svg::*;
pub use text::*;
pub use uniform_list::*;
+203
View File
@@ -0,0 +1,203 @@
use smallvec::SmallVec;
use crate::{
point, AnyElement, BorrowWindow, Bounds, Element, LayoutId, ParentComponent, Pixels, Point,
Size, Style,
};
pub struct OverlayState {
child_layout_ids: SmallVec<[LayoutId; 4]>,
}
pub struct Overlay<V> {
children: SmallVec<[AnyElement<V>; 2]>,
anchor_corner: AnchorCorner,
fit_mode: OverlayFitMode,
// todo!();
// anchor_position: Option<Vector2F>,
// position_mode: OverlayPositionMode,
}
/// overlay gives you a floating element that will avoid overflowing the window bounds.
/// Its children should have no margin to avoid measurement issues.
pub fn overlay<V: 'static>() -> Overlay<V> {
Overlay {
children: SmallVec::new(),
anchor_corner: AnchorCorner::TopLeft,
fit_mode: OverlayFitMode::SwitchAnchor,
}
}
impl<V> Overlay<V> {
/// Sets which corner of the overlay should be anchored to the current position.
pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
self.anchor_corner = anchor;
self
}
/// Snap to window edge instead of switching anchor corner when an overflow would occur.
pub fn snap_to_window(mut self) -> Self {
self.fit_mode = OverlayFitMode::SnapToWindow;
self
}
}
impl<V: 'static> ParentComponent<V> for Overlay<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
}
impl<V: 'static> Element<V> for Overlay<V> {
type ElementState = OverlayState;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn layout(
&mut self,
view_state: &mut V,
_: Option<Self::ElementState>,
cx: &mut crate::ViewContext<V>,
) -> (crate::LayoutId, Self::ElementState) {
let child_layout_ids = self
.children
.iter_mut()
.map(|child| child.layout(view_state, cx))
.collect::<SmallVec<_>>();
let layout_id = cx.request_layout(&Style::default(), child_layout_ids.iter().copied());
(layout_id, OverlayState { child_layout_ids })
}
fn paint(
&mut self,
bounds: crate::Bounds<crate::Pixels>,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut crate::ViewContext<V>,
) {
if element_state.child_layout_ids.is_empty() {
return;
}
let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default();
for child_layout_id in &element_state.child_layout_ids {
let child_bounds = cx.layout_bounds(*child_layout_id);
child_min = child_min.min(&child_bounds.origin);
child_max = child_max.max(&child_bounds.lower_right());
}
let size: Size<Pixels> = (child_max - child_min).into();
let origin = bounds.origin;
let mut desired = self.anchor_corner.get_bounds(origin, size);
let limits = Bounds {
origin: Point::zero(),
size: cx.viewport_size(),
};
match self.fit_mode {
OverlayFitMode::SnapToWindow => {
// Snap the horizontal edges of the overlay to the horizontal edges of the window if
// its horizontal bounds overflow
if desired.right() > limits.right() {
desired.origin.x -= desired.right() - limits.right();
} else if desired.left() < limits.left() {
desired.origin.x = limits.origin.x;
}
// Snap the vertical edges of the overlay to the vertical edges of the window if
// its vertical bounds overflow.
if desired.bottom() > limits.bottom() {
desired.origin.y -= desired.bottom() - limits.bottom();
} else if desired.top() < limits.top() {
desired.origin.y = limits.origin.y;
}
}
OverlayFitMode::SwitchAnchor => {
let mut anchor_corner = self.anchor_corner;
if desired.left() < limits.left() || desired.right() > limits.right() {
anchor_corner = anchor_corner.switch_axis(Axis::Horizontal);
}
if bounds.top() < limits.top() || bounds.bottom() > limits.bottom() {
anchor_corner = anchor_corner.switch_axis(Axis::Vertical);
}
// Update bounds if needed
if anchor_corner != self.anchor_corner {
desired = anchor_corner.get_bounds(origin, size)
}
}
OverlayFitMode::None => {}
}
cx.with_element_offset(desired.origin - bounds.origin, |cx| {
for child in &mut self.children {
child.paint(view_state, cx);
}
})
}
}
enum Axis {
Horizontal,
Vertical,
}
#[derive(Copy, Clone)]
pub enum OverlayFitMode {
SnapToWindow,
SwitchAnchor,
None,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AnchorCorner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
impl AnchorCorner {
fn get_bounds(&self, origin: Point<Pixels>, size: Size<Pixels>) -> Bounds<Pixels> {
let origin = match self {
Self::TopLeft => origin,
Self::TopRight => Point {
x: origin.x - size.width,
y: origin.y,
},
Self::BottomLeft => Point {
x: origin.x,
y: origin.y - size.height,
},
Self::BottomRight => Point {
x: origin.x - size.width,
y: origin.y - size.height,
},
};
Bounds { origin, size }
}
fn switch_axis(self, axis: Axis) -> Self {
match axis {
Axis::Vertical => match self {
AnchorCorner::TopLeft => AnchorCorner::BottomLeft,
AnchorCorner::TopRight => AnchorCorner::BottomRight,
AnchorCorner::BottomLeft => AnchorCorner::TopLeft,
AnchorCorner::BottomRight => AnchorCorner::TopRight,
},
Axis::Horizontal => match self {
AnchorCorner::TopLeft => AnchorCorner::TopRight,
AnchorCorner::TopRight => AnchorCorner::TopLeft,
AnchorCorner::BottomLeft => AnchorCorner::BottomRight,
AnchorCorner::BottomRight => AnchorCorner::BottomLeft,
},
}
}
}
+16
View File
@@ -421,6 +421,22 @@ impl<T> Bounds<T>
where
T: Add<T, Output = T> + Clone + Default + Debug,
{
pub fn top(&self) -> T {
self.origin.y.clone()
}
pub fn bottom(&self) -> T {
self.origin.y.clone() + self.size.height.clone()
}
pub fn left(&self) -> T {
self.origin.x.clone()
}
pub fn right(&self) -> T {
self.origin.x.clone() + self.size.width.clone()
}
pub fn upper_right(&self) -> Point<T> {
Point {
x: self.origin.x.clone() + self.size.width.clone(),
+11 -4
View File
@@ -130,6 +130,13 @@ pub fn init_settings(cx: &mut AppContext) {
pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
init_settings(cx);
file_associations::init(assets, cx);
cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &ToggleFocus, cx| {
workspace.toggle_panel_focus::<ProjectPanel>(cx);
});
})
.detach();
}
#[derive(Debug)]
@@ -1516,12 +1523,12 @@ impl workspace::dock::Panel for ProjectPanel {
cx.notify();
}
fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
Some("icons/project.svg")
fn icon(&self, _: &WindowContext) -> Option<ui::Icon> {
Some(ui::Icon::FileTree)
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
("Project Panel".into(), Some(Box::new(ToggleFocus)))
fn toggle_action(&self) -> Box<dyn Action> {
Box::new(ToggleFocus)
}
// fn should_change_position_on_event(event: &Self::Event) -> bool {
+8 -5
View File
@@ -1,5 +1,6 @@
use gpui::{div, prelude::*, px, Div, Render, SharedString, Stateful, Styled, View, WindowContext};
use theme2::ActiveTheme;
use ui::Tooltip;
pub struct ScrollStory;
@@ -35,16 +36,18 @@ impl Render for ScrollStory {
} else {
color_2
};
div().id(id).bg(bg).size(px(100. as f32)).when(
row >= 5 && column >= 5,
|d| {
div()
.id(id)
.tooltip(move |_, cx| Tooltip::text(format!("{}, {}", row, column), cx))
.bg(bg)
.size(px(100. as f32))
.when(row >= 5 && column >= 5, |d| {
d.overflow_scroll()
.child(div().size(px(50.)).bg(color_1))
.child(div().size(px(50.)).bg(color_2))
.child(div().size(px(50.)).bg(color_1))
.child(div().size(px(50.)).bg(color_2))
},
)
})
}))
}))
}
+1 -1
View File
@@ -61,7 +61,7 @@ impl ButtonVariant {
}
}
pub type ClickHandler<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) + Send + Sync>;
pub type ClickHandler<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>)>;
struct ButtonHandlers<V: 'static> {
click: Option<ClickHandler<V>>,
+2
View File
@@ -25,6 +25,7 @@ pub enum Icon {
ChevronRight,
ChevronUp,
Close,
Collab,
Dash,
Exit,
ExclamationTriangle,
@@ -83,6 +84,7 @@ impl Icon {
Icon::ChevronRight => "icons/chevron_right.svg",
Icon::ChevronUp => "icons/chevron_up.svg",
Icon::Close => "icons/x.svg",
Icon::Collab => "icons/user_group_16.svg",
Icon::Dash => "icons/dash.svg",
Icon::Exit => "icons/exit.svg",
Icon::ExclamationTriangle => "icons/warning.svg",
+19 -17
View File
@@ -1,5 +1,5 @@
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement, TextTooltip};
use gpui::{prelude::*, MouseButton, VisualContext};
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
use gpui::{prelude::*, AnyView, MouseButton};
use std::sync::Arc;
struct IconButtonHandlers<V: 'static> {
@@ -19,7 +19,7 @@ pub struct IconButton<V: 'static> {
color: TextColor,
variant: ButtonVariant,
state: InteractionState,
tooltip: Option<SharedString>,
tooltip: Option<Box<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>>,
handlers: IconButtonHandlers<V>,
}
@@ -56,22 +56,23 @@ impl<V: 'static> IconButton<V> {
self
}
pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
self.tooltip = Some(tooltip.into());
pub fn tooltip(
mut self,
tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
) -> Self {
self.tooltip = Some(Box::new(tooltip));
self
}
pub fn on_click(
mut self,
handler: impl 'static + Fn(&mut V, &mut ViewContext<V>) + Send + Sync,
) -> Self {
pub fn on_click(mut self, handler: impl 'static + Fn(&mut V, &mut ViewContext<V>)) -> Self {
self.handlers.click = Some(Arc::new(handler));
self
}
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
fn render(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
let icon_color = match (self.state, self.color) {
(InteractionState::Disabled, _) => TextColor::Disabled,
(InteractionState::Active, _) => TextColor::Error,
_ => self.color,
};
@@ -99,15 +100,16 @@ impl<V: 'static> IconButton<V> {
.child(IconElement::new(self.icon).color(icon_color));
if let Some(click_handler) = self.handlers.click.clone() {
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
cx.stop_propagation();
click_handler(state, cx);
});
button = button
.on_mouse_down(MouseButton::Left, move |state, event, cx| {
cx.stop_propagation();
click_handler(state, cx);
})
.cursor_pointer();
}
if let Some(tooltip) = self.tooltip.clone() {
button =
button.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(tooltip.clone())));
if let Some(tooltip) = self.tooltip.take() {
button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
}
button
+66 -25
View File
@@ -1,17 +1,53 @@
use gpui::{Div, Render};
use gpui::{overlay, Action, AnyView, Overlay, Render, VisualContext};
use settings2::Settings;
use theme2::{ActiveTheme, ThemeSettings};
use crate::prelude::*;
use crate::{h_stack, v_stack, KeyBinding, Label, LabelSize, StyledExt, TextColor};
pub struct TextTooltip {
pub struct Tooltip {
title: SharedString,
meta: Option<SharedString>,
key_binding: Option<KeyBinding>,
}
impl TextTooltip {
impl Tooltip {
pub fn text(title: impl Into<SharedString>, cx: &mut WindowContext) -> AnyView {
cx.build_view(|cx| Self {
title: title.into(),
meta: None,
key_binding: None,
})
.into()
}
pub fn for_action(
title: impl Into<SharedString>,
action: &dyn Action,
cx: &mut WindowContext,
) -> AnyView {
cx.build_view(|cx| Self {
title: title.into(),
meta: None,
key_binding: KeyBinding::for_action(action, cx),
})
.into()
}
pub fn with_meta(
title: impl Into<SharedString>,
action: Option<&dyn Action>,
meta: impl Into<SharedString>,
cx: &mut WindowContext,
) -> AnyView {
cx.build_view(|cx| Self {
title: title.into(),
meta: Some(meta.into()),
key_binding: action.and_then(|action| KeyBinding::for_action(action, cx)),
})
.into()
}
pub fn new(title: impl Into<SharedString>) -> Self {
Self {
title: title.into(),
@@ -31,31 +67,36 @@ impl TextTooltip {
}
}
impl Render for TextTooltip {
type Element = Div<Self>;
impl Render for Tooltip {
type Element = Overlay<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
v_stack()
.elevation_2(cx)
.font(ui_font)
.text_ui_sm()
.text_color(cx.theme().colors().text)
.py_1()
.px_2()
.child(
h_stack()
.child(self.title.clone())
.when_some(self.key_binding.clone(), |this, key_binding| {
this.justify_between().child(key_binding)
overlay().child(
// padding to avoid mouse cursor
div().pl_2().pt_2p5().child(
v_stack()
.elevation_2(cx)
.font(ui_font)
.text_ui_sm()
.text_color(cx.theme().colors().text)
.py_1()
.px_2()
.child(
h_stack()
.child(self.title.clone())
.when_some(self.key_binding.clone(), |this, key_binding| {
this.justify_between().child(key_binding)
}),
)
.when_some(self.meta.clone(), |this, meta| {
this.child(
Label::new(meta)
.size(LabelSize::Small)
.color(TextColor::Muted),
)
}),
)
.when_some(self.meta.clone(), |this, meta| {
this.child(
Label::new(meta)
.size(LabelSize::Small)
.color(TextColor::Muted),
)
})
),
)
}
}
+42 -23
View File
@@ -7,6 +7,7 @@ use gpui::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use ui::{h_stack, IconButton, InteractionState, Tooltip};
pub enum PanelEvent {
ChangePosition,
@@ -24,8 +25,8 @@ pub trait Panel: Render + EventEmitter<PanelEvent> {
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
fn size(&self, cx: &WindowContext) -> f32;
fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>);
fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>);
fn icon(&self, cx: &WindowContext) -> Option<ui::Icon>;
fn toggle_action(&self) -> Box<dyn Action>;
fn icon_label(&self, _: &WindowContext) -> Option<String> {
None
}
@@ -49,8 +50,8 @@ pub trait PanelHandle: Send + Sync {
fn set_active(&self, active: bool, cx: &mut WindowContext);
fn size(&self, cx: &WindowContext) -> f32;
fn set_size(&self, size: Option<f32>, cx: &mut WindowContext);
fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
fn icon(&self, cx: &WindowContext) -> Option<ui::Icon>;
fn toggle_action(&self, cx: &WindowContext) -> Box<dyn Action>;
fn icon_label(&self, cx: &WindowContext) -> Option<String>;
fn has_focus(&self, cx: &WindowContext) -> bool;
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
@@ -101,12 +102,12 @@ where
self.update(cx, |this, cx| this.set_size(size, cx))
}
fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
self.read(cx).icon_path(cx)
fn icon(&self, cx: &WindowContext) -> Option<ui::Icon> {
self.read(cx).icon(cx)
}
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>) {
self.read(cx).icon_tooltip()
fn toggle_action(&self, cx: &WindowContext) -> Box<dyn Action> {
self.read(cx).toggle_action()
}
fn icon_label(&self, cx: &WindowContext) -> Option<String> {
@@ -214,11 +215,11 @@ impl Dock {
// .find_map(|entry| entry.panel.as_any().clone().downcast())
// }
// pub fn panel_index_for_type<T: Panel>(&self) -> Option<usize> {
// self.panel_entries
// .iter()
// .position(|entry| entry.panel.as_any().is::<T>())
// }
pub fn panel_index_for_type<T: Panel>(&self) -> Option<usize> {
self.panel_entries
.iter()
.position(|entry| entry.panel.to_any().downcast::<T>().is_ok())
}
pub fn panel_index_for_ui_name(&self, _ui_name: &str, _cx: &AppContext) -> Option<usize> {
todo!()
@@ -644,11 +645,28 @@ impl Render for PanelButtons {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// todo!()
let dock = self.dock.read(cx);
div().children(
dock.panel_entries
.iter()
.map(|panel| panel.panel.persistent_name(cx)),
)
let active_index = dock.active_panel_index;
let is_open = dock.is_open;
let buttons = dock
.panel_entries
.iter()
.enumerate()
.filter_map(|(i, panel)| {
let icon = panel.panel.icon(cx)?;
let name = panel.panel.persistent_name(cx);
let action = panel.panel.toggle_action(cx);
let action2 = action.boxed_clone();
let mut button = IconButton::new(panel.panel.persistent_name(cx), icon)
.when(i == active_index, |el| el.state(InteractionState::Active))
.on_click(move |this, cx| cx.dispatch_action(action.boxed_clone()))
.tooltip(move |_, cx| Tooltip::for_action(name, &*action2, cx));
Some(button)
});
h_stack().children(buttons)
}
}
@@ -665,7 +683,7 @@ impl StatusItemView for PanelButtons {
#[cfg(any(test, feature = "test-support"))]
pub mod test {
use super::*;
use gpui::{div, Div, ViewContext, WindowContext};
use gpui::{actions, div, Div, ViewContext, WindowContext};
pub struct TestPanel {
pub position: DockPosition,
@@ -674,6 +692,7 @@ pub mod test {
pub has_focus: bool,
pub size: f32,
}
actions!(ToggleTestPanel);
impl EventEmitter<PanelEvent> for TestPanel {}
@@ -723,12 +742,12 @@ pub mod test {
self.size = size.unwrap_or(300.);
}
fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
Some("icons/test_panel.svg")
fn icon(&self, _: &WindowContext) -> Option<ui::Icon> {
None
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
("Test Panel".into(), None)
fn toggle_action(&self) -> Box<dyn Action> {
ToggleTestPanel.boxed_clone()
}
fn is_zoomed(&self, _: &WindowContext) -> bool {
+2 -2
View File
@@ -25,7 +25,7 @@ use std::{
},
};
use ui::v_stack;
use ui::{prelude::*, Icon, IconButton, IconElement, TextColor, TextTooltip};
use ui::{prelude::*, Icon, IconButton, IconElement, TextColor, Tooltip};
use util::truncate_and_remove_front;
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
@@ -1396,7 +1396,7 @@ impl Pane {
.id(item.id())
.cursor_pointer()
.when_some(item.tab_tooltip_text(cx), |div, text| {
div.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(text.clone())))
div.tooltip(move |_, cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
})
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
+3 -4
View File
@@ -6,6 +6,7 @@ use gpui::{
WindowContext,
};
use theme2::ActiveTheme;
use ui::h_stack;
use util::ResultExt;
pub trait StatusItemView: Render {
@@ -53,16 +54,14 @@ impl Render for StatusBar {
impl StatusBar {
fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl Component<Self> {
div()
.flex()
h_stack()
.items_center()
.gap_1()
.children(self.left_items.iter().map(|item| item.to_any()))
}
fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl Component<Self> {
div()
.flex()
h_stack()
.items_center()
.gap_2()
.children(self.right_items.iter().map(|item| item.to_any()))
+43 -43
View File
@@ -29,7 +29,7 @@ use client2::{
Client, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle as _};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
use futures::{
channel::{mpsc, oneshot},
future::try_join_all,
@@ -1599,52 +1599,52 @@ impl Workspace {
// .downcast()
// }
// /// Focus the panel of the given type if it isn't already focused. If it is
// /// already focused, then transfer focus back to the workspace center.
// pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
// self.focus_or_unfocus_panel::<T>(cx, |panel, cx| !panel.has_focus(cx));
// }
/// Focus the panel of the given type if it isn't already focused. If it is
/// already focused, then transfer focus back to the workspace center.
pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
self.focus_or_unfocus_panel::<T>(cx, |panel, cx| !panel.has_focus(cx));
}
// /// Focus or unfocus the given panel type, depending on the given callback.
// fn focus_or_unfocus_panel<T: Panel>(
// &mut self,
// cx: &mut ViewContext<Self>,
// should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext<Dock>) -> bool,
// ) -> Option<Rc<dyn PanelHandle>> {
// for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
// if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
// let mut focus_center = false;
// let mut reveal_dock = false;
// let panel = dock.update(cx, |dock, cx| {
// dock.activate_panel(panel_index, cx);
/// Focus or unfocus the given panel type, depending on the given callback.
fn focus_or_unfocus_panel<T: Panel>(
&mut self,
cx: &mut ViewContext<Self>,
should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext<Dock>) -> bool,
) -> Option<Arc<dyn PanelHandle>> {
for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
let mut focus_center = false;
let mut reveal_dock = false;
let panel = dock.update(cx, |dock, cx| {
dock.activate_panel(panel_index, cx);
// let panel = dock.active_panel().cloned();
// if let Some(panel) = panel.as_ref() {
// if should_focus(&**panel, cx) {
// dock.set_open(true, cx);
// cx.focus(panel.as_any());
// reveal_dock = true;
// } else {
// // if panel.is_zoomed(cx) {
// // dock.set_open(false, cx);
// // }
// focus_center = true;
// }
// }
// panel
// });
let panel = dock.active_panel().cloned();
if let Some(panel) = panel.as_ref() {
if should_focus(&**panel, cx) {
dock.set_open(true, cx);
panel.focus_handle(cx).focus(cx);
reveal_dock = true;
} else {
// if panel.is_zoomed(cx) {
// dock.set_open(false, cx);
// }
focus_center = true;
}
}
panel
});
// if focus_center {
// cx.focus_self();
// }
if focus_center {
self.active_pane.update(cx, |pane, cx| pane.focus(cx))
}
// self.serialize_workspace(cx);
// cx.notify();
// return panel;
// }
// }
// None
// }
self.serialize_workspace(cx);
cx.notify();
return panel;
}
}
None
}
// pub fn panel<T: Panel>(&self, cx: &WindowContext) -> Option<View<T>> {
// for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
+6 -6
View File
@@ -383,8 +383,8 @@ pub fn initialize_workspace(
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
// let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
// let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone());
// let channels_panel =
// collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
let channels_panel =
collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
// let chat_panel =
// collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone());
// let notification_panel = collab_ui::notification_panel::NotificationPanel::load(
@@ -395,16 +395,16 @@ pub fn initialize_workspace(
project_panel,
// terminal_panel,
// assistant_panel,
// channels_panel,
channels_panel,
// chat_panel,
// notification_panel,
) = futures::try_join!(
project_panel,
// terminal_panel,
// assistant_panel,
// channels_panel,
channels_panel,
// chat_panel,
// notification_panel,
// notification_panel,/
)?;
workspace_handle.update(&mut cx, |workspace, cx| {
@@ -412,7 +412,7 @@ pub fn initialize_workspace(
workspace.add_panel(project_panel, cx);
// workspace.add_panel(terminal_panel, cx);
// workspace.add_panel(assistant_panel, cx);
// workspace.add_panel(channels_panel, cx);
workspace.add_panel(channels_panel, cx);
// workspace.add_panel(chat_panel, cx);
// workspace.add_panel(notification_panel, cx);