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
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -522,23 +721,21 @@ impl Render for ContextMenu {
|
|||
.map(|(_, callback)| callback.clone());
|
||||
|
||||
h_flex()
|
||||
.when(is_wide_window, |this| {this.flex_row()})
|
||||
.when(!is_wide_window, |this| {this.flex_col()})
|
||||
.when(is_wide_window, |this| this.flex_row())
|
||||
.when(!is_wide_window, |this| this.flex_col())
|
||||
.w_full()
|
||||
.items_start()
|
||||
.gap_1()
|
||||
.child(
|
||||
div().children(aside.map(|aside|
|
||||
WithRemSize::new(ui_font_size)
|
||||
.occlude()
|
||||
.elevation_2(cx)
|
||||
.p_2()
|
||||
.overflow_hidden()
|
||||
.when(is_wide_window, |this| {this.max_w_96()})
|
||||
.when(!is_wide_window, |this| {this.max_w_48()})
|
||||
.child(aside(cx))
|
||||
))
|
||||
)
|
||||
.child(div().children(aside.map(|aside| {
|
||||
WithRemSize::new(ui_font_size)
|
||||
.occlude()
|
||||
.elevation_2(cx)
|
||||
.p_2()
|
||||
.overflow_hidden()
|
||||
.when(is_wide_window, |this| this.max_w_96())
|
||||
.when(!is_wide_window, |this| this.max_w_48())
|
||||
.child(aside(cx))
|
||||
})))
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.occlude()
|
||||
|
@ -579,229 +776,13 @@ impl Render for ContextMenu {
|
|||
}
|
||||
el
|
||||
})
|
||||
.child(List::new().children(self.items.iter_mut().enumerate().map(
|
||||
|(ix, item)| {
|
||||
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(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()
|
||||
}
|
||||
}
|
||||
},
|
||||
)))
|
||||
.child(
|
||||
List::new().children(
|
||||
self.items.iter().enumerate().map(|(ix, item)| {
|
||||
self.render_menu_item(ix, item, window, cx)
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue