mirror of https://github.com/YGGverse/Yoda.git
yggverse
2 months ago
6 changed files with 337 additions and 119 deletions
@ -0,0 +1,8 @@ |
|||||||
|
/* @TODO |
||||||
|
* not in use as defined inline: |
||||||
|
* src/browser/main/tab/page/content/text/gemini/reader.rs |
||||||
|
*/ |
||||||
|
label |
||||||
|
{ |
||||||
|
caret-color: transparent; |
||||||
|
} |
@ -0,0 +1,214 @@ |
|||||||
|
mod database; |
||||||
|
|
||||||
|
use database::Database; |
||||||
|
|
||||||
|
use gtk::{ |
||||||
|
gio::SimpleAction, |
||||||
|
glib::{timeout_add_local, ControlFlow, GString, SourceId}, |
||||||
|
prelude::{ActionExt, EditableExt, EntryExt}, |
||||||
|
Entry, |
||||||
|
}; |
||||||
|
use sqlite::Transaction; |
||||||
|
use std::{cell::RefCell, sync::Arc, time::Duration}; |
||||||
|
|
||||||
|
const PLACEHOLDER_TEXT: &str = "URL or search term..."; |
||||||
|
|
||||||
|
// Progress bar animation setup
|
||||||
|
const PROGRESS_ANIMATION_STEP: f64 = 0.05; |
||||||
|
const PROGRESS_ANIMATION_TIME: u64 = 20; //ms
|
||||||
|
|
||||||
|
struct Progress { |
||||||
|
fraction: RefCell<f64>, |
||||||
|
source_id: RefCell<Option<SourceId>>, |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Widget { |
||||||
|
gobject: Entry, |
||||||
|
progress: Arc<Progress>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Widget { |
||||||
|
// Construct
|
||||||
|
pub fn new_arc( |
||||||
|
action_update: Arc<SimpleAction>, |
||||||
|
action_tab_page_navigation_reload: Arc<SimpleAction>, // @TODO local `action_page_open`?
|
||||||
|
) -> Arc<Self> { |
||||||
|
// Init animated progress bar state
|
||||||
|
let progress = Arc::new(Progress { |
||||||
|
fraction: RefCell::new(0.0), |
||||||
|
source_id: RefCell::new(None), |
||||||
|
}); |
||||||
|
|
||||||
|
// Init widget
|
||||||
|
let gobject = Entry::builder() |
||||||
|
.placeholder_text(PLACEHOLDER_TEXT) |
||||||
|
.hexpand(true) |
||||||
|
.build(); |
||||||
|
|
||||||
|
// Connect events
|
||||||
|
gobject.connect_changed(move |_| { |
||||||
|
action_update.activate(None); |
||||||
|
}); |
||||||
|
|
||||||
|
gobject.connect_activate(move |_| { |
||||||
|
action_tab_page_navigation_reload.activate(None); |
||||||
|
}); |
||||||
|
|
||||||
|
// Return activated struct
|
||||||
|
Arc::new(Self { gobject, progress }) |
||||||
|
} |
||||||
|
|
||||||
|
// Actions
|
||||||
|
pub fn clean( |
||||||
|
&self, |
||||||
|
transaction: &Transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id: &i64, |
||||||
|
) -> Result<(), String> { |
||||||
|
match Database::records( |
||||||
|
transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id, |
||||||
|
) { |
||||||
|
Ok(records) => { |
||||||
|
for record in records { |
||||||
|
match Database::delete(transaction, &record.id) { |
||||||
|
Ok(_) => { |
||||||
|
// Delegate clean action to the item childs
|
||||||
|
// nothing yet..
|
||||||
|
} |
||||||
|
Err(e) => return Err(e.to_string()), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Err(e) => return Err(e.to_string()), |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn restore( |
||||||
|
&self, |
||||||
|
transaction: &Transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id: &i64, |
||||||
|
) -> Result<(), String> { |
||||||
|
match Database::records( |
||||||
|
transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id, |
||||||
|
) { |
||||||
|
Ok(records) => { |
||||||
|
for record in records { |
||||||
|
if let Some(text) = record.text { |
||||||
|
self.gobject.set_text(&text); |
||||||
|
} |
||||||
|
|
||||||
|
// Delegate restore action to the item childs
|
||||||
|
// nothing yet..
|
||||||
|
} |
||||||
|
} |
||||||
|
Err(e) => return Err(e.to_string()), |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn save( |
||||||
|
&self, |
||||||
|
transaction: &Transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id: &i64, |
||||||
|
) -> Result<(), String> { |
||||||
|
// Keep value in memory until operation complete
|
||||||
|
let text = self.gobject.text(); |
||||||
|
|
||||||
|
match Database::add( |
||||||
|
transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id, |
||||||
|
match text.is_empty() { |
||||||
|
true => None, |
||||||
|
false => Some(text.as_str()), |
||||||
|
}, |
||||||
|
) { |
||||||
|
Ok(_) => { |
||||||
|
// let id = Database::last_insert_id(transaction);
|
||||||
|
|
||||||
|
// Delegate save action to childs
|
||||||
|
// nothing yet..
|
||||||
|
} |
||||||
|
Err(e) => return Err(e.to_string()), |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn update(&self, progress_fraction: Option<f64>) { |
||||||
|
// Skip update animation for Non value
|
||||||
|
if let Some(value) = progress_fraction { |
||||||
|
// Update shared fraction value for async progressbar function, animate on changed only
|
||||||
|
if value != self.progress.fraction.replace(value) { |
||||||
|
// Start new frame on previous process function completed (`source_id` changed to 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 gobject = self.gobject.clone(); |
||||||
|
let progress = self.progress.clone(); |
||||||
|
|
||||||
|
// Frame
|
||||||
|
move || { |
||||||
|
// Animate
|
||||||
|
if *progress.fraction.borrow() > gobject.progress_fraction() { |
||||||
|
gobject.set_progress_fraction( |
||||||
|
// Currently, here is no outrange validation, seems that wrapper make this work @TODO
|
||||||
|
gobject.progress_fraction() + PROGRESS_ANIMATION_STEP, |
||||||
|
); |
||||||
|
return ControlFlow::Continue; |
||||||
|
} |
||||||
|
// Deactivate
|
||||||
|
progress.source_id.replace(None); |
||||||
|
|
||||||
|
// Reset (to hide progress widget)
|
||||||
|
gobject.set_progress_fraction(0.0); |
||||||
|
|
||||||
|
// Stop iteration
|
||||||
|
ControlFlow::Break |
||||||
|
} |
||||||
|
}, |
||||||
|
))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Setters
|
||||||
|
pub fn set_text(&self, value: &GString) { |
||||||
|
self.gobject.set_text(value); |
||||||
|
} |
||||||
|
|
||||||
|
// Getters
|
||||||
|
pub fn gobject(&self) -> &Entry { |
||||||
|
&self.gobject |
||||||
|
} |
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool { |
||||||
|
0 == self.gobject.text_length() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn text(&self) -> GString { |
||||||
|
self.gobject.text() |
||||||
|
} |
||||||
|
|
||||||
|
// Tools
|
||||||
|
pub fn migrate(tx: &Transaction) -> Result<(), String> { |
||||||
|
// Migrate self components
|
||||||
|
if let Err(e) = Database::init(&tx) { |
||||||
|
return Err(e.to_string()); |
||||||
|
} |
||||||
|
|
||||||
|
// Delegate migration to childs
|
||||||
|
// nothing yet..
|
||||||
|
|
||||||
|
// Success
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
use sqlite::{Error, Transaction}; |
||||||
|
|
||||||
|
pub struct Table { |
||||||
|
pub id: i64, |
||||||
|
// pub app_browser_window_tab_item_page_navigation_request_id: i64, not in use
|
||||||
|
pub text: Option<String>, // can be stored as NULL
|
||||||
|
} |
||||||
|
|
||||||
|
pub struct Database { |
||||||
|
// nothing yet..
|
||||||
|
} |
||||||
|
|
||||||
|
impl Database { |
||||||
|
pub fn init(tx: &Transaction) -> Result<usize, Error> { |
||||||
|
tx.execute( |
||||||
|
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page_navigation_request_widget` |
||||||
|
( |
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
||||||
|
`app_browser_window_tab_item_page_navigation_request_id` INTEGER NOT NULL, |
||||||
|
`text` VARCHAR(1024) |
||||||
|
)", |
||||||
|
[], |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn add( |
||||||
|
tx: &Transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id: &i64, |
||||||
|
text: Option<&str>, |
||||||
|
) -> Result<usize, Error> { |
||||||
|
tx.execute( |
||||||
|
"INSERT INTO `app_browser_window_tab_item_page_navigation_request_widget` ( |
||||||
|
`app_browser_window_tab_item_page_navigation_request_id`, |
||||||
|
`text` |
||||||
|
) VALUES (?, ?)", |
||||||
|
(app_browser_window_tab_item_page_navigation_request_id, text), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn records( |
||||||
|
tx: &Transaction, |
||||||
|
app_browser_window_tab_item_page_navigation_request_id: &i64, |
||||||
|
) -> Result<Vec<Table>, Error> { |
||||||
|
let mut stmt = tx.prepare( |
||||||
|
"SELECT `id`, |
||||||
|
`app_browser_window_tab_item_page_navigation_request_id`, |
||||||
|
`text` |
||||||
|
FROM `app_browser_window_tab_item_page_navigation_request_widget` |
||||||
|
WHERE `app_browser_window_tab_item_page_navigation_request_id` = ?", |
||||||
|
)?; |
||||||
|
|
||||||
|
let result = stmt.query_map( |
||||||
|
[app_browser_window_tab_item_page_navigation_request_id], |
||||||
|
|row| { |
||||||
|
Ok(Table { |
||||||
|
id: row.get(0)?, |
||||||
|
// app_browser_window_tab_item_page_navigation_request_id: row.get(1)?, not in use
|
||||||
|
text: row.get(2)?, |
||||||
|
}) |
||||||
|
}, |
||||||
|
)?; |
||||||
|
|
||||||
|
let mut records = Vec::new(); |
||||||
|
|
||||||
|
for record in result { |
||||||
|
let table = record?; |
||||||
|
records.push(table); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(records) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn delete(tx: &Transaction, id: &i64) -> Result<usize, Error> { |
||||||
|
tx.execute( |
||||||
|
"DELETE FROM `app_browser_window_tab_item_page_navigation_request_widget` WHERE `id` = ?", |
||||||
|
[id], |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
/* not in use
|
||||||
|
pub fn last_insert_id(tx: &Transaction) -> i64 { |
||||||
|
tx.last_insert_rowid() |
||||||
|
} */ |
||||||
|
} |
Loading…
Reference in new issue