Merge remote-tracking branch 'origin/master' into feat/multi-window-support

# Conflicts:
#	examples/events/src/main.rs
#	glutin/src/application.rs
#	native/src/window.rs
#	winit/src/window.rs
This commit is contained in:
Bingus 2023-01-18 15:01:17 -08:00
commit 70d487ba20
No known key found for this signature in database
GPG key ID: 5F84D2AA40A9F170
57 changed files with 815 additions and 446 deletions

View file

@ -63,7 +63,7 @@ body:
If you are using an older release, please upgrade to the latest one before filing an issue. If you are using an older release, please upgrade to the latest one before filing an issue.
options: options:
- master - master
- 0.6 - 0.7
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View file

@ -6,6 +6,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.7.0] - 2023-01-14
### Added
- Widget-driven animations. [#1647](https://github.com/iced-rs/iced/pull/1647)
- Multidirectional scrolling support for `Scrollable`. [#1550](https://github.com/iced-rs/iced/pull/1550)
- `VerticalSlider` widget. [#1596](https://github.com/iced-rs/iced/pull/1596)
- `Shift+Click` text selection support in `TextInput`. [#1622](https://github.com/iced-rs/iced/pull/1622)
- Profiling support with the `chrome-trace` feature. [#1565](https://github.com/iced-rs/iced/pull/1565)
- Customization of the handle of a `PickList`. [#1562](https://github.com/iced-rs/iced/pull/1562)
- `window` action to request user attention. [#1584](https://github.com/iced-rs/iced/pull/1584)
- `window` action to gain focus. [#1585](https://github.com/iced-rs/iced/pull/1585)
- `window` action to toggle decorations. [#1588](https://github.com/iced-rs/iced/pull/1588)
- `Copy` implementation for `gradient::Location`. [#1636](https://github.com/iced-rs/iced/pull/1636)
### Changed
- Replaced `Application::should_exit` with a `window::close` action. [#1606](https://github.com/iced-rs/iced/pull/1606)
- Made `focusable::Count` fields public. [#1635](https://github.com/iced-rs/iced/pull/1635)
- Added `Dependency` argument to the closure of `Lazy`. [#1646](https://github.com/iced-rs/iced/pull/1646)
- Switched arguments order of `Toggler::new` for consistency. [#1616](https://github.com/iced-rs/iced/pull/1616)
- Switched arguments order of `Checkbox::new` for consistency. [#1633](https://github.com/iced-rs/iced/pull/1633)
### Fixed
- Compilation error in `iced_glow` when the `image` feature is enabled but `svg` isn't. [#1593](https://github.com/iced-rs/iced/pull/1593)
- Widget operations for `Responsive` widget. [#1615](https://github.com/iced-rs/iced/pull/1615)
- Overlay placement for `Responsive`. [#1638](https://github.com/iced-rs/iced/pull/1638)
- `overlay` implementation for `Lazy`. [#1644](https://github.com/iced-rs/iced/pull/1644)
- Minor typo in documentation. [#1624](https://github.com/iced-rs/iced/pull/1624)
- Links in documentation. [#1634](https://github.com/iced-rs/iced/pull/1634)
- Missing comment in documentation. [#1648](https://github.com/iced-rs/iced/pull/1648)
Many thanks to...
- @13r0ck
- @Araxeus
- @ben-wallis
- @bungoboingo
- @casperstorm
- @nicksenger
- @Night-Hunter-NF
- @rpitasky
- @rs017991
- @tarkah
- @wiktor-k
## [0.6.0] - 2022-12-07 ## [0.6.0] - 2022-12-07
### Added ### Added
- Support for non-uniform border radius for `Primitive::Quad`. [#1506](https://github.com/iced-rs/iced/pull/1506) - Support for non-uniform border radius for `Primitive::Quad`. [#1506](https://github.com/iced-rs/iced/pull/1506)
@ -321,7 +364,8 @@ Many thanks to...
### Added ### Added
- First release! :tada: - First release! :tada:
[Unreleased]: https://github.com/iced-rs/iced/compare/0.6.0...HEAD [Unreleased]: https://github.com/iced-rs/iced/compare/0.7.0...HEAD
[0.7.0]: https://github.com/iced-rs/iced/compare/0.6.0...0.7.0
[0.6.0]: https://github.com/iced-rs/iced/compare/0.5.0...0.6.0 [0.6.0]: https://github.com/iced-rs/iced/compare/0.5.0...0.6.0
[0.5.0]: https://github.com/iced-rs/iced/compare/0.4.2...0.5.0 [0.5.0]: https://github.com/iced-rs/iced/compare/0.4.2...0.5.0
[0.4.2]: https://github.com/iced-rs/iced/compare/0.4.1...0.4.2 [0.4.2]: https://github.com/iced-rs/iced/compare/0.4.1...0.4.2

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced" name = "iced"
version = "0.6.0" version = "0.7.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"
description = "A cross-platform GUI library inspired by Elm" description = "A cross-platform GUI library inspired by Elm"
@ -68,13 +68,13 @@ members = [
] ]
[dependencies] [dependencies]
iced_core = { version = "0.6", path = "core" } iced_core = { version = "0.7", path = "core" }
iced_futures = { version = "0.5", path = "futures" } iced_futures = { version = "0.5", path = "futures" }
iced_native = { version = "0.7", path = "native" } iced_native = { version = "0.8", path = "native" }
iced_graphics = { version = "0.5", path = "graphics" } iced_graphics = { version = "0.6", path = "graphics" }
iced_winit = { version = "0.6", path = "winit", features = ["application"] } iced_winit = { version = "0.7", path = "winit", features = ["application"] }
iced_glutin = { version = "0.5", path = "glutin", optional = true } iced_glutin = { version = "0.6", path = "glutin", optional = true }
iced_glow = { version = "0.5", path = "glow", optional = true } iced_glow = { version = "0.6", path = "glow", optional = true }
thiserror = "1.0" thiserror = "1.0"
[dependencies.image_rs] [dependencies.image_rs]
@ -83,10 +83,10 @@ package = "image"
optional = true optional = true
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
iced_wgpu = { version = "0.7", path = "wgpu", optional = true } iced_wgpu = { version = "0.8", path = "wgpu", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
iced_wgpu = { version = "0.7", path = "wgpu", features = ["webgl"], optional = true } iced_wgpu = { version = "0.8", path = "wgpu", features = ["webgl"], optional = true }
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]

View file

@ -68,7 +68,7 @@ __Iced is currently experimental software.__ [Take a look at the roadmap],
Add `iced` as a dependency in your `Cargo.toml`: Add `iced` as a dependency in your `Cargo.toml`:
```toml ```toml
iced = "0.6" iced = "0.7"
``` ```
If your project is using a Rust edition older than 2021, then you will need to If your project is using a Rust edition older than 2021, then you will need to
@ -215,7 +215,7 @@ cargo run --features iced/glow --package game_of_life
and then use it in your project with and then use it in your project with
```toml ```toml
iced = { version = "0.6", default-features = false, features = ["glow"] } iced = { version = "0.7", default-features = false, features = ["glow"] }
``` ```
__NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0, __NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0,

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_core" name = "iced_core"
version = "0.6.2" version = "0.7.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"
description = "The essential concepts of Iced" description = "The essential concepts of Iced"
@ -15,4 +15,4 @@ version = "0.6"
optional = true optional = true
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-timer = { version = "0.2" } instant = "0.1"

View file

@ -18,7 +18,7 @@ This crate is meant to be a starting point for an Iced runtime.
Add `iced_core` as a dependency in your `Cargo.toml`: Add `iced_core` as a dependency in your `Cargo.toml`:
```toml ```toml
iced_core = "0.4" iced_core = "0.7"
``` ```
__Iced moves fast and the `master` branch can contain breaking changes!__ If __Iced moves fast and the `master` branch can contain breaking changes!__ If

View file

@ -7,7 +7,7 @@
//! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true) //! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true)
//! //!
//! [Iced]: https://github.com/iced-rs/iced //! [Iced]: https://github.com/iced-rs/iced
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
//! [`iced_web`]: https://github.com/iced-rs/iced_web //! [`iced_web`]: https://github.com/iced-rs/iced_web
#![doc( #![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"

View file

@ -1,9 +1,13 @@
//! Keep track of time, both in native and web platforms! //! Keep track of time, both in native and web platforms!
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub use wasm_timer::Instant; pub use instant::Instant;
#[cfg(target_arch = "wasm32")]
pub use instant::Duration;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub use std::time::Instant; pub use std::time::Instant;
#[cfg(not(target_arch = "wasm32"))]
pub use std::time::Duration; pub use std::time::Duration;

View file

@ -1,10 +0,0 @@
[package]
name = "cached"
version = "0.1.0"
authors = ["Nick Senger <dev@nsenger.com>"]
edition = "2021"
publish = false
[dependencies]
iced = { path = "../..", features = ["debug"] }
iced_lazy = { path = "../../lazy" }

View file

@ -1,139 +0,0 @@
use iced::theme;
use iced::widget::{
button, column, horizontal_space, row, scrollable, text, text_input,
};
use iced::{Element, Length, Sandbox, Settings};
use iced_lazy::lazy;
use std::collections::HashSet;
pub fn main() -> iced::Result {
App::run(Settings::default())
}
struct App {
options: HashSet<String>,
input: String,
order: Order,
}
impl Default for App {
fn default() -> Self {
Self {
options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"]
.into_iter()
.map(ToString::to_string)
.collect(),
input: Default::default(),
order: Order::Ascending,
}
}
}
#[derive(Debug, Clone)]
enum Message {
InputChanged(String),
ToggleOrder,
DeleteOption(String),
AddOption(String),
}
impl Sandbox for App {
type Message = Message;
fn new() -> Self {
Self::default()
}
fn title(&self) -> String {
String::from("Cached - Iced")
}
fn update(&mut self, message: Message) {
match message {
Message::InputChanged(input) => {
self.input = input;
}
Message::ToggleOrder => {
self.order = match self.order {
Order::Ascending => Order::Descending,
Order::Descending => Order::Ascending,
}
}
Message::AddOption(option) => {
self.options.insert(option);
self.input.clear();
}
Message::DeleteOption(option) => {
self.options.remove(&option);
}
}
}
fn view(&self) -> Element<Message> {
let options = lazy((&self.order, self.options.len()), || {
let mut options: Vec<_> = self.options.iter().collect();
options.sort_by(|a, b| match self.order {
Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()),
Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()),
});
column(
options
.into_iter()
.map(|option| {
row![
text(option),
horizontal_space(Length::Fill),
button("Delete")
.on_press(Message::DeleteOption(
option.to_string(),
),)
.style(theme::Button::Destructive)
]
.into()
})
.collect(),
)
.spacing(10)
});
column![
scrollable(options).height(Length::Fill),
row![
text_input(
"Add a new option",
&self.input,
Message::InputChanged,
)
.on_submit(Message::AddOption(self.input.clone())),
button(text(format!("Toggle Order ({})", self.order)))
.on_press(Message::ToggleOrder)
]
.spacing(10)
]
.spacing(20)
.padding(20)
.into()
}
}
#[derive(Debug, Hash)]
enum Order {
Ascending,
Descending,
}
impl std::fmt::Display for Order {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Ascending => "Ascending",
Self::Descending => "Descending",
}
)
}
}

View file

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

View file

@ -1,11 +1,12 @@
use iced::alignment; use iced::alignment;
use iced::executor; use iced::executor;
use iced::widget::{button, checkbox, container, text, Column}; use iced::widget::{button, checkbox, container, text, Column};
use iced::window;
use iced::{ use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription, Alignment, Application, Command, Element, Length, Settings, Subscription,
Theme, Theme,
}; };
use iced_native::{window, Event}; use iced_native::Event;
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
Events::run(Settings { Events::run(Settings {
@ -18,14 +19,13 @@ pub fn main() -> iced::Result {
struct Events { struct Events {
last: Vec<iced_native::Event>, last: Vec<iced_native::Event>,
enabled: bool, enabled: bool,
should_exit: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
EventOccurred(iced_native::Event), EventOccurred(iced_native::Event),
Toggled(bool), Toggled(bool),
Exit, Exit(window::Id),
} }
impl Application for Events { impl Application for Events {
@ -50,31 +50,29 @@ impl Application for Events {
if self.last.len() > 5 { if self.last.len() > 5 {
let _ = self.last.remove(0); let _ = self.last.remove(0);
} }
Command::none()
} }
Message::EventOccurred(event) => { Message::EventOccurred(event) => {
if let Event::Window(_, window::Event::CloseRequested) = event { if let Event::Window(id, window::Event::CloseRequested) = event {
self.should_exit = true; window::close(id)
} else {
Command::none()
} }
} }
Message::Toggled(enabled) => { Message::Toggled(enabled) => {
self.enabled = enabled; self.enabled = enabled;
}
Message::Exit => {
self.should_exit = true;
}
};
Command::none() Command::none()
}
Message::Exit(id) => window::close(id),
}
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
iced_native::subscription::events().map(Message::EventOccurred) iced_native::subscription::events().map(Message::EventOccurred)
} }
fn should_exit(&self) -> bool {
self.should_exit
}
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
let events = Column::with_children( let events = Column::with_children(
self.last self.last

View file

@ -1,5 +1,7 @@
use iced::executor;
use iced::widget::{button, column, container}; use iced::widget::{button, column, container};
use iced::{Alignment, Element, Length, Sandbox, Settings}; use iced::window;
use iced::{Alignment, Application, Command, Element, Length, Settings, Theme};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
Exit::run(Settings::default()) Exit::run(Settings::default())
@ -8,7 +10,6 @@ pub fn main() -> iced::Result {
#[derive(Default)] #[derive(Default)]
struct Exit { struct Exit {
show_confirm: bool, show_confirm: bool,
exit: bool,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -17,28 +18,27 @@ enum Message {
Exit, Exit,
} }
impl Sandbox for Exit { impl Application for Exit {
type Executor = executor::Default;
type Message = Message; type Message = Message;
type Theme = Theme;
type Flags = ();
fn new() -> Self { fn new(_flags: ()) -> (Self, Command<Message>) {
Self::default() (Self::default(), Command::none())
} }
fn title(&self) -> String { fn title(&self) -> String {
String::from("Exit - Iced") String::from("Exit - Iced")
} }
fn should_exit(&self) -> bool { fn update(&mut self, message: Message) -> Command<Message> {
self.exit
}
fn update(&mut self, message: Message) {
match message { match message {
Message::Confirm => { Message::Confirm => window::close(),
self.exit = true;
}
Message::Exit => { Message::Exit => {
self.show_confirm = true; self.show_confirm = true;
Command::none()
} }
} }
} }

View file

@ -1,18 +1,21 @@
use iced::theme; use iced::theme;
use iced::widget::{ use iced::widget::{
button, column, horizontal_space, row, scrollable, text, text_input, button, column, horizontal_space, pick_list, row, scrollable, text,
text_input,
}; };
use iced::{Element, Length, Sandbox, Settings}; use iced::{Element, Length, Sandbox, Settings};
use iced_lazy::lazy; use iced_lazy::lazy;
use std::collections::HashSet; use std::collections::HashSet;
use std::hash::Hash;
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
App::run(Settings::default()) App::run(Settings::default())
} }
struct App { struct App {
options: HashSet<String>, version: u8,
items: HashSet<Item>,
input: String, input: String,
order: Order, order: Order,
} }
@ -20,9 +23,10 @@ struct App {
impl Default for App { impl Default for App {
fn default() -> Self { fn default() -> Self {
Self { Self {
options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"] version: 0,
items: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"]
.into_iter() .into_iter()
.map(ToString::to_string) .map(From::from)
.collect(), .collect(),
input: Default::default(), input: Default::default(),
order: Order::Ascending, order: Order::Ascending,
@ -30,12 +34,92 @@ impl Default for App {
} }
} }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum Color {
#[default]
Black,
Red,
Orange,
Yellow,
Green,
Blue,
Purple,
}
impl Color {
const ALL: &[Color] = &[
Color::Black,
Color::Red,
Color::Orange,
Color::Yellow,
Color::Green,
Color::Blue,
Color::Purple,
];
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Black => "Black",
Self::Red => "Red",
Self::Orange => "Orange",
Self::Yellow => "Yellow",
Self::Green => "Green",
Self::Blue => "Blue",
Self::Purple => "Purple",
})
}
}
impl From<Color> for iced::Color {
fn from(value: Color) -> Self {
match value {
Color::Black => iced::Color::from_rgb8(0, 0, 0),
Color::Red => iced::Color::from_rgb8(220, 50, 47),
Color::Orange => iced::Color::from_rgb8(203, 75, 22),
Color::Yellow => iced::Color::from_rgb8(181, 137, 0),
Color::Green => iced::Color::from_rgb8(133, 153, 0),
Color::Blue => iced::Color::from_rgb8(38, 139, 210),
Color::Purple => iced::Color::from_rgb8(108, 113, 196),
}
}
}
#[derive(Clone, Debug, Eq)]
struct Item {
name: String,
color: Color,
}
impl Hash for Item {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for Item {
fn eq(&self, other: &Self) -> bool {
self.name.eq(&other.name)
}
}
impl From<&str> for Item {
fn from(s: &str) -> Self {
Self {
name: s.to_owned(),
color: Default::default(),
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
InputChanged(String), InputChanged(String),
ToggleOrder, ToggleOrder,
DeleteOption(String), DeleteItem(Item),
AddOption(String), AddItem(String),
ItemColorChanged(Item, Color),
} }
impl Sandbox for App { impl Sandbox for App {
@ -46,7 +130,7 @@ impl Sandbox for App {
} }
fn title(&self) -> String { fn title(&self) -> String {
String::from("Cached - Iced") String::from("Lazy - Iced")
} }
fn update(&mut self, message: Message) { fn update(&mut self, message: Message) {
@ -55,43 +139,71 @@ impl Sandbox for App {
self.input = input; self.input = input;
} }
Message::ToggleOrder => { Message::ToggleOrder => {
self.version = self.version.wrapping_add(1);
self.order = match self.order { self.order = match self.order {
Order::Ascending => Order::Descending, Order::Ascending => Order::Descending,
Order::Descending => Order::Ascending, Order::Descending => Order::Ascending,
} }
} }
Message::AddOption(option) => { Message::AddItem(name) => {
self.options.insert(option); self.version = self.version.wrapping_add(1);
self.items.insert(name.as_str().into());
self.input.clear(); self.input.clear();
} }
Message::DeleteOption(option) => { Message::DeleteItem(item) => {
self.options.remove(&option); self.version = self.version.wrapping_add(1);
self.items.remove(&item);
}
Message::ItemColorChanged(item, color) => {
self.version = self.version.wrapping_add(1);
if self.items.remove(&item) {
self.items.insert(Item {
name: item.name,
color,
});
}
} }
} }
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
let options = lazy((&self.order, self.options.len()), || { let options = lazy(self.version, |_| {
let mut options: Vec<_> = self.options.iter().collect(); let mut items: Vec<_> = self.items.iter().cloned().collect();
options.sort_by(|a, b| match self.order { items.sort_by(|a, b| match self.order {
Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()), Order::Ascending => {
Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()), a.name.to_lowercase().cmp(&b.name.to_lowercase())
}
Order::Descending => {
b.name.to_lowercase().cmp(&a.name.to_lowercase())
}
}); });
column( column(
options items
.into_iter() .into_iter()
.map(|option| { .map(|item| {
let button = button("Delete")
.on_press(Message::DeleteItem(item.clone()))
.style(theme::Button::Destructive);
row![ row![
text(option), text(&item.name)
.style(theme::Text::Color(item.color.into())),
horizontal_space(Length::Fill), horizontal_space(Length::Fill),
button("Delete") pick_list(
.on_press(Message::DeleteOption( Color::ALL,
option.to_string(), Some(item.color),
),) move |color| {
.style(theme::Button::Destructive) Message::ItemColorChanged(
item.clone(),
color,
)
}
),
button
] ]
.spacing(20)
.into() .into()
}) })
.collect(), .collect(),
@ -107,7 +219,7 @@ impl Sandbox for App {
&self.input, &self.input,
Message::InputChanged, Message::InputChanged,
) )
.on_submit(Message::AddOption(self.input.clone())), .on_submit(Message::AddItem(self.input.clone())),
button(text(format!("Toggle Order ({})", self.order))) button(text(format!("Toggle Order ({})", self.order)))
.on_press(Message::ToggleOrder) .on_press(Message::ToggleOrder)
] ]

View file

@ -9,7 +9,6 @@
use iced::application; use iced::application;
use iced::executor; use iced::executor;
use iced::theme::{self, Theme}; use iced::theme::{self, Theme};
use iced::time;
use iced::widget::canvas; use iced::widget::canvas;
use iced::widget::canvas::gradient::{self, Gradient}; use iced::widget::canvas::gradient::{self, Gradient};
use iced::widget::canvas::stroke::{self, Stroke}; use iced::widget::canvas::stroke::{self, Stroke};
@ -90,7 +89,7 @@ impl Application for SolarSystem {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
time::every(time::Duration::from_millis(10)).map(Message::Tick) window::frames().map(Message::Tick)
} }
} }

View file

@ -125,9 +125,9 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how /// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how
/// to listen to time. /// to listen to time.
/// ///
/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples /// [examples]: https://github.com/iced-rs/iced/tree/0.7/examples
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.6/examples/download_progress /// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.7/examples/download_progress
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.6/examples/stopwatch /// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.7/examples/stopwatch
pub trait Recipe<Hasher: std::hash::Hasher, Event> { pub trait Recipe<Hasher: std::hash::Hasher, Event> {
/// The events that will be produced by a [`Subscription`] with this /// The events that will be produced by a [`Subscription`] with this
/// [`Recipe`]. /// [`Recipe`].

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_glow" name = "iced_glow"
version = "0.5.1" version = "0.6.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"
description = "A glow renderer for iced" description = "A glow renderer for iced"
@ -34,11 +34,11 @@ bytemuck = "1.4"
log = "0.4" log = "0.4"
[dependencies.iced_native] [dependencies.iced_native]
version = "0.7" version = "0.8"
path = "../native" path = "../native"
[dependencies.iced_graphics] [dependencies.iced_graphics]
version = "0.5" version = "0.6"
path = "../graphics" path = "../graphics"
features = ["font-fallback", "font-icons", "opengl"] features = ["font-fallback", "font-icons", "opengl"]

View file

@ -28,7 +28,7 @@ Currently, `iced_glow` supports the following primitives:
Add `iced_glow` as a dependency in your `Cargo.toml`: Add `iced_glow` as a dependency in your `Cargo.toml`:
```toml ```toml
iced_glow = "0.2" iced_glow = "0.6"
``` ```
__Iced moves fast and the `master` branch can contain breaking changes!__ If __Iced moves fast and the `master` branch can contain breaking changes!__ If

View file

@ -3,7 +3,7 @@
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true) //! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//! //!
//! [`glow`]: https://github.com/grovesNL/glow //! [`glow`]: https://github.com/grovesNL/glow
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
#![doc( #![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)] )]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_glutin" name = "iced_glutin"
version = "0.5.0" version = "0.6.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"
description = "A glutin runtime for Iced" description = "A glutin runtime for Iced"
@ -19,26 +19,26 @@ multi_window = ["iced_winit/multi_window"]
[dependencies.raw-window-handle] [dependencies.raw-window-handle]
version = "0.5.0" version = "0.5.0"
[dependencies.log] [dependencies]
version = "0.4" log = "0.4"
[dependencies.glutin] [dependencies.glutin]
version = "0.30" version = "0.30"
[dependencies.iced_native] [dependencies.iced_native]
version = "0.7" version = "0.8"
path = "../native" path = "../native"
[dependencies.iced_winit] [dependencies.iced_winit]
version = "0.6" version = "0.7"
path = "../winit" path = "../winit"
features = ["application"] features = ["application"]
[dependencies.iced_graphics] [dependencies.iced_graphics]
version = "0.5" version = "0.6"
path = "../graphics" path = "../graphics"
features = ["opengl"] features = ["opengl"]
[dependencies.tracing] [dependencies.tracing]
version = "0.1.6" version = "0.1.6"
optional = true optional = true

View file

@ -20,7 +20,7 @@ It exposes a renderer-agnostic `Application` trait that can be implemented and t
Add `iced_glutin` as a dependency in your `Cargo.toml`: Add `iced_glutin` as a dependency in your `Cargo.toml`:
```toml ```toml
iced_glutin = "0.2" iced_glutin = "0.6"
``` ```
__Iced moves fast and the `master` branch can contain breaking changes!__ If __Iced moves fast and the `master` branch can contain breaking changes!__ If

View file

@ -11,9 +11,10 @@ use iced_winit::conversion;
use iced_winit::futures; use iced_winit::futures;
use iced_winit::futures::channel::mpsc; use iced_winit::futures::channel::mpsc;
use iced_winit::renderer; use iced_winit::renderer;
use iced_winit::time::Instant;
use iced_winit::user_interface; use iced_winit::user_interface;
use iced_winit::winit; use iced_winit::winit;
use iced_winit::{Clipboard, Command, Debug, Proxy, Settings}; use iced_winit::{Clipboard, Command, Debug, Event, Proxy, Settings};
use glutin::config::{ use glutin::config::{
Config, ConfigSurfaceTypes, ConfigTemplateBuilder, GlConfig, Config, ConfigSurfaceTypes, ConfigTemplateBuilder, GlConfig,
@ -280,7 +281,8 @@ where
let context = { context.make_not_current().expect("make context current") }; let context = { context.make_not_current().expect("make context current") };
let (mut sender, receiver) = mpsc::unbounded(); let (mut event_sender, event_receiver) = mpsc::unbounded();
let (control_sender, mut control_receiver) = mpsc::unbounded();
let mut instance = Box::pin({ let mut instance = Box::pin({
let run_instance = run_instance::<A, E, C>( let run_instance = run_instance::<A, E, C>(
@ -290,7 +292,8 @@ where
runtime, runtime,
proxy, proxy,
debug, debug,
receiver, event_receiver,
control_sender,
window, window,
surface, surface,
context, context,
@ -330,14 +333,20 @@ where
}; };
if let Some(event) = event { if let Some(event) = event {
sender.start_send(event).expect("Send event"); event_sender.start_send(event).expect("Send event");
let poll = instance.as_mut().poll(&mut context); let poll = instance.as_mut().poll(&mut context);
*control_flow = match poll { match poll {
task::Poll::Pending => ControlFlow::Wait, task::Poll::Pending => {
task::Poll::Ready(_) => ControlFlow::Exit, if let Ok(Some(flow)) = control_receiver.try_next() {
}; *control_flow = flow;
}
}
task::Poll::Ready(_) => {
*control_flow = ControlFlow::Exit;
}
}
} }
}); });
@ -351,7 +360,8 @@ async fn run_instance<A, E, C>(
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
mut proxy: winit::event_loop::EventLoopProxy<A::Message>, mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
mut debug: Debug, mut debug: Debug,
mut receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>>, mut event_receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>, >,
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
window: winit::window::Window, window: winit::window::Window,
surface: Surface<WindowSurface>, surface: Surface<WindowSurface>,
context: NotCurrentContext, context: NotCurrentContext,
@ -364,6 +374,7 @@ async fn run_instance<A, E, C>(
<A::Renderer as iced_native::Renderer>::Theme: StyleSheet, <A::Renderer as iced_native::Renderer>::Theme: StyleSheet,
{ {
use iced_winit::futures::stream::StreamExt; use iced_winit::futures::stream::StreamExt;
use winit::event_loop::ControlFlow;
use winit::event; use winit::event;
let context = { let context = {
@ -406,13 +417,22 @@ async fn run_instance<A, E, C>(
let mut mouse_interaction = mouse::Interaction::default(); let mut mouse_interaction = mouse::Interaction::default();
let mut events = Vec::new(); let mut events = Vec::new();
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut redraw_pending = false;
debug.startup_finished(); debug.startup_finished();
while let Some(event) = receiver.next().await { while let Some(event) = event_receiver.next().await {
match event { match event {
event::Event::NewEvents(start_cause) => {
redraw_pending = matches!(
start_cause,
event::StartCause::Init
| event::StartCause::Poll
| event::StartCause::ResumeTimeReached { .. }
);
}
event::Event::MainEventsCleared => { event::Event::MainEventsCleared => {
if events.is_empty() && messages.is_empty() { if !redraw_pending && events.is_empty() && messages.is_empty() {
continue; continue;
} }
@ -474,6 +494,24 @@ async fn run_instance<A, E, C>(
} }
} }
// TODO: Avoid redrawing all the time by forcing widgets to
// request redraws on state changes
//
// Then, we can use the `interface_state` here to decide if a redraw
// is needed right away, or simply wait until a specific time.
let redraw_event = Event::Window(
crate::window::Id::MAIN,
crate::window::Event::RedrawRequested(Instant::now()),
);
let (interface_state, _) = user_interface.update(
&[redraw_event.clone()],
state.cursor_position(),
&mut renderer,
&mut clipboard,
&mut messages,
);
debug.draw_started(); debug.draw_started();
let new_mouse_interaction = user_interface.draw( let new_mouse_interaction = user_interface.draw(
&mut renderer, &mut renderer,
@ -494,6 +532,23 @@ async fn run_instance<A, E, C>(
} }
window.request_redraw(); window.request_redraw();
runtime.broadcast((redraw_event, crate::event::Status::Ignored));
let _ = control_sender.start_send(match interface_state {
user_interface::State::Updated {
redraw_request: Some(redraw_request),
} => match redraw_request {
crate::window::RedrawRequest::NextFrame => {
ControlFlow::Poll
}
crate::window::RedrawRequest::At(at) => {
ControlFlow::WaitUntil(at)
}
},
_ => ControlFlow::Wait,
});
redraw_pending = false;
} }
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url), event::MacOS::ReceivedUrl(url),

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_graphics" name = "iced_graphics"
version = "0.5.0" version = "0.6.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"
description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced" description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced"
@ -44,11 +44,11 @@ version = "1.4"
features = ["derive"] features = ["derive"]
[dependencies.iced_native] [dependencies.iced_native]
version = "0.7" version = "0.8"
path = "../native" path = "../native"
[dependencies.iced_style] [dependencies.iced_style]
version = "0.5" version = "0.6"
path = "../style" path = "../style"
[dependencies.lyon] [dependencies.lyon]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_lazy" name = "iced_lazy"
version = "0.3.0" version = "0.4.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"
description = "Lazy widgets for Iced" description = "Lazy widgets for Iced"
@ -14,5 +14,5 @@ categories = ["gui"]
ouroboros = "0.13" ouroboros = "0.13"
[dependencies.iced_native] [dependencies.iced_native]
version = "0.7" version = "0.8"
path = "../native" path = "../native"

View file

@ -9,16 +9,17 @@ use iced_native::Element;
use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell, Size}; use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell, Size};
use ouroboros::self_referencing; use ouroboros::self_referencing;
use std::cell::{Ref, RefCell, RefMut}; use std::cell::RefCell;
use std::hash::{Hash, Hasher as H}; use std::hash::{Hash, Hasher as H};
use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Lazy<'a, Message, Renderer, Dependency, View> { pub struct Lazy<'a, Message, Renderer, Dependency, View> {
dependency: Dependency, dependency: Dependency,
view: Box<dyn Fn() -> View + 'a>, view: Box<dyn Fn(&Dependency) -> View + 'a>,
element: RefCell<Option<Rc<RefCell<Element<'static, Message, Renderer>>>>>, element: RefCell<
Option<Rc<RefCell<Option<Element<'static, Message, Renderer>>>>>,
>,
} }
impl<'a, Message, Renderer, Dependency, View> impl<'a, Message, Renderer, Dependency, View>
@ -27,7 +28,10 @@ where
Dependency: Hash + 'a, Dependency: Hash + 'a,
View: Into<Element<'static, Message, Renderer>>, View: Into<Element<'static, Message, Renderer>>,
{ {
pub fn new(dependency: Dependency, view: impl Fn() -> View + 'a) -> Self { pub fn new(
dependency: Dependency,
view: impl Fn(&Dependency) -> View + 'a,
) -> Self {
Self { Self {
dependency, dependency,
view: Box::new(view), view: Box::new(view),
@ -37,21 +41,35 @@ where
fn with_element<T>( fn with_element<T>(
&self, &self,
f: impl FnOnce(Ref<Element<Message, Renderer>>) -> T, f: impl FnOnce(&Element<Message, Renderer>) -> T,
) -> T { ) -> T {
f(self.element.borrow().as_ref().unwrap().borrow()) f(self
.element
.borrow()
.as_ref()
.unwrap()
.borrow()
.as_ref()
.unwrap())
} }
fn with_element_mut<T>( fn with_element_mut<T>(
&self, &self,
f: impl FnOnce(RefMut<Element<Message, Renderer>>) -> T, f: impl FnOnce(&mut Element<Message, Renderer>) -> T,
) -> T { ) -> T {
f(self.element.borrow().as_ref().unwrap().borrow_mut()) f(self
.element
.borrow()
.as_ref()
.unwrap()
.borrow_mut()
.as_mut()
.unwrap())
} }
} }
struct Internal<Message, Renderer> { struct Internal<Message, Renderer> {
element: Rc<RefCell<Element<'static, Message, Renderer>>>, element: Rc<RefCell<Option<Element<'static, Message, Renderer>>>>,
hash: u64, hash: u64,
} }
@ -73,7 +91,8 @@ where
self.dependency.hash(&mut hasher); self.dependency.hash(&mut hasher);
let hash = hasher.finish(); let hash = hasher.finish();
let element = Rc::new(RefCell::new((self.view)().into())); let element =
Rc::new(RefCell::new(Some((self.view)(&self.dependency).into())));
(*self.element.borrow_mut()) = Some(element.clone()); (*self.element.borrow_mut()) = Some(element.clone());
@ -81,9 +100,7 @@ where
} }
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<Tree> {
vec![Tree::new( self.with_element(|element| vec![Tree::new(element.as_widget())])
self.element.borrow().as_ref().unwrap().borrow().as_widget(),
)]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&self, tree: &mut Tree) {
@ -96,13 +113,13 @@ where
if current.hash != new_hash { if current.hash != new_hash {
current.hash = new_hash; current.hash = new_hash;
let element = (self.view)().into(); let element = (self.view)(&self.dependency).into();
current.element = Rc::new(RefCell::new(element)); current.element = Rc::new(RefCell::new(Some(element)));
(*self.element.borrow_mut()) = Some(current.element.clone()); (*self.element.borrow_mut()) = Some(current.element.clone());
tree.diff_children(std::slice::from_ref( self.with_element(|element| {
&self.element.borrow().as_ref().unwrap().borrow().as_widget(), tree.diff_children(std::slice::from_ref(&element.as_widget()))
)); });
} else { } else {
(*self.element.borrow_mut()) = Some(current.element.clone()); (*self.element.borrow_mut()) = Some(current.element.clone());
} }
@ -153,7 +170,7 @@ where
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) -> event::Status {
self.with_element_mut(|mut element| { self.with_element_mut(|element| {
element.as_widget_mut().on_event( element.as_widget_mut().on_event(
&mut tree.children[0], &mut tree.children[0],
event, event,
@ -214,23 +231,27 @@ where
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
) -> Option<overlay::Element<'_, Message, Renderer>> { ) -> Option<overlay::Element<'_, Message, Renderer>> {
let overlay = OverlayBuilder { let overlay = Overlay(Some(
cached: self, InnerBuilder {
tree: &mut tree.children[0], cell: self.element.borrow().as_ref().unwrap().clone(),
types: PhantomData, element: self
overlay_builder: |cached, tree| { .element
Rc::get_mut(cached.element.get_mut().as_mut().unwrap()) .borrow()
.as_ref()
.unwrap() .unwrap()
.get_mut() .borrow_mut()
.as_widget_mut() .take()
.overlay(tree, layout, renderer) .unwrap(),
}, tree: &mut tree.children[0],
} overlay_builder: |element, tree| {
.build(); element.as_widget_mut().overlay(tree, layout, renderer)
},
}
.build(),
));
let has_overlay = overlay.with_overlay(|overlay| { let has_overlay = overlay
overlay.as_ref().map(overlay::Element::position) .with_overlay_maybe(|overlay| overlay::Element::position(overlay));
});
has_overlay has_overlay
.map(|position| overlay::Element::new(position, Box::new(overlay))) .map(|position| overlay::Element::new(position, Box::new(overlay)))
@ -238,37 +259,50 @@ where
} }
#[self_referencing] #[self_referencing]
struct Overlay<'a, 'b, Message, Renderer, Dependency, View> { struct Inner<'a, Message, Renderer>
cached: &'a mut Lazy<'b, Message, Renderer, Dependency, View>, where
Message: 'a,
Renderer: 'a,
{
cell: Rc<RefCell<Option<Element<'static, Message, Renderer>>>>,
element: Element<'static, Message, Renderer>,
tree: &'a mut Tree, tree: &'a mut Tree,
types: PhantomData<(Message, Dependency, View)>,
#[borrows(mut cached, mut tree)] #[borrows(mut element, mut tree)]
#[covariant] #[covariant]
overlay: Option<overlay::Element<'this, Message, Renderer>>, overlay: Option<overlay::Element<'this, Message, Renderer>>,
} }
impl<'a, 'b, Message, Renderer, Dependency, View> struct Overlay<'a, Message, Renderer>(Option<Inner<'a, Message, Renderer>>);
Overlay<'a, 'b, Message, Renderer, Dependency, View>
{ impl<'a, Message, Renderer> Drop for Overlay<'a, Message, Renderer> {
fn drop(&mut self) {
let heads = self.0.take().unwrap().into_heads();
(*heads.cell.borrow_mut()) = Some(heads.element);
}
}
impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> {
fn with_overlay_maybe<T>( fn with_overlay_maybe<T>(
&self, &self,
f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T,
) -> Option<T> { ) -> Option<T> {
self.borrow_overlay().as_ref().map(f) self.0.as_ref().unwrap().borrow_overlay().as_ref().map(f)
} }
fn with_overlay_mut_maybe<T>( fn with_overlay_mut_maybe<T>(
&mut self, &mut self,
f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T,
) -> Option<T> { ) -> Option<T> {
self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) self.0
.as_mut()
.unwrap()
.with_overlay_mut(|overlay| overlay.as_mut().map(f))
} }
} }
impl<'a, 'b, Message, Renderer, Dependency, View> impl<'a, Message, Renderer> overlay::Overlay<Message, Renderer>
overlay::Overlay<Message, Renderer> for Overlay<'a, Message, Renderer>
for Overlay<'a, 'b, Message, Renderer, Dependency, View>
where where
Renderer: iced_native::Renderer, Renderer: iced_native::Renderer,
{ {

View file

@ -33,7 +33,7 @@ use std::hash::Hash;
pub fn lazy<'a, Message, Renderer, Dependency, View>( pub fn lazy<'a, Message, Renderer, Dependency, View>(
dependency: Dependency, dependency: Dependency,
view: impl Fn() -> View + 'a, view: impl Fn(&Dependency) -> View + 'a,
) -> Lazy<'a, Message, Renderer, Dependency, View> ) -> Lazy<'a, Message, Renderer, Dependency, View>
where where
Dependency: Hash + 'a, Dependency: Hash + 'a,

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_native" name = "iced_native"
version = "0.7.0" version = "0.8.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"
description = "A renderer-agnostic library for native GUIs" description = "A renderer-agnostic library for native GUIs"
@ -16,7 +16,7 @@ unicode-segmentation = "1.6"
num-traits = "0.2" num-traits = "0.2"
[dependencies.iced_core] [dependencies.iced_core]
version = "0.6" version = "0.7"
path = "../core" path = "../core"
[dependencies.iced_futures] [dependencies.iced_futures]
@ -25,5 +25,5 @@ path = "../futures"
features = ["thread-pool"] features = ["thread-pool"]
[dependencies.iced_style] [dependencies.iced_style]
version = "0.5.1" version = "0.6.0"
path = "../style" path = "../style"

View file

@ -28,7 +28,7 @@ To achieve this, it introduces a bunch of reusable interfaces:
Add `iced_native` as a dependency in your `Cargo.toml`: Add `iced_native` as a dependency in your `Cargo.toml`:
```toml ```toml
iced_native = "0.4" iced_native = "0.8"
``` ```
__Iced moves fast and the `master` branch can contain breaking changes!__ If __Iced moves fast and the `master` branch can contain breaking changes!__ If

View file

@ -9,6 +9,7 @@ use crate::{
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Widget, Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Widget,
}; };
use std::any::Any;
use std::borrow::Borrow; use std::borrow::Borrow;
/// A generic [`Widget`]. /// A generic [`Widget`].
@ -333,6 +334,10 @@ where
) { ) {
self.operation.text_input(state, id); self.operation.text_input(state, id);
} }
fn custom(&mut self, state: &mut dyn Any, id: Option<&widget::Id>) {
self.operation.custom(state, id);
}
} }
self.widget.operate( self.widget.operate(

View file

@ -23,8 +23,8 @@
//! - Build a new renderer, see the [renderer] module. //! - Build a new renderer, see the [renderer] module.
//! - Build a custom widget, start at the [`Widget`] trait. //! - Build a custom widget, start at the [`Widget`] trait.
//! //!
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.6/core //! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.7/core
//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.6/winit //! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.7/winit
//! [`druid`]: https://github.com/xi-editor/druid //! [`druid`]: https://github.com/xi-editor/druid
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle //! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
//! [renderer]: crate::renderer //! [renderer]: crate::renderer

View file

@ -7,6 +7,8 @@ use crate::renderer;
use crate::widget; use crate::widget;
use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector}; use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector};
use std::any::Any;
/// A generic [`Overlay`]. /// A generic [`Overlay`].
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Element<'a, Message, Renderer> { pub struct Element<'a, Message, Renderer> {
@ -188,6 +190,10 @@ where
) { ) {
self.operation.text_input(state, id) self.operation.text_input(state, id)
} }
fn custom(&mut self, state: &mut dyn Any, id: Option<&widget::Id>) {
self.operation.custom(state, id);
}
} }
self.content self.content

View file

@ -36,11 +36,11 @@ pub trait Renderer: Sized {
f: impl FnOnce(&mut Self), f: impl FnOnce(&mut Self),
); );
/// Clears all of the recorded primitives in the [`Renderer`].
fn clear(&mut self);
/// Fills a [`Quad`] with the provided [`Background`]. /// Fills a [`Quad`] with the provided [`Background`].
fn fill_quad(&mut self, quad: Quad, background: impl Into<Background>); fn fill_quad(&mut self, quad: Quad, background: impl Into<Background>);
/// Clears all of the recorded primitives in the [`Renderer`].
fn clear(&mut self);
} }
/// A polygon with four sides. /// A polygon with four sides.

View file

@ -1,3 +1,5 @@
use crate::window;
/// A connection to the state of a shell. /// A connection to the state of a shell.
/// ///
/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application, /// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
@ -7,6 +9,7 @@
#[derive(Debug)] #[derive(Debug)]
pub struct Shell<'a, Message> { pub struct Shell<'a, Message> {
messages: &'a mut Vec<Message>, messages: &'a mut Vec<Message>,
redraw_request: Option<window::RedrawRequest>,
is_layout_invalid: bool, is_layout_invalid: bool,
are_widgets_invalid: bool, are_widgets_invalid: bool,
} }
@ -16,11 +19,47 @@ impl<'a, Message> Shell<'a, Message> {
pub fn new(messages: &'a mut Vec<Message>) -> Self { pub fn new(messages: &'a mut Vec<Message>) -> Self {
Self { Self {
messages, messages,
redraw_request: None,
is_layout_invalid: false, is_layout_invalid: false,
are_widgets_invalid: false, are_widgets_invalid: false,
} }
} }
/// Publish the given `Message` for an application to process it.
pub fn publish(&mut self, message: Message) {
self.messages.push(message);
}
/// Requests a new frame to be drawn at the given [`Instant`].
pub fn request_redraw(&mut self, request: window::RedrawRequest) {
match self.redraw_request {
None => {
self.redraw_request = Some(request);
}
Some(current) if request < current => {
self.redraw_request = Some(request);
}
_ => {}
}
}
/// Returns the requested [`Instant`] a redraw should happen, if any.
pub fn redraw_request(&self) -> Option<window::RedrawRequest> {
self.redraw_request
}
/// Returns whether the current layout is invalid or not.
pub fn is_layout_invalid(&self) -> bool {
self.is_layout_invalid
}
/// Invalidates the current application layout.
///
/// The shell will relayout the application widgets.
pub fn invalidate_layout(&mut self) {
self.is_layout_invalid = true;
}
/// Triggers the given function if the layout is invalid, cleaning it in the /// Triggers the given function if the layout is invalid, cleaning it in the
/// process. /// process.
pub fn revalidate_layout(&mut self, f: impl FnOnce()) { pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
@ -31,21 +70,10 @@ impl<'a, Message> Shell<'a, Message> {
} }
} }
/// Returns whether the current layout is invalid or not. /// Returns whether the widgets of the current application have been
pub fn is_layout_invalid(&self) -> bool { /// invalidated.
self.is_layout_invalid pub fn are_widgets_invalid(&self) -> bool {
} self.are_widgets_invalid
/// Publish the given `Message` for an application to process it.
pub fn publish(&mut self, message: Message) {
self.messages.push(message);
}
/// Invalidates the current application layout.
///
/// The shell will relayout the application widgets.
pub fn invalidate_layout(&mut self) {
self.is_layout_invalid = true;
} }
/// Invalidates the current application widgets. /// Invalidates the current application widgets.
@ -62,16 +90,14 @@ impl<'a, Message> Shell<'a, Message> {
pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) { pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) {
self.messages.extend(other.messages.drain(..).map(f)); self.messages.extend(other.messages.drain(..).map(f));
if let Some(at) = other.redraw_request {
self.request_redraw(at);
}
self.is_layout_invalid = self.is_layout_invalid =
self.is_layout_invalid || other.is_layout_invalid; self.is_layout_invalid || other.is_layout_invalid;
self.are_widgets_invalid = self.are_widgets_invalid =
self.are_widgets_invalid || other.are_widgets_invalid; self.are_widgets_invalid || other.are_widgets_invalid;
} }
/// Returns whether the widgets of the current application have been
/// invalidated.
pub fn are_widgets_invalid(&self) -> bool {
self.are_widgets_invalid
}
} }

View file

@ -1,5 +1,6 @@
//! Listen to external events in your application. //! Listen to external events in your application.
use crate::event::{self, Event}; use crate::event::{self, Event};
use crate::window;
use crate::Hasher; use crate::Hasher;
use iced_futures::futures::{self, Future, Stream}; use iced_futures::futures::{self, Future, Stream};
@ -33,7 +34,7 @@ pub type Tracker =
pub use iced_futures::subscription::Recipe; pub use iced_futures::subscription::Recipe;
/// Returns a [`Subscription`] to all the runtime events. /// Returns a [`Subscription`] to all the ignored runtime events.
/// ///
/// This subscription will notify your application of any [`Event`] that was /// This subscription will notify your application of any [`Event`] that was
/// not captured by any widget. /// not captured by any widget.
@ -58,8 +59,36 @@ pub fn events_with<Message>(
where where
Message: 'static + MaybeSend, Message: 'static + MaybeSend,
{ {
#[derive(Hash)]
struct EventsWith;
Subscription::from_recipe(Runner { Subscription::from_recipe(Runner {
id: f, id: (EventsWith, f),
spawn: move |events| {
use futures::future;
use futures::stream::StreamExt;
events.filter_map(move |(event, status)| {
future::ready(match event {
Event::Window(window::Event::RedrawRequested(_)) => None,
_ => f(event, status),
})
})
},
})
}
pub(crate) fn raw_events<Message>(
f: fn(Event, event::Status) -> Option<Message>,
) -> Subscription<Message>
where
Message: 'static + MaybeSend,
{
#[derive(Hash)]
struct RawEvents;
Subscription::from_recipe(Runner {
id: (RawEvents, f),
spawn: move |events| { spawn: move |events| {
use futures::future; use futures::future;
use futures::stream::StreamExt; use futures::stream::StreamExt;
@ -155,7 +184,7 @@ where
/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket /// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
/// connection open. /// connection open.
/// ///
/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.6/examples/websocket /// [`websocket`]: https://github.com/iced-rs/iced/tree/0.7/examples/websocket
pub fn unfold<I, T, Fut, Message>( pub fn unfold<I, T, Fut, Message>(
id: I, id: I,
initial: T, initial: T,

View file

@ -5,6 +5,7 @@ use crate::layout;
use crate::mouse; use crate::mouse;
use crate::renderer; use crate::renderer;
use crate::widget; use crate::widget;
use crate::window;
use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// A set of interactive graphical elements with a specific [`Layout`]. /// A set of interactive graphical elements with a specific [`Layout`].
@ -18,8 +19,8 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// The [`integration_opengl`] & [`integration_wgpu`] examples use a /// The [`integration_opengl`] & [`integration_wgpu`] examples use a
/// [`UserInterface`] to integrate Iced in an existing graphical application. /// [`UserInterface`] to integrate Iced in an existing graphical application.
/// ///
/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_opengl /// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.7/examples/integration_opengl
/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_wgpu /// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.7/examples/integration_wgpu
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> { pub struct UserInterface<'a, Message, Renderer> {
root: Element<'a, Message, Renderer>, root: Element<'a, Message, Renderer>,
@ -188,7 +189,9 @@ where
) -> (State, Vec<event::Status>) { ) -> (State, Vec<event::Status>) {
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
let mut state = State::Updated; let mut outdated = false;
let mut redraw_request = None;
let mut manual_overlay = let mut manual_overlay =
ManuallyDrop::new(self.root.as_widget_mut().overlay( ManuallyDrop::new(self.root.as_widget_mut().overlay(
&mut self.state, &mut self.state,
@ -217,6 +220,16 @@ where
event_statuses.push(event_status); event_statuses.push(event_status);
match (redraw_request, shell.redraw_request()) {
(None, Some(at)) => {
redraw_request = Some(at);
}
(Some(current), Some(new)) if new < current => {
redraw_request = Some(new);
}
_ => {}
}
if shell.is_layout_invalid() { if shell.is_layout_invalid() {
let _ = ManuallyDrop::into_inner(manual_overlay); let _ = ManuallyDrop::into_inner(manual_overlay);
@ -244,7 +257,7 @@ where
} }
if shell.are_widgets_invalid() { if shell.are_widgets_invalid() {
state = State::Outdated; outdated = true;
} }
} }
@ -289,6 +302,16 @@ where
self.overlay = None; self.overlay = None;
} }
match (redraw_request, shell.redraw_request()) {
(None, Some(at)) => {
redraw_request = Some(at);
}
(Some(current), Some(new)) if new < current => {
redraw_request = Some(new);
}
_ => {}
}
shell.revalidate_layout(|| { shell.revalidate_layout(|| {
self.base = renderer.layout( self.base = renderer.layout(
&self.root, &self.root,
@ -299,14 +322,21 @@ where
}); });
if shell.are_widgets_invalid() { if shell.are_widgets_invalid() {
state = State::Outdated; outdated = true;
} }
event_status.merge(overlay_status) event_status.merge(overlay_status)
}) })
.collect(); .collect();
(state, event_statuses) (
if outdated {
State::Outdated
} else {
State::Updated { redraw_request }
},
event_statuses,
)
} }
/// Draws the [`UserInterface`] with the provided [`Renderer`]. /// Draws the [`UserInterface`] with the provided [`Renderer`].
@ -559,5 +589,8 @@ pub enum State {
/// The [`UserInterface`] is up-to-date and can be reused without /// The [`UserInterface`] is up-to-date and can be reused without
/// rebuilding. /// rebuilding.
Updated, Updated {
/// The [`Instant`] when a redraw should be performed.
redraw_request: Option<window::RedrawRequest>,
},
} }

View file

@ -110,12 +110,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
/// - [`geometry`], a custom widget showcasing how to draw geometry with the /// - [`geometry`], a custom widget showcasing how to draw geometry with the
/// `Mesh2D` primitive in [`iced_wgpu`]. /// `Mesh2D` primitive in [`iced_wgpu`].
/// ///
/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples /// [examples]: https://github.com/iced-rs/iced/tree/0.7/examples
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool /// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.7/examples/bezier_tool
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget /// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.7/examples/custom_widget
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry /// [`geometry`]: https://github.com/iced-rs/iced/tree/0.7/examples/geometry
/// [`lyon`]: https://github.com/nical/lyon /// [`lyon`]: https://github.com/nical/lyon
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu /// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.7/wgpu
pub trait Widget<Message, Renderer> pub trait Widget<Message, Renderer>
where where
Renderer: crate::Renderer, Renderer: crate::Renderer,

View file

@ -3,6 +3,7 @@ use crate::widget::Id;
use iced_futures::MaybeSend; use iced_futures::MaybeSend;
use std::any::Any;
use std::rc::Rc; use std::rc::Rc;
/// An operation to be performed on the widget tree. /// An operation to be performed on the widget tree.
@ -84,6 +85,10 @@ where
) { ) {
self.operation.focusable(state, id); self.operation.focusable(state, id);
} }
fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
self.operation.custom(state, id);
}
} }
let Self { operation, .. } = self; let Self { operation, .. } = self;
@ -118,6 +123,10 @@ where
self.operation.text_input(state, 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) -> operation::Outcome<B> { fn finish(&self) -> operation::Outcome<B> {
match self.operation.finish() { match self.operation.finish() {
operation::Outcome::None => operation::Outcome::None, operation::Outcome::None => operation::Outcome::None,

View file

@ -9,6 +9,7 @@ pub use text_input::TextInput;
use crate::widget::Id; use crate::widget::Id;
use std::any::Any;
use std::fmt; use std::fmt;
/// A piece of logic that can traverse the widget tree of an application in /// A piece of logic that can traverse the widget tree of an application in
@ -33,6 +34,9 @@ pub trait Operation<T> {
/// Operates on a widget that has text input. /// Operates on a widget that has text input.
fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {} 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`]. /// Finishes the [`Operation`] and returns its [`Outcome`].
fn finish(&self) -> Outcome<T> { fn finish(&self) -> Outcome<T> {
Outcome::None Outcome::None

View file

@ -6,7 +6,7 @@
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support. //! drag and drop, and hotkey support.
//! //!
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.7/examples/pane_grid
mod axis; mod axis;
mod configuration; mod configuration;
mod content; mod content;

View file

@ -18,10 +18,12 @@ use crate::layout;
use crate::mouse::{self, click}; use crate::mouse::{self, click};
use crate::renderer; use crate::renderer;
use crate::text::{self, Text}; use crate::text::{self, Text};
use crate::time::{Duration, Instant};
use crate::touch; use crate::touch;
use crate::widget; use crate::widget;
use crate::widget::operation::{self, Operation}; use crate::widget::operation::{self, Operation};
use crate::widget::tree::{self, Tree}; use crate::widget::tree::{self, Tree};
use crate::window;
use crate::{ use crate::{
Clipboard, Color, Command, Element, Layout, Length, Padding, Point, Clipboard, Color, Command, Element, Layout, Length, Padding, Point,
Rectangle, Shell, Size, Vector, Widget, Rectangle, Shell, Size, Vector, Widget,
@ -425,7 +427,18 @@ where
let state = state(); let state = state();
let is_clicked = layout.bounds().contains(cursor_position); let is_clicked = layout.bounds().contains(cursor_position);
state.is_focused = is_clicked; state.is_focused = if is_clicked {
state.is_focused.or_else(|| {
let now = Instant::now();
Some(Focus {
updated_at: now,
now,
})
})
} else {
None
};
if is_clicked { if is_clicked {
let text_layout = layout.children().next().unwrap(); let text_layout = layout.children().next().unwrap();
@ -541,26 +554,30 @@ where
Event::Keyboard(keyboard::Event::CharacterReceived(c)) => { Event::Keyboard(keyboard::Event::CharacterReceived(c)) => {
let state = state(); let state = state();
if state.is_focused if let Some(focus) = &mut state.is_focused {
&& state.is_pasting.is_none() if state.is_pasting.is_none()
&& !state.keyboard_modifiers.command() && !state.keyboard_modifiers.command()
&& !c.is_control() && !c.is_control()
{ {
let mut editor = Editor::new(value, &mut state.cursor); let mut editor = Editor::new(value, &mut state.cursor);
editor.insert(c); editor.insert(c);
let message = (on_change)(editor.contents()); let message = (on_change)(editor.contents());
shell.publish(message); shell.publish(message);
return event::Status::Captured; focus.updated_at = Instant::now();
return event::Status::Captured;
}
} }
} }
Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => { Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => {
let state = state(); let state = state();
if state.is_focused { if let Some(focus) = &mut state.is_focused {
let modifiers = state.keyboard_modifiers; let modifiers = state.keyboard_modifiers;
focus.updated_at = Instant::now();
match key_code { match key_code {
keyboard::KeyCode::Enter keyboard::KeyCode::Enter
@ -721,7 +738,7 @@ where
state.cursor.select_all(value); state.cursor.select_all(value);
} }
keyboard::KeyCode::Escape => { keyboard::KeyCode::Escape => {
state.is_focused = false; state.is_focused = None;
state.is_dragging = false; state.is_dragging = false;
state.is_pasting = None; state.is_pasting = None;
@ -742,7 +759,7 @@ where
Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => { Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => {
let state = state(); let state = state();
if state.is_focused { if state.is_focused.is_some() {
match key_code { match key_code {
keyboard::KeyCode::V => { keyboard::KeyCode::V => {
state.is_pasting = None; state.is_pasting = None;
@ -765,6 +782,21 @@ where
state.keyboard_modifiers = modifiers; state.keyboard_modifiers = modifiers;
} }
Event::Window(window::Event::RedrawRequested(now)) => {
let state = state();
if let Some(focus) = &mut state.is_focused {
focus.now = now;
let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS
- (now - focus.updated_at).as_millis()
% CURSOR_BLINK_INTERVAL_MILLIS;
shell.request_redraw(window::RedrawRequest::At(
now + Duration::from_millis(millis_until_redraw as u64),
));
}
}
_ => {} _ => {}
} }
@ -820,7 +852,7 @@ pub fn draw<Renderer>(
let text = value.to_string(); let text = value.to_string();
let size = size.unwrap_or_else(|| renderer.default_size()); let size = size.unwrap_or_else(|| renderer.default_size());
let (cursor, offset) = if state.is_focused() { let (cursor, offset) = if let Some(focus) = &state.is_focused {
match state.cursor.state(value) { match state.cursor.state(value) {
cursor::State::Index(position) => { cursor::State::Index(position) => {
let (text_value_width, offset) = let (text_value_width, offset) =
@ -833,7 +865,13 @@ pub fn draw<Renderer>(
font.clone(), font.clone(),
); );
( let is_cursor_visible = ((focus.now - focus.updated_at)
.as_millis()
/ CURSOR_BLINK_INTERVAL_MILLIS)
% 2
== 0;
let cursor = if is_cursor_visible {
Some(( Some((
renderer::Quad { renderer::Quad {
bounds: Rectangle { bounds: Rectangle {
@ -847,9 +885,12 @@ pub fn draw<Renderer>(
border_color: Color::TRANSPARENT, border_color: Color::TRANSPARENT,
}, },
theme.value_color(style), theme.value_color(style),
)), ))
offset, } else {
) None
};
(cursor, offset)
} }
cursor::State::Selection { start, end } => { cursor::State::Selection { start, end } => {
let left = start.min(end); let left = start.min(end);
@ -958,7 +999,7 @@ pub fn mouse_interaction(
/// The state of a [`TextInput`]. /// The state of a [`TextInput`].
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct State { pub struct State {
is_focused: bool, is_focused: Option<Focus>,
is_dragging: bool, is_dragging: bool,
is_pasting: Option<Value>, is_pasting: Option<Value>,
last_click: Option<mouse::Click>, last_click: Option<mouse::Click>,
@ -967,6 +1008,12 @@ pub struct State {
// TODO: Add stateful horizontal scrolling offset // TODO: Add stateful horizontal scrolling offset
} }
#[derive(Debug, Clone, Copy)]
struct Focus {
updated_at: Instant,
now: Instant,
}
impl State { impl State {
/// Creates a new [`State`], representing an unfocused [`TextInput`]. /// Creates a new [`State`], representing an unfocused [`TextInput`].
pub fn new() -> Self { pub fn new() -> Self {
@ -976,7 +1023,7 @@ impl State {
/// Creates a new [`State`], representing a focused [`TextInput`]. /// Creates a new [`State`], representing a focused [`TextInput`].
pub fn focused() -> Self { pub fn focused() -> Self {
Self { Self {
is_focused: true, is_focused: None,
is_dragging: false, is_dragging: false,
is_pasting: None, is_pasting: None,
last_click: None, last_click: None,
@ -987,7 +1034,7 @@ impl State {
/// Returns whether the [`TextInput`] is currently focused or not. /// Returns whether the [`TextInput`] is currently focused or not.
pub fn is_focused(&self) -> bool { pub fn is_focused(&self) -> bool {
self.is_focused self.is_focused.is_some()
} }
/// Returns the [`Cursor`] of the [`TextInput`]. /// Returns the [`Cursor`] of the [`TextInput`].
@ -997,13 +1044,19 @@ impl State {
/// Focuses the [`TextInput`]. /// Focuses the [`TextInput`].
pub fn focus(&mut self) { pub fn focus(&mut self) {
self.is_focused = true; let now = Instant::now();
self.is_focused = Some(Focus {
updated_at: now,
now,
});
self.move_cursor_to_end(); self.move_cursor_to_end();
} }
/// Unfocuses the [`TextInput`]. /// Unfocuses the [`TextInput`].
pub fn unfocus(&mut self) { pub fn unfocus(&mut self) {
self.is_focused = false; self.is_focused = None;
} }
/// Moves the [`Cursor`] of the [`TextInput`] to the front of the input text. /// Moves the [`Cursor`] of the [`TextInput`] to the front of the input text.
@ -1156,3 +1209,5 @@ where
) )
.map(text::Hit::cursor) .map(text::Hit::cursor)
} }
const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;

View file

@ -6,6 +6,7 @@ mod id;
mod mode; mod mode;
mod position; mod position;
mod settings; mod settings;
mod redraw_request;
mod user_attention; mod user_attention;
pub use action::Action; pub use action::Action;
@ -15,4 +16,23 @@ pub use id::Id;
pub use mode::Mode; pub use mode::Mode;
pub use position::Position; pub use position::Position;
pub use settings::Settings; pub use settings::Settings;
pub use redraw_request::RedrawRequest;
pub use user_attention::UserAttention; pub use user_attention::UserAttention;
use crate::subscription::{self, Subscription};
use crate::time::Instant;
/// Subscribes to the frames of the window of the running application.
///
/// The resulting [`Subscription`] will produce items at a rate equal to the
/// refresh rate of the window. Note that this rate may be variable, as it is
/// normally managed by the graphics driver and/or the OS.
///
/// In any case, this [`Subscription`] is useful to smoothly draw application-driven
/// animations without missing any frames.
pub fn frames() -> Subscription<Instant> {
subscription::raw_events(|event, _status| match event {
crate::Event::Window(Event::RedrawRequested(at)) => Some(at),
_ => None,
})
}

View file

@ -1,3 +1,5 @@
use crate::time::Instant;
use std::path::PathBuf; use std::path::PathBuf;
/// A window-related event. /// A window-related event.
@ -19,6 +21,11 @@ pub enum Event {
height: u32, height: u32,
}, },
/// A window redraw was requested.
///
/// The [`Instant`] contains the current time.
RedrawRequested(Instant),
/// The user has requested for the window to close. /// The user has requested for the window to close.
/// ///
/// Usually, you will want to terminate the execution whenever this event /// Usually, you will want to terminate the execution whenever this event

View file

@ -0,0 +1,38 @@
use crate::time::Instant;
/// A request to redraw a window.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RedrawRequest {
/// Redraw the next frame.
NextFrame,
/// Redraw at the given time.
At(Instant),
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, Instant};
#[test]
fn ordering() {
let now = Instant::now();
let later = now + Duration::from_millis(10);
assert_eq!(RedrawRequest::NextFrame, RedrawRequest::NextFrame);
assert_eq!(RedrawRequest::At(now), RedrawRequest::At(now));
assert!(RedrawRequest::NextFrame < RedrawRequest::At(now));
assert!(RedrawRequest::At(now) > RedrawRequest::NextFrame);
assert!(RedrawRequest::At(now) < RedrawRequest::At(later));
assert!(RedrawRequest::At(later) > RedrawRequest::At(now));
assert!(RedrawRequest::NextFrame <= RedrawRequest::NextFrame);
assert!(RedrawRequest::NextFrame <= RedrawRequest::At(now));
assert!(RedrawRequest::At(now) >= RedrawRequest::NextFrame);
assert!(RedrawRequest::At(now) <= RedrawRequest::At(now));
assert!(RedrawRequest::At(now) <= RedrawRequest::At(later));
assert!(RedrawRequest::At(later) >= RedrawRequest::At(now));
}
}

View file

@ -39,15 +39,15 @@ pub use iced_native::application::{Appearance, StyleSheet};
/// to listen to time. /// to listen to time.
/// - [`todos`], a todos tracker inspired by [TodoMVC]. /// - [`todos`], a todos tracker inspired by [TodoMVC].
/// ///
/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.6/examples /// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.7/examples
/// [`clock`]: https://github.com/iced-rs/iced/tree/0.6/examples/clock /// [`clock`]: https://github.com/iced-rs/iced/tree/0.7/examples/clock
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.6/examples/download_progress /// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.7/examples/download_progress
/// [`events`]: https://github.com/iced-rs/iced/tree/0.6/examples/events /// [`events`]: https://github.com/iced-rs/iced/tree/0.7/examples/events
/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.6/examples/game_of_life /// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.7/examples/game_of_life
/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.6/examples/pokedex /// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.7/examples/pokedex
/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.6/examples/solar_system /// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.7/examples/solar_system
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.6/examples/stopwatch /// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.7/examples/stopwatch
/// [`todos`]: https://github.com/iced-rs/iced/tree/0.6/examples/todos /// [`todos`]: https://github.com/iced-rs/iced/tree/0.7/examples/todos
/// [`Sandbox`]: crate::Sandbox /// [`Sandbox`]: crate::Sandbox
/// [`Canvas`]: crate::widget::Canvas /// [`Canvas`]: crate::widget::Canvas
/// [PokéAPI]: https://pokeapi.co/ /// [PokéAPI]: https://pokeapi.co/
@ -180,13 +180,6 @@ pub trait Application: Sized {
1.0 1.0
} }
/// Returns whether the [`Application`] should be terminated.
///
/// By default, it returns `false`.
fn should_exit(&self) -> bool {
false
}
/// Runs the [`Application`]. /// Runs the [`Application`].
/// ///
/// On native platforms, this method will take control of the current thread /// On native platforms, this method will take control of the current thread

View file

@ -24,13 +24,13 @@
//! [scrollables]: https://gfycat.com/perkybaggybaboon-rust-gui //! [scrollables]: https://gfycat.com/perkybaggybaboon-rust-gui
//! [Debug overlay with performance metrics]: https://gfycat.com/incredibledarlingbee //! [Debug overlay with performance metrics]: https://gfycat.com/incredibledarlingbee
//! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md //! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md
//! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/tree/0.6/native //! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/tree/0.7/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.6/wgpu //! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.7/wgpu
//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.6/winit //! [windowing shell]: https://github.com/iced-rs/iced/tree/0.7/winit
//! [`dodrio`]: https://github.com/fitzgen/dodrio //! [`dodrio`]: https://github.com/fitzgen/dodrio
//! [web runtime]: https://github.com/iced-rs/iced_web //! [web runtime]: https://github.com/iced-rs/iced_web
//! [examples]: https://github.com/iced-rs/iced/tree/0.6/examples //! [examples]: https://github.com/iced-rs/iced/tree/0.7/examples
//! [repository]: https://github.com/iced-rs/iced //! [repository]: https://github.com/iced-rs/iced
//! //!
//! # Overview //! # Overview
@ -97,6 +97,7 @@
//! text(self.value).size(50), //! text(self.value).size(50),
//! //!
//! // The decrement button. We tell it to produce a //! // The decrement button. We tell it to produce a
//! // `DecrementPressed` message when pressed
//! button("-").on_press(Message::DecrementPressed), //! button("-").on_press(Message::DecrementPressed),
//! ] //! ]
//! } //! }

View file

@ -34,19 +34,19 @@ use crate::{Application, Command, Element, Error, Settings, Subscription};
/// - [`tour`], a simple UI tour that can run both on native platforms and the /// - [`tour`], a simple UI tour that can run both on native platforms and the
/// web! /// web!
/// ///
/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.6/examples /// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.7/examples
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool /// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.7/examples/bezier_tool
/// [`counter`]: https://github.com/iced-rs/iced/tree/0.6/examples/counter /// [`counter`]: https://github.com/iced-rs/iced/tree/0.7/examples/counter
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget /// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.7/examples/custom_widget
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry /// [`geometry`]: https://github.com/iced-rs/iced/tree/0.7/examples/geometry
/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid /// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.7/examples/pane_grid
/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.6/examples/progress_bar /// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.7/examples/progress_bar
/// [`styling`]: https://github.com/iced-rs/iced/tree/0.6/examples/styling /// [`styling`]: https://github.com/iced-rs/iced/tree/0.7/examples/styling
/// [`svg`]: https://github.com/iced-rs/iced/tree/0.6/examples/svg /// [`svg`]: https://github.com/iced-rs/iced/tree/0.7/examples/svg
/// [`tour`]: https://github.com/iced-rs/iced/tree/0.6/examples/tour /// [`tour`]: https://github.com/iced-rs/iced/tree/0.7/examples/tour
/// [`Canvas widget`]: crate::widget::Canvas /// [`Canvas widget`]: crate::widget::Canvas
/// [the overview]: index.html#overview /// [the overview]: index.html#overview
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu /// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.7/wgpu
/// [`Svg` widget]: crate::widget::Svg /// [`Svg` widget]: crate::widget::Svg
/// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg /// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
/// ///
@ -140,13 +140,6 @@ pub trait Sandbox {
1.0 1.0
} }
/// Returns whether the [`Sandbox`] should be terminated.
///
/// By default, it returns `false`.
fn should_exit(&self) -> bool {
false
}
/// Runs the [`Sandbox`]. /// Runs the [`Sandbox`].
/// ///
/// On native platforms, this method will take control of the current thread /// On native platforms, this method will take control of the current thread
@ -203,8 +196,4 @@ where
fn scale_factor(&self) -> f64 { fn scale_factor(&self) -> f64 {
T::scale_factor(self) T::scale_factor(self)
} }
fn should_exit(&self) -> bool {
T::should_exit(self)
}
} }

View file

@ -56,7 +56,7 @@ pub mod pane_grid {
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support. //! drag and drop, and hotkey support.
//! //!
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.7/examples/pane_grid
pub use iced_native::widget::pane_grid::{ pub use iced_native::widget::pane_grid::{
Axis, Configuration, Direction, DragEvent, Line, Node, Pane, Axis, Configuration, Direction, DragEvent, Line, Node, Pane,
ResizeEvent, Split, State, StyleSheet, ResizeEvent, Split, State, StyleSheet,

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_style" name = "iced_style"
version = "0.5.1" version = "0.6.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"
description = "The default set of styles of Iced" description = "The default set of styles of Iced"
@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"] categories = ["gui"]
[dependencies.iced_core] [dependencies.iced_core]
version = "0.6" version = "0.7"
path = "../core" path = "../core"
features = ["palette"] features = ["palette"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_wgpu" name = "iced_wgpu"
version = "0.7.0" version = "0.8.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"
description = "A wgpu renderer for Iced" description = "A wgpu renderer for Iced"
@ -42,11 +42,11 @@ version = "1.9"
features = ["derive"] features = ["derive"]
[dependencies.iced_native] [dependencies.iced_native]
version = "0.7" version = "0.8"
path = "../native" path = "../native"
[dependencies.iced_graphics] [dependencies.iced_graphics]
version = "0.5" version = "0.6"
path = "../graphics" path = "../graphics"
features = ["font-fallback", "font-icons"] features = ["font-fallback", "font-icons"]

View file

@ -30,7 +30,7 @@ Currently, `iced_wgpu` supports the following primitives:
Add `iced_wgpu` as a dependency in your `Cargo.toml`: Add `iced_wgpu` as a dependency in your `Cargo.toml`:
```toml ```toml
iced_wgpu = "0.4" iced_wgpu = "0.8"
``` ```
__Iced moves fast and the `master` branch can contain breaking changes!__ If __Iced moves fast and the `master` branch can contain breaking changes!__ If

View file

@ -16,7 +16,7 @@
//! - Meshes of triangles, useful to draw geometry freely. //! - Meshes of triangles, useful to draw geometry freely.
//! //!
//! [Iced]: https://github.com/iced-rs/iced //! [Iced]: https://github.com/iced-rs/iced
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/ //! [WebGPU API]: https://gpuweb.github.io/gpuweb/
//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_winit" name = "iced_winit"
version = "0.6.0" version = "0.7.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"
description = "A winit runtime for Iced" description = "A winit runtime for Iced"
@ -29,11 +29,11 @@ git = "https://github.com/iced-rs/winit.git"
rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c" rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c"
[dependencies.iced_native] [dependencies.iced_native]
version = "0.7" version = "0.8"
path = "../native" path = "../native"
[dependencies.iced_graphics] [dependencies.iced_graphics]
version = "0.5" version = "0.6"
path = "../graphics" path = "../graphics"
[dependencies.iced_futures] [dependencies.iced_futures]

View file

@ -20,7 +20,7 @@ It exposes a renderer-agnostic `Application` trait that can be implemented and t
Add `iced_winit` as a dependency in your `Cargo.toml`: Add `iced_winit` as a dependency in your `Cargo.toml`:
```toml ```toml
iced_winit = "0.3" iced_winit = "0.7"
``` ```
__Iced moves fast and the `master` branch can contain breaking changes!__ If __Iced moves fast and the `master` branch can contain breaking changes!__ If

View file

@ -9,7 +9,7 @@ use crate::mouse;
use crate::renderer; use crate::renderer;
use crate::widget::operation; use crate::widget::operation;
use crate::{ use crate::{
Command, Debug, Error, Executor, Proxy, Runtime, Settings, Size, Command, Debug, Error, Event, Executor, Proxy, Runtime, Settings, Size,
Subscription, Subscription,
}; };
@ -18,6 +18,7 @@ use iced_futures::futures::channel::mpsc;
use iced_graphics::compositor; use iced_graphics::compositor;
use iced_graphics::window; use iced_graphics::window;
use iced_native::program::Program; use iced_native::program::Program;
use iced_native::time::Instant;
use iced_native::user_interface::{self, UserInterface}; use iced_native::user_interface::{self, UserInterface};
pub use iced_native::application::{Appearance, StyleSheet}; pub use iced_native::application::{Appearance, StyleSheet};
@ -184,7 +185,8 @@ where
let (compositor, renderer) = C::new(compositor_settings, Some(&window))?; let (compositor, renderer) = C::new(compositor_settings, Some(&window))?;
let (mut sender, receiver) = mpsc::unbounded(); let (mut event_sender, event_receiver) = mpsc::unbounded();
let (control_sender, mut control_receiver) = mpsc::unbounded();
let mut instance = Box::pin({ let mut instance = Box::pin({
let run_instance = run_instance::<A, E, C>( let run_instance = run_instance::<A, E, C>(
@ -194,7 +196,8 @@ where
runtime, runtime,
proxy, proxy,
debug, debug,
receiver, event_receiver,
control_sender,
init_command, init_command,
window, window,
settings.exit_on_close_request, settings.exit_on_close_request,
@ -232,13 +235,19 @@ where
}; };
if let Some(event) = event { if let Some(event) = event {
sender.start_send(event).expect("Send event"); event_sender.start_send(event).expect("Send event");
let poll = instance.as_mut().poll(&mut context); let poll = instance.as_mut().poll(&mut context);
*control_flow = match poll { match poll {
task::Poll::Pending => ControlFlow::Wait, task::Poll::Pending => {
task::Poll::Ready(_) => ControlFlow::Exit, if let Ok(Some(flow)) = control_receiver.try_next() {
*control_flow = flow;
}
}
task::Poll::Ready(_) => {
*control_flow = ControlFlow::Exit;
}
}; };
} }
}) })
@ -251,7 +260,10 @@ async fn run_instance<A, E, C>(
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
mut proxy: winit::event_loop::EventLoopProxy<A::Message>, mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
mut debug: Debug, mut debug: Debug,
mut receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>>, mut event_receiver: mpsc::UnboundedReceiver<
winit::event::Event<'_, A::Message>,
>,
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
init_command: Command<A::Message>, init_command: Command<A::Message>,
window: winit::window::Window, window: winit::window::Window,
exit_on_close_request: bool, exit_on_close_request: bool,
@ -263,6 +275,7 @@ async fn run_instance<A, E, C>(
{ {
use iced_futures::futures::stream::StreamExt; use iced_futures::futures::stream::StreamExt;
use winit::event; use winit::event;
use winit::event_loop::ControlFlow;
let mut clipboard = Clipboard::connect(&window); let mut clipboard = Clipboard::connect(&window);
let mut cache = user_interface::Cache::default(); let mut cache = user_interface::Cache::default();
@ -307,13 +320,22 @@ async fn run_instance<A, E, C>(
let mut mouse_interaction = mouse::Interaction::default(); let mut mouse_interaction = mouse::Interaction::default();
let mut events = Vec::new(); let mut events = Vec::new();
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut redraw_pending = false;
debug.startup_finished(); debug.startup_finished();
while let Some(event) = receiver.next().await { while let Some(event) = event_receiver.next().await {
match event { match event {
event::Event::NewEvents(start_cause) => {
redraw_pending = matches!(
start_cause,
event::StartCause::Init
| event::StartCause::Poll
| event::StartCause::ResumeTimeReached { .. }
);
}
event::Event::MainEventsCleared => { event::Event::MainEventsCleared => {
if events.is_empty() && messages.is_empty() { if !redraw_pending && events.is_empty() && messages.is_empty() {
continue; continue;
} }
@ -336,7 +358,7 @@ async fn run_instance<A, E, C>(
if !messages.is_empty() if !messages.is_empty()
|| matches!( || matches!(
interface_state, interface_state,
user_interface::State::Outdated, user_interface::State::Outdated
) )
{ {
let mut cache = let mut cache =
@ -374,6 +396,23 @@ async fn run_instance<A, E, C>(
} }
} }
// TODO: Avoid redrawing all the time by forcing widgets to
// request redraws on state changes
//
// Then, we can use the `interface_state` here to decide if a redraw
// is needed right away, or simply wait until a specific time.
let redraw_event = Event::Window(
crate::window::Event::RedrawRequested(Instant::now()),
);
let (interface_state, _) = user_interface.update(
&[redraw_event.clone()],
state.cursor_position(),
&mut renderer,
&mut clipboard,
&mut messages,
);
debug.draw_started(); debug.draw_started();
let new_mouse_interaction = user_interface.draw( let new_mouse_interaction = user_interface.draw(
&mut renderer, &mut renderer,
@ -394,6 +433,24 @@ async fn run_instance<A, E, C>(
} }
window.request_redraw(); window.request_redraw();
runtime
.broadcast((redraw_event, crate::event::Status::Ignored));
let _ = control_sender.start_send(match interface_state {
user_interface::State::Updated {
redraw_request: Some(redraw_request),
} => match redraw_request {
crate::window::RedrawRequest::NextFrame => {
ControlFlow::Poll
}
crate::window::RedrawRequest::At(at) => {
ControlFlow::WaitUntil(at)
}
},
_ => ControlFlow::Wait,
});
redraw_pending = false;
} }
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url), event::MacOS::ReceivedUrl(url),

View file

@ -1,7 +1,7 @@
//! Convert [`winit`] types into [`iced_native`] types, and viceversa. //! Convert [`winit`] types into [`iced_native`] types, and viceversa.
//! //!
//! [`winit`]: https://github.com/rust-windowing/winit //! [`winit`]: https://github.com/rust-windowing/winit
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
use crate::keyboard; use crate::keyboard;
use crate::mouse; use crate::mouse;
use crate::touch; use crate::touch;
@ -228,7 +228,7 @@ pub fn mode(mode: Option<winit::window::Fullscreen>) -> window::Mode {
/// Converts a `MouseCursor` from [`iced_native`] to a [`winit`] cursor icon. /// Converts a `MouseCursor` from [`iced_native`] to a [`winit`] cursor icon.
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native /// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
pub fn mouse_interaction( pub fn mouse_interaction(
interaction: mouse::Interaction, interaction: mouse::Interaction,
) -> winit::window::CursorIcon { ) -> winit::window::CursorIcon {
@ -252,7 +252,7 @@ pub fn mouse_interaction(
/// Converts a `MouseButton` from [`winit`] to an [`iced_native`] mouse button. /// Converts a `MouseButton` from [`winit`] to an [`iced_native`] mouse button.
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native /// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
match mouse_button { match mouse_button {
winit::event::MouseButton::Left => mouse::Button::Left, winit::event::MouseButton::Left => mouse::Button::Left,
@ -268,7 +268,7 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
/// modifiers state. /// modifiers state.
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native /// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
pub fn modifiers( pub fn modifiers(
modifiers: winit::event::ModifiersState, modifiers: winit::event::ModifiersState,
) -> keyboard::Modifiers { ) -> keyboard::Modifiers {
@ -295,7 +295,7 @@ pub fn cursor_position(
/// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event. /// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event.
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native /// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
pub fn touch_event( pub fn touch_event(
touch: winit::event::Touch, touch: winit::event::Touch,
scale_factor: f64, scale_factor: f64,
@ -326,7 +326,7 @@ pub fn touch_event(
/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code. /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code.
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native /// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
pub fn key_code( pub fn key_code(
virtual_keycode: winit::event::VirtualKeyCode, virtual_keycode: winit::event::VirtualKeyCode,
) -> keyboard::KeyCode { ) -> keyboard::KeyCode {

View file

@ -11,7 +11,7 @@
//! Additionally, a [`conversion`] module is available for users that decide to //! Additionally, a [`conversion`] module is available for users that decide to
//! implement a custom event loop. //! implement a custom event loop.
//! //!
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.7/native
//! [`winit`]: https://github.com/rust-windowing/winit //! [`winit`]: https://github.com/rust-windowing/winit
//! [`conversion`]: crate::conversion //! [`conversion`]: crate::conversion
#![doc( #![doc(

View file

@ -2,7 +2,7 @@
use crate::command::{self, Command}; use crate::command::{self, Command};
use iced_native::window; use iced_native::window;
pub use window::{Event, Id, Mode, UserAttention}; pub use window::{Event, Id, Mode, RedrawRequest, frames, UserAttention};
/// Closes the window. /// Closes the window.
pub fn close<Message>(id: window::Id) -> Command<Message> { pub fn close<Message>(id: window::Id) -> Command<Message> {