gpui_qrcode/lib.rs
1//! Render a QR code as qpui elements
2//!
3//! A simple Component to render a QR code as qpui elements with gpui-native APIs for
4//! customization and flexibility.
5//!
6//! To render a QR code just pass the size and a vector of booleans representing the data, where
7//! true means this block needs to be rendered as a dark square. The size indicates how many columns
8//! to sort the qrcode into, thus the data should usually have the a length of size squared.
9//!
10//! Examples:
11//! ```
12//! use gpui_qrcode::QrCode;
13//! use gpui::prelude::*;
14//!
15//! let qr_code = QrCode::new(10, vec![true, false, true, false]);
16//! ```
17//!
18//! You can also customize the appearance of the QR code by applying styles:
19//! ```
20//! use gpui_qrcode::QrCode;
21//! use gpui::prelude::*;
22//!
23//! let qr_code = QrCode::new(10, vec![true, false, true, false])
24//! .bg(gpui::blue())
25//! .p_2();
26//! ```
27//!
28//! To customize the appearance of the QR code dots use the `refine_dot_style` method:
29//! ```
30//! use gpui_qrcode::QrCode;
31//! use gpui::{prelude::*, StyleRefinement};
32//!
33//! let qr_code = QrCode::new(10, vec![true, false, true, false])
34//! .bg(gpui::blue())
35//! .p_2()
36//! .refine_dot_style(&StyleRefinement {
37//! background: Some(gpui::red().into()),
38//! ..Default::default()
39//! });
40//! ```
41//!
42
43use gpui::{
44 AbsoluteLength, DefiniteLength, IntoElement, Length, ParentElement, Refineable, RenderOnce,
45 SizeRefinement, StyleRefinement, Styled, div,
46};
47
48#[cfg(feature = "qrcode-support")]
49mod qrcode_lib_support {
50 /// Support for rendering QR codes directly from `qrcode::QrCode`.
51
52 #[derive(Debug, thiserror::Error)]
53 pub enum QrCodeRenderError {
54 #[error("Exceeded width of u16::MAX")]
55 ExceededWidth,
56 }
57
58 impl TryFrom<qrcode::QrCode> for super::QrCode {
59 type Error = QrCodeRenderError;
60
61 fn try_from(data: qrcode::QrCode) -> Result<Self, Self::Error> {
62 let width =
63 u16::try_from(data.width()).map_err(|_| QrCodeRenderError::ExceededWidth)?;
64 Ok(Self::new(
65 width,
66 data.into_colors()
67 .into_iter()
68 .map(|color| matches!(color, qrcode::Color::Dark))
69 .collect(),
70 ))
71 }
72 }
73}
74
75/// A simple Component to render a QR code as qpui elements
76///
77/// This is a `Styled` component, so you can use the regular styling methods of
78/// gpui like `.bg(gpui::blue())` to set the background or `.p_2()` to add padding.
79///
80/// To change the color and behavior of the dots apply the styles you want via the
81/// `refine_dot_style` method.
82#[derive(Clone, Debug, PartialEq, IntoElement)]
83pub struct QrCode {
84 cols: u16,
85 data: Vec<bool>,
86 style: StyleRefinement,
87 dot_style: StyleRefinement,
88}
89
90impl QrCode {
91 /// Create a new QR code component with of `cols` dots per row.
92 ///
93 /// For each entry in `data`, a dot will be rendered if the entry is `true`, thus
94 /// the data is expected to be of length `cols * cols` (but that isn't enforced).
95 pub fn new(cols: u16, data: Vec<bool>) -> Self {
96 Self {
97 cols,
98 data,
99
100 style: StyleRefinement {
101 background: Some(gpui::white().into()),
102 ..Default::default()
103 },
104 dot_style: StyleRefinement {
105 background: Some(gpui::black().into()),
106 min_size: SizeRefinement {
107 width: Some(Length::Definite(DefiniteLength::Absolute(
108 AbsoluteLength::Rems(gpui::Rems(0.25)),
109 ))),
110 height: Some(Length::Definite(DefiniteLength::Absolute(
111 AbsoluteLength::Rems(gpui::Rems(0.25)),
112 ))),
113 },
114 ..Default::default()
115 },
116 }
117 }
118
119 /// Refine the style of the dots in the QR code.
120 ///
121 /// For example, you can set the background color to red like this:
122 ///
123 /// ```rust
124 /// # use gpui_qrcode::QrCode;
125 /// # use gpui::StyleRefinement;
126 /// let qr_code = QrCode::new(10, vec![true; 100]);
127 /// let qr_code = qr_code.refine_dot_style(&StyleRefinement {
128 /// background: Some(gpui::red().into()),
129 /// ..Default::default()
130 /// });
131 /// ```
132 pub fn refine_dot_style(mut self, new_styles: &StyleRefinement) -> Self {
133 self.dot_style.refine(new_styles);
134 self
135 }
136}
137
138impl Styled for QrCode {
139 fn style(&mut self) -> &mut StyleRefinement {
140 &mut self.style
141 }
142}
143
144impl RenderOnce for QrCode {
145 fn render(self, _window: &mut gpui::Window, _cx: &mut gpui::App) -> impl gpui::IntoElement {
146 let mut d = div();
147 d.style().refine(&self.style);
148
149 d.grid()
150 .grid_cols(self.cols)
151 .children(self.data.into_iter().map(|is_dark| {
152 let mut d = div();
153 if is_dark {
154 d.style().refine(&self.dot_style)
155 }
156 d
157 }))
158 }
159}