diff --git a/Cargo.toml b/Cargo.toml index e6fc665..af2c3e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,18 +16,22 @@ documentation = "https://docs.rs/bevy_iced" touch = [] [dependencies] -bevy_app = "0.10" -bevy_derive = "0.10" -bevy_ecs = "0.10" -bevy_input = "0.10" -bevy_math = "0.10" -bevy_render = "0.10" -bevy_utils = "0.10" -bevy_window = "0.10" +bevy_app = "0.11" +bevy_derive = "0.11" +bevy_ecs = "0.11" +bevy_input = "0.11" +bevy_math = "0.11" +bevy_render = "0.11" +bevy_utils = "0.11" +bevy_window = "0.11" -iced_wgpu = "0.10" -iced_native = "0.10" +iced = "0.10.0" +iced_core = "0.10.0" +iced_graphics = "0.9.0" +iced_runtime = "0.1.0" +iced_wgpu = "0.11.0" +iced_winit = "0.10.0" [dev-dependencies] -bevy = "0.10" -rand = "0.8" +bevy = "0.11" +rand = "0.8" \ No newline at end of file diff --git a/README.md b/README.md index 4c7894e..7a5a0e8 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ See the [examples](https://github.com/tasgon/bevy_iced/tree/master/examples) and |Bevy Version |Crate Version | |--------------|---------------| -|`0.10` |`0.3`, `master`| +|`0.11` |`master` | +|`0.10` |`0.3`, | |`0.9` |`0.2` | |`0.7` |`0.1` | diff --git a/assets/fonts/AlphaProta-License.txt b/assets/fonts/AlphaProta-License.txt new file mode 100644 index 0000000..354f1e0 --- /dev/null +++ b/assets/fonts/AlphaProta-License.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/assets/fonts/AlphaProta.ttf b/assets/fonts/AlphaProta.ttf new file mode 100644 index 0000000..e729620 Binary files /dev/null and b/assets/fonts/AlphaProta.ttf differ diff --git a/assets/fonts/Rainbow2000-License.txt b/assets/fonts/Rainbow2000-License.txt new file mode 100644 index 0000000..354f1e0 --- /dev/null +++ b/assets/fonts/Rainbow2000-License.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/assets/fonts/Rainbow2000-Regular.ttf b/assets/fonts/Rainbow2000-Regular.ttf new file mode 100644 index 0000000..5c87fa5 Binary files /dev/null and b/assets/fonts/Rainbow2000-Regular.ttf differ diff --git a/examples/basic.rs b/examples/basic.rs index 11b2e2f..2601d01 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -2,14 +2,15 @@ use bevy::prelude::*; use bevy_iced::iced::widget::text; use bevy_iced::{IcedContext, IcedPlugin}; +#[derive(Event)] pub enum UiMessage {} pub fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugin(IcedPlugin) + .add_plugins(IcedPlugin::default()) .add_event::() - .add_system(ui_system) + .add_systems(Update, ui_system) .run(); } diff --git a/examples/fonts.rs b/examples/fonts.rs new file mode 100644 index 0000000..fc478e4 --- /dev/null +++ b/examples/fonts.rs @@ -0,0 +1,37 @@ +use bevy::prelude::*; +use bevy_iced::iced::{ + widget::{column, text}, + Font, +}; +use bevy_iced::{IcedContext, IcedPlugin}; + +static ALPHAPROTA_FONT_BYTES: &'static [u8] = include_bytes!("../assets/fonts/AlphaProta.ttf"); +static RAINBOW2000_FONT_BYTES: &'static [u8] = + include_bytes!("../assets/fonts/Rainbow2000-Regular.ttf"); + +const ALPHAPROTA_FONT: Font = Font::with_name("Alpha Prota"); +const RAINBOW2000_FONT: Font = Font::with_name("Rainbow 2000"); + +#[derive(Event)] +pub enum UiMessage {} + +pub fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugins(IcedPlugin { + fonts: vec![ALPHAPROTA_FONT_BYTES, RAINBOW2000_FONT_BYTES], + default_font: ALPHAPROTA_FONT, + default_text_size: 40.0, + ..Default::default() + }) + .add_event::() + .add_systems(Update, ui_system) + .run(); +} + +fn ui_system(mut ctx: IcedContext) { + ctx.display(column!( + text(format!("I am the default font")), + text(format!("I am another font")).font(RAINBOW2000_FONT) + )); +} diff --git a/examples/interactive.rs b/examples/interactive.rs index ff39cf8..38567d6 100644 --- a/examples/interactive.rs +++ b/examples/interactive.rs @@ -3,13 +3,15 @@ use bevy::{ input::mouse::{MouseButtonInput, MouseWheel}, prelude::*, }; -use bevy_iced::{ - iced::widget::{slider, text, text_input, Button, Column, Row}, - IcedContext, IcedPlugin, IcedSettings, +use bevy_iced::iced::{ + self, + widget::{slider, text, text_input, Button, Column, Row}, + Alignment, }; +use bevy_iced::{IcedContext, IcedPlugin, IcedSettings, IcedStyle}; use rand::random as rng; -#[derive(Clone)] +#[derive(Clone, Event)] enum UiMessage { BoxRequested, Scale(f32), @@ -34,9 +36,11 @@ pub fn main() { }), ..Default::default() })) - .add_plugin(IcedPlugin) - .add_plugin(FrameTimeDiagnosticsPlugin::default()) - .add_plugin(LogDiagnosticsPlugin::default()) + .add_plugins(( + IcedPlugin::default(), + FrameTimeDiagnosticsPlugin::default(), + LogDiagnosticsPlugin::default(), + )) .add_event::() .insert_resource(UiActive(true)) .insert_resource(UiData { @@ -45,17 +49,17 @@ pub fn main() { }) .insert_resource(IcedSettings { scale_factor: None, - theme: bevy_iced::iced_wgpu::Theme::Light, - style: bevy_iced::iced::renderer::Style { - text_color: bevy_iced::iced::Color::from_rgb(0.0, 1.0, 1.0), + theme: iced::Theme::Light, + style: IcedStyle { + text_color: iced::Color::from_rgb(0.0, 1.0, 1.0), }, + ..Default::default() }) - .add_startup_system(build_program) - .add_system(tick) - .add_system(box_system) - .add_system(update_scale_factor) - .add_system(toggle_ui) - .add_system(ui_system) + .add_systems(Startup, build_program) + .add_systems( + Update, + (tick, box_system, update_scale_factor, toggle_ui, ui_system), + ) .run(); } @@ -137,7 +141,7 @@ fn ui_system( let row = Row::new() .spacing(10) - .align_items(iced_native::Alignment::Center) + .align_items(Alignment::Center) .push(Button::new(text("Request box")).on_press(UiMessage::BoxRequested)) .push(text(format!( "{} boxes (amplitude: {})", @@ -146,7 +150,7 @@ fn ui_system( ))); let edit = text_input("", &data.text).on_input(UiMessage::Text); let column = Column::new() - .align_items(iced_native::Alignment::Center) + .align_items(Alignment::Center) .spacing(10) .push(edit) .push(slider(0.0..=100.0, data.scale, UiMessage::Scale)) diff --git a/examples/toggle.rs b/examples/toggle.rs index 2c76e6c..9da170d 100644 --- a/examples/toggle.rs +++ b/examples/toggle.rs @@ -4,6 +4,7 @@ use bevy_iced::{IcedContext, IcedPlugin}; use bevy_input::keyboard::KeyboardInput; use bevy_input::ButtonState; +#[derive(Event)] pub enum UiMessage {} #[derive(Resource, PartialEq, Eq)] @@ -12,11 +13,11 @@ pub struct UiActive(bool); pub fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugin(IcedPlugin) + .add_plugins(IcedPlugin::default()) .add_event::() .insert_resource(UiActive(true)) - .add_system(toggle_system) - .add_system(ui_system.run_if(resource_equals(UiActive(true)))) + .add_systems(Update, toggle_system) + .add_systems(Update, ui_system.run_if(resource_equals(UiActive(true)))) .run(); } diff --git a/src/conversions.rs b/src/conversions.rs index 8727a99..fbbe7d7 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -4,9 +4,9 @@ use bevy_input::prelude::MouseButton; use bevy_input::touch::{TouchInput, TouchPhase}; #[cfg(feature = "touch")] use bevy_math::Vec2; -use iced_native::keyboard::KeyCode as IcedKeyCode; +use iced::keyboard::KeyCode as IcedKeyCode; #[cfg(feature = "touch")] -use iced_native::{ +use iced::{ touch::{self, Finger}, Point, }; @@ -123,11 +123,11 @@ pub fn key_code(virtual_keycode: BevyKeyCode) -> IcedKeyCode { BevyKeyCode::Grave => IcedKeyCode::Grave, BevyKeyCode::Kana => IcedKeyCode::Kana, BevyKeyCode::Kanji => IcedKeyCode::Kanji, - BevyKeyCode::LAlt => IcedKeyCode::LAlt, - BevyKeyCode::LBracket => IcedKeyCode::LBracket, - BevyKeyCode::LControl => IcedKeyCode::LControl, - BevyKeyCode::LShift => IcedKeyCode::LShift, - BevyKeyCode::LWin => IcedKeyCode::LWin, + BevyKeyCode::AltLeft => IcedKeyCode::LAlt, + BevyKeyCode::BracketLeft => IcedKeyCode::LBracket, + BevyKeyCode::ControlLeft => IcedKeyCode::LControl, + BevyKeyCode::ShiftLeft => IcedKeyCode::LShift, + BevyKeyCode::SuperLeft => IcedKeyCode::LWin, BevyKeyCode::Mail => IcedKeyCode::Mail, BevyKeyCode::MediaSelect => IcedKeyCode::MediaSelect, BevyKeyCode::MediaStop => IcedKeyCode::MediaStop, @@ -147,11 +147,11 @@ pub fn key_code(virtual_keycode: BevyKeyCode) -> IcedKeyCode { BevyKeyCode::PlayPause => IcedKeyCode::PlayPause, BevyKeyCode::Power => IcedKeyCode::Power, BevyKeyCode::PrevTrack => IcedKeyCode::PrevTrack, - BevyKeyCode::RAlt => IcedKeyCode::RAlt, - BevyKeyCode::RBracket => IcedKeyCode::RBracket, - BevyKeyCode::RControl => IcedKeyCode::RControl, - BevyKeyCode::RShift => IcedKeyCode::RShift, - BevyKeyCode::RWin => IcedKeyCode::RWin, + BevyKeyCode::AltRight => IcedKeyCode::RAlt, + BevyKeyCode::BracketRight => IcedKeyCode::RBracket, + BevyKeyCode::ControlRight => IcedKeyCode::RControl, + BevyKeyCode::ShiftRight => IcedKeyCode::RShift, + BevyKeyCode::SuperRight => IcedKeyCode::RWin, BevyKeyCode::Semicolon => IcedKeyCode::Semicolon, BevyKeyCode::Slash => IcedKeyCode::Slash, BevyKeyCode::Sleep => IcedKeyCode::Sleep, @@ -179,13 +179,13 @@ pub fn key_code(virtual_keycode: BevyKeyCode) -> IcedKeyCode { } } -pub fn mouse_button(button: MouseButton) -> iced_native::mouse::Button { - use iced_native::mouse::Button; +pub fn mouse_button(button: MouseButton) -> iced::mouse::Button { + use iced::mouse::Button; match button { MouseButton::Left => Button::Left, MouseButton::Right => Button::Right, MouseButton::Middle => Button::Middle, - MouseButton::Other(val) => Button::Other(val as u8), + MouseButton::Other(val) => Button::Other(val), } } @@ -202,7 +202,7 @@ pub fn touch_event(bevy_touch_input: &TouchInput) -> touch::Event { position: Point { x, y }, }, TouchInput { - phase: TouchPhase::Cancelled, + phase: TouchPhase::Canceled, position: Vec2 { x, y }, id: finger, .. diff --git a/src/lib.rs b/src/lib.rs index dde4554..7431181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,14 +5,15 @@ //! use bevy_iced::iced::widget::text; //! use bevy_iced::{IcedContext, IcedPlugin}; //! +//! #[derive(Event)] //! pub enum UiMessage {} //! //! pub fn main() { //! App::new() //! .add_plugins(DefaultPlugins) -//! .add_plugin(IcedPlugin) +//! .add_plugin(IcedPlugin::default()) //! .add_event::() -//! .add_system(ui_system) +//! .add_systems(Update, ui_system) //! .run(); //! } //! @@ -33,30 +34,33 @@ use std::any::{Any, TypeId}; +use std::borrow::Cow; use std::sync::Arc; use std::sync::Mutex; -use crate::render::IcedNode; -use crate::render::ViewportResource; +use crate::render::{extract_iced_data, IcedNode, ViewportResource}; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin, Update}; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::event::Event; use bevy_ecs::prelude::{EventWriter, Query, With}; use bevy_ecs::system::{NonSendMut, Res, ResMut, Resource, SystemParam}; #[cfg(feature = "touch")] use bevy_input::touch::Touches; use bevy_math::Vec2; use bevy_render::render_graph::RenderGraph; -use bevy_render::renderer::RenderDevice; +use bevy_render::renderer::{RenderDevice, RenderQueue}; use bevy_render::{ExtractSchedule, RenderApp}; use bevy_utils::HashMap; use bevy_window::{PrimaryWindow, Window}; -use iced::{user_interface, Element, UserInterface}; -pub use iced_native as iced; -use iced_native::{Debug, Size}; +use iced::mouse::Cursor; +use iced_graphics::backend::Text; +use iced_runtime::user_interface::UserInterface; +use iced_winit::Viewport; + +pub use iced; +pub use iced_core::renderer::Style as IcedStyle; +pub use iced_graphics::Antialiasing as IcedAntialiasing; pub use iced_wgpu; -use iced_wgpu::{wgpu, Viewport}; mod conversions; mod render; @@ -65,57 +69,100 @@ mod systems; use systems::IcedEventQueue; /// The main feature of `bevy_iced`. -/// Add this to your [`App`] by calling `app.add_plugin(bevy_iced::IcedPlugin)`. -pub struct IcedPlugin; +/// Add this to your [`App`] by calling `app.add_plugin(bevy_iced::IcedPlugin::default())`. +pub struct IcedPlugin { + /// The default [`Font`] to use. + pub default_font: iced::Font, + /// The default size of text. + pub default_text_size: f32, + /// The antialiasing strategy that will be used for triangle primitives. + /// + /// By default, it is `None`. + pub antialiasing: Option, + /// Font file contents + pub fonts: Vec<&'static [u8]>, +} + +impl Default for IcedPlugin { + fn default() -> Self { + Self { + default_font: iced::Font::default(), + default_text_size: iced_wgpu::Settings::default().default_text_size, + antialiasing: None, + fonts: vec![], + } + } +} impl Plugin for IcedPlugin { fn build(&self, app: &mut App) { - let default_viewport = Viewport::with_physical_size(Size::new(1600, 900), 1.0); - let default_viewport = ViewportResource(default_viewport); - let iced_resource: IcedResource = IcedProps::new(app).into(); - - app.add_system(systems::process_input) - .add_system(render::update_viewport) + app.add_systems(Update, (systems::process_input, render::update_viewport)) .insert_resource(DidDraw::default()) - .insert_resource(iced_resource.clone()) .insert_resource(IcedSettings::default()) .insert_non_send_resource(IcedCache::default()) - .insert_resource(IcedEventQueue::default()) - .insert_resource(default_viewport.clone()); + .insert_resource(IcedEventQueue::default()); + } + + fn finish(&self, app: &mut App) { + let default_viewport = Viewport::with_physical_size(iced::Size::new(1600, 900), 1.0); + let default_viewport = ViewportResource(default_viewport); + let iced_resource: IcedResource = IcedProps::new( + app, + self.default_font, + self.default_text_size, + self.antialiasing, + &self.fonts, + ) + .into(); + + app.insert_resource(default_viewport.clone()) + .insert_resource(iced_resource.clone()); let render_app = app.sub_app_mut(RenderApp); render_app .insert_resource(default_viewport) .insert_resource(iced_resource) - .add_system(render::extract_iced_data.in_schedule(ExtractSchedule)); + .add_systems(ExtractSchedule, extract_iced_data); setup_pipeline(&mut render_app.world.get_resource_mut().unwrap()); } } struct IcedProps { - renderer: iced_wgpu::Renderer, - debug: iced_native::Debug, - clipboard: iced_native::clipboard::Null, + renderer: iced_wgpu::Renderer, + debug: iced_runtime::Debug, + clipboard: iced_core::clipboard::Null, } impl IcedProps { - fn new(app: &App) -> Self { - let device = app - .sub_app(RenderApp) - .world + fn new( + app: &App, + default_font: iced::Font, + default_text_size: f32, + antialiasing: Option, + fonts: &Vec<&'static [u8]>, + ) -> Self { + let render_world = &app.sub_app(RenderApp).world; + let device = render_world .get_resource::() .unwrap() .wgpu_device(); - let format = wgpu::TextureFormat::Bgra8UnormSrgb; + let queue = render_world.get_resource::().unwrap(); + let format = iced_wgpu::wgpu::TextureFormat::Bgra8UnormSrgb; + let settings = iced_wgpu::Settings { + default_font, + default_text_size, + antialiasing, + ..Default::default() + }; + let mut backend = iced_wgpu::Backend::new(device, queue, settings, format); + for font in fonts { + backend.load_font(Cow::Borrowed(*font)); + } Self { - renderer: iced_wgpu::Renderer::new(iced_wgpu::Backend::new( - device, - Default::default(), - format, - )), - debug: Debug::new(), - clipboard: iced_native::clipboard::Null, + renderer: iced_wgpu::Renderer::new(backend), + debug: iced_runtime::Debug::new(), + clipboard: iced_core::clipboard::Null, } } } @@ -147,11 +194,11 @@ fn setup_pipeline(graph: &mut RenderGraph) { #[doc(hidden)] #[derive(Default)] pub struct IcedCache { - cache: HashMap>, + cache: HashMap>, } impl IcedCache { - fn get(&mut self) -> &mut Option { + fn get(&mut self) -> &mut Option { let id = TypeId::of::(); if !self.cache.contains_key(&id) { self.cache.insert(id, Some(Default::default())); @@ -167,9 +214,9 @@ pub struct IcedSettings { /// Setting this to `None` defaults to using the `Window`s scale factor. pub scale_factor: Option, /// The theme to use for rendering Iced elements. - pub theme: iced_wgpu::Theme, + pub theme: iced::Theme, /// The style to use for rendering Iced elements. - pub style: iced_native::renderer::Style, + pub style: IcedStyle, } impl IcedSettings { @@ -183,9 +230,9 @@ impl Default for IcedSettings { fn default() -> Self { Self { scale_factor: None, - theme: iced_wgpu::Theme::Dark, - style: iced_native::renderer::Style { - text_color: iced_native::Color::WHITE, + theme: iced::Theme::Dark, + style: IcedStyle { + text_color: iced::Color::WHITE, }, } } @@ -196,7 +243,7 @@ impl Default for IcedSettings { pub(crate) struct DidDraw(std::sync::atomic::AtomicBool); /// The context for interacting with Iced. Add this as a parameter to your system. -/// ```no_run +/// ```ignore /// fn ui_system(..., mut ctx: IcedContext) { /// let element = ...; // Build your element /// ctx.display(element); @@ -206,7 +253,7 @@ pub(crate) struct DidDraw(std::sync::atomic::AtomicBool); /// `IcedContext` requires an event system to be defined in the [`App`]. /// Do so by invoking `app.add_event::()` when constructing your App. #[derive(SystemParam)] -pub struct IcedContext<'w, 's, Message: Event> { +pub struct IcedContext<'w, 's, Message: bevy_ecs::event::Event> { viewport: Res<'w, ViewportResource>, props: Res<'w, IcedResource>, settings: Res<'w, IcedSettings>, @@ -219,9 +266,12 @@ pub struct IcedContext<'w, 's, Message: Event> { touches: Res<'w, Touches>, } -impl<'w, 's, M: Event> IcedContext<'w, 's, M> { +impl<'w, 's, M: bevy_ecs::event::Event> IcedContext<'w, 's, M> { /// Display an [`Element`] to the screen. - pub fn display<'a>(&'a mut self, element: impl Into>) { + pub fn display<'a>( + &'a mut self, + element: impl Into>>, + ) { let IcedProps { ref mut renderer, ref mut clipboard, @@ -231,17 +281,16 @@ impl<'w, 's, M: Event> IcedContext<'w, 's, M> { let element = element.into(); - let cursor_position = { + let cursor = { let window = self.windows.single(); - - window - .cursor_position() - .map(|Vec2 { x, y }| iced_native::Point { - x: x * bounds.width / window.width(), - y: (window.height() - y) * bounds.height / window.height(), - }) - .or_else(|| process_touch_input(self)) - .unwrap_or(iced_native::Point::ORIGIN) + match window.cursor_position() { + Some(position) => { + Cursor::Available(process_cursor_position(position, bounds, window)) + } + None => process_touch_input(self) + .map(Cursor::Available) + .unwrap_or(Cursor::Unavailable), + } }; let mut messages = Vec::::new(); @@ -250,7 +299,7 @@ impl<'w, 's, M: Event> IcedContext<'w, 's, M> { let mut ui = UserInterface::build(element, bounds, cache, renderer); let (_, _event_statuses) = ui.update( self.events.as_slice(), - cursor_position, + cursor, renderer, clipboard, &mut messages, @@ -258,12 +307,7 @@ impl<'w, 's, M: Event> IcedContext<'w, 's, M> { messages.into_iter().for_each(|msg| self.messages.send(msg)); - ui.draw( - renderer, - &self.settings.theme, - &self.settings.style, - cursor_position, - ); + ui.draw(renderer, &self.settings.theme, &self.settings.style, cursor); self.events.clear(); *cache_entry = Some(ui.into_cache()); @@ -272,9 +316,16 @@ impl<'w, 's, M: Event> IcedContext<'w, 's, M> { } } +fn process_cursor_position(position: Vec2, bounds: iced::Size, window: &Window) -> iced::Point { + iced::Point { + x: position.x * bounds.width / window.width(), + y: position.y * bounds.height / window.height(), + } +} + #[cfg(feature = "touch")] /// To correctly process input as last resort events are used -fn process_touch_input(context: &IcedContext) -> Option { +fn process_touch_input(context: &IcedContext) -> Option { context .touches .first_pressed_position() @@ -283,16 +334,16 @@ fn process_touch_input(context: &IcedContext) -> Option(context: &IcedContext) -> Option(_: &IcedContext) -> Option { +fn process_touch_input(_: &IcedContext) -> Option { None } diff --git a/src/render.rs b/src/render.rs index afc2d05..88609b8 100644 --- a/src/render.rs +++ b/src/render.rs @@ -4,7 +4,7 @@ use bevy_ecs::{ system::{Commands, Res, Resource}, world::World, }; -use bevy_render::renderer::RenderDevice; +use bevy_render::renderer::{RenderDevice, RenderQueue}; use bevy_render::{ render_graph::{Node, NodeRunError, RenderGraphContext}, renderer::RenderContext, @@ -12,8 +12,9 @@ use bevy_render::{ Extract, }; use bevy_window::Window; -use iced_native::Size; -use iced_wgpu::{wgpu::util::StagingBelt, Viewport}; +use iced::Size; +use iced_wgpu::wgpu::util::StagingBelt; +use iced_winit::Viewport; use std::sync::Mutex; use crate::{DidDraw, IcedProps, IcedResource, IcedSettings}; @@ -80,12 +81,17 @@ impl Node for IcedNode { .unwrap() .windows .values() - .next() else { return Ok(()) }; + .next() + else { + return Ok(()); + }; let IcedProps { renderer, debug, .. } = &mut *world.resource::().lock().unwrap(); - let render_device = world.resource::(); + let render_device = world.resource::().wgpu_device(); + let render_queue = world.resource::(); + let viewport = world.resource::(); if !world .get_resource::() @@ -94,18 +100,15 @@ impl Node for IcedNode { { return Ok(()); } - - let view = extracted_window.swap_chain_texture.as_ref().unwrap(); + let view = extracted_window.swap_chain_texture_view.as_ref().unwrap(); let staging_belt = &mut *self.staging_belt.lock().unwrap(); - let viewport = world.resource::(); - let device = render_device.wgpu_device(); - renderer.with_primitives(|backend, primitives| { backend.present( - device, - staging_belt, + render_device, + render_queue, render_context.command_encoder(), + None, view, primitives, viewport, diff --git a/src/systems.rs b/src/systems.rs index bf877e9..8f36b18 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -13,10 +13,10 @@ use bevy_input::{ ButtonState, Input, }; use bevy_window::{CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter}; -use iced_native::{keyboard, mouse, Event as IcedEvent, Point}; +use iced::{keyboard, mouse, Event as IcedEvent, Point}; #[derive(Resource, Deref, DerefMut, Default)] -pub struct IcedEventQueue(Vec); +pub struct IcedEventQueue(Vec); #[derive(SystemParam)] pub struct InputEvents<'w, 's> { @@ -33,16 +33,16 @@ pub struct InputEvents<'w, 's> { fn compute_modifiers(input_map: &Input) -> keyboard::Modifiers { let mut modifiers = keyboard::Modifiers::default(); - if input_map.any_pressed([KeyCode::LControl, KeyCode::RControl]) { + if input_map.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]) { modifiers |= keyboard::Modifiers::CTRL; } - if input_map.any_pressed([KeyCode::LShift, KeyCode::RShift]) { + if input_map.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]) { modifiers |= keyboard::Modifiers::SHIFT; } - if input_map.any_pressed([KeyCode::LAlt, KeyCode::RAlt]) { + if input_map.any_pressed([KeyCode::AltLeft, KeyCode::AltRight]) { modifiers |= keyboard::Modifiers::ALT; } - if input_map.any_pressed([KeyCode::LWin, KeyCode::RWin]) { + if input_map.any_pressed([KeyCode::SuperLeft, KeyCode::SuperRight]) { modifiers |= keyboard::Modifiers::LOGO; } modifiers @@ -64,28 +64,28 @@ pub fn process_input( for ev in events.mouse_button.iter() { let button = conversions::mouse_button(ev.button); event_queue.push(IcedEvent::Mouse(match ev.state { - ButtonState::Pressed => iced_native::mouse::Event::ButtonPressed(button), - ButtonState::Released => iced_native::mouse::Event::ButtonReleased(button), + ButtonState::Pressed => iced::mouse::Event::ButtonPressed(button), + ButtonState::Released => iced::mouse::Event::ButtonReleased(button), })) } for _ev in events.cursor_entered.iter() { - event_queue.push(IcedEvent::Mouse(iced_native::mouse::Event::CursorEntered)); + event_queue.push(IcedEvent::Mouse(iced::mouse::Event::CursorEntered)); } for _ev in events.cursor_left.iter() { - event_queue.push(IcedEvent::Mouse(iced_native::mouse::Event::CursorLeft)); + event_queue.push(IcedEvent::Mouse(iced::mouse::Event::CursorLeft)); } for ev in events.mouse_wheel.iter() { - event_queue.push(IcedEvent::Mouse(iced_native::mouse::Event::WheelScrolled { + event_queue.push(IcedEvent::Mouse(iced::mouse::Event::WheelScrolled { delta: mouse::ScrollDelta::Pixels { x: ev.x, y: ev.y }, })); } for ev in events.received_character.iter() { event_queue.push(IcedEvent::Keyboard( - iced_native::keyboard::Event::CharacterReceived(ev.char), + iced::keyboard::Event::CharacterReceived(ev.char), )); } @@ -94,14 +94,14 @@ pub fn process_input( use keyboard::Event::*; let modifiers = compute_modifiers(&input_map); let event = match code { - KeyCode::LControl - | KeyCode::RControl - | KeyCode::LShift - | KeyCode::RShift - | KeyCode::LAlt - | KeyCode::RAlt - | KeyCode::LWin - | KeyCode::RWin => ModifiersChanged(modifiers), + KeyCode::ControlLeft + | KeyCode::ControlRight + | KeyCode::ShiftLeft + | KeyCode::ShiftRight + | KeyCode::AltLeft + | KeyCode::AltRight + | KeyCode::SuperLeft + | KeyCode::SuperRight => ModifiersChanged(modifiers), code => { let key_code = conversions::key_code(code); if ev.state.is_pressed() {