mirror of
https://github.com/jwinterm/grim.git
synced 2026-10-19 06:48:33 +02:00
qr: export to gif and png
This commit is contained in:
@@ -114,6 +114,10 @@ impl PlatformCallbacks for Android {
|
||||
fn switch_camera(&self) {
|
||||
self.call_java_method("switchCamera", "()V", &[]).unwrap();
|
||||
}
|
||||
|
||||
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io:: Write;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -20,6 +22,7 @@ use std::thread;
|
||||
use nokhwa::Camera;
|
||||
use nokhwa::pixel_format::RgbFormat;
|
||||
use nokhwa::utils::{CameraIndex, RequestedFormat, RequestedFormatType};
|
||||
use rfd::FileDialog;
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
|
||||
@@ -124,6 +127,19 @@ impl PlatformCallbacks for Desktop {
|
||||
fn switch_camera(&self) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error> {
|
||||
let folder = FileDialog::new()
|
||||
.set_directory(dirs::home_dir().unwrap())
|
||||
.set_file_name(name.clone())
|
||||
.save_file();
|
||||
if let Some(folder) = folder {
|
||||
let mut image = File::create(folder)?;
|
||||
image.write_all(data.as_slice())?;
|
||||
image.sync_all()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
||||
@@ -31,4 +31,5 @@ pub trait PlatformCallbacks {
|
||||
fn camera_image(&self) -> Option<(Vec<u8>, u32)>;
|
||||
fn can_switch_camera(&self) -> bool;
|
||||
fn switch_camera(&self);
|
||||
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error>;
|
||||
}
|
||||
+287
-103
@@ -12,15 +12,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::fs::{File, read};
|
||||
use std::io::Cursor;
|
||||
use std::mem::size_of;
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
use std::thread;
|
||||
use egui::{SizeHint, TextureHandle, TextureOptions};
|
||||
use egui::load::SizedTexture;
|
||||
use egui_extras::image::load_svg_bytes_with_size;
|
||||
use image::codecs::jpeg::JpegEncoder;
|
||||
use image::{ColorType, EncodableLayout, ExtendedColorType, ImageBuffer, ImageEncoder, Rgb, RgbImage};
|
||||
use image::codecs::png::{CompressionType, FilterType, PngDecoder, PngEncoder};
|
||||
use qrcodegen::QrCode;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::IMAGES_SQUARE;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
|
||||
use crate::gui::views::types::QrCreationState;
|
||||
use crate::gui::views::types::QrImageState;
|
||||
use crate::gui::views::View;
|
||||
|
||||
/// QR code image from text.
|
||||
@@ -38,10 +47,12 @@ pub struct QrCodeContent {
|
||||
|
||||
/// Texture handle to show image when created.
|
||||
texture_handle: Option<TextureHandle>,
|
||||
/// QR code image creation progress and result.
|
||||
qr_creation_state: Arc<RwLock<QrCreationState>>,
|
||||
/// QR code view data state.
|
||||
qr_image_state: Arc<RwLock<QrImageState>>,
|
||||
}
|
||||
|
||||
const DEFAULT_QR_SIZE: u32 = 380;
|
||||
|
||||
impl QrCodeContent {
|
||||
pub fn new(text: String, animated: bool) -> Self {
|
||||
Self {
|
||||
@@ -50,108 +61,199 @@ impl QrCodeContent {
|
||||
animated_index: None,
|
||||
animation_time: None,
|
||||
texture_handle: None,
|
||||
qr_creation_state: Arc::new(RwLock::new(QrCreationState::default())),
|
||||
qr_image_state: Arc::new(RwLock::new(QrImageState::default())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw QR code.
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, text: String) {
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, text: String, cb: &dyn PlatformCallbacks) {
|
||||
if self.animated {
|
||||
// Create animated QR code image if not created.
|
||||
if !self.has_image() {
|
||||
let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0;
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(space);
|
||||
View::big_loading_spinner(ui);
|
||||
ui.add_space(space);
|
||||
});
|
||||
|
||||
// Create multiple vector images from text if not creating.
|
||||
if !self.creating() {
|
||||
self.create_svg_list(text);
|
||||
}
|
||||
} else {
|
||||
let svg_list = {
|
||||
let r_create = self.qr_creation_state.read();
|
||||
r_create.svg_list.clone().unwrap()
|
||||
};
|
||||
|
||||
// Setup animated index.
|
||||
let now = chrono::Utc::now().timestamp_millis();
|
||||
if now - *self.animation_time.get_or_insert(now) > 100 {
|
||||
if let Some(i) = self.animated_index {
|
||||
self.animated_index = Some(i + 1);
|
||||
}
|
||||
if *self.animated_index.get_or_insert(0) == svg_list.len() {
|
||||
self.animated_index = Some(0);
|
||||
}
|
||||
self.animation_time = Some(now);
|
||||
}
|
||||
|
||||
let svg = svg_list[self.animated_index.unwrap_or(0)].clone();
|
||||
|
||||
// Create images from SVG data.
|
||||
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32);
|
||||
let color_img = load_svg_bytes_with_size(svg.as_slice(), Some(size)).unwrap();
|
||||
// Create image texture.
|
||||
let texture_handle = ui.ctx().load_texture("qr_code",
|
||||
color_img.clone(),
|
||||
TextureOptions::default());
|
||||
self.texture_handle = Some(texture_handle.clone());
|
||||
let img_size = egui::emath::vec2(color_img.width() as f32,
|
||||
color_img.height() as f32);
|
||||
let sized_img = SizedTexture::new(texture_handle.id(), img_size);
|
||||
// Add image to content.
|
||||
ui.add(egui::Image::from_texture(sized_img)
|
||||
.max_height(ui.available_width())
|
||||
.fit_to_original_size(1.0));
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
// Show animated QR code.
|
||||
self.animated_ui(ui, text, cb);
|
||||
} else {
|
||||
// Create vector QR code image if not created.
|
||||
if !self.has_image() {
|
||||
let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0;
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(space);
|
||||
View::big_loading_spinner(ui);
|
||||
ui.add_space(space);
|
||||
});
|
||||
|
||||
// Create vector image from text if not creating.
|
||||
if !self.creating() {
|
||||
self.create_svg(text);
|
||||
}
|
||||
} else {
|
||||
// Create image from SVG data.
|
||||
let r_create = self.qr_creation_state.read();
|
||||
let svg = r_create.svg.as_ref().unwrap();
|
||||
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32);
|
||||
let color_img = load_svg_bytes_with_size(svg, Some(size)).unwrap();
|
||||
// Create image texture.
|
||||
let texture_handle = ui.ctx().load_texture("qr_code",
|
||||
color_img.clone(),
|
||||
TextureOptions::default());
|
||||
self.texture_handle = Some(texture_handle.clone());
|
||||
let img_size = egui::emath::vec2(color_img.width() as f32,
|
||||
color_img.height() as f32);
|
||||
let sized_img = SizedTexture::new(texture_handle.id(), img_size);
|
||||
// Add image to content.
|
||||
ui.add(egui::Image::from_texture(sized_img)
|
||||
.max_height(ui.available_width())
|
||||
.fit_to_original_size(1.0));
|
||||
}
|
||||
// Show static QR code.
|
||||
self.static_ui(ui, text, cb);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if QR code is creating.
|
||||
fn creating(&self) -> bool {
|
||||
let r_create = self.qr_creation_state.read();
|
||||
r_create.creating
|
||||
/// Draw animated QR code content.
|
||||
fn animated_ui(&mut self, ui: &mut egui::Ui, text: String, cb: &dyn PlatformCallbacks) {
|
||||
if !self.has_image() {
|
||||
let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0;
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(space);
|
||||
View::big_loading_spinner(ui);
|
||||
ui.add_space(space);
|
||||
});
|
||||
|
||||
// Create multiple vector images from text if not creating.
|
||||
if !self.loading() {
|
||||
self.create_svg_list(text);
|
||||
}
|
||||
} else {
|
||||
let svg_list = {
|
||||
let r_create = self.qr_image_state.read();
|
||||
r_create.svg_list.clone().unwrap()
|
||||
};
|
||||
|
||||
// Setup animated index.
|
||||
let now = chrono::Utc::now().timestamp_millis();
|
||||
if now - *self.animation_time.get_or_insert(now) > 100 {
|
||||
if let Some(i) = self.animated_index {
|
||||
self.animated_index = Some(i + 1);
|
||||
}
|
||||
if *self.animated_index.get_or_insert(0) == svg_list.len() {
|
||||
self.animated_index = Some(0);
|
||||
}
|
||||
self.animation_time = Some(now);
|
||||
}
|
||||
|
||||
let svg = svg_list[self.animated_index.unwrap_or(0)].clone();
|
||||
|
||||
// Create images from SVG data.
|
||||
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32);
|
||||
let color_img = load_svg_bytes_with_size(svg.as_slice(), Some(size)).unwrap();
|
||||
// Create image texture.
|
||||
let texture_handle = ui.ctx().load_texture("qr_code",
|
||||
color_img.clone(),
|
||||
TextureOptions::default());
|
||||
self.texture_handle = Some(texture_handle.clone());
|
||||
let img_size = egui::emath::vec2(color_img.width() as f32,
|
||||
color_img.height() as f32);
|
||||
let sized_img = SizedTexture::new(texture_handle.id(), img_size);
|
||||
// Add image to content.
|
||||
ui.add(egui::Image::from_texture(sized_img)
|
||||
.max_height(ui.available_width())
|
||||
.fit_to_original_size(1.0));
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show QR code text.
|
||||
View::ellipsize_text(ui, text.clone(), 16.0, Colors::INACTIVE_TEXT);
|
||||
ui.add_space(6.0);
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
let sharing = {
|
||||
let r_state = self.qr_image_state.read();
|
||||
r_state.exporting || r_state.gif_creating
|
||||
};
|
||||
if !sharing {
|
||||
// Show button to share QR.
|
||||
let share_text = format!("{} {}", IMAGES_SQUARE, t!("share"));
|
||||
View::button(ui, share_text, Colors::GOLD, || {
|
||||
{
|
||||
let mut w_state = self.qr_image_state.write();
|
||||
w_state.exporting = true;
|
||||
}
|
||||
// Create GIF to export.
|
||||
self.create_qr_gif(text, DEFAULT_QR_SIZE as usize);
|
||||
});
|
||||
ui.add_space(2.0);
|
||||
} else {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(6.0);
|
||||
View::small_loading_spinner(ui);
|
||||
ui.add_space(10.0);
|
||||
});
|
||||
}
|
||||
|
||||
// Check if GIF was created to share.
|
||||
let has_gif = {
|
||||
let r_state = self.qr_image_state.read();
|
||||
r_state.gif_data.is_some()
|
||||
};
|
||||
if has_gif {
|
||||
let data = {
|
||||
let r_state = self.qr_image_state.read();
|
||||
r_state.gif_data.clone().unwrap()
|
||||
};
|
||||
let name = format!("{}.gif", chrono::Utc::now().timestamp());
|
||||
cb.share_data(name, data).unwrap_or_default();
|
||||
// Clear GIF data and exporting flag.
|
||||
{
|
||||
let mut w_state = self.qr_image_state.write();
|
||||
w_state.gif_data = None;
|
||||
w_state.exporting = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw static QR code content.
|
||||
fn static_ui(&mut self, ui: &mut egui::Ui, text: String, cb: &dyn PlatformCallbacks) {
|
||||
if !self.has_image() {
|
||||
let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0;
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(space);
|
||||
View::big_loading_spinner(ui);
|
||||
ui.add_space(space);
|
||||
});
|
||||
|
||||
// Create vector image from text if not creating.
|
||||
if !self.loading() {
|
||||
self.create_svg(text);
|
||||
}
|
||||
} else {
|
||||
// Create image from SVG data.
|
||||
let svg = {
|
||||
let r_state = self.qr_image_state.read();
|
||||
r_state.svg.clone().unwrap()
|
||||
};
|
||||
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32);
|
||||
let color_img = load_svg_bytes_with_size(svg.as_slice(), Some(size)).unwrap();
|
||||
// Create image texture.
|
||||
let texture_handle = ui.ctx().load_texture("qr_code",
|
||||
color_img.clone(),
|
||||
TextureOptions::default());
|
||||
self.texture_handle = Some(texture_handle.clone());
|
||||
let img_size = egui::emath::vec2(color_img.width() as f32,
|
||||
color_img.height() as f32);
|
||||
let sized_img = SizedTexture::new(texture_handle.id(), img_size);
|
||||
// Add image to content.
|
||||
ui.add(egui::Image::from_texture(sized_img)
|
||||
.max_height(ui.available_width())
|
||||
.fit_to_original_size(1.0));
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show QR code text.
|
||||
View::ellipsize_text(ui, text.clone(), 16.0, Colors::INACTIVE_TEXT);
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show button to share QR.
|
||||
ui.vertical_centered(|ui| {
|
||||
let share_text = format!("{} {}", IMAGES_SQUARE, t!("share"));
|
||||
View::button(ui, share_text, Colors::GOLD, || {
|
||||
if let Ok(qr) = QrCode::encode_text(text.as_str(), qrcodegen::QrCodeEcc::Low) {
|
||||
if let Some(data) = Self::qr_to_image_data(qr, DEFAULT_QR_SIZE as usize) {
|
||||
let mut png = vec![];
|
||||
let png_enc = PngEncoder::new_with_quality(&mut png,
|
||||
CompressionType::Best,
|
||||
FilterType::NoFilter);
|
||||
if let Ok(()) = png_enc.write_image(data.as_slice(),
|
||||
DEFAULT_QR_SIZE,
|
||||
DEFAULT_QR_SIZE,
|
||||
ExtendedColorType::L8) {
|
||||
let name = format!("{}.png", chrono::Utc::now().timestamp());
|
||||
cb.share_data(name, png).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if QR code is loading.
|
||||
fn loading(&self) -> bool {
|
||||
let r_state = self.qr_image_state.read();
|
||||
r_state.loading
|
||||
}
|
||||
|
||||
/// Create multiple vector QR code images at separate thread.
|
||||
fn create_svg_list(&self, text: String) {
|
||||
let qr_creation_state = self.qr_creation_state.clone();
|
||||
let qr_state = self.qr_image_state.clone();
|
||||
thread::spawn(move || {
|
||||
let mut encoder = ur::Encoder::bytes(text.as_bytes(), 100).unwrap();
|
||||
let mut data = Vec::with_capacity(encoder.fragment_count());
|
||||
@@ -162,29 +264,29 @@ impl QrCodeContent {
|
||||
data.push(svg.into_bytes());
|
||||
}
|
||||
}
|
||||
let mut w_create = qr_creation_state.write();
|
||||
let mut w_state = qr_state.write();
|
||||
if !data.is_empty() {
|
||||
w_create.svg_list = Some(data);
|
||||
w_state.svg_list = Some(data);
|
||||
}
|
||||
w_create.creating = false;
|
||||
w_state.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/// Check if image was created.
|
||||
fn has_image(&self) -> bool {
|
||||
let r_create = self.qr_creation_state.read();
|
||||
r_create.svg.is_some() || r_create.svg_list.is_some()
|
||||
let r_state = self.qr_image_state.read();
|
||||
r_state.svg.is_some() || r_state.svg_list.is_some()
|
||||
}
|
||||
|
||||
/// Create vector QR code image at separate thread.
|
||||
fn create_svg(&self, text: String) {
|
||||
let qr_creation_state = self.qr_creation_state.clone();
|
||||
let qr_state = self.qr_image_state.clone();
|
||||
thread::spawn(move || {
|
||||
if let Ok(qr) = QrCode::encode_text(text.as_str(), qrcodegen::QrCodeEcc::Low) {
|
||||
let svg = Self::qr_to_svg(qr, 0);
|
||||
let mut w_create = qr_creation_state.write();
|
||||
w_create.creating = false;
|
||||
w_create.svg = Some(svg.into_bytes());
|
||||
let mut w_state = qr_state.write();
|
||||
w_state.loading = false;
|
||||
w_state.svg = Some(svg.into_bytes());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -214,9 +316,91 @@ impl QrCodeContent {
|
||||
result
|
||||
}
|
||||
|
||||
/// Create GIF image at separate thread.
|
||||
fn create_qr_gif(&self, text: String, size: usize) {
|
||||
{
|
||||
let mut w_state = self.qr_image_state.write();
|
||||
w_state.gif_creating = true;
|
||||
|
||||
}
|
||||
let qr_state = self.qr_image_state.clone();
|
||||
thread::spawn(move || {
|
||||
// Setup GIF image encoder.
|
||||
let mut gif = vec![];
|
||||
if let Ok(mut gif_enc) = gif::Encoder::new(&mut gif,
|
||||
size as u16,
|
||||
size as u16,
|
||||
&[]) {
|
||||
gif_enc.set_repeat(gif::Repeat::Infinite).unwrap();
|
||||
// Generate QR codes from text.
|
||||
let mut ur_enc = ur::Encoder::bytes(text.as_bytes(), 100).unwrap();
|
||||
for _ in 0..ur_enc.fragment_count() {
|
||||
let ur = ur_enc.next_part().unwrap();
|
||||
if let Ok(qr) = QrCode::encode_text(ur.as_str(), qrcodegen::QrCodeEcc::Low) {
|
||||
// Create an image from QR data and write it to encoder.
|
||||
if let Some(image) = Self::qr_to_image_data(qr, size) {
|
||||
let mut frame = gif::Frame::from_indexed_pixels(size as u16,
|
||||
size as u16,
|
||||
image.as_slice(), None);
|
||||
frame.palette = Some(vec![]);
|
||||
// Write an image to GIF encoder.
|
||||
if let Ok(_) = gif_enc.write_frame(&frame) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Exit on error.
|
||||
let mut w_state = qr_state.write();
|
||||
w_state.gif_creating = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Setup GIF image data.
|
||||
let mut w_state = qr_state.write();
|
||||
if !gif.is_empty() {
|
||||
w_state.gif_data = Some(gif);
|
||||
}
|
||||
w_state.gif_creating = false;
|
||||
});
|
||||
}
|
||||
|
||||
/// Convert QR code to image data.
|
||||
fn qr_to_image_data(qr: QrCode, size: usize) -> Option<Vec<u8>> {
|
||||
if size >= 2usize.pow((size_of::<usize>() * 4) as u32) {
|
||||
return None;
|
||||
}
|
||||
let margin_size = 1;
|
||||
let s = qr.size();
|
||||
let data_length = s as usize;
|
||||
let data_length_with_margin = data_length + 2 * margin_size;
|
||||
let point_size = size / data_length_with_margin;
|
||||
if point_size == 0 {
|
||||
return None;
|
||||
}
|
||||
let margin = (size - (point_size * data_length)) / 2;
|
||||
let length = size * size;
|
||||
let mut img_raw: Vec<u8> = vec![255u8; length];
|
||||
for i in 0..s {
|
||||
for j in 0..s {
|
||||
if qr.get_module(i, j) {
|
||||
let x = i as usize * point_size + margin;
|
||||
let y = j as usize * point_size + margin;
|
||||
|
||||
for j in y..(y + point_size) {
|
||||
let offset = j * size;
|
||||
for i in x..(x + point_size) {
|
||||
img_raw[offset + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(img_raw)
|
||||
}
|
||||
|
||||
/// Reset QR code image content state to default.
|
||||
pub fn clear_state(&mut self) {
|
||||
let mut w_create = self.qr_creation_state.write();
|
||||
*w_create = QrCreationState::default();
|
||||
let mut w_create = self.qr_image_state.write();
|
||||
*w_create = QrImageState::default();
|
||||
}
|
||||
}
|
||||
+23
-12
@@ -175,11 +175,11 @@ impl QrScanResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// QR code scan state.
|
||||
/// QR code scanning state.
|
||||
pub struct QrScanState {
|
||||
// Flag to check if image is processing to find QR code.
|
||||
pub(crate) image_processing: bool,
|
||||
// Found QR code content.
|
||||
/// Flag to check if image is processing to find QR code.
|
||||
pub image_processing: bool,
|
||||
/// Processed QR code result.
|
||||
pub qr_scan_result: Option<QrScanResult>
|
||||
}
|
||||
|
||||
@@ -192,20 +192,31 @@ impl Default for QrScanState {
|
||||
}
|
||||
}
|
||||
|
||||
/// QR code image creation state.
|
||||
pub struct QrCreationState {
|
||||
// Flag to check if QR code image is creating.
|
||||
pub creating: bool,
|
||||
// Vector image data.
|
||||
/// QR code image data state.
|
||||
pub struct QrImageState {
|
||||
/// Flag to check if QR code image is loading.
|
||||
pub loading: bool,
|
||||
/// Flag to check if QR code image is exporting.
|
||||
pub exporting: bool,
|
||||
|
||||
/// Created GIF data from animated QR code.
|
||||
pub gif_data: Option<Vec<u8>>,
|
||||
/// Flag to check if GIF is creating.
|
||||
pub gif_creating: bool,
|
||||
|
||||
/// Vector image data.
|
||||
pub svg: Option<Vec<u8>>,
|
||||
// Multiple vector image data.
|
||||
/// Multiple vector image data for animated QR code.
|
||||
pub svg_list: Option<Vec<Vec<u8>>>
|
||||
}
|
||||
|
||||
impl Default for QrCreationState {
|
||||
impl Default for QrImageState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
creating: false,
|
||||
loading: false,
|
||||
exporting: false,
|
||||
gif_data: None,
|
||||
gif_creating: false,
|
||||
svg: None,
|
||||
svg_list: None,
|
||||
}
|
||||
|
||||
@@ -403,6 +403,8 @@ impl WalletContent {
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
View::horizontal_line(ui, Colors::ITEM_STROKE);
|
||||
ui.add_space(6.0);
|
||||
} else if let Some(result) = self.camera_content.qr_scan_result() {
|
||||
cb.stop_camera();
|
||||
self.camera_content.clear_state();
|
||||
|
||||
@@ -224,7 +224,7 @@ impl WalletMessages {
|
||||
}
|
||||
QR_SLATEPACK_MESSAGE_MODAL => {
|
||||
Modal::ui(ui.ctx(), |ui, modal| {
|
||||
self.qr_message_modal_ui(ui, modal);
|
||||
self.qr_message_modal_ui(ui, modal, cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
@@ -483,11 +483,7 @@ impl WalletMessages {
|
||||
if text.is_empty() {
|
||||
self.request_qr = false;
|
||||
}
|
||||
self.request_qr_content.ui(ui, text.clone());
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show QR code text.
|
||||
View::ellipsize_text(ui, text, 16.0, Colors::INACTIVE_TEXT);
|
||||
self.request_qr_content.ui(ui, text.clone(), cb);
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show button to close modal.
|
||||
@@ -853,7 +849,7 @@ impl WalletMessages {
|
||||
}
|
||||
|
||||
/// Draw QR code Slatepack message image [`Modal`] content.
|
||||
fn qr_message_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) {
|
||||
fn qr_message_modal_ui(&mut self, ui: &mut egui::Ui, m: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Setup title for Slatepack message.
|
||||
@@ -871,11 +867,7 @@ impl WalletMessages {
|
||||
|
||||
// Draw QR code content.
|
||||
let text = self.qr_message_text.clone().unwrap();
|
||||
self.qr_message_content.ui(ui, text.clone());
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show message text.
|
||||
View::ellipsize_text(ui, text, 16.0, Colors::INACTIVE_TEXT);
|
||||
self.qr_message_content.ui(ui, text.clone(), cb);
|
||||
ui.add_space(6.0);
|
||||
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
@@ -884,7 +876,7 @@ impl WalletMessages {
|
||||
self.qr_message_content.clear_state();
|
||||
self.response_edit.clear();
|
||||
self.message_slate = None;
|
||||
modal.close();
|
||||
m.close();
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -187,7 +187,7 @@ impl WalletTransport {
|
||||
}
|
||||
QR_ADDRESS_MODAL => {
|
||||
Modal::ui(ui.ctx(), |ui, modal| {
|
||||
self.qr_address_modal_ui(ui, modal);
|
||||
self.qr_address_modal_ui(ui, modal, cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
@@ -584,22 +584,18 @@ impl WalletTransport {
|
||||
}
|
||||
|
||||
/// Draw QR code image address [`Modal`] content.
|
||||
fn qr_address_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) {
|
||||
fn qr_address_modal_ui(&mut self, ui: &mut egui::Ui, m: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Draw QR code content.
|
||||
let text = self.qr_address_content.text.clone();
|
||||
self.qr_address_content.ui(ui, text.clone());
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show address.
|
||||
View::ellipsize_text(ui, text, 16.0, Colors::GRAY);
|
||||
ui.add_space(6.0);
|
||||
self.qr_address_content.ui(ui, text.clone(), cb);
|
||||
ui.add_space(10.0);
|
||||
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("close"), Colors::WHITE, || {
|
||||
self.qr_address_content.clear_state();
|
||||
modal.close();
|
||||
m.close();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
|
||||
@@ -122,7 +122,7 @@ impl WalletTab for WalletTransactions {
|
||||
.show_inside(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
let data = wallet.get_data().unwrap();
|
||||
self.txs_ui(ui, wallet, &data, cb);
|
||||
self.txs_ui(ui, wallet, &data);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -142,8 +142,7 @@ impl WalletTransactions {
|
||||
fn txs_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
data: &WalletData,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
data: &WalletData) {
|
||||
let amount_conf = data.info.amount_awaiting_confirmation;
|
||||
let amount_fin = data.info.amount_awaiting_finalization;
|
||||
let amount_locked = data.info.amount_locked;
|
||||
@@ -224,7 +223,7 @@ impl WalletTransactions {
|
||||
// Show transaction item.
|
||||
let tx = data.txs.get(index).unwrap();
|
||||
let rounding = View::item_rounding(index, data.txs.len(), false);
|
||||
self.tx_item_ui(ui, tx, rounding, padding, true, &data, wallet, cb);
|
||||
self.tx_item_ui(ui, tx, rounding, padding, true, &data, wallet);
|
||||
}
|
||||
});
|
||||
})
|
||||
@@ -291,8 +290,7 @@ impl WalletTransactions {
|
||||
extra_padding: bool,
|
||||
can_show_info: bool,
|
||||
data: &WalletData,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
wallet: &mut Wallet) {
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
if extra_padding {
|
||||
@@ -541,7 +539,7 @@ impl WalletTransactions {
|
||||
|
||||
// Show transaction amount status and time.
|
||||
let rounding = View::item_rounding(0, 2, false);
|
||||
self.tx_item_ui(ui, tx, rounding, false, false, &data, wallet, cb);
|
||||
self.tx_item_ui(ui, tx, rounding, false, false, &data, wallet);
|
||||
|
||||
// Show transaction ID info.
|
||||
if let Some(id) = tx.data.tx_slate_id {
|
||||
@@ -773,11 +771,7 @@ impl WalletTransactions {
|
||||
self.tx_info_show_qr = false;
|
||||
} else {
|
||||
// Draw QR code content.
|
||||
self.tx_info_qr_code_content.ui(ui, text.clone());
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show QR code text.
|
||||
View::ellipsize_text(ui, text, 16.0, Colors::INACTIVE_TEXT);
|
||||
self.tx_info_qr_code_content.ui(ui, text.clone(), cb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user