mod action; mod client; mod database; mod page; use super::{Action as TabAction, WindowAction}; use crate::Profile; use action::Action; use adw::TabPage; use anyhow::Result; use client::Client; use gtk::{ Box, prelude::{ActionExt, ActionMapExt, BoxExt, EditableExt}, }; use page::Page; use sqlite::Transaction; use std::rc::Rc; pub struct Item { // Multi-protocol handler pub client: Rc, // Components pub page: Rc, pub action: Rc, pub tab_page: TabPage, } impl Item { // Constructors /// Build new `Self` pub fn build( (tab_page, target_child): (&TabPage, &Box), profile: &Rc, (window_action, tab_action): (&Rc, &Rc), request: Option<&str>, is_load: bool, ) -> Self { // Init components let action = Rc::new(Action::new()); tab_action.simple_action_group.add_action(&action.home); tab_action.simple_action_group.add_action(&action.reload); tab_action.simple_action_group.add_action(&action.identity); tab_action .simple_action_group .add_action(&action.history.back); tab_action .simple_action_group .add_action(&action.history.forward); // Create new `Page` implementation for `TabPage` let page = Rc::new(Page::build( profile, (window_action, tab_action, &action), tab_page, )); target_child.append(&page.navigation.g_box); target_child.append(&page.content.g_box); target_child.append(&page.search.g_box); target_child.append(&page.input.clamp); // Update tab loading indicator let client = Rc::new(Client::init(profile, &page)); // Connect events action.home.connect_enabled_notify({ let window_action = window_action.clone(); move |this| { window_action .home .simple_action .set_enabled(this.is_enabled()) } }); action.home.connect_activate({ let client = client.clone(); let page = page.clone(); move |this, _| { this.set_enabled(false); match page.navigation.request.home() { Some(uri) => { let request = uri.to_string(); // prevent `changed` event extra emission // but make sure the entry is always up-to-date if page.navigation.request.entry.text() != request { page.navigation.request.entry.set_text(&request) } client.handle(&request, true, false) } None => panic!(), // unexpected } } }); action.load.connect_activate({ let c = client.clone(); let e = page.navigation.request.entry.clone(); move |request, is_snap_history, is_redirect| { match request { Some(request) => { // prevent `changed` event extra emission // but make sure the entry is always up-to-date if e.text() != request { e.set_text(&request) } c.handle(&request, is_snap_history, is_redirect) } None => panic!(), // unexpected } } }); action.identity.connect_activate({ let page = page.clone(); move |_, _| page.navigation.show_identity_dialog() }); action.reload.connect_activate({ let page = page.clone(); let client = client.clone(); move |_, _| client.handle(&page.navigation.request.entry.text(), true, false) }); action.reload.connect_enabled_notify({ let window_action = window_action.clone(); move |this| { window_action .reload .simple_action .set_enabled(this.is_enabled()) } }); action.history.back.connect_enabled_notify({ let window_action = window_action.clone(); move |this| { window_action .history_back .simple_action .set_enabled(this.is_enabled()) } }); action.history.forward.connect_enabled_notify({ let window_action = window_action.clone(); move |this| { window_action .history_forward .simple_action .set_enabled(this.is_enabled()) } }); // Handle immediately on request if is_load && let Some(request) = request { // prevent `changed` event extra emission // but make sure the entry is always up-to-date if page.navigation.request.entry.text() != request { page.navigation.request.entry.set_text(request) } client.handle(request, true, false) } Self { client, page, action, tab_page: tab_page.clone(), } } pub fn clean(&self, transaction: &Transaction, app_browser_window_tab_id: i64) -> Result<()> { for record in database::select(transaction, app_browser_window_tab_id)? { database::delete(transaction, record.id)?; // Delegate clean action to the item childs self.page.clean(transaction, record.id)?; } Ok(()) } pub fn save( &self, transaction: &Transaction, app_browser_window_tab_id: i64, page_position: i32, ) -> Result<()> { let id = database::insert( transaction, app_browser_window_tab_id, page_position, self.tab_page.is_pinned(), self.tab_page.is_selected(), )?; self.page.save(transaction, id)?; Ok(()) } } // Tools pub fn migrate(tx: &Transaction) -> Result<()> { // Migrate self components database::init(tx)?; // Delegate migration to childs page::migrate(tx)?; // Success Ok(()) } // This feature restore require parental implementation // * see `super::Tab::restore()` pub fn restore( transaction: &Transaction, app_browser_window_tab_id: i64, ) -> Result> { database::select(transaction, app_browser_window_tab_id) }