Derive autocomplete menu's width from the width of its largest item
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
1a6e972ed4
commit
c19d639e0a
5 changed files with 98 additions and 39 deletions
|
@ -1535,9 +1535,10 @@ impl Editor {
|
|||
self.completion_state.is_some()
|
||||
}
|
||||
|
||||
pub fn render_completions(&self) -> Option<ElementBox> {
|
||||
pub fn render_completions(&self, cx: &AppContext) -> Option<ElementBox> {
|
||||
self.completion_state.as_ref().map(|state| {
|
||||
let build_settings = self.build_settings.clone();
|
||||
let settings = build_settings(cx);
|
||||
let completions = state.completions.clone();
|
||||
UniformList::new(
|
||||
state.list.clone(),
|
||||
|
@ -1547,11 +1548,23 @@ impl Editor {
|
|||
for completion in &completions[range] {
|
||||
items.push(
|
||||
Label::new(completion.label().to_string(), settings.style.text.clone())
|
||||
.contained()
|
||||
.with_style(settings.style.autocomplete.item)
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
.with_width_from_item(
|
||||
state
|
||||
.completions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|(_, completion)| completion.label().chars().count())
|
||||
.map(|(ix, _)| ix),
|
||||
)
|
||||
.contained()
|
||||
.with_style(settings.style.autocomplete.container)
|
||||
.boxed()
|
||||
})
|
||||
}
|
||||
|
@ -4056,6 +4069,7 @@ impl EditorSettings {
|
|||
invalid_information_diagnostic: default_diagnostic_style.clone(),
|
||||
hint_diagnostic: default_diagnostic_style.clone(),
|
||||
invalid_hint_diagnostic: default_diagnostic_style.clone(),
|
||||
autocomplete: Default::default(),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -886,7 +886,7 @@ impl Element for EditorElement {
|
|||
.to_display_point(&snapshot);
|
||||
|
||||
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
||||
let list = view.render_completions().unwrap();
|
||||
let list = view.render_completions(cx).unwrap();
|
||||
completions = Some((newest_selection_head, list));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ where
|
|||
append_items: F,
|
||||
padding_top: f32,
|
||||
padding_bottom: f32,
|
||||
get_width_from_item: Option<usize>,
|
||||
}
|
||||
|
||||
impl<F> UniformList<F>
|
||||
|
@ -64,9 +65,15 @@ where
|
|||
append_items,
|
||||
padding_top: 0.,
|
||||
padding_bottom: 0.,
|
||||
get_width_from_item: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_width_from_item(mut self, item_ix: Option<usize>) -> Self {
|
||||
self.get_width_from_item = item_ix;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_padding_top(mut self, padding: f32) -> Self {
|
||||
self.padding_top = padding;
|
||||
self
|
||||
|
@ -155,46 +162,70 @@ where
|
|||
"UniformList does not support being rendered with an unconstrained height"
|
||||
);
|
||||
}
|
||||
let mut size = constraint.max;
|
||||
let mut item_constraint =
|
||||
SizeConstraint::new(vec2f(size.x(), 0.0), vec2f(size.x(), f32::INFINITY));
|
||||
let mut item_height = 0.;
|
||||
let mut scroll_max = 0.;
|
||||
|
||||
let mut items = Vec::new();
|
||||
(self.append_items)(0..1, &mut items, cx);
|
||||
if let Some(first_item) = items.first_mut() {
|
||||
let mut item_size = first_item.layout(item_constraint, cx);
|
||||
item_size.set_x(size.x());
|
||||
item_constraint.min = item_size;
|
||||
item_constraint.max = item_size;
|
||||
item_height = item_size.y();
|
||||
|
||||
let scroll_height = self.item_count as f32 * item_height;
|
||||
if scroll_height < size.y() {
|
||||
size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
|
||||
}
|
||||
|
||||
let scroll_height =
|
||||
item_height * self.item_count as f32 + self.padding_top + self.padding_bottom;
|
||||
scroll_max = (scroll_height - size.y()).max(0.);
|
||||
self.autoscroll(scroll_max, size.y(), item_height);
|
||||
|
||||
items.clear();
|
||||
let start = cmp::min(
|
||||
((self.scroll_top() - self.padding_top) / item_height) as usize,
|
||||
self.item_count,
|
||||
if self.item_count == 0 {
|
||||
return (
|
||||
constraint.min,
|
||||
LayoutState {
|
||||
item_height: 0.,
|
||||
scroll_max: 0.,
|
||||
items,
|
||||
},
|
||||
);
|
||||
let end = cmp::min(
|
||||
self.item_count,
|
||||
start + (size.y() / item_height).ceil() as usize + 1,
|
||||
);
|
||||
(self.append_items)(start..end, &mut items, cx);
|
||||
for item in &mut items {
|
||||
item.layout(item_constraint, cx);
|
||||
}
|
||||
}
|
||||
|
||||
let mut size = constraint.max;
|
||||
let mut item_size;
|
||||
if let Some(sample_item_ix) = self.get_width_from_item {
|
||||
(self.append_items)(sample_item_ix..sample_item_ix + 1, &mut items, cx);
|
||||
let sample_item = items.get_mut(0).unwrap();
|
||||
item_size = sample_item.layout(constraint, cx);
|
||||
size.set_x(item_size.x());
|
||||
} else {
|
||||
size = constraint.min;
|
||||
(self.append_items)(0..1, &mut items, cx);
|
||||
let first_item = items.first_mut().unwrap();
|
||||
item_size = first_item.layout(
|
||||
SizeConstraint::new(
|
||||
vec2f(constraint.max.x(), 0.0),
|
||||
vec2f(constraint.max.x(), f32::INFINITY),
|
||||
),
|
||||
cx,
|
||||
);
|
||||
item_size.set_x(size.x());
|
||||
}
|
||||
|
||||
let item_constraint = SizeConstraint {
|
||||
min: item_size,
|
||||
max: vec2f(constraint.max.x(), item_size.y()),
|
||||
};
|
||||
let item_height = item_size.y();
|
||||
|
||||
let scroll_height = self.item_count as f32 * item_height;
|
||||
if scroll_height < size.y() {
|
||||
size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
|
||||
}
|
||||
|
||||
let scroll_height =
|
||||
item_height * self.item_count as f32 + self.padding_top + self.padding_bottom;
|
||||
let scroll_max = (scroll_height - size.y()).max(0.);
|
||||
self.autoscroll(scroll_max, size.y(), item_height);
|
||||
|
||||
let start = cmp::min(
|
||||
((self.scroll_top() - self.padding_top) / item_height) as usize,
|
||||
self.item_count,
|
||||
);
|
||||
let end = cmp::min(
|
||||
self.item_count,
|
||||
start + (size.y() / item_height).ceil() as usize + 1,
|
||||
);
|
||||
items.clear();
|
||||
(self.append_items)(start..end, &mut items, cx);
|
||||
for item in &mut items {
|
||||
let item_size = item.layout(item_constraint, cx);
|
||||
if item_size.x() > size.x() {
|
||||
size.set_x(item_size.x());
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
|
|
|
@ -292,6 +292,7 @@ pub struct EditorStyle {
|
|||
pub invalid_information_diagnostic: DiagnosticStyle,
|
||||
pub hint_diagnostic: DiagnosticStyle,
|
||||
pub invalid_hint_diagnostic: DiagnosticStyle,
|
||||
pub autocomplete: AutocompleteStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
|
@ -321,6 +322,13 @@ pub struct DiagnosticStyle {
|
|||
pub text_scale_factor: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
pub struct AutocompleteStyle {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub item: ContainerStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Deserialize)]
|
||||
pub struct SelectionStyle {
|
||||
pub cursor: Color,
|
||||
|
@ -408,6 +416,7 @@ impl InputEditorStyle {
|
|||
invalid_information_diagnostic: default_diagnostic_style.clone(),
|
||||
hint_diagnostic: default_diagnostic_style.clone(),
|
||||
invalid_hint_diagnostic: default_diagnostic_style.clone(),
|
||||
autocomplete: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ corner_radius = 6
|
|||
|
||||
[project_panel]
|
||||
extends = "$panel"
|
||||
padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
|
||||
padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
|
||||
|
||||
[project_panel.entry]
|
||||
text = "$text.1"
|
||||
|
@ -314,6 +314,11 @@ extends = "$editor.hint_diagnostic"
|
|||
message.text.color = "$text.3.color"
|
||||
message.highlight_text.color = "$text.3.color"
|
||||
|
||||
[editor.autocomplete]
|
||||
background = "$surface.2"
|
||||
border = { width = 1, color = "$border.1" }
|
||||
item.padding = 2
|
||||
|
||||
[project_diagnostics]
|
||||
background = "$surface.1"
|
||||
empty_message = { extends = "$text.0", size = 18 }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue