use iced::alignment::{Horizontal, Vertical}; use iced::keyboard::KeyCode; use iced::theme::{Button, Container}; use iced::widget::runtime::{CropError, Screenshot}; use iced::widget::{ button, column as col, container, image as iced_image, row, text, text_input, }; use iced::{ event, executor, keyboard, subscription, Alignment, Application, Command, ContentFit, Element, Event, Length, Rectangle, Renderer, Subscription, Theme, }; use image as img; use image::ColorType; fn main() -> iced::Result { env_logger::builder().format_timestamp(None).init(); Example::run(iced::Settings::default()) } struct Example { screenshot: Option, saved_png_path: Option>, png_saving: bool, crop_error: Option, x_input_value: u32, y_input_value: u32, width_input_value: u32, height_input_value: u32, } #[derive(Clone, Debug)] enum Message { Crop, Screenshot, ScreenshotData(Screenshot), Png, PngSaved(Result), XInputChanged(String), YInputChanged(String), WidthInputChanged(String), HeightInputChanged(String), } impl Application for Example { type Executor = executor::Default; type Message = Message; type Theme = Theme; type Flags = (); fn new(_flags: Self::Flags) -> (Self, Command) { ( Example { screenshot: None, saved_png_path: None, png_saving: false, crop_error: None, x_input_value: 0, y_input_value: 0, width_input_value: 0, height_input_value: 0, }, Command::none(), ) } fn title(&self) -> String { "Screenshot".to_string() } fn update(&mut self, message: Self::Message) -> Command { match message { Message::Screenshot => { return iced::window::screenshot(Message::ScreenshotData); } Message::ScreenshotData(screenshot) => { self.screenshot = Some(screenshot); } Message::Png => { if let Some(screenshot) = &self.screenshot { return Command::perform( save_to_png(screenshot.clone()), Message::PngSaved, ); } self.png_saving = true; } Message::PngSaved(res) => { self.png_saving = false; self.saved_png_path = Some(res); } Message::XInputChanged(new) => { if let Ok(value) = new.parse::() { self.x_input_value = value; } } Message::YInputChanged(new) => { if let Ok(value) = new.parse::() { self.y_input_value = value; } } Message::WidthInputChanged(new) => { if let Ok(value) = new.parse::() { self.width_input_value = value; } } Message::HeightInputChanged(new) => { if let Ok(value) = new.parse::() { self.height_input_value = value; } } Message::Crop => { if let Some(screenshot) = &self.screenshot { let cropped = screenshot.crop(Rectangle:: { x: self.x_input_value, y: self.y_input_value, width: self.width_input_value, height: self.height_input_value, }); match cropped { Ok(screenshot) => { self.screenshot = Some(screenshot); self.crop_error = None; } Err(crop_error) => { self.crop_error = Some(crop_error); } } } } } Command::none() } fn view(&self) -> Element<'_, Self::Message, Renderer> { let image: Element = if let Some(screenshot) = &self.screenshot { iced_image(iced_image::Handle::from_pixels( screenshot.size.width, screenshot.size.height, screenshot.bytes.clone(), )) .content_fit(ContentFit::ScaleDown) .width(Length::Fill) .height(Length::Fill) .into() } else { text("Press the button to take a screenshot!").into() }; let image = container(image) .padding(10) .style(Container::Custom(Box::new(ScreenshotDisplayContainer))) .width(Length::FillPortion(2)) .height(Length::Fill) .center_x() .center_y(); let crop_origin_controls = row![ text("X:").vertical_alignment(Vertical::Center).width(14), text_input("0", &format!("{}", self.x_input_value),) .on_input(Message::XInputChanged) .width(40), text("Y:").vertical_alignment(Vertical::Center).width(14), text_input("0", &format!("{}", self.y_input_value),) .on_input(Message::YInputChanged) .width(40), ] .spacing(10) .align_items(Alignment::Center); let crop_dimension_controls = row![ text("W:").vertical_alignment(Vertical::Center).width(14), text_input("0", &format!("{}", self.width_input_value),) .on_input(Message::WidthInputChanged) .width(40), text("H:").vertical_alignment(Vertical::Center).width(14), text_input("0", &format!("{}", self.height_input_value),) .on_input(Message::HeightInputChanged) .width(40), ] .spacing(10) .align_items(Alignment::Center); let mut crop_controls = col![crop_origin_controls, crop_dimension_controls] .spacing(10) .align_items(Alignment::Center); if let Some(crop_error) = &self.crop_error { crop_controls = crop_controls .push(text(format!("Crop error! \n{}", crop_error))); } let png_button = if !self.png_saving { button("Save to png.") .style(Button::Secondary) .padding([10, 20, 10, 20]) .on_press(Message::Png) } else { button("Saving..") .style(Button::Secondary) .padding([10, 20, 10, 20]) }; let mut controls = col![ button("Screenshot!") .padding([10, 20, 10, 20]) .on_press(Message::Screenshot), button("Crop") .style(Button::Destructive) .padding([10, 20, 10, 20]) .on_press(Message::Crop), crop_controls, png_button, ] .spacing(40) .align_items(Alignment::Center); if let Some(png_result) = &self.saved_png_path { let msg = match png_result { Ok(path) => format!("Png saved as: {:?}!", path), Err(msg) => { format!("Png could not be saved due to:\n{:?}", msg) } }; controls = controls.push(text(msg)); } let side_content = container(controls) .align_x(Horizontal::Center) .width(Length::FillPortion(1)) .height(Length::Fill) .center_y() .center_x(); let content = row![side_content, image] .width(Length::Fill) .height(Length::Fill) .align_items(Alignment::Center); container(content) .padding(10) .width(Length::Fill) .height(Length::Fill) .center_x() .center_y() .into() } fn subscription(&self) -> Subscription { subscription::events_with(|event, status| { if let event::Status::Captured = status { return None; } if let Event::Keyboard(keyboard::Event::KeyPressed { key_code: KeyCode::F5, .. }) = event { Some(Message::Screenshot) } else { None } }) } } struct ScreenshotDisplayContainer; impl container::StyleSheet for ScreenshotDisplayContainer { type Style = Theme; fn appearance(&self, style: &Self::Style) -> container::Appearance { container::Appearance { text_color: None, background: None, border_radius: 5.0, border_width: 4.0, border_color: style.palette().primary, } } } async fn save_to_png(screenshot: Screenshot) -> Result { let path = "screenshot.png".to_string(); img::save_buffer( &path, &screenshot.bytes, screenshot.size.width, screenshot.size.height, ColorType::Rgba8, ) .map(|_| path) .map_err(|err| PngError(format!("{:?}", err))) } #[derive(Clone, Debug)] struct PngError(String);