Replace stateful widgets with new iced_pure API
This commit is contained in:
parent
c44267b85f
commit
ff2519b1d4
142 changed files with 3631 additions and 14494 deletions
|
|
@ -7,6 +7,7 @@ use crate::mouse;
|
|||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::touch;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Background, Clipboard, Color, Element, Layout, Length, Padding, Point,
|
||||
Rectangle, Shell, Vector, Widget,
|
||||
|
|
@ -17,8 +18,6 @@ pub use iced_style::button::{Appearance, StyleSheet};
|
|||
/// A generic widget that produces a message when pressed.
|
||||
///
|
||||
/// ```
|
||||
/// # use iced_native::widget::{button, Text};
|
||||
/// #
|
||||
/// # type Button<'a, Message> =
|
||||
/// # iced_native::widget::Button<'a, Message, iced_native::renderer::Null>;
|
||||
/// #
|
||||
|
|
@ -27,17 +26,13 @@ pub use iced_style::button::{Appearance, StyleSheet};
|
|||
/// ButtonPressed,
|
||||
/// }
|
||||
///
|
||||
/// let mut state = button::State::new();
|
||||
/// let button = Button::new(&mut state, Text::new("Press me!"))
|
||||
/// .on_press(Message::ButtonPressed);
|
||||
/// let button = Button::new("Press me!").on_press(Message::ButtonPressed);
|
||||
/// ```
|
||||
///
|
||||
/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will
|
||||
/// be disabled:
|
||||
///
|
||||
/// ```
|
||||
/// # use iced_native::widget::{button, Text};
|
||||
/// #
|
||||
/// # type Button<'a, Message> =
|
||||
/// # iced_native::widget::Button<'a, Message, iced_native::renderer::Null>;
|
||||
/// #
|
||||
|
|
@ -46,12 +41,12 @@ pub use iced_style::button::{Appearance, StyleSheet};
|
|||
/// ButtonPressed,
|
||||
/// }
|
||||
///
|
||||
/// fn disabled_button(state: &mut button::State) -> Button<'_, Message> {
|
||||
/// Button::new(state, Text::new("I'm disabled!"))
|
||||
/// fn disabled_button<'a>() -> Button<'a, Message> {
|
||||
/// Button::new("I'm disabled!")
|
||||
/// }
|
||||
///
|
||||
/// fn enabled_button(state: &mut button::State) -> Button<'_, Message> {
|
||||
/// disabled_button(state).on_press(Message::ButtonPressed)
|
||||
/// fn enabled_button<'a>() -> Button<'a, Message> {
|
||||
/// disabled_button().on_press(Message::ButtonPressed)
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(missing_debug_implementations)]
|
||||
|
|
@ -60,7 +55,6 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
state: &'a mut State,
|
||||
content: Element<'a, Message, Renderer>,
|
||||
on_press: Option<Message>,
|
||||
width: Length,
|
||||
|
|
@ -71,24 +65,18 @@ where
|
|||
|
||||
impl<'a, Message, Renderer> Button<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
/// Creates a new [`Button`] with some local [`State`] and the given
|
||||
/// content.
|
||||
pub fn new<E>(state: &'a mut State, content: E) -> Self
|
||||
where
|
||||
E: Into<Element<'a, Message, Renderer>>,
|
||||
{
|
||||
/// Creates a new [`Button`] with the given content.
|
||||
pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
|
||||
Button {
|
||||
state,
|
||||
content: content.into(),
|
||||
on_press: None,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
padding: Padding::new(5),
|
||||
style: Default::default(),
|
||||
style: <Renderer::Theme as StyleSheet>::Style::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,13 +99,14 @@ where
|
|||
}
|
||||
|
||||
/// Sets the message that will be produced when the [`Button`] is pressed.
|
||||
/// If on_press isn't set, button will be disabled.
|
||||
///
|
||||
/// Unless `on_press` is called, the [`Button`] will be disabled.
|
||||
pub fn on_press(mut self, msg: Message) -> Self {
|
||||
self.on_press = Some(msg);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style of this [`Button`].
|
||||
/// Sets the style variant of this [`Button`].
|
||||
pub fn style(
|
||||
mut self,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
|
|
@ -127,6 +116,159 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Button<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a + Clone,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![Tree::new(&self.content)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(std::slice::from_ref(&self.content))
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.padding,
|
||||
|renderer, limits| {
|
||||
self.content.as_widget().layout(renderer, limits)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
if let event::Status::Captured = self.content.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event.clone(),
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
) {
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
&self.on_press,
|
||||
|| tree.state.downcast_mut::<State>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
let bounds = layout.bounds();
|
||||
let content_layout = layout.children().next().unwrap();
|
||||
|
||||
let styling = draw(
|
||||
renderer,
|
||||
bounds,
|
||||
cursor_position,
|
||||
self.on_press.is_some(),
|
||||
theme,
|
||||
self.style,
|
||||
|| tree.state.downcast_ref::<State>(),
|
||||
);
|
||||
|
||||
self.content.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
&renderer::Style {
|
||||
text_color: styling.text_color,
|
||||
},
|
||||
content_layout,
|
||||
cursor_position,
|
||||
&bounds,
|
||||
);
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position, self.on_press.is_some())
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content.as_widget().overlay(
|
||||
&mut tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone + 'a,
|
||||
Renderer: crate::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(button: Button<'a, Message, Renderer>) -> Self {
|
||||
Self::new(button)
|
||||
}
|
||||
}
|
||||
|
||||
/// The local state of a [`Button`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct State {
|
||||
|
|
@ -292,131 +434,3 @@ pub fn mouse_interaction(
|
|||
mouse::Interaction::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Button<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.padding,
|
||||
|renderer, limits| self.content.layout(renderer, limits),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
if let event::Status::Captured = self.content.on_event(
|
||||
event.clone(),
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
) {
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
&self.on_press,
|
||||
|| &mut self.state,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position, self.on_press.is_some())
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
let bounds = layout.bounds();
|
||||
let content_layout = layout.children().next().unwrap();
|
||||
|
||||
let styling = draw(
|
||||
renderer,
|
||||
bounds,
|
||||
cursor_position,
|
||||
self.on_press.is_some(),
|
||||
theme,
|
||||
self.style,
|
||||
|| self.state,
|
||||
);
|
||||
|
||||
self.content.draw(
|
||||
renderer,
|
||||
theme,
|
||||
&renderer::Style {
|
||||
text_color: styling.text_color,
|
||||
},
|
||||
content_layout,
|
||||
cursor_position,
|
||||
&bounds,
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.content
|
||||
.overlay(layout.children().next().unwrap(), renderer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a + Clone,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
button: Button<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(button)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::mouse;
|
|||
use crate::renderer;
|
||||
use crate::text;
|
||||
use crate::touch;
|
||||
use crate::widget::{self, Row, Text};
|
||||
use crate::widget::{self, Row, Text, Tree};
|
||||
use crate::{
|
||||
Alignment, Clipboard, Element, Layout, Length, Point, Rectangle, Shell,
|
||||
Widget,
|
||||
|
|
@ -168,6 +168,7 @@ where
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -194,6 +195,7 @@ where
|
|||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
|
|
@ -208,6 +210,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::layout;
|
|||
use crate::mouse;
|
||||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
Alignment, Clipboard, Element, Layout, Length, Padding, Point, Rectangle,
|
||||
Shell, Widget,
|
||||
|
|
@ -19,7 +20,6 @@ pub struct Column<'a, Message, Renderer> {
|
|||
width: Length,
|
||||
height: Length,
|
||||
max_width: u32,
|
||||
max_height: u32,
|
||||
align_items: Alignment,
|
||||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
}
|
||||
|
|
@ -40,7 +40,6 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
|
|||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
max_width: u32::MAX,
|
||||
max_height: u32::MAX,
|
||||
align_items: Alignment::Start,
|
||||
children,
|
||||
}
|
||||
|
|
@ -80,12 +79,6 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the maximum height of the [`Column`] in pixels.
|
||||
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||
self.max_height = max_height;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the horizontal alignment of the contents of the [`Column`] .
|
||||
pub fn align_items(mut self, align: Alignment) -> Self {
|
||||
self.align_items = align;
|
||||
|
|
@ -93,10 +86,10 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
|
|||
}
|
||||
|
||||
/// Adds an element to the [`Column`].
|
||||
pub fn push<E>(mut self, child: E) -> Self
|
||||
where
|
||||
E: Into<Element<'a, Message, Renderer>>,
|
||||
{
|
||||
pub fn push(
|
||||
mut self,
|
||||
child: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
self.children.push(child.into());
|
||||
self
|
||||
}
|
||||
|
|
@ -113,6 +106,14 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
|||
where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
self.children.iter().map(Tree::new).collect()
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(&self.children);
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
|
@ -128,7 +129,6 @@ where
|
|||
) -> layout::Node {
|
||||
let limits = limits
|
||||
.max_width(self.max_width)
|
||||
.max_height(self.max_height)
|
||||
.width(self.width)
|
||||
.height(self.height);
|
||||
|
||||
|
|
@ -145,6 +145,7 @@ where
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -154,9 +155,11 @@ where
|
|||
) -> event::Status {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
child.widget.on_event(
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget_mut().on_event(
|
||||
state,
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor_position,
|
||||
|
|
@ -170,6 +173,7 @@ where
|
|||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
|
|
@ -177,9 +181,11 @@ where
|
|||
) -> mouse::Interaction {
|
||||
self.children
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
child.widget.mouse_interaction(
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget().mouse_interaction(
|
||||
state,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
|
|
@ -192,6 +198,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
@ -199,8 +206,14 @@ where
|
|||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
for (child, layout) in self.children.iter().zip(layout.children()) {
|
||||
child.draw(
|
||||
for ((child, state), layout) in self
|
||||
.children
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.zip(layout.children())
|
||||
{
|
||||
child.as_widget().draw(
|
||||
state,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
|
|
@ -211,30 +224,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(layout.children())
|
||||
.filter_map(|(child, layout)| {
|
||||
child.widget.overlay(layout, renderer)
|
||||
})
|
||||
.next()
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
overlay::from_children(&self.children, tree, layout, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Column<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Message: 'a,
|
||||
Renderer: crate::Renderer + 'a,
|
||||
{
|
||||
fn from(
|
||||
column: Column<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(column)
|
||||
fn from(column: Column<'a, Message, Renderer>) -> Self {
|
||||
Self::new(column)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crate::layout;
|
|||
use crate::mouse;
|
||||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
Background, Clipboard, Color, Element, Layout, Length, Padding, Point,
|
||||
Rectangle, Shell, Widget,
|
||||
|
|
@ -121,6 +122,144 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Container<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![Tree::new(&self.content)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(std::slice::from_ref(&self.content))
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.max_width,
|
||||
self.max_height,
|
||||
self.padding,
|
||||
self.horizontal_alignment,
|
||||
self.vertical_alignment,
|
||||
|renderer, limits| {
|
||||
self.content.as_widget().layout(renderer, limits)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.content.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.content.as_widget().mouse_interaction(
|
||||
&tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
renderer_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
let style = theme.appearance(self.style);
|
||||
|
||||
draw_background(renderer, &style, layout.bounds());
|
||||
|
||||
self.content.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
&renderer::Style {
|
||||
text_color: style
|
||||
.text_color
|
||||
.unwrap_or(renderer_style.text_color),
|
||||
},
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content.as_widget().overlay(
|
||||
&mut tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
column: Container<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(column)
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the layout of a [`Container`].
|
||||
pub fn layout<Renderer>(
|
||||
renderer: &Renderer,
|
||||
|
|
@ -155,110 +294,6 @@ pub fn layout<Renderer>(
|
|||
layout::Node::with_children(size.pad(padding), vec![content])
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Container<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.max_width,
|
||||
self.max_height,
|
||||
self.padding,
|
||||
self.horizontal_alignment,
|
||||
self.vertical_alignment,
|
||||
|renderer, limits| self.content.layout(renderer, limits),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.content.widget.on_event(
|
||||
event,
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.content.widget.mouse_interaction(
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
renderer_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
let style = theme.appearance(self.style);
|
||||
|
||||
draw_background(renderer, &style, layout.bounds());
|
||||
|
||||
self.content.draw(
|
||||
renderer,
|
||||
theme,
|
||||
&renderer::Style {
|
||||
text_color: style
|
||||
.text_color
|
||||
.unwrap_or(renderer_style.text_color),
|
||||
},
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.content
|
||||
.overlay(layout.children().next().unwrap(), renderer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the background of a [`Container`] given its [`Style`] and its `bounds`.
|
||||
pub fn draw_background<Renderer>(
|
||||
renderer: &mut Renderer,
|
||||
|
|
@ -281,17 +316,3 @@ pub fn draw_background<Renderer>(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
column: Container<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(column)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
283
native/src/widget/helpers.rs
Normal file
283
native/src/widget/helpers.rs
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
//! Helper functions to create pure widgets.
|
||||
use crate::widget;
|
||||
use crate::{Element, Length};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
/// Creates a [`Column`] with the given children.
|
||||
///
|
||||
/// [`Column`]: widget::Column
|
||||
#[macro_export]
|
||||
macro_rules! column {
|
||||
() => (
|
||||
$crate::widget::Column::new()
|
||||
);
|
||||
($($x:expr),+ $(,)?) => (
|
||||
$crate::widget::Column::with_children(vec![$($crate::Element::from($x)),+])
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a [Row`] with the given children.
|
||||
///
|
||||
/// [`Row`]: widget::Row
|
||||
#[macro_export]
|
||||
macro_rules! row {
|
||||
() => (
|
||||
$crate::widget::Row::new()
|
||||
);
|
||||
($($x:expr),+ $(,)?) => (
|
||||
$crate::widget::Row::with_children(vec![$($crate::Element::from($x)),+])
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a new [`Container`] with the provided content.
|
||||
///
|
||||
/// [`Container`]: widget::Container
|
||||
pub fn container<'a, Message, Renderer>(
|
||||
content: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> widget::Container<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::container::StyleSheet,
|
||||
{
|
||||
widget::Container::new(content)
|
||||
}
|
||||
|
||||
/// Creates a new [`Column`] with the given children.
|
||||
///
|
||||
/// [`Column`]: widget::Column
|
||||
pub fn column<'a, Message, Renderer>(
|
||||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
) -> widget::Row<'a, Message, Renderer> {
|
||||
widget::Row::with_children(children)
|
||||
}
|
||||
|
||||
/// Creates a new [`Row`] with the given children.
|
||||
///
|
||||
/// [`Row`]: widget::Row
|
||||
pub fn row<'a, Message, Renderer>(
|
||||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
) -> widget::Row<'a, Message, Renderer> {
|
||||
widget::Row::with_children(children)
|
||||
}
|
||||
|
||||
/// Creates a new [`Scrollable`] with the provided content.
|
||||
///
|
||||
/// [`Scrollable`]: widget::Scrollable
|
||||
pub fn scrollable<'a, Message, Renderer>(
|
||||
content: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> widget::Scrollable<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::scrollable::StyleSheet,
|
||||
{
|
||||
widget::Scrollable::new(content)
|
||||
}
|
||||
|
||||
/// Creates a new [`Button`] with the provided content.
|
||||
///
|
||||
/// [`Button`]: widget::Button
|
||||
pub fn button<'a, Message, Renderer>(
|
||||
content: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> widget::Button<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::button::StyleSheet,
|
||||
{
|
||||
widget::Button::new(content)
|
||||
}
|
||||
|
||||
/// Creates a new [`Tooltip`] with the provided content, tooltip text, and [`tooltip::Position`].
|
||||
///
|
||||
/// [`Tooltip`]: widget::Tooltip
|
||||
/// [`tooltip::Position`]: widget::tooltip::Position
|
||||
pub fn tooltip<'a, Message, Renderer>(
|
||||
content: impl Into<Element<'a, Message, Renderer>>,
|
||||
tooltip: impl ToString,
|
||||
position: widget::tooltip::Position,
|
||||
) -> widget::Tooltip<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::container::StyleSheet + widget::text::StyleSheet,
|
||||
{
|
||||
widget::Tooltip::new(content, tooltip, position)
|
||||
}
|
||||
|
||||
/// Creates a new [`Text`] widget with the provided content.
|
||||
///
|
||||
/// [`Text`]: widget::Text
|
||||
pub fn text<Renderer>(text: impl Into<String>) -> widget::Text<Renderer>
|
||||
where
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::text::StyleSheet,
|
||||
{
|
||||
widget::Text::new(text)
|
||||
}
|
||||
|
||||
/// Creates a new [`Checkbox`].
|
||||
///
|
||||
/// [`Checkbox`]: widget::Checkbox
|
||||
pub fn checkbox<'a, Message, Renderer>(
|
||||
label: impl Into<String>,
|
||||
is_checked: bool,
|
||||
f: impl Fn(bool) -> Message + 'a,
|
||||
) -> widget::Checkbox<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::checkbox::StyleSheet + widget::text::StyleSheet,
|
||||
{
|
||||
widget::Checkbox::new(is_checked, label, f)
|
||||
}
|
||||
|
||||
/// Creates a new [`Radio`].
|
||||
///
|
||||
/// [`Radio`]: widget::Radio
|
||||
pub fn radio<Message, Renderer, V>(
|
||||
label: impl Into<String>,
|
||||
value: V,
|
||||
selected: Option<V>,
|
||||
on_click: impl FnOnce(V) -> Message,
|
||||
) -> widget::Radio<Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::radio::StyleSheet,
|
||||
V: Copy + Eq,
|
||||
{
|
||||
widget::Radio::new(value, label, selected, on_click)
|
||||
}
|
||||
|
||||
/// Creates a new [`Toggler`].
|
||||
///
|
||||
/// [`Toggler`]: widget::Toggler
|
||||
pub fn toggler<'a, Message, Renderer>(
|
||||
label: impl Into<Option<String>>,
|
||||
is_checked: bool,
|
||||
f: impl Fn(bool) -> Message + 'a,
|
||||
) -> widget::Toggler<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::toggler::StyleSheet,
|
||||
{
|
||||
widget::Toggler::new(is_checked, label, f)
|
||||
}
|
||||
|
||||
/// Creates a new [`TextInput`].
|
||||
///
|
||||
/// [`TextInput`]: widget::TextInput
|
||||
pub fn text_input<'a, Message, Renderer>(
|
||||
placeholder: &str,
|
||||
value: &str,
|
||||
on_change: impl Fn(String) -> Message + 'a,
|
||||
) -> widget::TextInput<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::text_input::StyleSheet,
|
||||
{
|
||||
widget::TextInput::new(placeholder, value, on_change)
|
||||
}
|
||||
|
||||
/// Creates a new [`Slider`].
|
||||
///
|
||||
/// [`Slider`]: widget::Slider
|
||||
pub fn slider<'a, T, Message, Renderer>(
|
||||
range: std::ops::RangeInclusive<T>,
|
||||
value: T,
|
||||
on_change: impl Fn(T) -> Message + 'a,
|
||||
) -> widget::Slider<'a, T, Message, Renderer>
|
||||
where
|
||||
T: Copy + From<u8> + std::cmp::PartialOrd,
|
||||
Message: Clone,
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::slider::StyleSheet,
|
||||
{
|
||||
widget::Slider::new(range, value, on_change)
|
||||
}
|
||||
|
||||
/// Creates a new [`PickList`].
|
||||
///
|
||||
/// [`PickList`]: widget::PickList
|
||||
pub fn pick_list<'a, Message, Renderer, T>(
|
||||
options: impl Into<Cow<'a, [T]>>,
|
||||
selected: Option<T>,
|
||||
on_selected: impl Fn(T) -> Message + 'a,
|
||||
) -> widget::PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: ToString + Eq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::pick_list::StyleSheet,
|
||||
{
|
||||
widget::PickList::new(options, selected, on_selected)
|
||||
}
|
||||
|
||||
/// Creates a new [`Image`].
|
||||
///
|
||||
/// [`Image`]: widget::Image
|
||||
pub fn image<Handle>(handle: impl Into<Handle>) -> widget::Image<Handle> {
|
||||
widget::Image::new(handle.into())
|
||||
}
|
||||
|
||||
/// Creates a new horizontal [`Space`] with the given [`Length`].
|
||||
///
|
||||
/// [`Space`]: widget::Space
|
||||
pub fn horizontal_space(width: Length) -> widget::Space {
|
||||
widget::Space::with_width(width)
|
||||
}
|
||||
|
||||
/// Creates a new vertical [`Space`] with the given [`Length`].
|
||||
///
|
||||
/// [`Space`]: widget::Space
|
||||
pub fn vertical_space(height: Length) -> widget::Space {
|
||||
widget::Space::with_height(height)
|
||||
}
|
||||
|
||||
/// Creates a horizontal [`Rule`] with the given height.
|
||||
///
|
||||
/// [`Rule`]: widget::Rule
|
||||
pub fn horizontal_rule<Renderer>(height: u16) -> widget::Rule<Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::rule::StyleSheet,
|
||||
{
|
||||
widget::Rule::horizontal(height)
|
||||
}
|
||||
|
||||
/// Creates a vertical [`Rule`] with the given width.
|
||||
///
|
||||
/// [`Rule`]: widget::Rule
|
||||
pub fn vertical_rule<Renderer>(width: u16) -> widget::Rule<Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::rule::StyleSheet,
|
||||
{
|
||||
widget::Rule::vertical(width)
|
||||
}
|
||||
|
||||
/// Creates a new [`ProgressBar`].
|
||||
///
|
||||
/// It expects:
|
||||
/// * an inclusive range of possible values, and
|
||||
/// * the current value of the [`ProgressBar`].
|
||||
///
|
||||
/// [`ProgressBar`]: widget::ProgressBar
|
||||
pub fn progress_bar<Renderer>(
|
||||
range: RangeInclusive<f32>,
|
||||
value: f32,
|
||||
) -> widget::ProgressBar<Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::progress_bar::StyleSheet,
|
||||
{
|
||||
widget::ProgressBar::new(range, value)
|
||||
}
|
||||
|
||||
/// Creates a new [`Svg`] widget from the given [`Handle`].
|
||||
///
|
||||
/// [`Svg`]: widget::Svg
|
||||
/// [`Handle`]: widget::svg::Handle
|
||||
pub fn svg(handle: impl Into<widget::svg::Handle>) -> widget::Svg {
|
||||
widget::Svg::new(handle)
|
||||
}
|
||||
|
|
@ -5,12 +5,18 @@ pub use viewer::Viewer;
|
|||
use crate::image;
|
||||
use crate::layout;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
ContentFit, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Creates a new [`Viewer`] with the given image `Handle`.
|
||||
pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
|
||||
Viewer::new(handle)
|
||||
}
|
||||
|
||||
/// A frame that displays an image while keeping aspect ratio.
|
||||
///
|
||||
/// # Example
|
||||
|
|
@ -135,6 +141,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
_theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::image;
|
|||
use crate::layout;
|
||||
use crate::mouse;
|
||||
use crate::renderer;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector,
|
||||
Widget,
|
||||
|
|
@ -13,8 +14,7 @@ use std::hash::Hash;
|
|||
|
||||
/// A frame that displays an image with the ability to zoom in/out and pan.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Viewer<'a, Handle> {
|
||||
state: &'a mut State,
|
||||
pub struct Viewer<Handle> {
|
||||
padding: u16,
|
||||
width: Length,
|
||||
height: Length,
|
||||
|
|
@ -24,11 +24,10 @@ pub struct Viewer<'a, Handle> {
|
|||
handle: Handle,
|
||||
}
|
||||
|
||||
impl<'a, Handle> Viewer<'a, Handle> {
|
||||
impl<Handle> Viewer<Handle> {
|
||||
/// Creates a new [`Viewer`] with the given [`State`].
|
||||
pub fn new(state: &'a mut State, handle: Handle) -> Self {
|
||||
pub fn new(handle: Handle) -> Self {
|
||||
Viewer {
|
||||
state,
|
||||
padding: 0,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
|
|
@ -81,43 +80,21 @@ impl<'a, Handle> Viewer<'a, Handle> {
|
|||
self.scale_step = scale_step;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the bounds of the underlying image, given the bounds of
|
||||
/// the [`Viewer`]. Scaling will be applied and original aspect ratio
|
||||
/// will be respected.
|
||||
fn image_size<Renderer>(&self, renderer: &Renderer, bounds: Size) -> Size
|
||||
where
|
||||
Renderer: image::Renderer<Handle = Handle>,
|
||||
{
|
||||
let (width, height) = renderer.dimensions(&self.handle);
|
||||
|
||||
let (width, height) = {
|
||||
let dimensions = (width as f32, height as f32);
|
||||
|
||||
let width_ratio = bounds.width / dimensions.0;
|
||||
let height_ratio = bounds.height / dimensions.1;
|
||||
|
||||
let ratio = width_ratio.min(height_ratio);
|
||||
|
||||
let scale = self.state.scale;
|
||||
|
||||
if ratio < 1.0 {
|
||||
(dimensions.0 * ratio * scale, dimensions.1 * ratio * scale)
|
||||
} else {
|
||||
(dimensions.0 * scale, dimensions.1 * scale)
|
||||
}
|
||||
};
|
||||
|
||||
Size::new(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer, Handle> Widget<Message, Renderer>
|
||||
for Viewer<'a, Handle>
|
||||
impl<Message, Renderer, Handle> Widget<Message, Renderer> for Viewer<Handle>
|
||||
where
|
||||
Renderer: image::Renderer<Handle = Handle>,
|
||||
Handle: Clone + Hash,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
|
@ -164,6 +141,7 @@ where
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -181,39 +159,43 @@ where
|
|||
match delta {
|
||||
mouse::ScrollDelta::Lines { y, .. }
|
||||
| mouse::ScrollDelta::Pixels { y, .. } => {
|
||||
let previous_scale = self.state.scale;
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
let previous_scale = state.scale;
|
||||
|
||||
if y < 0.0 && previous_scale > self.min_scale
|
||||
|| y > 0.0 && previous_scale < self.max_scale
|
||||
{
|
||||
self.state.scale = (if y > 0.0 {
|
||||
self.state.scale * (1.0 + self.scale_step)
|
||||
state.scale = (if y > 0.0 {
|
||||
state.scale * (1.0 + self.scale_step)
|
||||
} else {
|
||||
self.state.scale / (1.0 + self.scale_step)
|
||||
state.scale / (1.0 + self.scale_step)
|
||||
})
|
||||
.max(self.min_scale)
|
||||
.min(self.max_scale);
|
||||
|
||||
let image_size =
|
||||
self.image_size(renderer, bounds.size());
|
||||
let image_size = image_size(
|
||||
renderer,
|
||||
&self.handle,
|
||||
state,
|
||||
bounds.size(),
|
||||
);
|
||||
|
||||
let factor =
|
||||
self.state.scale / previous_scale - 1.0;
|
||||
let factor = state.scale / previous_scale - 1.0;
|
||||
|
||||
let cursor_to_center =
|
||||
cursor_position - bounds.center();
|
||||
|
||||
let adjustment = cursor_to_center * factor
|
||||
+ self.state.current_offset * factor;
|
||||
+ state.current_offset * factor;
|
||||
|
||||
self.state.current_offset = Vector::new(
|
||||
state.current_offset = Vector::new(
|
||||
if image_size.width > bounds.width {
|
||||
self.state.current_offset.x + adjustment.x
|
||||
state.current_offset.x + adjustment.x
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
if image_size.height > bounds.height {
|
||||
self.state.current_offset.y + adjustment.y
|
||||
state.current_offset.y + adjustment.y
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
|
|
@ -227,21 +209,34 @@ where
|
|||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||
if is_mouse_over =>
|
||||
{
|
||||
self.state.cursor_grabbed_at = Some(cursor_position);
|
||||
self.state.starting_offset = self.state.current_offset;
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
state.cursor_grabbed_at = Some(cursor_position);
|
||||
state.starting_offset = state.current_offset;
|
||||
|
||||
event::Status::Captured
|
||||
}
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||
if self.state.cursor_grabbed_at.is_some() =>
|
||||
{
|
||||
self.state.cursor_grabbed_at = None;
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
event::Status::Captured
|
||||
if state.cursor_grabbed_at.is_some() {
|
||||
state.cursor_grabbed_at = None;
|
||||
|
||||
event::Status::Captured
|
||||
} else {
|
||||
event::Status::Ignored
|
||||
}
|
||||
}
|
||||
Event::Mouse(mouse::Event::CursorMoved { position }) => {
|
||||
if let Some(origin) = self.state.cursor_grabbed_at {
|
||||
let image_size = self.image_size(renderer, bounds.size());
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
if let Some(origin) = state.cursor_grabbed_at {
|
||||
let image_size = image_size(
|
||||
renderer,
|
||||
&self.handle,
|
||||
state,
|
||||
bounds.size(),
|
||||
);
|
||||
|
||||
let hidden_width = (image_size.width - bounds.width / 2.0)
|
||||
.max(0.0)
|
||||
|
|
@ -255,7 +250,7 @@ where
|
|||
let delta = position - origin;
|
||||
|
||||
let x = if bounds.width < image_size.width {
|
||||
(self.state.starting_offset.x - delta.x)
|
||||
(state.starting_offset.x - delta.x)
|
||||
.min(hidden_width)
|
||||
.max(-hidden_width)
|
||||
} else {
|
||||
|
|
@ -263,14 +258,14 @@ where
|
|||
};
|
||||
|
||||
let y = if bounds.height < image_size.height {
|
||||
(self.state.starting_offset.y - delta.y)
|
||||
(state.starting_offset.y - delta.y)
|
||||
.min(hidden_height)
|
||||
.max(-hidden_height)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
self.state.current_offset = Vector::new(x, y);
|
||||
state.current_offset = Vector::new(x, y);
|
||||
|
||||
event::Status::Captured
|
||||
} else {
|
||||
|
|
@ -283,15 +278,17 @@ where
|
|||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
let state = tree.state.downcast_ref::<State>();
|
||||
let bounds = layout.bounds();
|
||||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
|
||||
if self.state.is_cursor_grabbed() {
|
||||
if state.is_cursor_grabbed() {
|
||||
mouse::Interaction::Grabbing
|
||||
} else if is_mouse_over {
|
||||
mouse::Interaction::Grab
|
||||
|
|
@ -302,6 +299,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
_theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
|
|
@ -309,9 +307,11 @@ where
|
|||
_cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
let state = tree.state.downcast_ref::<State>();
|
||||
let bounds = layout.bounds();
|
||||
|
||||
let image_size = self.image_size(renderer, bounds.size());
|
||||
let image_size =
|
||||
image_size(renderer, &self.handle, state, bounds.size());
|
||||
|
||||
let translation = {
|
||||
let image_top_left = Vector::new(
|
||||
|
|
@ -319,7 +319,7 @@ where
|
|||
bounds.height / 2.0 - image_size.height / 2.0,
|
||||
);
|
||||
|
||||
image_top_left - self.state.offset(bounds, image_size)
|
||||
image_top_left - state.offset(bounds, image_size)
|
||||
};
|
||||
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
|
|
@ -385,14 +385,47 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer, Handle> From<Viewer<'a, Handle>>
|
||||
impl<'a, Message, Renderer, Handle> From<Viewer<Handle>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: 'a + image::Renderer<Handle = Handle>,
|
||||
Message: 'a,
|
||||
Handle: Clone + Hash + 'a,
|
||||
{
|
||||
fn from(viewer: Viewer<'a, Handle>) -> Element<'a, Message, Renderer> {
|
||||
fn from(viewer: Viewer<Handle>) -> Element<'a, Message, Renderer> {
|
||||
Element::new(viewer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bounds of the underlying image, given the bounds of
|
||||
/// the [`Viewer`]. Scaling will be applied and original aspect ratio
|
||||
/// will be respected.
|
||||
pub fn image_size<Renderer>(
|
||||
renderer: &Renderer,
|
||||
handle: &<Renderer as image::Renderer>::Handle,
|
||||
state: &State,
|
||||
bounds: Size,
|
||||
) -> Size
|
||||
where
|
||||
Renderer: image::Renderer,
|
||||
{
|
||||
let (width, height) = renderer.dimensions(handle);
|
||||
|
||||
let (width, height) = {
|
||||
let dimensions = (width as f32, height as f32);
|
||||
|
||||
let width_ratio = bounds.width / dimensions.0;
|
||||
let height_ratio = bounds.height / dimensions.1;
|
||||
|
||||
let ratio = width_ratio.min(height_ratio);
|
||||
let scale = state.scale;
|
||||
|
||||
if ratio < 1.0 {
|
||||
(dimensions.0 * ratio * scale, dimensions.1 * ratio * scale)
|
||||
} else {
|
||||
(dimensions.0 * scale, dimensions.1 * scale)
|
||||
}
|
||||
};
|
||||
|
||||
Size::new(width, height)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ pub use split::Split;
|
|||
pub use state::State;
|
||||
pub use title_bar::TitleBar;
|
||||
|
||||
pub use iced_style::pane_grid::{Line, StyleSheet};
|
||||
|
||||
use crate::event::{self, Event};
|
||||
use crate::layout;
|
||||
use crate::mouse;
|
||||
|
|
@ -37,13 +39,12 @@ use crate::overlay;
|
|||
use crate::renderer;
|
||||
use crate::touch;
|
||||
use crate::widget::container;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Color, Element, Layout, Length, Point, Rectangle, Shell, Size,
|
||||
Vector, Widget,
|
||||
};
|
||||
|
||||
pub use iced_style::pane_grid::{Line, StyleSheet};
|
||||
|
||||
/// A collection of panes distributed using either vertical or horizontal splits
|
||||
/// to completely fill the space available.
|
||||
///
|
||||
|
|
@ -66,7 +67,7 @@ pub use iced_style::pane_grid::{Line, StyleSheet};
|
|||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// # use iced_native::widget::{pane_grid, Text};
|
||||
/// # use iced_native::widget::{pane_grid, text};
|
||||
/// #
|
||||
/// # type PaneGrid<'a, Message> =
|
||||
/// # iced_native::widget::PaneGrid<'a, Message, iced_native::renderer::Null>;
|
||||
|
|
@ -84,10 +85,10 @@ pub use iced_style::pane_grid::{Line, StyleSheet};
|
|||
/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane);
|
||||
///
|
||||
/// let pane_grid =
|
||||
/// PaneGrid::new(&mut state, |pane, state| {
|
||||
/// PaneGrid::new(&state, |pane, state| {
|
||||
/// pane_grid::Content::new(match state {
|
||||
/// PaneState::SomePane => Text::new("This is some pane"),
|
||||
/// PaneState::AnotherKindOfPane => Text::new("This is another kind of pane"),
|
||||
/// PaneState::SomePane => text("This is some pane"),
|
||||
/// PaneState::AnotherKindOfPane => text("This is another kind of pane"),
|
||||
/// })
|
||||
/// })
|
||||
/// .on_drag(Message::PaneDragged)
|
||||
|
|
@ -99,8 +100,7 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
state: &'a mut state::Internal,
|
||||
action: &'a mut state::Action,
|
||||
state: &'a state::Internal,
|
||||
elements: Vec<(Pane, Content<'a, Message, Renderer>)>,
|
||||
width: Length,
|
||||
height: Length,
|
||||
|
|
@ -121,21 +121,20 @@ where
|
|||
/// The view function will be called to display each [`Pane`] present in the
|
||||
/// [`State`].
|
||||
pub fn new<T>(
|
||||
state: &'a mut State<T>,
|
||||
view: impl Fn(Pane, &'a mut T) -> Content<'a, Message, Renderer>,
|
||||
state: &'a State<T>,
|
||||
view: impl Fn(Pane, &'a T) -> Content<'a, Message, Renderer>,
|
||||
) -> Self {
|
||||
let elements = {
|
||||
state
|
||||
.panes
|
||||
.iter_mut()
|
||||
.iter()
|
||||
.map(|(pane, pane_state)| (*pane, view(*pane, pane_state)))
|
||||
.collect()
|
||||
};
|
||||
|
||||
Self {
|
||||
state: &mut state.internal,
|
||||
action: &mut state.action,
|
||||
elements,
|
||||
state: &state.internal,
|
||||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
spacing: 0,
|
||||
|
|
@ -211,6 +210,220 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for PaneGrid<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<state::Action>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(state::Action::Idle)
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
self.elements
|
||||
.iter()
|
||||
.map(|(_, content)| content.state())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children_custom(
|
||||
&self.elements,
|
||||
|state, (_, content)| content.diff(state),
|
||||
|(_, content)| content.state(),
|
||||
)
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.state,
|
||||
self.width,
|
||||
self.height,
|
||||
self.spacing,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
|element, renderer, limits| element.layout(renderer, limits),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
let action = tree.state.downcast_mut::<state::Action>();
|
||||
|
||||
let event_status = update(
|
||||
action,
|
||||
self.state,
|
||||
&event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
self.spacing,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
&self.on_click,
|
||||
&self.on_drag,
|
||||
&self.on_resize,
|
||||
);
|
||||
|
||||
let picked_pane = action.picked_pane().map(|(pane, _)| pane);
|
||||
|
||||
self.elements
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(((pane, content), tree), layout)| {
|
||||
let is_picked = picked_pane == Some(*pane);
|
||||
|
||||
content.on_event(
|
||||
tree,
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
is_picked,
|
||||
)
|
||||
})
|
||||
.fold(event_status, event::Status::merge)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(
|
||||
tree.state.downcast_ref(),
|
||||
self.state,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.spacing,
|
||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
self.elements
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(((_pane, content), tree), layout)| {
|
||||
content.mouse_interaction(
|
||||
tree,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
})
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
tree.state.downcast_ref(),
|
||||
self.state,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
viewport,
|
||||
self.spacing,
|
||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||
self.style,
|
||||
self.elements
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.map(|((pane, content), tree)| (*pane, (content, tree))),
|
||||
|(content, tree),
|
||||
renderer,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
rectangle| {
|
||||
content.draw(
|
||||
tree,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
rectangle,
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.elements
|
||||
.iter()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.filter_map(|(((_, pane), tree), layout)| {
|
||||
pane.overlay(tree, layout, renderer)
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<PaneGrid<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
pane_grid: PaneGrid<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(pane_grid)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the [`Layout`] of a [`PaneGrid`].
|
||||
pub fn layout<Renderer, T>(
|
||||
renderer: &Renderer,
|
||||
|
|
@ -656,175 +869,6 @@ pub struct ResizeEvent {
|
|||
pub ratio: f32,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for PaneGrid<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.state,
|
||||
self.width,
|
||||
self.height,
|
||||
self.spacing,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
|element, renderer, limits| element.layout(renderer, limits),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
let event_status = update(
|
||||
self.action,
|
||||
self.state,
|
||||
&event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
self.spacing,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
&self.on_click,
|
||||
&self.on_drag,
|
||||
&self.on_resize,
|
||||
);
|
||||
|
||||
let picked_pane = self.action.picked_pane().map(|(pane, _)| pane);
|
||||
|
||||
self.elements
|
||||
.iter_mut()
|
||||
.zip(layout.children())
|
||||
.map(|((pane, content), layout)| {
|
||||
let is_picked = picked_pane == Some(*pane);
|
||||
|
||||
content.on_event(
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
is_picked,
|
||||
)
|
||||
})
|
||||
.fold(event_status, event::Status::merge)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(
|
||||
self.action,
|
||||
self.state,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.spacing,
|
||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
self.elements
|
||||
.iter()
|
||||
.zip(layout.children())
|
||||
.map(|((_pane, content), layout)| {
|
||||
content.mouse_interaction(
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
})
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
self.action,
|
||||
self.state,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
viewport,
|
||||
self.spacing,
|
||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||
self.style,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
|pane, renderer, style, layout, cursor_position, rectangle| {
|
||||
pane.draw(
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
rectangle,
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.elements
|
||||
.iter_mut()
|
||||
.zip(layout.children())
|
||||
.filter_map(|((_, pane), layout)| pane.overlay(layout, renderer))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<PaneGrid<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
pane_grid: PaneGrid<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(pane_grid)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crate::overlay;
|
|||
use crate::renderer;
|
||||
use crate::widget::container;
|
||||
use crate::widget::pane_grid::{Draggable, TitleBar};
|
||||
use crate::widget::Tree;
|
||||
use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
|
||||
|
||||
/// The content of a [`Pane`].
|
||||
|
|
@ -59,11 +60,37 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: container::StyleSheet,
|
||||
{
|
||||
pub(super) fn state(&self) -> Tree {
|
||||
let children = if let Some(title_bar) = self.title_bar.as_ref() {
|
||||
vec![Tree::new(&self.body), title_bar.state()]
|
||||
} else {
|
||||
vec![Tree::new(&self.body), Tree::empty()]
|
||||
};
|
||||
|
||||
Tree {
|
||||
children,
|
||||
..Tree::empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn diff(&self, tree: &mut Tree) {
|
||||
if tree.children.len() == 2 {
|
||||
if let Some(title_bar) = self.title_bar.as_ref() {
|
||||
title_bar.diff(&mut tree.children[1]);
|
||||
}
|
||||
|
||||
tree.children[0].diff(&self.body);
|
||||
} else {
|
||||
*tree = self.state();
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`].
|
||||
///
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
/// [`Renderer`]: iced_native::Renderer
|
||||
pub fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
@ -89,6 +116,7 @@ where
|
|||
let show_controls = bounds.contains(cursor_position);
|
||||
|
||||
title_bar.draw(
|
||||
&tree.children[1],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
|
|
@ -98,7 +126,8 @@ where
|
|||
show_controls,
|
||||
);
|
||||
|
||||
self.body.draw(
|
||||
self.body.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
|
|
@ -107,7 +136,8 @@ where
|
|||
viewport,
|
||||
);
|
||||
} else {
|
||||
self.body.draw(
|
||||
self.body.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
|
|
@ -131,7 +161,7 @@ where
|
|||
|
||||
let title_bar_size = title_bar_layout.size();
|
||||
|
||||
let mut body_layout = self.body.layout(
|
||||
let mut body_layout = self.body.as_widget().layout(
|
||||
renderer,
|
||||
&layout::Limits::new(
|
||||
Size::ZERO,
|
||||
|
|
@ -149,12 +179,13 @@ where
|
|||
vec![title_bar_layout, body_layout],
|
||||
)
|
||||
} else {
|
||||
self.body.layout(renderer, limits)
|
||||
self.body.as_widget().layout(renderer, limits)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -169,6 +200,7 @@ where
|
|||
let mut children = layout.children();
|
||||
|
||||
event_status = title_bar.on_event(
|
||||
&mut tree.children[1],
|
||||
event.clone(),
|
||||
children.next().unwrap(),
|
||||
cursor_position,
|
||||
|
|
@ -185,7 +217,8 @@ where
|
|||
let body_status = if is_picked {
|
||||
event::Status::Ignored
|
||||
} else {
|
||||
self.body.on_event(
|
||||
self.body.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
body_layout,
|
||||
cursor_position,
|
||||
|
|
@ -200,6 +233,7 @@ where
|
|||
|
||||
pub(crate) fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
|
|
@ -218,6 +252,7 @@ where
|
|||
}
|
||||
|
||||
let mouse_interaction = title_bar.mouse_interaction(
|
||||
&tree.children[1],
|
||||
title_bar_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
|
|
@ -230,25 +265,46 @@ where
|
|||
};
|
||||
|
||||
self.body
|
||||
.mouse_interaction(body_layout, cursor_position, viewport, renderer)
|
||||
.as_widget()
|
||||
.mouse_interaction(
|
||||
&tree.children[0],
|
||||
body_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
.max(title_bar_interaction)
|
||||
}
|
||||
|
||||
pub(crate) fn overlay(
|
||||
&mut self,
|
||||
pub(crate) fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
if let Some(title_bar) = self.title_bar.as_mut() {
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
if let Some(title_bar) = self.title_bar.as_ref() {
|
||||
let mut children = layout.children();
|
||||
let title_bar_layout = children.next()?;
|
||||
|
||||
match title_bar.overlay(title_bar_layout, renderer) {
|
||||
let mut states = tree.children.iter_mut();
|
||||
let body_state = states.next().unwrap();
|
||||
let title_bar_state = states.next().unwrap();
|
||||
|
||||
match title_bar.overlay(title_bar_state, title_bar_layout, renderer)
|
||||
{
|
||||
Some(overlay) => Some(overlay),
|
||||
None => self.body.overlay(children.next()?, renderer),
|
||||
None => self.body.as_widget().overlay(
|
||||
body_state,
|
||||
children.next()?,
|
||||
renderer,
|
||||
),
|
||||
}
|
||||
} else {
|
||||
self.body.overlay(layout, renderer)
|
||||
self.body.as_widget().overlay(
|
||||
&mut tree.children[0],
|
||||
layout,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ pub struct State<T> {
|
|||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub internal: Internal,
|
||||
|
||||
pub(super) action: Action,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
|
|
@ -54,11 +52,7 @@ impl<T> State<T> {
|
|||
let internal =
|
||||
Internal::from_configuration(&mut panes, config.into(), 0);
|
||||
|
||||
State {
|
||||
panes,
|
||||
internal,
|
||||
action: Action::Idle,
|
||||
}
|
||||
State { panes, internal }
|
||||
}
|
||||
|
||||
/// Returns the total amount of panes in the [`State`].
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::mouse;
|
|||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::widget::container;
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size,
|
||||
};
|
||||
|
|
@ -86,11 +87,37 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: container::StyleSheet,
|
||||
{
|
||||
pub(super) fn state(&self) -> Tree {
|
||||
let children = if let Some(controls) = self.controls.as_ref() {
|
||||
vec![Tree::new(&self.content), Tree::new(controls)]
|
||||
} else {
|
||||
vec![Tree::new(&self.content), Tree::empty()]
|
||||
};
|
||||
|
||||
Tree {
|
||||
children,
|
||||
..Tree::empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn diff(&self, tree: &mut Tree) {
|
||||
if tree.children.len() == 2 {
|
||||
if let Some(controls) = self.controls.as_ref() {
|
||||
tree.children[1].diff(controls);
|
||||
}
|
||||
|
||||
tree.children[0].diff(&self.content);
|
||||
} else {
|
||||
*tree = self.state();
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`].
|
||||
///
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
/// [`Renderer`]: iced_native::Renderer
|
||||
pub fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
inherited_style: &renderer::Style,
|
||||
|
|
@ -118,14 +145,15 @@ where
|
|||
|
||||
if let Some(controls) = &self.controls {
|
||||
let controls_layout = children.next().unwrap();
|
||||
if title_layout.bounds().width + controls_layout.bounds().width
|
||||
> padded.bounds().width
|
||||
{
|
||||
show_title = false;
|
||||
}
|
||||
|
||||
if show_controls || self.always_show_controls {
|
||||
if title_layout.bounds().width + controls_layout.bounds().width
|
||||
> padded.bounds().width
|
||||
{
|
||||
show_title = false;
|
||||
}
|
||||
controls.draw(
|
||||
controls.as_widget().draw(
|
||||
&tree.children[1],
|
||||
renderer,
|
||||
theme,
|
||||
&inherited_style,
|
||||
|
|
@ -137,7 +165,8 @@ where
|
|||
}
|
||||
|
||||
if show_title {
|
||||
self.content.draw(
|
||||
self.content.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
&inherited_style,
|
||||
|
|
@ -186,11 +215,14 @@ where
|
|||
|
||||
let title_layout = self
|
||||
.content
|
||||
.as_widget()
|
||||
.layout(renderer, &layout::Limits::new(Size::ZERO, max_size));
|
||||
|
||||
let title_size = title_layout.size();
|
||||
|
||||
let mut node = if let Some(controls) = &self.controls {
|
||||
let mut controls_layout = controls
|
||||
.as_widget()
|
||||
.layout(renderer, &layout::Limits::new(Size::ZERO, max_size));
|
||||
|
||||
let controls_size = controls_layout.size();
|
||||
|
|
@ -221,6 +253,7 @@ where
|
|||
|
||||
pub(crate) fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -243,7 +276,8 @@ where
|
|||
show_title = false;
|
||||
}
|
||||
|
||||
controls.on_event(
|
||||
controls.as_widget_mut().on_event(
|
||||
&mut tree.children[1],
|
||||
event.clone(),
|
||||
controls_layout,
|
||||
cursor_position,
|
||||
|
|
@ -256,7 +290,8 @@ where
|
|||
};
|
||||
|
||||
let title_status = if show_title {
|
||||
self.content.on_event(
|
||||
self.content.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
title_layout,
|
||||
cursor_position,
|
||||
|
|
@ -273,6 +308,7 @@ where
|
|||
|
||||
pub(crate) fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
|
|
@ -284,7 +320,8 @@ where
|
|||
let mut children = padded.children();
|
||||
let title_layout = children.next().unwrap();
|
||||
|
||||
let title_interaction = self.content.mouse_interaction(
|
||||
let title_interaction = self.content.as_widget().mouse_interaction(
|
||||
&tree.children[0],
|
||||
title_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
|
|
@ -293,7 +330,8 @@ where
|
|||
|
||||
if let Some(controls) = &self.controls {
|
||||
let controls_layout = children.next().unwrap();
|
||||
let controls_interaction = controls.mouse_interaction(
|
||||
let controls_interaction = controls.as_widget().mouse_interaction(
|
||||
&tree.children[1],
|
||||
controls_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
|
|
@ -312,11 +350,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn overlay(
|
||||
&mut self,
|
||||
pub(crate) fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
let mut children = layout.children();
|
||||
let padded = children.next()?;
|
||||
|
||||
|
|
@ -327,12 +366,23 @@ where
|
|||
content, controls, ..
|
||||
} = self;
|
||||
|
||||
content.overlay(title_layout, renderer).or_else(move || {
|
||||
controls.as_mut().and_then(|controls| {
|
||||
let controls_layout = children.next()?;
|
||||
let mut states = tree.children.iter_mut();
|
||||
let title_state = states.next().unwrap();
|
||||
let controls_state = states.next().unwrap();
|
||||
|
||||
controls.overlay(controls_layout, renderer)
|
||||
content
|
||||
.as_widget()
|
||||
.overlay(title_state, title_layout, renderer)
|
||||
.or_else(move || {
|
||||
controls.as_ref().and_then(|controls| {
|
||||
let controls_layout = children.next()?;
|
||||
|
||||
controls.as_widget().overlay(
|
||||
controls_state,
|
||||
controls_layout,
|
||||
renderer,
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ use crate::overlay::menu::{self, Menu};
|
|||
use crate::renderer;
|
||||
use crate::text::{self, Text};
|
||||
use crate::touch;
|
||||
use crate::widget::container;
|
||||
use crate::widget::scrollable;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Size,
|
||||
Widget,
|
||||
|
|
@ -27,8 +26,7 @@ where
|
|||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
state: &'a mut State<T>,
|
||||
on_selected: Box<dyn Fn(T) -> Message>,
|
||||
on_selected: Box<dyn Fn(T) -> Message + 'a>,
|
||||
options: Cow<'a, [T]>,
|
||||
placeholder: Option<String>,
|
||||
selected: Option<T>,
|
||||
|
|
@ -39,35 +37,6 @@ where
|
|||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
}
|
||||
|
||||
/// The local state of a [`PickList`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State<T> {
|
||||
menu: menu::State,
|
||||
keyboard_modifiers: keyboard::Modifiers,
|
||||
is_open: bool,
|
||||
hovered_option: Option<usize>,
|
||||
last_selection: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
/// Creates a new [`State`] for a [`PickList`].
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
menu: menu::State::default(),
|
||||
keyboard_modifiers: keyboard::Modifiers::default(),
|
||||
is_open: bool::default(),
|
||||
hovered_option: Option::default(),
|
||||
last_selection: Option::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for State<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Message, Renderer> PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: ToString + Eq,
|
||||
|
|
@ -78,17 +47,14 @@ where
|
|||
/// The default padding of a [`PickList`].
|
||||
pub const DEFAULT_PADDING: Padding = Padding::new(5);
|
||||
|
||||
/// Creates a new [`PickList`] with the given [`State`], a list of options,
|
||||
/// the current selected value, and the message to produce when an option is
|
||||
/// selected.
|
||||
/// Creates a new [`PickList`] with the given list of options, the current
|
||||
/// selected value, and the message to produce when an option is selected.
|
||||
pub fn new(
|
||||
state: &'a mut State<T>,
|
||||
options: impl Into<Cow<'a, [T]>>,
|
||||
selected: Option<T>,
|
||||
on_selected: impl Fn(T) -> Message + 'static,
|
||||
on_selected: impl Fn(T) -> Message + 'a,
|
||||
) -> Self {
|
||||
Self {
|
||||
state,
|
||||
on_selected: Box::new(on_selected),
|
||||
options: options.into(),
|
||||
placeholder: None,
|
||||
|
|
@ -141,6 +107,168 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: Clone + ToString + Eq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State<T>>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State::<T>::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.width,
|
||||
self.padding,
|
||||
self.text_size,
|
||||
&self.font,
|
||||
self.placeholder.as_deref(),
|
||||
&self.options,
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_renderer: &Renderer,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
self.on_selected.as_ref(),
|
||||
self.selected.as_ref(),
|
||||
&self.options,
|
||||
|| tree.state.downcast_mut::<State<T>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
renderer,
|
||||
theme,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.padding,
|
||||
self.text_size,
|
||||
&self.font,
|
||||
self.placeholder.as_deref(),
|
||||
self.selected.as_ref(),
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
let state = tree.state.downcast_mut::<State<T>>();
|
||||
|
||||
overlay(
|
||||
layout,
|
||||
state,
|
||||
self.padding,
|
||||
self.text_size,
|
||||
self.font.clone(),
|
||||
&self.options,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Message, Renderer> From<PickList<'a, T, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
T: Clone + ToString + Eq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(pick_list: PickList<'a, T, Message, Renderer>) -> Self {
|
||||
Self::new(pick_list)
|
||||
}
|
||||
}
|
||||
|
||||
/// The local state of a [`PickList`].
|
||||
#[derive(Debug)]
|
||||
pub struct State<T> {
|
||||
menu: menu::State,
|
||||
keyboard_modifiers: keyboard::Modifiers,
|
||||
is_open: bool,
|
||||
hovered_option: Option<usize>,
|
||||
last_selection: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
/// Creates a new [`State`] for a [`PickList`].
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
menu: menu::State::default(),
|
||||
keyboard_modifiers: keyboard::Modifiers::default(),
|
||||
is_open: bool::default(),
|
||||
hovered_option: Option::default(),
|
||||
last_selection: Option::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for State<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the layout of a [`PickList`].
|
||||
pub fn layout<Renderer, T>(
|
||||
renderer: &Renderer,
|
||||
|
|
@ -429,127 +557,3 @@ pub fn draw<T, Renderer>(
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: Clone + ToString + Eq,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'static,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.width,
|
||||
self.padding,
|
||||
self.text_size,
|
||||
&self.font,
|
||||
self.placeholder.as_deref(),
|
||||
&self.options,
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_renderer: &Renderer,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
self.on_selected.as_ref(),
|
||||
self.selected.as_ref(),
|
||||
&self.options,
|
||||
|| &mut self.state,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
renderer,
|
||||
theme,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.padding,
|
||||
self.text_size,
|
||||
&self.font,
|
||||
self.placeholder.as_deref(),
|
||||
self.selected.as_ref(),
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
overlay(
|
||||
layout,
|
||||
self.state,
|
||||
self.padding,
|
||||
self.text_size,
|
||||
self.font.clone(),
|
||||
&self.options,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Message, Renderer> From<PickList<'a, T, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
T: Clone + ToString + Eq,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'static,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet
|
||||
+ container::StyleSheet
|
||||
+ scrollable::StyleSheet
|
||||
+ menu::StyleSheet,
|
||||
<Renderer::Theme as StyleSheet>::Style:
|
||||
Into<<Renderer::Theme as menu::StyleSheet>::Style>,
|
||||
{
|
||||
fn from(pick_list: PickList<'a, T, Message, Renderer>) -> Self {
|
||||
Element::new(pick_list)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Provide progress feedback to your users.
|
||||
use crate::layout;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{Color, Element, Layout, Length, Point, Rectangle, Size, Widget};
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
|
@ -105,6 +106,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::mouse;
|
|||
use crate::renderer;
|
||||
use crate::text;
|
||||
use crate::touch;
|
||||
use crate::widget::{self, Row, Text};
|
||||
use crate::widget::{self, Row, Text, Tree};
|
||||
use crate::{
|
||||
Alignment, Clipboard, Color, Element, Layout, Length, Point, Rectangle,
|
||||
Shell, Widget,
|
||||
|
|
@ -176,6 +176,7 @@ where
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_state: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -200,6 +201,7 @@ where
|
|||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
|
|
@ -214,6 +216,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
//! Distribute content horizontally.
|
||||
use crate::event::{self, Event};
|
||||
use crate::layout;
|
||||
use crate::layout::{self, Layout};
|
||||
use crate::mouse;
|
||||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
Alignment, Clipboard, Element, Layout, Length, Padding, Point, Rectangle,
|
||||
Shell, Widget,
|
||||
Alignment, Clipboard, Element, Length, Padding, Point, Rectangle, Shell,
|
||||
Widget,
|
||||
};
|
||||
|
||||
use std::u32;
|
||||
|
||||
/// A container that distributes its contents horizontally.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Row<'a, Message, Renderer> {
|
||||
|
|
@ -18,8 +17,6 @@ pub struct Row<'a, Message, Renderer> {
|
|||
padding: Padding,
|
||||
width: Length,
|
||||
height: Length,
|
||||
max_width: u32,
|
||||
max_height: u32,
|
||||
align_items: Alignment,
|
||||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
}
|
||||
|
|
@ -39,8 +36,6 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
|
|||
padding: Padding::ZERO,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
max_width: u32::MAX,
|
||||
max_height: u32::MAX,
|
||||
align_items: Alignment::Start,
|
||||
children,
|
||||
}
|
||||
|
|
@ -74,18 +69,6 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the maximum width of the [`Row`].
|
||||
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||
self.max_width = max_width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the maximum height of the [`Row`].
|
||||
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||
self.max_height = max_height;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the vertical alignment of the contents of the [`Row`] .
|
||||
pub fn align_items(mut self, align: Alignment) -> Self {
|
||||
self.align_items = align;
|
||||
|
|
@ -93,10 +76,10 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
|
|||
}
|
||||
|
||||
/// Adds an [`Element`] to the [`Row`].
|
||||
pub fn push<E>(mut self, child: E) -> Self
|
||||
where
|
||||
E: Into<Element<'a, Message, Renderer>>,
|
||||
{
|
||||
pub fn push(
|
||||
mut self,
|
||||
child: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
self.children.push(child.into());
|
||||
self
|
||||
}
|
||||
|
|
@ -113,6 +96,14 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
|||
where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
self.children.iter().map(Tree::new).collect()
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(&self.children)
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
|
@ -126,11 +117,7 @@ where
|
|||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits
|
||||
.max_width(self.max_width)
|
||||
.max_height(self.max_height)
|
||||
.width(self.width)
|
||||
.height(self.height);
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
|
||||
layout::flex::resolve(
|
||||
layout::flex::Axis::Horizontal,
|
||||
|
|
@ -145,6 +132,7 @@ where
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -154,9 +142,11 @@ where
|
|||
) -> event::Status {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
child.widget.on_event(
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget_mut().on_event(
|
||||
state,
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor_position,
|
||||
|
|
@ -170,6 +160,7 @@ where
|
|||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
|
|
@ -177,9 +168,11 @@ where
|
|||
) -> mouse::Interaction {
|
||||
self.children
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
child.widget.mouse_interaction(
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget().mouse_interaction(
|
||||
state,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
|
|
@ -192,6 +185,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
@ -199,8 +193,14 @@ where
|
|||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
for (child, layout) in self.children.iter().zip(layout.children()) {
|
||||
child.draw(
|
||||
for ((child, state), layout) in self
|
||||
.children
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.zip(layout.children())
|
||||
{
|
||||
child.as_widget().draw(
|
||||
state,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
|
|
@ -211,28 +211,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(layout.children())
|
||||
.filter_map(|(child, layout)| {
|
||||
child.widget.overlay(layout, renderer)
|
||||
})
|
||||
.next()
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
overlay::from_children(&self.children, tree, layout, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Row<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Message: 'a,
|
||||
Renderer: crate::Renderer + 'a,
|
||||
{
|
||||
fn from(row: Row<'a, Message, Renderer>) -> Element<'a, Message, Renderer> {
|
||||
Element::new(row)
|
||||
fn from(row: Row<'a, Message, Renderer>) -> Self {
|
||||
Self::new(row)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Display a horizontal or vertical rule for dividing content.
|
||||
use crate::layout;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{Color, Element, Layout, Length, Point, Rectangle, Size, Widget};
|
||||
|
||||
pub use iced_style::rule::{Appearance, FillMode, StyleSheet};
|
||||
|
|
@ -78,6 +79,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use crate::mouse;
|
|||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::touch;
|
||||
use crate::widget::Column;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Alignment, Background, Clipboard, Color, Element, Layout, Length, Padding,
|
||||
Point, Rectangle, Shell, Size, Vector, Widget,
|
||||
Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle,
|
||||
Shell, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
use std::{f32, u32};
|
||||
|
|
@ -30,13 +30,11 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
state: &'a mut State,
|
||||
height: Length,
|
||||
max_height: u32,
|
||||
scrollbar_width: u16,
|
||||
scrollbar_margin: u16,
|
||||
scroller_width: u16,
|
||||
content: Column<'a, Message, Renderer>,
|
||||
content: Element<'a, Message, Renderer>,
|
||||
on_scroll: Option<Box<dyn Fn(f32) -> Message + 'a>>,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
}
|
||||
|
|
@ -46,67 +44,25 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
/// Creates a new [`Scrollable`] with the given [`State`].
|
||||
pub fn new(state: &'a mut State) -> Self {
|
||||
/// Creates a new [`Scrollable`].
|
||||
pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
|
||||
Scrollable {
|
||||
state,
|
||||
height: Length::Shrink,
|
||||
max_height: u32::MAX,
|
||||
scrollbar_width: 10,
|
||||
scrollbar_margin: 0,
|
||||
scroller_width: 10,
|
||||
content: Column::new(),
|
||||
content: content.into(),
|
||||
on_scroll: None,
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the vertical spacing _between_ elements.
|
||||
///
|
||||
/// Custom margins per element do not exist in Iced. You should use this
|
||||
/// method instead! While less flexible, it helps you keep spacing between
|
||||
/// elements consistent.
|
||||
pub fn spacing(mut self, units: u16) -> Self {
|
||||
self.content = self.content.spacing(units);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Padding`] of the [`Scrollable`].
|
||||
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||
self.content = self.content.padding(padding);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the width of the [`Scrollable`].
|
||||
pub fn width(mut self, width: Length) -> Self {
|
||||
self.content = self.content.width(width);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the height of the [`Scrollable`].
|
||||
pub fn height(mut self, height: Length) -> Self {
|
||||
self.height = height;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the maximum width of the [`Scrollable`].
|
||||
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||
self.content = self.content.max_width(max_width);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the maximum height of the [`Scrollable`] in pixels.
|
||||
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||
self.max_height = max_height;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the horizontal alignment of the contents of the [`Scrollable`] .
|
||||
pub fn align_items(mut self, align_items: Alignment) -> Self {
|
||||
self.content = self.content.align_items(align_items);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the scrollbar width of the [`Scrollable`] .
|
||||
/// Silently enforces a minimum value of 1.
|
||||
pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self {
|
||||
|
|
@ -132,7 +88,7 @@ where
|
|||
///
|
||||
/// The function takes the new relative offset of the [`Scrollable`]
|
||||
/// (e.g. `0` means top, while `1` means bottom).
|
||||
pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'static) -> Self {
|
||||
pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'a) -> Self {
|
||||
self.on_scroll = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
|
@ -145,14 +101,189 @@ where
|
|||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an element to the [`Scrollable`].
|
||||
pub fn push<E>(mut self, child: E) -> Self
|
||||
where
|
||||
E: Into<Element<'a, Message, Renderer>>,
|
||||
{
|
||||
self.content = self.content.push(child);
|
||||
self
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Scrollable<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![Tree::new(&self.content)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(std::slice::from_ref(&self.content))
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.content.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
Widget::<Message, Renderer>::width(self),
|
||||
self.height,
|
||||
u32::MAX,
|
||||
|renderer, limits| {
|
||||
self.content.as_widget().layout(renderer, limits)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
tree.state.downcast_mut::<State>(),
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
clipboard,
|
||||
shell,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
&self.on_scroll,
|
||||
|event, layout, cursor_position, clipboard, shell| {
|
||||
self.content.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
tree.state.downcast_ref::<State>(),
|
||||
renderer,
|
||||
theme,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
self.style,
|
||||
|renderer, layout, cursor_position, viewport| {
|
||||
self.content.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(
|
||||
tree.state.downcast_ref::<State>(),
|
||||
layout,
|
||||
cursor_position,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
|layout, cursor_position, viewport| {
|
||||
self.content.as_widget().mouse_interaction(
|
||||
&tree.children[0],
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content
|
||||
.as_widget()
|
||||
.overlay(
|
||||
&mut tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
renderer,
|
||||
)
|
||||
.map(|overlay| {
|
||||
let bounds = layout.bounds();
|
||||
let content_layout = layout.children().next().unwrap();
|
||||
let content_bounds = content_layout.bounds();
|
||||
let offset = tree
|
||||
.state
|
||||
.downcast_ref::<State>()
|
||||
.offset(bounds, content_bounds);
|
||||
|
||||
overlay.translate(Vector::new(0.0, -(offset as f32)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
text_input: Scrollable<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(text_input)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -625,145 +756,6 @@ fn notify_on_scroll<Message>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Scrollable<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Widget::<Message, Renderer>::width(&self.content)
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
Widget::<Message, Renderer>::width(self),
|
||||
self.height,
|
||||
self.max_height,
|
||||
|renderer, limits| self.content.layout(renderer, limits),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
self.state,
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
clipboard,
|
||||
shell,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
&self.on_scroll,
|
||||
|event, layout, cursor_position, clipboard, shell| {
|
||||
self.content.on_event(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(
|
||||
self.state,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
|layout, cursor_position, viewport| {
|
||||
self.content.mouse_interaction(
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
self.state,
|
||||
renderer,
|
||||
theme,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
self.style,
|
||||
|renderer, layout, cursor_position, viewport| {
|
||||
self.content.draw(
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
let Self { content, state, .. } = self;
|
||||
|
||||
content
|
||||
.overlay(layout.children().next().unwrap(), renderer)
|
||||
.map(|overlay| {
|
||||
let bounds = layout.bounds();
|
||||
let content_layout = layout.children().next().unwrap();
|
||||
let content_bounds = content_layout.bounds();
|
||||
let offset = state.offset(bounds, content_bounds);
|
||||
|
||||
overlay.translate(Vector::new(0.0, -(offset as f32)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The local state of a [`Scrollable`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct State {
|
||||
|
|
@ -926,17 +918,3 @@ struct Scroller {
|
|||
/// The bounds of the [`Scroller`].
|
||||
bounds: Rectangle,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
scrollable: Scrollable<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(scrollable)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use crate::layout;
|
|||
use crate::mouse;
|
||||
use crate::renderer;
|
||||
use crate::touch;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle,
|
||||
Shell, Size, Widget,
|
||||
|
|
@ -29,15 +30,15 @@ pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet};
|
|||
/// # use iced_native::renderer::Null;
|
||||
/// #
|
||||
/// # type Slider<'a, T, Message> = slider::Slider<'a, T, Message, Null>;
|
||||
/// #
|
||||
/// #[derive(Clone)]
|
||||
/// pub enum Message {
|
||||
/// SliderChanged(f32),
|
||||
/// }
|
||||
///
|
||||
/// let state = &mut slider::State::new();
|
||||
/// let value = 50.0;
|
||||
///
|
||||
/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged);
|
||||
/// Slider::new(0.0..=100.0, value, Message::SliderChanged);
|
||||
/// ```
|
||||
///
|
||||
/// 
|
||||
|
|
@ -47,11 +48,10 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
state: &'a mut State,
|
||||
range: RangeInclusive<T>,
|
||||
step: T,
|
||||
value: T,
|
||||
on_change: Box<dyn Fn(T) -> Message>,
|
||||
on_change: Box<dyn Fn(T) -> Message + 'a>,
|
||||
on_release: Option<Message>,
|
||||
width: Length,
|
||||
height: u16,
|
||||
|
|
@ -71,20 +71,14 @@ where
|
|||
/// Creates a new [`Slider`].
|
||||
///
|
||||
/// It expects:
|
||||
/// * the local [`State`] of the [`Slider`]
|
||||
/// * an inclusive range of possible values
|
||||
/// * the current value of the [`Slider`]
|
||||
/// * a function that will be called when the [`Slider`] is dragged.
|
||||
/// It receives the new value of the [`Slider`] and must produce a
|
||||
/// `Message`.
|
||||
pub fn new<F>(
|
||||
state: &'a mut State,
|
||||
range: RangeInclusive<T>,
|
||||
value: T,
|
||||
on_change: F,
|
||||
) -> Self
|
||||
pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self
|
||||
where
|
||||
F: 'static + Fn(T) -> Message,
|
||||
F: 'a + Fn(T) -> Message,
|
||||
{
|
||||
let value = if value >= *range.start() {
|
||||
value
|
||||
|
|
@ -99,7 +93,6 @@ where
|
|||
};
|
||||
|
||||
Slider {
|
||||
state,
|
||||
value,
|
||||
range,
|
||||
step: T::from(1),
|
||||
|
|
@ -150,6 +143,120 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T, Message, Renderer> Widget<Message, Renderer>
|
||||
for Slider<'a, T, Message, Renderer>
|
||||
where
|
||||
T: Copy + Into<f64> + num_traits::FromPrimitive,
|
||||
Message: Clone,
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits =
|
||||
limits.width(self.width).height(Length::Units(self.height));
|
||||
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_renderer: &Renderer,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
tree.state.downcast_mut::<State>(),
|
||||
&mut self.value,
|
||||
&self.range,
|
||||
self.step,
|
||||
self.on_change.as_ref(),
|
||||
&self.on_release,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
renderer,
|
||||
layout,
|
||||
cursor_position,
|
||||
tree.state.downcast_ref::<State>(),
|
||||
self.value,
|
||||
&self.range,
|
||||
theme,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(
|
||||
layout,
|
||||
cursor_position,
|
||||
tree.state.downcast_ref::<State>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, Message, Renderer> From<Slider<'a, T, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
T: 'a + Copy + Into<f64> + num_traits::FromPrimitive,
|
||||
Message: 'a + Clone,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
slider: Slider<'a, T, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(slider)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes an [`Event`] and updates the [`State`] of a [`Slider`]
|
||||
/// accordingly.
|
||||
pub fn update<Message, T>(
|
||||
|
|
@ -366,102 +473,3 @@ impl State {
|
|||
State::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, Message, Renderer> Widget<Message, Renderer>
|
||||
for Slider<'a, T, Message, Renderer>
|
||||
where
|
||||
T: Copy + Into<f64> + num_traits::FromPrimitive,
|
||||
Message: Clone,
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits =
|
||||
limits.width(self.width).height(Length::Units(self.height));
|
||||
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_renderer: &Renderer,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
self.state,
|
||||
&mut self.value,
|
||||
&self.range,
|
||||
self.step,
|
||||
self.on_change.as_ref(),
|
||||
&self.on_release,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
renderer,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.state,
|
||||
self.value,
|
||||
&self.range,
|
||||
theme,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position, self.state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, Message, Renderer> From<Slider<'a, T, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
T: 'a + Copy + Into<f64> + num_traits::FromPrimitive,
|
||||
Message: 'a + Clone,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
slider: Slider<'a, T, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(slider)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Distribute content vertically.
|
||||
use crate::layout;
|
||||
use crate::renderer;
|
||||
use crate::widget::Tree;
|
||||
use crate::{Element, Layout, Length, Point, Rectangle, Size, Widget};
|
||||
|
||||
/// An amount of empty space.
|
||||
|
|
@ -59,6 +60,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
_renderer: &mut Renderer,
|
||||
_theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
//! Display vector graphics in your application.
|
||||
use crate::layout;
|
||||
use crate::renderer;
|
||||
use crate::svg::{self, Handle};
|
||||
use crate::svg;
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
ContentFit, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub use svg::Handle;
|
||||
|
||||
/// A vector graphics image.
|
||||
///
|
||||
/// An [`Svg`] image resizes smoothly without losing any quality.
|
||||
|
|
@ -109,6 +112,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
_theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use crate::alignment;
|
|||
use crate::layout;
|
||||
use crate::renderer;
|
||||
use crate::text;
|
||||
use crate::widget::Tree;
|
||||
use crate::{Element, Layout, Length, Point, Rectangle, Size, Widget};
|
||||
|
||||
pub use iced_style::text::{Appearance, StyleSheet};
|
||||
|
|
@ -145,6 +146,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
@ -243,3 +245,13 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(contents: &'a str) -> Self {
|
||||
Text::new(contents).into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use crate::mouse::{self, click};
|
|||
use crate::renderer;
|
||||
use crate::text::{self, Text};
|
||||
use crate::touch;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle,
|
||||
Shell, Size, Vector, Widget,
|
||||
|
|
@ -30,20 +31,15 @@ pub use iced_style::text_input::{Appearance, StyleSheet};
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use iced_native::renderer::Null;
|
||||
/// # use iced_native::widget::text_input;
|
||||
/// #
|
||||
/// # pub type TextInput<'a, Message> = iced_native::widget::TextInput<'a, Message, Null>;
|
||||
/// # pub type TextInput<'a, Message> = iced_native::widget::TextInput<'a, Message, iced_native::renderer::Null>;
|
||||
/// #[derive(Debug, Clone)]
|
||||
/// enum Message {
|
||||
/// TextInputChanged(String),
|
||||
/// }
|
||||
///
|
||||
/// let mut state = text_input::State::new();
|
||||
/// let value = "Some text";
|
||||
///
|
||||
/// let input = TextInput::new(
|
||||
/// &mut state,
|
||||
/// "This is the placeholder...",
|
||||
/// value,
|
||||
/// Message::TextInputChanged,
|
||||
|
|
@ -57,7 +53,6 @@ where
|
|||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
state: &'a mut State,
|
||||
placeholder: String,
|
||||
value: Value,
|
||||
is_secure: bool,
|
||||
|
|
@ -80,21 +75,14 @@ where
|
|||
/// Creates a new [`TextInput`].
|
||||
///
|
||||
/// It expects:
|
||||
/// - some [`State`]
|
||||
/// - a placeholder
|
||||
/// - the current value
|
||||
/// - a function that produces a message when the [`TextInput`] changes
|
||||
pub fn new<F>(
|
||||
state: &'a mut State,
|
||||
placeholder: &str,
|
||||
value: &str,
|
||||
on_change: F,
|
||||
) -> Self
|
||||
/// - a placeholder,
|
||||
/// - the current value, and
|
||||
/// - a function that produces a message when the [`TextInput`] changes.
|
||||
pub fn new<F>(placeholder: &str, value: &str, on_change: F) -> Self
|
||||
where
|
||||
F: 'a + Fn(String) -> Message,
|
||||
{
|
||||
TextInput {
|
||||
state,
|
||||
placeholder: String::from(placeholder),
|
||||
value: Value::new(value),
|
||||
is_secure: false,
|
||||
|
|
@ -127,7 +115,7 @@ where
|
|||
|
||||
/// Sets the [`Font`] of the [`TextInput`].
|
||||
///
|
||||
/// [`Font`]: crate::text::Renderer::Font
|
||||
/// [`Font`]: text::Renderer::Font
|
||||
pub fn font(mut self, font: Renderer::Font) -> Self {
|
||||
self.font = font;
|
||||
self
|
||||
|
|
@ -166,17 +154,13 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns the current [`State`] of the [`TextInput`].
|
||||
pub fn state(&self) -> &State {
|
||||
self.state
|
||||
}
|
||||
|
||||
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
|
||||
/// [`Value`] if provided.
|
||||
/// [`text_input::Value`] if provided.
|
||||
///
|
||||
/// [`Renderer`]: text::Renderer
|
||||
pub fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
layout: Layout<'_>,
|
||||
|
|
@ -188,7 +172,7 @@ where
|
|||
theme,
|
||||
layout,
|
||||
cursor_position,
|
||||
self.state,
|
||||
tree.state.downcast_ref::<State>(),
|
||||
value.unwrap_or(&self.value),
|
||||
&self.placeholder,
|
||||
self.size,
|
||||
|
|
@ -199,6 +183,116 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for TextInput<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(renderer, limits, self.width, self.padding, self.size)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
&mut self.value,
|
||||
self.size,
|
||||
&self.font,
|
||||
self.is_secure,
|
||||
self.on_change.as_ref(),
|
||||
self.on_paste.as_deref(),
|
||||
&self.on_submit,
|
||||
|| tree.state.downcast_mut::<State>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
draw(
|
||||
renderer,
|
||||
theme,
|
||||
layout,
|
||||
cursor_position,
|
||||
tree.state.downcast_ref::<State>(),
|
||||
&self.value,
|
||||
&self.placeholder,
|
||||
self.size,
|
||||
&self.font,
|
||||
self.is_secure,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a + Clone,
|
||||
Renderer: 'a + text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
text_input: TextInput<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(text_input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the layout of a [`TextInput`].
|
||||
pub fn layout<Renderer>(
|
||||
renderer: &Renderer,
|
||||
|
|
@ -777,93 +871,6 @@ pub fn mouse_interaction(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for TextInput<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout(renderer, limits, self.width, self.padding, self.size)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
update(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
&mut self.value,
|
||||
self.size,
|
||||
&self.font,
|
||||
self.is_secure,
|
||||
self.on_change.as_ref(),
|
||||
self.on_paste.as_deref(),
|
||||
&self.on_submit,
|
||||
|| &mut self.state,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
mouse_interaction(layout, cursor_position)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
self.draw(renderer, theme, layout, cursor_position, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a + Clone,
|
||||
Renderer: 'a + text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
text_input: TextInput<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(text_input)
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of a [`TextInput`].
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct State {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::layout;
|
|||
use crate::mouse;
|
||||
use crate::renderer;
|
||||
use crate::text;
|
||||
use crate::widget::{self, Row, Text};
|
||||
use crate::widget::{self, Row, Text, Tree};
|
||||
use crate::{
|
||||
Alignment, Clipboard, Element, Event, Layout, Length, Point, Rectangle,
|
||||
Shell, Widget,
|
||||
|
|
@ -180,6 +180,7 @@ where
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_state: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
|
|
@ -205,6 +206,7 @@ where
|
|||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
|
|
@ -219,6 +221,7 @@ where
|
|||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ use crate::renderer;
|
|||
use crate::text;
|
||||
use crate::widget;
|
||||
use crate::widget::container;
|
||||
use crate::widget::text::Text;
|
||||
use crate::widget::overlay;
|
||||
use crate::widget::{Text, Tree};
|
||||
use crate::{
|
||||
Clipboard, Element, Event, Layout, Length, Padding, Point, Rectangle,
|
||||
Shell, Size, Vector, Widget,
|
||||
|
|
@ -14,7 +15,7 @@ use crate::{
|
|||
|
||||
/// An element to display a widget over another.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Tooltip<'a, Message, Renderer>
|
||||
pub struct Tooltip<'a, Message, Renderer: text::Renderer>
|
||||
where
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
|
||||
|
|
@ -89,6 +90,153 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Tooltip<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
|
||||
{
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![Tree::new(&self.content)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(std::slice::from_ref(&self.content))
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.content.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.content.as_widget().height()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
self.content.as_widget().layout(renderer, limits)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.content.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.content.as_widget().mouse_interaction(
|
||||
&tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
inherited_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
self.content.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
inherited_style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
let tooltip = &self.tooltip;
|
||||
|
||||
draw(
|
||||
renderer,
|
||||
theme,
|
||||
inherited_style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
self.position,
|
||||
self.gap,
|
||||
self.padding,
|
||||
self.style,
|
||||
|renderer, limits| {
|
||||
Widget::<(), Renderer>::layout(tooltip, renderer, limits)
|
||||
},
|
||||
|renderer, defaults, layout, cursor_position, viewport| {
|
||||
Widget::<(), Renderer>::draw(
|
||||
tooltip,
|
||||
&Tree::empty(),
|
||||
renderer,
|
||||
theme,
|
||||
defaults,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content.as_widget().overlay(
|
||||
&mut tree.children[0],
|
||||
layout,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Tooltip<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + text::Renderer,
|
||||
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
tooltip: Tooltip<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
/// The position of the tooltip. Defaults to following the cursor.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Position {
|
||||
|
|
@ -220,122 +368,3 @@ pub fn draw<Renderer>(
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Tooltip<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.content.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.content.height()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
self.content.layout(renderer, limits)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.content.widget.on_event(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.content.mouse_interaction(
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
inherited_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
self.content.draw(
|
||||
renderer,
|
||||
theme,
|
||||
inherited_style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
let tooltip = &self.tooltip;
|
||||
|
||||
draw(
|
||||
renderer,
|
||||
theme,
|
||||
inherited_style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
self.position,
|
||||
self.gap,
|
||||
self.padding,
|
||||
self.style,
|
||||
|renderer, limits| {
|
||||
Widget::<(), Renderer>::layout(tooltip, renderer, limits)
|
||||
},
|
||||
|renderer, defaults, layout, cursor_position, viewport| {
|
||||
Widget::<(), Renderer>::draw(
|
||||
tooltip,
|
||||
renderer,
|
||||
theme,
|
||||
defaults,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Tooltip<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'a,
|
||||
Renderer: 'a + text::Renderer,
|
||||
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
|
||||
{
|
||||
fn from(
|
||||
tooltip: Tooltip<'a, Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(tooltip)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
187
native/src/widget/tree.rs
Normal file
187
native/src/widget/tree.rs
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
//! Store internal widget state in a state tree to ensure continuity.
|
||||
use crate::Widget;
|
||||
|
||||
use std::any::{self, Any};
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt;
|
||||
|
||||
/// A persistent state widget tree.
|
||||
///
|
||||
/// A [`Tree`] is normally associated with a specific widget in the widget tree.
|
||||
#[derive(Debug)]
|
||||
pub struct Tree {
|
||||
/// The tag of the [`Tree`].
|
||||
pub tag: Tag,
|
||||
|
||||
/// The [`State`] of the [`Tree`].
|
||||
pub state: State,
|
||||
|
||||
/// The children of the root widget of the [`Tree`].
|
||||
pub children: Vec<Tree>,
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
/// Creates an empty, stateless [`Tree`] with no children.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
tag: Tag::stateless(),
|
||||
state: State::None,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`Tree`] for the provided [`Element`].
|
||||
pub fn new<'a, Message, Renderer>(
|
||||
widget: impl Borrow<dyn Widget<Message, Renderer> + 'a>,
|
||||
) -> Self
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
let widget = widget.borrow();
|
||||
|
||||
Self {
|
||||
tag: widget.tag(),
|
||||
state: widget.state(),
|
||||
children: widget.children(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconciliates the current tree with the provided [`Element`].
|
||||
///
|
||||
/// If the tag of the [`Element`] matches the tag of the [`Tree`], then the
|
||||
/// [`Element`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called).
|
||||
///
|
||||
/// Otherwise, the whole [`Tree`] is recreated.
|
||||
///
|
||||
/// [`Widget::diff`]: crate::Widget::diff
|
||||
pub fn diff<'a, Message, Renderer>(
|
||||
&mut self,
|
||||
new: impl Borrow<dyn Widget<Message, Renderer> + 'a>,
|
||||
) where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
if self.tag == new.borrow().tag() {
|
||||
new.borrow().diff(self)
|
||||
} else {
|
||||
*self = Self::new(new);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconciliates the children of the tree with the provided list of [`Element`].
|
||||
pub fn diff_children<'a, Message, Renderer>(
|
||||
&mut self,
|
||||
new_children: &[impl Borrow<dyn Widget<Message, Renderer> + 'a>],
|
||||
) where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
self.diff_children_custom(
|
||||
new_children,
|
||||
|tree, widget| tree.diff(widget.borrow()),
|
||||
|widget| Self::new(widget.borrow()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Reconciliates the children of the tree with the provided list of [`Element`] using custom
|
||||
/// logic both for diffing and creating new widget state.
|
||||
pub fn diff_children_custom<T>(
|
||||
&mut self,
|
||||
new_children: &[T],
|
||||
diff: impl Fn(&mut Tree, &T),
|
||||
new_state: impl Fn(&T) -> Self,
|
||||
) {
|
||||
if self.children.len() > new_children.len() {
|
||||
self.children.truncate(new_children.len());
|
||||
}
|
||||
|
||||
for (child_state, new) in
|
||||
self.children.iter_mut().zip(new_children.iter())
|
||||
{
|
||||
diff(child_state, new);
|
||||
}
|
||||
|
||||
if self.children.len() < new_children.len() {
|
||||
self.children.extend(
|
||||
new_children[self.children.len()..].iter().map(new_state),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The identifier of some widget state.
|
||||
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
pub struct Tag(any::TypeId);
|
||||
|
||||
impl Tag {
|
||||
/// Creates a [`Tag`] for a state of type `T`.
|
||||
pub fn of<T>() -> Self
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
Self(any::TypeId::of::<T>())
|
||||
}
|
||||
|
||||
/// Creates a [`Tag`] for a stateless widget.
|
||||
pub fn stateless() -> Self {
|
||||
Self::of::<()>()
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal [`State`] of a widget.
|
||||
pub enum State {
|
||||
/// No meaningful internal state.
|
||||
None,
|
||||
|
||||
/// Some meaningful internal state.
|
||||
Some(Box<dyn Any>),
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Creates a new [`State`].
|
||||
pub fn new<T>(state: T) -> Self
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
State::Some(Box::new(state))
|
||||
}
|
||||
|
||||
/// Downcasts the [`State`] to `T` and returns a reference to it.
|
||||
///
|
||||
/// # Panics
|
||||
/// This method will panic if the downcast fails or the [`State`] is [`State::None`].
|
||||
pub fn downcast_ref<T>(&self) -> &T
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
match self {
|
||||
State::None => panic!("Downcast on stateless state"),
|
||||
State::Some(state) => {
|
||||
state.downcast_ref().expect("Downcast widget state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Downcasts the [`State`] to `T` and returns a mutable reference to it.
|
||||
///
|
||||
/// # Panics
|
||||
/// This method will panic if the downcast fails or the [`State`] is [`State::None`].
|
||||
pub fn downcast_mut<T>(&mut self) -> &mut T
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
match self {
|
||||
State::None => panic!("Downcast on stateless state"),
|
||||
State::Some(state) => {
|
||||
state.downcast_mut().expect("Downcast widget state")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for State {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::None => write!(f, "State::None"),
|
||||
Self::Some(_) => write!(f, "State::Some"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue