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
- name: Check compilation of `todos` example
run: cargo build --package todos --target wasm32-unknown-unknown
- name: Check compilation of `integration_wgpu` example
run: cargo build --package integration_wgpu --target wasm32-unknown-unknown
- name: Check compilation of `integration` example
run: cargo build --package integration --target wasm32-unknown-unknown

View file

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

View file

@ -35,9 +35,9 @@ Inspired by [Elm].
* First-class support for async actions (use futures!)
* [Modular ecosystem] split into reusable parts:
* 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_glow`] supporting OpenGL 2.1+ and OpenGL ES 2.0+
* [`iced_tiny_skia`] offering a software alternative as a fallback
* A [windowing shell]
* 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
[renderer-agnostic native runtime]: native/
[`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_glow`]: glow/
[`iced_tiny_skia`]: tiny_skia/
[built-in renderers]: ECOSYSTEM.md#Renderers
[windowing shell]: winit/
[`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
[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
Contributions are greatly appreciated! If you want to contribute, please

View file

@ -9,6 +9,8 @@ repository = "https://github.com/iced-rs/iced"
[dependencies]
bitflags = "1.2"
thiserror = "1"
twox-hash = { version = "1.5", default-features = false }
[dependencies.palette]
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
/// producing them. Let's implement our __view logic__ now:
///
/// ```
/// ```no_run
/// # mod counter {
/// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
/// #
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {}
/// # pub struct Counter;
/// #
/// # impl Counter {
/// # pub fn view(&mut self) -> Text {
/// # Text::new("")
/// # pub fn view(
/// # &self,
/// # ) -> iced_core::Element<Message, iced_core::renderer::Null> {
/// # unimplemented!()
/// # }
/// # }
/// # }
/// #
/// # mod iced_wgpu {
/// # pub use iced_native::renderer::Null as Renderer;
/// # mod iced {
/// # 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;
/// #
/// # struct ManyCounters {
/// # counters: Vec<Counter>,
/// # }
/// #
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {
/// # Counter(usize, counter::Message)
/// # }
/// use iced_native::Element;
/// use iced_native::widget::Row;
/// use iced_wgpu::Renderer;
/// use counter::Counter;
///
/// use iced::widget::Row;
/// use iced::{Element, Renderer};
///
/// struct ManyCounters {
/// counters: Vec<Counter>,
/// }
///
/// #[derive(Debug, Clone, Copy)]
/// pub enum Message {
/// Counter(usize, counter::Message),
/// }
///
/// 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
/// self.counters.iter_mut().enumerate().fold(
/// Row::new().spacing(20),
@ -137,9 +161,10 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// // Here we turn our `Element<counter::Message>` into
/// // an `Element<Message>` by combining the `index` and the
/// // 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`:
///
/// ```
/// 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::Captured), Status::Captured);

View file

@ -1,19 +1,102 @@
//! Load and use fonts.
use std::hash::Hash;
/// A font.
#[derive(Debug, Clone, Copy, Default)]
pub enum Font {
/// The default font.
///
/// This is normally a font configured in a renderer or loaded from the
/// system.
#[default]
Default,
/// An external font.
External {
/// The name of the external font
name: &'static str,
/// The bytes of the external font
bytes: &'static [u8],
},
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Font {
/// The [`Family`] of the [`Font`].
pub family: Family,
/// The [`Weight`] of the [`Font`].
pub weight: Weight,
/// The [`Stretch`] of the [`Font`].
pub stretch: Stretch,
/// Whether if the [`Font`] is monospaced or not.
pub monospaced: bool,
}
impl Font {
/// A non-monospaced sans-serif font with normal [`Weight`].
pub const DEFAULT: Font = Font {
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;
/// A handle of some image data.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle {
id: u64,
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 {
fn as_ref(&self) -> &[u8] {
self.0.as_ref().as_ref()
@ -125,7 +133,7 @@ impl std::ops::Deref for Bytes {
}
/// The data of a raster image.
#[derive(Clone, Hash)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Data {
/// File data
Path(PathBuf),

View file

@ -1,3 +1,5 @@
use crate::Pixels;
/// The strategy used to fill space in a specific dimension.
#[derive(Debug, Clone, Copy, PartialEq)]
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 {
fn from(amount: f32) -> Self {
Length::Fixed(amount)

View file

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

View file

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

View file

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

View file

@ -1,12 +1,10 @@
use iced_core::{Point, Rectangle, Size};
use crate::event;
use crate::layout;
use crate::mouse;
use crate::overlay;
use crate::renderer;
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`]
/// children.

View file

@ -20,3 +20,9 @@ impl From<u16> for Pixels {
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)
}
/// 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`].
pub fn contains(&self, point: Point) -> bool {
self.x <= point.x
@ -74,6 +79,15 @@ impl Rectangle<f32> {
&& 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`].
pub fn intersection(
&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.
pub fn snap(self) -> Rectangle<u32> {
Rectangle {
@ -109,6 +147,16 @@ impl Rectangle<f32> {
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> {

View file

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

View file

@ -1,6 +1,8 @@
use crate::renderer::{self, Renderer};
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.
///
@ -16,7 +18,7 @@ impl Null {
}
impl Renderer for Null {
type Theme = Theme;
type Theme = ();
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 {
type Font = Font;
const ICON_FONT: Font = Font::Default;
const ICON_FONT: Font = Font::DEFAULT;
const CHECKMARK_ICON: char = '0';
const ARROW_DOWN_ICON: char = '0';
fn default_size(&self) -> f32 {
20.0
fn default_font(&self) -> Self::Font {
Font::default()
}
fn default_size(&self) -> f32 {
16.0
}
fn load_font(&mut self, _font: Cow<'static, [u8]>) {}
fn measure(
&self,
_content: &str,
_size: f32,
_line_height: text::LineHeight,
_font: Font,
_bounds: Size,
_shaping: text::Shaping,
) -> (f32, f32) {
(0.0, 20.0)
}
@ -62,8 +72,10 @@ impl text::Renderer for Null {
&self,
_contents: &str,
_size: f32,
_line_height: text::LineHeight,
_font: Self::Font,
_bounds: Size,
_shaping: text::Shaping,
_point: Point,
_nearest_only: bool,
) -> Option<text::Hit> {

View file

@ -1,7 +1,7 @@
use crate::{Padding, Vector};
/// 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> {
/// The width.
pub width: T,

View file

@ -7,7 +7,7 @@ use std::path::PathBuf;
use std::sync::Arc;
/// A handle of Svg data.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle {
id: u64,
data: Arc<Data>,
@ -57,7 +57,7 @@ impl Hash for Handle {
}
/// The data of a vectorial image.
#[derive(Clone, Hash)]
#[derive(Clone, Hash, PartialEq, Eq)]
pub enum Data {
/// File data
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.
//!
//! # 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;
//! Create custom widgets and operate on them.
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_input;
pub mod toggler;
pub mod tooltip;
pub mod tree;
pub mod vertical_slider;
mod action;
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 operation::Operation;
pub use text::Text;
pub use tree::Tree;
use crate::event::{self, Event};
use crate::layout;
use crate::layout::{self, Layout};
use crate::mouse;
use crate::overlay;
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.
///

View file

@ -24,7 +24,7 @@ impl Id {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Internal {
enum Internal {
Unique(usize),
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::text;
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;
pub use iced_style::text::{Appearance, StyleSheet};
pub use text::{LineHeight, Shaping};
/// 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)]
pub struct Text<'a, Renderer>
where
@ -33,11 +21,13 @@ where
{
content: Cow<'a, str>,
size: Option<f32>,
line_height: LineHeight,
width: Length,
height: Length,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
font: Renderer::Font,
font: Option<Renderer::Font>,
shaping: Shaping,
style: <Renderer::Theme as StyleSheet>::Style,
}
@ -51,11 +41,13 @@ where
Text {
content: content.into(),
size: None,
font: Default::default(),
line_height: LineHeight::default(),
font: None,
width: Length::Shrink,
height: Length::Shrink,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
shaping: Shaping::Basic,
style: Default::default(),
}
}
@ -66,11 +58,17 @@ where
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`].
///
/// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = font.into();
self.font = Some(font.into());
self
}
@ -112,6 +110,12 @@ where
self.vertical_alignment = alignment;
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>
@ -138,8 +142,14 @@ where
let bounds = limits.max();
let (width, height) =
renderer.measure(&self.content, size, self.font.clone(), bounds);
let (width, height) = renderer.measure(
&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));
@ -162,10 +172,12 @@ where
layout,
&self.content,
self.size,
self.font.clone(),
theme.appearance(self.style),
self.line_height,
self.font,
theme.appearance(self.style.clone()),
self.horizontal_alignment,
self.vertical_alignment,
self.shaping,
);
}
}
@ -186,10 +198,12 @@ pub fn draw<Renderer>(
layout: Layout<'_>,
content: &str,
size: Option<f32>,
font: Renderer::Font,
line_height: LineHeight,
font: Option<Renderer::Font>,
appearance: Appearance,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
shaping: Shaping,
) where
Renderer: text::Renderer,
{
@ -207,14 +221,18 @@ pub fn draw<Renderer>(
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,
size: size.unwrap_or_else(|| renderer.default_size()),
size,
line_height,
bounds: Rectangle { x, y, ..bounds },
color: appearance.color.unwrap_or(style.text_color),
font,
font: font.unwrap_or_else(|| renderer.default_font()),
horizontal_alignment,
vertical_alignment,
shaping,
});
}
@ -238,22 +256,52 @@ where
Self {
content: self.content.clone(),
size: self.size,
line_height: self.line_height,
width: self.width,
height: self.height,
horizontal_alignment: self.horizontal_alignment,
vertical_alignment: self.vertical_alignment,
font: self.font.clone(),
style: self.style,
font: self.font,
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>
where
Renderer: text::Renderer + 'a,
Renderer::Theme: StyleSheet,
{
fn from(contents: &'a str) -> Self {
Text::new(contents).into()
fn from(content: &'a str) -> Self {
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.
- [`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).
- [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application.
- [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application.
- [`integration`](integration), 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.
- [`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].

View file

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

View file

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

Binary file not shown.

View file

@ -1,10 +1,9 @@
use iced::widget::{checkbox, column, container};
use iced::{Element, Font, Length, Sandbox, Settings};
use iced::executor;
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 {
name: "Icons",
bytes: include_bytes!("../fonts/icons.ttf"),
};
const ICON_FONT: Font = Font::with_name("icons");
pub fn main() -> iced::Result {
Example::run(Settings::default())
@ -20,24 +19,35 @@ struct Example {
enum Message {
DefaultChecked(bool),
CustomChecked(bool),
FontLoaded(Result<(), font::Error>),
}
impl Sandbox for Example {
impl Application for Example {
type Message = Message;
type Flags = ();
type Executor = executor::Default;
type Theme = Theme;
fn new() -> Self {
Default::default()
fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
(
Self::default(),
font::load(include_bytes!("../fonts/icons.ttf").as_ref())
.map(Message::FontLoaded),
)
}
fn title(&self) -> String {
String::from("Checkbox - Iced")
}
fn update(&mut self, message: Message) {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::DefaultChecked(value) => self.default_checkbox = value,
Message::CustomChecked(value) => self.custom_checkbox = value,
Message::FontLoaded(_) => (),
}
Command::none()
}
fn view(&self) -> Element<Message> {
@ -49,6 +59,8 @@ impl Sandbox for Example {
font: ICON_FONT,
code_point: '\u{e901}',
size: None,
line_height: text::LineHeight::Relative(1.0),
shaping: text::Shaping::Basic,
});
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::{
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
Subscription, Theme, Vector,
Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
Settings, Subscription, Theme, Vector,
};
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 = ();
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
let clock = self.clock.draw(bounds.size(), |frame| {
let clock = self.clock.draw(renderer, bounds.size(), |frame| {
let center = frame.center();
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::{column, row, text, Slider};
use iced::{
alignment, Alignment, Color, Element, Length, Point, Rectangle, Sandbox,
Settings, Size, Vector,
alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer,
Sandbox, Settings, Size, Vector,
};
use palette::{self, convert::FromColor, Hsl, Srgb};
use std::marker::PhantomData;
@ -243,11 +243,12 @@ impl<Message> canvas::Program<Message> for Theme {
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
_theme: &iced::Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
let theme = self.canvas_cache.draw(bounds.size(), |frame| {
let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| {
self.draw(frame);
});

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,10 +9,10 @@ mod circle {
// 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_native::layout::{self, Layout};
use iced_native::renderer;
use iced_native::widget::{self, Widget};
use iced_native::{Color, Element, Length, Point, Rectangle, Size};
use iced::advanced::layout::{self, Layout};
use iced::advanced::renderer;
use iced::advanced::widget::{self, Widget};
use iced::{Color, Element, Length, Point, Rectangle, Size};
pub struct Circle {
radius: f32,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,23 +1,13 @@
//! This example showcases a simple native custom widget that renders using
//! arbitrary low-level geometry.
mod rainbow {
// For now, to implement a custom native widget you will need to add
// `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_graphics::primitive::{ColoredVertex2D, Primitive};
use iced_native::layout;
use iced_native::widget::{self, Widget};
use iced_native::{
Element, Layout, Length, Point, Rectangle, Size, Vector,
use iced::advanced::layout::{self, Layout};
use iced::advanced::renderer;
use iced::advanced::widget::{self, Widget};
use iced::{
Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector,
};
#[derive(Debug, Clone, Copy, Default)]
@ -27,10 +17,7 @@ mod rainbow {
Rainbow
}
impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow
where
B: Backend,
{
impl<Message> Widget<Message, Renderer> for Rainbow {
fn width(&self) -> Length {
Length::Fill
}
@ -41,7 +28,7 @@ mod rainbow {
fn layout(
&self,
_renderer: &Renderer<B, T>,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let size = limits.width(Length::Fill).resolve(Size::ZERO);
@ -52,15 +39,15 @@ mod rainbow {
fn draw(
&self,
_tree: &widget::Tree,
renderer: &mut Renderer<B, T>,
_theme: &T,
renderer: &mut Renderer,
_theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) {
use iced_graphics::triangle::Mesh2D;
use iced_native::Renderer as _;
use iced::advanced::Renderer as _;
use iced_graphics::primitive::Mesh2D;
let b = layout.bounds();
@ -151,10 +138,7 @@ mod rainbow {
}
}
impl<'a, Message, B, T> From<Rainbow> for Element<'a, Message, Renderer<B, T>>
where
B: Backend,
{
impl<'a, Message> From<Rainbow> for Element<'a, Message, Renderer> {
fn from(rainbow: Rainbow) -> Self {
Self::new(rainbow)
}

View file

@ -1,5 +1,5 @@
[package]
name = "integration_wgpu"
name = "integration"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
@ -7,7 +7,9 @@ publish = false
[dependencies]
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"
[target.'cfg(target_arch = "wasm32")'.dependencies]

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
use iced_wgpu::wgpu;
use iced_winit::Color;
use iced_winit::core::Color;
pub struct Scene {
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
[dependencies]
iced = { path = "../..", features = ["debug"] }
iced_lazy = { path = "../../lazy" }
iced = { path = "../..", features = ["debug", "lazy"] }

View file

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

View file

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

View file

@ -176,12 +176,15 @@ impl App {
}
mod modal {
use iced_native::alignment::Alignment;
use iced_native::widget::{self, Tree};
use iced_native::{
event, layout, mouse, overlay, renderer, Clipboard, Color, Element,
Event, Layout, Length, Point, Rectangle, Shell, Size, Widget,
};
use iced::advanced::layout::{self, Layout};
use iced::advanced::overlay;
use iced::advanced::renderer;
use iced::advanced::widget::{self, 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
pub struct Modal<'a, Message, Renderer> {
@ -216,14 +219,17 @@ mod modal {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Modal<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: advanced::Renderer,
Message: Clone,
{
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.base), Tree::new(&self.modal)]
fn children(&self) -> Vec<widget::Tree> {
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]);
}
@ -245,7 +251,7 @@ mod modal {
fn on_event(
&mut self,
state: &mut Tree,
state: &mut widget::Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
@ -266,9 +272,9 @@ mod modal {
fn draw(
&self,
state: &Tree,
state: &widget::Tree,
renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme,
theme: &<Renderer as advanced::Renderer>::Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
@ -287,7 +293,7 @@ mod modal {
fn overlay<'b>(
&'b mut self,
state: &'b mut Tree,
state: &'b mut widget::Tree,
layout: Layout<'_>,
_renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
@ -304,7 +310,7 @@ mod modal {
fn mouse_interaction(
&self,
state: &Tree,
state: &widget::Tree,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
@ -321,7 +327,7 @@ mod modal {
fn operate(
&self,
state: &mut Tree,
state: &mut widget::Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
@ -337,7 +343,7 @@ mod modal {
struct Overlay<'a, 'b, Message, Renderer> {
content: &'b mut Element<'a, Message, Renderer>,
tree: &'b mut Tree,
tree: &'b mut widget::Tree,
size: Size,
on_blur: Option<Message>,
}
@ -345,7 +351,7 @@ mod modal {
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: advanced::Renderer,
Message: Clone,
{
fn layout(
@ -467,7 +473,7 @@ mod modal {
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a + iced_native::Renderer,
Renderer: 'a + advanced::Renderer,
Message: 'a + Clone,
{
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 = ();
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> 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 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::{
executor, touch, window, Application, Color, Command, Element, Length,
Point, Rectangle, Settings, Subscription, Theme,
Point, Rectangle, Renderer, Settings, Subscription, Theme,
};
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 = ();
fn update(
@ -125,11 +125,12 @@ impl canvas::Program<Message> for State {
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
let fingerweb = self.cache.draw(bounds.size(), |frame| {
let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| {
if self.fingers.len() < 2 {
return;
}

View file

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

View file

@ -1,14 +1,16 @@
use iced::alignment::{self, Alignment};
use iced::event::{self, Event};
use iced::executor;
use iced::keyboard;
use iced::subscription;
use iced::theme::{self, Theme};
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::{
Application, Color, Command, Element, Length, Settings, Size, Subscription,
};
use iced_lazy::responsive;
use iced_native::{event, subscription, Event};
pub fn main() -> iced::Result {
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::{column, row, slider, text};
use iced::{
Application, Color, Command, Length, Point, Rectangle, Settings, Size,
Theme,
Application, Color, Command, Length, Point, Rectangle, Renderer, Settings,
Size, Theme,
};
use rand::Rng;
@ -134,11 +134,12 @@ impl canvas::Program<Message> for SierpinskiGraph {
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: canvas::Cursor,
) -> Vec<canvas::Geometry> {
let geom = self.cache.draw(bounds.size(), |frame| {
let geom = self.cache.draw(renderer, bounds.size(), |frame| {
frame.stroke(
&canvas::Path::rectangle(Point::ORIGIN, frame.size()),
canvas::Stroke::default(),

View file

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

View file

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

View file

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

Binary file not shown.

View file

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

Binary file not shown.

View file

@ -565,11 +565,6 @@ impl<'a> Step {
is_secure: bool,
is_showing_icon: bool,
) -> 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)
.on_input(StepMessage::InputChanged)
.padding(10)
@ -577,9 +572,9 @@ impl<'a> Step {
if is_showing_icon {
text_input = text_input.icon(text_input::Icon {
font: ICON_FONT,
code_point: '\u{E900}',
size: Some(35.0),
font: Font::default(),
code_point: '🚀',
size: Some(28.0),
spacing: 10.0,
side: text_input::Side::Right,
});

View file

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

View file

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

View file

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

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