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:
Max Brunsfeld 2022-01-31 13:01:20 -08:00
parent 1a6e972ed4
commit c19d639e0a
5 changed files with 98 additions and 39 deletions

View file

@ -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(),
}
},
}

View file

@ -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));
}
}

View file

@ -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,20 +162,44 @@ 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();
if self.item_count == 0 {
return (
constraint.min,
LayoutState {
item_height: 0.,
scroll_max: 0.,
items,
},
);
}
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 {
(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);
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());
item_constraint.min = item_size;
item_constraint.max = item_size;
item_height = item_size.y();
}
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() {
@ -177,10 +208,9 @@ where
let scroll_height =
item_height * self.item_count as f32 + self.padding_top + self.padding_bottom;
scroll_max = (scroll_height - size.y()).max(0.);
let 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,
@ -189,12 +219,13 @@ where
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 {
item.layout(item_constraint, cx);
let item_size = item.layout(item_constraint, cx);
if item_size.x() > size.x() {
size.set_x(item_size.x());
}
} else {
size = constraint.min;
}
(

View file

@ -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(),
}
}
}

View file

@ -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 }