summaryrefslogtreecommitdiffstats
path: root/examples/screenshot
diff options
context:
space:
mode:
authorLibravatar Bingus <shankern@protonmail.com>2023-03-25 10:45:39 -0700
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-06-06 15:37:30 +0200
commit233196eb14b40f8bd5201ea0262571f82136ad53 (patch)
tree410cf71cda98bdcb55ecc384f7fbc2f4ef669edd /examples/screenshot
parentc15f1b5f6575792cc89bb5fba2e613428397e46a (diff)
downloadiced-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.toml11
-rw-r--r--examples/screenshot/src/main.rs305
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);