agg_gui/widgets/
qr_view.rs1use std::cell::{Cell, RefCell};
17use std::rc::Rc;
18
19use qrcodegen::{QrCode, QrCodeEcc};
20
21use crate::color::Color;
22use crate::draw_ctx::DrawCtx;
23use crate::event::{Event, EventResult};
24use crate::geometry::{Point, Rect, Size};
25use crate::widget::Widget;
26
27pub struct QrView {
28 bounds: Rect,
29 children: Vec<Box<dyn Widget>>, text: String,
32 text_source: Option<Rc<RefCell<String>>>,
34 rasterised_text: String,
37 cache: crate::widget::BackbufferCache,
38 visible: Option<Rc<Cell<bool>>>,
40 quiet_zone: f64,
42}
43
44impl QrView {
45 pub fn new<S: Into<String>>(text: S) -> Self {
47 Self {
48 bounds: Rect::new(0.0, 0.0, 160.0, 160.0),
49 children: Vec::new(),
50 text: text.into(),
51 text_source: None,
52 rasterised_text: String::new(),
53 cache: crate::widget::BackbufferCache::new(),
54 visible: None,
55 quiet_zone: 0.08,
56 }
57 }
58
59 pub fn with_text_source(mut self, source: Rc<RefCell<String>>) -> Self {
62 self.text_source = Some(source);
63 self
64 }
65
66 pub fn with_visibility(mut self, visible: Rc<Cell<bool>>) -> Self {
68 self.visible = Some(visible);
69 self
70 }
71
72 pub fn with_quiet_zone(mut self, fraction: f64) -> Self {
73 self.quiet_zone = fraction.max(0.0);
74 self
75 }
76
77 pub fn set_text(&mut self, text: &str) {
79 if self.text == text {
80 return;
81 }
82 self.text.clear();
83 self.text.push_str(text);
84 self.cache.invalidate();
85 }
86
87 fn current_text(&self) -> String {
89 match &self.text_source {
90 Some(src) => src.borrow().clone(),
91 None => self.text.clone(),
92 }
93 }
94
95 fn is_shown(&self) -> bool {
96 self.visible.as_ref().map(|c| c.get()).unwrap_or(true)
97 }
98}
99
100impl Widget for QrView {
101 fn type_name(&self) -> &'static str {
102 "QrView"
103 }
104
105 fn bounds(&self) -> Rect {
106 self.bounds
107 }
108
109 fn set_bounds(&mut self, bounds: Rect) {
110 if (bounds.width - self.bounds.width).abs() > 0.5
111 || (bounds.height - self.bounds.height).abs() > 0.5
112 {
113 self.cache.invalidate();
114 }
115 self.bounds = bounds;
116 }
117
118 fn children(&self) -> &[Box<dyn Widget>] {
119 &self.children
120 }
121
122 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
123 &mut self.children
124 }
125
126 fn hit_test(&self, _local_pos: Point) -> bool {
127 false
128 }
129
130 fn is_visible(&self) -> bool {
131 self.is_shown()
132 }
133
134 fn layout(&mut self, available: Size) -> Size {
135 self.bounds = Rect::new(0.0, 0.0, available.width, available.height);
136 available
137 }
138
139 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
140 let text = self.current_text();
141 if text != self.rasterised_text {
142 self.rasterised_text.clear();
143 self.rasterised_text.push_str(&text);
144 self.cache.invalidate();
145 }
146
147 let w = self.bounds.width;
148 let h = self.bounds.height;
149 if w <= 0.0 || h <= 0.0 {
150 return;
151 }
152
153 let side = w.min(h);
156 let ox = (w - side) * 0.5;
157 let oy = (h - side) * 0.5;
158
159 ctx.set_fill_color(Color::from_rgb8(255, 255, 255));
162 ctx.begin_path();
163 ctx.rect(ox, oy, side, side);
164 ctx.fill();
165
166 let qr = match QrCode::encode_text(&text, QrCodeEcc::Low) {
167 Ok(qr) => qr,
168 Err(_) => return,
169 };
170 let modules = qr.size();
171 if modules <= 0 {
172 return;
173 }
174
175 let pad = side * self.quiet_zone;
176 let inner = side - 2.0 * pad;
177 if inner <= 0.0 {
178 return;
179 }
180 let module_size = inner / modules as f64;
181 let origin_x = ox + pad;
182 let origin_y = oy + pad;
184
185 ctx.set_fill_color(Color::from_rgb8(0, 0, 0));
186 for j in 0..modules {
187 for i in 0..modules {
188 if !qr.get_module(i, j) {
189 continue;
190 }
191 let x = origin_x + i as f64 * module_size;
192 let y = origin_y + (modules - 1 - j) as f64 * module_size;
193 ctx.begin_path();
194 ctx.rect(x, y, module_size + 0.5, module_size + 0.5);
195 ctx.fill();
196 }
197 }
198 }
199
200 fn on_event(&mut self, _event: &Event) -> EventResult {
201 EventResult::Ignored
202 }
203
204 fn backbuffer_cache_mut(&mut self) -> Option<&mut crate::widget::BackbufferCache> {
205 if !self.is_shown() {
208 return None;
209 }
210 Some(&mut self.cache)
211 }
212}