mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-23 18:32:17 +00:00
Permanent fix to repeat MouseRegion Tag failure in Workspace
Polish tab bar buttons Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
69ecbb644d
commit
6b26965074
33 changed files with 542 additions and 545 deletions
|
@ -278,7 +278,7 @@ impl View for ActivityIndicator {
|
||||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||||
let (icon, message, action) = self.content_to_render(cx);
|
let (icon, message, action) = self.content_to_render(cx);
|
||||||
|
|
||||||
let mut element = MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
|
let mut element = MouseEventHandler::<Self>::new(0, cx, |state, cx| {
|
||||||
let theme = &cx
|
let theme = &cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl View for UpdateNotification {
|
||||||
let theme = cx.global::<Settings>().theme.clone();
|
let theme = cx.global::<Settings>().theme.clone();
|
||||||
let theme = &theme.update_notification;
|
let theme = &theme.update_notification;
|
||||||
|
|
||||||
MouseEventHandler::new::<ViewReleaseNotes, _, _>(0, cx, |state, cx| {
|
MouseEventHandler::<ViewReleaseNotes>::new(0, cx, |state, cx| {
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.with_child(
|
.with_child(
|
||||||
Flex::row()
|
Flex::row()
|
||||||
|
@ -47,7 +47,7 @@ impl View for UpdateNotification {
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::new::<Cancel, _, _>(0, cx, |state, _| {
|
MouseEventHandler::<Cancel>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state, false);
|
||||||
Svg::new("icons/x_mark_thin_8.svg")
|
Svg::new("icons/x_mark_thin_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
|
|
|
@ -308,7 +308,7 @@ impl ChatPanel {
|
||||||
enum SignInPromptLabel {}
|
enum SignInPromptLabel {}
|
||||||
|
|
||||||
Align::new(
|
Align::new(
|
||||||
MouseEventHandler::new::<SignInPromptLabel, _, _>(0, cx, |mouse_state, _| {
|
MouseEventHandler::<SignInPromptLabel>::new(0, cx, |mouse_state, _| {
|
||||||
Label::new(
|
Label::new(
|
||||||
"Sign in to use chat".to_string(),
|
"Sign in to use chat".to_string(),
|
||||||
if mouse_state.hovered {
|
if mouse_state.hovered {
|
||||||
|
|
|
@ -276,7 +276,7 @@ impl ContactsPanel {
|
||||||
Section::Offline => "Offline",
|
Section::Offline => "Offline",
|
||||||
};
|
};
|
||||||
let icon_size = theme.section_icon_size;
|
let icon_size = theme.section_icon_size;
|
||||||
MouseEventHandler::new::<Header, _, _>(section as usize, cx, |_, _| {
|
MouseEventHandler::<Header>::new(section as usize, cx, |_, _| {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Svg::new(if is_collapsed {
|
Svg::new(if is_collapsed {
|
||||||
|
@ -375,7 +375,7 @@ impl ContactsPanel {
|
||||||
let baseline_offset =
|
let baseline_offset =
|
||||||
row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
|
row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
|
||||||
|
|
||||||
MouseEventHandler::new::<JoinProject, _, _>(project_id as usize, cx, |mouse_state, cx| {
|
MouseEventHandler::<JoinProject>::new(project_id as usize, cx, |mouse_state, cx| {
|
||||||
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
||||||
let row = theme.project_row.style_for(mouse_state, is_selected);
|
let row = theme.project_row.style_for(mouse_state, is_selected);
|
||||||
|
|
||||||
|
@ -424,7 +424,7 @@ impl ContactsPanel {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let button = MouseEventHandler::new::<ToggleProjectOnline, _, _>(
|
let button = MouseEventHandler::<ToggleProjectOnline>::new(
|
||||||
project_id as usize,
|
project_id as usize,
|
||||||
cx,
|
cx,
|
||||||
|state, _| {
|
|state, _| {
|
||||||
|
@ -529,7 +529,7 @@ impl ContactsPanel {
|
||||||
enum ToggleOnline {}
|
enum ToggleOnline {}
|
||||||
|
|
||||||
let project_id = project_handle.id();
|
let project_id = project_handle.id();
|
||||||
MouseEventHandler::new::<LocalProject, _, _>(project_id, cx, |state, cx| {
|
MouseEventHandler::<LocalProject>::new(project_id, cx, |state, cx| {
|
||||||
let row = theme.project_row.style_for(state, is_selected);
|
let row = theme.project_row.style_for(state, is_selected);
|
||||||
let mut worktree_root_names = String::new();
|
let mut worktree_root_names = String::new();
|
||||||
let project = if let Some(project) = project_handle.upgrade(cx.deref_mut()) {
|
let project = if let Some(project) = project_handle.upgrade(cx.deref_mut()) {
|
||||||
|
@ -548,7 +548,7 @@ impl ContactsPanel {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child({
|
.with_child({
|
||||||
let button =
|
let button =
|
||||||
MouseEventHandler::new::<ToggleOnline, _, _>(project_id, cx, |state, _| {
|
MouseEventHandler::<ToggleOnline>::new(project_id, cx, |state, _| {
|
||||||
let mut style = *theme.private_button.style_for(state, false);
|
let mut style = *theme.private_button.style_for(state, false);
|
||||||
if is_going_online {
|
if is_going_online {
|
||||||
style.color = theme.disabled_button.color;
|
style.color = theme.disabled_button.color;
|
||||||
|
@ -636,7 +636,7 @@ impl ContactsPanel {
|
||||||
|
|
||||||
if is_incoming {
|
if is_incoming {
|
||||||
row.add_children([
|
row.add_children([
|
||||||
MouseEventHandler::new::<Decline, _, _>(user.id as usize, cx, |mouse_state, _| {
|
MouseEventHandler::<Decline>::new(user.id as usize, cx, |mouse_state, _| {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
|
@ -658,7 +658,7 @@ impl ContactsPanel {
|
||||||
.contained()
|
.contained()
|
||||||
.with_margin_right(button_spacing)
|
.with_margin_right(button_spacing)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
MouseEventHandler::new::<Accept, _, _>(user.id as usize, cx, |mouse_state, _| {
|
MouseEventHandler::<Accept>::new(user.id as usize, cx, |mouse_state, _| {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
|
@ -680,7 +680,7 @@ impl ContactsPanel {
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
row.add_child(
|
row.add_child(
|
||||||
MouseEventHandler::new::<Cancel, _, _>(user.id as usize, cx, |mouse_state, _| {
|
MouseEventHandler::<Cancel>::new(user.id as usize, cx, |mouse_state, _| {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
|
@ -1071,7 +1071,7 @@ impl View for ContactsPanel {
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::new::<AddContact, _, _>(0, cx, |_, _| {
|
MouseEventHandler::<AddContact>::new(0, cx, |_, _| {
|
||||||
Svg::new("icons/user_plus_16.svg")
|
Svg::new("icons/user_plus_16.svg")
|
||||||
.with_color(theme.add_contact_button.color)
|
.with_color(theme.add_contact_button.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -1102,35 +1102,31 @@ impl View for ContactsPanel {
|
||||||
|
|
||||||
if info.count > 0 {
|
if info.count > 0 {
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::new::<InviteLink, _, _>(
|
MouseEventHandler::<InviteLink>::new(0, cx, |state, cx| {
|
||||||
0,
|
let style =
|
||||||
cx,
|
theme.invite_row.style_for(state, false).clone();
|
||||||
|state, cx| {
|
|
||||||
let style =
|
|
||||||
theme.invite_row.style_for(state, false).clone();
|
|
||||||
|
|
||||||
let copied =
|
let copied =
|
||||||
cx.read_from_clipboard().map_or(false, |item| {
|
cx.read_from_clipboard().map_or(false, |item| {
|
||||||
item.text().as_str() == info.url.as_ref()
|
item.text().as_str() == info.url.as_ref()
|
||||||
});
|
});
|
||||||
|
|
||||||
Label::new(
|
Label::new(
|
||||||
format!(
|
format!(
|
||||||
"{} invite link ({} left)",
|
"{} invite link ({} left)",
|
||||||
if copied { "Copied" } else { "Copy" },
|
if copied { "Copied" } else { "Copy" },
|
||||||
info.count
|
info.count
|
||||||
),
|
),
|
||||||
style.label.clone(),
|
style.label.clone(),
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
.left()
|
.left()
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_height(theme.row_height)
|
.with_height(theme.row_height)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
.boxed()
|
.boxed()
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.on_click(MouseButton::Left, move |_, cx| {
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
cx.write_to_clipboard(ClipboardItem::new(
|
cx.write_to_clipboard(ClipboardItem::new(
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::new::<Dismiss, _, _>(user.id as usize, cx, |state, _| {
|
MouseEventHandler::<Dismiss>::new(user.id as usize, cx, |state, _| {
|
||||||
render_icon_button(
|
render_icon_button(
|
||||||
theme.dismiss_button.style_for(state, false),
|
theme.dismiss_button.style_for(state, false),
|
||||||
"icons/x_mark_thin_8.svg",
|
"icons/x_mark_thin_8.svg",
|
||||||
|
@ -90,7 +90,7 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_children(buttons.into_iter().enumerate().map(
|
.with_children(buttons.into_iter().enumerate().map(
|
||||||
|(ix, (message, action))| {
|
|(ix, (message, action))| {
|
||||||
MouseEventHandler::new::<Button, _, _>(ix, cx, |state, _| {
|
MouseEventHandler::<Button>::new(ix, cx, |state, _| {
|
||||||
let button = theme.button.style_for(state, false);
|
let button = theme.button.style_for(state, false);
|
||||||
Label::new(message.to_string(), button.text.clone())
|
Label::new(message.to_string(), button.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
|
|
|
@ -22,7 +22,6 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(ContextMenu::cancel);
|
cx.add_action(ContextMenu::cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
pub enum ContextMenuItem {
|
pub enum ContextMenuItem {
|
||||||
Item {
|
Item {
|
||||||
label: String,
|
label: String,
|
||||||
|
@ -57,7 +56,8 @@ impl ContextMenuItem {
|
||||||
|
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
show_count: usize,
|
show_count: usize,
|
||||||
position: Vector2F,
|
anchor_position: Vector2F,
|
||||||
|
anchor_corner: AnchorCorner,
|
||||||
items: Vec<ContextMenuItem>,
|
items: Vec<ContextMenuItem>,
|
||||||
selected_index: Option<usize>,
|
selected_index: Option<usize>,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
|
@ -100,9 +100,10 @@ impl View for ContextMenu {
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
Overlay::new(expanded_menu)
|
Overlay::new(expanded_menu)
|
||||||
.hoverable(true)
|
.with_hoverable(true)
|
||||||
.fit_mode(OverlayFitMode::SnapToWindow)
|
.with_fit_mode(OverlayFitMode::SnapToWindow)
|
||||||
.with_abs_position(self.position)
|
.with_anchor_position(self.anchor_position)
|
||||||
|
.with_anchor_corner(self.anchor_corner)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +116,8 @@ impl ContextMenu {
|
||||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
show_count: 0,
|
show_count: 0,
|
||||||
position: Default::default(),
|
anchor_position: Default::default(),
|
||||||
|
anchor_corner: AnchorCorner::TopLeft,
|
||||||
items: Default::default(),
|
items: Default::default(),
|
||||||
selected_index: Default::default(),
|
selected_index: Default::default(),
|
||||||
visible: Default::default(),
|
visible: Default::default(),
|
||||||
|
@ -226,14 +228,16 @@ impl ContextMenu {
|
||||||
|
|
||||||
pub fn show(
|
pub fn show(
|
||||||
&mut self,
|
&mut self,
|
||||||
position: Vector2F,
|
anchor_position: Vector2F,
|
||||||
|
anchor_corner: AnchorCorner,
|
||||||
items: impl IntoIterator<Item = ContextMenuItem>,
|
items: impl IntoIterator<Item = ContextMenuItem>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
let mut items = items.into_iter().peekable();
|
let mut items = items.into_iter().peekable();
|
||||||
if items.peek().is_some() {
|
if items.peek().is_some() {
|
||||||
self.items = items.collect();
|
self.items = items.collect();
|
||||||
self.position = position;
|
self.anchor_position = anchor_position;
|
||||||
|
self.anchor_corner = anchor_corner;
|
||||||
self.visible = true;
|
self.visible = true;
|
||||||
self.show_count += 1;
|
self.show_count += 1;
|
||||||
if !cx.is_self_focused() {
|
if !cx.is_self_focused() {
|
||||||
|
@ -310,13 +314,13 @@ impl ContextMenu {
|
||||||
enum Menu {}
|
enum Menu {}
|
||||||
enum MenuItem {}
|
enum MenuItem {}
|
||||||
let style = cx.global::<Settings>().theme.context_menu.clone();
|
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||||
MouseEventHandler::new::<Menu, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<Menu>::new(0, cx, |_, cx| {
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
match item {
|
match item {
|
||||||
ContextMenuItem::Item { label, action } => {
|
ContextMenuItem::Item { label, action } => {
|
||||||
let action = action.boxed_clone();
|
let action = action.boxed_clone();
|
||||||
MouseEventHandler::new::<MenuItem, _, _>(ix, cx, |state, _| {
|
MouseEventHandler::<MenuItem>::new(ix, cx, |state, _| {
|
||||||
let style =
|
let style =
|
||||||
style.item.style_for(state, Some(ix) == self.selected_index);
|
style.item.style_for(state, Some(ix) == self.selected_index);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl View for DiagnosticIndicator {
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
let in_progress = !self.in_progress_checks.is_empty();
|
let in_progress = !self.in_progress_checks.is_empty();
|
||||||
let mut element = Flex::row().with_child(
|
let mut element = Flex::row().with_child(
|
||||||
MouseEventHandler::new::<Summary, _, _>(0, cx, |state, cx| {
|
MouseEventHandler::<Summary>::new(0, cx, |state, cx| {
|
||||||
let style = cx
|
let style = cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
@ -190,7 +190,7 @@ impl View for DiagnosticIndicator {
|
||||||
} else if let Some(diagnostic) = &self.current_diagnostic {
|
} else if let Some(diagnostic) = &self.current_diagnostic {
|
||||||
let message_style = style.diagnostic_message.clone();
|
let message_style = style.diagnostic_message.clone();
|
||||||
element.add_child(
|
element.add_child(
|
||||||
MouseEventHandler::new::<Message, _, _>(1, cx, |state, _| {
|
MouseEventHandler::<Message>::new(1, cx, |state, _| {
|
||||||
Label::new(
|
Label::new(
|
||||||
diagnostic.message.split('\n').next().unwrap().to_string(),
|
diagnostic.message.split('\n').next().unwrap().to_string(),
|
||||||
message_style.style_for(state, false).text.clone(),
|
message_style.style_for(state, false).text.clone(),
|
||||||
|
|
|
@ -114,8 +114,9 @@ impl<V: View> DragAndDrop<V> {
|
||||||
|
|
||||||
let position = position + region_offset;
|
let position = position + region_offset;
|
||||||
|
|
||||||
|
enum DraggedElementHandler {}
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<DraggedElementHandler>::new(0, cx, |_, cx| {
|
||||||
Container::new(render(payload, cx))
|
Container::new(render(payload, cx))
|
||||||
.with_margin_left(position.x())
|
.with_margin_left(position.x())
|
||||||
.with_margin_top(position.y())
|
.with_margin_top(position.y())
|
||||||
|
@ -174,7 +175,7 @@ pub trait Draggable {
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Draggable for MouseEventHandler {
|
impl<Tag> Draggable for MouseEventHandler<Tag> {
|
||||||
fn as_draggable<V: View, P: Any>(
|
fn as_draggable<V: View, P: Any>(
|
||||||
self,
|
self,
|
||||||
payload: P,
|
payload: P,
|
||||||
|
|
|
@ -682,7 +682,7 @@ impl CompletionsMenu {
|
||||||
let completion = &completions[mat.candidate_id];
|
let completion = &completions[mat.candidate_id];
|
||||||
let item_ix = start_ix + ix;
|
let item_ix = start_ix + ix;
|
||||||
items.push(
|
items.push(
|
||||||
MouseEventHandler::new::<CompletionTag, _, _>(
|
MouseEventHandler::<CompletionTag>::new(
|
||||||
mat.candidate_id,
|
mat.candidate_id,
|
||||||
cx,
|
cx,
|
||||||
|state, _| {
|
|state, _| {
|
||||||
|
@ -830,7 +830,7 @@ impl CodeActionsMenu {
|
||||||
for (ix, action) in actions[range].iter().enumerate() {
|
for (ix, action) in actions[range].iter().enumerate() {
|
||||||
let item_ix = start_ix + ix;
|
let item_ix = start_ix + ix;
|
||||||
items.push(
|
items.push(
|
||||||
MouseEventHandler::new::<ActionTag, _, _>(item_ix, cx, |state, _| {
|
MouseEventHandler::<ActionTag>::new(item_ix, cx, |state, _| {
|
||||||
let item_style = if item_ix == selected_item {
|
let item_style = if item_ix == selected_item {
|
||||||
style.autocomplete.selected_item
|
style.autocomplete.selected_item
|
||||||
} else if state.hovered {
|
} else if state.hovered {
|
||||||
|
@ -2735,7 +2735,7 @@ impl Editor {
|
||||||
if self.available_code_actions.is_some() {
|
if self.available_code_actions.is_some() {
|
||||||
enum Tag {}
|
enum Tag {}
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
|
MouseEventHandler::<Tag>::new(0, cx, |_, _| {
|
||||||
Svg::new("icons/bolt_8.svg")
|
Svg::new("icons/bolt_8.svg")
|
||||||
.with_color(style.code_actions.indicator)
|
.with_color(style.code_actions.indicator)
|
||||||
.boxed()
|
.boxed()
|
||||||
|
@ -7100,7 +7100,7 @@ mod tests {
|
||||||
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
||||||
cx.set_global(Settings::test(cx));
|
cx.set_global(Settings::test(cx));
|
||||||
use workspace::Item;
|
use workspace::Item;
|
||||||
let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(false, cx));
|
let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
||||||
|
|
||||||
cx.add_view(&pane, |cx| {
|
cx.add_view(&pane, |cx| {
|
||||||
|
|
|
@ -1138,7 +1138,7 @@ impl EditorElement {
|
||||||
|
|
||||||
enum JumpIcon {}
|
enum JumpIcon {}
|
||||||
cx.render(&editor, |_, cx| {
|
cx.render(&editor, |_, cx| {
|
||||||
MouseEventHandler::new::<JumpIcon, _, _>(*key, cx, |state, _| {
|
MouseEventHandler::<JumpIcon>::new(*key, cx, |state, _| {
|
||||||
let style = style.jump_icon.style_for(state, false);
|
let style = style.jump_icon.style_for(state, false);
|
||||||
Svg::new("icons/arrow_up_right_8.svg")
|
Svg::new("icons/arrow_up_right_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
|
|
|
@ -312,7 +312,7 @@ pub struct InfoPopover {
|
||||||
|
|
||||||
impl InfoPopover {
|
impl InfoPopover {
|
||||||
pub fn render(&self, style: &EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
|
pub fn render(&self, style: &EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
|
||||||
MouseEventHandler::new::<InfoPopover, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<InfoPopover>::new(0, cx, |_, cx| {
|
||||||
let mut flex = Flex::new(Axis::Vertical).scrollable::<HoverBlock, _>(1, None, cx);
|
let mut flex = Flex::new(Axis::Vertical).scrollable::<HoverBlock, _>(1, None, cx);
|
||||||
flex.extend(self.contents.iter().map(|content| {
|
flex.extend(self.contents.iter().map(|content| {
|
||||||
let project = self.project.read(cx);
|
let project = self.project.read(cx);
|
||||||
|
@ -383,7 +383,7 @@ impl DiagnosticPopover {
|
||||||
|
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
|
|
||||||
MouseEventHandler::new::<DiagnosticPopover, _, _>(0, cx, |_, _| {
|
MouseEventHandler::<DiagnosticPopover>::new(0, cx, |_, _| {
|
||||||
Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style)
|
Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style)
|
||||||
.with_soft_wrap(true)
|
.with_soft_wrap(true)
|
||||||
.contained()
|
.contained()
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use context_menu::ContextMenuItem;
|
use context_menu::ContextMenuItem;
|
||||||
use gpui::{geometry::vector::Vector2F, impl_internal_actions, MutableAppContext, ViewContext};
|
use gpui::{
|
||||||
|
elements::AnchorCorner, geometry::vector::Vector2F, impl_internal_actions, MutableAppContext,
|
||||||
|
ViewContext,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
|
DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
|
||||||
|
@ -46,6 +49,7 @@ pub fn deploy_context_menu(
|
||||||
editor.mouse_context_menu.update(cx, |menu, cx| {
|
editor.mouse_context_menu.update(cx, |menu, cx| {
|
||||||
menu.show(
|
menu.show(
|
||||||
position,
|
position,
|
||||||
|
AnchorCorner::TopLeft,
|
||||||
vec![
|
vec![
|
||||||
ContextMenuItem::item("Rename Symbol", Rename),
|
ContextMenuItem::item("Rename Symbol", Rename),
|
||||||
ContextMenuItem::item("Go To Definition", GoToDefinition),
|
ContextMenuItem::item("Go To Definition", GoToDefinition),
|
||||||
|
|
|
@ -4076,10 +4076,7 @@ impl<'a, V: View> RenderContext<'a, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
|
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
|
||||||
let region_id = MouseRegionId {
|
let region_id = MouseRegionId::new::<Tag>(self.view_id, region_id);
|
||||||
view_id: self.view_id,
|
|
||||||
discriminant: (TypeId::of::<Tag>(), region_id),
|
|
||||||
};
|
|
||||||
MouseState {
|
MouseState {
|
||||||
hovered: self.hovered_region_ids.contains(®ion_id),
|
hovered: self.hovered_region_ids.contains(®ion_id),
|
||||||
clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| {
|
clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| {
|
||||||
|
@ -6032,12 +6029,12 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::View for View {
|
impl super::View for View {
|
||||||
fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||||
|
enum Handler {}
|
||||||
let mouse_down_count = self.mouse_down_count.clone();
|
let mouse_down_count = self.mouse_down_count.clone();
|
||||||
EventHandler::new(Empty::new().boxed())
|
MouseEventHandler::<Handler>::new(0, cx, |_, _| Empty::new().boxed())
|
||||||
.on_mouse_down(move |_| {
|
.on_down(MouseButton::Left, move |_, _| {
|
||||||
mouse_down_count.fetch_add(1, SeqCst);
|
mouse_down_count.fetch_add(1, SeqCst);
|
||||||
true
|
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ mod canvas;
|
||||||
mod constrained_box;
|
mod constrained_box;
|
||||||
mod container;
|
mod container;
|
||||||
mod empty;
|
mod empty;
|
||||||
mod event_handler;
|
|
||||||
mod expanded;
|
mod expanded;
|
||||||
mod flex;
|
mod flex;
|
||||||
mod hook;
|
mod hook;
|
||||||
|
@ -21,9 +20,9 @@ mod uniform_list;
|
||||||
|
|
||||||
use self::expanded::Expanded;
|
use self::expanded::Expanded;
|
||||||
pub use self::{
|
pub use self::{
|
||||||
align::*, canvas::*, constrained_box::*, container::*, empty::*, event_handler::*, flex::*,
|
align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
|
||||||
hook::*, image::*, keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*,
|
keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, stack::*, svg::*,
|
||||||
stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
|
text::*, tooltip::*, uniform_list::*,
|
||||||
};
|
};
|
||||||
pub use crate::presenter::ChildView;
|
pub use crate::presenter::ChildView;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
use crate::{
|
|
||||||
geometry::vector::Vector2F, presenter::MeasurementContext, scene::HandlerSet, CursorRegion,
|
|
||||||
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseButton,
|
|
||||||
MouseButtonEvent, MouseRegion, NavigationDirection, PaintContext, SizeConstraint,
|
|
||||||
};
|
|
||||||
use pathfinder_geometry::rect::RectF;
|
|
||||||
use serde_json::json;
|
|
||||||
use std::{any::TypeId, ops::Range};
|
|
||||||
|
|
||||||
pub struct EventHandler {
|
|
||||||
child: ElementBox,
|
|
||||||
capture_all: Option<(TypeId, usize)>,
|
|
||||||
mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
|
|
||||||
right_mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
|
|
||||||
navigate_mouse_down: Option<Box<dyn FnMut(NavigationDirection, &mut EventContext) -> bool>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventHandler {
|
|
||||||
pub fn new(child: ElementBox) -> Self {
|
|
||||||
Self {
|
|
||||||
child,
|
|
||||||
capture_all: None,
|
|
||||||
mouse_down: None,
|
|
||||||
right_mouse_down: None,
|
|
||||||
navigate_mouse_down: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_mouse_down<F>(mut self, callback: F) -> Self
|
|
||||||
where
|
|
||||||
F: 'static + FnMut(&mut EventContext) -> bool,
|
|
||||||
{
|
|
||||||
self.mouse_down = Some(Box::new(callback));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_right_mouse_down<F>(mut self, callback: F) -> Self
|
|
||||||
where
|
|
||||||
F: 'static + FnMut(&mut EventContext) -> bool,
|
|
||||||
{
|
|
||||||
self.right_mouse_down = Some(Box::new(callback));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_navigate_mouse_down<F>(mut self, callback: F) -> Self
|
|
||||||
where
|
|
||||||
F: 'static + FnMut(NavigationDirection, &mut EventContext) -> bool,
|
|
||||||
{
|
|
||||||
self.navigate_mouse_down = Some(Box::new(callback));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn capture_all<T: 'static>(mut self, id: usize) -> Self {
|
|
||||||
self.capture_all = Some((TypeId::of::<T>(), id));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element for EventHandler {
|
|
||||||
type LayoutState = ();
|
|
||||||
type PaintState = ();
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
constraint: SizeConstraint,
|
|
||||||
cx: &mut LayoutContext,
|
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
|
||||||
let size = self.child.layout(constraint, cx);
|
|
||||||
(size, ())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
_: &mut Self::LayoutState,
|
|
||||||
cx: &mut PaintContext,
|
|
||||||
) -> Self::PaintState {
|
|
||||||
if let Some(discriminant) = self.capture_all {
|
|
||||||
cx.scene.push_stacking_context(None);
|
|
||||||
cx.scene.push_cursor_region(CursorRegion {
|
|
||||||
bounds: visible_bounds,
|
|
||||||
style: Default::default(),
|
|
||||||
});
|
|
||||||
cx.scene.push_mouse_region(MouseRegion {
|
|
||||||
view_id: cx.current_view_id(),
|
|
||||||
discriminant,
|
|
||||||
bounds: visible_bounds,
|
|
||||||
handlers: HandlerSet::capture_all(),
|
|
||||||
hoverable: true,
|
|
||||||
});
|
|
||||||
cx.scene.pop_stacking_context();
|
|
||||||
}
|
|
||||||
self.child.paint(bounds.origin(), visible_bounds, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_event(
|
|
||||||
&mut self,
|
|
||||||
event: &Event,
|
|
||||||
_: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
_: &mut Self::LayoutState,
|
|
||||||
_: &mut Self::PaintState,
|
|
||||||
cx: &mut EventContext,
|
|
||||||
) -> bool {
|
|
||||||
if self.capture_all.is_some() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.child.dispatch_event(event, cx) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
match event {
|
|
||||||
Event::MouseDown(MouseButtonEvent {
|
|
||||||
button: MouseButton::Left,
|
|
||||||
position,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if let Some(callback) = self.mouse_down.as_mut() {
|
|
||||||
if visible_bounds.contains_point(*position) {
|
|
||||||
return callback(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Event::MouseDown(MouseButtonEvent {
|
|
||||||
button: MouseButton::Right,
|
|
||||||
position,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if let Some(callback) = self.right_mouse_down.as_mut() {
|
|
||||||
if visible_bounds.contains_point(*position) {
|
|
||||||
return callback(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Event::MouseDown(MouseButtonEvent {
|
|
||||||
button: MouseButton::Navigate(direction),
|
|
||||||
position,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if let Some(callback) = self.navigate_mouse_down.as_mut() {
|
|
||||||
if visible_bounds.contains_point(*position) {
|
|
||||||
return callback(*direction, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_for_text_range(
|
|
||||||
&self,
|
|
||||||
range_utf16: Range<usize>,
|
|
||||||
_: RectF,
|
|
||||||
_: RectF,
|
|
||||||
_: &Self::LayoutState,
|
|
||||||
_: &Self::PaintState,
|
|
||||||
cx: &MeasurementContext,
|
|
||||||
) -> Option<RectF> {
|
|
||||||
self.child.rect_for_text_range(range_utf16, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(
|
|
||||||
&self,
|
|
||||||
_: RectF,
|
|
||||||
_: &Self::LayoutState,
|
|
||||||
_: &Self::PaintState,
|
|
||||||
cx: &DebugContext,
|
|
||||||
) -> serde_json::Value {
|
|
||||||
json!({
|
|
||||||
"type": "EventHandler",
|
|
||||||
"child": self.child.debug(cx),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,31 +13,32 @@ use crate::{
|
||||||
MouseButton, MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View,
|
MouseButton, MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{any::TypeId, ops::Range};
|
use std::{marker::PhantomData, ops::Range};
|
||||||
|
|
||||||
pub struct MouseEventHandler {
|
pub struct MouseEventHandler<Tag: 'static> {
|
||||||
child: ElementBox,
|
child: ElementBox,
|
||||||
discriminant: (TypeId, usize),
|
region_id: usize,
|
||||||
cursor_style: Option<CursorStyle>,
|
cursor_style: Option<CursorStyle>,
|
||||||
handlers: HandlerSet,
|
handlers: HandlerSet,
|
||||||
hoverable: bool,
|
hoverable: bool,
|
||||||
padding: Padding,
|
padding: Padding,
|
||||||
|
_tag: PhantomData<Tag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MouseEventHandler {
|
impl<Tag> MouseEventHandler<Tag> {
|
||||||
pub fn new<Tag, V, F>(id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
|
pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
|
||||||
where
|
where
|
||||||
Tag: 'static,
|
|
||||||
V: View,
|
V: View,
|
||||||
F: FnOnce(MouseState, &mut RenderContext<V>) -> ElementBox,
|
F: FnOnce(MouseState, &mut RenderContext<V>) -> ElementBox,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
child: render_child(cx.mouse_state::<Tag>(id), cx),
|
child: render_child(cx.mouse_state::<Tag>(region_id), cx),
|
||||||
|
region_id,
|
||||||
cursor_style: None,
|
cursor_style: None,
|
||||||
discriminant: (TypeId::of::<Tag>(), id),
|
|
||||||
handlers: Default::default(),
|
handlers: Default::default(),
|
||||||
hoverable: true,
|
hoverable: true,
|
||||||
padding: Default::default(),
|
padding: Default::default(),
|
||||||
|
_tag: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +141,7 @@ impl MouseEventHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for MouseEventHandler {
|
impl<Tag> Element for MouseEventHandler<Tag> {
|
||||||
type LayoutState = ();
|
type LayoutState = ();
|
||||||
type PaintState = ();
|
type PaintState = ();
|
||||||
|
|
||||||
|
@ -167,13 +168,15 @@ impl Element for MouseEventHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.scene.push_mouse_region(MouseRegion {
|
cx.scene.push_mouse_region(
|
||||||
view_id: cx.current_view_id(),
|
MouseRegion::from_handlers::<Tag>(
|
||||||
discriminant: self.discriminant,
|
cx.current_view_id(),
|
||||||
bounds: hit_bounds,
|
self.region_id,
|
||||||
handlers: self.handlers.clone(),
|
hit_bounds,
|
||||||
hoverable: self.hoverable,
|
self.handlers.clone(),
|
||||||
});
|
)
|
||||||
|
.with_hoverable(self.hoverable),
|
||||||
|
);
|
||||||
|
|
||||||
self.child.paint(bounds.origin(), visible_bounds, cx);
|
self.child.paint(bounds.origin(), visible_bounds, cx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,98 @@
|
||||||
use std::{any::TypeId, ops::Range};
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json::ToJson,
|
json::ToJson,
|
||||||
presenter::MeasurementContext,
|
presenter::MeasurementContext,
|
||||||
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion,
|
Axis, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion,
|
||||||
PaintContext, SizeConstraint,
|
PaintContext, SizeConstraint,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
pub struct Overlay {
|
pub struct Overlay {
|
||||||
child: ElementBox,
|
child: ElementBox,
|
||||||
abs_position: Option<Vector2F>,
|
anchor_position: Option<Vector2F>,
|
||||||
fit_mode: OverlayFitMode,
|
fit_mode: OverlayFitMode,
|
||||||
|
anchor_corner: AnchorCorner,
|
||||||
hoverable: bool,
|
hoverable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum OverlayFitMode {
|
pub enum OverlayFitMode {
|
||||||
SnapToWindow,
|
SnapToWindow,
|
||||||
FlipAlignment,
|
SwitchAnchor,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AnchorCorner {
|
||||||
|
TopLeft,
|
||||||
|
TopRight,
|
||||||
|
BottomLeft,
|
||||||
|
BottomRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnchorCorner {
|
||||||
|
fn get_bounds(&self, anchor_position: Vector2F, size: Vector2F) -> RectF {
|
||||||
|
match self {
|
||||||
|
Self::TopLeft => RectF::from_points(anchor_position, anchor_position + size),
|
||||||
|
Self::TopRight => RectF::from_points(
|
||||||
|
anchor_position - Vector2F::new(size.x(), 0.),
|
||||||
|
anchor_position + Vector2F::new(0., size.y()),
|
||||||
|
),
|
||||||
|
Self::BottomLeft => RectF::from_points(
|
||||||
|
anchor_position - Vector2F::new(0., size.y()),
|
||||||
|
anchor_position + Vector2F::new(size.x(), 0.),
|
||||||
|
),
|
||||||
|
Self::BottomRight => RectF::from_points(anchor_position - size, anchor_position),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Overlay {
|
impl Overlay {
|
||||||
pub fn new(child: ElementBox) -> Self {
|
pub fn new(child: ElementBox) -> Self {
|
||||||
Self {
|
Self {
|
||||||
child,
|
child,
|
||||||
abs_position: None,
|
anchor_position: None,
|
||||||
fit_mode: OverlayFitMode::None,
|
fit_mode: OverlayFitMode::None,
|
||||||
|
anchor_corner: AnchorCorner::TopLeft,
|
||||||
hoverable: false,
|
hoverable: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_abs_position(mut self, position: Vector2F) -> Self {
|
pub fn with_anchor_position(mut self, position: Vector2F) -> Self {
|
||||||
self.abs_position = Some(position);
|
self.anchor_position = Some(position);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fit_mode(mut self, fit_mode: OverlayFitMode) -> Self {
|
pub fn with_anchor_corner(mut self, anchor_corner: AnchorCorner) -> Self {
|
||||||
|
self.anchor_corner = anchor_corner;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_fit_mode(mut self, fit_mode: OverlayFitMode) -> Self {
|
||||||
self.fit_mode = fit_mode;
|
self.fit_mode = fit_mode;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hoverable(mut self, hoverable: bool) -> Self {
|
pub fn with_hoverable(mut self, hoverable: bool) -> Self {
|
||||||
self.hoverable = hoverable;
|
self.hoverable = hoverable;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -58,7 +107,7 @@ impl Element for Overlay {
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let constraint = if self.abs_position.is_some() {
|
let constraint = if self.anchor_position.is_some() {
|
||||||
SizeConstraint::new(Vector2F::zero(), cx.window_size)
|
SizeConstraint::new(Vector2F::zero(), cx.window_size)
|
||||||
} else {
|
} else {
|
||||||
constraint
|
constraint
|
||||||
|
@ -74,46 +123,74 @@ impl Element for Overlay {
|
||||||
size: &mut Self::LayoutState,
|
size: &mut Self::LayoutState,
|
||||||
cx: &mut PaintContext,
|
cx: &mut PaintContext,
|
||||||
) {
|
) {
|
||||||
let mut bounds = RectF::new(self.abs_position.unwrap_or_else(|| bounds.origin()), *size);
|
let anchor_position = self.anchor_position.unwrap_or_else(|| bounds.origin());
|
||||||
|
let mut bounds = self.anchor_corner.get_bounds(anchor_position, *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 bounds.max_x() > cx.window_size.x() {
|
||||||
|
let mut lower_right = bounds.lower_right();
|
||||||
|
lower_right.set_x(cx.window_size.x());
|
||||||
|
bounds = RectF::from_points(lower_right - *size, lower_right);
|
||||||
|
} else if bounds.min_x() < 0. {
|
||||||
|
let mut upper_left = bounds.origin();
|
||||||
|
upper_left.set_x(0.);
|
||||||
|
bounds = RectF::from_points(upper_left, upper_left + *size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snap the vertical edges of the overlay to the vertical edges of the window if
|
||||||
|
// its vertical bounds overflow.
|
||||||
|
if bounds.max_y() > cx.window_size.y() {
|
||||||
|
let mut lower_right = bounds.lower_right();
|
||||||
|
lower_right.set_y(cx.window_size.y());
|
||||||
|
bounds = RectF::from_points(lower_right - *size, lower_right);
|
||||||
|
} else if bounds.min_y() < 0. {
|
||||||
|
let mut upper_left = bounds.origin();
|
||||||
|
upper_left.set_y(0.);
|
||||||
|
bounds = RectF::from_points(upper_left, upper_left + *size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OverlayFitMode::SwitchAnchor => {
|
||||||
|
let mut anchor_corner = self.anchor_corner;
|
||||||
|
|
||||||
|
if bounds.max_x() > cx.window_size.x() {
|
||||||
|
anchor_corner = anchor_corner.switch_axis(Axis::Horizontal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if bounds.max_y() > cx.window_size.y() {
|
||||||
|
anchor_corner = anchor_corner.switch_axis(Axis::Vertical);
|
||||||
|
}
|
||||||
|
|
||||||
|
if bounds.min_x() < 0. {
|
||||||
|
anchor_corner = anchor_corner.switch_axis(Axis::Horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bounds.min_y() < 0. {
|
||||||
|
anchor_corner = anchor_corner.switch_axis(Axis::Vertical)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update bounds if needed
|
||||||
|
if anchor_corner != self.anchor_corner {
|
||||||
|
bounds = anchor_corner.get_bounds(anchor_position, *size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OverlayFitMode::None => {}
|
||||||
|
}
|
||||||
|
|
||||||
cx.scene.push_stacking_context(None);
|
cx.scene.push_stacking_context(None);
|
||||||
|
|
||||||
if self.hoverable {
|
if self.hoverable {
|
||||||
enum OverlayHoverCapture {}
|
enum OverlayHoverCapture {}
|
||||||
cx.scene.push_mouse_region(MouseRegion {
|
cx.scene.push_mouse_region(
|
||||||
view_id: cx.current_view_id(),
|
MouseRegion::new::<OverlayHoverCapture>(
|
||||||
bounds,
|
cx.current_view_id(),
|
||||||
discriminant: (TypeId::of::<OverlayHoverCapture>(), cx.current_view_id()),
|
cx.current_view_id(),
|
||||||
handlers: Default::default(),
|
bounds,
|
||||||
hoverable: true,
|
)
|
||||||
});
|
.with_hoverable(true),
|
||||||
}
|
);
|
||||||
|
|
||||||
match self.fit_mode {
|
|
||||||
OverlayFitMode::SnapToWindow => {
|
|
||||||
// Snap the right edge of the overlay to the right edge of the window if
|
|
||||||
// its horizontal bounds overflow.
|
|
||||||
if bounds.lower_right().x() > cx.window_size.x() {
|
|
||||||
bounds.set_origin_x((cx.window_size.x() - bounds.width()).max(0.));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snap the bottom edge of the overlay to the bottom edge of the window if
|
|
||||||
// its vertical bounds overflow.
|
|
||||||
if bounds.lower_right().y() > cx.window_size.y() {
|
|
||||||
bounds.set_origin_y((cx.window_size.y() - bounds.height()).max(0.));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OverlayFitMode::FlipAlignment => {
|
|
||||||
// Right-align overlay if its horizontal bounds overflow.
|
|
||||||
if bounds.lower_right().x() > cx.window_size.x() {
|
|
||||||
bounds.set_origin_x(bounds.origin_x() - bounds.width());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bottom-align overlay if its vertical bounds overflow.
|
|
||||||
if bounds.lower_right().y() > cx.window_size.y() {
|
|
||||||
bounds.set_origin_y(bounds.origin_y() - bounds.height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OverlayFitMode::None => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.child.paint(bounds.origin(), bounds, cx);
|
self.child.paint(bounds.origin(), bounds, cx);
|
||||||
|
@ -153,7 +230,7 @@ impl Element for Overlay {
|
||||||
) -> serde_json::Value {
|
) -> serde_json::Value {
|
||||||
json!({
|
json!({
|
||||||
"type": "Overlay",
|
"type": "Overlay",
|
||||||
"abs_position": self.abs_position.to_json(),
|
"abs_position": self.anchor_position.to_json(),
|
||||||
"child": self.child.debug(cx),
|
"child": self.child.debug(cx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,42 +84,41 @@ impl Tooltip {
|
||||||
})
|
})
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.fit_mode(OverlayFitMode::FlipAlignment)
|
.with_fit_mode(OverlayFitMode::SwitchAnchor)
|
||||||
.with_abs_position(state.position.get())
|
.with_anchor_position(state.position.get())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let child =
|
let child = MouseEventHandler::<MouseEventHandlerState<Tag>>::new(id, cx, |_, _| child)
|
||||||
MouseEventHandler::new::<MouseEventHandlerState<Tag>, _, _>(id, cx, |_, _| child)
|
.on_hover(move |e, cx| {
|
||||||
.on_hover(move |e, cx| {
|
let position = e.position;
|
||||||
let position = e.position;
|
let window_id = cx.window_id();
|
||||||
let window_id = cx.window_id();
|
if let Some(view_id) = cx.view_id() {
|
||||||
if let Some(view_id) = cx.view_id() {
|
if e.started {
|
||||||
if e.started {
|
if !state.visible.get() {
|
||||||
if !state.visible.get() {
|
state.position.set(position);
|
||||||
state.position.set(position);
|
|
||||||
|
|
||||||
let mut debounce = state.debounce.borrow_mut();
|
let mut debounce = state.debounce.borrow_mut();
|
||||||
if debounce.is_none() {
|
if debounce.is_none() {
|
||||||
*debounce = Some(cx.spawn({
|
*debounce = Some(cx.spawn({
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
|mut cx| async move {
|
|mut cx| async move {
|
||||||
cx.background().timer(DEBOUNCE_TIMEOUT).await;
|
cx.background().timer(DEBOUNCE_TIMEOUT).await;
|
||||||
state.visible.set(true);
|
state.visible.set(true);
|
||||||
cx.update(|cx| cx.notify_view(window_id, view_id));
|
cx.update(|cx| cx.notify_view(window_id, view_id));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state.visible.set(false);
|
|
||||||
state.debounce.take();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
state.visible.set(false);
|
||||||
|
state.debounce.take();
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.boxed();
|
})
|
||||||
|
.boxed();
|
||||||
Self {
|
Self {
|
||||||
child,
|
child,
|
||||||
tooltip,
|
tooltip,
|
||||||
|
|
|
@ -372,13 +372,13 @@ impl Presenter {
|
||||||
//Ensure that hover entrance events aren't sent twice
|
//Ensure that hover entrance events aren't sent twice
|
||||||
if self.hovered_region_ids.insert(region.id()) {
|
if self.hovered_region_ids.insert(region.id()) {
|
||||||
valid_regions.push(region.clone());
|
valid_regions.push(region.clone());
|
||||||
invalidated_views.insert(region.view_id);
|
invalidated_views.insert(region.id().view_id());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ensure that hover exit events aren't sent twice
|
// Ensure that hover exit events aren't sent twice
|
||||||
if self.hovered_region_ids.remove(®ion.id()) {
|
if self.hovered_region_ids.remove(®ion.id()) {
|
||||||
valid_regions.push(region.clone());
|
valid_regions.push(region.clone());
|
||||||
invalidated_views.insert(region.view_id);
|
invalidated_views.insert(region.id().view_id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,8 +452,10 @@ impl Presenter {
|
||||||
|
|
||||||
if let Some(callback) = valid_region.handlers.get(®ion_event.handler_key()) {
|
if let Some(callback) = valid_region.handlers.get(®ion_event.handler_key()) {
|
||||||
event_cx.handled = true;
|
event_cx.handled = true;
|
||||||
event_cx.invalidated_views.insert(valid_region.view_id);
|
event_cx
|
||||||
event_cx.with_current_view(valid_region.view_id, {
|
.invalidated_views
|
||||||
|
.insert(valid_region.id().view_id());
|
||||||
|
event_cx.with_current_view(valid_region.id().view_id(), {
|
||||||
let region_event = region_event.clone();
|
let region_event = region_event.clone();
|
||||||
|cx| {
|
|cx| {
|
||||||
callback(region_event, cx);
|
callback(region_event, cx);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod mouse_region;
|
mod mouse_region;
|
||||||
mod mouse_region_event;
|
mod mouse_region_event;
|
||||||
|
|
||||||
|
use collections::HashSet;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
@ -20,6 +21,8 @@ pub struct Scene {
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
stacking_contexts: Vec<StackingContext>,
|
stacking_contexts: Vec<StackingContext>,
|
||||||
active_stacking_context_stack: Vec<usize>,
|
active_stacking_context_stack: Vec<usize>,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
mouse_region_ids: HashSet<MouseRegionId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StackingContext {
|
struct StackingContext {
|
||||||
|
@ -177,6 +180,8 @@ impl Scene {
|
||||||
scale_factor,
|
scale_factor,
|
||||||
stacking_contexts: vec![stacking_context],
|
stacking_contexts: vec![stacking_context],
|
||||||
active_stacking_context_stack: vec![0],
|
active_stacking_context_stack: vec![0],
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
mouse_region_ids: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +246,23 @@ impl Scene {
|
||||||
|
|
||||||
pub fn push_mouse_region(&mut self, region: MouseRegion) {
|
pub fn push_mouse_region(&mut self, region: MouseRegion) {
|
||||||
if can_draw(region.bounds) {
|
if can_draw(region.bounds) {
|
||||||
self.active_layer().push_mouse_region(region);
|
// Ensure that Regions cannot be added to a scene with the same region id.
|
||||||
|
let region_id;
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
region_id = region.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.active_layer().push_mouse_region(region) {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
if !self.mouse_region_ids.insert(region_id) {
|
||||||
|
let tag_name = region_id.tag_type_name();
|
||||||
|
panic!("Same MouseRegionId: {region_id:?} inserted multiple times to the same scene. \
|
||||||
|
Will cause problems! Look for MouseRegion that uses Tag: {tag_name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,15 +385,17 @@ impl Layer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_mouse_region(&mut self, region: MouseRegion) {
|
fn push_mouse_region(&mut self, region: MouseRegion) -> bool {
|
||||||
if let Some(bounds) = region
|
if let Some(bounds) = region
|
||||||
.bounds
|
.bounds
|
||||||
.intersection(self.clip_bounds.unwrap_or(region.bounds))
|
.intersection(self.clip_bounds.unwrap_or(region.bounds))
|
||||||
{
|
{
|
||||||
if can_draw(bounds) {
|
if can_draw(bounds) {
|
||||||
self.mouse_regions.push(region);
|
self.mouse_regions.push(region);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_underline(&mut self, underline: Underline) {
|
fn push_underline(&mut self, underline: Underline) {
|
||||||
|
@ -537,10 +560,7 @@ impl ToJson for Border {
|
||||||
|
|
||||||
impl MouseRegion {
|
impl MouseRegion {
|
||||||
pub fn id(&self) -> MouseRegionId {
|
pub fn id(&self) -> MouseRegionId {
|
||||||
MouseRegionId {
|
self.id
|
||||||
view_id: self.view_id,
|
|
||||||
discriminant: self.discriminant,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{any::TypeId, mem::Discriminant, rc::Rc};
|
use std::{any::TypeId, fmt::Debug, mem::Discriminant, rc::Rc};
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
|
||||||
|
@ -16,8 +16,7 @@ use super::{
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MouseRegion {
|
pub struct MouseRegion {
|
||||||
pub view_id: usize,
|
pub id: MouseRegionId,
|
||||||
pub discriminant: (TypeId, usize),
|
|
||||||
pub bounds: RectF,
|
pub bounds: RectF,
|
||||||
pub handlers: HandlerSet,
|
pub handlers: HandlerSet,
|
||||||
pub hoverable: bool,
|
pub hoverable: bool,
|
||||||
|
@ -43,8 +42,13 @@ impl MouseRegion {
|
||||||
handlers: HandlerSet,
|
handlers: HandlerSet,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_id,
|
id: MouseRegionId {
|
||||||
discriminant: (TypeId::of::<Tag>(), region_id),
|
view_id,
|
||||||
|
tag: TypeId::of::<Tag>(),
|
||||||
|
region_id,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tag_type_name: std::any::type_name::<Tag>(),
|
||||||
|
},
|
||||||
bounds,
|
bounds,
|
||||||
handlers,
|
handlers,
|
||||||
hoverable: true,
|
hoverable: true,
|
||||||
|
@ -137,8 +141,32 @@ impl MouseRegion {
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
pub struct MouseRegionId {
|
pub struct MouseRegionId {
|
||||||
pub view_id: usize,
|
view_id: usize,
|
||||||
pub discriminant: (TypeId, usize),
|
tag: TypeId,
|
||||||
|
region_id: usize,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tag_type_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MouseRegionId {
|
||||||
|
pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
|
||||||
|
MouseRegionId {
|
||||||
|
view_id,
|
||||||
|
region_id,
|
||||||
|
tag: TypeId::of::<Tag>(),
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tag_type_name: std::any::type_name::<Tag>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view_id(&self) -> usize {
|
||||||
|
self.view_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub fn tag_type_name(&self) -> &'static str {
|
||||||
|
self.tag_type_name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl View for Select {
|
||||||
Default::default()
|
Default::default()
|
||||||
};
|
};
|
||||||
let mut result = Flex::column().with_child(
|
let mut result = Flex::column().with_child(
|
||||||
MouseEventHandler::new::<Header, _, _>(self.handle.id(), cx, |mouse_state, cx| {
|
MouseEventHandler::<Header>::new(self.handle.id(), cx, |mouse_state, cx| {
|
||||||
Container::new((self.render_item)(
|
Container::new((self.render_item)(
|
||||||
self.selected_item_ix,
|
self.selected_item_ix,
|
||||||
ItemType::Header,
|
ItemType::Header,
|
||||||
|
@ -137,22 +137,18 @@ impl View for Select {
|
||||||
let selected_item_ix = this.selected_item_ix;
|
let selected_item_ix = this.selected_item_ix;
|
||||||
range.end = range.end.min(this.item_count);
|
range.end = range.end.min(this.item_count);
|
||||||
items.extend(range.map(|ix| {
|
items.extend(range.map(|ix| {
|
||||||
MouseEventHandler::new::<Item, _, _>(
|
MouseEventHandler::<Item>::new(ix, cx, |mouse_state, cx| {
|
||||||
ix,
|
(this.render_item)(
|
||||||
cx,
|
ix,
|
||||||
|mouse_state, cx| {
|
if ix == selected_item_ix {
|
||||||
(this.render_item)(
|
ItemType::Selected
|
||||||
ix,
|
} else {
|
||||||
if ix == selected_item_ix {
|
ItemType::Unselected
|
||||||
ItemType::Selected
|
},
|
||||||
} else {
|
mouse_state.hovered,
|
||||||
ItemType::Unselected
|
cx,
|
||||||
},
|
)
|
||||||
mouse_state.hovered,
|
})
|
||||||
cx,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.on_click(MouseButton::Left, move |_, cx| {
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
cx.dispatch_action(SelectItem(ix))
|
cx.dispatch_action(SelectItem(ix))
|
||||||
})
|
})
|
||||||
|
|
|
@ -85,7 +85,7 @@ impl<D: PickerDelegate> View for Picker<D> {
|
||||||
let selected_ix = delegate.read(cx).selected_index();
|
let selected_ix = delegate.read(cx).selected_index();
|
||||||
range.end = cmp::min(range.end, delegate.read(cx).match_count());
|
range.end = cmp::min(range.end, delegate.read(cx).match_count());
|
||||||
items.extend(range.map(move |ix| {
|
items.extend(range.map(move |ix| {
|
||||||
MouseEventHandler::new::<D, _, _>(ix, cx, |state, cx| {
|
MouseEventHandler::<D>::new(ix, cx, |state, cx| {
|
||||||
delegate
|
delegate
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.render_match(ix, state, ix == selected_ix, cx)
|
.render_match(ix, state, ix == selected_ix, cx)
|
||||||
|
|
|
@ -5,8 +5,8 @@ use gpui::{
|
||||||
actions,
|
actions,
|
||||||
anyhow::{anyhow, Result},
|
anyhow::{anyhow, Result},
|
||||||
elements::{
|
elements::{
|
||||||
ChildView, ConstrainedBox, Empty, Flex, Label, MouseEventHandler, ParentElement,
|
AnchorCorner, ChildView, ConstrainedBox, Empty, Flex, Label, MouseEventHandler,
|
||||||
ScrollTarget, Stack, Svg, UniformList, UniformListState,
|
ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState,
|
||||||
},
|
},
|
||||||
geometry::vector::Vector2F,
|
geometry::vector::Vector2F,
|
||||||
impl_internal_actions, keymap,
|
impl_internal_actions, keymap,
|
||||||
|
@ -302,7 +302,7 @@ impl ProjectPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.context_menu.update(cx, |menu, cx| {
|
self.context_menu.update(cx, |menu, cx| {
|
||||||
menu.show(action.position, menu_entries, cx);
|
menu.show(action.position, AnchorCorner::TopLeft, menu_entries, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -1012,7 +1012,7 @@ impl ProjectPanel {
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
let kind = details.kind;
|
let kind = details.kind;
|
||||||
let show_editor = details.is_editing && !details.is_processing;
|
let show_editor = details.is_editing && !details.is_processing;
|
||||||
MouseEventHandler::new::<Self, _, _>(entry_id.to_usize(), cx, |state, _| {
|
MouseEventHandler::<Self>::new(entry_id.to_usize(), cx, |state, _| {
|
||||||
let padding = theme.container.padding.left + details.depth as f32 * theme.indent_width;
|
let padding = theme.container.padding.left + details.depth as f32 * theme.indent_width;
|
||||||
let mut style = theme.entry.style_for(state, details.is_selected).clone();
|
let mut style = theme.entry.style_for(state, details.is_selected).clone();
|
||||||
if details.is_ignored {
|
if details.is_ignored {
|
||||||
|
@ -1107,7 +1107,7 @@ impl View for ProjectPanel {
|
||||||
let last_worktree_root_id = self.last_worktree_root_id;
|
let last_worktree_root_id = self.last_worktree_root_id;
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::new::<Tag, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<Tag>::new(0, cx, |_, cx| {
|
||||||
UniformList::new(
|
UniformList::new(
|
||||||
self.list.clone(),
|
self.list.clone(),
|
||||||
self.visible_entries
|
self.visible_entries
|
||||||
|
|
|
@ -319,7 +319,7 @@ impl BufferSearchBar {
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
let is_active = self.is_search_option_enabled(option);
|
let is_active = self.is_search_option_enabled(option);
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::new::<Self, _, _>(option as usize, cx, |state, cx| {
|
MouseEventHandler::<Self>::new(option as usize, cx, |state, cx| {
|
||||||
let style = &cx
|
let style = &cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
@ -367,7 +367,7 @@ impl BufferSearchBar {
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
|
|
||||||
enum NavButton {}
|
enum NavButton {}
|
||||||
MouseEventHandler::new::<NavButton, _, _>(direction as usize, cx, |state, cx| {
|
MouseEventHandler::<NavButton>::new(direction as usize, cx, |state, cx| {
|
||||||
let style = &cx
|
let style = &cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
|
|
@ -176,7 +176,7 @@ impl View for ProjectSearchView {
|
||||||
} else {
|
} else {
|
||||||
"No results"
|
"No results"
|
||||||
};
|
};
|
||||||
MouseEventHandler::new::<Status, _, _>(0, cx, |_, _| {
|
MouseEventHandler::<Status>::new(0, cx, |_, _| {
|
||||||
Label::new(text.to_string(), theme.search.results_status.clone())
|
Label::new(text.to_string(), theme.search.results_status.clone())
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
|
@ -723,7 +723,7 @@ impl ProjectSearchBar {
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
|
|
||||||
enum NavButton {}
|
enum NavButton {}
|
||||||
MouseEventHandler::new::<NavButton, _, _>(direction as usize, cx, |state, cx| {
|
MouseEventHandler::<NavButton>::new(direction as usize, cx, |state, cx| {
|
||||||
let style = &cx
|
let style = &cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
@ -758,7 +758,7 @@ impl ProjectSearchBar {
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
let is_active = self.is_option_enabled(option, cx);
|
let is_active = self.is_option_enabled(option, cx);
|
||||||
MouseEventHandler::new::<Self, _, _>(option as usize, cx, |state, cx| {
|
MouseEventHandler::<Self>::new(option as usize, cx, |state, cx| {
|
||||||
let style = &cx
|
let style = &cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
|
|
@ -4,7 +4,7 @@ use alacritty_terminal::{index::Point, term::TermMode};
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{ChildView, ParentElement, Stack},
|
elements::{AnchorCorner, ChildView, ParentElement, Stack},
|
||||||
geometry::vector::Vector2F,
|
geometry::vector::Vector2F,
|
||||||
impl_internal_actions,
|
impl_internal_actions,
|
||||||
keymap::Keystroke,
|
keymap::Keystroke,
|
||||||
|
@ -139,8 +139,9 @@ impl TerminalView {
|
||||||
ContextMenuItem::item("Close Terminal", pane::CloseActiveItem),
|
ContextMenuItem::item("Close Terminal", pane::CloseActiveItem),
|
||||||
];
|
];
|
||||||
|
|
||||||
self.context_menu
|
self.context_menu.update(cx, |menu, cx| {
|
||||||
.update(cx, |menu, cx| menu.show(action.position, menu_entries, cx));
|
menu.show(action.position, AnchorCorner::TopLeft, menu_entries, cx)
|
||||||
|
});
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,12 @@ impl Default for DockPosition {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DockPosition {
|
impl DockPosition {
|
||||||
|
fn anchor(&self) -> DockAnchor {
|
||||||
|
match self {
|
||||||
|
DockPosition::Shown(anchor) | DockPosition::Hidden(anchor) => *anchor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn toggle(self) -> Self {
|
fn toggle(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
DockPosition::Shown(anchor) => DockPosition::Hidden(anchor),
|
DockPosition::Shown(anchor) => DockPosition::Hidden(anchor),
|
||||||
|
@ -70,7 +76,8 @@ pub struct Dock {
|
||||||
|
|
||||||
impl Dock {
|
impl Dock {
|
||||||
pub fn new(cx: &mut ViewContext<Workspace>, default_item_factory: DefaultItemFactory) -> Self {
|
pub fn new(cx: &mut ViewContext<Workspace>, default_item_factory: DefaultItemFactory) -> Self {
|
||||||
let pane = cx.add_view(|cx| Pane::new(true, cx));
|
let anchor = cx.global::<Settings>().default_dock_anchor;
|
||||||
|
let pane = cx.add_view(|cx| Pane::new(Some(anchor), cx));
|
||||||
let pane_id = pane.id();
|
let pane_id = pane.id();
|
||||||
cx.subscribe(&pane, move |workspace, _, event, cx| {
|
cx.subscribe(&pane, move |workspace, _, event, cx| {
|
||||||
workspace.handle_pane_event(pane_id, event, cx);
|
workspace.handle_pane_event(pane_id, event, cx);
|
||||||
|
@ -79,7 +86,7 @@ impl Dock {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pane,
|
pane,
|
||||||
position: DockPosition::Hidden(cx.global::<Settings>().default_dock_anchor),
|
position: DockPosition::Hidden(anchor),
|
||||||
default_item_factory,
|
default_item_factory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +105,11 @@ impl Dock {
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
) {
|
||||||
workspace.dock.position = new_position;
|
workspace.dock.position = new_position;
|
||||||
|
// Tell the pane about the new anchor position
|
||||||
|
workspace.dock.pane.update(cx, |pane, cx| {
|
||||||
|
pane.set_docked(Some(new_position.anchor()), cx)
|
||||||
|
});
|
||||||
|
|
||||||
let now_visible = workspace.dock.visible_pane().is_some();
|
let now_visible = workspace.dock.visible_pane().is_some();
|
||||||
if now_visible {
|
if now_visible {
|
||||||
// Ensure that the pane has at least one item or construct a default item to put in it
|
// Ensure that the pane has at least one item or construct a default item to put in it
|
||||||
|
@ -164,7 +176,7 @@ impl Dock {
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
DockAnchor::Expanded => Container::new(
|
DockAnchor::Expanded => Container::new(
|
||||||
MouseEventHandler::new::<Dock, _, _>(0, cx, |_state, _cx| {
|
MouseEventHandler::<Dock>::new(0, cx, |_state, _cx| {
|
||||||
Container::new(ChildView::new(self.pane.clone()).boxed())
|
Container::new(ChildView::new(self.pane.clone()).boxed())
|
||||||
.with_style(style.maximized)
|
.with_style(style.maximized)
|
||||||
.boxed()
|
.boxed()
|
||||||
|
@ -205,7 +217,7 @@ impl View for ToggleDockButton {
|
||||||
.map(|workspace| workspace.read(cx).dock.position.visible().is_some())
|
.map(|workspace| workspace.read(cx).dock.position.visible().is_some())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
|
MouseEventHandler::<Self>::new(0, cx, |state, cx| {
|
||||||
let theme = &cx
|
let theme = &cx
|
||||||
.global::<Settings>()
|
.global::<Settings>()
|
||||||
.theme
|
.theme
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use super::{ItemHandle, SplitDirection};
|
use super::{ItemHandle, SplitDirection};
|
||||||
use crate::{
|
use crate::{
|
||||||
dock::MoveDock, toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHandle,
|
dock::{MoveDock, ToggleDock},
|
||||||
Workspace,
|
toolbar::Toolbar,
|
||||||
|
Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
|
@ -18,7 +19,7 @@ use gpui::{
|
||||||
},
|
},
|
||||||
impl_actions, impl_internal_actions,
|
impl_actions, impl_internal_actions,
|
||||||
platform::{CursorStyle, NavigationDirection},
|
platform::{CursorStyle, NavigationDirection},
|
||||||
AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
||||||
ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
||||||
ViewContext, ViewHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
|
@ -204,8 +205,8 @@ pub struct Pane {
|
||||||
autoscroll: bool,
|
autoscroll: bool,
|
||||||
nav_history: Rc<RefCell<NavHistory>>,
|
nav_history: Rc<RefCell<NavHistory>>,
|
||||||
toolbar: ViewHandle<Toolbar>,
|
toolbar: ViewHandle<Toolbar>,
|
||||||
context_menu: ViewHandle<ContextMenu>,
|
tab_bar_context_menu: ViewHandle<ContextMenu>,
|
||||||
is_dock: bool,
|
docked: Option<DockAnchor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ItemNavHistory {
|
pub struct ItemNavHistory {
|
||||||
|
@ -255,7 +256,7 @@ pub enum ReorderBehavior {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pane {
|
impl Pane {
|
||||||
pub fn new(is_dock: bool, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(docked: Option<DockAnchor>, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let handle = cx.weak_handle();
|
let handle = cx.weak_handle();
|
||||||
let context_menu = cx.add_view(ContextMenu::new);
|
let context_menu = cx.add_view(ContextMenu::new);
|
||||||
Self {
|
Self {
|
||||||
|
@ -273,8 +274,8 @@ impl Pane {
|
||||||
pane: handle.clone(),
|
pane: handle.clone(),
|
||||||
})),
|
})),
|
||||||
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
||||||
context_menu,
|
tab_bar_context_menu: context_menu,
|
||||||
is_dock,
|
docked,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +284,11 @@ impl Pane {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_docked(&mut self, docked: Option<DockAnchor>, cx: &mut ViewContext<Self>) {
|
||||||
|
self.docked = docked;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn nav_history_for_item<T: Item>(&self, item: &ViewHandle<T>) -> ItemNavHistory {
|
pub fn nav_history_for_item<T: Item>(&self, item: &ViewHandle<T>) -> ItemNavHistory {
|
||||||
ItemNavHistory {
|
ItemNavHistory {
|
||||||
history: self.nav_history.clone(),
|
history: self.nav_history.clone(),
|
||||||
|
@ -983,9 +989,10 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deploy_split_menu(&mut self, action: &DeploySplitMenu, cx: &mut ViewContext<Self>) {
|
fn deploy_split_menu(&mut self, action: &DeploySplitMenu, cx: &mut ViewContext<Self>) {
|
||||||
self.context_menu.update(cx, |menu, cx| {
|
self.tab_bar_context_menu.update(cx, |menu, cx| {
|
||||||
menu.show(
|
menu.show(
|
||||||
action.position,
|
action.position,
|
||||||
|
AnchorCorner::TopRight,
|
||||||
vec![
|
vec![
|
||||||
ContextMenuItem::item("Split Right", SplitRight),
|
ContextMenuItem::item("Split Right", SplitRight),
|
||||||
ContextMenuItem::item("Split Left", SplitLeft),
|
ContextMenuItem::item("Split Left", SplitLeft),
|
||||||
|
@ -998,9 +1005,10 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deploy_dock_menu(&mut self, action: &DeployDockMenu, cx: &mut ViewContext<Self>) {
|
fn deploy_dock_menu(&mut self, action: &DeployDockMenu, cx: &mut ViewContext<Self>) {
|
||||||
self.context_menu.update(cx, |menu, cx| {
|
self.tab_bar_context_menu.update(cx, |menu, cx| {
|
||||||
menu.show(
|
menu.show(
|
||||||
action.position,
|
action.position,
|
||||||
|
AnchorCorner::TopRight,
|
||||||
vec![
|
vec![
|
||||||
ContextMenuItem::item("Move Dock Right", MoveDock(DockAnchor::Right)),
|
ContextMenuItem::item("Move Dock Right", MoveDock(DockAnchor::Right)),
|
||||||
ContextMenuItem::item("Move Dock Bottom", MoveDock(DockAnchor::Bottom)),
|
ContextMenuItem::item("Move Dock Bottom", MoveDock(DockAnchor::Bottom)),
|
||||||
|
@ -1012,9 +1020,10 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
|
fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
|
||||||
self.context_menu.update(cx, |menu, cx| {
|
self.tab_bar_context_menu.update(cx, |menu, cx| {
|
||||||
menu.show(
|
menu.show(
|
||||||
action.position,
|
action.position,
|
||||||
|
AnchorCorner::TopRight,
|
||||||
vec![
|
vec![
|
||||||
ContextMenuItem::item("New File", NewFile),
|
ContextMenuItem::item("New File", NewFile),
|
||||||
ContextMenuItem::item("New Terminal", NewTerminal),
|
ContextMenuItem::item("New Terminal", NewTerminal),
|
||||||
|
@ -1047,7 +1056,7 @@ impl Pane {
|
||||||
enum Tab {}
|
enum Tab {}
|
||||||
enum Filler {}
|
enum Filler {}
|
||||||
let pane = cx.handle();
|
let pane = cx.handle();
|
||||||
MouseEventHandler::new::<Tabs, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<Tabs>::new(0, cx, |_, cx| {
|
||||||
let autoscroll = if mem::take(&mut self.autoscroll) {
|
let autoscroll = if mem::take(&mut self.autoscroll) {
|
||||||
Some(self.active_item_index)
|
Some(self.active_item_index)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1068,7 +1077,7 @@ impl Pane {
|
||||||
let tab_active = ix == self.active_item_index;
|
let tab_active = ix == self.active_item_index;
|
||||||
|
|
||||||
row.add_child({
|
row.add_child({
|
||||||
MouseEventHandler::new::<Tab, _, _>(ix, cx, {
|
MouseEventHandler::<Tab>::new(ix, cx, {
|
||||||
let item = item.clone();
|
let item = item.clone();
|
||||||
let pane = pane.clone();
|
let pane = pane.clone();
|
||||||
let detail = detail.clone();
|
let detail = detail.clone();
|
||||||
|
@ -1143,7 +1152,7 @@ impl Pane {
|
||||||
// the filler
|
// the filler
|
||||||
let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
|
let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
|
||||||
row.add_child(
|
row.add_child(
|
||||||
MouseEventHandler::new::<Filler, _, _>(0, cx, |mouse_state, cx| {
|
MouseEventHandler::<Filler>::new(0, cx, |mouse_state, cx| {
|
||||||
let mut filler = Empty::new()
|
let mut filler = Empty::new()
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(filler_style.container)
|
.with_style(filler_style.container)
|
||||||
|
@ -1265,17 +1274,13 @@ impl Pane {
|
||||||
let item_id = item.id();
|
let item_id = item.id();
|
||||||
enum TabCloseButton {}
|
enum TabCloseButton {}
|
||||||
let icon = Svg::new("icons/x_mark_thin_8.svg");
|
let icon = Svg::new("icons/x_mark_thin_8.svg");
|
||||||
MouseEventHandler::new::<TabCloseButton, _, _>(
|
MouseEventHandler::<TabCloseButton>::new(item_id, cx, |mouse_state, _| {
|
||||||
item_id,
|
if mouse_state.hovered {
|
||||||
cx,
|
icon.with_color(tab_style.icon_close_active).boxed()
|
||||||
|mouse_state, _| {
|
} else {
|
||||||
if mouse_state.hovered {
|
icon.with_color(tab_style.icon_close).boxed()
|
||||||
icon.with_color(tab_style.icon_close_active).boxed()
|
}
|
||||||
} else {
|
})
|
||||||
icon.with_color(tab_style.icon_close).boxed()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.with_padding(Padding::uniform(4.))
|
.with_padding(Padding::uniform(4.))
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.on_click(MouseButton::Left, {
|
.on_click(MouseButton::Left, {
|
||||||
|
@ -1351,132 +1356,132 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||||
enum SplitIcon {}
|
|
||||||
|
|
||||||
let this = cx.handle();
|
let this = cx.handle();
|
||||||
|
|
||||||
let is_dock = self.is_dock;
|
enum MouseNavigationHandler {}
|
||||||
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
EventHandler::new(if let Some(active_item) = self.active_item() {
|
MouseEventHandler::<MouseNavigationHandler>::new(0, cx, |_, cx| {
|
||||||
Flex::column()
|
if let Some(active_item) = self.active_item() {
|
||||||
.with_child({
|
Flex::column()
|
||||||
let mut tab_row = Flex::row()
|
.with_child({
|
||||||
.with_child(self.render_tab_bar(cx).flex(1., true).named("tabs"));
|
let mut tab_row = Flex::row().with_child(
|
||||||
|
self.render_tab_bar(cx).flex(1., true).named("tabs"),
|
||||||
|
);
|
||||||
|
|
||||||
if self.is_active {
|
if self.is_active {
|
||||||
tab_row.add_children([
|
tab_row.add_child(
|
||||||
MouseEventHandler::new::<SplitIcon, _, _>(
|
Flex::row()
|
||||||
0,
|
// New menu
|
||||||
cx,
|
.with_child(tab_bar_button(
|
||||||
|mouse_state, cx| {
|
0,
|
||||||
let theme =
|
"icons/plus_12.svg",
|
||||||
&cx.global::<Settings>().theme.workspace.tab_bar;
|
cx,
|
||||||
let style =
|
|position| DeployNewMenu { position },
|
||||||
theme.pane_button.style_for(mouse_state, false);
|
))
|
||||||
Svg::new("icons/plus_12.svg")
|
.with_child(
|
||||||
.with_color(style.color)
|
self.docked
|
||||||
.constrained()
|
.map(|anchor| {
|
||||||
.with_width(style.icon_width)
|
// Add the dock menu button if this pane is a dock
|
||||||
.aligned()
|
let dock_icon = match anchor {
|
||||||
.contained()
|
DockAnchor::Right => {
|
||||||
.with_style(style.container)
|
"icons/dock_right_12.svg"
|
||||||
.constrained()
|
}
|
||||||
.with_width(style.button_width)
|
DockAnchor::Bottom => {
|
||||||
.with_height(style.button_width)
|
"icons/dock_bottom_12.svg"
|
||||||
.aligned()
|
}
|
||||||
.boxed()
|
DockAnchor::Expanded => {
|
||||||
},
|
"icons/dock_modal_12.svg"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tab_bar_button(
|
||||||
|
2,
|
||||||
|
dock_icon,
|
||||||
|
cx,
|
||||||
|
|position| DeployDockMenu { position },
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// Add the split menu if this pane is not a dock
|
||||||
|
tab_bar_button(
|
||||||
|
1,
|
||||||
|
"icons/split_12.svg",
|
||||||
|
cx,
|
||||||
|
|position| DeployNewMenu { position },
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// Add the close dock button if this pane is a dock
|
||||||
|
.with_children(self.docked.map(|_| {
|
||||||
|
tab_bar_button(3, "icons/x_mark_12.svg", cx, |_| {
|
||||||
|
ToggleDock
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
.contained()
|
||||||
|
.with_style(
|
||||||
|
cx.global::<Settings>()
|
||||||
|
.theme
|
||||||
|
.workspace
|
||||||
|
.tab_bar
|
||||||
|
.pane_button
|
||||||
|
.default
|
||||||
|
.container,
|
||||||
|
)
|
||||||
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
}
|
||||||
.on_down(MouseButton::Left, |e, cx| {
|
|
||||||
cx.dispatch_action(DeployNewMenu {
|
tab_row
|
||||||
position: e.region.lower_right(),
|
.constrained()
|
||||||
});
|
.with_height(
|
||||||
})
|
cx.global::<Settings>().theme.workspace.tab_bar.height,
|
||||||
.boxed(),
|
|
||||||
MouseEventHandler::new::<SplitIcon, _, _>(
|
|
||||||
1,
|
|
||||||
cx,
|
|
||||||
|mouse_state, cx| {
|
|
||||||
let theme =
|
|
||||||
&cx.global::<Settings>().theme.workspace.tab_bar;
|
|
||||||
let style =
|
|
||||||
theme.pane_button.style_for(mouse_state, false);
|
|
||||||
Svg::new("icons/split_12.svg")
|
|
||||||
.with_color(style.color)
|
|
||||||
.constrained()
|
|
||||||
.with_width(style.icon_width)
|
|
||||||
.aligned()
|
|
||||||
.contained()
|
|
||||||
.with_style(style.container)
|
|
||||||
.constrained()
|
|
||||||
.with_width(style.button_width)
|
|
||||||
.with_height(style.button_width)
|
|
||||||
.aligned()
|
|
||||||
.boxed()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.named("tab bar")
|
||||||
.on_down(MouseButton::Left, move |e, cx| {
|
})
|
||||||
if is_dock {
|
.with_child(ChildView::new(&self.toolbar).boxed())
|
||||||
cx.dispatch_action(DeployDockMenu {
|
.with_child(ChildView::new(active_item).flex(1., true).boxed())
|
||||||
position: e.region.lower_right(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
cx.dispatch_action(DeploySplitMenu {
|
|
||||||
position: e.region.lower_right(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.boxed(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
tab_row
|
|
||||||
.constrained()
|
|
||||||
.with_height(cx.global::<Settings>().theme.workspace.tab_bar.height)
|
|
||||||
.named("tab bar")
|
|
||||||
})
|
|
||||||
.with_child(ChildView::new(&self.toolbar).boxed())
|
|
||||||
.with_child(ChildView::new(active_item).flex(1., true).boxed())
|
|
||||||
.boxed()
|
|
||||||
} else {
|
|
||||||
enum EmptyPane {}
|
|
||||||
let theme = cx.global::<Settings>().theme.clone();
|
|
||||||
|
|
||||||
MouseEventHandler::new::<EmptyPane, _, _>(0, cx, |_, _| {
|
|
||||||
Empty::new()
|
|
||||||
.contained()
|
|
||||||
.with_background_color(theme.workspace.background)
|
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
} else {
|
||||||
.on_down(MouseButton::Left, |_, cx| {
|
enum EmptyPane {}
|
||||||
cx.focus_parent_view();
|
let theme = cx.global::<Settings>().theme.clone();
|
||||||
})
|
|
||||||
.on_up(MouseButton::Left, {
|
|
||||||
let pane = this.clone();
|
|
||||||
move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, 0, cx)
|
|
||||||
})
|
|
||||||
.boxed()
|
|
||||||
})
|
|
||||||
.on_navigate_mouse_down(move |direction, cx| {
|
|
||||||
let this = this.clone();
|
|
||||||
match direction {
|
|
||||||
NavigationDirection::Back => {
|
|
||||||
cx.dispatch_action(GoBack { pane: Some(this) })
|
|
||||||
}
|
|
||||||
NavigationDirection::Forward => {
|
|
||||||
cx.dispatch_action(GoForward { pane: Some(this) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
MouseEventHandler::<EmptyPane>::new(0, cx, |_, _| {
|
||||||
|
Empty::new()
|
||||||
|
.contained()
|
||||||
|
.with_background_color(theme.workspace.background)
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.on_down(MouseButton::Left, |_, cx| {
|
||||||
|
cx.focus_parent_view();
|
||||||
|
})
|
||||||
|
.on_up(MouseButton::Left, {
|
||||||
|
let pane = this.clone();
|
||||||
|
move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, 0, cx)
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_down(MouseButton::Navigate(NavigationDirection::Back), {
|
||||||
|
let this = this.clone();
|
||||||
|
move |_, cx| {
|
||||||
|
cx.dispatch_action(GoBack {
|
||||||
|
pane: Some(this.clone()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_down(MouseButton::Navigate(NavigationDirection::Forward), {
|
||||||
|
let this = this.clone();
|
||||||
|
move |_, cx| {
|
||||||
|
cx.dispatch_action(GoForward {
|
||||||
|
pane: Some(this.clone()),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(ChildView::new(&self.context_menu).boxed())
|
.with_child(ChildView::new(&self.tab_bar_context_menu).boxed())
|
||||||
.named("pane")
|
.named("pane")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1498,6 +1503,36 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tab_bar_button<A: Action>(
|
||||||
|
index: usize,
|
||||||
|
icon: &'static str,
|
||||||
|
cx: &mut RenderContext<Pane>,
|
||||||
|
action_builder: impl 'static + Fn(Vector2F) -> A,
|
||||||
|
) -> ElementBox {
|
||||||
|
enum TabBarButton {}
|
||||||
|
|
||||||
|
MouseEventHandler::<TabBarButton>::new(index, cx, |mouse_state, cx| {
|
||||||
|
let theme = &cx.global::<Settings>().theme.workspace.tab_bar;
|
||||||
|
let style = theme.pane_button.style_for(mouse_state, false);
|
||||||
|
Svg::new(icon)
|
||||||
|
.with_color(style.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.icon_width)
|
||||||
|
.aligned()
|
||||||
|
.contained()
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.button_width)
|
||||||
|
.with_height(style.button_width)
|
||||||
|
.aligned()
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |e, cx| {
|
||||||
|
cx.dispatch_action(action_builder(e.region.lower_right()));
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
impl ItemNavHistory {
|
impl ItemNavHistory {
|
||||||
pub fn push<D: 'static + Any>(&self, data: Option<D>, cx: &mut MutableAppContext) {
|
pub fn push<D: 'static + Any>(&self, data: Option<D>, cx: &mut MutableAppContext) {
|
||||||
self.history.borrow_mut().push(data, self.item.clone(), cx);
|
self.history.borrow_mut().push(data, self.item.clone(), cx);
|
||||||
|
|
|
@ -176,7 +176,7 @@ impl Sidebar {
|
||||||
let actual_width = self.actual_width.clone();
|
let actual_width = self.actual_width.clone();
|
||||||
let custom_width = self.custom_width.clone();
|
let custom_width = self.custom_width.clone();
|
||||||
let side = self.side;
|
let side = self.side;
|
||||||
MouseEventHandler::new::<Self, _, _>(side as usize, cx, |_, _| {
|
MouseEventHandler::<Self>::new(side as usize, cx, |_, _| {
|
||||||
Empty::new()
|
Empty::new()
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(theme.workspace.sidebar_resize_handle)
|
.with_style(theme.workspace.sidebar_resize_handle)
|
||||||
|
@ -291,7 +291,7 @@ impl View for SidebarButtons {
|
||||||
side,
|
side,
|
||||||
item_index: ix,
|
item_index: ix,
|
||||||
};
|
};
|
||||||
MouseEventHandler::new::<Self, _, _>(ix, cx, move |state, cx| {
|
MouseEventHandler::<Self>::new(ix, cx, move |state, cx| {
|
||||||
let is_active = is_open && ix == active_ix;
|
let is_active = is_open && ix == active_ix;
|
||||||
let style = item_style.style_for(state, is_active);
|
let style = item_style.style_for(state, is_active);
|
||||||
Stack::new()
|
Stack::new()
|
||||||
|
|
|
@ -166,7 +166,7 @@ fn nav_button<A: Action + Clone>(
|
||||||
action_name: &str,
|
action_name: &str,
|
||||||
cx: &mut RenderContext<Toolbar>,
|
cx: &mut RenderContext<Toolbar>,
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
MouseEventHandler::new::<A, _, _>(0, cx, |state, _| {
|
MouseEventHandler::<A>::new(0, cx, |state, _| {
|
||||||
let style = if enabled {
|
let style = if enabled {
|
||||||
style.style_for(state, false)
|
style.style_for(state, false)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -955,7 +955,7 @@ impl Workspace {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let center_pane = cx.add_view(|cx| Pane::new(false, cx));
|
let center_pane = cx.add_view(|cx| Pane::new(None, cx));
|
||||||
let pane_id = center_pane.id();
|
let pane_id = center_pane.id();
|
||||||
cx.subscribe(¢er_pane, move |this, _, event, cx| {
|
cx.subscribe(¢er_pane, move |this, _, event, cx| {
|
||||||
this.handle_pane_event(pane_id, event, cx)
|
this.handle_pane_event(pane_id, event, cx)
|
||||||
|
@ -1542,7 +1542,7 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
|
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
|
||||||
let pane = cx.add_view(|cx| Pane::new(false, cx));
|
let pane = cx.add_view(|cx| Pane::new(None, cx));
|
||||||
let pane_id = pane.id();
|
let pane_id = pane.id();
|
||||||
cx.subscribe(&pane, move |this, _, event, cx| {
|
cx.subscribe(&pane, move |this, _, event, cx| {
|
||||||
this.handle_pane_event(pane_id, event, cx)
|
this.handle_pane_event(pane_id, event, cx)
|
||||||
|
@ -1999,8 +1999,9 @@ impl Workspace {
|
||||||
theme.workspace.titlebar.container
|
theme.workspace.titlebar.container
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum TitleBar {}
|
||||||
ConstrainedBox::new(
|
ConstrainedBox::new(
|
||||||
MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<TitleBar>::new(0, cx, |_, cx| {
|
||||||
Container::new(
|
Container::new(
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -2129,7 +2130,7 @@ impl Workspace {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::new::<Authenticate, _, _>(0, cx, |state, _| {
|
MouseEventHandler::<Authenticate>::new(0, cx, |state, _| {
|
||||||
let style = theme
|
let style = theme
|
||||||
.workspace
|
.workspace
|
||||||
.titlebar
|
.titlebar
|
||||||
|
@ -2189,7 +2190,7 @@ impl Workspace {
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
if let Some((peer_id, peer_github_login)) = peer {
|
if let Some((peer_id, peer_github_login)) = peer {
|
||||||
MouseEventHandler::new::<ToggleFollow, _, _>(replica_id.into(), cx, move |_, _| content)
|
MouseEventHandler::<ToggleFollow>::new(replica_id.into(), cx, move |_, _| content)
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.on_click(MouseButton::Left, move |_, cx| {
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
cx.dispatch_action(ToggleFollow(peer_id))
|
cx.dispatch_action(ToggleFollow(peer_id))
|
||||||
|
@ -2215,7 +2216,7 @@ impl Workspace {
|
||||||
if self.project.read(cx).is_read_only() {
|
if self.project.read(cx).is_read_only() {
|
||||||
enum DisconnectedOverlay {}
|
enum DisconnectedOverlay {}
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::new::<DisconnectedOverlay, _, _>(0, cx, |_, cx| {
|
MouseEventHandler::<DisconnectedOverlay>::new(0, cx, |_, cx| {
|
||||||
let theme = &cx.global::<Settings>().theme;
|
let theme = &cx.global::<Settings>().theme;
|
||||||
Label::new(
|
Label::new(
|
||||||
"Your connection to the remote project has been lost.".to_string(),
|
"Your connection to the remote project has been lost.".to_string(),
|
||||||
|
@ -2226,6 +2227,7 @@ impl Workspace {
|
||||||
.with_style(theme.workspace.disconnected_overlay.container)
|
.with_style(theme.workspace.disconnected_overlay.container)
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
})
|
||||||
|
.with_cursor_style(CursorStyle::Arrow)
|
||||||
.capture_all()
|
.capture_all()
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl View for FeedbackLink {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> gpui::ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> gpui::ElementBox {
|
||||||
MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
|
MouseEventHandler::<Self>::new(0, cx, |state, cx| {
|
||||||
let theme = &cx.global::<Settings>().theme;
|
let theme = &cx.global::<Settings>().theme;
|
||||||
let theme = &theme.workspace.status_bar.feedback;
|
let theme = &theme.workspace.status_bar.feedback;
|
||||||
Text::new(
|
Text::new(
|
||||||
|
|
Loading…
Reference in a new issue