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 { if self.spin { window::frames().map(Message::RedrawRequested) } else { Subscription::none() } } fn view(&self) -> Element { 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>, value: String, ) -> Element<'a, Message> { column![control.into(), text(value).size(12).line_height(1.0)] .spacing(2) .align_items(Alignment::Center) .into() }