Put keystrokes in their own column
This requires rendering the menu for measurement in a totally different way, where the top level is a flex row. We don't want to render the menu like this for presentation because of hovers / highlights on individual items needing to include the keystrokes. Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
a8483ba458
commit
c0aafac387
2 changed files with 94 additions and 58 deletions
|
@ -42,16 +42,14 @@ impl View for ContextMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||||
enum Tag {}
|
|
||||||
|
|
||||||
if !self.visible {
|
if !self.visible {
|
||||||
return Empty::new().boxed();
|
return Empty::new().boxed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the menu once at minimum width.
|
// Render the menu once at minimum width.
|
||||||
let mut collapsed_menu = self.render_menu::<()>(false, cx).boxed();
|
let mut collapsed_menu = self.render_menu_for_measurement(cx).boxed();
|
||||||
let expanded_menu = self
|
let expanded_menu = self
|
||||||
.render_menu::<Tag>(true, cx)
|
.render_menu(cx)
|
||||||
.constrained()
|
.constrained()
|
||||||
.dynamically(move |constraint, cx| {
|
.dynamically(move |constraint, cx| {
|
||||||
SizeConstraint::strict_along(
|
SizeConstraint::strict_along(
|
||||||
|
@ -97,64 +95,100 @@ impl ContextMenu {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_menu<Tag: 'static>(
|
fn render_menu_for_measurement(&self, cx: &mut RenderContext<Self>) -> impl Element {
|
||||||
&mut self,
|
|
||||||
expanded: bool,
|
|
||||||
cx: &mut RenderContext<Self>,
|
|
||||||
) -> impl Element {
|
|
||||||
let style = cx.global::<Settings>().theme.context_menu.clone();
|
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||||
Flex::column()
|
Flex::row()
|
||||||
.with_children(
|
.with_child(
|
||||||
(0..self.items.len())
|
Flex::column()
|
||||||
.map(|ix| self.render_menu_item::<Tag>(ix, expanded, cx, &style)),
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
|
match item {
|
||||||
|
ContextMenuItem::Item { label, .. } => {
|
||||||
|
let style = style.item.style_for(
|
||||||
|
&Default::default(),
|
||||||
|
Some(ix) == self.selected_index,
|
||||||
|
);
|
||||||
|
Label::new(label.to_string(), style.label.clone()).boxed()
|
||||||
|
}
|
||||||
|
ContextMenuItem::Separator => Empty::new()
|
||||||
|
.collapsed()
|
||||||
|
.contained()
|
||||||
|
.with_style(style.separator)
|
||||||
|
.constrained()
|
||||||
|
.with_height(1.)
|
||||||
|
.boxed(),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
Flex::column()
|
||||||
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
|
match item {
|
||||||
|
ContextMenuItem::Item { action, .. } => {
|
||||||
|
let style = style.item.style_for(
|
||||||
|
&Default::default(),
|
||||||
|
Some(ix) == self.selected_index,
|
||||||
|
);
|
||||||
|
KeystrokeLabel::new(
|
||||||
|
action.boxed_clone(),
|
||||||
|
style.keystroke.container,
|
||||||
|
style.keystroke.text.clone(),
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
ContextMenuItem::Separator => Empty::new()
|
||||||
|
.collapsed()
|
||||||
|
.contained()
|
||||||
|
.with_style(style.separator)
|
||||||
|
.constrained()
|
||||||
|
.with_height(1.)
|
||||||
|
.boxed(),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.boxed(),
|
||||||
)
|
)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_menu_item<T: 'static>(
|
fn render_menu(&self, cx: &mut RenderContext<Self>) -> impl Element {
|
||||||
&self,
|
enum Tag {}
|
||||||
ix: usize,
|
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||||
expanded: bool,
|
Flex::column()
|
||||||
cx: &mut RenderContext<ContextMenu>,
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
style: &theme::ContextMenu,
|
match item {
|
||||||
) -> ElementBox {
|
ContextMenuItem::Item { label, action } => {
|
||||||
match &self.items[ix] {
|
let action = action.boxed_clone();
|
||||||
ContextMenuItem::Item { label, action } => {
|
MouseEventHandler::new::<Tag, _, _>(ix, cx, |state, _| {
|
||||||
let action = action.boxed_clone();
|
let style =
|
||||||
MouseEventHandler::new::<T, _, _>(ix, cx, |state, _| {
|
style.item.style_for(state, Some(ix) == self.selected_index);
|
||||||
let style = style.item.style_for(state, Some(ix) == self.selected_index);
|
Flex::row()
|
||||||
Flex::row()
|
.with_child(
|
||||||
.with_child(Label::new(label.to_string(), style.label.clone()).boxed())
|
Label::new(label.to_string(), style.label.clone()).boxed(),
|
||||||
.with_child({
|
)
|
||||||
let label = KeystrokeLabel::new(
|
.with_child({
|
||||||
action.boxed_clone(),
|
KeystrokeLabel::new(
|
||||||
style.keystroke.container,
|
action.boxed_clone(),
|
||||||
style.keystroke.text.clone(),
|
style.keystroke.container,
|
||||||
);
|
style.keystroke.text.clone(),
|
||||||
if expanded {
|
)
|
||||||
label.flex_float().boxed()
|
.flex_float()
|
||||||
} else {
|
.boxed()
|
||||||
label.boxed()
|
})
|
||||||
}
|
.boxed()
|
||||||
})
|
})
|
||||||
|
.on_click(move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()))
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
}
|
||||||
.on_click(move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()))
|
ContextMenuItem::Separator => Empty::new()
|
||||||
.boxed()
|
.contained()
|
||||||
}
|
.with_style(style.separator)
|
||||||
ContextMenuItem::Separator => {
|
.constrained()
|
||||||
let mut separator = Empty::new();
|
.with_height(1.)
|
||||||
if !expanded {
|
.boxed(),
|
||||||
separator = separator.collapsed();
|
|
||||||
}
|
}
|
||||||
separator
|
}))
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.separator)
|
.with_style(style.container)
|
||||||
.constrained()
|
|
||||||
.with_height(1.)
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import Theme from "../themes/common/theme";
|
import Theme from "../themes/common/theme";
|
||||||
import { shadow, text } from "./components";
|
import { backgroundColor, shadow, text } from "./components";
|
||||||
|
|
||||||
export default function contextMenu(theme: Theme) {
|
export default function contextMenu(theme: Theme) {
|
||||||
return {
|
return {
|
||||||
background: "#ff0000",
|
background: backgroundColor(theme, 300, "base"),
|
||||||
// background: backgroundColor(theme, 300, "base"),
|
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
padding: {
|
padding: {
|
||||||
bottom: 2,
|
bottom: 2,
|
||||||
|
@ -15,7 +14,10 @@ export default function contextMenu(theme: Theme) {
|
||||||
shadow: shadow(theme),
|
shadow: shadow(theme),
|
||||||
item: {
|
item: {
|
||||||
label: text(theme, "sans", "secondary", { size: "sm" }),
|
label: text(theme, "sans", "secondary", { size: "sm" }),
|
||||||
keystroke: text(theme, "sans", "muted", { size: "sm", weight: "bold" }),
|
keystroke: {
|
||||||
|
margin: { left: 60 },
|
||||||
|
...text(theme, "sans", "muted", { size: "sm", weight: "bold" })
|
||||||
|
},
|
||||||
},
|
},
|
||||||
separator: {
|
separator: {
|
||||||
background: "#00ff00"
|
background: "#00ff00"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue