Merge pull request #1830 from iced-rs/advanced-text

Advanced text
This commit is contained in:
Héctor Ramón 2023-05-11 16:45:08 +02:00 committed by GitHub
commit 669f7cc74b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
321 changed files with 8736 additions and 9553 deletions

View file

@ -37,5 +37,5 @@ jobs:
run: cargo build --package tour --target wasm32-unknown-unknown run: cargo build --package tour --target wasm32-unknown-unknown
- name: Check compilation of `todos` example - name: Check compilation of `todos` example
run: cargo build --package todos --target wasm32-unknown-unknown run: cargo build --package todos --target wasm32-unknown-unknown
- name: Check compilation of `integration_wgpu` example - name: Check compilation of `integration` example
run: cargo build --package integration_wgpu --target wasm32-unknown-unknown run: cargo build --package integration --target wasm32-unknown-unknown

View file

@ -13,20 +13,18 @@ categories = ["gui"]
[features] [features]
default = ["wgpu"] default = ["wgpu"]
# Enable the `wgpu` GPU-accelerated renderer backend
wgpu = ["iced_renderer/wgpu"]
# Enables the `Image` widget # Enables the `Image` widget
image = ["iced_wgpu?/image", "iced_glow?/image", "image_rs"] image = ["iced_widget/image", "image_rs"]
# Enables the `Svg` widget # Enables the `Svg` widget
svg = ["iced_wgpu?/svg", "iced_glow?/svg"] svg = ["iced_widget/svg"]
# Enables the `Canvas` widget # Enables the `Canvas` widget
canvas = ["iced_graphics/canvas"] canvas = ["iced_widget/canvas"]
# Enables the `QRCode` widget # Enables the `QRCode` widget
qr_code = ["iced_graphics/qr_code"] qr_code = ["iced_widget/qr_code"]
# Enables the `iced_wgpu` renderer # Enables lazy widgets
wgpu = ["iced_wgpu"] lazy = ["iced_widget/lazy"]
# Enables using system fonts
default_system_font = ["iced_wgpu?/default_system_font", "iced_glow?/default_system_font"]
# Enables the `iced_glow` renderer. Overrides `iced_wgpu`
glow = ["iced_glow", "iced_glutin"]
# Enables a debug view in native platforms (press F12) # Enables a debug view in native platforms (press F12)
debug = ["iced_winit/debug"] debug = ["iced_winit/debug"]
# Enables `tokio` as the `executor::Default` on native platforms # Enables `tokio` as the `executor::Default` on native platforms
@ -39,13 +37,8 @@ smol = ["iced_futures/smol"]
palette = ["iced_core/palette"] palette = ["iced_core/palette"]
# Enables querying system information # Enables querying system information
system = ["iced_winit/system"] system = ["iced_winit/system"]
# Enables chrome traces # Enables the advanced module
chrome-trace = [ advanced = []
"iced_winit/chrome-trace",
"iced_glutin?/trace",
"iced_wgpu?/tracing",
"iced_glow?/tracing",
]
[badges] [badges]
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
@ -55,12 +48,12 @@ members = [
"core", "core",
"futures", "futures",
"graphics", "graphics",
"glow", "runtime",
"glutin", "renderer",
"lazy",
"native",
"style", "style",
"tiny_skia",
"wgpu", "wgpu",
"widget",
"winit", "winit",
"examples/*", "examples/*",
] ]
@ -68,24 +61,16 @@ members = [
[dependencies] [dependencies]
iced_core = { version = "0.9", path = "core" } iced_core = { version = "0.9", path = "core" }
iced_futures = { version = "0.6", path = "futures" } iced_futures = { version = "0.6", path = "futures" }
iced_native = { version = "0.10", path = "native" } iced_renderer = { version = "0.1", path = "renderer" }
iced_graphics = { version = "0.8", path = "graphics" } iced_widget = { version = "0.1", path = "widget" }
iced_winit = { version = "0.9", path = "winit", features = ["application"] } iced_winit = { version = "0.9", path = "winit", features = ["application"] }
iced_glutin = { version = "0.8", path = "glutin", optional = true } thiserror = "1"
iced_glow = { version = "0.8", path = "glow", optional = true }
thiserror = "1.0"
[dependencies.image_rs] [dependencies.image_rs]
version = "0.24" version = "0.24"
package = "image" package = "image"
optional = true optional = true
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
iced_wgpu = { version = "0.10", path = "wgpu", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced_wgpu = { version = "0.10", path = "wgpu", features = ["webgl"], optional = true }
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
features = ["image", "svg", "canvas", "qr_code"] features = ["image", "svg", "canvas", "qr_code"]

View file

@ -35,9 +35,9 @@ Inspired by [Elm].
* First-class support for async actions (use futures!) * First-class support for async actions (use futures!)
* [Modular ecosystem] split into reusable parts: * [Modular ecosystem] split into reusable parts:
* A [renderer-agnostic native runtime] enabling integration with existing systems * A [renderer-agnostic native runtime] enabling integration with existing systems
* Two [built-in renderers] leveraging [`wgpu`] and [`glow`] * Two [built-in renderers] leveraging [`wgpu`] and [`tiny-skia`]
* [`iced_wgpu`] supporting Vulkan, Metal and DX12 * [`iced_wgpu`] supporting Vulkan, Metal and DX12
* [`iced_glow`] supporting OpenGL 2.1+ and OpenGL ES 2.0+ * [`iced_tiny_skia`] offering a software alternative as a fallback
* A [windowing shell] * A [windowing shell]
* A [web runtime] leveraging the DOM * A [web runtime] leveraging the DOM
@ -52,9 +52,9 @@ __Iced is currently experimental software.__ [Take a look at the roadmap],
[Modular ecosystem]: ECOSYSTEM.md [Modular ecosystem]: ECOSYSTEM.md
[renderer-agnostic native runtime]: native/ [renderer-agnostic native runtime]: native/
[`wgpu`]: https://github.com/gfx-rs/wgpu [`wgpu`]: https://github.com/gfx-rs/wgpu
[`glow`]: https://github.com/grovesNL/glow [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia
[`iced_wgpu`]: wgpu/ [`iced_wgpu`]: wgpu/
[`iced_glow`]: glow/ [`iced_tiny_skia`]: tiny_skia/
[built-in renderers]: ECOSYSTEM.md#Renderers [built-in renderers]: ECOSYSTEM.md#Renderers
[windowing shell]: winit/ [windowing shell]: winit/
[`dodrio`]: https://github.com/fitzgen/dodrio [`dodrio`]: https://github.com/fitzgen/dodrio
@ -196,34 +196,6 @@ end-user-oriented GUI library, while keeping [the ecosystem] modular:
[`ggez`]: https://github.com/ggez/ggez [`ggez`]: https://github.com/ggez/ggez
[the ecosystem]: ECOSYSTEM.md [the ecosystem]: ECOSYSTEM.md
## Troubleshooting
### `GraphicsAdapterNotFound`
This occurs when the selected [built-in renderer] is not able to create a context.
Often this will occur while using [`iced_wgpu`] as the renderer without
supported hardware (needs Vulkan, Metal or DX12). In this case, you could try using the
[`iced_glow`] renderer:
First, check if it works with
```console
cargo run --features iced/glow --package game_of_life
```
and then use it in your project with
```toml
iced = { version = "0.9", default-features = false, features = ["glow"] }
```
__NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0,
but if you don't, right now there's no software fallback, so it means your hardware
doesn't support Iced.
[built-in renderer]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md#Renderers
## Contributing / Feedback ## Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please Contributions are greatly appreciated! If you want to contribute, please

View file

@ -9,6 +9,8 @@ repository = "https://github.com/iced-rs/iced"
[dependencies] [dependencies]
bitflags = "1.2" bitflags = "1.2"
thiserror = "1"
twox-hash = { version = "1.5", default-features = false }
[dependencies.palette] [dependencies.palette]
version = "0.6" version = "0.6"

23
core/src/clipboard.rs Normal file
View file

@ -0,0 +1,23 @@
//! Access the clipboard.
/// A buffer for short-term storage and transfer within and between
/// applications.
pub trait Clipboard {
/// Reads the current content of the [`Clipboard`] as text.
fn read(&self) -> Option<String>;
/// Writes the given text contents to the [`Clipboard`].
fn write(&mut self, contents: String);
}
/// A null implementation of the [`Clipboard`] trait.
#[derive(Debug, Clone, Copy)]
pub struct Null;
impl Clipboard for Null {
fn read(&self) -> Option<String> {
None
}
fn write(&mut self, _contents: String) {}
}

View file

@ -90,41 +90,65 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// We compose the previous __messages__ with the index of the counter /// We compose the previous __messages__ with the index of the counter
/// producing them. Let's implement our __view logic__ now: /// producing them. Let's implement our __view logic__ now:
/// ///
/// ``` /// ```no_run
/// # mod counter { /// # mod counter {
/// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
/// #
/// # #[derive(Debug, Clone, Copy)] /// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {} /// # pub enum Message {}
/// # pub struct Counter; /// # pub struct Counter;
/// # /// #
/// # impl Counter { /// # impl Counter {
/// # pub fn view(&mut self) -> Text { /// # pub fn view(
/// # Text::new("") /// # &self,
/// # ) -> iced_core::Element<Message, iced_core::renderer::Null> {
/// # unimplemented!()
/// # } /// # }
/// # } /// # }
/// # } /// # }
/// # /// #
/// # mod iced_wgpu { /// # mod iced {
/// # pub use iced_native::renderer::Null as Renderer; /// # pub use iced_core::renderer::Null as Renderer;
/// # pub use iced_core::Element;
/// #
/// # pub mod widget {
/// # pub struct Row<Message> {
/// # _t: std::marker::PhantomData<Message>,
/// # }
/// #
/// # impl<Message> Row<Message> {
/// # pub fn new() -> Self {
/// # unimplemented!()
/// # }
/// #
/// # pub fn spacing(mut self, _: u32) -> Self {
/// # unimplemented!()
/// # }
/// #
/// # pub fn push(
/// # mut self,
/// # _: iced_core::Element<Message, iced_core::renderer::Null>,
/// # ) -> Self {
/// # unimplemented!()
/// # }
/// # }
/// # }
/// # } /// # }
/// # /// #
/// # use counter::Counter; /// use counter::Counter;
/// # ///
/// # struct ManyCounters { /// use iced::widget::Row;
/// # counters: Vec<Counter>, /// use iced::{Element, Renderer};
/// # } ///
/// # /// struct ManyCounters {
/// # #[derive(Debug, Clone, Copy)] /// counters: Vec<Counter>,
/// # pub enum Message { /// }
/// # Counter(usize, counter::Message) ///
/// # } /// #[derive(Debug, Clone, Copy)]
/// use iced_native::Element; /// pub enum Message {
/// use iced_native::widget::Row; /// Counter(usize, counter::Message),
/// use iced_wgpu::Renderer; /// }
/// ///
/// impl ManyCounters { /// impl ManyCounters {
/// pub fn view(&mut self) -> Row<Message, Renderer> { /// pub fn view(&mut self) -> Row<Message> {
/// // We can quickly populate a `Row` by folding over our counters /// // We can quickly populate a `Row` by folding over our counters
/// self.counters.iter_mut().enumerate().fold( /// self.counters.iter_mut().enumerate().fold(
/// Row::new().spacing(20), /// Row::new().spacing(20),
@ -137,9 +161,10 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// // Here we turn our `Element<counter::Message>` into /// // Here we turn our `Element<counter::Message>` into
/// // an `Element<Message>` by combining the `index` and the /// // an `Element<Message>` by combining the `index` and the
/// // message of the `element`. /// // message of the `element`.
/// element.map(move |message| Message::Counter(index, message)) /// element
/// .map(move |message| Message::Counter(index, message)),
/// ) /// )
/// } /// },
/// ) /// )
/// } /// }
/// } /// }

View file

@ -62,7 +62,7 @@ impl Status {
/// `Captured` takes precedence over `Ignored`: /// `Captured` takes precedence over `Ignored`:
/// ///
/// ``` /// ```
/// use iced_native::event::Status; /// use iced_core::event::Status;
/// ///
/// assert_eq!(Status::Ignored.merge(Status::Ignored), Status::Ignored); /// assert_eq!(Status::Ignored.merge(Status::Ignored), Status::Ignored);
/// assert_eq!(Status::Ignored.merge(Status::Captured), Status::Captured); /// assert_eq!(Status::Ignored.merge(Status::Captured), Status::Captured);

View file

@ -1,19 +1,102 @@
//! Load and use fonts.
use std::hash::Hash;
/// A font. /// A font.
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Font { pub struct Font {
/// The default font. /// The [`Family`] of the [`Font`].
/// pub family: Family,
/// This is normally a font configured in a renderer or loaded from the /// The [`Weight`] of the [`Font`].
/// system. pub weight: Weight,
#[default] /// The [`Stretch`] of the [`Font`].
Default, pub stretch: Stretch,
/// Whether if the [`Font`] is monospaced or not.
/// An external font. pub monospaced: bool,
External { }
/// The name of the external font
name: &'static str, impl Font {
/// A non-monospaced sans-serif font with normal [`Weight`].
/// The bytes of the external font pub const DEFAULT: Font = Font {
bytes: &'static [u8], family: Family::SansSerif,
}, weight: Weight::Normal,
stretch: Stretch::Normal,
monospaced: false,
};
/// A monospaced font with normal [`Weight`].
pub const MONOSPACE: Font = Font {
family: Family::Monospace,
monospaced: true,
..Self::DEFAULT
};
/// Creates a non-monospaced [`Font`] with the given [`Family::Name`] and
/// normal [`Weight`].
pub const fn with_name(name: &'static str) -> Self {
Font {
family: Family::Name(name),
..Self::DEFAULT
}
}
}
/// A font family.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Family {
/// The name of a font family of choice.
Name(&'static str),
/// Serif fonts represent the formal text style for a script.
Serif,
/// Glyphs in sans-serif fonts, as the term is used in CSS, are generally low
/// contrast and have stroke endings that are plain — without any flaring,
/// cross stroke, or other ornamentation.
#[default]
SansSerif,
/// Glyphs in cursive fonts generally use a more informal script style, and
/// the result looks more like handwritten pen or brush writing than printed
/// letterwork.
Cursive,
/// Fantasy fonts are primarily decorative or expressive fonts that contain
/// decorative or expressive representations of characters.
Fantasy,
/// The sole criterion of a monospace font is that all glyphs have the same
/// fixed width.
Monospace,
}
/// The weight of some text.
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Weight {
Thin,
ExtraLight,
Light,
#[default]
Normal,
Medium,
Semibold,
Bold,
ExtraBold,
Black,
}
/// The width of some text.
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Stretch {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
#[default]
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
} }

View file

@ -6,7 +6,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
/// A handle of some image data. /// A handle of some image data.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle { pub struct Handle {
id: u64, id: u64,
data: Data, data: Data,
@ -110,6 +110,14 @@ impl std::hash::Hash for Bytes {
} }
} }
impl PartialEq for Bytes {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for Bytes {}
impl AsRef<[u8]> for Bytes { impl AsRef<[u8]> for Bytes {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
self.0.as_ref().as_ref() self.0.as_ref().as_ref()
@ -125,7 +133,7 @@ impl std::ops::Deref for Bytes {
} }
/// The data of a raster image. /// The data of a raster image.
#[derive(Clone, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub enum Data { pub enum Data {
/// File data /// File data
Path(PathBuf), Path(PathBuf),

View file

@ -1,3 +1,5 @@
use crate::Pixels;
/// The strategy used to fill space in a specific dimension. /// The strategy used to fill space in a specific dimension.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Length { pub enum Length {
@ -36,6 +38,12 @@ impl Length {
} }
} }
impl From<Pixels> for Length {
fn from(amount: Pixels) -> Self {
Length::Fixed(f32::from(amount))
}
}
impl From<f32> for Length { impl From<f32> for Length {
fn from(amount: f32) -> Self { fn from(amount: f32) -> Self {
Length::Fixed(amount) Length::Fixed(amount)

View file

@ -25,31 +25,57 @@
#![forbid(unsafe_code, rust_2018_idioms)] #![forbid(unsafe_code, rust_2018_idioms)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)] #![allow(clippy::inherent_to_string, clippy::type_complexity)]
pub mod alignment; pub mod alignment;
pub mod clipboard;
pub mod event;
pub mod font;
pub mod gradient;
pub mod image;
pub mod keyboard; pub mod keyboard;
pub mod layout;
pub mod mouse; pub mod mouse;
pub mod overlay;
pub mod renderer;
pub mod svg;
pub mod text;
pub mod time; pub mod time;
pub mod touch;
pub mod widget;
pub mod window;
mod background; mod background;
mod color; mod color;
mod content_fit; mod content_fit;
mod font; mod element;
mod hasher;
mod length; mod length;
mod padding; mod padding;
mod pixels; mod pixels;
mod point; mod point;
mod rectangle; mod rectangle;
mod shell;
mod size; mod size;
mod vector; mod vector;
pub use alignment::Alignment; pub use alignment::Alignment;
pub use background::Background; pub use background::Background;
pub use clipboard::Clipboard;
pub use color::Color; pub use color::Color;
pub use content_fit::ContentFit; pub use content_fit::ContentFit;
pub use element::Element;
pub use event::Event;
pub use font::Font; pub use font::Font;
pub use gradient::Gradient;
pub use hasher::Hasher;
pub use layout::Layout;
pub use length::Length; pub use length::Length;
pub use overlay::Overlay;
pub use padding::Padding; pub use padding::Padding;
pub use pixels::Pixels; pub use pixels::Pixels;
pub use point::Point; pub use point::Point;
pub use rectangle::Rectangle; pub use rectangle::Rectangle;
pub use renderer::Renderer;
pub use shell::Shell;
pub use size::Size; pub use size::Size;
pub use text::Text;
pub use vector::Vector; pub use vector::Vector;
pub use widget::Widget;

View file

@ -1,8 +1,11 @@
//! Handle mouse events. //! Handle mouse events.
pub mod click;
mod button; mod button;
mod event; mod event;
mod interaction; mod interaction;
pub use button::Button; pub use button::Button;
pub use click::Click;
pub use event::{Event, ScrollDelta}; pub use event::{Event, ScrollDelta};
pub use interaction::Interaction; pub use interaction::Interaction;

View file

@ -2,11 +2,8 @@
mod element; mod element;
mod group; mod group;
pub mod menu;
pub use element::Element; pub use element::Element;
pub use group::Group; pub use group::Group;
pub use menu::Menu;
use crate::event::{self, Event}; use crate::event::{self, Event};
use crate::layout; use crate::layout;

View file

@ -1,12 +1,10 @@
use iced_core::{Point, Rectangle, Size};
use crate::event; use crate::event;
use crate::layout; use crate::layout;
use crate::mouse; use crate::mouse;
use crate::overlay; use crate::overlay;
use crate::renderer; use crate::renderer;
use crate::widget; use crate::widget;
use crate::{Clipboard, Event, Layout, Overlay, Shell}; use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size};
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`] /// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
/// children. /// children.

View file

@ -20,3 +20,9 @@ impl From<u16> for Pixels {
Self(f32::from(amount)) Self(f32::from(amount))
} }
} }
impl From<Pixels> for f32 {
fn from(pixels: Pixels) -> Self {
pixels.0
}
}

View file

@ -66,6 +66,11 @@ impl Rectangle<f32> {
Size::new(self.width, self.height) Size::new(self.width, self.height)
} }
/// Returns the area of the [`Rectangle`].
pub fn area(&self) -> f32 {
self.width * self.height
}
/// Returns true if the given [`Point`] is contained in the [`Rectangle`]. /// Returns true if the given [`Point`] is contained in the [`Rectangle`].
pub fn contains(&self, point: Point) -> bool { pub fn contains(&self, point: Point) -> bool {
self.x <= point.x self.x <= point.x
@ -74,6 +79,15 @@ impl Rectangle<f32> {
&& point.y <= self.y + self.height && point.y <= self.y + self.height
} }
/// Returns true if the current [`Rectangle`] is completely within the given
/// `container`.
pub fn is_within(&self, container: &Rectangle) -> bool {
container.contains(self.position())
&& container.contains(
self.position() + Vector::new(self.width, self.height),
)
}
/// Computes the intersection with the given [`Rectangle`]. /// Computes the intersection with the given [`Rectangle`].
pub fn intersection( pub fn intersection(
&self, &self,
@ -100,6 +114,30 @@ impl Rectangle<f32> {
} }
} }
/// Returns whether the [`Rectangle`] intersects with the given one.
pub fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}
/// Computes the union with the given [`Rectangle`].
pub fn union(&self, other: &Self) -> Self {
let x = self.x.min(other.x);
let y = self.y.min(other.y);
let lower_right_x = (self.x + self.width).max(other.x + other.width);
let lower_right_y = (self.y + self.height).max(other.y + other.height);
let width = lower_right_x - x;
let height = lower_right_y - y;
Rectangle {
x,
y,
width,
height,
}
}
/// Snaps the [`Rectangle`] to __unsigned__ integer coordinates. /// Snaps the [`Rectangle`] to __unsigned__ integer coordinates.
pub fn snap(self) -> Rectangle<u32> { pub fn snap(self) -> Rectangle<u32> {
Rectangle { Rectangle {
@ -109,6 +147,16 @@ impl Rectangle<f32> {
height: self.height as u32, height: self.height as u32,
} }
} }
/// Expands the [`Rectangle`] a given amount.
pub fn expand(self, amount: f32) -> Self {
Self {
x: self.x - amount,
y: self.y - amount,
width: self.width + amount * 2.0,
height: self.height + amount * 2.0,
}
}
} }
impl std::ops::Mul<f32> for Rectangle<f32> { impl std::ops::Mul<f32> for Rectangle<f32> {

View file

@ -1,6 +1,7 @@
//! Write your own renderer. //! Write your own renderer.
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
mod null; mod null;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub use null::Null; pub use null::Null;

View file

@ -1,6 +1,8 @@
use crate::renderer::{self, Renderer}; use crate::renderer::{self, Renderer};
use crate::text::{self, Text}; use crate::text::{self, Text};
use crate::{Background, Font, Point, Rectangle, Size, Theme, Vector}; use crate::{Background, Font, Point, Rectangle, Size, Vector};
use std::borrow::Cow;
/// A renderer that does nothing. /// A renderer that does nothing.
/// ///
@ -16,7 +18,7 @@ impl Null {
} }
impl Renderer for Null { impl Renderer for Null {
type Theme = Theme; type Theme = ();
fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {}
@ -40,20 +42,28 @@ impl Renderer for Null {
impl text::Renderer for Null { impl text::Renderer for Null {
type Font = Font; type Font = Font;
const ICON_FONT: Font = Font::Default; const ICON_FONT: Font = Font::DEFAULT;
const CHECKMARK_ICON: char = '0'; const CHECKMARK_ICON: char = '0';
const ARROW_DOWN_ICON: char = '0'; const ARROW_DOWN_ICON: char = '0';
fn default_size(&self) -> f32 { fn default_font(&self) -> Self::Font {
20.0 Font::default()
} }
fn default_size(&self) -> f32 {
16.0
}
fn load_font(&mut self, _font: Cow<'static, [u8]>) {}
fn measure( fn measure(
&self, &self,
_content: &str, _content: &str,
_size: f32, _size: f32,
_line_height: text::LineHeight,
_font: Font, _font: Font,
_bounds: Size, _bounds: Size,
_shaping: text::Shaping,
) -> (f32, f32) { ) -> (f32, f32) {
(0.0, 20.0) (0.0, 20.0)
} }
@ -62,8 +72,10 @@ impl text::Renderer for Null {
&self, &self,
_contents: &str, _contents: &str,
_size: f32, _size: f32,
_line_height: text::LineHeight,
_font: Self::Font, _font: Self::Font,
_bounds: Size, _bounds: Size,
_shaping: text::Shaping,
_point: Point, _point: Point,
_nearest_only: bool, _nearest_only: bool,
) -> Option<text::Hit> { ) -> Option<text::Hit> {

View file

@ -1,7 +1,7 @@
use crate::{Padding, Vector}; use crate::{Padding, Vector};
/// An amount of space in 2 dimensions. /// An amount of space in 2 dimensions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Size<T = f32> { pub struct Size<T = f32> {
/// The width. /// The width.
pub width: T, pub width: T,

View file

@ -7,7 +7,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
/// A handle of Svg data. /// A handle of Svg data.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle { pub struct Handle {
id: u64, id: u64,
data: Arc<Data>, data: Arc<Data>,
@ -57,7 +57,7 @@ impl Hash for Handle {
} }
/// The data of a vectorial image. /// The data of a vectorial image.
#[derive(Clone, Hash)] #[derive(Clone, Hash, PartialEq, Eq)]
pub enum Data { pub enum Data {
/// File data /// File data
Path(PathBuf), Path(PathBuf),

212
core/src/text.rs Normal file
View file

@ -0,0 +1,212 @@
//! Draw and interact with text.
use crate::alignment;
use crate::{Color, Pixels, Point, Rectangle, Size};
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
/// A paragraph.
#[derive(Debug, Clone, Copy)]
pub struct Text<'a, Font> {
/// The content of the paragraph.
pub content: &'a str,
/// The bounds of the paragraph.
pub bounds: Rectangle,
/// The size of the [`Text`] in logical pixels.
pub size: f32,
/// The line height of the [`Text`].
pub line_height: LineHeight,
/// The color of the [`Text`].
pub color: Color,
/// The font of the [`Text`].
pub font: Font,
/// The horizontal alignment of the [`Text`].
pub horizontal_alignment: alignment::Horizontal,
/// The vertical alignment of the [`Text`].
pub vertical_alignment: alignment::Vertical,
/// The [`Shaping`] strategy of the [`Text`].
pub shaping: Shaping,
}
/// The shaping strategy of some text.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Shaping {
/// No shaping and no font fallback.
///
/// This shaping strategy is very cheap, but it will not display complex
/// scripts properly nor try to find missing glyphs in your system fonts.
///
/// You should use this strategy when you have complete control of the text
/// and the font you are displaying in your application.
///
/// This is the default.
#[default]
Basic,
/// Advanced text shaping and font fallback.
///
/// You will need to enable this flag if the text contains a complex
/// script, the font used needs it, and/or multiple fonts in your system
/// may be needed to display all of the glyphs.
///
/// Advanced shaping is expensive! You should only enable it when necessary.
Advanced,
}
/// The height of a line of text in a paragraph.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LineHeight {
/// A factor of the size of the text.
Relative(f32),
/// An absolute height in logical pixels.
Absolute(Pixels),
}
impl LineHeight {
/// Returns the [`LineHeight`] in absolute logical pixels.
pub fn to_absolute(self, text_size: Pixels) -> Pixels {
match self {
Self::Relative(factor) => Pixels(factor * text_size.0),
Self::Absolute(pixels) => pixels,
}
}
}
impl Default for LineHeight {
fn default() -> Self {
Self::Relative(1.3)
}
}
impl From<f32> for LineHeight {
fn from(factor: f32) -> Self {
Self::Relative(factor)
}
}
impl From<Pixels> for LineHeight {
fn from(pixels: Pixels) -> Self {
Self::Absolute(pixels)
}
}
impl Hash for LineHeight {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Self::Relative(factor) => {
state.write_u8(0);
factor.to_bits().hash(state);
}
Self::Absolute(pixels) => {
state.write_u8(1);
f32::from(*pixels).to_bits().hash(state);
}
}
}
}
/// The result of hit testing on text.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Hit {
/// The point was within the bounds of the returned character index.
CharOffset(usize),
}
impl Hit {
/// Computes the cursor position of the [`Hit`] .
pub fn cursor(self) -> usize {
match self {
Self::CharOffset(i) => i,
}
}
}
/// A renderer capable of measuring and drawing [`Text`].
pub trait Renderer: crate::Renderer {
/// The font type used.
type Font: Copy;
/// The icon font of the backend.
const ICON_FONT: Self::Font;
/// The `char` representing a ✔ icon in the [`ICON_FONT`].
///
/// [`ICON_FONT`]: Self::ICON_FONT
const CHECKMARK_ICON: char;
/// The `char` representing a ▼ icon in the built-in [`ICON_FONT`].
///
/// [`ICON_FONT`]: Self::ICON_FONT
const ARROW_DOWN_ICON: char;
/// Returns the default [`Self::Font`].
fn default_font(&self) -> Self::Font;
/// Returns the default size of [`Text`].
fn default_size(&self) -> f32;
/// Measures the text in the given bounds and returns the minimum boundaries
/// that can fit the contents.
fn measure(
&self,
content: &str,
size: f32,
line_height: LineHeight,
font: Self::Font,
bounds: Size,
shaping: Shaping,
) -> (f32, f32);
/// Measures the width of the text as if it were laid out in a single line.
fn measure_width(
&self,
content: &str,
size: f32,
font: Self::Font,
shaping: Shaping,
) -> f32 {
let (width, _) = self.measure(
content,
size,
LineHeight::Absolute(Pixels(size)),
font,
Size::INFINITY,
shaping,
);
width
}
/// Tests whether the provided point is within the boundaries of text
/// laid out with the given parameters, returning information about
/// the nearest character.
///
/// If `nearest_only` is true, the hit test does not consider whether the
/// the point is interior to any glyph bounds, returning only the character
/// with the nearest centeroid.
fn hit_test(
&self,
contents: &str,
size: f32,
line_height: LineHeight,
font: Self::Font,
bounds: Size,
shaping: Shaping,
point: Point,
nearest_only: bool,
) -> Option<Hit>;
/// Loads a [`Self::Font`] from its bytes.
fn load_font(&mut self, font: Cow<'static, [u8]>);
/// Draws the given [`Text`].
fn fill_text(&mut self, text: Text<'_, Self::Font>);
}

View file

@ -1,101 +1,21 @@
//! Use the built-in widgets or create your own. //! Create custom widgets and operate on them.
//!
//! # Built-in widgets
//! Every built-in drawable widget has its own module with a `Renderer` trait
//! that must be implemented by a [renderer] before being able to use it as
//! a [`Widget`].
//!
//! # Custom widgets
//! If you want to implement a custom widget, you simply need to implement the
//! [`Widget`] trait. You can use the API of the built-in widgets as a guide or
//! source of inspiration.
//!
//! [renderer]: crate::renderer
pub mod button;
pub mod checkbox;
pub mod column;
pub mod container;
pub mod helpers;
pub mod image;
pub mod mouse_area;
pub mod operation; pub mod operation;
pub mod pane_grid;
pub mod pick_list;
pub mod progress_bar;
pub mod radio;
pub mod row;
pub mod rule;
pub mod scrollable;
pub mod slider;
pub mod space;
pub mod svg;
pub mod text; pub mod text;
pub mod text_input;
pub mod toggler;
pub mod tooltip;
pub mod tree; pub mod tree;
pub mod vertical_slider;
mod action;
mod id; mod id;
#[doc(no_inline)]
pub use button::Button;
#[doc(no_inline)]
pub use checkbox::Checkbox;
#[doc(no_inline)]
pub use column::Column;
#[doc(no_inline)]
pub use container::Container;
#[doc(no_inline)]
pub use helpers::*;
#[doc(no_inline)]
pub use image::Image;
#[doc(no_inline)]
pub use mouse_area::MouseArea;
#[doc(no_inline)]
pub use pane_grid::PaneGrid;
#[doc(no_inline)]
pub use pick_list::PickList;
#[doc(no_inline)]
pub use progress_bar::ProgressBar;
#[doc(no_inline)]
pub use radio::Radio;
#[doc(no_inline)]
pub use row::Row;
#[doc(no_inline)]
pub use rule::Rule;
#[doc(no_inline)]
pub use scrollable::Scrollable;
#[doc(no_inline)]
pub use slider::Slider;
#[doc(no_inline)]
pub use space::Space;
#[doc(no_inline)]
pub use svg::Svg;
#[doc(no_inline)]
pub use text::Text;
#[doc(no_inline)]
pub use text_input::TextInput;
#[doc(no_inline)]
pub use toggler::Toggler;
#[doc(no_inline)]
pub use tooltip::Tooltip;
#[doc(no_inline)]
pub use tree::Tree;
#[doc(no_inline)]
pub use vertical_slider::VerticalSlider;
pub use action::Action;
pub use id::Id; pub use id::Id;
pub use operation::Operation; pub use operation::Operation;
pub use text::Text;
pub use tree::Tree;
use crate::event::{self, Event}; use crate::event::{self, Event};
use crate::layout; use crate::layout::{self, Layout};
use crate::mouse; use crate::mouse;
use crate::overlay; use crate::overlay;
use crate::renderer; use crate::renderer;
use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell}; use crate::{Clipboard, Length, Point, Rectangle, Shell};
/// A component that displays information and allows interaction. /// A component that displays information and allows interaction.
/// ///

View file

@ -24,7 +24,7 @@ impl Id {
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Internal { enum Internal {
Unique(usize), Unique(usize),
Custom(borrow::Cow<'static, str>), Custom(borrow::Cow<'static, str>),
} }

View file

@ -0,0 +1,226 @@
//! Query or update internal widget state.
pub mod focusable;
pub mod scrollable;
pub mod text_input;
pub use focusable::Focusable;
pub use scrollable::Scrollable;
pub use text_input::TextInput;
use crate::widget::Id;
use std::any::Any;
use std::fmt;
use std::rc::Rc;
/// A piece of logic that can traverse the widget tree of an application in
/// order to query or update some widget state.
pub trait Operation<T> {
/// Operates on a widget that contains other widgets.
///
/// The `operate_on_children` function can be called to return control to
/// the widget tree and keep traversing it.
fn container(
&mut self,
id: Option<&Id>,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
);
/// Operates on a widget that can be focused.
fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {}
/// Operates on a widget that can be scrolled.
fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {}
/// Operates on a widget that has text input.
fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
/// Operates on a custom widget with some state.
fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {}
/// Finishes the [`Operation`] and returns its [`Outcome`].
fn finish(&self) -> Outcome<T> {
Outcome::None
}
}
/// The result of an [`Operation`].
pub enum Outcome<T> {
/// The [`Operation`] produced no result.
None,
/// The [`Operation`] produced some result.
Some(T),
/// The [`Operation`] needs to be followed by another [`Operation`].
Chain(Box<dyn Operation<T>>),
}
impl<T> fmt::Debug for Outcome<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "Outcome::None"),
Self::Some(output) => write!(f, "Outcome::Some({output:?})"),
Self::Chain(_) => write!(f, "Outcome::Chain(...)"),
}
}
}
/// Maps the output of an [`Operation`] using the given function.
pub fn map<A, B>(
operation: Box<dyn Operation<A>>,
f: impl Fn(A) -> B + 'static,
) -> impl Operation<B>
where
A: 'static,
B: 'static,
{
#[allow(missing_debug_implementations)]
struct Map<A, B> {
operation: Box<dyn Operation<A>>,
f: Rc<dyn Fn(A) -> B>,
}
impl<A, B> Operation<B> for Map<A, B>
where
A: 'static,
B: 'static,
{
fn container(
&mut self,
id: Option<&Id>,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
) {
struct MapRef<'a, A> {
operation: &'a mut dyn Operation<A>,
}
impl<'a, A, B> Operation<B> for MapRef<'a, A> {
fn container(
&mut self,
id: Option<&Id>,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
) {
let Self { operation, .. } = self;
operation.container(id, &mut |operation| {
operate_on_children(&mut MapRef { operation });
});
}
fn scrollable(
&mut self,
state: &mut dyn Scrollable,
id: Option<&Id>,
) {
self.operation.scrollable(state, id);
}
fn focusable(
&mut self,
state: &mut dyn Focusable,
id: Option<&Id>,
) {
self.operation.focusable(state, id);
}
fn text_input(
&mut self,
state: &mut dyn TextInput,
id: Option<&Id>,
) {
self.operation.text_input(state, id);
}
fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
self.operation.custom(state, id);
}
}
let Self { operation, .. } = self;
MapRef {
operation: operation.as_mut(),
}
.container(id, operate_on_children);
}
fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
self.operation.focusable(state, id);
}
fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) {
self.operation.scrollable(state, id);
}
fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
self.operation.text_input(state, id);
}
fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
self.operation.custom(state, id);
}
fn finish(&self) -> Outcome<B> {
match self.operation.finish() {
Outcome::None => Outcome::None,
Outcome::Some(output) => Outcome::Some((self.f)(output)),
Outcome::Chain(next) => Outcome::Chain(Box::new(Map {
operation: next,
f: self.f.clone(),
})),
}
}
}
Map {
operation,
f: Rc::new(f),
}
}
/// Produces an [`Operation`] that applies the given [`Operation`] to the
/// children of a container with the given [`Id`].
pub fn scope<T: 'static>(
target: Id,
operation: impl Operation<T> + 'static,
) -> impl Operation<T> {
struct ScopedOperation<Message> {
target: Id,
operation: Box<dyn Operation<Message>>,
}
impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
fn container(
&mut self,
id: Option<&Id>,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
) {
if id == Some(&self.target) {
operate_on_children(self.operation.as_mut());
} else {
operate_on_children(self);
}
}
fn finish(&self) -> Outcome<Message> {
match self.operation.finish() {
Outcome::Chain(next) => {
Outcome::Chain(Box::new(ScopedOperation {
target: self.target.clone(),
operation: next,
}))
}
outcome => outcome,
}
}
}
ScopedOperation {
target,
operation: Box::new(operation),
}
}

View file

@ -4,27 +4,15 @@ use crate::layout;
use crate::renderer; use crate::renderer;
use crate::text; use crate::text;
use crate::widget::Tree; use crate::widget::Tree;
use crate::{Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget}; use crate::{
Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget,
};
use std::borrow::Cow; use std::borrow::Cow;
pub use iced_style::text::{Appearance, StyleSheet}; pub use text::{LineHeight, Shaping};
/// A paragraph of text. /// A paragraph of text.
///
/// # Example
///
/// ```
/// # use iced_native::Color;
/// #
/// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
/// #
/// Text::new("I <3 iced!")
/// .size(40)
/// .style(Color::from([0.0, 0.0, 1.0]));
/// ```
///
/// ![Text drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text.png?raw=true)
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Text<'a, Renderer> pub struct Text<'a, Renderer>
where where
@ -33,11 +21,13 @@ where
{ {
content: Cow<'a, str>, content: Cow<'a, str>,
size: Option<f32>, size: Option<f32>,
line_height: LineHeight,
width: Length, width: Length,
height: Length, height: Length,
horizontal_alignment: alignment::Horizontal, horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
font: Renderer::Font, font: Option<Renderer::Font>,
shaping: Shaping,
style: <Renderer::Theme as StyleSheet>::Style, style: <Renderer::Theme as StyleSheet>::Style,
} }
@ -51,11 +41,13 @@ where
Text { Text {
content: content.into(), content: content.into(),
size: None, size: None,
font: Default::default(), line_height: LineHeight::default(),
font: None,
width: Length::Shrink, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
shaping: Shaping::Basic,
style: Default::default(), style: Default::default(),
} }
} }
@ -66,11 +58,17 @@ where
self self
} }
/// Sets the [`LineHeight`] of the [`Text`].
pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
self.line_height = line_height.into();
self
}
/// Sets the [`Font`] of the [`Text`]. /// Sets the [`Font`] of the [`Text`].
/// ///
/// [`Font`]: crate::text::Renderer::Font /// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = font.into(); self.font = Some(font.into());
self self
} }
@ -112,6 +110,12 @@ where
self.vertical_alignment = alignment; self.vertical_alignment = alignment;
self self
} }
/// Sets the [`Shaping`] strategy of the [`Text`].
pub fn shaping(mut self, shaping: Shaping) -> Self {
self.shaping = shaping;
self
}
} }
impl<'a, Message, Renderer> Widget<Message, Renderer> for Text<'a, Renderer> impl<'a, Message, Renderer> Widget<Message, Renderer> for Text<'a, Renderer>
@ -138,8 +142,14 @@ where
let bounds = limits.max(); let bounds = limits.max();
let (width, height) = let (width, height) = renderer.measure(
renderer.measure(&self.content, size, self.font.clone(), bounds); &self.content,
size,
self.line_height,
self.font.unwrap_or_else(|| renderer.default_font()),
bounds,
self.shaping,
);
let size = limits.resolve(Size::new(width, height)); let size = limits.resolve(Size::new(width, height));
@ -162,10 +172,12 @@ where
layout, layout,
&self.content, &self.content,
self.size, self.size,
self.font.clone(), self.line_height,
theme.appearance(self.style), self.font,
theme.appearance(self.style.clone()),
self.horizontal_alignment, self.horizontal_alignment,
self.vertical_alignment, self.vertical_alignment,
self.shaping,
); );
} }
} }
@ -186,10 +198,12 @@ pub fn draw<Renderer>(
layout: Layout<'_>, layout: Layout<'_>,
content: &str, content: &str,
size: Option<f32>, size: Option<f32>,
font: Renderer::Font, line_height: LineHeight,
font: Option<Renderer::Font>,
appearance: Appearance, appearance: Appearance,
horizontal_alignment: alignment::Horizontal, horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
shaping: Shaping,
) where ) where
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
@ -207,14 +221,18 @@ pub fn draw<Renderer>(
alignment::Vertical::Bottom => bounds.y + bounds.height, alignment::Vertical::Bottom => bounds.y + bounds.height,
}; };
renderer.fill_text(crate::text::Text { let size = size.unwrap_or_else(|| renderer.default_size());
renderer.fill_text(crate::Text {
content, content,
size: size.unwrap_or_else(|| renderer.default_size()), size,
line_height,
bounds: Rectangle { x, y, ..bounds }, bounds: Rectangle { x, y, ..bounds },
color: appearance.color.unwrap_or(style.text_color), color: appearance.color.unwrap_or(style.text_color),
font, font: font.unwrap_or_else(|| renderer.default_font()),
horizontal_alignment, horizontal_alignment,
vertical_alignment, vertical_alignment,
shaping,
}); });
} }
@ -238,22 +256,52 @@ where
Self { Self {
content: self.content.clone(), content: self.content.clone(),
size: self.size, size: self.size,
line_height: self.line_height,
width: self.width, width: self.width,
height: self.height, height: self.height,
horizontal_alignment: self.horizontal_alignment, horizontal_alignment: self.horizontal_alignment,
vertical_alignment: self.vertical_alignment, vertical_alignment: self.vertical_alignment,
font: self.font.clone(), font: self.font,
style: self.style, style: self.style.clone(),
shaping: self.shaping,
} }
} }
} }
impl<'a, Renderer> From<&'a str> for Text<'a, Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(content: &'a str) -> Self {
Self::new(content)
}
}
impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer> impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer>
where where
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
{ {
fn from(contents: &'a str) -> Self { fn from(content: &'a str) -> Self {
Text::new(contents).into() Text::from(content).into()
} }
} }
/// The style sheet of some text.
pub trait StyleSheet {
/// The supported style of the [`StyleSheet`].
type Style: Default + Clone;
/// Produces the [`Appearance`] of some text.
fn appearance(&self, style: Self::Style) -> Appearance;
}
/// The apperance of some text.
#[derive(Debug, Clone, Copy, Default)]
pub struct Appearance {
/// The [`Color`] of the text.
///
/// The default, `None`, means using the inherited color.
pub color: Option<Color>,
}

