diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 68bbf2605b..8b2e950020 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -9,7 +9,7 @@ pub fn main() -> iced::Result { #[derive(Default)] struct QRGenerator { data: String, - qr_code: Option, + qr_code: Option, theme: Theme, } @@ -38,17 +38,13 @@ impl Sandbox for QRGenerator { self.qr_code = if data.is_empty() { None } else { - qr_code::State::new(&data).ok() + qr_code::Data::new(&data).ok() }; self.data = data; } Message::ThemeChanged(theme) => { self.theme = theme; - - if self.qr_code.is_some() { - self.qr_code = qr_code::State::new(&self.data).ok(); - } } } } diff --git a/style/src/qr_code.rs b/style/src/qr_code.rs index a024506ca7..02c4709a5f 100644 --- a/style/src/qr_code.rs +++ b/style/src/qr_code.rs @@ -2,7 +2,7 @@ use crate::core::Color; /// The appearance of a QR code. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Appearance { /// The color of the QR code data cells pub cell: Color, diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index b6c60bf6a1..ece9b9e104 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -3,15 +3,17 @@ use crate::canvas; use crate::core::layout; use crate::core::mouse; use crate::core::renderer::{self, Renderer as _}; -use crate::core::widget::Tree; +use crate::core::widget::tree::{self, Tree}; use crate::core::{ Element, Layout, Length, Point, Rectangle, Size, Vector, Widget, }; use crate::graphics::geometry::Renderer as _; use crate::Renderer; + +use std::cell::RefCell; use thiserror::Error; -pub use crate::style::qr_code::StyleSheet; +pub use crate::style::qr_code::{Appearance, StyleSheet}; const DEFAULT_CELL_SIZE: u16 = 4; const QUIET_ZONE: usize = 2; @@ -23,7 +25,7 @@ pub struct QRCode<'a, Theme = crate::Theme> where Theme: StyleSheet, { - state: &'a State, + data: &'a Data, cell_size: u16, style: Theme::Style, } @@ -32,11 +34,11 @@ impl<'a, Theme> QRCode<'a, Theme> where Theme: StyleSheet, { - /// Creates a new [`QRCode`] with the provided [`State`]. - pub fn new(state: &'a State) -> Self { + /// Creates a new [`QRCode`] with the provided [`Data`]. + pub fn new(data: &'a Data) -> Self { Self { + data, cell_size: DEFAULT_CELL_SIZE, - state, style: Default::default(), } } @@ -58,6 +60,14 @@ impl<'a, Message, Theme> Widget for QRCode<'a, Theme> where Theme: StyleSheet, { + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + fn size(&self) -> Size { Size { width: Length::Shrink, @@ -71,7 +81,7 @@ where _renderer: &Renderer, _limits: &layout::Limits, ) -> layout::Node { - let side_length = (self.state.width + 2 * QUIET_ZONE) as f32 + let side_length = (self.data.width + 2 * QUIET_ZONE) as f32 * f32::from(self.cell_size); layout::Node::new(Size::new(side_length, side_length)) @@ -79,7 +89,7 @@ where fn draw( &self, - _state: &Tree, + tree: &Tree, renderer: &mut Renderer, theme: &Theme, _style: &renderer::Style, @@ -87,47 +97,52 @@ where _cursor: mouse::Cursor, _viewport: &Rectangle, ) { + let state = tree.state.downcast_ref::(); + let bounds = layout.bounds(); - let side_length = self.state.width + 2 * QUIET_ZONE; + let side_length = self.data.width + 2 * QUIET_ZONE; + + let appearance = theme.appearance(&self.style); + let mut last_appearance = state.last_appearance.borrow_mut(); + + if Some(appearance) != *last_appearance { + self.data.cache.clear(); - let style = theme.appearance(&self.style); + *last_appearance = Some(appearance); + } // Reuse cache if possible - let geometry = - self.state.cache.draw(renderer, bounds.size(), |frame| { - // Scale units to cell size - frame.scale(self.cell_size); - - // Draw background - frame.fill_rectangle( - Point::ORIGIN, - Size::new(side_length as f32, side_length as f32), - style.background, - ); - - // Avoid drawing on the quiet zone - frame.translate(Vector::new( - QUIET_ZONE as f32, - QUIET_ZONE as f32, - )); - - // Draw contents - self.state - .contents - .iter() - .enumerate() - .filter(|(_, value)| **value == qrcode::Color::Dark) - .for_each(|(index, _)| { - let row = index / self.state.width; - let column = index % self.state.width; - - frame.fill_rectangle( - Point::new(column as f32, row as f32), - Size::UNIT, - style.cell, - ); - }); - }); + let geometry = self.data.cache.draw(renderer, bounds.size(), |frame| { + // Scale units to cell size + frame.scale(self.cell_size); + + // Draw background + frame.fill_rectangle( + Point::ORIGIN, + Size::new(side_length as f32, side_length as f32), + appearance.background, + ); + + // Avoid drawing on the quiet zone + frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32)); + + // Draw contents + self.data + .contents + .iter() + .enumerate() + .filter(|(_, value)| **value == qrcode::Color::Dark) + .for_each(|(index, _)| { + let row = index / self.data.width; + let column = index % self.data.width; + + frame.fill_rectangle( + Point::new(column as f32, row as f32), + Size::UNIT, + appearance.cell, + ); + }); + }); renderer.with_translation( bounds.position() - Point::ORIGIN, @@ -148,17 +163,17 @@ where } } -/// The state of a [`QRCode`]. +/// The data of a [`QRCode`]. /// -/// It stores the data that will be displayed. +/// It stores the contents that will be displayed. #[derive(Debug)] -pub struct State { +pub struct Data { contents: Vec, width: usize, cache: canvas::Cache, } -impl State { +impl Data { /// Creates a new [`State`] with the provided data. /// /// This method uses an [`ErrorCorrection::Medium`] and chooses the smallest @@ -310,3 +325,8 @@ impl From for Error { } } } + +#[derive(Default)] +struct State { + last_appearance: RefCell>, +}