From 17a80ca09b8ccbb4273d19c89e5cd6c6cf8b3e72 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 14 Dec 2023 09:44:04 -0700 Subject: [PATCH 1/4] Ignore invisible layers for mouse events --- crates/gpui2/src/elements/div.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 1954e3086c..6b1dd3e545 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -890,10 +890,11 @@ impl Interactivity { }); } - if style - .background - .as_ref() - .is_some_and(|fill| fill.color().is_some()) + if style.visibility == Visibility::Visible + && style + .background + .as_ref() + .is_some_and(|fill| fill.color().is_some()) { cx.with_z_index(style.z_index.unwrap_or(0), |cx| cx.add_opaque_layer(bounds)) } From c2c7eead8a22baf006bfbb7ae619f5377c87587e Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 14 Dec 2023 10:02:54 -0700 Subject: [PATCH 2/4] Robustify checks for visibility --- crates/gpui2/src/elements/div.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 6b1dd3e545..bb8e76c627 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -642,10 +642,6 @@ impl Element for Div { &mut element_state.interactive_state, cx, |style, scroll_offset, cx| { - if style.visibility == Visibility::Hidden { - return; - } - let z_index = style.z_index.unwrap_or(0); cx.with_z_index(z_index, |cx| { @@ -779,6 +775,10 @@ impl Interactivity { ) { let style = self.compute_style(Some(bounds), element_state, cx); + if style.visibility == Visibility::Hidden { + return; + } + #[cfg(debug_assertions)] if self.element_id.is_some() && (style.debug || style.debug_below || cx.has_global::()) @@ -890,11 +890,10 @@ impl Interactivity { }); } - if style.visibility == Visibility::Visible - && style - .background - .as_ref() - .is_some_and(|fill| fill.color().is_some()) + if style + .background + .as_ref() + .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent())) { cx.with_z_index(style.z_index.unwrap_or(0), |cx| cx.add_opaque_layer(bounds)) } From d8cb0e8a2afe5eff4edfe6fd57a4b86f1d823123 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 14 Dec 2023 10:36:16 -0700 Subject: [PATCH 3/4] Fix z-indexes in editor element * Ensure that scroll events from blocks scroll the editor * Ensure that scroll bars show behind hover things --- crates/editor2/src/element.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 2b6db125da..b0341e2d4b 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -1095,7 +1095,7 @@ impl EditorElement { } }); - cx.with_z_index(1, |cx| { + cx.with_z_index(2, |cx| { if let Some((position, mut context_menu)) = layout.context_menu.take() { let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); @@ -2825,14 +2825,16 @@ impl Element for EditorElement { self.paint_text(text_bounds, &mut layout, cx); if !layout.blocks.is_empty() { - cx.with_z_index(1, |cx| { + // z-index of 0 so that it is on the same mouse event layer as the editor's + // mouse events + cx.with_z_index(0, |cx| { cx.with_element_id(Some("editor_blocks"), |cx| { self.paint_blocks(bounds, &mut layout, cx); }); }) } - cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); + cx.with_z_index(1, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); }); }); }) From 4e1b4c439047dec2e44f0afa58458defb0b34909 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 14 Dec 2023 10:48:15 -0700 Subject: [PATCH 4/4] Refactor editor to be more clear about stacking --- crates/editor2/src/element.rs | 265 +++++++++++++++++----------------- 1 file changed, 129 insertions(+), 136 deletions(-) diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index b0341e2d4b..9b95e256a6 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -1094,123 +1094,120 @@ impl EditorElement { cursor.paint(content_origin, cx); } }); - - cx.with_z_index(2, |cx| { - if let Some((position, mut context_menu)) = layout.context_menu.take() { - let available_space = - size(AvailableSpace::MinContent, AvailableSpace::MinContent); - let context_menu_size = context_menu.measure(available_space, cx); - - let cursor_row_layout = &layout.position_map.line_layouts - [(position.row() - start_row) as usize] - .line; - let x = cursor_row_layout.x_for_index(position.column() as usize) - - layout.position_map.scroll_position.x; - let y = (position.row() + 1) as f32 * layout.position_map.line_height - - layout.position_map.scroll_position.y; - let mut list_origin = content_origin + point(x, y); - let list_width = context_menu_size.width; - let list_height = context_menu_size.height; - - // Snap the right edge of the list to the right edge of the window if - // its horizontal bounds overflow. - if list_origin.x + list_width > cx.viewport_size().width { - list_origin.x = - (cx.viewport_size().width - list_width).max(Pixels::ZERO); - } - - if list_origin.y + list_height > text_bounds.lower_right().y { - list_origin.y -= layout.position_map.line_height + list_height; - } - - cx.break_content_mask(|cx| { - context_menu.draw(list_origin, available_space, cx) - }); - } - - if let Some((position, mut hover_popovers)) = layout.hover_popovers.take() { - let available_space = - size(AvailableSpace::MinContent, AvailableSpace::MinContent); - - // This is safe because we check on layout whether the required row is available - let hovered_row_layout = &layout.position_map.line_layouts - [(position.row() - start_row) as usize] - .line; - - // Minimum required size: Take the first popover, and add 1.5 times the minimum popover - // height. This is the size we will use to decide whether to render popovers above or below - // the hovered line. - let first_size = hover_popovers[0].measure(available_space, cx); - let height_to_reserve = first_size.height - + 1.5 * MIN_POPOVER_LINE_HEIGHT * layout.position_map.line_height; - - // Compute Hovered Point - let x = hovered_row_layout.x_for_index(position.column() as usize) - - layout.position_map.scroll_position.x; - let y = position.row() as f32 * layout.position_map.line_height - - layout.position_map.scroll_position.y; - let hovered_point = content_origin + point(x, y); - - if hovered_point.y - height_to_reserve > Pixels::ZERO { - // There is enough space above. Render popovers above the hovered point - let mut current_y = hovered_point.y; - for mut hover_popover in hover_popovers { - let size = hover_popover.measure(available_space, cx); - let mut popover_origin = - point(hovered_point.x, current_y - size.height); - - let x_out_of_bounds = - text_bounds.upper_right().x - (popover_origin.x + size.width); - if x_out_of_bounds < Pixels::ZERO { - popover_origin.x = popover_origin.x + x_out_of_bounds; - } - - cx.break_content_mask(|cx| { - hover_popover.draw(popover_origin, available_space, cx) - }); - - current_y = popover_origin.y - HOVER_POPOVER_GAP; - } - } else { - // There is not enough space above. Render popovers below the hovered point - let mut current_y = hovered_point.y + layout.position_map.line_height; - for mut hover_popover in hover_popovers { - let size = hover_popover.measure(available_space, cx); - let mut popover_origin = point(hovered_point.x, current_y); - - let x_out_of_bounds = - text_bounds.upper_right().x - (popover_origin.x + size.width); - if x_out_of_bounds < Pixels::ZERO { - popover_origin.x = popover_origin.x + x_out_of_bounds; - } - - hover_popover.draw(popover_origin, available_space, cx); - - current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP; - } - } - } - - if let Some(mouse_context_menu) = - self.editor.read(cx).mouse_context_menu.as_ref() - { - let element = overlay() - .position(mouse_context_menu.position) - .child(mouse_context_menu.context_menu.clone()) - .anchor(AnchorCorner::TopLeft) - .snap_to_window(); - element.draw( - gpui::Point::default(), - size(AvailableSpace::MinContent, AvailableSpace::MinContent), - cx, - |_, _| {}, - ); - } - }) }, ) } + fn paint_overlays( + &mut self, + text_bounds: Bounds, + layout: &mut LayoutState, + cx: &mut WindowContext, + ) { + let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO); + let start_row = layout.visible_display_row_range.start; + if let Some((position, mut context_menu)) = layout.context_menu.take() { + let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); + let context_menu_size = context_menu.measure(available_space, cx); + + let cursor_row_layout = + &layout.position_map.line_layouts[(position.row() - start_row) as usize].line; + let x = cursor_row_layout.x_for_index(position.column() as usize) + - layout.position_map.scroll_position.x; + let y = (position.row() + 1) as f32 * layout.position_map.line_height + - layout.position_map.scroll_position.y; + let mut list_origin = content_origin + point(x, y); + let list_width = context_menu_size.width; + let list_height = context_menu_size.height; + + // Snap the right edge of the list to the right edge of the window if + // its horizontal bounds overflow. + if list_origin.x + list_width > cx.viewport_size().width { + list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO); + } + + if list_origin.y + list_height > text_bounds.lower_right().y { + list_origin.y -= layout.position_map.line_height + list_height; + } + + cx.break_content_mask(|cx| context_menu.draw(list_origin, available_space, cx)); + } + + if let Some((position, mut hover_popovers)) = layout.hover_popovers.take() { + let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); + + // This is safe because we check on layout whether the required row is available + let hovered_row_layout = + &layout.position_map.line_layouts[(position.row() - start_row) as usize].line; + + // Minimum required size: Take the first popover, and add 1.5 times the minimum popover + // height. This is the size we will use to decide whether to render popovers above or below + // the hovered line. + let first_size = hover_popovers[0].measure(available_space, cx); + let height_to_reserve = + first_size.height + 1.5 * MIN_POPOVER_LINE_HEIGHT * layout.position_map.line_height; + + // Compute Hovered Point + let x = hovered_row_layout.x_for_index(position.column() as usize) + - layout.position_map.scroll_position.x; + let y = position.row() as f32 * layout.position_map.line_height + - layout.position_map.scroll_position.y; + let hovered_point = content_origin + point(x, y); + + if hovered_point.y - height_to_reserve > Pixels::ZERO { + // There is enough space above. Render popovers above the hovered point + let mut current_y = hovered_point.y; + for mut hover_popover in hover_popovers { + let size = hover_popover.measure(available_space, cx); + let mut popover_origin = point(hovered_point.x, current_y - size.height); + + let x_out_of_bounds = + text_bounds.upper_right().x - (popover_origin.x + size.width); + if x_out_of_bounds < Pixels::ZERO { + popover_origin.x = popover_origin.x + x_out_of_bounds; + } + + cx.break_content_mask(|cx| { + hover_popover.draw(popover_origin, available_space, cx) + }); + + current_y = popover_origin.y - HOVER_POPOVER_GAP; + } + } else { + // There is not enough space above. Render popovers below the hovered point + let mut current_y = hovered_point.y + layout.position_map.line_height; + for mut hover_popover in hover_popovers { + let size = hover_popover.measure(available_space, cx); + let mut popover_origin = point(hovered_point.x, current_y); + + let x_out_of_bounds = + text_bounds.upper_right().x - (popover_origin.x + size.width); + if x_out_of_bounds < Pixels::ZERO { + popover_origin.x = popover_origin.x + x_out_of_bounds; + } + + hover_popover.draw(popover_origin, available_space, cx); + + current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP; + } + } + } + + if let Some(mouse_context_menu) = self.editor.read(cx).mouse_context_menu.as_ref() { + let element = overlay() + .position(mouse_context_menu.position) + .child(mouse_context_menu.context_menu.clone()) + .anchor(AnchorCorner::TopLeft) + .snap_to_window(); + element.draw( + gpui::Point::default(), + size(AvailableSpace::MinContent, AvailableSpace::MinContent), + cx, + |_, _| {}, + ); + } + } + fn scrollbar_left(&self, bounds: &Bounds) -> Pixels { bounds.upper_right().x - self.style.scrollbar_width } @@ -2807,34 +2804,30 @@ impl Element for EditorElement { self.register_actions(cx); self.register_key_listeners(cx); - // We call with_z_index to establish a new stacking context. - cx.with_z_index(0, |cx| { - cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - // Paint mouse listeners at z-index 0 so any elements we paint on top of the editor - // take precedence. - cx.with_z_index(0, |cx| { - self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx); - }); - let input_handler = ElementInputHandler::new(bounds, self.editor.clone(), cx); - cx.handle_input(&focus_handle, input_handler); + cx.with_content_mask(Some(ContentMask { bounds }), |cx| { + let input_handler = ElementInputHandler::new(bounds, self.editor.clone(), cx); + cx.handle_input(&focus_handle, input_handler); - self.paint_background(gutter_bounds, text_bounds, &layout, cx); - if layout.gutter_size.width > Pixels::ZERO { - self.paint_gutter(gutter_bounds, &mut layout, cx); - } - self.paint_text(text_bounds, &mut layout, cx); + self.paint_background(gutter_bounds, text_bounds, &layout, cx); + if layout.gutter_size.width > Pixels::ZERO { + self.paint_gutter(gutter_bounds, &mut layout, cx); + } + self.paint_text(text_bounds, &mut layout, cx); + + cx.with_z_index(0, |cx| { + self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx); if !layout.blocks.is_empty() { - // z-index of 0 so that it is on the same mouse event layer as the editor's - // mouse events - cx.with_z_index(0, |cx| { - cx.with_element_id(Some("editor_blocks"), |cx| { - self.paint_blocks(bounds, &mut layout, cx); - }); - }) + cx.with_element_id(Some("editor_blocks"), |cx| { + self.paint_blocks(bounds, &mut layout, cx); + }); } + }); - cx.with_z_index(1, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); + cx.with_z_index(1, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); + + cx.with_z_index(2, |cx| { + self.paint_overlays(text_bounds, &mut layout, cx); }); }); })