13
core/src/window.rs Normal file
View file

@ -0,0 +1,13 @@
//! Build window-based GUI applications.
pub mod icon;
mod event;
mod mode;
mod redraw_request;
mod user_attention;
pub use event::Event;
pub use icon::Icon;
pub use mode::Mode;
pub use redraw_request::RedrawRequest;
pub use user_attention::UserAttention;

View file

@ -93,8 +93,7 @@ A bunch of simpler examples exist:
- [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress.
- [`events`](events), a log of native events displayed using a conditional `Subscription`. - [`events`](events), a log of native events displayed using a conditional `Subscription`.
- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). - [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu).
- [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application. - [`integration`](integration), a demonstration of how to integrate Iced in an existing [`wgpu`] application.
- [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application.
- [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized.
- [`pick_list`](pick_list), a dropdown list of selectable options. - [`pick_list`](pick_list), a dropdown list of selectable options.
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].

View file

@ -5,8 +5,8 @@ use iced::widget::canvas::{
self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke, self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke,
}; };
use iced::{ use iced::{
Application, Command, Element, Length, Point, Rectangle, Settings, Application, Command, Element, Length, Point, Rectangle, Renderer,
Subscription, Theme, Settings, Subscription, Theme,
}; };
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
@ -75,11 +75,12 @@ impl<Message> canvas::Program<Message> for Arc {
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
theme: &Theme, theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: Cursor, _cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let geometry = self.cache.draw(bounds.size(), |frame| { let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
let palette = theme.palette(); let palette = theme.palette();
let center = frame.center(); let center = frame.center();

View file

@ -64,7 +64,7 @@ mod bezier {
use iced::widget::canvas::{ use iced::widget::canvas::{
self, Canvas, Cursor, Frame, Geometry, Path, Stroke, self, Canvas, Cursor, Frame, Geometry, Path, Stroke,
}; };
use iced::{Element, Length, Point, Rectangle, Theme}; use iced::{Element, Length, Point, Rectangle, Renderer, Theme};
#[derive(Default)] #[derive(Default)]
pub struct State { pub struct State {
@ -152,22 +152,26 @@ mod bezier {
fn draw( fn draw(
&self, &self,
state: &Self::State, state: &Self::State,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
cursor: Cursor, cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let content = let content = self.state.cache.draw(
self.state.cache.draw(bounds.size(), |frame: &mut Frame| { renderer,
bounds.size(),
|frame: &mut Frame| {
Curve::draw_all(self.curves, frame); Curve::draw_all(self.curves, frame);
frame.stroke( frame.stroke(
&Path::rectangle(Point::ORIGIN, frame.size()), &Path::rectangle(Point::ORIGIN, frame.size()),
Stroke::default().with_width(2.0), Stroke::default().with_width(2.0),
); );
}); },
);
if let Some(pending) = state { if let Some(pending) = state {
let pending_curve = pending.draw(bounds, cursor); let pending_curve = pending.draw(renderer, bounds, cursor);
vec![content, pending_curve] vec![content, pending_curve]
} else { } else {
@ -216,8 +220,13 @@ mod bezier {
} }
impl Pending { impl Pending {
fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Geometry { fn draw(
let mut frame = Frame::new(bounds.size()); &self,
renderer: &Renderer,
bounds: Rectangle,
cursor: Cursor,
) -> Geometry {
let mut frame = Frame::new(renderer, bounds.size());
if let Some(cursor_position) = cursor.position_in(&bounds) { if let Some(cursor_position) = cursor.position_in(&bounds) {
match *self { match *self {

Binary file not shown.

View file

@ -1,10 +1,9 @@
use iced::widget::{checkbox, column, container}; use iced::executor;
use iced::{Element, Font, Length, Sandbox, Settings}; use iced::font::{self, Font};
use iced::widget::{checkbox, column, container, text};
use iced::{Application, Command, Element, Length, Settings, Theme};
const ICON_FONT: Font = Font::External { const ICON_FONT: Font = Font::with_name("icons");
name: "Icons",
bytes: include_bytes!("../fonts/icons.ttf"),
};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
Example::run(Settings::default()) Example::run(Settings::default())
@ -20,24 +19,35 @@ struct Example {
enum Message { enum Message {
DefaultChecked(bool), DefaultChecked(bool),
CustomChecked(bool), CustomChecked(bool),
FontLoaded(Result<(), font::Error>),
} }
impl Sandbox for Example { impl Application for Example {
type Message = Message; type Message = Message;
type Flags = ();
type Executor = executor::Default;
type Theme = Theme;
fn new() -> Self { fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
Default::default() (
Self::default(),
font::load(include_bytes!("../fonts/icons.ttf").as_ref())
.map(Message::FontLoaded),
)
} }
fn title(&self) -> String { fn title(&self) -> String {
String::from("Checkbox - Iced") String::from("Checkbox - Iced")
} }
fn update(&mut self, message: Message) { fn update(&mut self, message: Message) -> Command<Message> {
match message { match message {
Message::DefaultChecked(value) => self.default_checkbox = value, Message::DefaultChecked(value) => self.default_checkbox = value,
Message::CustomChecked(value) => self.custom_checkbox = value, Message::CustomChecked(value) => self.custom_checkbox = value,
Message::FontLoaded(_) => (),
} }
Command::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
@ -49,6 +59,8 @@ impl Sandbox for Example {
font: ICON_FONT, font: ICON_FONT,
code_point: '\u{e901}', code_point: '\u{e901}',
size: None, size: None,
line_height: text::LineHeight::Relative(1.0),
shaping: text::Shaping::Basic,
}); });
let content = column![default_checkbox, custom_checkbox].spacing(22); let content = column![default_checkbox, custom_checkbox].spacing(22);

View file

@ -4,8 +4,8 @@ use iced::widget::canvas::{
}; };
use iced::widget::{canvas, container}; use iced::widget::{canvas, container};
use iced::{ use iced::{
Application, Color, Command, Element, Length, Point, Rectangle, Settings, Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
Subscription, Theme, Vector, Settings, Subscription, Theme, Vector,
}; };
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
@ -83,17 +83,18 @@ impl Application for Clock {
} }
} }
impl<Message> canvas::Program<Message> for Clock { impl<Message> canvas::Program<Message, Renderer> for Clock {
type State = (); type State = ();
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: Cursor, _cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let clock = self.clock.draw(bounds.size(), |frame| { let clock = self.clock.draw(renderer, bounds.size(), |frame| {
let center = frame.center(); let center = frame.center();
let radius = frame.width().min(frame.height()) / 2.0; let radius = frame.width().min(frame.height()) / 2.0;

View file

@ -1,8 +1,8 @@
use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path}; use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path};
use iced::widget::{column, row, text, Slider}; use iced::widget::{column, row, text, Slider};
use iced::{ use iced::{
alignment, Alignment, Color, Element, Length, Point, Rectangle, Sandbox, alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer,
Settings, Size, Vector, Sandbox, Settings, Size, Vector,
}; };
use palette::{self, convert::FromColor, Hsl, Srgb}; use palette::{self, convert::FromColor, Hsl, Srgb};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -243,11 +243,12 @@ impl<Message> canvas::Program<Message> for Theme {
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
_theme: &iced::Theme, _theme: &iced::Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: Cursor, _cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let theme = self.canvas_cache.draw(bounds.size(), |frame| { let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| {
self.draw(frame); self.draw(frame);
}); });

View file

@ -6,6 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = ["debug"] } iced = { path = "../..", features = ["debug", "lazy"] }
iced_native = { path = "../../native" }
iced_lazy = { path = "../../lazy" }

View file

@ -47,9 +47,8 @@ impl Sandbox for Component {
mod numeric_input { mod numeric_input {
use iced::alignment::{self, Alignment}; use iced::alignment::{self, Alignment};
use iced::widget::{self, button, row, text, text_input}; use iced::widget::{button, component, row, text, text_input, Component};
use iced::{Element, Length}; use iced::{Element, Length, Renderer};
use iced_lazy::{self, Component};
pub struct NumericInput<Message> { pub struct NumericInput<Message> {
value: Option<u32>, value: Option<u32>,
@ -82,13 +81,7 @@ mod numeric_input {
} }
} }
impl<Message, Renderer> Component<Message, Renderer> for NumericInput<Message> impl<Message> Component<Message, Renderer> for NumericInput<Message> {
where
Renderer: iced_native::text::Renderer + 'static,
Renderer::Theme: widget::button::StyleSheet
+ widget::text_input::StyleSheet
+ widget::text::StyleSheet,
{
type State = (); type State = ();
type Event = Event; type Event = Event;
@ -152,17 +145,12 @@ mod numeric_input {
} }
} }
impl<'a, Message, Renderer> From<NumericInput<Message>> impl<'a, Message> From<NumericInput<Message>> for Element<'a, Message, Renderer>
for Element<'a, Message, Renderer>
where where
Message: 'a, Message: 'a,
Renderer: 'static + iced_native::text::Renderer,
Renderer::Theme: widget::button::StyleSheet
+ widget::text_input::StyleSheet
+ widget::text::StyleSheet,
{ {
fn from(numeric_input: NumericInput<Message>) -> Self { fn from(numeric_input: NumericInput<Message>) -> Self {
iced_lazy::component(numeric_input) component(numeric_input)
} }
} }
} }

View file

@ -6,5 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../.." } iced = { path = "../..", features = ["advanced"] }
iced_native = { path = "../../native" }

View file

@ -1,9 +1,9 @@
//! This example showcases a drawing a quad. //! This example showcases a drawing a quad.
mod quad { mod quad {
use iced_native::layout::{self, Layout}; use iced::advanced::layout::{self, Layout};
use iced_native::renderer; use iced::advanced::renderer;
use iced_native::widget::{self, Widget}; use iced::advanced::widget::{self, Widget};
use iced_native::{Color, Element, Length, Point, Rectangle, Size}; use iced::{Color, Element, Length, Point, Rectangle, Size};
pub struct CustomQuad { pub struct CustomQuad {
size: f32, size: f32,

View file

@ -6,5 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../.." } iced = { path = "../..", features = ["advanced"] }
iced_native = { path = "../../native" }

View file

@ -9,10 +9,10 @@ mod circle {
// Of course, you can choose to make the implementation renderer-agnostic, // Of course, you can choose to make the implementation renderer-agnostic,
// if you wish to, by creating your own `Renderer` trait, which could be // if you wish to, by creating your own `Renderer` trait, which could be
// implemented by `iced_wgpu` and other renderers. // implemented by `iced_wgpu` and other renderers.
use iced_native::layout::{self, Layout}; use iced::advanced::layout::{self, Layout};
use iced_native::renderer; use iced::advanced::renderer;
use iced_native::widget::{self, Widget}; use iced::advanced::widget::{self, Widget};
use iced_native::{Color, Element, Length, Point, Rectangle, Size}; use iced::{Color, Element, Length, Point, Rectangle, Size};
pub struct Circle { pub struct Circle {
radius: f32, radius: f32,

View file

@ -7,8 +7,6 @@ publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = ["tokio"] } iced = { path = "../..", features = ["tokio"] }
iced_native = { path = "../../native" }
iced_futures = { path = "../../futures" }
[dependencies.reqwest] [dependencies.reqwest]
version = "0.11" version = "0.11"

View file

@ -1,4 +1,4 @@
use iced_native::subscription; use iced::subscription;
use std::hash::Hash; use std::hash::Hash;

View file

@ -7,4 +7,3 @@ publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = ["debug"] } iced = { path = "../..", features = ["debug"] }
iced_native = { path = "../../native" }

View file

@ -1,12 +1,13 @@
use iced::alignment; use iced::alignment;
use iced::executor; use iced::executor;
use iced::subscription;
use iced::widget::{button, checkbox, container, text, Column}; use iced::widget::{button, checkbox, container, text, Column};
use iced::window; use iced::window;
use iced::Event;
use iced::{ use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription, Alignment, Application, Command, Element, Length, Settings, Subscription,
Theme, Theme,
}; };
use iced_native::Event;
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
Events::run(Settings { Events::run(Settings {
@ -17,13 +18,13 @@ pub fn main() -> iced::Result {
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Events { struct Events {
last: Vec<iced_native::Event>, last: Vec<Event>,
enabled: bool, enabled: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
EventOccurred(iced_native::Event), EventOccurred(Event),
Toggled(bool), Toggled(bool),
Exit, Exit,
} }
@ -70,7 +71,7 @@ impl Application for Events {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
iced_native::subscription::events().map(Message::EventOccurred) subscription::events().map(Message::EventOccurred)
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {

View file

@ -145,7 +145,7 @@ impl Application for GameOfLife {
self.grid self.grid
.view() .view()
.map(move |message| Message::Grid(message, version)), .map(move |message| Message::Grid(message, version)),
controls controls,
]; ];
container(content) container(content)
@ -211,8 +211,8 @@ mod grid {
Cache, Canvas, Cursor, Frame, Geometry, Path, Text, Cache, Canvas, Cursor, Frame, Geometry, Path, Text,
}; };
use iced::{ use iced::{
alignment, mouse, Color, Element, Length, Point, Rectangle, Size, alignment, mouse, Color, Element, Length, Point, Rectangle, Renderer,
Theme, Vector, Size, Theme, Vector,
}; };
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use std::future::Future; use std::future::Future;
@ -536,13 +536,14 @@ mod grid {
fn draw( fn draw(
&self, &self,
_interaction: &Interaction, _interaction: &Interaction,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
cursor: Cursor, cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0); let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0);
let life = self.life_cache.draw(bounds.size(), |frame| { let life = self.life_cache.draw(renderer, bounds.size(), |frame| {
let background = Path::rectangle(Point::ORIGIN, frame.size()); let background = Path::rectangle(Point::ORIGIN, frame.size());
frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B)); frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B));
@ -565,7 +566,7 @@ mod grid {
}); });
let overlay = { let overlay = {
let mut frame = Frame::new(bounds.size()); let mut frame = Frame::new(renderer, bounds.size());
let hovered_cell = let hovered_cell =
cursor.position_in(&bounds).map(|position| { cursor.position_in(&bounds).map(|position| {
@ -626,38 +627,40 @@ mod grid {
if self.scaling < 0.2 || !self.show_lines { if self.scaling < 0.2 || !self.show_lines {
vec![life, overlay] vec![life, overlay]
} else { } else {
let grid = self.grid_cache.draw(bounds.size(), |frame| { let grid =
frame.translate(center); self.grid_cache.draw(renderer, bounds.size(), |frame| {
frame.scale(self.scaling); frame.translate(center);
frame.translate(self.translation); frame.scale(self.scaling);
frame.scale(Cell::SIZE as f32); frame.translate(self.translation);
frame.scale(Cell::SIZE as f32);
let region = self.visible_region(frame.size()); let region = self.visible_region(frame.size());
let rows = region.rows(); let rows = region.rows();
let columns = region.columns(); let columns = region.columns();
let (total_rows, total_columns) = let (total_rows, total_columns) =
(rows.clone().count(), columns.clone().count()); (rows.clone().count(), columns.clone().count());
let width = 2.0 / Cell::SIZE as f32; let width = 2.0 / Cell::SIZE as f32;
let color = Color::from_rgb8(70, 74, 83); let color = Color::from_rgb8(70, 74, 83);
frame.translate(Vector::new(-width / 2.0, -width / 2.0)); frame
.translate(Vector::new(-width / 2.0, -width / 2.0));
for row in region.rows() { for row in region.rows() {
frame.fill_rectangle( frame.fill_rectangle(
Point::new(*columns.start() as f32, row as f32), Point::new(*columns.start() as f32, row as f32),
Size::new(total_columns as f32, width), Size::new(total_columns as f32, width),
color, color,
); );
} }
for column in region.columns() { for column in region.columns() {
frame.fill_rectangle( frame.fill_rectangle(
Point::new(column as f32, *rows.start() as f32), Point::new(column as f32, *rows.start() as f32),
Size::new(width, total_rows as f32), Size::new(width, total_rows as f32),
color, color,
); );
} }
}); });
vec![life, grid, overlay] vec![life, grid, overlay]
} }

View file

@ -6,6 +6,5 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../.." } iced = { path = "../..", features = ["advanced"] }
iced_native = { path = "../../native" }
iced_graphics = { path = "../../graphics" } iced_graphics = { path = "../../graphics" }

View file

@ -1,23 +1,13 @@
//! This example showcases a simple native custom widget that renders using //! This example showcases a simple native custom widget that renders using
//! arbitrary low-level geometry. //! arbitrary low-level geometry.
mod rainbow { mod rainbow {
// For now, to implement a custom native widget you will need to add use iced_graphics::primitive::{ColoredVertex2D, Primitive};
// `iced_native` and `iced_wgpu` to your dependencies.
//
// Then, you simply need to define your widget type and implement the
// `iced_native::Widget` trait with the `iced_wgpu::Renderer`.
//
// Of course, you can choose to make the implementation renderer-agnostic,
// if you wish to, by creating your own `Renderer` trait, which could be
// implemented by `iced_wgpu` and other renderers.
use iced_graphics::renderer::{self, Renderer};
use iced_graphics::triangle::ColoredVertex2D;
use iced_graphics::{Backend, Primitive};
use iced_native::layout; use iced::advanced::layout::{self, Layout};
use iced_native::widget::{self, Widget}; use iced::advanced::renderer;
use iced_native::{ use iced::advanced::widget::{self, Widget};
Element, Layout, Length, Point, Rectangle, Size, Vector, use iced::{
Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector,
}; };
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
@ -27,10 +17,7 @@ mod rainbow {
Rainbow Rainbow
} }
impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow impl<Message> Widget<Message, Renderer> for Rainbow {
where
B: Backend,
{
fn width(&self) -> Length { fn width(&self) -> Length {
Length::Fill Length::Fill
} }
@ -41,7 +28,7 @@ mod rainbow {
fn layout( fn layout(
&self, &self,
_renderer: &Renderer<B, T>, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let size = limits.width(Length::Fill).resolve(Size::ZERO); let size = limits.width(Length::Fill).resolve(Size::ZERO);
@ -52,15 +39,15 @@ mod rainbow {
fn draw( fn draw(
&self, &self,
_tree: &widget::Tree, _tree: &widget::Tree,
renderer: &mut Renderer<B, T>, renderer: &mut Renderer,
_theme: &T, _theme: &Theme,
_style: &renderer::Style, _style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
_viewport: &Rectangle, _viewport: &Rectangle,
) { ) {
use iced_graphics::triangle::Mesh2D; use iced::advanced::Renderer as _;
use iced_native::Renderer as _; use iced_graphics::primitive::Mesh2D;
let b = layout.bounds(); let b = layout.bounds();
@ -151,10 +138,7 @@ mod rainbow {
} }
} }
impl<'a, Message, B, T> From<Rainbow> for Element<'a, Message, Renderer<B, T>> impl<'a, Message> From<Rainbow> for Element<'a, Message, Renderer> {
where
B: Backend,
{
fn from(rainbow: Rainbow) -> Self { fn from(rainbow: Rainbow) -> Self {
Self::new(rainbow) Self::new(rainbow)
} }

View file

@ -1,5 +1,5 @@
[package] [package]
name = "integration_wgpu" name = "integration"
version = "0.1.0" version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021" edition = "2021"
@ -7,7 +7,9 @@ publish = false
[dependencies] [dependencies]
iced_winit = { path = "../../winit" } iced_winit = { path = "../../winit" }
iced_wgpu = { path = "../../wgpu", features = ["webgl"] } iced_wgpu = { path = "../../wgpu" }
iced_widget = { path = "../../widget" }
iced_renderer = { path = "../../renderer", features = ["wgpu"] }
env_logger = "0.10" env_logger = "0.10"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]

View file

@ -8,8 +8,8 @@
<h1>integration_wgpu</h1> <h1>integration_wgpu</h1>
<canvas id="iced_canvas"></canvas> <canvas id="iced_canvas"></canvas>
<script type="module"> <script type="module">
import init from "./integration_wgpu.js"; import init from "./integration.js";
init('./integration_wgpu_bg.wasm'); init('./integration_bg.wasm');
</script> </script>
<style> <style>
body { body {

View file

@ -1,6 +1,8 @@
use iced_wgpu::Renderer; use iced_wgpu::Renderer;
use iced_winit::widget::{slider, text_input, Column, Row, Text}; use iced_widget::{slider, text_input, Column, Row, Text};
use iced_winit::{Alignment, Color, Command, Element, Length, Program}; use iced_winit::core::{Alignment, Color, Element, Length};
use iced_winit::runtime::{Command, Program};
use iced_winit::style::Theme;
pub struct Controls { pub struct Controls {
background_color: Color, background_color: Color,
@ -27,7 +29,7 @@ impl Controls {
} }
impl Program for Controls { impl Program for Controls {
type Renderer = Renderer; type Renderer = Renderer<Theme>;
type Message = Message; type Message = Message;
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {
@ -43,7 +45,7 @@ impl Program for Controls {
Command::none() Command::none()
} }
fn view(&self) -> Element<Message, Renderer> { fn view(&self) -> Element<Message, Renderer<Theme>> {
let background_color = self.background_color; let background_color = self.background_color;
let text = &self.text; let text = &self.text;

View file

@ -4,11 +4,14 @@ mod scene;
use controls::Controls; use controls::Controls;
use scene::Scene; use scene::Scene;
use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; use iced_wgpu::graphics::Viewport;
use iced_winit::{ use iced_wgpu::{wgpu, Backend, Renderer, Settings};
conversion, futures, program, renderer, winit, Clipboard, Color, Debug, use iced_winit::core::renderer;
Size, use iced_winit::core::{Color, Size};
}; use iced_winit::runtime::program;
use iced_winit::runtime::Debug;
use iced_winit::style::Theme;
use iced_winit::{conversion, futures, winit, Clipboard};
use winit::{ use winit::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
@ -34,7 +37,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
.and_then(|win| win.document()) .and_then(|win| win.document())
.and_then(|doc| doc.get_element_by_id("iced_canvas")) .and_then(|doc| doc.get_element_by_id("iced_canvas"))
.and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok()) .and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok())
.expect("Canvas with id `iced_canvas` is missing") .expect("Get canvas element")
}; };
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
env_logger::init(); env_logger::init();
@ -74,47 +77,49 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}); });
let surface = unsafe { instance.create_surface(&window) }?; let surface = unsafe { instance.create_surface(&window) }?;
let (format, (device, queue)) = futures::executor::block_on(async { let (format, (device, queue)) =
let adapter = wgpu::util::initialize_adapter_from_env_or_default( futures::futures::executor::block_on(async {
&instance, let adapter = wgpu::util::initialize_adapter_from_env_or_default(
backend, &instance,
Some(&surface), backend,
) Some(&surface),
.await )
.expect("No suitable GPU adapters found on the system!"); .await
.expect("Create adapter");
let adapter_features = adapter.features(); let adapter_features = adapter.features();
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() let needed_limits = wgpu::Limits::downlevel_webgl2_defaults()
.using_resolution(adapter.limits()); .using_resolution(adapter.limits());
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
let needed_limits = wgpu::Limits::default(); let needed_limits = wgpu::Limits::default();
let capabilities = surface.get_capabilities(&adapter); let capabilities = surface.get_capabilities(&adapter);
( (
capabilities capabilities
.formats .formats
.iter() .iter()
.copied() .copied()
.find(wgpu::TextureFormat::is_srgb) .find(wgpu::TextureFormat::is_srgb)
.or_else(|| capabilities.formats.first().copied()) .or_else(|| capabilities.formats.first().copied())
.expect("Get preferred format"), .expect("Get preferred format"),
adapter adapter
.request_device( .request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
label: None, label: None,
features: adapter_features & wgpu::Features::default(), features: adapter_features
limits: needed_limits, & wgpu::Features::default(),
}, limits: needed_limits,
None, },
) None,
.await )
.expect("Request device"), .await
) .expect("Request device"),
}); )
});
surface.configure( surface.configure(
&device, &device,
@ -131,17 +136,18 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut resized = false; let mut resized = false;
// Initialize staging belt
let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024);
// Initialize scene and GUI controls // Initialize scene and GUI controls
let scene = Scene::new(&device, format); let scene = Scene::new(&device, format);
let controls = Controls::new(); let controls = Controls::new();
// Initialize iced // Initialize iced
let mut debug = Debug::new(); let mut debug = Debug::new();
let mut renderer = let mut renderer = Renderer::new(Backend::new(
Renderer::new(Backend::new(&device, Settings::default(), format)); &device,
&queue,
Settings::default(),
format,
));
let mut state = program::State::new( let mut state = program::State::new(
controls, controls,
@ -193,7 +199,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
viewport.scale_factor(), viewport.scale_factor(),
), ),
&mut renderer, &mut renderer,
&iced_wgpu::Theme::Dark, &Theme::Dark,
&renderer::Style { text_color: Color::WHITE }, &renderer::Style { text_color: Color::WHITE },
&mut clipboard, &mut clipboard,
&mut debug, &mut debug,
@ -221,7 +227,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
height: size.height, height: size.height,
present_mode: wgpu::PresentMode::AutoVsync, present_mode: wgpu::PresentMode::AutoVsync,
alpha_mode: wgpu::CompositeAlphaMode::Auto, alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![] view_formats: vec![],
}, },
); );
@ -254,8 +260,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
renderer.with_primitives(|backend, primitive| { renderer.with_primitives(|backend, primitive| {
backend.present( backend.present(
&device, &device,
&mut staging_belt, &queue,
&mut encoder, &mut encoder,
None,
&view, &view,
primitive, primitive,
&viewport, &viewport,
@ -264,7 +271,6 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}); });
// Then we submit the work // Then we submit the work
staging_belt.finish();
queue.submit(Some(encoder.finish())); queue.submit(Some(encoder.finish()));
frame.present(); frame.present();
@ -274,10 +280,6 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
state.mouse_interaction(), state.mouse_interaction(),
), ),
); );
// And recall staging buffers
staging_belt.recall();
} }
Err(error) => match error { Err(error) => match error {
wgpu::SurfaceError::OutOfMemory => { wgpu::SurfaceError::OutOfMemory => {

View file

@ -1,5 +1,5 @@
use iced_wgpu::wgpu; use iced_wgpu::wgpu;
use iced_winit::Color; use iced_winit::core::Color;
pub struct Scene { pub struct Scene {
pipeline: wgpu::RenderPipeline, pipeline: wgpu::RenderPipeline,

View file

@ -1,12 +0,0 @@
[package]
name = "integration_opengl"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
publish = false
[dependencies]
iced_glutin = { path = "../../glutin" }
iced_glow = { path = "../../glow" }
iced_winit = { path = "../../winit" }
env_logger = "0.8"

View file

@ -1,16 +0,0 @@
## OpenGL integration
A demonstration of how to integrate Iced in an existing graphical OpenGL application.
The __[`main`]__ file contains all the code of the example.
<div align="center">
<a href="https://imgbox.com/9P9ETcod" target="_blank"><img src="https://images2.imgbox.com/2a/51/9P9ETcod_o.gif" alt="image host"/></a>
</div>
You can run it with `cargo run`:
```
cargo run --package integration_opengl
```
[`main`]: src/main.rs

View file

@ -1,101 +0,0 @@
use iced_glow::Renderer;
use iced_glutin::widget::Slider;
use iced_glutin::widget::{Column, Row, Text};
use iced_glutin::{Alignment, Color, Command, Element, Length, Program};
pub struct Controls {
background_color: Color,
}
#[derive(Debug, Clone)]
pub enum Message {
BackgroundColorChanged(Color),
}
impl Controls {
pub fn new() -> Controls {
Controls {
background_color: Color::BLACK,
}
}
pub fn background_color(&self) -> Color {
self.background_color
}
}
impl Program for Controls {
type Renderer = Renderer;
type Message = Message;
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::BackgroundColorChanged(color) => {
self.background_color = color;
}
}
Command::none()
}
fn view(&self) -> Element<Message, Renderer> {
let background_color = self.background_color;
let sliders = Row::new()
.width(500)
.spacing(20)
.push(
Slider::new(0.0..=1.0, background_color.r, move |r| {
Message::BackgroundColorChanged(Color {
r,
..background_color
})
})
.step(0.01),
)
.push(
Slider::new(0.0..=1.0, background_color.g, move |g| {
Message::BackgroundColorChanged(Color {
g,
..background_color
})
})
.step(0.01),
)
.push(
Slider::new(0.0..=1.0, background_color.b, move |b| {
Message::BackgroundColorChanged(Color {
b,
..background_color
})
})
.step(0.01),
);
Row::new()
.width(Length::Fill)
.height(Length::Fill)
.align_items(Alignment::End)
.push(
Column::new()
.width(Length::Fill)
.align_items(Alignment::End)
.push(
Column::new()
.padding(10)
.spacing(10)
.push(
Text::new("Background color")
.style(Color::WHITE),
)
.push(sliders)
.push(
Text::new(format!("{background_color:?}"))
.size(14)
.style(Color::WHITE),
),
),
)
.into()
}
}

View file

@ -1,187 +0,0 @@
mod controls;
mod scene;
use controls::Controls;
use scene::Scene;
use glow::*;
use glutin::dpi::PhysicalPosition;
use glutin::event::{Event, ModifiersState, WindowEvent};
use glutin::event_loop::ControlFlow;
use iced_glow::glow;
use iced_glow::{Backend, Renderer, Settings, Viewport};
use iced_glutin::conversion;
use iced_glutin::glutin;
use iced_glutin::renderer;
use iced_glutin::{program, Clipboard, Color, Debug, Size};
pub fn main() {
env_logger::init();
let (gl, event_loop, windowed_context, shader_version) = {
let el = glutin::event_loop::EventLoop::new();
let wb = glutin::window::WindowBuilder::new()
.with_title("OpenGL integration example")
.with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0));
let windowed_context = glutin::ContextBuilder::new()
.with_vsync(true)
.build_windowed(wb, &el)
.unwrap();
unsafe {
let windowed_context = windowed_context.make_current().unwrap();
let gl = glow::Context::from_loader_function(|s| {
windowed_context.get_proc_address(s) as *const _
});
// Enable auto-conversion from/to sRGB
gl.enable(glow::FRAMEBUFFER_SRGB);
// Enable alpha blending
gl.enable(glow::BLEND);
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
// Disable multisampling by default
gl.disable(glow::MULTISAMPLE);
(gl, el, windowed_context, "#version 410")
}
};
let physical_size = windowed_context.window().inner_size();
let mut viewport = Viewport::with_physical_size(
Size::new(physical_size.width, physical_size.height),
windowed_context.window().scale_factor(),
);
let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
let mut modifiers = ModifiersState::default();
let mut clipboard = Clipboard::connect(windowed_context.window());
let mut renderer = Renderer::new(Backend::new(&gl, Settings::default()));
let mut debug = Debug::new();
let controls = Controls::new();
let mut state = program::State::new(
controls,
viewport.logical_size(),
&mut renderer,
&mut debug,
);
let mut resized = false;
let scene = Scene::new(&gl, shader_version);
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CursorMoved { position, .. } => {
cursor_position = position;
}
WindowEvent::ModifiersChanged(new_modifiers) => {
modifiers = new_modifiers;
}
WindowEvent::Resized(physical_size) => {
viewport = Viewport::with_physical_size(
Size::new(
physical_size.width,
physical_size.height,
),
windowed_context.window().scale_factor(),
);
resized = true;
}
WindowEvent::CloseRequested => {
scene.cleanup(&gl);
*control_flow = ControlFlow::Exit
}
_ => (),
}
// Map window event to iced event
if let Some(event) = iced_winit::conversion::window_event(
&event,
windowed_context.window().scale_factor(),
modifiers,
) {
state.queue_event(event);
}
}
Event::MainEventsCleared => {
// If there are events pending
if !state.is_queue_empty() {
// We update iced
let _ = state.update(
viewport.logical_size(),
conversion::cursor_position(
cursor_position,
viewport.scale_factor(),
),
&mut renderer,
&iced_glow::Theme::Dark,
&renderer::Style {
text_color: Color::WHITE,
},
&mut clipboard,
&mut debug,
);
// and request a redraw
windowed_context.window().request_redraw();
}
}
Event::RedrawRequested(_) => {
if resized {
let size = windowed_context.window().inner_size();
unsafe {
gl.viewport(
0,
0,
size.width as i32,
size.height as i32,
);
}
resized = false;
}
let program = state.program();
{
// We clear the frame
scene.clear(&gl, program.background_color());
// Draw the scene
scene.draw(&gl);
}
// And then iced on top
renderer.with_primitives(|backend, primitive| {
backend.present(
&gl,
primitive,
&viewport,
&debug.overlay(),
);
});
// Update the mouse cursor
windowed_context.window().set_cursor_icon(
iced_winit::conversion::mouse_interaction(
state.mouse_interaction(),
),
);
windowed_context.swap_buffers().unwrap();
}
_ => (),
}
});
}

View file

@ -1,102 +0,0 @@
use glow::*;
use iced_glow::glow;
use iced_glow::Color;
pub struct Scene {
program: glow::Program,
vertex_array: glow::VertexArray,
}
impl Scene {
pub fn new(gl: &glow::Context, shader_version: &str) -> Self {
unsafe {
let vertex_array = gl
.create_vertex_array()
.expect("Cannot create vertex array");
gl.bind_vertex_array(Some(vertex_array));
let program = gl.create_program().expect("Cannot create program");
let (vertex_shader_source, fragment_shader_source) = (
r#"const vec2 verts[3] = vec2[3](
vec2(0.5f, 1.0f),
vec2(0.0f, 0.0f),
vec2(1.0f, 0.0f)
);
out vec2 vert;
void main() {
vert = verts[gl_VertexID];
gl_Position = vec4(vert - 0.5, 0.0, 1.0);
}"#,
r#"precision highp float;
in vec2 vert;
out vec4 color;
void main() {
color = vec4(vert, 0.5, 1.0);
}"#,
);
let shader_sources = [
(glow::VERTEX_SHADER, vertex_shader_source),
(glow::FRAGMENT_SHADER, fragment_shader_source),
];
let mut shaders = Vec::with_capacity(shader_sources.len());
for (shader_type, shader_source) in shader_sources.iter() {
let shader = gl
.create_shader(*shader_type)
.expect("Cannot create shader");
gl.shader_source(
shader,
&format!("{shader_version}\n{shader_source}"),
);
gl.compile_shader(shader);
if !gl.get_shader_compile_status(shader) {
panic!("{}", gl.get_shader_info_log(shader));
}
gl.attach_shader(program, shader);
shaders.push(shader);
}
gl.link_program(program);
if !gl.get_program_link_status(program) {
panic!("{}", gl.get_program_info_log(program));
}
for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}
gl.use_program(Some(program));
Self {
program,
vertex_array,
}
}
}
pub fn clear(&self, gl: &glow::Context, background_color: Color) {
let [r, g, b, a] = background_color.into_linear();
unsafe {
gl.clear_color(r, g, b, a);
gl.clear(glow::COLOR_BUFFER_BIT);
}
}
pub fn draw(&self, gl: &glow::Context) {
unsafe {
gl.bind_vertex_array(Some(self.vertex_array));
gl.use_program(Some(self.program));
gl.draw_arrays(glow::TRIANGLES, 0, 3);
}
}
pub fn cleanup(&self, gl: &glow::Context) {
unsafe {
gl.delete_program(self.program);
gl.delete_vertex_array(self.vertex_array);
}
}
}

View file

@ -6,5 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = ["debug"] } iced = { path = "../..", features = ["debug", "lazy"] }
iced_lazy = { path = "../../lazy" }

View file

@ -1,10 +1,9 @@
use iced::theme; use iced::theme;
use iced::widget::{ use iced::widget::{
button, column, horizontal_space, pick_list, row, scrollable, text, button, column, horizontal_space, lazy, pick_list, row, scrollable, text,
text_input, text_input,
}; };
use iced::{Element, Length, Sandbox, Settings}; use iced::{Element, Length, Sandbox, Settings};
use iced_lazy::lazy;
use std::collections::HashSet; use std::collections::HashSet;
use std::hash::Hash; use std::hash::Hash;

View file

@ -6,5 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = [] } iced = { path = "../..", features = ["advanced"] }
iced_native = { path = "../../native" }

View file

@ -176,12 +176,15 @@ impl App {
} }
mod modal { mod modal {
use iced_native::alignment::Alignment; use iced::advanced::layout::{self, Layout};
use iced_native::widget::{self, Tree}; use iced::advanced::overlay;
use iced_native::{ use iced::advanced::renderer;
event, layout, mouse, overlay, renderer, Clipboard, Color, Element, use iced::advanced::widget::{self, Widget};
Event, Layout, Length, Point, Rectangle, Shell, Size, Widget, use iced::advanced::{self, Clipboard, Shell};
}; use iced::alignment::Alignment;
use iced::event;
use iced::mouse;
use iced::{Color, Element, Event, Length, Point, Rectangle, Size};
/// A widget that centers a modal element over some base element /// A widget that centers a modal element over some base element
pub struct Modal<'a, Message, Renderer> { pub struct Modal<'a, Message, Renderer> {
@ -216,14 +219,17 @@ mod modal {
impl<'a, Message, Renderer> Widget<Message, Renderer> impl<'a, Message, Renderer> Widget<Message, Renderer>
for Modal<'a, Message, Renderer> for Modal<'a, Message, Renderer>
where where
Renderer: iced_native::Renderer, Renderer: advanced::Renderer,
Message: Clone, Message: Clone,
{ {
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<widget::Tree> {
vec![Tree::new(&self.base), Tree::new(&self.modal)] vec![
widget::Tree::new(&self.base),
widget::Tree::new(&self.modal),
]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&self, tree: &mut widget::Tree) {
tree.diff_children(&[&self.base, &self.modal]); tree.diff_children(&[&self.base, &self.modal]);
} }
@ -245,7 +251,7 @@ mod modal {
fn on_event( fn on_event(
&mut self, &mut self,
state: &mut Tree, state: &mut widget::Tree,
event: Event, event: Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
@ -266,9 +272,9 @@ mod modal {
fn draw( fn draw(
&self, &self,
state: &Tree, state: &widget::Tree,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme, theme: &<Renderer as advanced::Renderer>::Theme,
style: &renderer::Style, style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
@ -287,7 +293,7 @@ mod modal {
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
state: &'b mut Tree, state: &'b mut widget::Tree,
layout: Layout<'_>, layout: Layout<'_>,
_renderer: &Renderer, _renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> { ) -> Option<overlay::Element<'b, Message, Renderer>> {
@ -304,7 +310,7 @@ mod modal {
fn mouse_interaction( fn mouse_interaction(
&self, &self,
state: &Tree, state: &widget::Tree,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
viewport: &Rectangle, viewport: &Rectangle,
@ -321,7 +327,7 @@ mod modal {
fn operate( fn operate(
&self, &self,
state: &mut Tree, state: &mut widget::Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>, operation: &mut dyn widget::Operation<Message>,
@ -337,7 +343,7 @@ mod modal {
struct Overlay<'a, 'b, Message, Renderer> { struct Overlay<'a, 'b, Message, Renderer> {
content: &'b mut Element<'a, Message, Renderer>, content: &'b mut Element<'a, Message, Renderer>,
tree: &'b mut Tree, tree: &'b mut widget::Tree,
size: Size, size: Size,
on_blur: Option<Message>, on_blur: Option<Message>,
} }
@ -345,7 +351,7 @@ mod modal {
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer> for Overlay<'a, 'b, Message, Renderer>
where where
Renderer: iced_native::Renderer, Renderer: advanced::Renderer,
Message: Clone, Message: Clone,
{ {
fn layout( fn layout(
@ -467,7 +473,7 @@ mod modal {
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>> impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: 'a + iced_native::Renderer, Renderer: 'a + advanced::Renderer,
Message: 'a + Clone, Message: 'a + Clone,
{ {
fn from(modal: Modal<'a, Message, Renderer>) -> Self { fn from(modal: Modal<'a, Message, Renderer>) -> Self {

View file

@ -55,17 +55,18 @@ impl Application for ModernArt {
} }
} }
impl<Message> canvas::Program<Message> for ModernArt { impl<Message> canvas::Program<Message, Renderer> for ModernArt {
type State = (); type State = ();
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: Cursor, _cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let geometry = self.cache.draw(bounds.size(), |frame| { let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
let num_squares = thread_rng().gen_range(0..1200); let num_squares = thread_rng().gen_range(0..1200);
let mut i = 0; let mut i = 0;

View file

@ -6,7 +6,7 @@ use iced::widget::canvas::stroke::{self, Stroke};
use iced::widget::canvas::{self, Canvas, Cursor, Geometry}; use iced::widget::canvas::{self, Canvas, Cursor, Geometry};
use iced::{ use iced::{
executor, touch, window, Application, Color, Command, Element, Length, executor, touch, window, Application, Color, Command, Element, Length,
Point, Rectangle, Settings, Subscription, Theme, Point, Rectangle, Renderer, Settings, Subscription, Theme,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -95,7 +95,7 @@ impl Application for Multitouch {
} }
} }
impl canvas::Program<Message> for State { impl canvas::Program<Message, Renderer> for State {
type State = (); type State = ();
fn update( fn update(
@ -125,11 +125,12 @@ impl canvas::Program<Message> for State {
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: Cursor, _cursor: Cursor,
) -> Vec<Geometry> { ) -> Vec<Geometry> {
let fingerweb = self.cache.draw(bounds.size(), |frame| { let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| {
if self.fingers.len() < 2 { if self.fingers.len() < 2 {
return; return;
} }

View file

@ -6,6 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = ["debug"] } iced = { path = "../..", features = ["debug", "lazy"] }
iced_native = { path = "../../native" }
iced_lazy = { path = "../../lazy" }

View file

@ -1,14 +1,16 @@
use iced::alignment::{self, Alignment}; use iced::alignment::{self, Alignment};
use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::keyboard; use iced::keyboard;
use iced::subscription;
use iced::theme::{self, Theme}; use iced::theme::{self, Theme};
use iced::widget::pane_grid::{self, PaneGrid}; use iced::widget::pane_grid::{self, PaneGrid};
use iced::widget::{button, column, container, row, scrollable, text}; use iced::widget::{
button, column, container, responsive, row, scrollable, text,
};
use iced::{ use iced::{
Application, Color, Command, Element, Length, Settings, Size, Subscription, Application, Color, Command, Element, Length, Settings, Size, Subscription,
}; };
use iced_lazy::responsive;
use iced_native::{event, subscription, Event};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
Example::run(Settings::default()) Example::run(Settings::default())

View file

@ -5,8 +5,8 @@ use iced::widget::canvas::event::{self, Event};
use iced::widget::canvas::{self, Canvas}; use iced::widget::canvas::{self, Canvas};
use iced::widget::{column, row, slider, text}; use iced::widget::{column, row, slider, text};
use iced::{ use iced::{
Application, Color, Command, Length, Point, Rectangle, Settings, Size, Application, Color, Command, Length, Point, Rectangle, Renderer, Settings,
Theme, Size, Theme,
}; };
use rand::Rng; use rand::Rng;
@ -134,11 +134,12 @@ impl canvas::Program<Message> for SierpinskiGraph {
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: canvas::Cursor, _cursor: canvas::Cursor,
) -> Vec<canvas::Geometry> { ) -> Vec<canvas::Geometry> {
let geom = self.cache.draw(bounds.size(), |frame| { let geom = self.cache.draw(renderer, bounds.size(), |frame| {
frame.stroke( frame.stroke(
&canvas::Path::rectangle(Point::ORIGIN, frame.size()), &canvas::Path::rectangle(Point::ORIGIN, frame.size()),
canvas::Stroke::default(), canvas::Stroke::default(),

View file

@ -15,8 +15,8 @@ use iced::widget::canvas::stroke::{self, Stroke};
use iced::widget::canvas::{Cursor, Path}; use iced::widget::canvas::{Cursor, Path};
use iced::window; use iced::window;
use iced::{ use iced::{
Application, Color, Command, Element, Length, Point, Rectangle, Settings, Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
Size, Subscription, Vector, Settings, Size, Subscription, Vector,
}; };
use std::time::Instant; use std::time::Instant;
@ -156,24 +156,26 @@ impl<Message> canvas::Program<Message> for State {
fn draw( fn draw(
&self, &self,
_state: &Self::State, _state: &Self::State,
renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: Cursor, _cursor: Cursor,
) -> Vec<canvas::Geometry> { ) -> Vec<canvas::Geometry> {
use std::f32::consts::PI; use std::f32::consts::PI;
let background = self.space_cache.draw(bounds.size(), |frame| { let background =
let stars = Path::new(|path| { self.space_cache.draw(renderer, bounds.size(), |frame| {
for (p, size) in &self.stars { let stars = Path::new(|path| {
path.rectangle(*p, Size::new(*size, *size)); for (p, size) in &self.stars {
} path.rectangle(*p, Size::new(*size, *size));
}
});
frame.translate(frame.center() - Point::ORIGIN);
frame.fill(&stars, Color::WHITE);
}); });
frame.translate(frame.center() - Point::ORIGIN); let system = self.system_cache.draw(renderer, bounds.size(), |frame| {
frame.fill(&stars, Color::WHITE);
});
let system = self.system_cache.draw(bounds.size(), |frame| {
let center = frame.center(); let center = frame.center();
let sun = Path::circle(center, Self::SUN_RADIUS); let sun = Path::circle(center, Self::SUN_RADIUS);

View file

@ -6,5 +6,4 @@ edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = [] } iced = { path = "../..", features = ["advanced"] }
iced_native = { path = "../../native" }

View file

@ -178,17 +178,23 @@ mod toast {
use std::fmt; use std::fmt;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use iced::advanced;
use iced::advanced::layout::{self, Layout};
use iced::advanced::overlay;
use iced::advanced::renderer;
use iced::advanced::widget::{self, Operation, Tree};
use iced::advanced::{Clipboard, Shell, Widget};
use iced::event::{self, Event};
use iced::mouse;
use iced::theme; use iced::theme;
use iced::widget::{ use iced::widget::{
button, column, container, horizontal_rule, horizontal_space, row, text, button, column, container, horizontal_rule, horizontal_space, row, text,
}; };
use iced::window;
use iced::{ use iced::{
Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme, Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme,
Vector, Vector,
}; };
use iced_native::widget::{tree, Operation, Tree};
use iced_native::{event, layout, mouse, overlay, renderer, window};
use iced_native::{Clipboard, Event, Layout, Shell, Widget};
pub const DEFAULT_TIMEOUT: u64 = 5; pub const DEFAULT_TIMEOUT: u64 = 5;
@ -326,13 +332,13 @@ mod toast {
self.content.as_widget().layout(renderer, limits) self.content.as_widget().layout(renderer, limits)
} }
fn tag(&self) -> tree::Tag { fn tag(&self) -> widget::tree::Tag {
struct Marker(Vec<Instant>); struct Marker(Vec<Instant>);
iced_native::widget::tree::Tag::of::<Marker>() widget::tree::Tag::of::<Marker>()
} }
fn state(&self) -> tree::State { fn state(&self) -> widget::tree::State {
iced_native::widget::tree::State::new(Vec::<Option<Instant>>::new()) widget::tree::State::new(Vec::<Option<Instant>>::new())
} }
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<Tree> {
@ -586,7 +592,7 @@ mod toast {
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme, theme: &<Renderer as advanced::Renderer>::Theme,
style: &renderer::Style, style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
@ -615,7 +621,7 @@ mod toast {
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn iced_native::widget::Operation<Message>, operation: &mut dyn widget::Operation<Message>,
) { ) {
operation.container(None, &mut |operation| { operation.container(None, &mut |operation| {
self.toasts self.toasts

Binary file not shown.

View file

@ -1,5 +1,6 @@
use iced::alignment::{self, Alignment}; use iced::alignment::{self, Alignment};
use iced::event::{self, Event}; use iced::event::{self, Event};
use iced::font::{self, Font};
use iced::keyboard::{self, KeyCode, Modifiers}; use iced::keyboard::{self, KeyCode, Modifiers};
use iced::subscription; use iced::subscription;
use iced::theme::{self, Theme}; use iced::theme::{self, Theme};
@ -9,7 +10,7 @@ use iced::widget::{
}; };
use iced::window; use iced::window;
use iced::{Application, Element}; use iced::{Application, Element};
use iced::{Color, Command, Font, Length, Settings, Subscription}; use iced::{Color, Command, Length, Settings, Subscription};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -44,6 +45,7 @@ struct State {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
Loaded(Result<SavedState, LoadError>), Loaded(Result<SavedState, LoadError>),
FontLoaded(Result<(), font::Error>),
Saved(Result<(), SaveError>), Saved(Result<(), SaveError>),
InputChanged(String), InputChanged(String),
CreateTask, CreateTask,
@ -62,7 +64,11 @@ impl Application for Todos {
fn new(_flags: ()) -> (Todos, Command<Message>) { fn new(_flags: ()) -> (Todos, Command<Message>) {
( (
Todos::Loading, Todos::Loading,
Command::perform(SavedState::load(), Message::Loaded), Command::batch(vec![
font::load(include_bytes!("../fonts/icons.ttf").as_slice())
.map(Message::FontLoaded),
Command::perform(SavedState::load(), Message::Loaded),
]),
) )
} }
@ -358,7 +364,8 @@ impl Task {
self.completed, self.completed,
TaskMessage::Completed, TaskMessage::Completed,
) )
.width(Length::Fill); .width(Length::Fill)
.text_shaping(text::Shaping::Advanced);
row![ row![
checkbox, checkbox,
@ -381,10 +388,14 @@ impl Task {
row![ row![
text_input, text_input,
button(row![delete_icon(), "Delete"].spacing(10)) button(
.on_press(TaskMessage::Delete) row![delete_icon(), "Delete"]
.padding(10) .spacing(10)
.style(theme::Button::Destructive) .align_items(Alignment::Center)
)
.on_press(TaskMessage::Delete)
.padding(10)
.style(theme::Button::Destructive)
] ]
.spacing(20) .spacing(20)
.align_items(Alignment::Center) .align_items(Alignment::Center)
@ -398,7 +409,7 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element<Message> {
let tasks_left = tasks.iter().filter(|task| !task.completed).count(); let tasks_left = tasks.iter().filter(|task| !task.completed).count();
let filter_button = |label, filter, current_filter| { let filter_button = |label, filter, current_filter| {
let label = text(label).size(16); let label = text(label);
let button = button(label).style(if filter == current_filter { let button = button(label).style(if filter == current_filter {
theme::Button::Primary theme::Button::Primary
@ -415,8 +426,7 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element<Message> {
tasks_left, tasks_left,
if tasks_left == 1 { "task" } else { "tasks" } if tasks_left == 1 { "task" } else { "tasks" }
)) ))
.width(Length::Fill) .width(Length::Fill),
.size(16),
row![ row![
filter_button("All", Filter::All, current_filter), filter_button("All", Filter::All, current_filter),
filter_button("Active", Filter::Active, current_filter), filter_button("Active", Filter::Active, current_filter),
@ -477,17 +487,13 @@ fn empty_message(message: &str) -> Element<'_, Message> {
} }
// Fonts // Fonts
const ICONS: Font = Font::External { const ICONS: Font = Font::with_name("Iced-Todos-Icons");
name: "Icons",
bytes: include_bytes!("../../todos/fonts/icons.ttf"),
};
fn icon(unicode: char) -> Text<'static> { fn icon(unicode: char) -> Text<'static> {
text(unicode.to_string()) text(unicode.to_string())
.font(ICONS) .font(ICONS)
.width(20) .width(20)
.horizontal_alignment(alignment::Horizontal::Center) .horizontal_alignment(alignment::Horizontal::Center)
.size(20)
} }
fn edit_icon() -> Text<'static> { fn edit_icon() -> Text<'static> {

Binary file not shown.

View file

@ -565,11 +565,6 @@ impl<'a> Step {
is_secure: bool, is_secure: bool,
is_showing_icon: bool, is_showing_icon: bool,
) -> Column<'a, StepMessage> { ) -> Column<'a, StepMessage> {
const ICON_FONT: Font = Font::External {
name: "Icons",
bytes: include_bytes!("../fonts/icons.ttf"),
};
let mut text_input = text_input("Type something to continue...", value) let mut text_input = text_input("Type something to continue...", value)
.on_input(StepMessage::InputChanged) .on_input(StepMessage::InputChanged)
.padding(10) .padding(10)
@ -577,9 +572,9 @@ impl<'a> Step {
if is_showing_icon { if is_showing_icon {
text_input = text_input.icon(text_input::Icon { text_input = text_input.icon(text_input::Icon {
font: ICON_FONT, font: Font::default(),
code_point: '\u{E900}', code_point: '🚀',
size: Some(35.0), size: Some(28.0),
spacing: 10.0, spacing: 10.0,
side: text_input::Side::Right, side: text_input::Side::Right,
}); });

View file

@ -7,4 +7,3 @@ publish = false
[dependencies] [dependencies]
iced = { path = "../.." } iced = { path = "../.." }
iced_native = { path = "../../native" }

View file

@ -1,12 +1,10 @@
use iced::event::{Event, MacOS, PlatformSpecific};
use iced::executor; use iced::executor;
use iced::subscription;
use iced::widget::{container, text}; use iced::widget::{container, text};
use iced::{ use iced::{
Application, Command, Element, Length, Settings, Subscription, Theme, Application, Command, Element, Length, Settings, Subscription, Theme,
}; };
use iced_native::{
event::{MacOS, PlatformSpecific},
Event,
};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
App::run(Settings::default()) App::run(Settings::default())
@ -19,7 +17,7 @@ struct App {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
EventOccurred(iced_native::Event), EventOccurred(Event),
} }
impl Application for App { impl Application for App {
@ -52,7 +50,7 @@ impl Application for App {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
iced_native::subscription::events().map(Message::EventOccurred) subscription::events().map(Message::EventOccurred)
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {

View file

@ -7,8 +7,6 @@ publish = false
[dependencies] [dependencies]
iced = { path = "../..", features = ["tokio", "debug"] } iced = { path = "../..", features = ["tokio", "debug"] }
iced_native = { path = "../../native" }
iced_futures = { path = "../../futures" }
once_cell = "1.15" once_cell = "1.15"
[dependencies.async-tungstenite] [dependencies.async-tungstenite]

Some files were not shown because too many files have changed in this diff Show more