diff options
Diffstat (limited to 'graphics/src')
| -rw-r--r-- | graphics/src/backend.rs | 7 | ||||
| -rw-r--r-- | graphics/src/font.rs | 6 | ||||
| -rw-r--r-- | graphics/src/lib.rs | 5 | ||||
| -rw-r--r-- | graphics/src/overlay.rs | 1 | ||||
| -rw-r--r-- | graphics/src/overlay/menu.rs | 107 | ||||
| -rw-r--r-- | graphics/src/renderer.rs | 35 | ||||
| -rw-r--r-- | graphics/src/widget.rs | 1 | ||||
| -rw-r--r-- | graphics/src/widget/canvas.rs | 2 | ||||
| -rw-r--r-- | graphics/src/widget/combo_box.rs | 96 | 
9 files changed, 254 insertions, 6 deletions
diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index b73c636e..dd7dbbc2 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -22,9 +22,14 @@ pub trait Text {      /// The `char` representing a ✔ icon in the [`ICON_FONT`].      /// -    /// [`ICON_FONT`]: #associatedconst.ICON_FONt +    /// [`ICON_FONT`]: #associatedconst.ICON_FONT      const CHECKMARK_ICON: char; +    /// The `char` representing a ▼ icon in the built-in [`ICONS`] font. +    /// +    /// [`ICON_FONT`]: #associatedconst.ICON_FONT +    const ARROW_DOWN_ICON: char; +      /// Returns the default size of text.      fn default_size(&self) -> u16; diff --git a/graphics/src/font.rs b/graphics/src/font.rs index bcc28857..5c62681c 100644 --- a/graphics/src/font.rs +++ b/graphics/src/font.rs @@ -31,3 +31,9 @@ pub const ICONS: iced_native::Font = iced_native::Font::External {  #[cfg(feature = "font-icons")]  #[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))]  pub const CHECKMARK_ICON: char = '\u{F00C}'; + +/// The `char` representing a ▼ icon in the built-in [`ICONS`] font. +/// +/// [`ICONS`]: const.ICONS.html +#[cfg(feature = "font-icons")] +pub const ARROW_DOWN_ICON: char = '\u{E800}'; diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 38d8dffa..0c427634 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -2,7 +2,7 @@  //! for [`iced`].  //!  //! [`iced`]: https://github.com/hecrj/iced -#![deny(missing_docs)] +//#![deny(missing_docs)]  #![deny(missing_debug_implementations)]  #![deny(unused_results)]  #![deny(unsafe_code)] @@ -13,13 +13,14 @@ mod primitive;  mod renderer;  mod transformation;  mod viewport; -mod widget;  pub mod backend;  pub mod defaults;  pub mod font;  pub mod layer; +pub mod overlay;  pub mod triangle; +pub mod widget;  pub mod window;  #[doc(no_inline)] diff --git a/graphics/src/overlay.rs b/graphics/src/overlay.rs new file mode 100644 index 00000000..b9a0e3e0 --- /dev/null +++ b/graphics/src/overlay.rs @@ -0,0 +1 @@ +pub mod menu; diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs new file mode 100644 index 00000000..89a9cd03 --- /dev/null +++ b/graphics/src/overlay/menu.rs @@ -0,0 +1,107 @@ +use crate::backend::{self, Backend}; +use crate::{Primitive, Renderer}; +use iced_native::{ +    mouse, overlay, Color, Font, HorizontalAlignment, Point, Rectangle, +    VerticalAlignment, +}; + +pub use iced_style::menu::Style; + +impl<B> overlay::menu::Renderer for Renderer<B> +where +    B: Backend + backend::Text, +{ +    type Style = Style; + +    fn decorate( +        &mut self, +        bounds: Rectangle, +        _cursor_position: Point, +        style: &Style, +        (primitives, mouse_cursor): Self::Output, +    ) -> Self::Output { +        ( +            Primitive::Group { +                primitives: vec![ +                    Primitive::Quad { +                        bounds, +                        background: style.background, +                        border_color: style.border_color, +                        border_width: style.border_width, +                        border_radius: 0, +                    }, +                    primitives, +                ], +            }, +            mouse_cursor, +        ) +    } + +    fn draw<T: ToString>( +        &mut self, +        bounds: Rectangle, +        cursor_position: Point, +        options: &[T], +        hovered_option: Option<usize>, +        padding: u16, +        text_size: u16, +        font: Font, +        style: &Style, +    ) -> Self::Output { +        use std::f32; + +        let is_mouse_over = bounds.contains(cursor_position); + +        let mut primitives = Vec::new(); + +        for (i, option) in options.iter().enumerate() { +            let is_selected = hovered_option == Some(i); + +            let bounds = Rectangle { +                x: bounds.x, +                y: bounds.y +                    + ((text_size as usize + padding as usize * 2) * i) as f32, +                width: bounds.width, +                height: f32::from(text_size + padding * 2), +            }; + +            if is_selected { +                primitives.push(Primitive::Quad { +                    bounds, +                    background: style.selected_background, +                    border_color: Color::TRANSPARENT, +                    border_width: 0, +                    border_radius: 0, +                }); +            } + +            primitives.push(Primitive::Text { +                content: option.to_string(), +                bounds: Rectangle { +                    x: bounds.x + f32::from(padding), +                    y: bounds.center_y(), +                    width: f32::INFINITY, +                    ..bounds +                }, +                size: f32::from(text_size), +                font, +                color: if is_selected { +                    style.selected_text_color +                } else { +                    style.text_color +                }, +                horizontal_alignment: HorizontalAlignment::Left, +                vertical_alignment: VerticalAlignment::Center, +            }); +        } + +        ( +            Primitive::Group { primitives }, +            if is_mouse_over { +                mouse::Interaction::Pointer +            } else { +                mouse::Interaction::default() +            }, +        ) +    } +} diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index c9360f3a..5ca6c057 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,7 +1,9 @@  use crate::{Backend, Defaults, Primitive};  use iced_native::layout::{self, Layout};  use iced_native::mouse; -use iced_native::{Background, Color, Element, Point, Widget}; +use iced_native::{ +    Background, Color, Element, Point, Rectangle, Vector, Widget, +};  /// A backend-agnostic renderer that supports all the built-in widgets.  #[derive(Debug)] @@ -53,6 +55,35 @@ where          layout      } + +    fn overlay( +        &mut self, +        (base_primitive, base_cursor): (Primitive, mouse::Interaction), +        (overlay_primitives, overlay_cursor): (Primitive, mouse::Interaction), +        overlay_bounds: Rectangle, +    ) -> (Primitive, mouse::Interaction) { +        ( +            Primitive::Group { +                primitives: vec![ +                    base_primitive, +                    Primitive::Clip { +                        bounds: Rectangle { +                            width: overlay_bounds.width + 0.5, +                            height: overlay_bounds.height + 0.5, +                            ..overlay_bounds +                        }, +                        offset: Vector::new(0, 0), +                        content: Box::new(overlay_primitives), +                    }, +                ], +            }, +            if base_cursor > overlay_cursor { +                base_cursor +            } else { +                overlay_cursor +            }, +        ) +    }  }  impl<B> layout::Debugger for Renderer<B> @@ -62,7 +93,7 @@ where      fn explain<Message>(          &mut self,          defaults: &Defaults, -        widget: &dyn Widget<Message, Self>, +        widget: &dyn Widget<'_, Message, Self>,          layout: Layout<'_>,          cursor_position: Point,          color: Color, diff --git a/graphics/src/widget.rs b/graphics/src/widget.rs index 1f6d6559..a0d06999 100644 --- a/graphics/src/widget.rs +++ b/graphics/src/widget.rs @@ -9,6 +9,7 @@  //! ```  pub mod button;  pub mod checkbox; +pub mod combo_box;  pub mod container;  pub mod image;  pub mod pane_grid; diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index b8466239..0257f819 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -134,7 +134,7 @@ impl<Message, P: Program<Message>> Canvas<Message, P> {      }  } -impl<Message, P, B> Widget<Message, Renderer<B>> for Canvas<Message, P> +impl<'a, Message, P, B> Widget<'a, Message, Renderer<B>> for Canvas<Message, P>  where      P: Program<Message>,      B: Backend, diff --git a/graphics/src/widget/combo_box.rs b/graphics/src/widget/combo_box.rs new file mode 100644 index 00000000..e7ed4e04 --- /dev/null +++ b/graphics/src/widget/combo_box.rs @@ -0,0 +1,96 @@ +use crate::backend::{self, Backend}; +use crate::{Primitive, Renderer}; +use iced_native::{ +    mouse, Font, HorizontalAlignment, Point, Rectangle, VerticalAlignment, +}; +use iced_style::menu; + +pub use iced_native::combo_box::State; +pub use iced_style::combo_box::{Style, StyleSheet}; + +/// A widget allowing the selection of a single value from a list of options. +pub type ComboBox<'a, T, Message, Backend> = +    iced_native::ComboBox<'a, T, Message, Renderer<Backend>>; + +impl<B> iced_native::combo_box::Renderer for Renderer<B> +where +    B: Backend + backend::Text, +{ +    type Style = Box<dyn StyleSheet>; + +    const DEFAULT_PADDING: u16 = 5; + +    fn menu_style(style: &Box<dyn StyleSheet>) -> menu::Style { +        style.menu() +    } + +    fn draw( +        &mut self, +        bounds: Rectangle, +        cursor_position: Point, +        selected: Option<String>, +        padding: u16, +        text_size: u16, +        font: Font, +        style: &Box<dyn StyleSheet>, +    ) -> Self::Output { +        let is_mouse_over = bounds.contains(cursor_position); + +        let style = if is_mouse_over { +            style.hovered() +        } else { +            style.active() +        }; + +        let background = Primitive::Quad { +            bounds, +            background: style.background, +            border_color: style.border_color, +            border_width: style.border_width, +            border_radius: style.border_radius, +        }; + +        let arrow_down = Primitive::Text { +            content: B::ARROW_DOWN_ICON.to_string(), +            font: B::ICON_FONT, +            size: bounds.height * style.icon_size, +            bounds: Rectangle { +                x: bounds.x + bounds.width - f32::from(padding) * 2.0, +                y: bounds.center_y(), +                ..bounds +            }, +            color: style.text_color, +            horizontal_alignment: HorizontalAlignment::Right, +            vertical_alignment: VerticalAlignment::Center, +        }; + +        ( +            Primitive::Group { +                primitives: if let Some(label) = selected { +                    let label = Primitive::Text { +                        content: label, +                        size: f32::from(text_size), +                        font, +                        color: style.text_color, +                        bounds: Rectangle { +                            x: bounds.x + f32::from(padding), +                            y: bounds.center_y(), +                            ..bounds +                        }, +                        horizontal_alignment: HorizontalAlignment::Left, +                        vertical_alignment: VerticalAlignment::Center, +                    }; + +                    vec![background, label, arrow_down] +                } else { +                    vec![background, arrow_down] +                }, +            }, +            if is_mouse_over { +                mouse::Interaction::Pointer +            } else { +                mouse::Interaction::default() +            }, +        ) +    } +}  | 
