ui: Split up ContextMenu::render
into smaller methods (#26489)
This PR refactors the `ContextMenu::render` method to extract a couple smaller methods from it. The existing `render` method was suffering from its size, with some of the `match` arms not being able to be formatted with `rustfmt`. Release Notes: - N/A
This commit is contained in:
parent
8d6abf6537
commit
55a90f576a
1 changed files with 218 additions and 237 deletions
|
@ -495,6 +495,205 @@ impl ContextMenu {
|
||||||
self._on_blur_subscription = new_subscription;
|
self._on_blur_subscription = new_subscription;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_menu_item(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
item: &ContextMenuItem,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> impl IntoElement {
|
||||||
|
match item {
|
||||||
|
ContextMenuItem::Separator => ListSeparator.into_any_element(),
|
||||||
|
ContextMenuItem::Header(header) => ListSubHeader::new(header.clone())
|
||||||
|
.inset(true)
|
||||||
|
.into_any_element(),
|
||||||
|
ContextMenuItem::Label(label) => ListItem::new(ix)
|
||||||
|
.inset(true)
|
||||||
|
.disabled(true)
|
||||||
|
.child(Label::new(label.clone()))
|
||||||
|
.into_any_element(),
|
||||||
|
ContextMenuItem::Entry(entry) => self
|
||||||
|
.render_menu_entry(ix, entry, window, cx)
|
||||||
|
.into_any_element(),
|
||||||
|
ContextMenuItem::CustomEntry {
|
||||||
|
entry_render,
|
||||||
|
handler,
|
||||||
|
selectable,
|
||||||
|
} => {
|
||||||
|
let handler = handler.clone();
|
||||||
|
let menu = cx.entity().downgrade();
|
||||||
|
let selectable = *selectable;
|
||||||
|
ListItem::new(ix)
|
||||||
|
.inset(true)
|
||||||
|
.toggle_state(if selectable {
|
||||||
|
Some(ix) == self.selected_index
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
})
|
||||||
|
.selectable(selectable)
|
||||||
|
.when(selectable, |item| {
|
||||||
|
item.on_click({
|
||||||
|
let context = self.action_context.clone();
|
||||||
|
move |_, window, cx| {
|
||||||
|
handler(context.as_ref(), window, cx);
|
||||||
|
menu.update(cx, |menu, cx| {
|
||||||
|
menu.clicked = true;
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.child(entry_render(window, cx))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_menu_entry(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
entry: &ContextMenuEntry,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> impl IntoElement {
|
||||||
|
let ContextMenuEntry {
|
||||||
|
toggle,
|
||||||
|
label,
|
||||||
|
handler,
|
||||||
|
icon,
|
||||||
|
icon_position,
|
||||||
|
icon_size,
|
||||||
|
icon_color,
|
||||||
|
action,
|
||||||
|
disabled,
|
||||||
|
documentation_aside,
|
||||||
|
} = entry;
|
||||||
|
|
||||||
|
let handler = handler.clone();
|
||||||
|
let menu = cx.entity().downgrade();
|
||||||
|
|
||||||
|
let icon_color = if *disabled {
|
||||||
|
Color::Muted
|
||||||
|
} else if toggle.is_some() {
|
||||||
|
icon_color.unwrap_or(Color::Accent)
|
||||||
|
} else {
|
||||||
|
icon_color.unwrap_or(Color::Default)
|
||||||
|
};
|
||||||
|
|
||||||
|
let label_color = if *disabled {
|
||||||
|
Color::Disabled
|
||||||
|
} else {
|
||||||
|
Color::Default
|
||||||
|
};
|
||||||
|
|
||||||
|
let label_element = if let Some(icon_name) = icon {
|
||||||
|
h_flex()
|
||||||
|
.gap_1p5()
|
||||||
|
.when(
|
||||||
|
*icon_position == IconPosition::Start && toggle.is_none(),
|
||||||
|
|flex| flex.child(Icon::new(*icon_name).size(*icon_size).color(icon_color)),
|
||||||
|
)
|
||||||
|
.child(Label::new(label.clone()).color(label_color))
|
||||||
|
.when(*icon_position == IconPosition::End, |flex| {
|
||||||
|
flex.child(Icon::new(*icon_name).size(*icon_size).color(icon_color))
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
|
} else {
|
||||||
|
Label::new(label.clone())
|
||||||
|
.color(label_color)
|
||||||
|
.into_any_element()
|
||||||
|
};
|
||||||
|
|
||||||
|
let documentation_aside_callback = documentation_aside.clone();
|
||||||
|
|
||||||
|
div()
|
||||||
|
.id(("context-menu-child", ix))
|
||||||
|
.when_some(
|
||||||
|
documentation_aside_callback.clone(),
|
||||||
|
|this, documentation_aside_callback| {
|
||||||
|
this.occlude()
|
||||||
|
.on_hover(cx.listener(move |menu, hovered, _, cx| {
|
||||||
|
if *hovered {
|
||||||
|
menu.documentation_aside =
|
||||||
|
Some((ix, documentation_aside_callback.clone()));
|
||||||
|
cx.notify();
|
||||||
|
} else if matches!(menu.documentation_aside, Some((id, _)) if id == ix)
|
||||||
|
{
|
||||||
|
menu.documentation_aside = None;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ListItem::new(ix)
|
||||||
|
.inset(true)
|
||||||
|
.disabled(*disabled)
|
||||||
|
.toggle_state(Some(ix) == self.selected_index)
|
||||||
|
.when_some(*toggle, |list_item, (position, toggled)| {
|
||||||
|
let contents = div()
|
||||||
|
.flex_none()
|
||||||
|
.child(
|
||||||
|
Icon::new(icon.unwrap_or(IconName::Check))
|
||||||
|
.color(icon_color)
|
||||||
|
.size(*icon_size),
|
||||||
|
)
|
||||||
|
.when(!toggled, |contents| contents.invisible());
|
||||||
|
|
||||||
|
match position {
|
||||||
|
IconPosition::Start => list_item.start_slot(contents),
|
||||||
|
IconPosition::End => list_item.end_slot(contents),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.justify_between()
|
||||||
|
.child(label_element)
|
||||||
|
.debug_selector(|| format!("MENU_ITEM-{}", label))
|
||||||
|
.children(action.as_ref().and_then(|action| {
|
||||||
|
self.action_context
|
||||||
|
.as_ref()
|
||||||
|
.map(|focus| {
|
||||||
|
KeyBinding::for_action_in(&**action, focus, window, cx)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
KeyBinding::for_action(&**action, window, cx)
|
||||||
|
})
|
||||||
|
.map(|binding| {
|
||||||
|
div().ml_4().child(binding).when(
|
||||||
|
*disabled && documentation_aside_callback.is_some(),
|
||||||
|
|parent| parent.invisible(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
.when(
|
||||||
|
*disabled && documentation_aside_callback.is_some(),
|
||||||
|
|parent| {
|
||||||
|
parent.child(
|
||||||
|
Icon::new(IconName::Info)
|
||||||
|
.size(IconSize::XSmall)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_click({
|
||||||
|
let context = self.action_context.clone();
|
||||||
|
move |_, window, cx| {
|
||||||
|
handler(context.as_ref(), window, cx);
|
||||||
|
menu.update(cx, |menu, cx| {
|
||||||
|
menu.clicked = true;
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenuItem {
|
impl ContextMenuItem {
|
||||||
|
@ -522,23 +721,21 @@ impl Render for ContextMenu {
|
||||||
.map(|(_, callback)| callback.clone());
|
.map(|(_, callback)| callback.clone());
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.when(is_wide_window, |this| {this.flex_row()})
|
.when(is_wide_window, |this| this.flex_row())
|
||||||
.when(!is_wide_window, |this| {this.flex_col()})
|
.when(!is_wide_window, |this| this.flex_col())
|
||||||
.w_full()
|
.w_full()
|
||||||
.items_start()
|
.items_start()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(
|
.child(div().children(aside.map(|aside| {
|
||||||
div().children(aside.map(|aside|
|
WithRemSize::new(ui_font_size)
|
||||||
WithRemSize::new(ui_font_size)
|
.occlude()
|
||||||
.occlude()
|
.elevation_2(cx)
|
||||||
.elevation_2(cx)
|
.p_2()
|
||||||
.p_2()
|
.overflow_hidden()
|
||||||
.overflow_hidden()
|
.when(is_wide_window, |this| this.max_w_96())
|
||||||
.when(is_wide_window, |this| {this.max_w_96()})
|
.when(!is_wide_window, |this| this.max_w_48())
|
||||||
.when(!is_wide_window, |this| {this.max_w_48()})
|
.child(aside(cx))
|
||||||
.child(aside(cx))
|
})))
|
||||||
))
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
WithRemSize::new(ui_font_size)
|
WithRemSize::new(ui_font_size)
|
||||||
.occlude()
|
.occlude()
|
||||||
|
@ -579,229 +776,13 @@ impl Render for ContextMenu {
|
||||||
}
|
}
|
||||||
el
|
el
|
||||||
})
|
})
|
||||||
.child(List::new().children(self.items.iter_mut().enumerate().map(
|
.child(
|
||||||
|(ix, item)| {
|
List::new().children(
|
||||||
match item {
|
self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
ContextMenuItem::Separator => {
|
self.render_menu_item(ix, item, window, cx)
|
||||||
ListSeparator.into_any_element()
|
}),
|
||||||
}
|
),
|
||||||
ContextMenuItem::Header(header) => {
|
),
|
||||||
ListSubHeader::new(header.clone())
|
|
||||||
.inset(true)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
ContextMenuItem::Label(label) => ListItem::new(ix)
|
|
||||||
.inset(true)
|
|
||||||
.disabled(true)
|
|
||||||
.child(Label::new(label.clone()))
|
|
||||||
.into_any_element(),
|
|
||||||
ContextMenuItem::Entry(ContextMenuEntry {
|
|
||||||
toggle,
|
|
||||||
label,
|
|
||||||
handler,
|
|
||||||
icon,
|
|
||||||
icon_position,
|
|
||||||
icon_size,
|
|
||||||
icon_color,
|
|
||||||
action,
|
|
||||||
disabled,
|
|
||||||
documentation_aside,
|
|
||||||
}) => {
|
|
||||||
let handler = handler.clone();
|
|
||||||
let menu = cx.entity().downgrade();
|
|
||||||
|
|
||||||
let icon_color = if *disabled {
|
|
||||||
Color::Muted
|
|
||||||
} else if toggle.is_some() {
|
|
||||||
icon_color.unwrap_or(Color::Accent)
|
|
||||||
} else {
|
|
||||||
icon_color.unwrap_or(Color::Default)
|
|
||||||
};
|
|
||||||
|
|
||||||
let label_color = if *disabled {
|
|
||||||
Color::Disabled
|
|
||||||
} else {
|
|
||||||
Color::Default
|
|
||||||
};
|
|
||||||
|
|
||||||
let label_element = if let Some(icon_name) = icon {
|
|
||||||
h_flex()
|
|
||||||
.gap_1p5()
|
|
||||||
.when(
|
|
||||||
*icon_position == IconPosition::Start && toggle.is_none(),
|
|
||||||
|flex| {
|
|
||||||
flex.child(
|
|
||||||
Icon::new(*icon_name)
|
|
||||||
.size(*icon_size)
|
|
||||||
.color(icon_color),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Label::new(label.clone())
|
|
||||||
.color(label_color),
|
|
||||||
)
|
|
||||||
.when(
|
|
||||||
*icon_position == IconPosition::End,
|
|
||||||
|flex| {
|
|
||||||
flex.child(
|
|
||||||
Icon::new(*icon_name)
|
|
||||||
.size(*icon_size)
|
|
||||||
.color(icon_color),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
} else {
|
|
||||||
Label::new(label.clone())
|
|
||||||
.color(label_color)
|
|
||||||
.into_any_element()
|
|
||||||
};
|
|
||||||
|
|
||||||
let documentation_aside_callback =
|
|
||||||
documentation_aside.clone();
|
|
||||||
|
|
||||||
div()
|
|
||||||
.id(("context-menu-child", ix))
|
|
||||||
.when_some(
|
|
||||||
documentation_aside_callback.clone(),
|
|
||||||
|this, documentation_aside_callback| {
|
|
||||||
this.occlude().on_hover(cx.listener(
|
|
||||||
move |menu, hovered, _, cx| {
|
|
||||||
if *hovered {
|
|
||||||
menu.documentation_aside = Some((ix, documentation_aside_callback.clone()));
|
|
||||||
cx.notify();
|
|
||||||
} else if matches!(menu.documentation_aside, Some((id, _)) if id == ix) {
|
|
||||||
menu.documentation_aside = None;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
ListItem::new(ix)
|
|
||||||
.inset(true)
|
|
||||||
.disabled(*disabled)
|
|
||||||
.toggle_state(
|
|
||||||
Some(ix) == self.selected_index,
|
|
||||||
)
|
|
||||||
.when_some(
|
|
||||||
*toggle,
|
|
||||||
|list_item, (position, toggled)| {
|
|
||||||
let contents =
|
|
||||||
div().flex_none().child(
|
|
||||||
Icon::new(icon.unwrap_or(IconName::Check))
|
|
||||||
.color(icon_color)
|
|
||||||
.size(*icon_size)
|
|
||||||
)
|
|
||||||
.when(!toggled, |contents|
|
|
||||||
contents.invisible()
|
|
||||||
);
|
|
||||||
|
|
||||||
match position {
|
|
||||||
IconPosition::Start => {
|
|
||||||
list_item
|
|
||||||
.start_slot(contents)
|
|
||||||
}
|
|
||||||
IconPosition::End => {
|
|
||||||
list_item.end_slot(contents)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.w_full()
|
|
||||||
.justify_between()
|
|
||||||
.child(label_element)
|
|
||||||
.debug_selector(|| {
|
|
||||||
format!("MENU_ITEM-{}", label)
|
|
||||||
})
|
|
||||||
.children(
|
|
||||||
action.as_ref().and_then(
|
|
||||||
|action| {
|
|
||||||
self.action_context
|
|
||||||
.as_ref()
|
|
||||||
.map(|focus| {
|
|
||||||
KeyBinding::for_action_in(
|
|
||||||
&**action, focus,
|
|
||||||
window,
|
|
||||||
cx
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
KeyBinding::for_action(
|
|
||||||
&**action, window, cx
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map(|binding| {
|
|
||||||
div().ml_4().child(binding)
|
|
||||||
.when(*disabled && documentation_aside_callback.is_some(), |parent| {
|
|
||||||
parent.invisible()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.when(*disabled && documentation_aside_callback.is_some(), |parent| {
|
|
||||||
parent.child(Icon::new(IconName::Info).size(IconSize::XSmall).color(Color::Muted))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.on_click({
|
|
||||||
let context =
|
|
||||||
self.action_context.clone();
|
|
||||||
move |_, window, cx| {
|
|
||||||
handler(
|
|
||||||
context.as_ref(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
menu.update(cx, |menu, cx| {
|
|
||||||
menu.clicked = true;
|
|
||||||
cx.emit(DismissEvent);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
ContextMenuItem::CustomEntry {
|
|
||||||
entry_render,
|
|
||||||
handler,
|
|
||||||
selectable,
|
|
||||||
} => {
|
|
||||||
let handler = handler.clone();
|
|
||||||
let menu = cx.entity().downgrade();
|
|
||||||
let selectable = *selectable;
|
|
||||||
ListItem::new(ix)
|
|
||||||
.inset(true)
|
|
||||||
.toggle_state(if selectable {
|
|
||||||
Some(ix) == self.selected_index
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
})
|
|
||||||
.selectable(selectable)
|
|
||||||
.when(selectable, |item| {
|
|
||||||
item.on_click({
|
|
||||||
let context = self.action_context.clone();
|
|
||||||
move |_, window, cx| {
|
|
||||||
handler(context.as_ref(), window, cx);
|
|
||||||
menu.update(cx, |menu, cx| {
|
|
||||||
menu.clicked = true;
|
|
||||||
cx.emit(DismissEvent);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.child(entry_render(window, cx))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)))
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue