begin request entry refactory

This commit is contained in:
yggverse 2025-01-26 19:58:24 +02:00
parent 5255708be3
commit e7bd5bbdc6
24 changed files with 351 additions and 849 deletions

View file

@ -3,6 +3,7 @@ use sqlite::{Error, Transaction};
pub struct Table {
pub id: i64,
// pub app_browser_window_tab_item_page_navigation_id: i64, not in use
pub text: Option<String>, // can be stored as NULL
}
pub fn init(tx: &Transaction) -> Result<usize, Error> {
@ -11,6 +12,7 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
(
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`app_browser_window_tab_item_page_navigation_id` INTEGER NOT NULL,
`text` VARCHAR(1024),
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_id`) REFERENCES `app_browser_window_tab_item_page_navigation`(`id`)
)",
@ -21,12 +23,14 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
pub fn insert(
tx: &Transaction,
app_browser_window_tab_item_page_navigation_id: &i64,
text: Option<&str>,
) -> Result<usize, Error> {
tx.execute(
"INSERT INTO `app_browser_window_tab_item_page_navigation_request` (
`app_browser_window_tab_item_page_navigation_id`
) VALUES (?)",
[app_browser_window_tab_item_page_navigation_id],
`app_browser_window_tab_item_page_navigation_id`,
`text`
) VALUES (?, ?)",
(app_browser_window_tab_item_page_navigation_id, text),
)
}
@ -36,7 +40,8 @@ pub fn select(
) -> Result<Vec<Table>, Error> {
let mut stmt = tx.prepare(
"SELECT `id`,
`app_browser_window_tab_item_page_navigation_id`
`app_browser_window_tab_item_page_navigation_id`,
`text`
FROM `app_browser_window_tab_item_page_navigation_request`
WHERE `app_browser_window_tab_item_page_navigation_id` = ?",
)?;
@ -45,6 +50,7 @@ pub fn select(
Ok(Table {
id: row.get(0)?,
// app_browser_window_tab_item_page_navigation_id: row.get(1)?, not in use
text: row.get(2)?,
})
})?;
@ -65,6 +71,7 @@ pub fn delete(tx: &Transaction, id: &i64) -> Result<usize, Error> {
)
}
/* not in use
pub fn last_insert_id(tx: &Transaction) -> i64 {
tx.last_insert_rowid()
}
} */

View file

@ -1,5 +0,0 @@
#[test]
fn strip_prefix() {
assert_eq!(super::strip_prefix("source:gemini".into()), "gemini");
assert_eq!(super::strip_prefix("download:gemini".into()), "gemini");
}

View file

@ -1,286 +0,0 @@
mod database;
mod primary_icon;
use primary_icon::PrimaryIcon;
use super::{BrowserAction, ItemAction};
use gtk::{
glib::{timeout_add_local, ControlFlow, SourceId},
prelude::{EditableExt, EntryExt, WidgetExt},
Entry, EntryIconPosition, StateFlags,
};
use sqlite::Transaction;
use std::{
cell::{Cell, RefCell},
rc::Rc,
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 {
pub entry: Entry,
progress: Rc<Progress>,
}
impl Widget {
// Constructors
/// Build new `Self`
pub fn build((browser_action, item_action): (&Rc<BrowserAction>, &Rc<ItemAction>)) -> Self {
// Init animated progress bar state
let progress = Rc::new(Progress {
fraction: RefCell::new(0.0),
source_id: RefCell::new(None),
});
// Init main widget
let entry = Entry::builder()
.placeholder_text(PLACEHOLDER_TEXT)
.secondary_icon_tooltip_text("Go to the location")
.hexpand(true)
.build();
// Connect events
entry.connect_icon_release({
let item_action = item_action.clone();
move |this, position| match position {
EntryIconPosition::Primary => item_action.ident.activate(), // @TODO PrimaryIcon impl
EntryIconPosition::Secondary => item_action.load.activate(Some(&this.text()), true),
_ => todo!(), // unexpected
}
});
entry.connect_has_focus_notify(|this| {
if this.focus_child().is_some_and(|text| text.has_focus()) {
this.set_secondary_icon_name(Some("pan-end-symbolic"));
} else {
this.set_secondary_icon_name(None);
this.select_region(0, 0);
}
});
entry.connect_changed({
let browser_action = browser_action.clone();
move |_| {
browser_action.update.activate(None);
}
});
entry.connect_activate({
let item_action = item_action.clone();
move |entry| {
item_action.load.activate(Some(&entry.text()), true);
}
});
entry.connect_state_flags_changed({
// Define last focus state container
let has_focus = Cell::new(false);
move |entry, state| {
// Select entire text on first click (release)
// this behavior implemented in most web-browsers,
// to simply overwrite current request with new value
// Note:
// * Custom GestureClick is not an option here, as GTK Entry has default controller
// * This is experimental feature does not follow native GTK behavior @TODO make optional
if !has_focus.take()
&& state.contains(StateFlags::ACTIVE | StateFlags::FOCUS_WITHIN)
&& entry.selection_bounds().is_none()
{
entry.select_region(0, entry.text_length().into());
}
// Update last focus state
has_focus.replace(state.contains(StateFlags::FOCUS_WITHIN));
}
});
// Return activated `Self`
Self { entry, progress }
}
// Actions
pub fn clean(
&self,
transaction: &Transaction,
app_browser_window_tab_item_page_navigation_request_id: &i64,
) -> Result<(), String> {
match database::select(
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::select(
transaction,
app_browser_window_tab_item_page_navigation_request_id,
) {
Ok(records) => {
for record in records {
if let Some(text) = record.text {
self.entry.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.entry.text();
match database::insert(
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, is_identity_active: bool) {
// Update primary icon
self.entry
.first_child()
.unwrap()
.remove_css_class("success"); // @TODO handle
self.entry.set_primary_icon_activatable(false);
self.entry.set_primary_icon_sensitive(false);
match primary_icon::from(&self.entry.text()) {
PrimaryIcon::Download { name, tooltip } => {
self.entry.set_primary_icon_name(Some(name));
self.entry.set_primary_icon_tooltip_text(Some(tooltip));
}
PrimaryIcon::Gemini { name, tooltip } | PrimaryIcon::Titan { name, tooltip } => {
self.entry.set_primary_icon_activatable(true);
self.entry.set_primary_icon_sensitive(true);
self.entry.set_primary_icon_name(Some(name));
if is_identity_active {
self.entry.first_child().unwrap().add_css_class("success"); // @TODO handle
self.entry.set_primary_icon_tooltip_text(Some(tooltip.1));
} else {
self.entry.set_primary_icon_tooltip_text(Some(tooltip.0));
}
}
PrimaryIcon::Search { name, tooltip } => {
self.entry.set_primary_icon_name(Some(name));
self.entry.set_primary_icon_tooltip_text(Some(tooltip));
}
PrimaryIcon::Source { name, tooltip } => {
self.entry.set_primary_icon_name(Some(name));
self.entry.set_primary_icon_tooltip_text(Some(tooltip));
}
}
// Update progress
// * @TODO skip update animation for None value
let value = self.entry.progress_fraction();
// Update shared fraction on new value was changed
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 entry = self.entry.clone();
let progress = self.progress.clone();
// Frame
move || {
// Animate
if *progress.fraction.borrow() > entry.progress_fraction() {
entry.set_progress_fraction(
// Currently, here is no outrange validation, seems that wrapper make this work @TODO
entry.progress_fraction() + PROGRESS_ANIMATION_STEP,
);
return ControlFlow::Continue;
}
// Deactivate
progress.source_id.replace(None);
// Reset on 100% (to hide progress bar)
// or, just await for new value request
if entry.progress_fraction() == 1.0 {
entry.set_progress_fraction(0.0);
}
// Stop iteration
ControlFlow::Break
}
},
)));
}
}
}
}
// 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(())
}

View file

@ -1,80 +0,0 @@
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 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),
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_request_id`) REFERENCES `app_browser_window_tab_item_page_navigation_request`(`id`)
)",
[],
)
}
pub fn insert(
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 select(
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()
} */