use gtk::{ gio::SimpleAction, glib::{timeout_add_local, ControlFlow, GString, SourceId, Uri, UriFlags}, prelude::{ActionExt, EditableExt, EntryExt}, Entry, }; use std::{cell::RefCell, sync::Arc, time::Duration}; // Progressbar animation setup const PROGRESS_ANIMATION_STEP: f64 = 0.05; const PROGRESS_ANIMATION_TIME: u64 = 25; //ms struct Progress { fraction: RefCell, source_id: RefCell>, } // Main pub struct Request { progress: Arc, widget: Entry, } impl Request { // Construct pub fn new( text: Option, // Actions action_update: Arc, action_tab_page_reload: Arc, ) -> Self { // GTK let widget = Entry::builder() .placeholder_text("URL or search term...") .hexpand(true) .text(match text { Some(text) => text, None => GString::new(), }) .build(); // Connect events widget.connect_changed(move |_| { action_update.activate(None); }); widget.connect_activate(move |_| { action_tab_page_reload.activate(None); }); // Init animated progressbar state let progress = Arc::new(Progress { fraction: RefCell::new(0.0), source_id: RefCell::new(None), }); // Result Self { progress, widget } } // Actions pub fn update(&self, progress_fraction: Option) { // Skip Non value if let Some(value) = progress_fraction { // Update shared fraction value for async progressbar animation self.progress.fraction.replace(value); // Start new frame on previous process completed only (`source_id` is None) // If previous process still active, we have just updated shared fraction value before, to use it inside the active process if self.progress.source_id.borrow().is_none() { // Start new animation frame iterator, update `source_id` self.progress.source_id.replace(Some(timeout_add_local( Duration::from_millis(PROGRESS_ANIMATION_TIME), { // Clone async pointers dependency let widget = self.widget.clone(); let progress = self.progress.clone(); // Frame move || { // Animate if *progress.fraction.borrow() > widget.progress_fraction() { widget.set_progress_fraction( // Currently, here is no outrange validation, seems that wrapper make this work @TODO widget.progress_fraction() + PROGRESS_ANIMATION_STEP, ); return ControlFlow::Continue; } // Deactivate progress.source_id.replace(None); // Reset widget.set_progress_fraction(0.0); // Stop iteration ControlFlow::Break } }, ))); } } } // Setters pub fn set_text(&self, value: &GString, activate: bool) { self.widget.set_text(value); if activate { self.widget.emit_activate(); } } // Getters pub fn widget(&self) -> &Entry { &self.widget } pub fn is_empty(&self) -> bool { 0 == self.widget.text_length() } pub fn text(&self) -> GString { self.widget.text() } pub fn uri(&self) -> Option { match Uri::parse(&self.widget.text(), UriFlags::NONE) { Ok(uri) => Some(uri), _ => None, } } }