diff options
Diffstat (limited to '')
| -rw-r--r-- | graphics/Cargo.toml | 9 | ||||
| -rw-r--r-- | graphics/src/layer.rs | 40 | ||||
| -rw-r--r-- | graphics/src/overlay/menu.rs | 12 | ||||
| -rw-r--r-- | graphics/src/viewport.rs | 2 | ||||
| -rw-r--r-- | graphics/src/widget.rs | 6 | ||||
| -rw-r--r-- | graphics/src/widget/button.rs | 4 | ||||
| -rw-r--r-- | graphics/src/widget/canvas.rs | 4 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/frame.rs | 2 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/program.rs | 2 | ||||
| -rw-r--r-- | graphics/src/widget/image.rs | 5 | ||||
| -rw-r--r-- | graphics/src/widget/image/viewer.rs | 55 | ||||
| -rw-r--r-- | graphics/src/widget/pane_grid.rs | 134 | ||||
| -rw-r--r-- | graphics/src/widget/pick_list.rs | 21 | ||||
| -rw-r--r-- | graphics/src/widget/scrollable.rs | 10 | ||||
| -rw-r--r-- | graphics/src/widget/toggler.rs | 99 | ||||
| -rw-r--r-- | graphics/src/widget/tooltip.rs | 168 | ||||
| -rw-r--r-- | graphics/src/window/compositor.rs | 5 | 
17 files changed, 498 insertions, 80 deletions
| diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 73dc47bf..ea9471c6 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -1,6 +1,6 @@  [package]  name = "iced_graphics" -version = "0.1.0" +version = "0.2.0"  authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]  edition = "2018"  description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced" @@ -28,11 +28,11 @@ version = "1.4"  features = ["derive"]  [dependencies.iced_native] -version = "0.3" +version = "0.4"  path = "../native"  [dependencies.iced_style] -version = "0.2" +version = "0.3"  path = "../style"  [dependencies.lyon] @@ -42,9 +42,10 @@ optional = true  [dependencies.qrcode]  version = "0.12"  optional = true +default-features = false  [dependencies.font-kit] -version = "0.8" +version = "0.10"  optional = true  [package.metadata.docs.rs] diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index ab40b114..7dce1d4c 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -82,7 +82,12 @@ impl<'a> Layer<'a> {          let mut layers = vec![first_layer]; -        Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive); +        Self::process_primitive( +            &mut layers, +            Vector::new(0.0, 0.0), +            primitive, +            0, +        );          layers      } @@ -91,13 +96,19 @@ impl<'a> Layer<'a> {          layers: &mut Vec<Self>,          translation: Vector,          primitive: &'a Primitive, +        current_layer: usize,      ) {          match primitive {              Primitive::None => {}              Primitive::Group { primitives } => {                  // TODO: Inspect a bit and regroup (?)                  for primitive in primitives { -                    Self::process_primitive(layers, translation, primitive) +                    Self::process_primitive( +                        layers, +                        translation, +                        primitive, +                        current_layer, +                    )                  }              }              Primitive::Text { @@ -109,7 +120,7 @@ impl<'a> Layer<'a> {                  horizontal_alignment,                  vertical_alignment,              } => { -                let layer = layers.last_mut().unwrap(); +                let layer = &mut layers[current_layer];                  layer.text.push(Text {                      content, @@ -128,7 +139,7 @@ impl<'a> Layer<'a> {                  border_width,                  border_color,              } => { -                let layer = layers.last_mut().unwrap(); +                let layer = &mut layers[current_layer];                  // TODO: Move some of these computations to the GPU (?)                  layer.quads.push(Quad { @@ -146,7 +157,7 @@ impl<'a> Layer<'a> {                  });              }              Primitive::Mesh2D { buffers, size } => { -                let layer = layers.last_mut().unwrap(); +                let layer = &mut layers[current_layer];                  let bounds = Rectangle::new(                      Point::new(translation.x, translation.y), @@ -167,7 +178,7 @@ impl<'a> Layer<'a> {                  offset,                  content,              } => { -                let layer = layers.last_mut().unwrap(); +                let layer = &mut layers[current_layer];                  let translated_bounds = *bounds + translation;                  // Only draw visible content @@ -175,16 +186,15 @@ impl<'a> Layer<'a> {                      layer.bounds.intersection(&translated_bounds)                  {                      let clip_layer = Layer::new(clip_bounds); -                    let new_layer = Layer::new(layer.bounds); -                      layers.push(clip_layer); +                      Self::process_primitive(                          layers,                          translation                              - Vector::new(offset.x as f32, offset.y as f32),                          content, +                        layers.len() - 1,                      ); -                    layers.push(new_layer);                  }              }              Primitive::Translate { @@ -195,13 +205,19 @@ impl<'a> Layer<'a> {                      layers,                      translation + *new_translation,                      &content, +                    current_layer,                  );              }              Primitive::Cached { cache } => { -                Self::process_primitive(layers, translation, &cache); +                Self::process_primitive( +                    layers, +                    translation, +                    &cache, +                    current_layer, +                );              }              Primitive::Image { handle, bounds } => { -                let layer = layers.last_mut().unwrap(); +                let layer = &mut layers[current_layer];                  layer.images.push(Image::Raster {                      handle: handle.clone(), @@ -209,7 +225,7 @@ impl<'a> Layer<'a> {                  });              }              Primitive::Svg { handle, bounds } => { -                let layer = layers.last_mut().unwrap(); +                let layer = &mut layers[current_layer];                  layer.images.push(Image::Vector {                      handle: handle.clone(), diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs index ffe998c5..9e91a0ef 100644 --- a/graphics/src/overlay/menu.rs +++ b/graphics/src/overlay/menu.rs @@ -2,8 +2,8 @@  use crate::backend::{self, Backend};  use crate::{Primitive, Renderer};  use iced_native::{ -    mouse, overlay, Color, Font, HorizontalAlignment, Point, Rectangle, -    VerticalAlignment, +    mouse, overlay, Color, Font, HorizontalAlignment, Padding, Point, +    Rectangle, VerticalAlignment,  };  pub use iced_style::menu::Style; @@ -45,7 +45,7 @@ where          viewport: &Rectangle,          options: &[T],          hovered_option: Option<usize>, -        padding: u16, +        padding: Padding,          text_size: u16,          font: Font,          style: &Style, @@ -53,7 +53,7 @@ where          use std::f32;          let is_mouse_over = bounds.contains(cursor_position); -        let option_height = text_size as usize + padding as usize * 2; +        let option_height = (text_size + padding.vertical()) as usize;          let mut primitives = Vec::new(); @@ -72,7 +72,7 @@ where                  x: bounds.x,                  y: bounds.y + (option_height * i) as f32,                  width: bounds.width, -                height: f32::from(text_size + padding * 2), +                height: f32::from(text_size + padding.vertical()),              };              if is_selected { @@ -88,7 +88,7 @@ where              primitives.push(Primitive::Text {                  content: option.to_string(),                  bounds: Rectangle { -                    x: bounds.x + f32::from(padding), +                    x: bounds.x + padding.left as f32,                      y: bounds.center_y(),                      width: f32::INFINITY,                      ..bounds diff --git a/graphics/src/viewport.rs b/graphics/src/viewport.rs index 78d539af..2c0b541a 100644 --- a/graphics/src/viewport.rs +++ b/graphics/src/viewport.rs @@ -31,7 +31,7 @@ impl Viewport {      /// Returns the physical width of the [`Viewport`].      pub fn physical_width(&self) -> u32 { -        self.physical_size.height +        self.physical_size.width      }      /// Returns the physical height of the [`Viewport`]. diff --git a/graphics/src/widget.rs b/graphics/src/widget.rs index 159ca91b..e34d267f 100644 --- a/graphics/src/widget.rs +++ b/graphics/src/widget.rs @@ -20,6 +20,8 @@ pub mod scrollable;  pub mod slider;  pub mod svg;  pub mod text_input; +pub mod toggler; +pub mod tooltip;  mod column;  mod row; @@ -48,6 +50,10 @@ pub use scrollable::Scrollable;  pub use slider::Slider;  #[doc(no_inline)]  pub use text_input::TextInput; +#[doc(no_inline)] +pub use toggler::Toggler; +#[doc(no_inline)] +pub use tooltip::Tooltip;  pub use column::Column;  pub use image::Image; diff --git a/graphics/src/widget/button.rs b/graphics/src/widget/button.rs index 2e3f78ca..60400ed8 100644 --- a/graphics/src/widget/button.rs +++ b/graphics/src/widget/button.rs @@ -5,7 +5,7 @@ use crate::defaults::{self, Defaults};  use crate::{Backend, Primitive, Renderer};  use iced_native::mouse;  use iced_native::{ -    Background, Color, Element, Layout, Point, Rectangle, Vector, +    Background, Color, Element, Layout, Padding, Point, Rectangle, Vector,  };  pub use iced_native::button::State; @@ -21,7 +21,7 @@ impl<B> iced_native::button::Renderer for Renderer<B>  where      B: Backend,  { -    const DEFAULT_PADDING: u16 = 5; +    const DEFAULT_PADDING: Padding = Padding::new(5);      type Style = Box<dyn StyleSheet>; diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 95ede50f..7897c8ec 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -154,9 +154,9 @@ where          event: iced_native::Event,          layout: Layout<'_>,          cursor_position: Point, -        messages: &mut Vec<Message>,          _renderer: &Renderer<B>, -        _clipboard: Option<&dyn Clipboard>, +        _clipboard: &mut dyn Clipboard, +        messages: &mut Vec<Message>,      ) -> event::Status {          let bounds = layout.bounds(); diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index b86f9e04..5af9d11f 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -54,7 +54,7 @@ impl Frame {          self.size.width      } -    /// Returns the width of the [`Frame`]. +    /// Returns the height of the [`Frame`].      #[inline]      pub fn height(&self) -> f32 {          self.size.height diff --git a/graphics/src/widget/canvas/program.rs b/graphics/src/widget/canvas/program.rs index d703caad..85a2f67b 100644 --- a/graphics/src/widget/canvas/program.rs +++ b/graphics/src/widget/canvas/program.rs @@ -34,7 +34,7 @@ pub trait Program<Message> {      /// [`Geometry`] can be easily generated with a [`Frame`] or stored in a      /// [`Cache`].      /// -    /// [`Frame`]: crate::widget::canvas::Cache +    /// [`Frame`]: crate::widget::canvas::Frame      /// [`Cache`]: crate::widget::canvas::Cache      fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry>; diff --git a/graphics/src/widget/image.rs b/graphics/src/widget/image.rs index 30f446e8..bdf03de3 100644 --- a/graphics/src/widget/image.rs +++ b/graphics/src/widget/image.rs @@ -1,11 +1,14 @@  //! Display images in your user interface. +pub mod viewer; +  use crate::backend::{self, Backend}; +  use crate::{Primitive, Renderer};  use iced_native::image;  use iced_native::mouse;  use iced_native::Layout; -pub use iced_native::image::{Handle, Image}; +pub use iced_native::image::{Handle, Image, Viewer};  impl<B> image::Renderer for Renderer<B>  where diff --git a/graphics/src/widget/image/viewer.rs b/graphics/src/widget/image/viewer.rs new file mode 100644 index 00000000..28dffc4f --- /dev/null +++ b/graphics/src/widget/image/viewer.rs @@ -0,0 +1,55 @@ +//! Zoom and pan on an image. +use crate::backend::{self, Backend}; +use crate::{Primitive, Renderer}; + +use iced_native::image; +use iced_native::image::viewer; +use iced_native::mouse; +use iced_native::{Rectangle, Size, Vector}; + +impl<B> viewer::Renderer for Renderer<B> +where +    B: Backend + backend::Image, +{ +    fn draw( +        &mut self, +        state: &viewer::State, +        bounds: Rectangle, +        image_size: Size, +        translation: Vector, +        handle: image::Handle, +        is_mouse_over: bool, +    ) -> Self::Output { +        ( +            { +                Primitive::Clip { +                    bounds, +                    content: Box::new(Primitive::Translate { +                        translation, +                        content: Box::new(Primitive::Image { +                            handle, +                            bounds: Rectangle { +                                x: bounds.x, +                                y: bounds.y, +                                ..Rectangle::with_size(image_size) +                            }, +                        }), +                    }), +                    offset: Vector::new(0, 0), +                } +            }, +            { +                if state.is_cursor_grabbed() { +                    mouse::Interaction::Grabbing +                } else if is_mouse_over +                    && (image_size.width > bounds.width +                        || image_size.height > bounds.height) +                { +                    mouse::Interaction::Grab +                } else { +                    mouse::Interaction::Idle +                } +            }, +        ) +    } +} diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index f09984fc..92cdbb77 100644 --- a/graphics/src/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -6,23 +6,21 @@  //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,  //! drag and drop, and hotkey support.  //! -//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid -use crate::backend::{self, Backend}; +//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid  use crate::defaults; -use crate::{Primitive, Renderer}; +use crate::{Backend, Color, Primitive, Renderer}; +use iced_native::container;  use iced_native::mouse;  use iced_native::pane_grid; -use iced_native::text; -use iced_native::{ -    Element, HorizontalAlignment, Layout, Point, Rectangle, Vector, -    VerticalAlignment, -}; +use iced_native::{Element, Layout, Point, Rectangle, Vector};  pub use iced_native::pane_grid::{ -    Axis, Configuration, Content, Direction, DragEvent, Pane, ResizeEvent, -    Split, State, TitleBar, +    Axis, Configuration, Content, Direction, DragEvent, Node, Pane, +    ResizeEvent, Split, State, TitleBar,  }; +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.  /// @@ -34,16 +32,20 @@ pub type PaneGrid<'a, Message, Backend> =  impl<B> pane_grid::Renderer for Renderer<B>  where -    B: Backend + backend::Text, +    B: Backend,  { +    type Style = Box<dyn StyleSheet>; +      fn draw<Message>(          &mut self,          defaults: &Self::Defaults,          content: &[(Pane, Content<'_, Message, Self>)],          dragging: Option<(Pane, Point)>, -        resizing: Option<Axis>, +        resizing: Option<(Axis, Rectangle, bool)>,          layout: Layout<'_>, +        style_sheet: &<Self as pane_grid::Renderer>::Style,          cursor_position: Point, +        viewport: &Rectangle,      ) -> Self::Output {          let pane_cursor_position = if dragging.is_some() {              // TODO: Remove once cursor availability is encoded in the type @@ -61,8 +63,13 @@ where              .zip(layout.children())              .enumerate()              .map(|(i, ((id, pane), layout))| { -                let (primitive, new_mouse_interaction) = -                    pane.draw(self, defaults, layout, pane_cursor_position); +                let (primitive, new_mouse_interaction) = pane.draw( +                    self, +                    defaults, +                    layout, +                    pane_cursor_position, +                    viewport, +                );                  if new_mouse_interaction > mouse_interaction {                      mouse_interaction = new_mouse_interaction; @@ -78,7 +85,8 @@ where              })              .collect(); -        let primitives = if let Some((index, layout, origin)) = dragged_pane { +        let mut primitives = if let Some((index, layout, origin)) = dragged_pane +        {              let pane = panes.remove(index);              let bounds = layout.bounds(); @@ -108,15 +116,62 @@ where              panes          }; +        let (primitives, mouse_interaction) = +            if let Some((axis, split_region, is_picked)) = resizing { +                let highlight = if is_picked { +                    style_sheet.picked_split() +                } else { +                    style_sheet.hovered_split() +                }; + +                if let Some(highlight) = highlight { +                    primitives.push(Primitive::Quad { +                        bounds: match axis { +                            Axis::Horizontal => Rectangle { +                                x: split_region.x, +                                y: (split_region.y +                                    + (split_region.height - highlight.width) +                                        / 2.0) +                                    .round(), +                                width: split_region.width, +                                height: highlight.width, +                            }, +                            Axis::Vertical => Rectangle { +                                x: (split_region.x +                                    + (split_region.width - highlight.width) +                                        / 2.0) +                                    .round(), +                                y: split_region.y, +                                width: highlight.width, +                                height: split_region.height, +                            }, +                        }, +                        background: highlight.color.into(), +                        border_radius: 0.0, +                        border_width: 0.0, +                        border_color: Color::TRANSPARENT, +                    }); +                } + +                ( +                    primitives, +                    match axis { +                        Axis::Horizontal => { +                            mouse::Interaction::ResizingVertically +                        } +                        Axis::Vertical => { +                            mouse::Interaction::ResizingHorizontally +                        } +                    }, +                ) +            } else { +                (primitives, mouse_interaction) +            }; +          (              Primitive::Group { primitives },              if dragging.is_some() {                  mouse::Interaction::Grabbing -            } else if let Some(axis) = resizing { -                match axis { -                    Axis::Horizontal => mouse::Interaction::ResizingVertically, -                    Axis::Vertical => mouse::Interaction::ResizingHorizontally, -                }              } else {                  mouse_interaction              }, @@ -127,16 +182,17 @@ where          &mut self,          defaults: &Self::Defaults,          bounds: Rectangle, -        style_sheet: &Self::Style, +        style_sheet: &<Self as container::Renderer>::Style,          title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>,          body: (&Element<'_, Message, Self>, Layout<'_>),          cursor_position: Point, +        viewport: &Rectangle,      ) -> Self::Output {          let style = style_sheet.style();          let (body, body_layout) = body;          let (body_primitive, body_interaction) = -            body.draw(self, defaults, body_layout, cursor_position, &bounds); +            body.draw(self, defaults, body_layout, cursor_position, viewport);          let background = crate::widget::container::background(bounds, &style); @@ -150,6 +206,7 @@ where                  defaults,                  title_bar_layout,                  cursor_position, +                viewport,                  show_controls,              ); @@ -161,10 +218,10 @@ where                          body_primitive,                      ],                  }, -                if is_over_pick_area { -                    mouse::Interaction::Grab -                } else if title_bar_interaction > body_interaction { +                if title_bar_interaction > body_interaction {                      title_bar_interaction +                } else if is_over_pick_area { +                    mouse::Interaction::Grab                  } else {                      body_interaction                  }, @@ -187,15 +244,14 @@ where          &mut self,          defaults: &Self::Defaults,          bounds: Rectangle, -        style_sheet: &Self::Style, -        title: &str, -        title_size: u16, -        title_font: Self::Font, -        title_bounds: Rectangle, +        style_sheet: &<Self as container::Renderer>::Style, +        content: (&Element<'_, Message, Self>, Layout<'_>),          controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>,          cursor_position: Point, +        viewport: &Rectangle,      ) -> Self::Output {          let style = style_sheet.style(); +        let (title_content, title_layout) = content;          let defaults = Self::Defaults {              text: defaults::Text { @@ -205,16 +261,12 @@ where          let background = crate::widget::container::background(bounds, &style); -        let (title_primitive, _) = text::Renderer::draw( +        let (title_primitive, title_interaction) = title_content.draw(              self,              &defaults, -            title_bounds, -            title, -            title_size, -            title_font, -            None, -            HorizontalAlignment::Left, -            VerticalAlignment::Top, +            title_layout, +            cursor_position, +            viewport,          );          if let Some((controls, controls_layout)) = controls { @@ -223,7 +275,7 @@ where                  &defaults,                  controls_layout,                  cursor_position, -                &bounds, +                viewport,              );              ( @@ -234,7 +286,7 @@ where                          controls_primitive,                      ],                  }, -                controls_interaction, +                controls_interaction.max(title_interaction),              )          } else {              ( @@ -245,7 +297,7 @@ where                  } else {                      title_primitive                  }, -                mouse::Interaction::default(), +                title_interaction,              )          }      } diff --git a/graphics/src/widget/pick_list.rs b/graphics/src/widget/pick_list.rs index f42a8707..88a590b5 100644 --- a/graphics/src/widget/pick_list.rs +++ b/graphics/src/widget/pick_list.rs @@ -2,7 +2,8 @@  use crate::backend::{self, Backend};  use crate::{Primitive, Renderer};  use iced_native::{ -    mouse, Font, HorizontalAlignment, Point, Rectangle, VerticalAlignment, +    mouse, Font, HorizontalAlignment, Padding, Point, Rectangle, +    VerticalAlignment,  };  use iced_style::menu; @@ -19,7 +20,7 @@ where  {      type Style = Box<dyn StyleSheet>; -    const DEFAULT_PADDING: u16 = 5; +    const DEFAULT_PADDING: Padding = Padding::new(5);      fn menu_style(style: &Box<dyn StyleSheet>) -> menu::Style {          style.menu() @@ -30,12 +31,14 @@ where          bounds: Rectangle,          cursor_position: Point,          selected: Option<String>, -        padding: u16, +        placeholder: Option<&str>, +        padding: Padding,          text_size: u16,          font: Font,          style: &Box<dyn StyleSheet>,      ) -> Self::Output {          let is_mouse_over = bounds.contains(cursor_position); +        let is_selected = selected.is_some();          let style = if is_mouse_over {              style.hovered() @@ -56,7 +59,7 @@ where              font: B::ICON_FONT,              size: bounds.height * style.icon_size,              bounds: Rectangle { -                x: bounds.x + bounds.width - f32::from(padding) * 2.0, +                x: bounds.x + bounds.width - f32::from(padding.horizontal()),                  y: bounds.center_y(),                  ..bounds              }, @@ -67,14 +70,18 @@ where          (              Primitive::Group { -                primitives: if let Some(label) = selected { +                primitives: if let Some(label) = +                    selected.or_else(|| placeholder.map(str::to_string)) +                {                      let label = Primitive::Text {                          content: label,                          size: f32::from(text_size),                          font, -                        color: style.text_color, +                        color: is_selected +                            .then(|| style.text_color) +                            .unwrap_or(style.placeholder_color),                          bounds: Rectangle { -                            x: bounds.x + f32::from(padding), +                            x: bounds.x + f32::from(padding.left),                              y: bounds.center_y(),                              ..bounds                          }, diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index 57065ba2..2220e4b8 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -134,8 +134,16 @@ where                      Primitive::None                  }; +                let scroll = Primitive::Clip { +                    bounds, +                    offset: Vector::new(0, 0), +                    content: Box::new(Primitive::Group { +                        primitives: vec![scrollbar, scroller], +                    }), +                }; +                  Primitive::Group { -                    primitives: vec![clip, scrollbar, scroller], +                    primitives: vec![clip, scroll],                  }              } else {                  content diff --git a/graphics/src/widget/toggler.rs b/graphics/src/widget/toggler.rs new file mode 100644 index 00000000..852d18ee --- /dev/null +++ b/graphics/src/widget/toggler.rs @@ -0,0 +1,99 @@ +//! Show toggle controls using togglers. +use crate::backend::{self, Backend}; +use crate::{Primitive, Renderer}; +use iced_native::mouse; +use iced_native::toggler; +use iced_native::Rectangle; + +pub use iced_style::toggler::{Style, StyleSheet}; + +/// Makes sure that the border radius of the toggler looks good at every size. +const BORDER_RADIUS_RATIO: f32 = 32.0 / 13.0; + +/// The space ratio between the background Quad and the Toggler bounds, and +/// between the background Quad and foreground Quad. +const SPACE_RATIO: f32 = 0.05; + +/// A toggler that can be toggled. +/// +/// This is an alias of an `iced_native` toggler with an `iced_wgpu::Renderer`. +pub type Toggler<Message, Backend> = +    iced_native::Toggler<Message, Renderer<Backend>>; + +impl<B> toggler::Renderer for Renderer<B> +where +    B: Backend + backend::Text, +{ +    type Style = Box<dyn StyleSheet>; + +    const DEFAULT_SIZE: u16 = 20; + +    fn draw( +        &mut self, +        bounds: Rectangle, +        is_active: bool, +        is_mouse_over: bool, +        label: Option<Self::Output>, +        style_sheet: &Self::Style, +    ) -> Self::Output { +        let style = if is_mouse_over { +            style_sheet.hovered(is_active) +        } else { +            style_sheet.active(is_active) +        }; + +        let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO; +        let space = SPACE_RATIO * bounds.height as f32; + +        let toggler_background_bounds = Rectangle { +            x: bounds.x + space, +            y: bounds.y + space, +            width: bounds.width - (2.0 * space), +            height: bounds.height - (2.0 * space), +        }; + +        let toggler_background = Primitive::Quad { +            bounds: toggler_background_bounds, +            background: style.background.into(), +            border_radius, +            border_width: 1.0, +            border_color: style.background_border.unwrap_or(style.background), +        }; + +        let toggler_foreground_bounds = Rectangle { +            x: bounds.x +                + if is_active { +                    bounds.width - 2.0 * space - (bounds.height - (4.0 * space)) +                } else { +                    2.0 * space +                }, +            y: bounds.y + (2.0 * space), +            width: bounds.height - (4.0 * space), +            height: bounds.height - (4.0 * space), +        }; + +        let toggler_foreground = Primitive::Quad { +            bounds: toggler_foreground_bounds, +            background: style.foreground.into(), +            border_radius, +            border_width: 1.0, +            border_color: style.foreground_border.unwrap_or(style.foreground), +        }; + +        ( +            Primitive::Group { +                primitives: match label { +                    Some((l, _)) => { +                        vec![l, toggler_background, toggler_foreground] +                    } +                    None => vec![toggler_background, toggler_foreground], +                }, +            }, +            if is_mouse_over { +                mouse::Interaction::Pointer +            } else { +                mouse::Interaction::default() +            }, +        ) +    } +} diff --git a/graphics/src/widget/tooltip.rs b/graphics/src/widget/tooltip.rs new file mode 100644 index 00000000..493a6389 --- /dev/null +++ b/graphics/src/widget/tooltip.rs @@ -0,0 +1,168 @@ +//! Decorate content and apply alignment. +use crate::backend::{self, Backend}; +use crate::defaults::{self, Defaults}; +use crate::{Primitive, Renderer, Vector}; + +use iced_native::container; +use iced_native::layout::{self, Layout}; +use iced_native::{Element, Padding, Point, Rectangle, Size, Text}; + +/// An element decorating some content. +/// +/// This is an alias of an `iced_native` tooltip with a default +/// `Renderer`. +pub type Tooltip<'a, Message, Backend> = +    iced_native::Tooltip<'a, Message, Renderer<Backend>>; + +pub use iced_native::tooltip::Position; + +impl<B> iced_native::tooltip::Renderer for Renderer<B> +where +    B: Backend + backend::Text, +{ +    const DEFAULT_PADDING: u16 = 5; + +    fn draw<Message>( +        &mut self, +        defaults: &Defaults, +        cursor_position: Point, +        content_layout: Layout<'_>, +        viewport: &Rectangle, +        content: &Element<'_, Message, Self>, +        tooltip: &Text<Self>, +        position: Position, +        style_sheet: &<Self as container::Renderer>::Style, +        gap: u16, +        padding: u16, +    ) -> Self::Output { +        let (content, mouse_interaction) = content.draw( +            self, +            &defaults, +            content_layout, +            cursor_position, +            viewport, +        ); + +        let bounds = content_layout.bounds(); + +        if bounds.contains(cursor_position) { +            use iced_native::Widget; + +            let gap = f32::from(gap); +            let style = style_sheet.style(); + +            let defaults = Defaults { +                text: defaults::Text { +                    color: style.text_color.unwrap_or(defaults.text.color), +                }, +            }; + +            let text_layout = Widget::<(), Self>::layout( +                tooltip, +                self, +                &layout::Limits::new(Size::ZERO, viewport.size()) +                    .pad(Padding::new(padding)), +            ); + +            let padding = f32::from(padding); +            let text_bounds = text_layout.bounds(); +            let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0; +            let y_center = +                bounds.y + (bounds.height - text_bounds.height) / 2.0; + +            let mut tooltip_bounds = { +                let offset = match position { +                    Position::Top => Vector::new( +                        x_center, +                        bounds.y - text_bounds.height - gap - padding, +                    ), +                    Position::Bottom => Vector::new( +                        x_center, +                        bounds.y + bounds.height + gap + padding, +                    ), +                    Position::Left => Vector::new( +                        bounds.x - text_bounds.width - gap - padding, +                        y_center, +                    ), +                    Position::Right => Vector::new( +                        bounds.x + bounds.width + gap + padding, +                        y_center, +                    ), +                    Position::FollowCursor => Vector::new( +                        cursor_position.x, +                        cursor_position.y - text_bounds.height, +                    ), +                }; + +                Rectangle { +                    x: offset.x - padding, +                    y: offset.y - padding, +                    width: text_bounds.width + padding * 2.0, +                    height: text_bounds.height + padding * 2.0, +                } +            }; + +            if tooltip_bounds.x < viewport.x { +                tooltip_bounds.x = viewport.x; +            } else if viewport.x + viewport.width +                < tooltip_bounds.x + tooltip_bounds.width +            { +                tooltip_bounds.x = +                    viewport.x + viewport.width - tooltip_bounds.width; +            } + +            if tooltip_bounds.y < viewport.y { +                tooltip_bounds.y = viewport.y; +            } else if viewport.y + viewport.height +                < tooltip_bounds.y + tooltip_bounds.height +            { +                tooltip_bounds.y = +                    viewport.y + viewport.height - tooltip_bounds.height; +            } + +            let (tooltip, _) = Widget::<(), Self>::draw( +                tooltip, +                self, +                &defaults, +                Layout::with_offset( +                    Vector::new( +                        tooltip_bounds.x + padding, +                        tooltip_bounds.y + padding, +                    ), +                    &text_layout, +                ), +                cursor_position, +                viewport, +            ); + +            ( +                Primitive::Group { +                    primitives: vec![ +                        content, +                        Primitive::Clip { +                            bounds: *viewport, +                            offset: Vector::new(0, 0), +                            content: Box::new( +                                if let Some(background) = +                                    crate::container::background( +                                        tooltip_bounds, +                                        &style, +                                    ) +                                { +                                    Primitive::Group { +                                        primitives: vec![background, tooltip], +                                    } +                                } else { +                                    tooltip +                                }, +                            ), +                        }, +                    ], +                }, +                mouse_interaction, +            ) +        } else { +            (content, mouse_interaction) +        } +    } +} diff --git a/graphics/src/window/compositor.rs b/graphics/src/window/compositor.rs index 39485153..7342245c 100644 --- a/graphics/src/window/compositor.rs +++ b/graphics/src/window/compositor.rs @@ -17,7 +17,10 @@ pub trait Compositor: Sized {      type SwapChain;      /// Creates a new [`Compositor`]. -    fn new(settings: Self::Settings) -> Result<(Self, Self::Renderer), Error>; +    fn new<W: HasRawWindowHandle>( +        settings: Self::Settings, +        compatible_window: Option<&W>, +    ) -> Result<(Self, Self::Renderer), Error>;      /// Crates a new [`Surface`] for the given window.      /// | 
