Merge branch 'master' into feat/multi-window-support
This commit is contained in:
commit
e09b4e24dd
331 changed files with 12085 additions and 3976 deletions
|
|
@ -1,24 +1,22 @@
|
|||
[package]
|
||||
name = "iced_runtime"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "A renderer-agnostic library for native GUIs"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/iced-rs/iced"
|
||||
description = "A renderer-agnostic runtime for iced"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
categories.workspace = true
|
||||
keywords.workspace = true
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
multi-window = []
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1"
|
||||
iced_core.workspace = true
|
||||
iced_futures.workspace = true
|
||||
iced_futures.features = ["thread-pool"]
|
||||
|
||||
[dependencies.iced_core]
|
||||
version = "0.9"
|
||||
path = "../core"
|
||||
|
||||
[dependencies.iced_futures]
|
||||
version = "0.6"
|
||||
path = "../futures"
|
||||
features = ["thread-pool"]
|
||||
thiserror.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# `iced_runtime`
|
||||
[][documentation]
|
||||
[](https://crates.io/crates/iced_native)
|
||||
[](https://github.com/iced-rs/iced/blob/master/LICENSE)
|
||||
[][documentation]
|
||||
[](https://crates.io/crates/iced_runtime)
|
||||
[](https://github.com/iced-rs/iced/blob/master/LICENSE)
|
||||
[](https://discord.gg/3xZJ65GAhd)
|
||||
|
||||
`iced_runtime` takes [`iced_core`] and builds a native runtime on top of it.
|
||||
`iced_runtime` takes [`iced_core`] and builds a runtime on top of it.
|
||||
|
||||
[documentation]: https://docs.rs/iced_native
|
||||
[documentation]: https://docs.rs/iced_runtime
|
||||
[`iced_core`]: ../core
|
||||
[`iced_winit`]: ../winit
|
||||
[`druid`]: https://github.com/xi-editor/druid
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ mod action;
|
|||
pub use action::Action;
|
||||
|
||||
use crate::core::widget;
|
||||
use crate::futures::futures;
|
||||
use crate::futures::MaybeSend;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::Stream;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
|
||||
|
|
@ -40,14 +43,24 @@ impl<T> Command<T> {
|
|||
|
||||
/// Creates a [`Command`] that performs the action of the given future.
|
||||
pub fn perform<A>(
|
||||
future: impl Future<Output = T> + 'static + MaybeSend,
|
||||
f: impl FnOnce(T) -> A + 'static + MaybeSend,
|
||||
) -> Command<A> {
|
||||
use iced_futures::futures::FutureExt;
|
||||
future: impl Future<Output = A> + 'static + MaybeSend,
|
||||
f: impl FnOnce(A) -> T + 'static + MaybeSend,
|
||||
) -> Command<T> {
|
||||
use futures::FutureExt;
|
||||
|
||||
Command::single(Action::Future(Box::pin(future.map(f))))
|
||||
}
|
||||
|
||||
/// Creates a [`Command`] that runs the given stream to completion.
|
||||
pub fn run<A>(
|
||||
stream: impl Stream<Item = A> + 'static + MaybeSend,
|
||||
f: impl Fn(A) -> T + 'static + MaybeSend,
|
||||
) -> Command<T> {
|
||||
use futures::StreamExt;
|
||||
|
||||
Command::single(Action::Stream(Box::pin(stream.map(f))))
|
||||
}
|
||||
|
||||
/// Creates a [`Command`] that performs the actions of all the given
|
||||
/// commands.
|
||||
///
|
||||
|
|
@ -106,3 +119,23 @@ impl<T> fmt::Debug for Command<T> {
|
|||
command.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`Command`] that produces the `Message`s published from a [`Future`]
|
||||
/// to an [`mpsc::Sender`] with the given bounds.
|
||||
pub fn channel<Fut, Message>(
|
||||
size: usize,
|
||||
f: impl FnOnce(mpsc::Sender<Message>) -> Fut + MaybeSend + 'static,
|
||||
) -> Command<Message>
|
||||
where
|
||||
Fut: Future<Output = ()> + MaybeSend + 'static,
|
||||
Message: 'static + MaybeSend,
|
||||
{
|
||||
use futures::future;
|
||||
use futures::stream::{self, StreamExt};
|
||||
|
||||
let (sender, receiver) = mpsc::channel(size);
|
||||
|
||||
let runner = stream::once(f(sender)).filter_map(|_| future::ready(None));
|
||||
|
||||
Command::single(Action::Stream(Box::pin(stream::select(receiver, runner))))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ pub enum Action<T> {
|
|||
/// [`Future`]: iced_futures::BoxFuture
|
||||
Future(iced_futures::BoxFuture<T>),
|
||||
|
||||
/// Run a [`Stream`] to completion.
|
||||
///
|
||||
/// [`Stream`]: iced_futures::BoxStream
|
||||
Stream(iced_futures::BoxStream<T>),
|
||||
|
||||
/// Run a clipboard action.
|
||||
Clipboard(clipboard::Action<T>),
|
||||
|
||||
|
|
@ -52,10 +57,11 @@ impl<T> Action<T> {
|
|||
A: 'static,
|
||||
T: 'static,
|
||||
{
|
||||
use iced_futures::futures::FutureExt;
|
||||
use iced_futures::futures::{FutureExt, StreamExt};
|
||||
|
||||
match self {
|
||||
Self::Future(future) => Action::Future(Box::pin(future.map(f))),
|
||||
Self::Stream(stream) => Action::Stream(Box::pin(stream.map(f))),
|
||||
Self::Clipboard(action) => Action::Clipboard(action.map(f)),
|
||||
Self::Window(id, window) => Action::Window(id, window.map(f)),
|
||||
Self::System(system) => Action::System(system.map(f)),
|
||||
|
|
@ -74,6 +80,7 @@ impl<T> fmt::Debug for Action<T> {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Future(_) => write!(f, "Action::Future"),
|
||||
Self::Stream(_) => write!(f, "Action::Stream"),
|
||||
Self::Clipboard(action) => {
|
||||
write!(f, "Action::Clipboard({action:?})")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn startup_finished(&mut self) {
|
||||
self.startup_duration = time::Instant::now() - self.startup_start;
|
||||
self.startup_duration = self.startup_start.elapsed();
|
||||
}
|
||||
|
||||
pub fn update_started(&mut self) {
|
||||
|
|
@ -83,8 +83,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn update_finished(&mut self) {
|
||||
self.update_durations
|
||||
.push(time::Instant::now() - self.update_start);
|
||||
self.update_durations.push(self.update_start.elapsed());
|
||||
}
|
||||
|
||||
pub fn view_started(&mut self) {
|
||||
|
|
@ -92,8 +91,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn view_finished(&mut self) {
|
||||
self.view_durations
|
||||
.push(time::Instant::now() - self.view_start);
|
||||
self.view_durations.push(self.view_start.elapsed());
|
||||
}
|
||||
|
||||
pub fn layout_started(&mut self) {
|
||||
|
|
@ -101,8 +99,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn layout_finished(&mut self) {
|
||||
self.layout_durations
|
||||
.push(time::Instant::now() - self.layout_start);
|
||||
self.layout_durations.push(self.layout_start.elapsed());
|
||||
}
|
||||
|
||||
pub fn event_processing_started(&mut self) {
|
||||
|
|
@ -110,8 +107,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn event_processing_finished(&mut self) {
|
||||
self.event_durations
|
||||
.push(time::Instant::now() - self.event_start);
|
||||
self.event_durations.push(self.event_start.elapsed());
|
||||
}
|
||||
|
||||
pub fn draw_started(&mut self) {
|
||||
|
|
@ -119,8 +115,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn draw_finished(&mut self) {
|
||||
self.draw_durations
|
||||
.push(time::Instant::now() - self.draw_start);
|
||||
self.draw_durations.push(self.draw_start.elapsed());
|
||||
}
|
||||
|
||||
pub fn render_started(&mut self) {
|
||||
|
|
@ -128,8 +123,7 @@ impl Debug {
|
|||
}
|
||||
|
||||
pub fn render_finished(&mut self) {
|
||||
self.render_durations
|
||||
.push(time::Instant::now() - self.render_start);
|
||||
self.render_durations.push(self.render_start.elapsed());
|
||||
}
|
||||
|
||||
pub fn log_message<Message: std::fmt::Debug>(&mut self, message: &Message) {
|
||||
|
|
|
|||
|
|
@ -2,46 +2,19 @@
|
|||
//!
|
||||
//! 
|
||||
//!
|
||||
//! `iced_native` takes [`iced_core`] and builds a native runtime on top of it,
|
||||
//! featuring:
|
||||
//! `iced_runtime` takes [`iced_core`] and builds a native runtime on top of it.
|
||||
//!
|
||||
//! - A custom layout engine, greatly inspired by [`druid`]
|
||||
//! - Event handling for all the built-in widgets
|
||||
//! - A renderer-agnostic API
|
||||
//!
|
||||
//! To achieve this, it introduces a couple of reusable interfaces:
|
||||
//!
|
||||
//! - A [`Widget`] trait, which is used to implement new widgets: from layout
|
||||
//! requirements to event and drawing logic.
|
||||
//! - A bunch of `Renderer` traits, meant to keep the crate renderer-agnostic.
|
||||
//!
|
||||
//! # Usage
|
||||
//! The strategy to use this crate depends on your particular use case. If you
|
||||
//! want to:
|
||||
//! - Implement a custom shell or integrate it in your own system, check out the
|
||||
//! [`UserInterface`] type.
|
||||
//! - Build a new renderer, see the [renderer] module.
|
||||
//! - Build a custom widget, start at the [`Widget`] trait.
|
||||
//!
|
||||
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.9/core
|
||||
//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.9/winit
|
||||
//! [`druid`]: https://github.com/xi-editor/druid
|
||||
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
|
||||
//! [renderer]: crate::renderer
|
||||
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.10/core
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
|
||||
)]
|
||||
#![forbid(unsafe_code, rust_2018_idioms)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
unused_results,
|
||||
clippy::extra_unused_lifetimes,
|
||||
clippy::from_over_into,
|
||||
clippy::needless_borrow,
|
||||
clippy::new_without_default,
|
||||
clippy::useless_conversion
|
||||
rustdoc::broken_intra_doc_links
|
||||
)]
|
||||
#![forbid(unsafe_code, rust_2018_idioms)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
pub mod clipboard;
|
||||
pub mod command;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ use crate::core::mouse;
|
|||
use crate::core::overlay;
|
||||
use crate::core::renderer;
|
||||
use crate::core::widget;
|
||||
use crate::core::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size};
|
||||
use crate::core::{
|
||||
Clipboard, Event, Layout, Point, Rectangle, Shell, Size, Vector,
|
||||
};
|
||||
|
||||
/// An [`Overlay`] container that displays nested overlays
|
||||
/// An overlay container that displays nested overlays
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Nested<'a, Message, Renderer> {
|
||||
overlay: overlay::Element<'a, Message, Renderer>,
|
||||
|
|
@ -27,23 +29,24 @@ where
|
|||
}
|
||||
|
||||
/// Returns the layout [`Node`] of the [`Nested`] overlay.
|
||||
///
|
||||
/// [`Node`]: layout::Node
|
||||
pub fn layout(
|
||||
&mut self,
|
||||
renderer: &Renderer,
|
||||
bounds: Size,
|
||||
position: Point,
|
||||
_position: Point,
|
||||
translation: Vector,
|
||||
) -> layout::Node {
|
||||
fn recurse<Message, Renderer>(
|
||||
element: &mut overlay::Element<'_, Message, Renderer>,
|
||||
renderer: &Renderer,
|
||||
bounds: Size,
|
||||
position: Point,
|
||||
translation: Vector,
|
||||
) -> layout::Node
|
||||
where
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
let translation = position - Point::ORIGIN;
|
||||
|
||||
let node = element.layout(renderer, bounds, translation);
|
||||
|
||||
if let Some(mut nested) =
|
||||
|
|
@ -53,7 +56,7 @@ where
|
|||
node.size(),
|
||||
vec![
|
||||
node,
|
||||
recurse(&mut nested, renderer, bounds, position),
|
||||
recurse(&mut nested, renderer, bounds, translation),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
|
|
@ -61,7 +64,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
recurse(&mut self.overlay, renderer, bounds, position)
|
||||
recurse(&mut self.overlay, renderer, bounds, translation)
|
||||
}
|
||||
|
||||
/// Draws the [`Nested`] overlay using the associated `Renderer`.
|
||||
|
|
@ -162,7 +165,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
recurse(&mut self.overlay, layout, renderer, operation)
|
||||
recurse(&mut self.overlay, layout, renderer, operation);
|
||||
}
|
||||
|
||||
/// Processes a runtime [`Event`].
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ where
|
|||
(uncaptured_events, command)
|
||||
}
|
||||
|
||||
/// Applies [`widget::Operation`]s to the [`State`]
|
||||
/// Applies [`Operation`]s to the [`State`]
|
||||
pub fn operate(
|
||||
&mut self,
|
||||
renderer: &mut P::Renderer,
|
||||
|
|
@ -200,7 +200,7 @@ where
|
|||
match operation.finish() {
|
||||
operation::Outcome::None => {}
|
||||
operation::Outcome::Some(message) => {
|
||||
self.queued_messages.push(message)
|
||||
self.queued_messages.push(message);
|
||||
}
|
||||
operation::Outcome::Chain(next) => {
|
||||
current_operation = Some(next);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use crate::core::mouse;
|
|||
use crate::core::renderer;
|
||||
use crate::core::widget;
|
||||
use crate::core::window;
|
||||
use crate::core::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
|
||||
use crate::core::{
|
||||
Clipboard, Element, Layout, Point, Rectangle, Shell, Size, Vector,
|
||||
};
|
||||
use crate::overlay;
|
||||
|
||||
/// A set of interactive graphical elements with a specific [`Layout`].
|
||||
|
|
@ -19,7 +21,7 @@ use crate::overlay;
|
|||
/// The [`integration`] example uses a [`UserInterface`] to integrate Iced in an
|
||||
/// existing graphical application.
|
||||
///
|
||||
/// [`integration`]: https://github.com/iced-rs/iced/tree/0.9/examples/integration
|
||||
/// [`integration`]: https://github.com/iced-rs/iced/tree/0.10/examples/integration
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct UserInterface<'a, Message, Renderer> {
|
||||
root: Element<'a, Message, Renderer>,
|
||||
|
|
@ -95,8 +97,11 @@ where
|
|||
let Cache { mut state } = cache;
|
||||
state.diff(root.as_widget());
|
||||
|
||||
let base =
|
||||
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds));
|
||||
let base = root.as_widget().layout(
|
||||
&mut state,
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, bounds),
|
||||
);
|
||||
|
||||
UserInterface {
|
||||
root,
|
||||
|
|
@ -196,7 +201,8 @@ where
|
|||
let bounds = self.bounds;
|
||||
|
||||
let mut overlay = manual_overlay.as_mut().unwrap();
|
||||
let mut layout = overlay.layout(renderer, bounds, Point::ORIGIN);
|
||||
let mut layout =
|
||||
overlay.layout(renderer, bounds, Point::ORIGIN, Vector::ZERO);
|
||||
let mut event_statuses = Vec::new();
|
||||
|
||||
for event in events.iter().cloned() {
|
||||
|
|
@ -226,8 +232,9 @@ where
|
|||
if shell.is_layout_invalid() {
|
||||
let _ = ManuallyDrop::into_inner(manual_overlay);
|
||||
|
||||
self.base = renderer.layout(
|
||||
&self.root,
|
||||
self.base = self.root.as_widget().layout(
|
||||
&mut self.state,
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, self.bounds),
|
||||
);
|
||||
|
||||
|
|
@ -249,8 +256,12 @@ where
|
|||
overlay = manual_overlay.as_mut().unwrap();
|
||||
|
||||
shell.revalidate_layout(|| {
|
||||
layout =
|
||||
overlay.layout(renderer, bounds, Point::ORIGIN);
|
||||
layout = overlay.layout(
|
||||
renderer,
|
||||
bounds,
|
||||
Point::ORIGIN,
|
||||
Vector::ZERO,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -284,12 +295,14 @@ where
|
|||
(cursor, vec![event::Status::Ignored; events.len()])
|
||||
};
|
||||
|
||||
let viewport = Rectangle::with_size(self.bounds);
|
||||
|
||||
let _ = ManuallyDrop::into_inner(manual_overlay);
|
||||
|
||||
let event_statuses = events
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(overlay_statuses.into_iter())
|
||||
.zip(overlay_statuses)
|
||||
.map(|(event, overlay_status)| {
|
||||
if matches!(overlay_status, event::Status::Captured) {
|
||||
return overlay_status;
|
||||
|
|
@ -305,6 +318,7 @@ where
|
|||
renderer,
|
||||
clipboard,
|
||||
&mut shell,
|
||||
&viewport,
|
||||
);
|
||||
|
||||
if matches!(event_status, event::Status::Captured) {
|
||||
|
|
@ -322,8 +336,9 @@ where
|
|||
}
|
||||
|
||||
shell.revalidate_layout(|| {
|
||||
self.base = renderer.layout(
|
||||
&self.root,
|
||||
self.base = self.root.as_widget().layout(
|
||||
&mut self.state,
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, self.bounds),
|
||||
);
|
||||
|
||||
|
|
@ -353,7 +368,7 @@ where
|
|||
/// It returns the current [`mouse::Interaction`]. You should update the
|
||||
/// icon of the mouse cursor accordingly in your system.
|
||||
///
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
/// [`Renderer`]: crate::core::Renderer
|
||||
///
|
||||
/// # Example
|
||||
/// We can finally draw our [counter](index.html#usage) by
|
||||
|
|
@ -440,7 +455,12 @@ where
|
|||
.map(overlay::Nested::new)
|
||||
{
|
||||
let overlay_layout = self.overlay.take().unwrap_or_else(|| {
|
||||
overlay.layout(renderer, self.bounds, Point::ORIGIN)
|
||||
overlay.layout(
|
||||
renderer,
|
||||
self.bounds,
|
||||
Point::ORIGIN,
|
||||
Vector::ZERO,
|
||||
)
|
||||
});
|
||||
|
||||
let cursor = if cursor
|
||||
|
|
@ -510,17 +530,13 @@ where
|
|||
renderer,
|
||||
);
|
||||
|
||||
let overlay_bounds = layout.bounds();
|
||||
|
||||
renderer.with_layer(overlay_bounds, |renderer| {
|
||||
overlay.draw(
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
Layout::new(layout),
|
||||
cursor,
|
||||
);
|
||||
});
|
||||
overlay.draw(
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
Layout::new(layout),
|
||||
cursor,
|
||||
);
|
||||
|
||||
if cursor
|
||||
.position()
|
||||
|
|
@ -562,8 +578,12 @@ where
|
|||
.map(overlay::Nested::new)
|
||||
{
|
||||
if self.overlay.is_none() {
|
||||
self.overlay =
|
||||
Some(overlay.layout(renderer, self.bounds, Point::ORIGIN));
|
||||
self.overlay = Some(overlay.layout(
|
||||
renderer,
|
||||
self.bounds,
|
||||
Point::ORIGIN,
|
||||
Vector::ZERO,
|
||||
));
|
||||
}
|
||||
|
||||
overlay.operate(
|
||||
|
|
@ -620,7 +640,7 @@ pub enum State {
|
|||
/// The [`UserInterface`] is up-to-date and can be reused without
|
||||
/// rebuilding.
|
||||
Updated {
|
||||
/// The [`Instant`] when a redraw should be performed.
|
||||
/// The [`window::RedrawRequest`] when a redraw should be performed.
|
||||
redraw_request: Option<window::RedrawRequest>,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ use crate::command::{self, Command};
|
|||
use crate::core::time::Instant;
|
||||
use crate::core::window::{self, Event, Icon, Level, Mode, UserAttention};
|
||||
use crate::core::Size;
|
||||
use crate::futures::subscription::{self, Subscription};
|
||||
use crate::futures::event;
|
||||
use crate::futures::Subscription;
|
||||
|
||||
/// Subscribes to the frames of the window of the running application.
|
||||
///
|
||||
|
|
@ -22,8 +23,8 @@ use crate::futures::subscription::{self, Subscription};
|
|||
/// In any case, this [`Subscription`] is useful to smoothly draw application-driven
|
||||
/// animations without missing any frames.
|
||||
pub fn frames() -> Subscription<Instant> {
|
||||
subscription::raw_events(|event, _status| match event {
|
||||
iced_core::Event::Window(_, Event::RedrawRequested(at)) => Some(at),
|
||||
event::listen_raw(|event, _status| match event {
|
||||
crate::core::Event::Window(_, Event::RedrawRequested(at)) => Some(at),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::sync::Arc;
|
|||
|
||||
/// Data of a screenshot, captured with `window::screenshot()`.
|
||||
///
|
||||
/// The `bytes` of this screenshot will always be ordered as `RGBA` in the sRGB color space.
|
||||
/// The `bytes` of this screenshot will always be ordered as `RGBA` in the `sRGB` color space.
|
||||
#[derive(Clone)]
|
||||
pub struct Screenshot {
|
||||
/// The bytes of the [`Screenshot`].
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue