diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find.rs index 862a24a1..6ffe9eae 100644 --- a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find.rs +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find.rs @@ -1,9 +1,18 @@ +mod close; +mod entry; +mod match_case; +mod navigation; +mod tag; + +use navigation::Navigation; +use tag::Tag; + use gtk::{ - gdk::{Cursor, RGBA}, prelude::{BoxExt, ButtonExt, CheckButtonExt, EditableExt, EntryExt, TextBufferExt, WidgetExt}, - Box, Button, CheckButton, Entry, EntryIconPosition, Orientation, TextBuffer, TextIter, - TextSearchFlags, TextTag, + Box, Button, Entry, EntryIconPosition, Orientation, TextBuffer, TextIter, TextSearchFlags, + TextTag, }; +use std::{cell::Cell, rc::Rc}; const MARGIN: i32 = 6; @@ -16,65 +25,15 @@ pub struct Find { impl Find { // Construct pub fn new(text_buffer: &TextBuffer) -> Self { + // Init shared matches holder + let matches = Rc::new(Cell::new(Vec::<(TextIter, TextIter)>::new())); + // Init components - let close = Button::builder() - .cursor(&Cursor::from_name("default", None).unwrap()) - .icon_name("window-close-symbolic") - .margin_bottom(MARGIN) - .margin_end(MARGIN) - .margin_start(MARGIN) - .margin_top(MARGIN) - .tooltip_text("Close find bar") - .build(); - - let match_case = CheckButton::builder() - .cursor(&Cursor::from_name("default", None).unwrap()) - .label("Match case") - .build(); - - let navigation = Box::builder() - .css_classes([ - "linked", // merge childs - ]) - .margin_end(MARGIN) - .orientation(Orientation::Horizontal) - .build(); - - let back = Button::builder() - .icon_name("go-previous-symbolic") - .margin_bottom(MARGIN) - .margin_top(MARGIN) - .sensitive(false) - .tooltip_text("Back") - .build(); - - let forward = Button::builder() - .icon_name("go-next-symbolic") - .margin_bottom(MARGIN) - .margin_top(MARGIN) - .sensitive(false) - .tooltip_text("Forward") - .build(); - - navigation.append(&back); - navigation.append(&forward); - - let entry = Entry::builder() - .hexpand(true) - .margin_bottom(MARGIN) - .margin_end(MARGIN) - .margin_start(MARGIN) - .margin_top(MARGIN) - .placeholder_text("Find in text..") - .primary_icon_activatable(false) - .primary_icon_sensitive(false) - .primary_icon_name("system-search-symbolic") - .build(); - - let found_tag = TextTag::builder() - .background_rgba(&RGBA::new(0.502, 0.502, 0.502, 0.5)) // @TODO - .build(); - text_buffer.tag_table().add(&found_tag); + let close = close::new(); + let entry = entry::new(); + let match_case = match_case::new(); + let navigation = Navigation::new(); + let tag = Tag::new(text_buffer.tag_table()); // Init main container let g_box = Box::builder() @@ -83,7 +42,7 @@ impl Find { .build(); g_box.append(&entry); - g_box.append(&navigation); + g_box.append(&navigation.g_box); g_box.append(&match_case); g_box.append(&close); @@ -94,30 +53,34 @@ impl Find { }); entry.connect_changed({ + let back = navigation.back.clone(); let entry = entry.clone(); - let found_tag = found_tag.clone(); + let forward = navigation.forward.clone(); + let found_tag = tag.found.clone(); let match_case = match_case.clone(); + let matches = matches.clone(); let text_buffer = text_buffer.clone(); move |this| { - // toggle clear action + // toggle entry clear button if this.text().is_empty() { this.set_secondary_icon_name(None); } else { this.set_secondary_icon_name(Some("edit-clear-symbolic")); } - // apply changes - if find( + + // do search + let result = find( &text_buffer, &found_tag, entry.text().as_str(), match_case.is_active(), - ) - .is_empty() - { - entry.add_css_class("error"); - } else { - entry.remove_css_class("error"); - } + ); + + // update components + update(&entry, &back, &forward, result.is_empty()); + + // update matches index + matches.replace(result); } }); @@ -128,21 +91,28 @@ impl Find { match_case.connect_toggled({ let entry = entry.clone(); - let found_tag = found_tag.clone(); + let found_tag = tag.found.clone(); + let matches = matches.clone(); let text_buffer = text_buffer.clone(); move |this| { - if find( + // do search + let result = find( &text_buffer, &found_tag, entry.text().as_str(), this.is_active(), - ) - .is_empty() - { - entry.add_css_class("error"); - } else { - entry.remove_css_class("error"); - } + ); + + // update components + update( + &entry, + &navigation.back, + &navigation.forward, + result.is_empty(), + ); + + // update matches index + matches.replace(result); } }); @@ -155,12 +125,14 @@ impl Find { } } +// Tools + fn find( text_buffer: &TextBuffer, found_tag: &TextTag, subject: &str, is_match_case: bool, -) -> Vec { +) -> Vec<(TextIter, TextIter)> { // Init start matches result let mut result = Vec::new(); @@ -182,8 +154,20 @@ fn find( None, // unlimited ) { text_buffer.apply_tag(found_tag, &start, &end); - result.push(start); - next = end; + next = end.clone(); + result.push((start, end)); } result } + +fn update(entry: &Entry, back: &Button, forward: &Button, is_empty: bool) { + if is_empty { + entry.add_css_class("error"); + back.set_sensitive(false); + forward.set_sensitive(false); + } else { + entry.remove_css_class("error"); + back.set_sensitive(false); + forward.set_sensitive(true); + } +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/close.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/close.rs new file mode 100644 index 00000000..986e300f --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/close.rs @@ -0,0 +1,14 @@ +use super::MARGIN; +use gtk::{gdk::Cursor, Button}; + +pub fn new() -> Button { + Button::builder() + .cursor(&Cursor::from_name("default", None).unwrap()) + .icon_name("window-close-symbolic") + .margin_bottom(MARGIN) + .margin_end(MARGIN) + .margin_start(MARGIN) + .margin_top(MARGIN) + .tooltip_text("Close find bar") + .build() +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/entry.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/entry.rs new file mode 100644 index 00000000..0d7f5e4c --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/entry.rs @@ -0,0 +1,16 @@ +use super::MARGIN; +use gtk::Entry; + +pub fn new() -> Entry { + Entry::builder() + .hexpand(true) + .margin_bottom(MARGIN) + .margin_end(MARGIN) + .margin_start(MARGIN) + .margin_top(MARGIN) + .placeholder_text("Find in text..") + .primary_icon_activatable(false) + .primary_icon_sensitive(false) + .primary_icon_name("system-search-symbolic") + .build() +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/match_case.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/match_case.rs new file mode 100644 index 00000000..4112c5d5 --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/match_case.rs @@ -0,0 +1,8 @@ +use gtk::{gdk::Cursor, CheckButton}; + +pub fn new() -> CheckButton { + CheckButton::builder() + .cursor(&Cursor::from_name("default", None).unwrap()) + .label("Match case") + .build() +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation.rs new file mode 100644 index 00000000..c65d465f --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation.rs @@ -0,0 +1,40 @@ +mod back; +mod forward; + +use super::MARGIN; +use gtk::{prelude::BoxExt, Box, Button, Orientation}; + +pub struct Navigation { + pub back: Button, + pub forward: Button, + pub g_box: Box, +} + +impl Navigation { + // Constructors + + /// Create new `Self` + pub fn new() -> Self { + // Init components + let back = back::new(); + let forward = forward::new(); + + // Init main container + let g_box = Box::builder() + .css_classes([ + "linked", // merge childs + ]) + .margin_end(MARGIN) + .orientation(Orientation::Horizontal) + .build(); + + g_box.append(&back); + g_box.append(&forward); + + Self { + back, + forward, + g_box, + } + } +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation/back.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation/back.rs new file mode 100644 index 00000000..ef44000c --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation/back.rs @@ -0,0 +1,12 @@ +use super::MARGIN; +use gtk::Button; + +pub fn new() -> Button { + Button::builder() + .icon_name("go-previous-symbolic") + .margin_bottom(MARGIN) + .margin_top(MARGIN) + .sensitive(false) + .tooltip_text("Back") + .build() +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation/forward.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation/forward.rs new file mode 100644 index 00000000..154f9d89 --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/navigation/forward.rs @@ -0,0 +1,12 @@ +use super::MARGIN; +use gtk::Button; + +pub fn new() -> Button { + Button::builder() + .icon_name("go-next-symbolic") + .margin_bottom(MARGIN) + .margin_top(MARGIN) + .sensitive(false) + .tooltip_text("Forward") + .build() +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag.rs new file mode 100644 index 00000000..babaa21f --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag.rs @@ -0,0 +1,27 @@ +mod current; +mod found; + +use gtk::{TextTag, TextTagTable}; + +pub struct Tag { + // pub current: TextTag, + pub found: TextTag, +} + +impl Tag { + // Constructors + + pub fn new(tag_table: TextTagTable) -> Self { + // Init components + let current = current::new(); + let found = found::new(); + + // Init `Self` + tag_table.add(¤t); + tag_table.add(&found); + + Self { + /*current,*/ found, + } + } +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag/current.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag/current.rs new file mode 100644 index 00000000..1b2f6f42 --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag/current.rs @@ -0,0 +1,7 @@ +use gtk::{gdk::RGBA, TextTag}; + +pub fn new() -> TextTag { + TextTag::builder() + .background_rgba(&RGBA::new(0.502, 0.502, 0.502, 0.5)) // @TODO + .build() +} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag/found.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag/found.rs new file mode 100644 index 00000000..1b2f6f42 --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/widget/find/tag/found.rs @@ -0,0 +1,7 @@ +use gtk::{gdk::RGBA, TextTag}; + +pub fn new() -> TextTag { + TextTag::builder() + .background_rgba(&RGBA::new(0.502, 0.502, 0.502, 0.5)) // @TODO + .build() +}