diff options
author | 2023-03-25 10:45:39 -0700 | |
---|---|---|
committer | 2023-06-06 15:37:30 +0200 | |
commit | 233196eb14b40f8bd5201ea0262571f82136ad53 (patch) | |
tree | 410cf71cda98bdcb55ecc384f7fbc2f4ef669edd /examples/screenshot | |
parent | c15f1b5f6575792cc89bb5fba2e613428397e46a (diff) | |
download | iced-233196eb14b40f8bd5201ea0262571f82136ad53.tar.gz iced-233196eb14b40f8bd5201ea0262571f82136ad53.tar.bz2 iced-233196eb14b40f8bd5201ea0262571f82136ad53.zip |
Added offscreen rendering support for wgpu & tiny-skia exposed with the window::screenshot command.
Diffstat (limited to 'examples/screenshot')
-rw-r--r-- | examples/screenshot/Cargo.toml | 11 | ||||
-rw-r--r-- | examples/screenshot/src/main.rs | 305 |
2 files changed, 316 insertions, 0 deletions
diff --git a/examples/screenshot/Cargo.toml b/examples/screenshot/Cargo.toml new file mode 100644 index 00000000..b79300b7 --- /dev/null +++ b/examples/screenshot/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "screenshot" +version = "0.1.0" +authors = ["Bingus <shankern@protonmail.com>"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = ["debug", "image", "advanced"] } +image = { version = "0.24.6", features = ["png"]} +env_logger = "0.10.0" diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs new file mode 100644 index 00000000..29d961d9 --- /dev/null +++ b/examples/screenshot/src/main.rs @@ -0,0 +1,305 @@ +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<Screenshot>, + saved_png_path: Option<Result<String, PngError>>, + png_saving: bool, + crop_error: Option<CropError>, + 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<String, PngError>), + 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<Self::Message>) { + ( + 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<Self::Message> { + 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::<u32>() { + self.x_input_value = value; + } + } + Message::YInputChanged(new) => { + if let Ok(value) = new.parse::<u32>() { + self.y_input_value = value; + } + } + Message::WidthInputChanged(new) => { + if let Ok(value) = new.parse::<u32>() { + self.width_input_value = value; + } + } + Message::HeightInputChanged(new) => { + if let Ok(value) = new.parse::<u32>() { + self.height_input_value = value; + } + } + Message::Crop => { + if let Some(screenshot) = &self.screenshot { + let cropped = screenshot.crop(Rectangle::<u32> { + 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<Self::Theme>> { + let image: Element<Message> = 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<Self::Message> { + 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<String, PngError> { + 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); |