summaryrefslogtreecommitdiffstats
path: root/graphics/src
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/src')
-rw-r--r--graphics/src/backend.rs7
-rw-r--r--graphics/src/font.rs6
-rw-r--r--graphics/src/lib.rs5
-rw-r--r--graphics/src/overlay.rs1
-rw-r--r--graphics/src/overlay/menu.rs107
-rw-r--r--graphics/src/renderer.rs35
-rw-r--r--graphics/src/widget.rs1
-rw-r--r--graphics/src/widget/canvas.rs2
-rw-r--r--graphics/src/widget/combo_box.rs96
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()
+ },
+ )
+ }
+}