From 5a30923b70dfdd257ef97c5e76ca48cf1ebbb327 Mon Sep 17 00:00:00 2001 From: Joonas Satka Date: Tue, 18 Jul 2023 13:40:13 +0300 Subject: [PATCH 1/5] Upgrade bevy to 0.11 --- Cargo.toml | 27 ++++---- README.md | 3 +- examples/basic.rs | 7 +- examples/interactive.rs | 35 +++++----- examples/toggle.rs | 9 +-- src/conversions.rs | 32 ++++----- src/lib.rs | 139 ++++++++++++++++++++++------------------ src/render.rs | 27 ++++---- src/systems.rs | 40 ++++++------ 9 files changed, 170 insertions(+), 149 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6fc665..b30bda3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,18 +16,21 @@ 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 = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } +iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } +iced_runtime = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } +iced_wgpu = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } +iced_winit = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } [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/examples/basic.rs b/examples/basic.rs index 11b2e2f..00a1bb7 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,15 +1,16 @@ use bevy::prelude::*; -use bevy_iced::iced::widget::text; +use bevy_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) .add_event::() - .add_system(ui_system) + .add_systems(Update, ui_system) .run(); } diff --git a/examples/interactive.rs b/examples/interactive.rs index ff39cf8..8d7d7d6 100644 --- a/examples/interactive.rs +++ b/examples/interactive.rs @@ -4,12 +4,12 @@ use bevy::{ prelude::*, }; use bevy_iced::{ - iced::widget::{slider, text, text_input, Button, Column, Row}, - IcedContext, IcedPlugin, IcedSettings, + widget::{slider, text, text_input, Button, Column, Row}, + Alignment, IcedContext, IcedPlugin, IcedSettings, }; use rand::random as rng; -#[derive(Clone)] +#[derive(Clone, Event)] enum UiMessage { BoxRequested, Scale(f32), @@ -34,9 +34,11 @@ pub fn main() { }), ..Default::default() })) - .add_plugin(IcedPlugin) - .add_plugin(FrameTimeDiagnosticsPlugin::default()) - .add_plugin(LogDiagnosticsPlugin::default()) + .add_plugins(( + IcedPlugin, + FrameTimeDiagnosticsPlugin::default(), + LogDiagnosticsPlugin::default(), + )) .add_event::() .insert_resource(UiActive(true)) .insert_resource(UiData { @@ -45,17 +47,16 @@ 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: bevy_iced::Theme::Light, + style: bevy_iced::Style { + text_color: bevy_iced::Color::from_rgb(0.0, 1.0, 1.0), }, }) - .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 +138,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 +147,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..265a045 100644 --- a/examples/toggle.rs +++ b/examples/toggle.rs @@ -1,9 +1,10 @@ use bevy::prelude::*; -use bevy_iced::iced::widget::text; +use bevy_iced::widget::text; 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) .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..a9d7c9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,10 @@ //! //! ```no_run //! use bevy::prelude::*; -//! use bevy_iced::iced::widget::text; +//! use bevy_iced::widget::text; //! use bevy_iced::{IcedContext, IcedPlugin}; //! +//! #[derive(Event)] //! pub enum UiMessage {} //! //! pub fn main() { @@ -36,27 +37,27 @@ use std::any::{Any, TypeId}; 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_core::mouse::Cursor; +use iced_runtime::user_interface::UserInterface; +use iced_winit::Viewport; + +pub use iced::*; +pub use iced_core::renderer::Style; pub use iced_wgpu; -use iced_wgpu::{wgpu, Viewport}; mod conversions; mod render; @@ -70,52 +71,58 @@ pub struct IcedPlugin; impl Plugin for IcedPlugin { fn build(&self, app: &mut App) { + app.add_systems(Update, (systems::process_input, render::update_viewport)) + .insert_resource(DidDraw::default()) + .insert_resource(IcedSettings::default()) + .insert_non_send_resource(IcedCache::default()) + .insert_resource(IcedEventQueue::default()); + } + + fn finish(&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) - .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()); + 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 + 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 debug = iced_runtime::Debug::new(); + let clipboard = iced_core::clipboard::Null; Self { renderer: iced_wgpu::Renderer::new(iced_wgpu::Backend::new( device, + queue, Default::default(), format, )), - debug: Debug::new(), - clipboard: iced_native::clipboard::Null, + debug, + clipboard, } } } @@ -147,11 +154,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 +174,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: Theme, /// The style to use for rendering Iced elements. - pub style: iced_native::renderer::Style, + pub style: Style, } impl IcedSettings { @@ -183,9 +190,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: Theme::Dark, + style: Style { + text_color: Color::WHITE, }, } } @@ -196,7 +203,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 +213,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 +226,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 +241,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 +259,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 +267,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 +276,16 @@ impl<'w, 's, M: Event> IcedContext<'w, 's, M> { } } +fn process_cursor_position(position: Vec2, bounds: 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 +294,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() { From 5776cfc5764c582325d7133d7154fa1b2f05ac0a Mon Sep 17 00:00:00 2001 From: Joonas Satka Date: Wed, 19 Jul 2023 08:42:13 +0300 Subject: [PATCH 2/5] Enable setting default font, default text size and anti-aliasing --- Cargo.toml | 1 + examples/basic.rs | 4 +- examples/interactive.rs | 15 +++++--- examples/toggle.rs | 4 +- src/lib.rs | 82 ++++++++++++++++++++++++++++------------- 5 files changed, 71 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b30bda3..f1f7b1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ bevy_window = "0.11" iced = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } +iced_graphics = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } iced_runtime = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } iced_wgpu = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } iced_winit = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } diff --git a/examples/basic.rs b/examples/basic.rs index 00a1bb7..2601d01 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use bevy_iced::widget::text; +use bevy_iced::iced::widget::text; use bevy_iced::{IcedContext, IcedPlugin}; #[derive(Event)] @@ -8,7 +8,7 @@ pub enum UiMessage {} pub fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(IcedPlugin) + .add_plugins(IcedPlugin::default()) .add_event::() .add_systems(Update, ui_system) .run(); diff --git a/examples/interactive.rs b/examples/interactive.rs index 8d7d7d6..38567d6 100644 --- a/examples/interactive.rs +++ b/examples/interactive.rs @@ -3,10 +3,12 @@ use bevy::{ input::mouse::{MouseButtonInput, MouseWheel}, prelude::*, }; -use bevy_iced::{ +use bevy_iced::iced::{ + self, widget::{slider, text, text_input, Button, Column, Row}, - Alignment, IcedContext, IcedPlugin, IcedSettings, + Alignment, }; +use bevy_iced::{IcedContext, IcedPlugin, IcedSettings, IcedStyle}; use rand::random as rng; #[derive(Clone, Event)] @@ -35,7 +37,7 @@ pub fn main() { ..Default::default() })) .add_plugins(( - IcedPlugin, + IcedPlugin::default(), FrameTimeDiagnosticsPlugin::default(), LogDiagnosticsPlugin::default(), )) @@ -47,10 +49,11 @@ pub fn main() { }) .insert_resource(IcedSettings { scale_factor: None, - theme: bevy_iced::Theme::Light, - style: bevy_iced::Style { - text_color: bevy_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_systems(Startup, build_program) .add_systems( diff --git a/examples/toggle.rs b/examples/toggle.rs index 265a045..9da170d 100644 --- a/examples/toggle.rs +++ b/examples/toggle.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use bevy_iced::widget::text; +use bevy_iced::iced::widget::text; use bevy_iced::{IcedContext, IcedPlugin}; use bevy_input::keyboard::KeyboardInput; use bevy_input::ButtonState; @@ -13,7 +13,7 @@ pub struct UiActive(bool); pub fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(IcedPlugin) + .add_plugins(IcedPlugin::default()) .add_event::() .insert_resource(UiActive(true)) .add_systems(Update, toggle_system) diff --git a/src/lib.rs b/src/lib.rs index a9d7c9f..44adebe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! ```no_run //! use bevy::prelude::*; -//! use bevy_iced::widget::text; +//! use bevy_iced::iced::widget::text; //! use bevy_iced::{IcedContext, IcedPlugin}; //! //! #[derive(Event)] @@ -11,9 +11,9 @@ //! 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(); //! } //! @@ -51,13 +51,13 @@ use bevy_render::renderer::{RenderDevice, RenderQueue}; use bevy_render::{ExtractSchedule, RenderApp}; use bevy_utils::HashMap; use bevy_window::{PrimaryWindow, Window}; -use iced_core::mouse::Cursor; +use iced::mouse::Cursor; use iced_runtime::user_interface::UserInterface; use iced_winit::Viewport; -pub use iced::*; -pub use iced_core::renderer::Style; -pub use iced_wgpu; +pub use iced; +pub use iced_core::renderer::Style as IcedStyle; +pub use iced_graphics::Antialiasing as IcedAntialiasing; mod conversions; mod render; @@ -66,8 +66,27 @@ 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, +} + +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, + } + } +} impl Plugin for IcedPlugin { fn build(&self, app: &mut App) { @@ -79,9 +98,15 @@ impl Plugin for IcedPlugin { } fn finish(&self, app: &mut App) { - let default_viewport = Viewport::with_physical_size(Size::new(1600, 900), 1.0); + 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).into(); + let iced_resource: IcedResource = IcedProps::new( + app, + self.default_font, + self.default_text_size, + self.antialiasing, + ) + .into(); app.insert_resource(default_viewport.clone()) .insert_resource(iced_resource.clone()); @@ -96,13 +121,18 @@ impl Plugin for IcedPlugin { } struct IcedProps { - renderer: iced_wgpu::Renderer, + renderer: iced_wgpu::Renderer, debug: iced_runtime::Debug, clipboard: iced_core::clipboard::Null, } impl IcedProps { - fn new(app: &App) -> Self { + fn new( + app: &App, + default_font: iced::Font, + default_text_size: f32, + antialiasing: Option, + ) -> Self { let render_world = &app.sub_app(RenderApp).world; let device = render_world .get_resource::() @@ -110,16 +140,18 @@ impl IcedProps { .wgpu_device(); 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 debug = iced_runtime::Debug::new(); let clipboard = iced_core::clipboard::Null; Self { renderer: iced_wgpu::Renderer::new(iced_wgpu::Backend::new( - device, - queue, - Default::default(), - format, + device, queue, settings, format, )), debug, clipboard, @@ -174,9 +206,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: Theme, + pub theme: iced::Theme, /// The style to use for rendering Iced elements. - pub style: Style, + pub style: IcedStyle, } impl IcedSettings { @@ -190,9 +222,9 @@ impl Default for IcedSettings { fn default() -> Self { Self { scale_factor: None, - theme: Theme::Dark, - style: Style { - text_color: Color::WHITE, + theme: iced::Theme::Dark, + style: IcedStyle { + text_color: iced::Color::WHITE, }, } } @@ -230,7 +262,7 @@ 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>>, + element: impl Into>>, ) { let IcedProps { ref mut renderer, @@ -276,7 +308,7 @@ impl<'w, 's, M: bevy_ecs::event::Event> IcedContext<'w, 's, M> { } } -fn process_cursor_position(position: Vec2, bounds: Size, window: &Window) -> iced::Point { +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(), From d281fe1cec604127d219d766b9c8cda28a6f72fa Mon Sep 17 00:00:00 2001 From: Joonas Satka Date: Tue, 25 Jul 2023 16:25:36 +0300 Subject: [PATCH 3/5] Re-export iced_wgpu --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 44adebe..75bb0f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ use iced_runtime::user_interface::UserInterface; use iced_winit::Viewport; pub use iced; +pub use iced_wgpu; pub use iced_core::renderer::Style as IcedStyle; pub use iced_graphics::Antialiasing as IcedAntialiasing; From d3e2452256233477bfcf2800add242421fcf779e Mon Sep 17 00:00:00 2001 From: Joonas Satka Date: Tue, 25 Jul 2023 21:47:50 +0300 Subject: [PATCH 4/5] Add support for custom fonts --- assets/fonts/AlphaProta-License.txt | 121 +++++++++++++++++++++++++++ assets/fonts/AlphaProta.ttf | Bin 0 -> 4124 bytes assets/fonts/Rainbow2000-License.txt | 121 +++++++++++++++++++++++++++ assets/fonts/Rainbow2000-Regular.ttf | Bin 0 -> 92448 bytes examples/fonts.rs | 33 ++++++++ src/lib.rs | 23 +++-- 6 files changed, 290 insertions(+), 8 deletions(-) create mode 100644 assets/fonts/AlphaProta-License.txt create mode 100644 assets/fonts/AlphaProta.ttf create mode 100644 assets/fonts/Rainbow2000-License.txt create mode 100644 assets/fonts/Rainbow2000-Regular.ttf create mode 100644 examples/fonts.rs 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 0000000000000000000000000000000000000000..e72962027d02c5bdd4b3cd226d6546d305f0de03 GIT binary patch literal 4124 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 + .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) + )); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 75bb0f0..7431181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,7 @@ use std::any::{Any, TypeId}; +use std::borrow::Cow; use std::sync::Arc; use std::sync::Mutex; @@ -52,13 +53,14 @@ use bevy_render::{ExtractSchedule, RenderApp}; use bevy_utils::HashMap; use bevy_window::{PrimaryWindow, Window}; 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_wgpu; pub use iced_core::renderer::Style as IcedStyle; pub use iced_graphics::Antialiasing as IcedAntialiasing; +pub use iced_wgpu; mod conversions; mod render; @@ -77,6 +79,8 @@ pub struct IcedPlugin { /// /// By default, it is `None`. pub antialiasing: Option, + /// Font file contents + pub fonts: Vec<&'static [u8]>, } impl Default for IcedPlugin { @@ -85,6 +89,7 @@ impl Default for IcedPlugin { default_font: iced::Font::default(), default_text_size: iced_wgpu::Settings::default().default_text_size, antialiasing: None, + fonts: vec![], } } } @@ -106,6 +111,7 @@ impl Plugin for IcedPlugin { self.default_font, self.default_text_size, self.antialiasing, + &self.fonts, ) .into(); @@ -133,6 +139,7 @@ impl IcedProps { 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 @@ -147,15 +154,15 @@ impl IcedProps { antialiasing, ..Default::default() }; - let debug = iced_runtime::Debug::new(); - let clipboard = iced_core::clipboard::Null; + 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, queue, settings, format, - )), - debug, - clipboard, + renderer: iced_wgpu::Renderer::new(backend), + debug: iced_runtime::Debug::new(), + clipboard: iced_core::clipboard::Null, } } } From f2ca2b4b0b65531f15dfffd6f6a260933db8b20e Mon Sep 17 00:00:00 2001 From: Joonas Satka Date: Mon, 31 Jul 2023 13:13:13 +0300 Subject: [PATCH 5/5] Upgrade iced to 0.10 --- Cargo.toml | 12 ++++++------ examples/fonts.rs | 10 +++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1f7b1b..af2c3e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,12 +25,12 @@ bevy_render = "0.11" bevy_utils = "0.11" bevy_window = "0.11" -iced = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } -iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } -iced_graphics = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } -iced_runtime = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } -iced_wgpu = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } -iced_winit = { git = "https://github.com/iced-rs/iced.git", rev = "5dd9234" } +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.11" diff --git a/examples/fonts.rs b/examples/fonts.rs index 534903d..fc478e4 100644 --- a/examples/fonts.rs +++ b/examples/fonts.rs @@ -1,9 +1,13 @@ use bevy::prelude::*; -use bevy_iced::iced::{Font, widget::{text, column}}; +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"); +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"); @@ -30,4 +34,4 @@ fn ui_system(mut ctx: IcedContext) { text(format!("I am the default font")), text(format!("I am another font")).font(RAINBOW2000_FONT) )); -} \ No newline at end of file +}