mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-04-02 09:35:28 +00:00
normalize tab items component
This commit is contained in:
parent
47e2bc4617
commit
65502c247d
29 changed files with 427 additions and 117 deletions
79
src/app/browser/window/tab/item/page/navigation/base.rs
Normal file
79
src/app/browser/window/tab/item/page/navigation/base.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use gtk::{
|
||||
gio::SimpleAction,
|
||||
glib::{gformat, GString, Uri},
|
||||
prelude::{ActionExt, ButtonExt, WidgetExt},
|
||||
Button,
|
||||
};
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
pub struct Base {
|
||||
// Actions
|
||||
action_tab_page_navigation_base: Arc<SimpleAction>,
|
||||
// Mutable URI cache (parsed on update)
|
||||
uri: RefCell<Option<Uri>>,
|
||||
// GTK
|
||||
widget: Button,
|
||||
}
|
||||
|
||||
impl Base {
|
||||
// Construct
|
||||
pub fn new(action_tab_page_navigation_base: Arc<SimpleAction>) -> Self {
|
||||
// Init widget
|
||||
let widget = Button::builder()
|
||||
.icon_name("go-home-symbolic")
|
||||
.tooltip_text("Base")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
// Init events
|
||||
widget.connect_clicked({
|
||||
let action_tab_page_navigation_base = action_tab_page_navigation_base.clone();
|
||||
move |_| {
|
||||
action_tab_page_navigation_base.activate(None);
|
||||
}
|
||||
});
|
||||
|
||||
// Return activated struct
|
||||
Self {
|
||||
action_tab_page_navigation_base,
|
||||
uri: RefCell::new(None),
|
||||
widget,
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
pub fn update(&self, uri: Option<Uri>) {
|
||||
// Update sensitivity
|
||||
let status = match &uri {
|
||||
Some(uri) => "/" != uri.path(),
|
||||
None => false,
|
||||
};
|
||||
|
||||
self.action_tab_page_navigation_base.set_enabled(status);
|
||||
self.widget.set_sensitive(status);
|
||||
|
||||
// Update parsed cache
|
||||
self.uri.replace(uri);
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn widget(&self) -> &Button {
|
||||
&self.widget
|
||||
}
|
||||
|
||||
pub fn url(&self) -> Option<GString> {
|
||||
// Build URL from parsed URI cache
|
||||
if let Some(uri) = self.uri.take() {
|
||||
let scheme = uri.scheme();
|
||||
let port = uri.port();
|
||||
if let Some(host) = uri.host() {
|
||||
if port.is_positive() {
|
||||
return Some(gformat!("{scheme}://{host}:{port}/"));
|
||||
} else {
|
||||
return Some(gformat!("{scheme}://{host}/"));
|
||||
} // @TODO auth params
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
28
src/app/browser/window/tab/item/page/navigation/bookmark.rs
Normal file
28
src/app/browser/window/tab/item/page/navigation/bookmark.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use gtk::Button;
|
||||
|
||||
pub struct Bookmark {
|
||||
widget: Button,
|
||||
}
|
||||
|
||||
impl Bookmark {
|
||||
// Construct
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
widget: Button::builder()
|
||||
.icon_name("starred-symbolic")
|
||||
.tooltip_text("Bookmark")
|
||||
.sensitive(false)
|
||||
.build(),
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
pub fn update(&self) {
|
||||
// @TODO
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn widget(&self) -> &Button {
|
||||
&self.widget
|
||||
}
|
||||
}
|
||||
139
src/app/browser/window/tab/item/page/navigation/history.rs
Normal file
139
src/app/browser/window/tab/item/page/navigation/history.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
mod back;
|
||||
mod forward;
|
||||
|
||||
use back::Back;
|
||||
use forward::Forward;
|
||||
|
||||
use gtk::{gio::SimpleAction, glib::GString, prelude::BoxExt, Box, Orientation};
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
struct Memory {
|
||||
request: GString,
|
||||
// time: SystemTime,
|
||||
}
|
||||
|
||||
pub struct History {
|
||||
// Components
|
||||
back: Back,
|
||||
forward: Forward,
|
||||
// Extras
|
||||
memory: RefCell<Vec<Memory>>,
|
||||
index: RefCell<Option<usize>>,
|
||||
// GTK
|
||||
widget: Box,
|
||||
}
|
||||
|
||||
impl History {
|
||||
// Construct
|
||||
pub fn new(
|
||||
action_tab_page_navigation_history_back: Arc<SimpleAction>,
|
||||
action_tab_page_navigation_history_forward: Arc<SimpleAction>,
|
||||
) -> Self {
|
||||
// init components
|
||||
let back = Back::new(action_tab_page_navigation_history_back);
|
||||
let forward = Forward::new(action_tab_page_navigation_history_forward);
|
||||
|
||||
// Init widget
|
||||
let widget = Box::builder()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.css_classes([
|
||||
"linked", // merge childs
|
||||
])
|
||||
.build();
|
||||
|
||||
widget.append(back.widget());
|
||||
widget.append(forward.widget());
|
||||
|
||||
// Init memory
|
||||
let memory = RefCell::new(Vec::new());
|
||||
|
||||
// Init index
|
||||
let index = RefCell::new(None);
|
||||
|
||||
Self {
|
||||
// Actions
|
||||
back,
|
||||
forward,
|
||||
// Extras
|
||||
memory,
|
||||
index,
|
||||
// GTK
|
||||
widget,
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
pub fn add(&self, request: GString, follow_to_index: bool) {
|
||||
// Append new Memory record
|
||||
self.memory.borrow_mut().push(Memory {
|
||||
request: request.clone(),
|
||||
//time: SystemTime::now(),
|
||||
});
|
||||
|
||||
if follow_to_index {
|
||||
// Even push action make positive len value, make sure twice
|
||||
if !self.memory.borrow().is_empty() {
|
||||
// Navigate to the last record appended
|
||||
self.index.replace(Some(self.memory.borrow().len() - 1));
|
||||
} else {
|
||||
self.index.replace(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn back(&self, follow_to_index: bool) -> Option<GString> {
|
||||
let index = self.index.borrow().clone(); // keep outside as borrow
|
||||
if let Some(usize) = index {
|
||||
// Make sure value positive to prevent panic
|
||||
if usize > 0 {
|
||||
if let Some(memory) = self.memory.borrow().get(usize - 1) {
|
||||
if follow_to_index {
|
||||
self.index.replace(Some(usize - 1));
|
||||
}
|
||||
return Some(memory.request.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Option<GString> {
|
||||
let index = self.index.borrow().clone(); // keep outside as borrow
|
||||
if let Some(usize) = index {
|
||||
if let Some(memory) = self.memory.borrow().get(usize) {
|
||||
return Some(memory.request.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn forward(&self, follow_to_index: bool) -> Option<GString> {
|
||||
let index = self.index.borrow().clone(); // keep outside as borrow
|
||||
if let Some(usize) = index {
|
||||
if let Some(memory) = self.memory.borrow().get(usize + 1) {
|
||||
if follow_to_index {
|
||||
self.index.replace(Some(usize + 1));
|
||||
}
|
||||
return Some(memory.request.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
match self.back(false) {
|
||||
Some(_) => self.back.update(true),
|
||||
None => self.back.update(false),
|
||||
};
|
||||
|
||||
match self.forward(false) {
|
||||
Some(_) => self.forward.update(true),
|
||||
None => self.forward.update(false),
|
||||
};
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn widget(&self) -> &Box {
|
||||
&self.widget
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
use gtk::{
|
||||
gio::SimpleAction,
|
||||
prelude::{ActionExt, ButtonExt, WidgetExt},
|
||||
Button,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Back {
|
||||
action_tab_page_navigation_history_back: Arc<SimpleAction>,
|
||||
widget: Button,
|
||||
}
|
||||
|
||||
impl Back {
|
||||
// Construct
|
||||
pub fn new(action_tab_page_navigation_history_back: Arc<SimpleAction>) -> Self {
|
||||
// Init widget
|
||||
let widget = Button::builder()
|
||||
.icon_name("go-previous-symbolic")
|
||||
.tooltip_text("Back")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
// Init events
|
||||
widget.connect_clicked({
|
||||
let action_tab_page_navigation_history_back =
|
||||
action_tab_page_navigation_history_back.clone();
|
||||
move |_| {
|
||||
action_tab_page_navigation_history_back.activate(None);
|
||||
}
|
||||
});
|
||||
|
||||
// Return activated struct
|
||||
Self {
|
||||
action_tab_page_navigation_history_back,
|
||||
widget,
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
pub fn update(&self, status: bool) {
|
||||
self.action_tab_page_navigation_history_back
|
||||
.set_enabled(status);
|
||||
self.widget.set_sensitive(status);
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn widget(&self) -> &Button {
|
||||
&self.widget
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
use gtk::{
|
||||
prelude::{ActionExt, ButtonExt, WidgetExt},
|
||||
{gio::SimpleAction, Button},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Forward {
|
||||
action_tab_page_navigation_history_forward: Arc<SimpleAction>,
|
||||
widget: Button,
|
||||
}
|
||||
|
||||
impl Forward {
|
||||
// Construct
|
||||
pub fn new(action_tab_page_navigation_history_forward: Arc<SimpleAction>) -> Self {
|
||||
// Init widget
|
||||
let widget = Button::builder()
|
||||
.icon_name("go-next-symbolic")
|
||||
.tooltip_text("Forward")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
// Init events
|
||||
widget.connect_clicked({
|
||||
let action_tab_page_navigation_history_forward =
|
||||
action_tab_page_navigation_history_forward.clone();
|
||||
move |_| {
|
||||
action_tab_page_navigation_history_forward.activate(None);
|
||||
}
|
||||
});
|
||||
|
||||
// Return activated struct
|
||||
Self {
|
||||
action_tab_page_navigation_history_forward,
|
||||
widget,
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
pub fn update(&self, status: bool) {
|
||||
self.action_tab_page_navigation_history_forward
|
||||
.set_enabled(status);
|
||||
self.widget.set_sensitive(status);
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn widget(&self) -> &Button {
|
||||
&self.widget
|
||||
}
|
||||
}
|
||||
49
src/app/browser/window/tab/item/page/navigation/reload.rs
Normal file
49
src/app/browser/window/tab/item/page/navigation/reload.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use gtk::{
|
||||
gio::SimpleAction,
|
||||
prelude::{ActionExt, ButtonExt, WidgetExt},
|
||||
Button,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Reload {
|
||||
action_tab_page_navigation_reload: Arc<SimpleAction>,
|
||||
widget: Button,
|
||||
}
|
||||
|
||||
impl Reload {
|
||||
// Construct
|
||||
pub fn new(action_tab_page_navigation_reload: Arc<SimpleAction>) -> Self {
|
||||
// Init widget
|
||||
let widget = Button::builder()
|
||||
.icon_name("view-refresh-symbolic")
|
||||
.tooltip_text("Reload")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
// Init events
|
||||
widget.connect_clicked({
|
||||
let action_tab_page_navigation_reload = action_tab_page_navigation_reload.clone();
|
||||
move |_| {
|
||||
action_tab_page_navigation_reload.activate(None);
|
||||
}
|
||||
});
|
||||
|
||||
// Return activated struct
|
||||
Self {
|
||||
action_tab_page_navigation_reload,
|
||||
widget,
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
pub fn update(&self, is_enabled: bool) {
|
||||
self.action_tab_page_navigation_reload
|
||||
.set_enabled(is_enabled);
|
||||
self.widget.set_sensitive(is_enabled);
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn widget(&self) -> &Button {
|
||||
&self.widget
|
||||
}
|
||||
}
|
||||
129
src/app/browser/window/tab/item/page/navigation/request.rs
Normal file
129
src/app/browser/window/tab/item/page/navigation/request.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
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 = 20; //ms
|
||||
|
||||
struct Progress {
|
||||
fraction: RefCell<f64>,
|
||||
source_id: RefCell<Option<SourceId>>,
|
||||
}
|
||||
|
||||
// Main
|
||||
pub struct Request {
|
||||
progress: Arc<Progress>,
|
||||
widget: Entry,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
// Construct
|
||||
pub fn new(
|
||||
text: Option<GString>,
|
||||
// Actions
|
||||
action_update: Arc<SimpleAction>,
|
||||
action_tab_page_navigation_reload: Arc<SimpleAction>, // @TODO local `action_page_open`?
|
||||
) -> 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_navigation_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<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 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 (to hide progress widget)
|
||||
widget.set_progress_fraction(0.0);
|
||||
|
||||
// Stop iteration
|
||||
ControlFlow::Break
|
||||
}
|
||||
},
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setters
|
||||
pub fn set_text(&self, value: &GString) {
|
||||
self.widget.set_text(value);
|
||||
}
|
||||
|
||||
// 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<Uri> {
|
||||
match Uri::parse(&self.widget.text(), UriFlags::NONE) {
|
||||
Ok(uri) => Some(uri),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue