Skip to content

Commit

Permalink
Invalidate QRCode cache on Appearance change
Browse files Browse the repository at this point in the history
  • Loading branch information
hecrj committed Feb 9, 2024
1 parent 4c6ea3c commit b535f7a
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 56 deletions.
8 changes: 2 additions & 6 deletions examples/qr_code/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn main() -> iced::Result {
#[derive(Default)]
struct QRGenerator {
data: String,
qr_code: Option<qr_code::State>,
qr_code: Option<qr_code::Data>,
theme: Theme,
}

Expand Down Expand Up @@ -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();
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion style/src/qr_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
118 changes: 69 additions & 49 deletions widget/src/qr_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
}
Expand All @@ -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(),
}
}
Expand All @@ -58,6 +60,14 @@ impl<'a, Message, Theme> Widget<Message, Theme, Renderer> for QRCode<'a, Theme>
where
Theme: StyleSheet,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<State>()
}

fn state(&self) -> tree::State {
tree::State::new(State::default())
}

fn size(&self) -> Size<Length> {
Size {
width: Length::Shrink,
Expand All @@ -71,63 +81,68 @@ 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))
}

fn draw(
&self,
_state: &Tree,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let state = tree.state.downcast_ref::<State>();

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,
Expand All @@ -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<qrcode::Color>,
width: usize,
cache: canvas::Cache,
}

impl State {
impl Data {
/// Creates a new [`State`] with the provided data.

Check warning on line 177 in widget/src/qr_code.rs

View workflow job for this annotation

GitHub Actions / all

public documentation for `new` links to private item `State`
///
/// This method uses an [`ErrorCorrection::Medium`] and chooses the smallest
Expand Down Expand Up @@ -310,3 +325,8 @@ impl From<qrcode::types::QrError> for Error {
}
}
}

#[derive(Default)]
struct State {
last_appearance: RefCell<Option<Appearance>>,
}

0 comments on commit b535f7a

Please sign in to comment.