summaryrefslogtreecommitdiffstats
path: root/widget
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-02-09 23:57:11 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-02-09 23:57:11 +0100
commitb535f7ae385658829f57f742eb1cc6f2f40cddf5 (patch)
tree47da7d68d90308648590aecd53009749968e70b3 /widget
parent4c6ea3cfe2f6023a2f92d8ebfc227f0381c8ac78 (diff)
downloadiced-b535f7ae385658829f57f742eb1cc6f2f40cddf5.tar.gz
iced-b535f7ae385658829f57f742eb1cc6f2f40cddf5.tar.bz2
iced-b535f7ae385658829f57f742eb1cc6f2f40cddf5.zip
Invalidate `QRCode` cache on `Appearance` change
Diffstat (limited to 'widget')
-rw-r--r--widget/src/qr_code.rs118
1 files changed, 69 insertions, 49 deletions
diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs
index b6c60bf6..ece9b9e1 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<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,
@@ -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::<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,
@@ -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.
///
/// This method uses an [`ErrorCorrection::Medium`] and chooses the smallest
@@ -310,3 +325,8 @@ impl From<qrcode::types::QrError> for Error {
}
}
}
+
+#[derive(Default)]
+struct State {
+ last_appearance: RefCell<Option<Appearance>>,
+}