diff options
author | 2023-07-12 12:23:18 -0700 | |
---|---|---|
committer | 2023-07-12 12:23:18 -0700 | |
commit | 633f405f3f78bc7f82d2b2061491b0e011137451 (patch) | |
tree | 5ebfc1f45d216a5c14a90492563599e6969eab4d /examples/screenshot/src/main.rs | |
parent | 41836dd80d0534608e7aedfbf2319c540a23de1a (diff) | |
parent | 21bd51426d900e271206f314e0c915dd41065521 (diff) | |
download | iced-633f405f3f78bc7f82d2b2061491b0e011137451.tar.gz iced-633f405f3f78bc7f82d2b2061491b0e011137451.tar.bz2 iced-633f405f3f78bc7f82d2b2061491b0e011137451.zip |
Merge remote-tracking branch 'origin/master' into feat/multi-window-support
# Conflicts:
# Cargo.toml
# core/src/window/icon.rs
# core/src/window/id.rs
# core/src/window/position.rs
# core/src/window/settings.rs
# examples/integration/src/main.rs
# examples/integration_opengl/src/main.rs
# glutin/src/application.rs
# native/src/subscription.rs
# native/src/window.rs
# runtime/src/window/action.rs
# src/lib.rs
# src/window.rs
# winit/Cargo.toml
# winit/src/application.rs
# winit/src/icon.rs
# winit/src/settings.rs
# winit/src/window.rs
Diffstat (limited to 'examples/screenshot/src/main.rs')
-rw-r--r-- | examples/screenshot/src/main.rs | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs new file mode 100644 index 00000000..83824535 --- /dev/null +++ b/examples/screenshot/src/main.rs @@ -0,0 +1,320 @@ +use iced::alignment; +use iced::keyboard::KeyCode; +use iced::theme::{Button, Container}; +use iced::widget::{button, column, container, image, row, text, text_input}; +use iced::window::screenshot::{self, Screenshot}; +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<screenshot::CropError>, + x_input_value: Option<u32>, + y_input_value: Option<u32>, + width_input_value: Option<u32>, + height_input_value: Option<u32>, +} + +#[derive(Clone, Debug)] +enum Message { + Crop, + Screenshot, + ScreenshotData(Screenshot), + Png, + PngSaved(Result<String, PngError>), + XInputChanged(Option<u32>), + YInputChanged(Option<u32>), + WidthInputChanged(Option<u32>), + HeightInputChanged(Option<u32>), +} + +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: None, + y_input_value: None, + width_input_value: None, + height_input_value: None, + }, + 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 { + self.png_saving = true; + + return Command::perform( + save_to_png(screenshot.clone()), + Message::PngSaved, + ); + } + } + Message::PngSaved(res) => { + self.png_saving = false; + self.saved_png_path = Some(res); + } + Message::XInputChanged(new_value) => { + self.x_input_value = new_value; + } + Message::YInputChanged(new_value) => { + self.y_input_value = new_value; + } + Message::WidthInputChanged(new_value) => { + self.width_input_value = new_value; + } + Message::HeightInputChanged(new_value) => { + self.height_input_value = new_value; + } + Message::Crop => { + if let Some(screenshot) = &self.screenshot { + let cropped = screenshot.crop(Rectangle::<u32> { + x: self.x_input_value.unwrap_or(0), + y: self.y_input_value.unwrap_or(0), + width: self.width_input_value.unwrap_or(0), + height: self.height_input_value.unwrap_or(0), + }); + + 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 + { + image(image::Handle::from_pixels( + screenshot.size.width, + screenshot.size.height, + screenshot.clone(), + )) + .content_fit(ContentFit::Contain) + .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::Box) + .width(Length::FillPortion(2)) + .height(Length::Fill) + .center_x() + .center_y(); + + let crop_origin_controls = row![ + text("X:") + .vertical_alignment(alignment::Vertical::Center) + .width(30), + numeric_input("0", self.x_input_value).map(Message::XInputChanged), + text("Y:") + .vertical_alignment(alignment::Vertical::Center) + .width(30), + numeric_input("0", self.y_input_value).map(Message::YInputChanged) + ] + .spacing(10) + .align_items(Alignment::Center); + + let crop_dimension_controls = row![ + text("W:") + .vertical_alignment(alignment::Vertical::Center) + .width(30), + numeric_input("0", self.width_input_value) + .map(Message::WidthInputChanged), + text("H:") + .vertical_alignment(alignment::Vertical::Center) + .width(30), + numeric_input("0", self.height_input_value) + .map(Message::HeightInputChanged) + ] + .spacing(10) + .align_items(Alignment::Center); + + let mut crop_controls = + column![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 mut controls = column![ + column![ + button(centered_text("Screenshot!")) + .padding([10, 20, 10, 20]) + .width(Length::Fill) + .on_press(Message::Screenshot), + if !self.png_saving { + button(centered_text("Save as png")).on_press_maybe( + self.screenshot.is_some().then(|| Message::Png), + ) + } else { + button(centered_text("Saving...")).style(Button::Secondary) + } + .style(Button::Secondary) + .padding([10, 20, 10, 20]) + .width(Length::Fill) + ] + .spacing(10), + column![ + crop_controls, + button(centered_text("Crop")) + .on_press(Message::Crop) + .style(Button::Destructive) + .padding([10, 20, 10, 20]) + .width(Length::Fill), + ] + .spacing(10) + .align_items(Alignment::Center), + ] + .spacing(40); + + 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(alignment::Horizontal::Center) + .width(Length::FillPortion(1)) + .height(Length::Fill) + .center_y() + .center_x(); + + let content = row![side_content, image] + .spacing(10) + .width(Length::Fill) + .height(Length::Fill) + .align_items(Alignment::Center); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .padding(10) + .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 + } + }) + } +} + +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); + +fn numeric_input( + placeholder: &str, + value: Option<u32>, +) -> Element<'_, Option<u32>> { + text_input( + placeholder, + &value + .as_ref() + .map(ToString::to_string) + .unwrap_or_else(String::new), + ) + .on_input(move |text| { + if text.is_empty() { + None + } else if let Ok(new_value) = text.parse() { + Some(new_value) + } else { + value + } + }) + .width(40) + .into() +} + +fn centered_text(content: &str) -> Element<'_, Message> { + text(content) + .width(Length::Fill) + .horizontal_alignment(alignment::Horizontal::Center) + .into() +} |