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()
|
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| {
|
self.completion_state.as_ref().map(|state| {
|
||||||
let build_settings = self.build_settings.clone();
|
let build_settings = self.build_settings.clone();
|
||||||
|
let settings = build_settings(cx);
|
||||||
let completions = state.completions.clone();
|
let completions = state.completions.clone();
|
||||||
UniformList::new(
|
UniformList::new(
|
||||||
state.list.clone(),
|
state.list.clone(),
|
||||||
|
@ -1547,11 +1548,23 @@ impl Editor {
|
||||||
for completion in &completions[range] {
|
for completion in &completions[range] {
|
||||||
items.push(
|
items.push(
|
||||||
Label::new(completion.label().to_string(), settings.style.text.clone())
|
Label::new(completion.label().to_string(), settings.style.text.clone())
|
||||||
|
.contained()
|
||||||
|
.with_style(settings.style.autocomplete.item)
|
||||||
.boxed(),
|
.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()
|
.boxed()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4056,6 +4069,7 @@ impl EditorSettings {
|
||||||
invalid_information_diagnostic: default_diagnostic_style.clone(),
|
invalid_information_diagnostic: default_diagnostic_style.clone(),
|
||||||
hint_diagnostic: default_diagnostic_style.clone(),
|
hint_diagnostic: default_diagnostic_style.clone(),
|
||||||
invalid_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);
|
.to_display_point(&snapshot);
|
||||||
|
|
||||||
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
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));
|
completions = Some((newest_selection_head, list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ where
|
||||||
append_items: F,
|
append_items: F,
|
||||||
padding_top: f32,
|
padding_top: f32,
|
||||||
padding_bottom: f32,
|
padding_bottom: f32,
|
||||||
|
get_width_from_item: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> UniformList<F>
|
impl<F> UniformList<F>
|
||||||
|
@ -64,9 +65,15 @@ where
|
||||||
append_items,
|
append_items,
|
||||||
padding_top: 0.,
|
padding_top: 0.,
|
||||||
padding_bottom: 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 {
|
pub fn with_padding_top(mut self, padding: f32) -> Self {
|
||||||
self.padding_top = padding;
|
self.padding_top = padding;
|
||||||
self
|
self
|
||||||
|
@ -155,46 +162,70 @@ where
|
||||||
"UniformList does not support being rendered with an unconstrained height"
|
"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();
|
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 self.item_count == 0 {
|
||||||
if scroll_height < size.y() {
|
return (
|
||||||
size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
|
constraint.min,
|
||||||
}
|
LayoutState {
|
||||||
|
item_height: 0.,
|
||||||
let scroll_height =
|
scroll_max: 0.,
|
||||||
item_height * self.item_count as f32 + self.padding_top + self.padding_bottom;
|
items,
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
let end = cmp::min(
|
}
|
||||||
self.item_count,
|
|
||||||
start + (size.y() / item_height).ceil() as usize + 1,
|
let mut size = constraint.max;
|
||||||
);
|
let mut item_size;
|
||||||
(self.append_items)(start..end, &mut items, cx);
|
if let Some(sample_item_ix) = self.get_width_from_item {
|
||||||
for item in &mut items {
|
(self.append_items)(sample_item_ix..sample_item_ix + 1, &mut items, cx);
|
||||||
item.layout(item_constraint, cx);
|
let sample_item = items.get_mut(0).unwrap();
|
||||||
}
|
item_size = sample_item.layout(constraint, cx);
|
||||||
|
size.set_x(item_size.x());
|
||||||
} else {
|
} 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 invalid_information_diagnostic: DiagnosticStyle,
|
||||||
pub hint_diagnostic: DiagnosticStyle,
|
pub hint_diagnostic: DiagnosticStyle,
|
||||||
pub invalid_hint_diagnostic: DiagnosticStyle,
|
pub invalid_hint_diagnostic: DiagnosticStyle,
|
||||||
|
pub autocomplete: AutocompleteStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
@ -321,6 +322,13 @@ pub struct DiagnosticStyle {
|
||||||
pub text_scale_factor: f32,
|
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)]
|
#[derive(Clone, Copy, Default, Deserialize)]
|
||||||
pub struct SelectionStyle {
|
pub struct SelectionStyle {
|
||||||
pub cursor: Color,
|
pub cursor: Color,
|
||||||
|
@ -408,6 +416,7 @@ impl InputEditorStyle {
|
||||||
invalid_information_diagnostic: default_diagnostic_style.clone(),
|
invalid_information_diagnostic: default_diagnostic_style.clone(),
|
||||||
hint_diagnostic: default_diagnostic_style.clone(),
|
hint_diagnostic: default_diagnostic_style.clone(),
|
||||||
invalid_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]
|
[project_panel]
|
||||||
extends = "$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]
|
[project_panel.entry]
|
||||||
text = "$text.1"
|
text = "$text.1"
|
||||||
|
@ -314,6 +314,11 @@ extends = "$editor.hint_diagnostic"
|
||||||
message.text.color = "$text.3.color"
|
message.text.color = "$text.3.color"
|
||||||
message.highlight_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]
|
[project_diagnostics]
|
||||||
background = "$surface.1"
|
background = "$surface.1"
|
||||||
empty_message = { extends = "$text.0", size = 18 }
|
empty_message = { extends = "$text.0", size = 18 }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue