diff options
Diffstat (limited to 'examples')
37 files changed, 646 insertions, 584 deletions
diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index 38949336..bec4a954 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -1,5 +1,5 @@ -use iced::widget::{checkbox, column, container, row, text}; -use iced::{Element, Font, Length}; +use iced::widget::{center, checkbox, column, row, text}; +use iced::{Element, Font}; const ICON_FONT: Font = Font::with_name("icons"); @@ -68,11 +68,6 @@ impl Example { let content = column![default_checkbox, checkboxes, custom_checkbox].spacing(20); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } } diff --git a/examples/combo_box/src/main.rs b/examples/combo_box/src/main.rs index 2feb4522..ff759ab4 100644 --- a/examples/combo_box/src/main.rs +++ b/examples/combo_box/src/main.rs @@ -1,5 +1,5 @@ use iced::widget::{ - column, combo_box, container, scrollable, text, vertical_space, + center, column, combo_box, scrollable, text, vertical_space, }; use iced::{Alignment, Element, Length}; @@ -68,12 +68,7 @@ impl Example { .align_items(Alignment::Center) .spacing(10); - container(scrollable(content)) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(scrollable(content)).into() } } diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index b2c71b3f..5625f12a 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -1,5 +1,5 @@ -use iced::widget::container; -use iced::{Element, Length}; +use iced::widget::center; +use iced::Element; use numeric_input::numeric_input; @@ -27,10 +27,8 @@ impl Component { } fn view(&self) -> Element<Message> { - container(numeric_input(self.value, Message::NumericInputChanged)) + center(numeric_input(self.value, Message::NumericInputChanged)) .padding(20) - .height(Length::Fill) - .center_y() .into() } } diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index c093e240..b3eee218 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -81,8 +81,8 @@ mod quad { } } -use iced::widget::{column, container, slider, text}; -use iced::{Alignment, Color, Element, Length, Shadow, Vector}; +use iced::widget::{center, column, slider, text}; +use iced::{Alignment, Color, Element, Shadow, Vector}; pub fn main() -> iced::Result { iced::run("Custom Quad - Iced", Example::update, Example::view) @@ -187,12 +187,7 @@ impl Example { .max_width(500) .align_items(Alignment::Center); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } } diff --git a/examples/custom_shader/src/main.rs b/examples/custom_shader/src/main.rs index aa3dafe9..463b2df9 100644 --- a/examples/custom_shader/src/main.rs +++ b/examples/custom_shader/src/main.rs @@ -4,7 +4,7 @@ use scene::Scene; use iced::time::Instant; use iced::widget::shader::wgpu; -use iced::widget::{checkbox, column, container, row, shader, slider, text}; +use iced::widget::{center, checkbox, column, row, shader, slider, text}; use iced::window; use iced::{Alignment, Color, Element, Length, Subscription}; @@ -123,12 +123,7 @@ impl IcedCubes { let shader = shader(&self.scene).width(Length::Fill).height(Length::Fill); - container(column![shader, controls].align_items(Alignment::Center)) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(column![shader, controls].align_items(Alignment::Center)).into() } fn subscription(&self) -> Subscription<Message> { diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index aa49ebd0..261dcb81 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -82,8 +82,8 @@ mod circle { } use circle::circle; -use iced::widget::{column, container, slider, text}; -use iced::{Alignment, Element, Length}; +use iced::widget::{center, column, slider, text}; +use iced::{Alignment, Element}; pub fn main() -> iced::Result { iced::run("Custom Widget - Iced", Example::update, Example::view) @@ -122,12 +122,7 @@ impl Example { .max_width(500) .align_items(Alignment::Center); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } } diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 9f4769e0..e031ac44 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -1,7 +1,7 @@ mod download; -use iced::widget::{button, column, container, progress_bar, text, Column}; -use iced::{Alignment, Element, Length, Subscription}; +use iced::widget::{button, center, column, progress_bar, text, Column}; +use iced::{Alignment, Element, Subscription}; pub fn main() -> iced::Result { iced::program("Download Progress - Iced", Example::update, Example::view) @@ -67,13 +67,7 @@ impl Example { .spacing(20) .align_items(Alignment::End); - container(downloads) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .padding(20) - .into() + center(downloads).padding(20).into() } } diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index ed16018a..c20a7d67 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -277,7 +277,7 @@ fn action<'a, Message: Clone + 'a>( label: &'a str, on_press: Option<Message>, ) -> Element<'a, Message> { - let action = button(container(content).width(30).center_x()); + let action = button(container(content).center_x(30)); if let Some(on_press) = on_press { tooltip( diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index bf568c94..999ce8ef 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -1,6 +1,6 @@ use iced::alignment; use iced::event::{self, Event}; -use iced::widget::{button, checkbox, container, text, Column}; +use iced::widget::{button, center, checkbox, text, Column}; use iced::window; use iced::{Alignment, Command, Element, Length, Subscription}; @@ -84,11 +84,6 @@ impl Events { .push(toggle) .push(exit); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } } diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs index 7bed272d..2de97e20 100644 --- a/examples/exit/src/main.rs +++ b/examples/exit/src/main.rs @@ -1,6 +1,6 @@ -use iced::widget::{button, column, container}; +use iced::widget::{button, center, column}; use iced::window; -use iced::{Alignment, Command, Element, Length}; +use iced::{Alignment, Command, Element}; pub fn main() -> iced::Result { iced::program("Exit - Iced", Exit::update, Exit::view).run() @@ -46,12 +46,6 @@ impl Exit { .spacing(10) .align_items(Alignment::Center); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .padding(20) - .center_x() - .center_y() - .into() + center(content).padding(20).into() } } diff --git a/examples/ferris/Cargo.toml b/examples/ferris/Cargo.toml new file mode 100644 index 00000000..e98341d2 --- /dev/null +++ b/examples/ferris/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ferris" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2021" +publish = false + +[dependencies] +iced.workspace = true +iced.features = ["image", "tokio", "debug"] diff --git a/examples/ferris/src/main.rs b/examples/ferris/src/main.rs new file mode 100644 index 00000000..0400c376 --- /dev/null +++ b/examples/ferris/src/main.rs @@ -0,0 +1,211 @@ +use iced::time::Instant; +use iced::widget::{ + center, checkbox, column, container, image, pick_list, row, slider, text, +}; +use iced::window; +use iced::{ + Alignment, Color, ContentFit, Degrees, Element, Length, Radians, Rotation, + Subscription, Theme, +}; + +pub fn main() -> iced::Result { + iced::program("Ferris - Iced", Image::update, Image::view) + .subscription(Image::subscription) + .theme(|_| Theme::TokyoNight) + .run() +} + +struct Image { + width: f32, + opacity: f32, + rotation: Rotation, + content_fit: ContentFit, + spin: bool, + last_tick: Instant, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + WidthChanged(f32), + OpacityChanged(f32), + RotationStrategyChanged(RotationStrategy), + RotationChanged(Degrees), + ContentFitChanged(ContentFit), + SpinToggled(bool), + RedrawRequested(Instant), +} + +impl Image { + fn update(&mut self, message: Message) { + match message { + Message::WidthChanged(width) => { + self.width = width; + } + Message::OpacityChanged(opacity) => { + self.opacity = opacity; + } + Message::RotationStrategyChanged(strategy) => { + self.rotation = match strategy { + RotationStrategy::Floating => { + Rotation::Floating(self.rotation.radians()) + } + RotationStrategy::Solid => { + Rotation::Solid(self.rotation.radians()) + } + }; + } + Message::RotationChanged(rotation) => { + self.rotation = match self.rotation { + Rotation::Floating(_) => { + Rotation::Floating(rotation.into()) + } + Rotation::Solid(_) => Rotation::Solid(rotation.into()), + }; + } + Message::ContentFitChanged(content_fit) => { + self.content_fit = content_fit; + } + Message::SpinToggled(spin) => { + self.spin = spin; + self.last_tick = Instant::now(); + } + Message::RedrawRequested(now) => { + const ROTATION_SPEED: Degrees = Degrees(360.0); + + let delta = (now - self.last_tick).as_millis() as f32 / 1_000.0; + + *self.rotation.radians_mut() = (self.rotation.radians() + + ROTATION_SPEED * delta) + % (2.0 * Radians::PI); + + self.last_tick = now; + } + } + } + + fn subscription(&self) -> Subscription<Message> { + if self.spin { + window::frames().map(Message::RedrawRequested) + } else { + Subscription::none() + } + } + + fn view(&self) -> Element<Message> { + let i_am_ferris = column![ + "Hello!", + Element::from( + image(format!( + "{}/../tour/images/ferris.png", + env!("CARGO_MANIFEST_DIR") + )) + .width(self.width) + .content_fit(self.content_fit) + .rotation(self.rotation) + .opacity(self.opacity) + ) + .explain(Color::WHITE), + "I am Ferris!" + ] + .spacing(20) + .align_items(Alignment::Center); + + let fit = row![ + pick_list( + [ + ContentFit::Contain, + ContentFit::Cover, + ContentFit::Fill, + ContentFit::None, + ContentFit::ScaleDown + ], + Some(self.content_fit), + Message::ContentFitChanged + ) + .width(Length::Fill), + pick_list( + [RotationStrategy::Floating, RotationStrategy::Solid], + Some(match self.rotation { + Rotation::Floating(_) => RotationStrategy::Floating, + Rotation::Solid(_) => RotationStrategy::Solid, + }), + Message::RotationStrategyChanged, + ) + .width(Length::Fill), + ] + .spacing(10) + .align_items(Alignment::End); + + let properties = row![ + with_value( + slider(100.0..=500.0, self.width, Message::WidthChanged), + format!("Width: {}px", self.width) + ), + with_value( + slider(0.0..=1.0, self.opacity, Message::OpacityChanged) + .step(0.01), + format!("Opacity: {:.2}", self.opacity) + ), + with_value( + row![ + slider( + Degrees::RANGE, + self.rotation.degrees(), + Message::RotationChanged + ), + checkbox("Spin!", self.spin) + .text_size(12) + .on_toggle(Message::SpinToggled) + .size(12) + ] + .spacing(10) + .align_items(Alignment::Center), + format!("Rotation: {:.0}°", f32::from(self.rotation.degrees())) + ) + ] + .spacing(10) + .align_items(Alignment::End); + + container(column![fit, center(i_am_ferris), properties].spacing(10)) + .padding(10) + .into() + } +} + +impl Default for Image { + fn default() -> Self { + Self { + width: 300.0, + opacity: 1.0, + rotation: Rotation::default(), + content_fit: ContentFit::default(), + spin: false, + last_tick: Instant::now(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum RotationStrategy { + Floating, + Solid, +} + +impl std::fmt::Display for RotationStrategy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Floating => "Floating", + Self::Solid => "Solid", + }) + } +} + +fn with_value<'a>( + control: impl Into<Element<'a, Message>>, + value: String, +) -> Element<'a, Message> { + column![control.into(), text(value).size(12).line_height(1.0)] + .spacing(2) + .align_items(Alignment::Center) + .into() +} diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 31b8a4ce..3c7969c5 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -176,12 +176,7 @@ fn view(_state: &()) -> Element<'_, ()> { .spacing(20) .max_width(500); - let scrollable = - scrollable(container(content).width(Length::Fill).center_x()); - - container(scrollable) - .width(Length::Fill) - .height(Length::Fill) - .center_y() - .into() + let scrollable = scrollable(container(content).center_x(Length::Fill)); + + container(scrollable).center_y(Length::Fill).into() } diff --git a/examples/integration/README.md b/examples/integration/README.md index aa3a6e94..bac0640c 100644 --- a/examples/integration/README.md +++ b/examples/integration/README.md @@ -10,25 +10,8 @@ The __[`main`]__ file contains all the code of the example. You can run it with `cargo run`: ``` -cargo run --package integration_wgpu +cargo run --package integration ``` -### How to run this example with WebGL backend -NOTE: Currently, WebGL backend is still experimental, so expect bugs. - -```sh -# 0. Install prerequisites -cargo install wasm-bindgen-cli https -# 1. cd to the current folder -# 2. Compile wasm module -cargo build -p integration_wgpu --target wasm32-unknown-unknown -# 3. Invoke wasm-bindgen -wasm-bindgen ../../target/wasm32-unknown-unknown/debug/integration_wgpu.wasm --out-dir . --target web --no-typescript -# 4. run http server -http -# 5. Open 127.0.0.1:8000 in browser -``` - - [`main`]: src/main.rs [`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration/index.html b/examples/integration/index.html deleted file mode 100644 index 920bc4a0..00000000 --- a/examples/integration/index.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> - <title>Iced - wgpu + WebGL integration</title> - </head> - <body> - <h1>integration_wgpu</h1> - <canvas id="iced_canvas"></canvas> - <script type="module"> - import init from "./integration.js"; - init('./integration_bg.wasm'); - </script> - <style> - body { - width: 100%; - text-align: center; - } - </style> - </body> -</html> diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index c4b57ecf..e1c7d62f 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -18,305 +18,342 @@ use iced_winit::winit; use iced_winit::Clipboard; use winit::{ - event::{Event, WindowEvent}, + event::WindowEvent, event_loop::{ControlFlow, EventLoop}, keyboard::ModifiersState, }; use std::sync::Arc; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::JsCast; -#[cfg(target_arch = "wasm32")] -use web_sys::HtmlCanvasElement; -#[cfg(target_arch = "wasm32")] -use winit::platform::web::WindowBuilderExtWebSys; - -pub fn main() -> Result<(), Box<dyn std::error::Error>> { - #[cfg(target_arch = "wasm32")] - let canvas_element = { - console_log::init().expect("Initialize logger"); - - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok()) - .expect("Get canvas element") - }; - - #[cfg(not(target_arch = "wasm32"))] +pub fn main() -> Result<(), winit::error::EventLoopError> { tracing_subscriber::fmt::init(); // Initialize winit let event_loop = EventLoop::new()?; - #[cfg(target_arch = "wasm32")] - let window = winit::window::WindowBuilder::new() - .with_canvas(Some(canvas_element)) - .build(&event_loop)?; - - #[cfg(not(target_arch = "wasm32"))] - let window = winit::window::Window::new(&event_loop)?; - - let window = Arc::new(window); - - let physical_size = window.inner_size(); - let mut viewport = Viewport::with_physical_size( - Size::new(physical_size.width, physical_size.height), - window.scale_factor(), - ); - let mut cursor_position = None; - let mut modifiers = ModifiersState::default(); - let mut clipboard = Clipboard::connect(&window); - - // Initialize wgpu - #[cfg(target_arch = "wasm32")] - let default_backend = wgpu::Backends::GL; - #[cfg(not(target_arch = "wasm32"))] - let default_backend = wgpu::Backends::PRIMARY; - - let backend = - wgpu::util::backend_bits_from_env().unwrap_or(default_backend); - - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: backend, - ..Default::default() - }); - let surface = instance.create_surface(window.clone())?; - - let (format, adapter, device, queue) = - futures::futures::executor::block_on(async { - let adapter = wgpu::util::initialize_adapter_from_env_or_default( - &instance, - Some(&surface), - ) - .await - .expect("Create adapter"); - - let adapter_features = adapter.features(); - - #[cfg(target_arch = "wasm32")] - let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()); - - #[cfg(not(target_arch = "wasm32"))] - let needed_limits = wgpu::Limits::default(); - - let capabilities = surface.get_capabilities(&adapter); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: adapter_features - & wgpu::Features::default(), - required_limits: needed_limits, + #[allow(clippy::large_enum_variant)] + enum Runner { + Loading, + Ready { + window: Arc<winit::window::Window>, + device: wgpu::Device, + queue: wgpu::Queue, + surface: wgpu::Surface<'static>, + format: wgpu::TextureFormat, + engine: Engine, + renderer: Renderer, + scene: Scene, + state: program::State<Controls>, + cursor_position: Option<winit::dpi::PhysicalPosition<f64>>, + clipboard: Clipboard, + viewport: Viewport, + modifiers: ModifiersState, + resized: bool, + debug: Debug, + }, + } + + impl winit::application::ApplicationHandler for Runner { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if let Self::Loading = self { + let window = Arc::new( + event_loop + .create_window( + winit::window::WindowAttributes::default(), + ) + .expect("Create window"), + ); + + let physical_size = window.inner_size(); + let viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + window.scale_factor(), + ); + let clipboard = Clipboard::connect(&window); + + let backend = + wgpu::util::backend_bits_from_env().unwrap_or_default(); + + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: backend, + ..Default::default() + }); + let surface = instance + .create_surface(window.clone()) + .expect("Create window surface"); + + let (format, adapter, device, queue) = + futures::futures::executor::block_on(async { + let adapter = + wgpu::util::initialize_adapter_from_env_or_default( + &instance, + Some(&surface), + ) + .await + .expect("Create adapter"); + + let adapter_features = adapter.features(); + + let capabilities = surface.get_capabilities(&adapter); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: adapter_features + & wgpu::Features::default(), + required_limits: wgpu::Limits::default(), + }, + None, + ) + .await + .expect("Request device"); + + ( + capabilities + .formats + .iter() + .copied() + .find(wgpu::TextureFormat::is_srgb) + .or_else(|| { + capabilities.formats.first().copied() + }) + .expect("Get preferred format"), + adapter, + device, + queue, + ) + }); + + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format, + width: physical_size.width, + height: physical_size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], + desired_maximum_frame_latency: 2, }, - None, - ) - .await - .expect("Request device"); - - ( - capabilities - .formats - .iter() - .copied() - .find(wgpu::TextureFormat::is_srgb) - .or_else(|| capabilities.formats.first().copied()) - .expect("Get preferred format"), - adapter, + ); + + // Initialize scene and GUI controls + let scene = Scene::new(&device, format); + let controls = Controls::new(); + + // Initialize iced + let mut debug = Debug::new(); + let engine = + Engine::new(&adapter, &device, &queue, format, None); + let mut renderer = Renderer::new( + &device, + &engine, + Font::default(), + Pixels::from(16), + ); + + let state = program::State::new( + controls, + viewport.logical_size(), + &mut renderer, + &mut debug, + ); + + // You should change this if you want to render continuously + event_loop.set_control_flow(ControlFlow::Wait); + + *self = Self::Ready { + window, + device, + queue, + surface, + format, + engine, + renderer, + scene, + state, + cursor_position: None, + modifiers: ModifiersState::default(), + clipboard, + viewport, + resized: false, + debug, + }; + } + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let Self::Ready { + window, device, queue, - ) - }); - - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format, - width: physical_size.width, - height: physical_size.height, - present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }, - ); - - let mut resized = false; - - // Initialize scene and GUI controls - let scene = Scene::new(&device, format); - let controls = Controls::new(); - - // Initialize iced - let mut debug = Debug::new(); - let mut engine = Engine::new(&adapter, &device, &queue, format, None); - let mut renderer = - Renderer::new(&engine, Font::default(), Pixels::from(16)); - - let mut state = program::State::new( - controls, - viewport.logical_size(), - &mut renderer, - &mut debug, - ); - - // Run event loop - event_loop.run(move |event, window_target| { - // You should change this if you want to render continuously - window_target.set_control_flow(ControlFlow::Wait); - - match event { - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - if resized { - let size = window.inner_size(); - - viewport = Viewport::with_physical_size( - Size::new(size.width, size.height), - window.scale_factor(), - ); - - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }, - ); - - resized = false; - } - - match surface.get_current_texture() { - Ok(frame) => { - let mut encoder = device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { label: None }, + surface, + format, + engine, + renderer, + scene, + state, + viewport, + cursor_position, + modifiers, + clipboard, + resized, + debug, + } = self + else { + return; + }; + + match event { + WindowEvent::RedrawRequested => { + if *resized { + let size = window.inner_size(); + + *viewport = Viewport::with_physical_size( + Size::new(size.width, size.height), + window.scale_factor(), ); - let program = state.program(); - - let view = frame.texture.create_view( - &wgpu::TextureViewDescriptor::default(), + surface.configure( + device, + &wgpu::SurfaceConfiguration { + format: *format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], + desired_maximum_frame_latency: 2, + }, ); - { - // We clear the frame - let mut render_pass = Scene::clear( - &view, - &mut encoder, - program.background_color(), + *resized = false; + } + + match surface.get_current_texture() { + Ok(frame) => { + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { label: None }, ); - // Draw the scene - scene.draw(&mut render_pass); - } + let program = state.program(); - // And then iced on top - renderer.present( - &mut engine, - &device, - &queue, - &mut encoder, - None, - frame.texture.format(), - &view, - &viewport, - &debug.overlay(), - ); + let view = frame.texture.create_view( + &wgpu::TextureViewDescriptor::default(), + ); + + { + // We clear the frame + let mut render_pass = Scene::clear( + &view, + &mut encoder, + program.background_color(), + ); + + // Draw the scene + scene.draw(&mut render_pass); + } + + // And then iced on top + renderer.present( + engine, + device, + queue, + &mut encoder, + None, + frame.texture.format(), + &view, + viewport, + &debug.overlay(), + ); - // Then we submit the work - queue.submit(Some(encoder.finish())); - frame.present(); + // Then we submit the work + engine.submit(queue, encoder); + frame.present(); - // Update the mouse cursor - window.set_cursor_icon( - iced_winit::conversion::mouse_interaction( - state.mouse_interaction(), - ), - ); - } - Err(error) => match error { - wgpu::SurfaceError::OutOfMemory => { - panic!( - "Swapchain error: {error}. \ - Rendering cannot continue." - ) - } - _ => { - // Try rendering again next frame. - window.request_redraw(); + // Update the mouse cursor + window.set_cursor( + iced_winit::conversion::mouse_interaction( + state.mouse_interaction(), + ), + ); } - }, - } - } - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CursorMoved { position, .. } => { - cursor_position = Some(position); - } - WindowEvent::ModifiersChanged(new_modifiers) => { - modifiers = new_modifiers.state(); - } - WindowEvent::Resized(_) => { - resized = true; - } - WindowEvent::CloseRequested => { - window_target.exit(); + Err(error) => match error { + wgpu::SurfaceError::OutOfMemory => { + panic!( + "Swapchain error: {error}. \ + Rendering cannot continue." + ) + } + _ => { + // Try rendering again next frame. + window.request_redraw(); + } + }, } - _ => {} } - - // Map window event to iced event - if let Some(event) = iced_winit::conversion::window_event( - window::Id::MAIN, - event, - window.scale_factor(), - modifiers, - ) { - state.queue_event(event); + WindowEvent::CursorMoved { position, .. } => { + *cursor_position = Some(position); + } + WindowEvent::ModifiersChanged(new_modifiers) => { + *modifiers = new_modifiers.state(); } + WindowEvent::Resized(_) => { + *resized = true; + } + WindowEvent::CloseRequested => { + event_loop.exit(); + } + _ => {} } - _ => {} - } - // If there are events pending - if !state.is_queue_empty() { - // We update iced - let _ = state.update( - viewport.logical_size(), - cursor_position - .map(|p| { - conversion::cursor_position(p, viewport.scale_factor()) - }) - .map(mouse::Cursor::Available) - .unwrap_or(mouse::Cursor::Unavailable), - &mut renderer, - &Theme::Dark, - &renderer::Style { - text_color: Color::WHITE, - }, - &mut clipboard, - &mut debug, - ); - - // and request a redraw - window.request_redraw(); + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + window::Id::MAIN, + event, + window.scale_factor(), + *modifiers, + ) { + state.queue_event(event); + } + + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + cursor_position + .map(|p| { + conversion::cursor_position( + p, + viewport.scale_factor(), + ) + }) + .map(mouse::Cursor::Available) + .unwrap_or(mouse::Cursor::Unavailable), + renderer, + &Theme::Dark, + &renderer::Style { + text_color: Color::WHITE, + }, + clipboard, + debug, + ); + + // and request a redraw + window.request_redraw(); + } } - })?; + } - Ok(()) + let mut runner = Runner::Loading; + event_loop.run_app(&mut runner) } diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index 66d79091..c40ac820 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -1,8 +1,8 @@ use iced::keyboard; use iced::mouse; use iced::widget::{ - button, canvas, checkbox, column, container, horizontal_space, pick_list, - row, scrollable, text, + button, canvas, center, checkbox, column, container, horizontal_space, + pick_list, row, scrollable, text, }; use iced::{ color, Alignment, Element, Font, Length, Point, Rectangle, Renderer, @@ -76,7 +76,7 @@ impl Layout { .spacing(20) .align_items(Alignment::Center); - let example = container(if self.explain { + let example = center(if self.explain { self.example.view().explain(color!(0x0000ff)) } else { self.example.view() @@ -87,11 +87,7 @@ impl Layout { container::Style::default() .with_border(palette.background.strong.color, 4.0) }) - .padding(4) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y(); + .padding(4); let controls = row([ (!self.example.is_first()).then_some( @@ -195,12 +191,7 @@ impl Default for Example { } fn centered<'a>() -> Element<'a, Message> { - container(text("I am centered!").size(50)) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(text("I am centered!").size(50)).into() } fn column_<'a>() -> Element<'a, Message> { @@ -260,8 +251,7 @@ fn application<'a>() -> Element<'a, Message> { .align_items(Alignment::Center), ) .style(container::rounded_box) - .height(Length::Fill) - .center_y(); + .center_y(Length::Fill); let content = container( scrollable( diff --git a/examples/loading_spinners/src/main.rs b/examples/loading_spinners/src/main.rs index eaa4d57e..e8d67ab5 100644 --- a/examples/loading_spinners/src/main.rs +++ b/examples/loading_spinners/src/main.rs @@ -1,5 +1,5 @@ -use iced::widget::{column, container, row, slider, text}; -use iced::{Element, Length}; +use iced::widget::{center, column, row, slider, text}; +use iced::Element; use std::time::Duration; @@ -73,7 +73,7 @@ impl LoadingSpinners { }) .spacing(20); - container( + center( column.push( row![ text("Cycle duration:"), @@ -87,10 +87,6 @@ impl LoadingSpinners { .spacing(20.0), ), ) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() .into() } } diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index 42b7f471..c4d3b449 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -1,5 +1,5 @@ -use iced::widget::{button, column, container, text}; -use iced::{Alignment, Element, Length}; +use iced::widget::{button, center, column, text}; +use iced::{Alignment, Element}; use loupe::loupe; @@ -31,7 +31,7 @@ impl Loupe { } fn view(&self) -> Element<Message> { - container(loupe( + center(loupe( 3.0, column![ button("Increment").on_press(Message::Increment), @@ -41,10 +41,6 @@ impl Loupe { .padding(20) .align_items(Alignment::Center), )) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() .into() } } diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index a345924d..a012c310 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -2,8 +2,8 @@ use iced::event::{self, Event}; use iced::keyboard; use iced::keyboard::key; use iced::widget::{ - self, button, column, container, horizontal_space, mouse_area, opaque, - pick_list, row, stack, text, text_input, + self, button, center, column, container, horizontal_space, mouse_area, + opaque, pick_list, row, stack, text, text_input, }; use iced::{Alignment, Color, Command, Element, Length, Subscription}; @@ -98,13 +98,7 @@ impl App { row![text("Top Left"), horizontal_space(), text("Top Right")] .align_items(Alignment::Start) .height(Length::Fill), - container( - button(text("Show Modal")).on_press(Message::ShowModal) - ) - .center_x() - .center_y() - .width(Length::Fill) - .height(Length::Fill), + center(button(text("Show Modal")).on_press(Message::ShowModal)), row![ text("Bottom Left"), horizontal_space(), @@ -115,9 +109,7 @@ impl App { ] .height(Length::Fill), ) - .padding(10) - .width(Length::Fill) - .height(Length::Fill); + .padding(10); if self.show_modal { let signup = container( @@ -210,25 +202,18 @@ where { stack![ base.into(), - mouse_area( - container(opaque(content)) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .style(|_theme| { - container::Style { - background: Some( - Color { - a: 0.8, - ..Color::BLACK - } - .into(), - ), - ..container::Style::default() + mouse_area(center(opaque(content)).style(|_theme| { + container::Style { + background: Some( + Color { + a: 0.8, + ..Color::BLACK } - }) - ) + .into(), + ), + ..container::Style::default() + } + })) .on_press(on_blur) ] .into() diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index 5a5e70c1..31c2e4f6 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -1,7 +1,9 @@ use iced::event; use iced::executor; use iced::multi_window::{self, Application}; -use iced::widget::{button, column, container, scrollable, text, text_input}; +use iced::widget::{ + button, center, column, container, scrollable, text, text_input, +}; use iced::window; use iced::{ Alignment, Command, Element, Length, Point, Settings, Subscription, Theme, @@ -128,12 +130,7 @@ impl multi_window::Application for Example { fn view(&self, window: window::Id) -> Element<Message> { let content = self.windows.get(&window).unwrap().view(window); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } fn theme(&self, window: window::Id) -> Self::Theme { @@ -210,6 +207,6 @@ impl Window { .align_items(Alignment::Center), ); - container(content).width(200).center_x().into() + container(content).center_x(200).into() } } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 17112785..0def1c2b 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -292,10 +292,8 @@ fn view_content<'a>( .align_items(Alignment::Center); container(scrollable(content)) - .width(Length::Fill) - .height(Length::Fill) + .center_y(Length::Fill) .padding(5) - .center_y() .into() } diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 35f23236..be20094d 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -1,5 +1,5 @@ use iced::futures; -use iced::widget::{self, column, container, image, row, text}; +use iced::widget::{self, center, column, image, row, text}; use iced::{Alignment, Command, Element, Length}; pub fn main() -> iced::Result { @@ -83,12 +83,7 @@ impl Pokedex { .align_items(Alignment::End), }; - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } } diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index b93adf04..c6a90458 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -1,7 +1,5 @@ -use iced::widget::{ - column, container, pick_list, qr_code, row, text, text_input, -}; -use iced::{Alignment, Element, Length, Theme}; +use iced::widget::{center, column, pick_list, qr_code, row, text, text_input}; +use iced::{Alignment, Element, Theme}; pub fn main() -> iced::Result { iced::program( @@ -72,13 +70,7 @@ impl QRGenerator { .spacing(20) .align_items(Alignment::Center); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .padding(20) - .center_x() - .center_y() - .into() + center(content).padding(20).into() } fn theme(&self) -> Theme { diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 5c175ccc..bd670322 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -123,12 +123,9 @@ impl Example { }; let image = container(image) + .center_y(Length::FillPortion(2)) .padding(10) - .style(container::rounded_box) - .width(Length::FillPortion(2)) - .height(Length::Fill) - .center_x() - .center_y(); + .style(container::rounded_box); let crop_origin_controls = row![ text("X:") @@ -213,12 +210,7 @@ impl Example { .spacing(40) }; - let side_content = container(controls) - .align_x(alignment::Horizontal::Center) - .width(Length::FillPortion(1)) - .height(Length::Fill) - .center_y() - .center_x(); + let side_content = container(controls).center_y(Length::Fill); let content = row![side_content, image] .spacing(10) @@ -226,13 +218,7 @@ impl Example { .height(Length::Fill) .align_items(Alignment::Center); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .padding(10) - .center_x() - .center_y() - .into() + container(content).padding(10).into() } fn subscription(&self) -> Subscription<Message> { diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index c02e754b..bbb6497f 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -327,7 +327,7 @@ impl ScrollableDemo { .spacing(10) .into(); - container(content).padding(20).center_x().center_y().into() + container(content).padding(20).into() } fn theme(&self) -> Theme { diff --git a/examples/slider/src/main.rs b/examples/slider/src/main.rs index b3a47614..0b4c29aa 100644 --- a/examples/slider/src/main.rs +++ b/examples/slider/src/main.rs @@ -1,4 +1,4 @@ -use iced::widget::{column, container, slider, text, vertical_slider}; +use iced::widget::{center, column, container, slider, text, vertical_slider}; use iced::{Element, Length}; pub fn main() -> iced::Result { @@ -54,18 +54,14 @@ impl Slider { let text = text(self.value); - container( + center( column![ - container(v_slider).width(Length::Fill).center_x(), - container(h_slider).width(Length::Fill).center_x(), - container(text).width(Length::Fill).center_x(), + container(v_slider).center_x(Length::Fill), + container(h_slider).center_x(Length::Fill), + container(text).center_x(Length::Fill) ] .spacing(25), ) - .height(Length::Fill) - .width(Length::Fill) - .center_x() - .center_y() .into() } } diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs index b9eb19cf..bbe9d0ff 100644 --- a/examples/stopwatch/src/main.rs +++ b/examples/stopwatch/src/main.rs @@ -1,8 +1,8 @@ use iced::alignment; use iced::keyboard; use iced::time; -use iced::widget::{button, column, container, row, text}; -use iced::{Alignment, Element, Length, Subscription, Theme}; +use iced::widget::{button, center, column, row, text}; +use iced::{Alignment, Element, Subscription, Theme}; use std::time::{Duration, Instant}; @@ -128,12 +128,7 @@ impl Stopwatch { .align_items(Alignment::Center) .spacing(20); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } fn theme(&self) -> Theme { diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 73268da0..57e8f47e 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -1,7 +1,7 @@ use iced::widget::{ - button, checkbox, column, container, horizontal_rule, pick_list, - progress_bar, row, scrollable, slider, text, text_input, toggler, - vertical_rule, vertical_space, + button, center, checkbox, column, horizontal_rule, pick_list, progress_bar, + row, scrollable, slider, text, text_input, toggler, vertical_rule, + vertical_space, }; use iced::{Alignment, Element, Length, Theme}; @@ -106,12 +106,7 @@ impl Styling { .padding(20) .max_width(600); - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content).into() } fn theme(&self) -> Theme { diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index 0dcf9bc1..e071c3af 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -1,4 +1,4 @@ -use iced::widget::{checkbox, column, container, svg}; +use iced::widget::{center, checkbox, column, container, svg}; use iced::{color, Element, Length}; pub fn main() -> iced::Result { @@ -44,19 +44,12 @@ impl Tiger { checkbox("Apply a color filter", self.apply_color_filter) .on_toggle(Message::ToggleColorFilter); - container( - column![ - svg, - container(apply_color_filter).width(Length::Fill).center_x() - ] - .spacing(20) - .height(Length::Fill), + center( + column![svg, container(apply_color_filter).center_x(Length::Fill)] + .spacing(20) + .height(Length::Fill), ) - .width(Length::Fill) - .height(Length::Fill) .padding(20) - .center_x() - .center_y() .into() } } diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs index a6ac27a6..66bc4979 100644 --- a/examples/system_information/src/main.rs +++ b/examples/system_information/src/main.rs @@ -1,5 +1,5 @@ -use iced::widget::{button, column, container, text}; -use iced::{system, Command, Element, Length}; +use iced::widget::{button, center, column, text}; +use iced::{system, Command, Element}; pub fn main() -> iced::Result { iced::program("System Information - Iced", Example::update, Example::view) @@ -136,11 +136,6 @@ impl Example { } }; - container(content) - .center_x() - .center_y() - .width(Length::Fill) - .height(Length::Fill) - .into() + center(content).into() } } diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 9968962c..0fcf08c4 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -2,7 +2,7 @@ use iced::event::{self, Event}; use iced::keyboard; use iced::keyboard::key; use iced::widget::{ - self, button, column, container, pick_list, row, slider, text, text_input, + self, button, center, column, pick_list, row, slider, text, text_input, }; use iced::{Alignment, Command, Element, Length, Subscription}; @@ -102,7 +102,7 @@ impl App { .then_some(Message::Add), ); - let content = container( + let content = center( column![ subtitle( "Title", @@ -146,11 +146,7 @@ impl App { ] .spacing(10) .max_width(200), - ) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y(); + ); toast::Manager::new(content, &self.toasts, Message::Close) .timeout(self.timeout_secs) diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 7768c1d5..e7e05b29 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -1,8 +1,8 @@ use iced::alignment::{self, Alignment}; use iced::keyboard; use iced::widget::{ - self, button, checkbox, column, container, keyed_column, row, scrollable, - text, text_input, Text, + self, button, center, checkbox, column, container, keyed_column, row, + scrollable, text, text_input, Text, }; use iced::window; use iced::{Command, Element, Font, Length, Subscription}; @@ -238,7 +238,10 @@ impl Todos { .spacing(20) .max_width(800); - scrollable(container(content).padding(40).center_x()).into() + scrollable( + container(content).center_x(Length::Fill).padding(40), + ) + .into() } } } @@ -435,19 +438,16 @@ impl Filter { } fn loading_message<'a>() -> Element<'a, Message> { - container( + center( text("Loading...") .horizontal_alignment(alignment::Horizontal::Center) .size(50), ) - .width(Length::Fill) - .height(Length::Fill) - .center_y() .into() } fn empty_message(message: &str) -> Element<'_, Message> { - container( + center( text(message) .width(Length::Fill) .size(25) @@ -455,7 +455,6 @@ fn empty_message(message: &str) -> Element<'_, Message> { .color([0.7, 0.7, 0.7]), ) .height(200) - .center_y() .into() } diff --git a/examples/tooltip/src/main.rs b/examples/tooltip/src/main.rs index b6603068..f48f688a 100644 --- a/examples/tooltip/src/main.rs +++ b/examples/tooltip/src/main.rs @@ -1,6 +1,6 @@ use iced::widget::tooltip::Position; -use iced::widget::{button, container, tooltip}; -use iced::{Element, Length}; +use iced::widget::{button, center, container, tooltip}; +use iced::Element; pub fn main() -> iced::Result { iced::run("Tooltip - Iced", Tooltip::update, Tooltip::view) @@ -43,12 +43,7 @@ impl Tooltip { .gap(10) .style(container::rounded_box); - container(tooltip) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(tooltip).into() } } diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index e3a2ca87..59107258 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -76,11 +76,10 @@ impl Tour { } else { content }) - .width(Length::Fill) - .center_x(), + .center_x(Length::Fill), ); - container(scrollable).height(Length::Fill).center_y().into() + container(scrollable).center_y(Length::Fill).into() } } @@ -670,8 +669,7 @@ fn ferris<'a>( .filter_method(filter_method) .width(width), ) - .width(Length::Fill) - .center_x() + .center_x(Length::Fill) } fn padded_button<Message: Clone>(label: &str) -> Button<'_, Message> { diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs index df705b6c..800a188b 100644 --- a/examples/url_handler/src/main.rs +++ b/examples/url_handler/src/main.rs @@ -1,6 +1,6 @@ use iced::event::{self, Event}; -use iced::widget::{container, text}; -use iced::{Element, Length, Subscription}; +use iced::widget::{center, text}; +use iced::{Element, Subscription}; pub fn main() -> iced::Result { iced::program("URL Handler - Iced", App::update, App::view) @@ -44,11 +44,6 @@ impl App { None => text("No URL received yet!"), }; - container(content.size(48)) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() + center(content.size(48)).into() } } diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index b479fe89..ba1e1029 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -2,7 +2,7 @@ mod echo; use iced::alignment::{self, Alignment}; use iced::widget::{ - button, column, container, row, scrollable, text, text_input, + self, button, center, column, row, scrollable, text, text_input, }; use iced::{color, Command, Element, Length, Subscription}; use once_cell::sync::Lazy; @@ -31,7 +31,10 @@ enum Message { impl WebSocket { fn load() -> Command<Message> { - Command::perform(echo::server::run(), |_| Message::Server) + Command::batch([ + Command::perform(echo::server::run(), |_| Message::Server), + widget::focus_next(), + ]) } fn update(&mut self, message: Message) -> Command<Message> { @@ -85,14 +88,10 @@ impl WebSocket { fn view(&self) -> Element<Message> { let message_log: Element<_> = if self.messages.is_empty() { - container( + center( text("Your messages will appear here...") .color(color!(0x888888)), ) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() .into() } else { scrollable( |