Ignore custom buffer line height for context menus (#25172)
Closes #24504 Release Notes: - Fixed a visual bug that could make context menus unusable when setting a custom `buffer_line_height`.
This commit is contained in:
parent
6a9b1f0dc4
commit
1ce8a51712
1 changed files with 112 additions and 95 deletions
|
@ -68,7 +68,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use text::BufferId;
|
use text::BufferId;
|
||||||
use theme::{ActiveTheme, Appearance, PlayerColor};
|
use theme::{ActiveTheme, Appearance, BufferLineHeight, PlayerColor};
|
||||||
use ui::{
|
use ui::{
|
||||||
h_flex, prelude::*, ButtonLike, ButtonStyle, ContextMenu, IconButtonShape, KeyBinding, Tooltip,
|
h_flex, prelude::*, ButtonLike, ButtonStyle, ContextMenu, IconButtonShape, KeyBinding, Tooltip,
|
||||||
POPOVER_Y_PADDING,
|
POPOVER_Y_PADDING,
|
||||||
|
@ -3334,90 +3334,99 @@ impl EditorElement {
|
||||||
&mut App,
|
&mut App,
|
||||||
) -> Vec<(CursorPopoverType, AnyElement, Size<Pixels>)>,
|
) -> Vec<(CursorPopoverType, AnyElement, Size<Pixels>)>,
|
||||||
) -> Option<(Vec<(CursorPopoverType, Bounds<Pixels>)>, bool)> {
|
) -> Option<(Vec<(CursorPopoverType, Bounds<Pixels>)>, bool)> {
|
||||||
// If the max height won't fit below and there is more space above, put it above the line.
|
let text_style = TextStyleRefinement {
|
||||||
let bottom_y_when_flipped = target_position.y - line_height;
|
line_height: Some(DefiniteLength::Fraction(
|
||||||
let available_above = bottom_y_when_flipped - text_hitbox.top();
|
BufferLineHeight::Comfortable.value(),
|
||||||
let available_below = text_hitbox.bottom() - target_position.y;
|
)),
|
||||||
let y_overflows_below = max_height > available_below;
|
..Default::default()
|
||||||
let mut y_flipped = y_overflows_below && available_above > available_below;
|
|
||||||
let mut height = cmp::min(
|
|
||||||
max_height,
|
|
||||||
if y_flipped {
|
|
||||||
available_above
|
|
||||||
} else {
|
|
||||||
available_below
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the min height doesn't fit within text bounds, instead fit within the window.
|
|
||||||
if height < min_height {
|
|
||||||
let available_above = bottom_y_when_flipped;
|
|
||||||
let available_below = viewport_bounds.bottom() - target_position.y;
|
|
||||||
if available_below > min_height {
|
|
||||||
y_flipped = false;
|
|
||||||
height = min_height;
|
|
||||||
} else if available_above > min_height {
|
|
||||||
y_flipped = true;
|
|
||||||
height = min_height;
|
|
||||||
} else if available_above > available_below {
|
|
||||||
y_flipped = true;
|
|
||||||
height = available_above;
|
|
||||||
} else {
|
|
||||||
y_flipped = false;
|
|
||||||
height = available_below;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let max_width_for_stable_x = viewport_bounds.right() - target_position.x;
|
|
||||||
|
|
||||||
// TODO: Use viewport_bounds.width as a max width so that it doesn't get clipped on the left
|
|
||||||
// for very narrow windows.
|
|
||||||
let popovers = make_sized_popovers(height, max_width_for_stable_x, y_flipped, window, cx);
|
|
||||||
if popovers.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let max_width = popovers
|
|
||||||
.iter()
|
|
||||||
.map(|(_, _, size)| size.width)
|
|
||||||
.max()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let mut current_position = gpui::Point {
|
|
||||||
// Snap the right edge of the list to the right edge of the window if its horizontal bounds
|
|
||||||
// overflow. Include space for the scrollbar.
|
|
||||||
x: target_position
|
|
||||||
.x
|
|
||||||
.min((viewport_bounds.right() - max_width).max(Pixels::ZERO)),
|
|
||||||
y: if y_flipped {
|
|
||||||
bottom_y_when_flipped
|
|
||||||
} else {
|
|
||||||
target_position.y
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
window.with_text_style(Some(text_style), |window| {
|
||||||
let mut laid_out_popovers = popovers
|
// If the max height won't fit below and there is more space above, put it above the line.
|
||||||
.into_iter()
|
let bottom_y_when_flipped = target_position.y - line_height;
|
||||||
.map(|(popover_type, element, size)| {
|
let available_above = bottom_y_when_flipped - text_hitbox.top();
|
||||||
|
let available_below = text_hitbox.bottom() - target_position.y;
|
||||||
|
let y_overflows_below = max_height > available_below;
|
||||||
|
let mut y_flipped = y_overflows_below && available_above > available_below;
|
||||||
|
let mut height = cmp::min(
|
||||||
|
max_height,
|
||||||
if y_flipped {
|
if y_flipped {
|
||||||
current_position.y -= size.height;
|
available_above
|
||||||
}
|
|
||||||
let position = current_position;
|
|
||||||
window.defer_draw(element, current_position, 1);
|
|
||||||
if !y_flipped {
|
|
||||||
current_position.y += size.height + MENU_GAP;
|
|
||||||
} else {
|
} else {
|
||||||
current_position.y -= MENU_GAP;
|
available_below
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the min height doesn't fit within text bounds, instead fit within the window.
|
||||||
|
if height < min_height {
|
||||||
|
let available_above = bottom_y_when_flipped;
|
||||||
|
let available_below = viewport_bounds.bottom() - target_position.y;
|
||||||
|
if available_below > min_height {
|
||||||
|
y_flipped = false;
|
||||||
|
height = min_height;
|
||||||
|
} else if available_above > min_height {
|
||||||
|
y_flipped = true;
|
||||||
|
height = min_height;
|
||||||
|
} else if available_above > available_below {
|
||||||
|
y_flipped = true;
|
||||||
|
height = available_above;
|
||||||
|
} else {
|
||||||
|
y_flipped = false;
|
||||||
|
height = available_below;
|
||||||
}
|
}
|
||||||
(popover_type, Bounds::new(position, size))
|
}
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if y_flipped {
|
let max_width_for_stable_x = viewport_bounds.right() - target_position.x;
|
||||||
laid_out_popovers.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((laid_out_popovers, y_flipped))
|
// TODO: Use viewport_bounds.width as a max width so that it doesn't get clipped on the left
|
||||||
|
// for very narrow windows.
|
||||||
|
let popovers =
|
||||||
|
make_sized_popovers(height, max_width_for_stable_x, y_flipped, window, cx);
|
||||||
|
if popovers.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_width = popovers
|
||||||
|
.iter()
|
||||||
|
.map(|(_, _, size)| size.width)
|
||||||
|
.max()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut current_position = gpui::Point {
|
||||||
|
// Snap the right edge of the list to the right edge of the window if its horizontal bounds
|
||||||
|
// overflow. Include space for the scrollbar.
|
||||||
|
x: target_position
|
||||||
|
.x
|
||||||
|
.min((viewport_bounds.right() - max_width).max(Pixels::ZERO)),
|
||||||
|
y: if y_flipped {
|
||||||
|
bottom_y_when_flipped
|
||||||
|
} else {
|
||||||
|
target_position.y
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut laid_out_popovers = popovers
|
||||||
|
.into_iter()
|
||||||
|
.map(|(popover_type, element, size)| {
|
||||||
|
if y_flipped {
|
||||||
|
current_position.y -= size.height;
|
||||||
|
}
|
||||||
|
let position = current_position;
|
||||||
|
window.defer_draw(element, current_position, 1);
|
||||||
|
if !y_flipped {
|
||||||
|
current_position.y += size.height + MENU_GAP;
|
||||||
|
} else {
|
||||||
|
current_position.y -= MENU_GAP;
|
||||||
|
}
|
||||||
|
(popover_type, Bounds::new(position, size))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if y_flipped {
|
||||||
|
laid_out_popovers.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((laid_out_popovers, y_flipped))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -3978,25 +3987,33 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut element = self.editor.update(cx, |editor, _| {
|
let text_style = TextStyleRefinement {
|
||||||
let mouse_context_menu = editor.mouse_context_menu.as_ref()?;
|
line_height: Some(DefiniteLength::Fraction(
|
||||||
let context_menu = mouse_context_menu.context_menu.clone();
|
BufferLineHeight::Comfortable.value(),
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
window.with_text_style(Some(text_style), |window| {
|
||||||
|
let mut element = self.editor.update(cx, |editor, _| {
|
||||||
|
let mouse_context_menu = editor.mouse_context_menu.as_ref()?;
|
||||||
|
let context_menu = mouse_context_menu.context_menu.clone();
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
deferred(
|
deferred(
|
||||||
anchored()
|
anchored()
|
||||||
.position(position)
|
.position(position)
|
||||||
.child(context_menu)
|
.child(context_menu)
|
||||||
.anchor(Corner::TopLeft)
|
.anchor(Corner::TopLeft)
|
||||||
.snap_to_window_with_margin(px(8.)),
|
.snap_to_window_with_margin(px(8.)),
|
||||||
|
)
|
||||||
|
.with_priority(1)
|
||||||
|
.into_any(),
|
||||||
)
|
)
|
||||||
.with_priority(1)
|
})?;
|
||||||
.into_any(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
element.prepaint_as_root(position, AvailableSpace::min_size(), window, cx);
|
element.prepaint_as_root(position, AvailableSpace::min_size(), window, cx);
|
||||||
Some(element)
|
Some(element)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue