Add image and hash snapshot-based testing to iced_test
This commit is contained in:
parent
8e3636d769
commit
1aeb317f2d
17 changed files with 280 additions and 105 deletions
|
|
@ -166,11 +166,13 @@ num-traits = "0.2"
|
||||||
once_cell = "1.0"
|
once_cell = "1.0"
|
||||||
ouroboros = "0.18"
|
ouroboros = "0.18"
|
||||||
palette = "0.7"
|
palette = "0.7"
|
||||||
|
png = "0.17"
|
||||||
pulldown-cmark = "0.11"
|
pulldown-cmark = "0.11"
|
||||||
qrcode = { version = "0.13", default-features = false }
|
qrcode = { version = "0.13", default-features = false }
|
||||||
raw-window-handle = "0.6"
|
raw-window-handle = "0.6"
|
||||||
resvg = "0.42"
|
resvg = "0.42"
|
||||||
rustc-hash = "2.0"
|
rustc-hash = "2.0"
|
||||||
|
sha2 = "0.10"
|
||||||
smol = "1.0"
|
smol = "1.0"
|
||||||
smol_str = "0.2"
|
smol_str = "0.2"
|
||||||
softbuffer = "0.4"
|
softbuffer = "0.4"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ pub mod palette;
|
||||||
|
|
||||||
pub use palette::Palette;
|
pub use palette::Palette;
|
||||||
|
|
||||||
|
use crate::Color;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -246,3 +248,35 @@ impl fmt::Display for Custom {
|
||||||
write!(f, "{}", self.name)
|
write!(f, "{}", self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The base style of a [`Theme`].
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Style {
|
||||||
|
/// The background [`Color`] of the application.
|
||||||
|
pub background_color: Color,
|
||||||
|
|
||||||
|
/// The default text [`Color`] of the application.
|
||||||
|
pub text_color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The default blank style of a [`Theme`].
|
||||||
|
pub trait Base {
|
||||||
|
/// Returns the default base [`Style`] of a [`Theme`].
|
||||||
|
fn base(&self) -> Style;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Base for Theme {
|
||||||
|
fn base(&self) -> Style {
|
||||||
|
default(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The default [`Style`] of a built-in [`Theme`].
|
||||||
|
pub fn default(theme: &Theme) -> Style {
|
||||||
|
let palette = theme.extended_palette();
|
||||||
|
|
||||||
|
Style {
|
||||||
|
background_color: palette.background.base.color,
|
||||||
|
text_color: palette.background.base.text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//! Build window-based GUI applications.
|
//! Build window-based GUI applications.
|
||||||
pub mod icon;
|
pub mod icon;
|
||||||
|
pub mod screenshot;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
mod event;
|
mod event;
|
||||||
|
|
@ -17,5 +18,6 @@ pub use level::Level;
|
||||||
pub use mode::Mode;
|
pub use mode::Mode;
|
||||||
pub use position::Position;
|
pub use position::Position;
|
||||||
pub use redraw_request::RedrawRequest;
|
pub use redraw_request::RedrawRequest;
|
||||||
|
pub use screenshot::Screenshot;
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
pub use user_attention::UserAttention;
|
pub use user_attention::UserAttention;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! Take screenshots of a window.
|
//! Take screenshots of a window.
|
||||||
use crate::core::{Rectangle, Size};
|
use crate::{Rectangle, Size};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use iced::application;
|
|
||||||
use iced::gradient;
|
use iced::gradient;
|
||||||
|
use iced::theme;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
checkbox, column, container, horizontal_space, row, slider, text,
|
checkbox, column, container, horizontal_space, row, slider, text,
|
||||||
};
|
};
|
||||||
|
|
@ -95,16 +95,14 @@ impl Gradient {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self, theme: &Theme) -> application::Appearance {
|
fn style(&self, theme: &Theme) -> theme::Style {
|
||||||
use application::DefaultStyle;
|
|
||||||
|
|
||||||
if self.transparent {
|
if self.transparent {
|
||||||
application::Appearance {
|
theme::Style {
|
||||||
background_color: Color::TRANSPARENT,
|
background_color: Color::TRANSPARENT,
|
||||||
text_color: theme.palette().text,
|
text_color: theme.palette().text,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Theme::default_style(theme)
|
theme::default(theme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
examples/todos/snapshots/creates_a_new_task.sha256
Normal file
1
examples/todos/snapshots/creates_a_new_task.sha256
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
5047c10c4ea050393b686185b8d03b3c3738b47182f9892fb68df589ec46bd4b
|
||||||
|
|
@ -15,7 +15,7 @@ pub fn main() -> iced::Result {
|
||||||
|
|
||||||
iced::application(Todos::title, Todos::update, Todos::view)
|
iced::application(Todos::title, Todos::update, Todos::view)
|
||||||
.subscription(Todos::subscription)
|
.subscription(Todos::subscription)
|
||||||
.font(include_bytes!("../fonts/icons.ttf").as_slice())
|
.font(Todos::ICON_FONT)
|
||||||
.window_size((500.0, 800.0))
|
.window_size((500.0, 800.0))
|
||||||
.run_with(Todos::new)
|
.run_with(Todos::new)
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +48,8 @@ enum Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Todos {
|
impl Todos {
|
||||||
|
const ICON_FONT: &[u8] = include_bytes!("../fonts/icons.ttf");
|
||||||
|
|
||||||
fn new() -> (Self, Command<Message>) {
|
fn new() -> (Self, Command<Message>) {
|
||||||
(
|
(
|
||||||
Self::Loading,
|
Self::Loading,
|
||||||
|
|
@ -449,11 +451,10 @@ fn empty_message(message: &str) -> Element<'_, Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
const ICONS: Font = Font::with_name("Iced-Todos-Icons");
|
|
||||||
|
|
||||||
fn icon(unicode: char) -> Text<'static> {
|
fn icon(unicode: char) -> Text<'static> {
|
||||||
text(unicode.to_string())
|
text(unicode.to_string())
|
||||||
.font(ICONS)
|
.font(Font::with_name("Iced-Todos-Icons"))
|
||||||
.width(20)
|
.width(20)
|
||||||
.align_x(Center)
|
.align_x(Center)
|
||||||
}
|
}
|
||||||
|
|
@ -594,6 +595,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_creates_a_new_task() -> Result<(), test::Error> {
|
fn it_creates_a_new_task() -> Result<(), test::Error> {
|
||||||
|
test::load_font(Todos::ICON_FONT)?;
|
||||||
|
|
||||||
let (mut todos, _command) = Todos::new();
|
let (mut todos, _command) = Todos::new();
|
||||||
let _command = todos.update(Message::Loaded(Err(LoadError::File)));
|
let _command = todos.update(Message::Loaded(Err(LoadError::File)));
|
||||||
|
|
||||||
|
|
@ -610,6 +613,12 @@ mod tests {
|
||||||
let mut interface = test::interface(todos.view());
|
let mut interface = test::interface(todos.view());
|
||||||
let _ = interface.find(selector::text("Create the universe"))?;
|
let _ = interface.find(selector::text("Create the universe"))?;
|
||||||
|
|
||||||
|
let snapshot = interface.snapshot()?;
|
||||||
|
assert!(
|
||||||
|
snapshot.matches_hash("snapshots/creates_a_new_task")?,
|
||||||
|
"snapshots should match!"
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
//! Build window-based GUI applications.
|
//! Build window-based GUI applications.
|
||||||
pub mod screenshot;
|
|
||||||
|
|
||||||
pub use screenshot::Screenshot;
|
|
||||||
|
|
||||||
use crate::core::time::Instant;
|
use crate::core::time::Instant;
|
||||||
use crate::core::window::{
|
use crate::core::window::{
|
||||||
Event, Icon, Id, Level, Mode, Settings, UserAttention,
|
Event, Icon, Id, Level, Mode, Screenshot, Settings, UserAttention,
|
||||||
};
|
};
|
||||||
use crate::core::{Point, Size};
|
use crate::core::{Point, Size};
|
||||||
use crate::futures::event;
|
use crate::futures::event;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
use crate::program::{self, Program};
|
use crate::program::{self, Program};
|
||||||
|
use crate::theme;
|
||||||
use crate::window;
|
use crate::window;
|
||||||
use crate::{
|
use crate::{
|
||||||
Element, Executor, Font, Result, Settings, Size, Subscription, Task,
|
Element, Executor, Font, Result, Settings, Size, Subscription, Task,
|
||||||
|
|
@ -38,8 +39,6 @@ use crate::{
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub use crate::shell::program::{Appearance, DefaultStyle};
|
|
||||||
|
|
||||||
/// Creates an iced [`Application`] given its title, update, and view logic.
|
/// Creates an iced [`Application`] given its title, update, and view logic.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
|
@ -76,7 +75,7 @@ pub fn application<State, Message, Theme, Renderer>(
|
||||||
where
|
where
|
||||||
State: 'static,
|
State: 'static,
|
||||||
Message: Send + std::fmt::Debug + 'static,
|
Message: Send + std::fmt::Debug + 'static,
|
||||||
Theme: Default + DefaultStyle,
|
Theme: Default + theme::Base,
|
||||||
Renderer: program::Renderer,
|
Renderer: program::Renderer,
|
||||||
{
|
{
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
@ -94,7 +93,7 @@ where
|
||||||
for Instance<State, Message, Theme, Renderer, Update, View>
|
for Instance<State, Message, Theme, Renderer, Update, View>
|
||||||
where
|
where
|
||||||
Message: Send + std::fmt::Debug + 'static,
|
Message: Send + std::fmt::Debug + 'static,
|
||||||
Theme: Default + DefaultStyle,
|
Theme: Default + theme::Base,
|
||||||
Renderer: program::Renderer,
|
Renderer: program::Renderer,
|
||||||
Update: self::Update<State, Message>,
|
Update: self::Update<State, Message>,
|
||||||
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
||||||
|
|
@ -352,7 +351,7 @@ impl<P: Program> Application<P> {
|
||||||
/// Sets the style logic of the [`Application`].
|
/// Sets the style logic of the [`Application`].
|
||||||
pub fn style(
|
pub fn style(
|
||||||
self,
|
self,
|
||||||
f: impl Fn(&P::State, &P::Theme) -> Appearance,
|
f: impl Fn(&P::State, &P::Theme) -> theme::Style,
|
||||||
) -> Application<
|
) -> Application<
|
||||||
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
|
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
|
||||||
> {
|
> {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
//! Create and run daemons that run in the background.
|
//! Create and run daemons that run in the background.
|
||||||
use crate::application;
|
use crate::application;
|
||||||
use crate::program::{self, Program};
|
use crate::program::{self, Program};
|
||||||
|
use crate::theme;
|
||||||
use crate::window;
|
use crate::window;
|
||||||
use crate::{Element, Executor, Font, Result, Settings, Subscription, Task};
|
use crate::{Element, Executor, Font, Result, Settings, Subscription, Task};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub use crate::shell::program::{Appearance, DefaultStyle};
|
|
||||||
|
|
||||||
/// Creates an iced [`Daemon`] given its title, update, and view logic.
|
/// Creates an iced [`Daemon`] given its title, update, and view logic.
|
||||||
///
|
///
|
||||||
/// A [`Daemon`] will not open a window by default, but will run silently
|
/// A [`Daemon`] will not open a window by default, but will run silently
|
||||||
|
|
@ -26,7 +25,7 @@ pub fn daemon<State, Message, Theme, Renderer>(
|
||||||
where
|
where
|
||||||
State: 'static,
|
State: 'static,
|
||||||
Message: Send + std::fmt::Debug + 'static,
|
Message: Send + std::fmt::Debug + 'static,
|
||||||
Theme: Default + DefaultStyle,
|
Theme: Default + theme::Base,
|
||||||
Renderer: program::Renderer,
|
Renderer: program::Renderer,
|
||||||
{
|
{
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
@ -44,7 +43,7 @@ where
|
||||||
for Instance<State, Message, Theme, Renderer, Update, View>
|
for Instance<State, Message, Theme, Renderer, Update, View>
|
||||||
where
|
where
|
||||||
Message: Send + std::fmt::Debug + 'static,
|
Message: Send + std::fmt::Debug + 'static,
|
||||||
Theme: Default + DefaultStyle,
|
Theme: Default + theme::Base,
|
||||||
Renderer: program::Renderer,
|
Renderer: program::Renderer,
|
||||||
Update: application::Update<State, Message>,
|
Update: application::Update<State, Message>,
|
||||||
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
||||||
|
|
@ -201,7 +200,7 @@ impl<P: Program> Daemon<P> {
|
||||||
/// Sets the style logic of the [`Daemon`].
|
/// Sets the style logic of the [`Daemon`].
|
||||||
pub fn style(
|
pub fn style(
|
||||||
self,
|
self,
|
||||||
f: impl Fn(&P::State, &P::Theme) -> Appearance,
|
f: impl Fn(&P::State, &P::Theme) -> theme::Style,
|
||||||
) -> Daemon<
|
) -> Daemon<
|
||||||
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
|
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
|
||||||
> {
|
> {
|
||||||
|
|
|
||||||
|
|
@ -688,7 +688,7 @@ pub fn run<State, Message, Theme, Renderer>(
|
||||||
where
|
where
|
||||||
State: Default + 'static,
|
State: Default + 'static,
|
||||||
Message: std::fmt::Debug + Send + 'static,
|
Message: std::fmt::Debug + Send + 'static,
|
||||||
Theme: Default + program::DefaultStyle + 'static,
|
Theme: Default + theme::Base + 'static,
|
||||||
Renderer: program::Renderer + 'static,
|
Renderer: program::Renderer + 'static,
|
||||||
{
|
{
|
||||||
application(title, update, view).run()
|
application(title, update, view).run()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
use crate::graphics::compositor;
|
use crate::graphics::compositor;
|
||||||
use crate::shell;
|
use crate::shell;
|
||||||
|
use crate::theme;
|
||||||
use crate::window;
|
use crate::window;
|
||||||
use crate::{Element, Executor, Result, Settings, Subscription, Task};
|
use crate::{Element, Executor, Result, Settings, Subscription, Task};
|
||||||
|
|
||||||
pub use crate::shell::program::{Appearance, DefaultStyle};
|
|
||||||
|
|
||||||
/// The internal definition of a [`Program`].
|
/// The internal definition of a [`Program`].
|
||||||
///
|
///
|
||||||
/// You should not need to implement this trait directly. Instead, use the
|
/// You should not need to implement this trait directly. Instead, use the
|
||||||
|
|
@ -19,7 +18,7 @@ pub trait Program: Sized {
|
||||||
type Message: Send + std::fmt::Debug + 'static;
|
type Message: Send + std::fmt::Debug + 'static;
|
||||||
|
|
||||||
/// The theme of the program.
|
/// The theme of the program.
|
||||||
type Theme: Default + DefaultStyle;
|
type Theme: Default + theme::Base;
|
||||||
|
|
||||||
/// The renderer of the program.
|
/// The renderer of the program.
|
||||||
type Renderer: Renderer;
|
type Renderer: Renderer;
|
||||||
|
|
@ -51,11 +50,11 @@ pub trait Program: Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme {
|
fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme {
|
||||||
Self::Theme::default()
|
<Self::Theme as Default>::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance {
|
fn style(&self, _state: &Self::State, theme: &Self::Theme) -> theme::Style {
|
||||||
DefaultStyle::default_style(theme)
|
theme::Base::base(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 {
|
fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 {
|
||||||
|
|
@ -153,7 +152,7 @@ pub trait Program: Sized {
|
||||||
self.program.theme(&self.state, window)
|
self.program.theme(&self.state, window)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self, theme: &Self::Theme) -> Appearance {
|
fn style(&self, theme: &Self::Theme) -> theme::Style {
|
||||||
self.program.style(&self.state, theme)
|
self.program.style(&self.state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,7 +251,7 @@ pub fn with_title<P: Program>(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
theme: &Self::Theme,
|
theme: &Self::Theme,
|
||||||
) -> Appearance {
|
) -> theme::Style {
|
||||||
self.program.style(state, theme)
|
self.program.style(state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,7 +321,7 @@ pub fn with_subscription<P: Program>(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
theme: &Self::Theme,
|
theme: &Self::Theme,
|
||||||
) -> Appearance {
|
) -> theme::Style {
|
||||||
self.program.style(state, theme)
|
self.program.style(state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,7 +394,7 @@ pub fn with_theme<P: Program>(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
theme: &Self::Theme,
|
theme: &Self::Theme,
|
||||||
) -> Appearance {
|
) -> theme::Style {
|
||||||
self.program.style(state, theme)
|
self.program.style(state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -409,7 +408,7 @@ pub fn with_theme<P: Program>(
|
||||||
|
|
||||||
pub fn with_style<P: Program>(
|
pub fn with_style<P: Program>(
|
||||||
program: P,
|
program: P,
|
||||||
f: impl Fn(&P::State, &P::Theme) -> Appearance,
|
f: impl Fn(&P::State, &P::Theme) -> theme::Style,
|
||||||
) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
|
) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
|
||||||
struct WithStyle<P, F> {
|
struct WithStyle<P, F> {
|
||||||
program: P,
|
program: P,
|
||||||
|
|
@ -418,7 +417,7 @@ pub fn with_style<P: Program>(
|
||||||
|
|
||||||
impl<P: Program, F> Program for WithStyle<P, F>
|
impl<P: Program, F> Program for WithStyle<P, F>
|
||||||
where
|
where
|
||||||
F: Fn(&P::State, &P::Theme) -> Appearance,
|
F: Fn(&P::State, &P::Theme) -> theme::Style,
|
||||||
{
|
{
|
||||||
type State = P::State;
|
type State = P::State;
|
||||||
type Message = P::Message;
|
type Message = P::Message;
|
||||||
|
|
@ -430,7 +429,7 @@ pub fn with_style<P: Program>(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
theme: &Self::Theme,
|
theme: &Self::Theme,
|
||||||
) -> Appearance {
|
) -> theme::Style {
|
||||||
(self.style)(state, theme)
|
(self.style)(state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,7 +534,7 @@ pub fn with_scale_factor<P: Program>(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
theme: &Self::Theme,
|
theme: &Self::Theme,
|
||||||
) -> Appearance {
|
) -> theme::Style {
|
||||||
self.program.style(state, theme)
|
self.program.style(state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,7 +608,7 @@ pub fn with_executor<P: Program, E: Executor>(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
theme: &Self::Theme,
|
theme: &Self::Theme,
|
||||||
) -> Appearance {
|
) -> theme::Style {
|
||||||
self.program.style(state, theme)
|
self.program.style(state, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,6 @@ iced_tiny_skia.workspace = true
|
||||||
|
|
||||||
iced_renderer.workspace = true
|
iced_renderer.workspace = true
|
||||||
iced_renderer.features = ["tiny-skia"]
|
iced_renderer.features = ["tiny-skia"]
|
||||||
|
|
||||||
|
png.workspace = true
|
||||||
|
sha2.workspace = true
|
||||||
|
|
|
||||||
167
test/src/lib.rs
167
test/src/lib.rs
|
|
@ -12,15 +12,26 @@ use iced_tiny_skia as tiny_skia;
|
||||||
use crate::core::clipboard;
|
use crate::core::clipboard;
|
||||||
use crate::core::keyboard;
|
use crate::core::keyboard;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
|
use crate::core::theme;
|
||||||
|
use crate::core::time;
|
||||||
use crate::core::widget;
|
use crate::core::widget;
|
||||||
|
use crate::core::window;
|
||||||
use crate::core::{Element, Event, Font, Pixels, Rectangle, Size, SmolStr};
|
use crate::core::{Element, Event, Font, Pixels, Rectangle, Size, SmolStr};
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::runtime::user_interface;
|
use crate::runtime::user_interface;
|
||||||
use crate::runtime::UserInterface;
|
use crate::runtime::UserInterface;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn interface<'a, Message, Theme>(
|
pub fn interface<'a, Message, Theme>(
|
||||||
element: impl Into<Element<'a, Message, Theme, Renderer>>,
|
element: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
) -> Interface<'a, Message, Theme, Renderer> {
|
) -> Interface<'a, Message, Theme, Renderer> {
|
||||||
|
let size = Size::new(512.0, 512.0);
|
||||||
|
|
||||||
let mut renderer = Renderer::Secondary(tiny_skia::Renderer::new(
|
let mut renderer = Renderer::Secondary(tiny_skia::Renderer::new(
|
||||||
Font::default(),
|
Font::default(),
|
||||||
Pixels(16.0),
|
Pixels(16.0),
|
||||||
|
|
@ -28,7 +39,7 @@ pub fn interface<'a, Message, Theme>(
|
||||||
|
|
||||||
let raw = UserInterface::build(
|
let raw = UserInterface::build(
|
||||||
element,
|
element,
|
||||||
Size::new(1024.0, 1024.0),
|
size,
|
||||||
user_interface::Cache::default(),
|
user_interface::Cache::default(),
|
||||||
&mut renderer,
|
&mut renderer,
|
||||||
);
|
);
|
||||||
|
|
@ -36,13 +47,24 @@ pub fn interface<'a, Message, Theme>(
|
||||||
Interface {
|
Interface {
|
||||||
raw,
|
raw,
|
||||||
renderer,
|
renderer,
|
||||||
|
size,
|
||||||
messages: Vec::new(),
|
messages: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_font(font: impl Into<Cow<'static, [u8]>>) -> Result<(), Error> {
|
||||||
|
renderer::graphics::text::font_system()
|
||||||
|
.write()
|
||||||
|
.expect("Write to font system")
|
||||||
|
.load_font(font.into());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Interface<'a, Message, Theme, Renderer> {
|
pub struct Interface<'a, Message, Theme, Renderer> {
|
||||||
raw: UserInterface<'a, Message, Theme, Renderer>,
|
raw: UserInterface<'a, Message, Theme, Renderer>,
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
|
size: Size,
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,9 +72,9 @@ pub struct Target {
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message, Theme, Renderer> Interface<'_, Message, Theme, Renderer>
|
impl<Message, Theme> Interface<'_, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: core::Renderer,
|
Theme: Default + theme::Base,
|
||||||
{
|
{
|
||||||
pub fn find(
|
pub fn find(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -256,11 +278,129 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&mut self) -> Result<Snapshot, Error> {
|
||||||
|
let theme = Theme::default();
|
||||||
|
let base = theme.base();
|
||||||
|
|
||||||
|
let _ = self.raw.update(
|
||||||
|
&[Event::Window(window::Event::RedrawRequested(
|
||||||
|
time::Instant::now(),
|
||||||
|
))],
|
||||||
|
mouse::Cursor::Unavailable,
|
||||||
|
&mut self.renderer,
|
||||||
|
&mut clipboard::Null,
|
||||||
|
&mut self.messages,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = self.raw.draw(
|
||||||
|
&mut self.renderer,
|
||||||
|
&theme,
|
||||||
|
&core::renderer::Style {
|
||||||
|
text_color: base.text_color,
|
||||||
|
},
|
||||||
|
mouse::Cursor::Unavailable,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Renderer::Secondary(renderer) = &mut self.renderer {
|
||||||
|
let scale_factor = 2.0;
|
||||||
|
|
||||||
|
let viewport = renderer::graphics::Viewport::with_physical_size(
|
||||||
|
Size::new(
|
||||||
|
(self.size.width * scale_factor).round() as u32,
|
||||||
|
(self.size.height * scale_factor).round() as u32,
|
||||||
|
),
|
||||||
|
f64::from(scale_factor),
|
||||||
|
);
|
||||||
|
|
||||||
|
let rgba = tiny_skia::window::compositor::screenshot::<&str>(
|
||||||
|
renderer,
|
||||||
|
&viewport,
|
||||||
|
base.background_color,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Snapshot {
|
||||||
|
screenshot: window::Screenshot::new(
|
||||||
|
rgba,
|
||||||
|
viewport.physical_size(),
|
||||||
|
viewport.scale_factor(),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_messages(self) -> impl IntoIterator<Item = Message> {
|
pub fn into_messages(self) -> impl IntoIterator<Item = Message> {
|
||||||
self.messages
|
self.messages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Snapshot {
|
||||||
|
screenshot: window::Screenshot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshot {
|
||||||
|
pub fn matches_image(&self, path: impl AsRef<Path>) -> Result<bool, Error> {
|
||||||
|
let path = path.as_ref().with_extension("png");
|
||||||
|
|
||||||
|
if path.exists() {
|
||||||
|
let file = fs::File::open(&path)?;
|
||||||
|
let decoder = png::Decoder::new(file);
|
||||||
|
|
||||||
|
let mut reader = decoder.read_info()?;
|
||||||
|
let mut bytes = vec![0; reader.output_buffer_size()];
|
||||||
|
let info = reader.next_frame(&mut bytes)?;
|
||||||
|
|
||||||
|
Ok(self.screenshot.bytes == bytes[..info.buffer_size()])
|
||||||
|
} else {
|
||||||
|
if let Some(directory) = path.parent() {
|
||||||
|
fs::create_dir_all(directory)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = fs::File::create(path)?;
|
||||||
|
|
||||||
|
let mut encoder = png::Encoder::new(
|
||||||
|
file,
|
||||||
|
self.screenshot.size.width,
|
||||||
|
self.screenshot.size.height,
|
||||||
|
);
|
||||||
|
encoder.set_color(png::ColorType::Rgba);
|
||||||
|
|
||||||
|
let mut writer = encoder.write_header()?;
|
||||||
|
writer.write_image_data(&self.screenshot.bytes)?;
|
||||||
|
writer.finish()?;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matches_hash(&self, path: impl AsRef<Path>) -> Result<bool, Error> {
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
let path = path.as_ref().with_extension("sha256");
|
||||||
|
|
||||||
|
let hash = {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&self.screenshot.bytes);
|
||||||
|
format!("{:x}", hasher.finalize())
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.exists() {
|
||||||
|
let saved_hash = fs::read_to_string(&path)?;
|
||||||
|
|
||||||
|
Ok(hash == saved_hash)
|
||||||
|
} else {
|
||||||
|
if let Some(directory) = path.parent() {
|
||||||
|
fs::create_dir_all(directory)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(path, hash)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn key_press_and_release(
|
fn key_press_and_release(
|
||||||
key: impl Into<keyboard::Key>,
|
key: impl Into<keyboard::Key>,
|
||||||
text: Option<SmolStr>,
|
text: Option<SmolStr>,
|
||||||
|
|
@ -293,4 +433,25 @@ fn key_press_and_release(
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
NotFound(Selector),
|
NotFound(Selector),
|
||||||
|
IOFailed(Arc<io::Error>),
|
||||||
|
PngDecodingFailed(Arc<png::DecodingError>),
|
||||||
|
PngEncodingFailed(Arc<png::EncodingError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(error: io::Error) -> Self {
|
||||||
|
Self::IOFailed(Arc::new(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<png::DecodingError> for Error {
|
||||||
|
fn from(error: png::DecodingError) -> Self {
|
||||||
|
Self::PngDecodingFailed(Arc::new(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<png::EncodingError> for Error {
|
||||||
|
fn from(error: png::EncodingError) -> Self {
|
||||||
|
Self::PngEncodingFailed(Arc::new(error))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@ use crate::conversion;
|
||||||
use crate::core;
|
use crate::core;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::renderer;
|
use crate::core::renderer;
|
||||||
|
use crate::core::theme;
|
||||||
use crate::core::time::Instant;
|
use crate::core::time::Instant;
|
||||||
use crate::core::widget::operation;
|
use crate::core::widget::operation;
|
||||||
use crate::core::window;
|
use crate::core::window;
|
||||||
use crate::core::{Color, Element, Point, Size, Theme};
|
use crate::core::{Element, Point, Size};
|
||||||
use crate::futures::futures::channel::mpsc;
|
use crate::futures::futures::channel::mpsc;
|
||||||
use crate::futures::futures::channel::oneshot;
|
use crate::futures::futures::channel::oneshot;
|
||||||
use crate::futures::futures::task;
|
use crate::futures::futures::task;
|
||||||
|
|
@ -46,7 +47,7 @@ use std::sync::Arc;
|
||||||
pub trait Program
|
pub trait Program
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
Self::Theme: DefaultStyle,
|
Self::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
/// The type of __messages__ your [`Program`] will produce.
|
/// The type of __messages__ your [`Program`] will produce.
|
||||||
type Message: std::fmt::Debug + Send;
|
type Message: std::fmt::Debug + Send;
|
||||||
|
|
@ -106,8 +107,8 @@ where
|
||||||
fn theme(&self, window: window::Id) -> Self::Theme;
|
fn theme(&self, window: window::Id) -> Self::Theme;
|
||||||
|
|
||||||
/// Returns the `Style` variation of the `Theme`.
|
/// Returns the `Style` variation of the `Theme`.
|
||||||
fn style(&self, theme: &Self::Theme) -> Appearance {
|
fn style(&self, theme: &Self::Theme) -> theme::Style {
|
||||||
theme.default_style()
|
theme::Base::base(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the event `Subscription` for the current state of the
|
/// Returns the event `Subscription` for the current state of the
|
||||||
|
|
@ -138,37 +139,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The appearance of a program.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub struct Appearance {
|
|
||||||
/// The background [`Color`] of the application.
|
|
||||||
pub background_color: Color,
|
|
||||||
|
|
||||||
/// The default text [`Color`] of the application.
|
|
||||||
pub text_color: Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The default style of a [`Program`].
|
|
||||||
pub trait DefaultStyle {
|
|
||||||
/// Returns the default style of a [`Program`].
|
|
||||||
fn default_style(&self) -> Appearance;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DefaultStyle for Theme {
|
|
||||||
fn default_style(&self) -> Appearance {
|
|
||||||
default(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The default [`Appearance`] of a [`Program`] with the built-in [`Theme`].
|
|
||||||
pub fn default(theme: &Theme) -> Appearance {
|
|
||||||
let palette = theme.extended_palette();
|
|
||||||
|
|
||||||
Appearance {
|
|
||||||
background_color: palette.background.base.color,
|
|
||||||
text_color: palette.background.base.text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Runs a [`Program`] with an executor, compositor, and the provided
|
/// Runs a [`Program`] with an executor, compositor, and the provided
|
||||||
/// settings.
|
/// settings.
|
||||||
pub fn run<P, C>(
|
pub fn run<P, C>(
|
||||||
|
|
@ -180,7 +150,7 @@ pub fn run<P, C>(
|
||||||
where
|
where
|
||||||
P: Program + 'static,
|
P: Program + 'static,
|
||||||
C: Compositor<Renderer = P::Renderer> + 'static,
|
C: Compositor<Renderer = P::Renderer> + 'static,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
|
|
||||||
|
|
@ -674,7 +644,7 @@ async fn run_instance<P, C>(
|
||||||
) where
|
) where
|
||||||
P: Program + 'static,
|
P: Program + 'static,
|
||||||
C: Compositor<Renderer = P::Renderer> + 'static,
|
C: Compositor<Renderer = P::Renderer> + 'static,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
use winit::event;
|
use winit::event;
|
||||||
use winit::event_loop::ControlFlow;
|
use winit::event_loop::ControlFlow;
|
||||||
|
|
@ -1170,7 +1140,7 @@ fn build_user_interface<'a, P: Program>(
|
||||||
id: window::Id,
|
id: window::Id,
|
||||||
) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
|
) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
|
||||||
where
|
where
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
debug.view_started();
|
debug.view_started();
|
||||||
let view = program.view(id);
|
let view = program.view(id);
|
||||||
|
|
@ -1189,7 +1159,7 @@ fn update<P: Program, E: Executor>(
|
||||||
debug: &mut Debug,
|
debug: &mut Debug,
|
||||||
messages: &mut Vec<P::Message>,
|
messages: &mut Vec<P::Message>,
|
||||||
) where
|
) where
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
for message in messages.drain(..) {
|
for message in messages.drain(..) {
|
||||||
debug.log_message(&message);
|
debug.log_message(&message);
|
||||||
|
|
@ -1226,7 +1196,7 @@ fn run_action<P, C>(
|
||||||
) where
|
) where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer> + 'static,
|
C: Compositor<Renderer = P::Renderer> + 'static,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
use crate::runtime::clipboard;
|
use crate::runtime::clipboard;
|
||||||
use crate::runtime::system;
|
use crate::runtime::system;
|
||||||
|
|
@ -1461,7 +1431,7 @@ fn run_action<P, C>(
|
||||||
&debug.overlay(),
|
&debug.overlay(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = channel.send(window::Screenshot::new(
|
let _ = channel.send(core::window::Screenshot::new(
|
||||||
bytes,
|
bytes,
|
||||||
window.state.physical_size(),
|
window.state.physical_size(),
|
||||||
window.state.viewport().scale_factor(),
|
window.state.viewport().scale_factor(),
|
||||||
|
|
@ -1536,7 +1506,7 @@ pub fn build_user_interfaces<'a, P: Program, C>(
|
||||||
) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
|
) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
|
||||||
where
|
where
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
cached_user_interfaces
|
cached_user_interfaces
|
||||||
.drain()
|
.drain()
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
use crate::conversion;
|
use crate::conversion;
|
||||||
use crate::core::{mouse, window};
|
use crate::core::{mouse, theme, window};
|
||||||
use crate::core::{Color, Size};
|
use crate::core::{Color, Size};
|
||||||
use crate::graphics::Viewport;
|
use crate::graphics::Viewport;
|
||||||
use crate::program::{self, Program};
|
use crate::program::Program;
|
||||||
use std::fmt::{Debug, Formatter};
|
|
||||||
|
|
||||||
use winit::event::{Touch, WindowEvent};
|
use winit::event::{Touch, WindowEvent};
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
/// The state of a multi-windowed [`Program`].
|
/// The state of a multi-windowed [`Program`].
|
||||||
pub struct State<P: Program>
|
pub struct State<P: Program>
|
||||||
where
|
where
|
||||||
P::Theme: program::DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
title: String,
|
title: String,
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
|
|
@ -20,12 +21,12 @@ where
|
||||||
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
|
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
|
||||||
modifiers: winit::keyboard::ModifiersState,
|
modifiers: winit::keyboard::ModifiersState,
|
||||||
theme: P::Theme,
|
theme: P::Theme,
|
||||||
appearance: program::Appearance,
|
style: theme::Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Program> Debug for State<P>
|
impl<P: Program> Debug for State<P>
|
||||||
where
|
where
|
||||||
P::Theme: program::DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("multi_window::State")
|
f.debug_struct("multi_window::State")
|
||||||
|
|
@ -34,14 +35,14 @@ where
|
||||||
.field("viewport", &self.viewport)
|
.field("viewport", &self.viewport)
|
||||||
.field("viewport_version", &self.viewport_version)
|
.field("viewport_version", &self.viewport_version)
|
||||||
.field("cursor_position", &self.cursor_position)
|
.field("cursor_position", &self.cursor_position)
|
||||||
.field("appearance", &self.appearance)
|
.field("style", &self.style)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Program> State<P>
|
impl<P: Program> State<P>
|
||||||
where
|
where
|
||||||
P::Theme: program::DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
/// Creates a new [`State`] for the provided [`Program`]'s `window`.
|
/// Creates a new [`State`] for the provided [`Program`]'s `window`.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
@ -52,7 +53,7 @@ where
|
||||||
let title = application.title(window_id);
|
let title = application.title(window_id);
|
||||||
let scale_factor = application.scale_factor(window_id);
|
let scale_factor = application.scale_factor(window_id);
|
||||||
let theme = application.theme(window_id);
|
let theme = application.theme(window_id);
|
||||||
let appearance = application.style(&theme);
|
let style = application.style(&theme);
|
||||||
|
|
||||||
let viewport = {
|
let viewport = {
|
||||||
let physical_size = window.inner_size();
|
let physical_size = window.inner_size();
|
||||||
|
|
@ -71,7 +72,7 @@ where
|
||||||
cursor_position: None,
|
cursor_position: None,
|
||||||
modifiers: winit::keyboard::ModifiersState::default(),
|
modifiers: winit::keyboard::ModifiersState::default(),
|
||||||
theme,
|
theme,
|
||||||
appearance,
|
style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,12 +128,12 @@ where
|
||||||
|
|
||||||
/// Returns the current background [`Color`] of the [`State`].
|
/// Returns the current background [`Color`] of the [`State`].
|
||||||
pub fn background_color(&self) -> Color {
|
pub fn background_color(&self) -> Color {
|
||||||
self.appearance.background_color
|
self.style.background_color
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current text [`Color`] of the [`State`].
|
/// Returns the current text [`Color`] of the [`State`].
|
||||||
pub fn text_color(&self) -> Color {
|
pub fn text_color(&self) -> Color {
|
||||||
self.appearance.text_color
|
self.style.text_color
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the provided window event and updates the [`State`] accordingly.
|
/// Processes the provided window event and updates the [`State`] accordingly.
|
||||||
|
|
@ -237,6 +238,6 @@ where
|
||||||
|
|
||||||
// Update theme and appearance
|
// Update theme and appearance
|
||||||
self.theme = application.theme(window_id);
|
self.theme = application.theme(window_id);
|
||||||
self.appearance = application.style(&self.theme);
|
self.style = application.style(&self.theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
|
use crate::core::theme;
|
||||||
use crate::core::time::Instant;
|
use crate::core::time::Instant;
|
||||||
use crate::core::window::Id;
|
use crate::core::window::Id;
|
||||||
use crate::core::{Point, Size};
|
use crate::core::{Point, Size};
|
||||||
use crate::graphics::Compositor;
|
use crate::graphics::Compositor;
|
||||||
use crate::program::{DefaultStyle, Program, State};
|
use crate::program::{Program, State};
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -14,7 +15,7 @@ pub struct WindowManager<P, C>
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
aliases: BTreeMap<winit::window::WindowId, Id>,
|
aliases: BTreeMap<winit::window::WindowId, Id>,
|
||||||
entries: BTreeMap<Id, Window<P, C>>,
|
entries: BTreeMap<Id, Window<P, C>>,
|
||||||
|
|
@ -24,7 +25,7 @@ impl<P, C> WindowManager<P, C>
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -132,7 +133,7 @@ impl<P, C> Default for WindowManager<P, C>
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
|
|
@ -144,7 +145,7 @@ pub struct Window<P, C>
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
pub raw: Arc<winit::window::Window>,
|
pub raw: Arc<winit::window::Window>,
|
||||||
pub state: State<P>,
|
pub state: State<P>,
|
||||||
|
|
@ -160,7 +161,7 @@ impl<P, C> Window<P, C>
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
pub fn position(&self) -> Option<Point> {
|
pub fn position(&self) -> Option<Point> {
|
||||||
self.raw
|
self.raw
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue