update for multi-menu

This commit is contained in:
Ashley Wulber 2023-12-01 14:18:53 -05:00 committed by Ashley Wulber
parent 5f2e83b04d
commit 0e9b46af72
3 changed files with 133 additions and 72 deletions

2
iced

@ -1 +1 @@
Subproject commit 14ef12f429078be78c45cd3348162b5f8459e993 Subproject commit d67f1a1c79cf3dcb378520ef7e4e9ab653f75bea

View file

@ -190,7 +190,12 @@ impl<'a, Message: 'a> Overlay<'a, Message> {
} }
impl<'a, Message> iced_core::Overlay<Message, crate::Renderer> for Overlay<'a, Message> { impl<'a, Message> iced_core::Overlay<Message, crate::Renderer> for Overlay<'a, Message> {
fn layout(&self, renderer: &crate::Renderer, bounds: Size, position: Point) -> layout::Node { fn layout(
&mut self,
renderer: &crate::Renderer,
bounds: Size,
position: Point,
) -> layout::Node {
let space_below = bounds.height - (position.y + self.target_height); let space_below = bounds.height - (position.y + self.target_height);
let space_above = position.y; let space_above = position.y;
@ -207,7 +212,9 @@ impl<'a, Message> iced_core::Overlay<Message, crate::Renderer> for Overlay<'a, M
) )
.width(self.width); .width(self.width);
let mut node = self.container.layout(renderer, &limits); let mut node = self
.container
.layout(&mut self.state.children[0], renderer, &limits);
node.move_to(if space_below > space_above { node.move_to(if space_below > space_above {
position + Vector::new(0.0, self.target_height) position + Vector::new(0.0, self.target_height)
@ -296,13 +303,18 @@ where
Length::Shrink Length::Shrink
} }
fn layout(&self, renderer: &crate::Renderer, limits: &layout::Limits) -> layout::Node { fn layout(
&self,
_tree: &mut Tree,
renderer: &crate::Renderer,
limits: &layout::Limits,
) -> layout::Node {
use std::f32; use std::f32;
let limits = limits.width(Length::Fill).height(Length::Shrink); let limits = limits.width(Length::Fill).height(Length::Shrink);
let text_size = self let text_size = self
.text_size .text_size
.unwrap_or_else(|| text::Renderer::default_size(renderer)); .unwrap_or_else(|| text::Renderer::default_size(renderer).0);
let text_line_height = self.text_line_height.to_absolute(Pixels(text_size)); let text_line_height = self.text_line_height.to_absolute(Pixels(text_size));
@ -359,7 +371,7 @@ where
if let Some(cursor_position) = cursor.position_in(bounds) { if let Some(cursor_position) = cursor.position_in(bounds) {
let text_size = self let text_size = self
.text_size .text_size
.unwrap_or_else(|| text::Renderer::default_size(renderer)); .unwrap_or_else(|| text::Renderer::default_size(renderer).0);
let text_line_height = let text_line_height =
f32::from(self.text_line_height.to_absolute(Pixels(text_size))); f32::from(self.text_line_height.to_absolute(Pixels(text_size)));
@ -406,7 +418,7 @@ where
if let Some(cursor_position) = cursor.position_in(bounds) { if let Some(cursor_position) = cursor.position_in(bounds) {
let text_size = self let text_size = self
.text_size .text_size
.unwrap_or_else(|| text::Renderer::default_size(renderer)); .unwrap_or_else(|| text::Renderer::default_size(renderer).0);
let text_line_height = let text_line_height =
f32::from(self.text_line_height.to_absolute(Pixels(text_size))); f32::from(self.text_line_height.to_absolute(Pixels(text_size)));
@ -488,7 +500,7 @@ where
let text_size = self let text_size = self
.text_size .text_size
.unwrap_or_else(|| text::Renderer::default_size(renderer)); .unwrap_or_else(|| text::Renderer::default_size(renderer).0);
let offset = viewport.y - bounds.y; let offset = viewport.y - bounds.y;
@ -571,24 +583,26 @@ where
(appearance.text_color, crate::font::FONT) (appearance.text_color, crate::font::FONT)
}; };
let bounds = Rectangle {
x: bounds.x + self.padding.left,
y: bounds.y + self.padding.top,
width: bounds.width,
height: bounds.height,
};
text::Renderer::fill_text( text::Renderer::fill_text(
renderer, renderer,
Text { Text {
content: option.as_ref(), content: option.as_ref(),
bounds: Rectangle { bounds: bounds.size(),
x: bounds.x + self.padding.left, size: iced::Pixels(text_size),
y: bounds.center_y(),
width: bounds.width,
..bounds
},
size: text_size,
line_height: self.text_line_height, line_height: self.text_line_height,
font, font,
color,
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
}, },
bounds.position(),
color,
); );
} }
@ -618,23 +632,25 @@ where
} }
OptionElement::Description(description) => { OptionElement::Description(description) => {
let bounds = Rectangle {
x: bounds.center_x(),
y: bounds.center_y(),
..bounds
};
text::Renderer::fill_text( text::Renderer::fill_text(
renderer, renderer,
Text { Text {
content: description.as_ref(), content: description.as_ref(),
bounds: Rectangle { bounds: bounds.size(),
x: bounds.center_x(), size: iced::Pixels(text_size),
y: bounds.center_y(),
..bounds
},
size: text_size,
line_height: text::LineHeight::Absolute(Pixels(text_line_height + 4.0)), line_height: text::LineHeight::Absolute(Pixels(text_line_height + 4.0)),
font: crate::font::FONT, font: crate::font::FONT,
color: appearance.description_color,
horizontal_alignment: alignment::Horizontal::Center, horizontal_alignment: alignment::Horizontal::Center,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
}, },
bounds.position(),
appearance.description_color,
); );
} }
} }

View file

@ -6,7 +6,7 @@ use super::menu::{self, Menu};
use crate::widget::icon; use crate::widget::icon;
use derive_setters::Setters; use derive_setters::Setters;
use iced_core::event::{self, Event}; use iced_core::event::{self, Event};
use iced_core::text::{self, Text}; use iced_core::text::{self, Paragraph, Text};
use iced_core::widget::tree::{self, Tree}; use iced_core::widget::tree::{self, Tree};
use iced_core::{alignment, keyboard, layout, mouse, overlay, renderer, svg, touch}; use iced_core::{alignment, keyboard, layout, mouse, overlay, renderer, svg, touch};
use iced_core::{Clipboard, Layout, Length, Padding, Pixels, Rectangle, Shell, Size, Widget}; use iced_core::{Clipboard, Layout, Length, Padding, Pixels, Rectangle, Shell, Size, Widget};
@ -78,7 +78,12 @@ impl<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static>
Length::Shrink Length::Shrink
} }
fn layout(&self, renderer: &crate::Renderer, limits: &layout::Limits) -> layout::Node { fn layout(
&self,
tree: &mut Tree,
renderer: &crate::Renderer,
limits: &layout::Limits,
) -> layout::Node {
layout( layout(
renderer, renderer,
limits, limits,
@ -88,11 +93,16 @@ impl<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static>
self.text_size.unwrap_or(14.0), self.text_size.unwrap_or(14.0),
self.text_line_height, self.text_line_height,
self.font, self.font,
self.selections self.selections.selected.as_ref().and_then(|id| {
.selected self.selections.get(id).map(AsRef::as_ref).zip(
.as_ref() tree.state
.and_then(|id| self.selections.get(id)) .downcast_mut::<State<Item>>()
.map(AsRef::as_ref), .selections
.iter_mut()
.find(|(i, _)| i == id)
.map(|(_, p)| p),
)
}),
) )
} }
@ -177,6 +187,7 @@ impl<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static>
self.padding, self.padding,
self.text_size.unwrap_or(14.0), self.text_size.unwrap_or(14.0),
self.font, self.font,
self.text_line_height,
self.selections, self.selections,
&self.on_selected, &self.on_selected,
) )
@ -199,6 +210,8 @@ pub struct State<Item: Clone + PartialEq + 'static> {
keyboard_modifiers: keyboard::Modifiers, keyboard_modifiers: keyboard::Modifiers,
is_open: bool, is_open: bool,
hovered_option: Option<Item>, hovered_option: Option<Item>,
selections: Vec<(Item, crate::Paragraph)>,
descriptions: Vec<crate::Paragraph>,
} }
impl<Item: Clone + PartialEq + 'static> State<Item> { impl<Item: Clone + PartialEq + 'static> State<Item> {
@ -217,6 +230,8 @@ impl<Item: Clone + PartialEq + 'static> State<Item> {
keyboard_modifiers: keyboard::Modifiers::default(), keyboard_modifiers: keyboard::Modifiers::default(),
is_open: false, is_open: false,
hovered_option: None, hovered_option: None,
selections: Vec::new(),
descriptions: Vec::new(),
} }
} }
} }
@ -238,7 +253,7 @@ pub fn layout(
text_size: f32, text_size: f32,
text_line_height: text::LineHeight, text_line_height: text::LineHeight,
font: Option<crate::font::Font>, font: Option<crate::font::Font>,
selection: Option<&str>, selection: Option<(&str, &mut crate::Paragraph)>,
) -> layout::Node { ) -> layout::Node {
use std::f32; use std::f32;
@ -246,16 +261,18 @@ pub fn layout(
let max_width = match width { let max_width = match width {
Length::Shrink => { Length::Shrink => {
let measure = |label: &str| -> f32 { let measure = move |(label, paragraph): (_, &mut crate::Paragraph)| -> f32 {
let width = text::Renderer::measure_width( paragraph.update(Text {
renderer, content: label,
label, bounds: Size::new(f32::MAX, f32::MAX),
text_size, size: iced::Pixels(text_size),
font.unwrap_or_else(|| text::Renderer::default_font(renderer)), line_height: text_line_height,
text::Shaping::Advanced, font: font.unwrap_or_else(|| text::Renderer::default_font(renderer)),
); horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
width.round() shaping: text::Shaping::Advanced,
});
paragraph.min_width().round()
}; };
selection.map(measure).unwrap_or_default() selection.map(measure).unwrap_or_default()
@ -359,6 +376,7 @@ pub fn overlay<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static
padding: Padding, padding: Padding,
text_size: f32, text_size: f32,
font: Option<crate::font::Font>, font: Option<crate::font::Font>,
text_line_height: text::LineHeight,
selections: &'a super::Model<S, Item>, selections: &'a super::Model<S, Item>,
on_selected: &'a dyn Fn(Item) -> Message, on_selected: &'a dyn Fn(Item) -> Message,
) -> Option<overlay::Element<'a, Message, crate::Renderer>> { ) -> Option<overlay::Element<'a, Message, crate::Renderer>> {
@ -378,38 +396,62 @@ pub fn overlay<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static
None, None,
) )
.width({ .width({
let measure = |label: &str| -> f32 { let measure = |label: &str, paragraph: &mut crate::Paragraph| {
let width = text::Renderer::measure_width( paragraph.update(Text {
renderer, content: label,
label, bounds: Size::new(f32::MAX, f32::MAX),
text_size, size: iced::Pixels(text_size),
crate::font::FONT, line_height: text_line_height,
text::Shaping::Advanced, font: font.unwrap_or_else(|| text::Renderer::default_font(renderer)),
); horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
width.round() shaping: text::Shaping::Advanced,
});
paragraph.min_width().round()
}; };
let measure_description = |label: &str| -> f32 { let measure_description = |label: &str, paragraph: &mut crate::Paragraph| {
let width = text::Renderer::measure_width( paragraph.update(Text {
renderer, content: label,
label, bounds: Size::new(f32::MAX, f32::MAX),
text_size + 4.0, size: iced::Pixels(text_size + 4.0),
crate::font::FONT, line_height: text_line_height,
text::Shaping::Advanced, font: font.unwrap_or_else(|| text::Renderer::default_font(renderer)),
); horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
width.round() shaping: text::Shaping::Advanced,
});
paragraph.min_width().round()
}; };
let mut desc_count = 0;
selections selections
.elements() .elements()
.map(|element| match element { .map(|element| match element {
super::menu::OptionElement::Description(desc) => { super::menu::OptionElement::Description(desc) => {
measure_description(desc.as_ref()) let paragraph = if state.descriptions.len() > desc_count {
&mut state.descriptions[desc_count]
} else {
state.descriptions.push(crate::Paragraph::new());
state.descriptions.last_mut().unwrap()
};
desc_count += 1;
measure_description(desc.as_ref(), paragraph)
} }
super::menu::OptionElement::Option((option, _item)) => measure(option.as_ref()), super::menu::OptionElement::Option((option, item)) => {
let paragraph = if let Some(index) =
state.selections.iter().position(|(i, _)| i == item)
{
&mut state.selections[index].1
} else {
state
.selections
.push((item.clone(), crate::Paragraph::new()));
&mut state.selections.last_mut().unwrap().1
};
measure(option.as_ref(), paragraph)
}
super::menu::OptionElement::Separator => 1.0, super::menu::OptionElement::Separator => 1.0,
}) })
@ -482,26 +524,29 @@ pub fn draw<'a, S, Item: Clone + PartialEq + 'static>(
} }
if let Some(content) = selected.map(AsRef::as_ref) { if let Some(content) = selected.map(AsRef::as_ref) {
let text_size = text_size.unwrap_or_else(|| text::Renderer::default_size(renderer)); let text_size = text_size.unwrap_or_else(|| text::Renderer::default_size(renderer).0);
let bounds = Rectangle {
x: bounds.x + padding.left,
y: bounds.center_y(),
width: bounds.width - padding.horizontal(),
height: f32::from(text_line_height.to_absolute(Pixels(text_size))),
};
text::Renderer::fill_text( text::Renderer::fill_text(
renderer, renderer,
Text { Text {
content, content,
size: text_size, size: iced::Pixels(text_size),
line_height: text_line_height, line_height: text_line_height,
font, font,
color: style.text_color, bounds: bounds.size(),
bounds: Rectangle {
x: bounds.x + padding.left,
y: bounds.center_y(),
width: bounds.width - padding.horizontal(),
height: f32::from(text_line_height.to_absolute(Pixels(text_size))),
},
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
}, },
bounds.position(),
style.text_color,
); );
} }
} }