implement self update

This commit is contained in:
yggverse 2024-12-15 18:30:36 +02:00
parent 24160d39eb
commit b3e64a485b
7 changed files with 170 additions and 98 deletions

View File

@ -52,7 +52,7 @@ impl Widget {
let text_view = text_view.clone(); let text_view = text_view.clone();
move |_| { move |_| {
text_view.set_gutter(TextWindowType::Bottom, Some(&find.g_box)); text_view.set_gutter(TextWindowType::Bottom, Some(&find.g_box));
find.entry.grab_focus(); find.input.entry.grab_focus();
} }
}); });

View File

@ -1,21 +1,22 @@
mod close; mod close;
mod entry; mod input;
mod match_case; mod match_case;
mod navigation; mod navigation;
mod tag; mod tag;
use input::Input;
use navigation::Navigation; use navigation::Navigation;
use tag::Tag; use tag::Tag;
use gtk::{ use gtk::{
prelude::{BoxExt, ButtonExt, CheckButtonExt, EditableExt, TextBufferExt, WidgetExt}, prelude::{BoxExt, ButtonExt, CheckButtonExt, EditableExt, TextBufferExt},
Box, Button, Entry, Orientation, TextBuffer, TextIter, TextSearchFlags, TextTag, Box, Button, Orientation, TextBuffer, TextIter, TextSearchFlags, TextTag,
}; };
use std::rc::Rc; use std::rc::Rc;
pub struct Find { pub struct Find {
pub close: Button, pub close: Button,
pub entry: Entry, pub input: Rc<Input>,
pub g_box: Box, pub g_box: Box,
} }
@ -24,7 +25,7 @@ impl Find {
pub fn new(text_buffer: &TextBuffer) -> Self { pub fn new(text_buffer: &TextBuffer) -> Self {
// Init components // Init components
let close = close::new(); let close = close::new();
let entry = entry::new(); let input = Rc::new(Input::new());
let match_case = match_case::new(); let match_case = match_case::new();
let navigation = Rc::new(Navigation::new()); let navigation = Rc::new(Navigation::new());
let tag = Rc::new(Tag::new(text_buffer.tag_table())); let tag = Rc::new(Tag::new(text_buffer.tag_table()));
@ -35,19 +36,19 @@ impl Find {
.orientation(Orientation::Horizontal) .orientation(Orientation::Horizontal)
.build(); .build();
g_box.append(&entry); g_box.append(&input.entry);
g_box.append(&navigation.g_box); g_box.append(&navigation.g_box);
g_box.append(&match_case); g_box.append(&match_case);
g_box.append(&close); g_box.append(&close);
// Connect events // Connect events
close.connect_clicked({ close.connect_clicked({
let entry = entry.clone(); let input = input.clone();
move |_| entry.delete_text(0, -1) move |_| input.clean()
}); });
entry.connect_changed({ input.entry.connect_changed({
let entry = entry.clone(); let input = input.clone();
let match_case = match_case.clone(); let match_case = match_case.clone();
let navigation = navigation.clone(); let navigation = navigation.clone();
let tag = tag.clone(); let tag = tag.clone();
@ -56,15 +57,15 @@ impl Find {
navigation.update(find( navigation.update(find(
&text_buffer, &text_buffer,
&tag.found, &tag.found,
entry.text().as_str(), input.entry.text().as_str(),
match_case.is_active(), match_case.is_active(),
)); ));
update(&entry, &navigation); input.update(navigation.is_match());
} }
}); });
match_case.connect_toggled({ match_case.connect_toggled({
let entry = entry.clone(); let input = input.clone();
let navigation = navigation.clone(); let navigation = navigation.clone();
let tag = tag.clone(); let tag = tag.clone();
let text_buffer = text_buffer.clone(); let text_buffer = text_buffer.clone();
@ -72,17 +73,17 @@ impl Find {
navigation.update(find( navigation.update(find(
&text_buffer, &text_buffer,
&tag.found, &tag.found,
entry.text().as_str(), input.entry.text().as_str(),
this.is_active(), this.is_active(),
)); ));
update(&entry, &navigation); input.update(navigation.is_match());
} }
}); });
// Done // Done
Self { Self {
close, close,
entry, input,
g_box, g_box,
} }
} }
@ -122,15 +123,3 @@ fn find(
} }
result result
} }
fn update(entry: &Entry, navigation: &Rc<Navigation>) {
if navigation.matches.borrow().is_empty() {
entry.add_css_class("error");
navigation.back.set_sensitive(false);
navigation.forward.set_sensitive(false);
} else {
entry.remove_css_class("error");
navigation.back.set_sensitive(false);
navigation.forward.set_sensitive(true);
}
}

View File

@ -1,39 +0,0 @@
use gtk::{
prelude::{EditableExt, EntryExt},
Entry, EntryIconPosition,
};
const MARGIN: i32 = 6;
pub fn new() -> Entry {
// Init widget
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();
// Connect events
entry.connect_icon_release(|this, position| match position {
EntryIconPosition::Secondary => this.delete_text(0, -1),
_ => todo!(), // unexpected
});
entry.connect_changed(|this| {
// 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"));
}
});
// Done
entry
}

View File

@ -0,0 +1,66 @@
use gtk::{
prelude::{EditableExt, EntryExt, WidgetExt},
Entry, EntryIconPosition,
};
const MARGIN: i32 = 6;
pub struct Input {
pub entry: Entry,
}
impl Input {
// Constructors
/// Create new `Self`
pub fn new() -> Self {
// Init widget
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();
// Connect events
entry.connect_icon_release(|this, position| match position {
EntryIconPosition::Secondary => clean(this),
_ => todo!(), // unexpected
});
entry.connect_changed(|this| {
// 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"));
}
});
// Done
Self { entry }
}
// Actions
pub fn clean(&self) {
clean(&self.entry)
}
pub fn update(&self, is_match: bool) {
if is_match {
self.entry.remove_css_class("error");
} else {
self.entry.add_css_class("error");
}
}
}
fn clean(entry: &Entry) {
entry.delete_text(0, -1)
}

View File

@ -1,16 +1,19 @@
mod back; mod back;
mod forward; mod forward;
use gtk::{prelude::BoxExt, Box, Button, Orientation, TextIter}; use back::Back;
use forward::Forward;
use gtk::{prelude::BoxExt, Box, Orientation, TextIter};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
const MARGIN: i32 = 6; const MARGIN: i32 = 6;
pub struct Navigation { pub struct Navigation {
pub back: Button, pub back: Back,
pub forward: Button, pub forward: Forward,
pub g_box: Box, pub g_box: Box,
pub matches: Rc<RefCell<Vec<(TextIter, TextIter)>>>, matches: Rc<RefCell<Vec<(TextIter, TextIter)>>>,
} }
impl Navigation { impl Navigation {
@ -22,8 +25,8 @@ impl Navigation {
let matches = Rc::new(RefCell::new(Vec::new())); let matches = Rc::new(RefCell::new(Vec::new()));
// Init components // Init components
let back = back::new(); let back = Back::new();
let forward = forward::new(); let forward = Forward::new();
// Init main container // Init main container
let g_box = Box::builder() let g_box = Box::builder()
@ -34,8 +37,8 @@ impl Navigation {
.orientation(Orientation::Horizontal) .orientation(Orientation::Horizontal)
.build(); .build();
g_box.append(&back); g_box.append(&back.button);
g_box.append(&forward); g_box.append(&forward.button);
Self { Self {
back, back,
@ -45,7 +48,24 @@ impl Navigation {
} }
} }
// Actions
pub fn update(&self, matches: Vec<(TextIter, TextIter)>) { pub fn update(&self, matches: Vec<(TextIter, TextIter)>) {
// Update self
self.matches.replace(matches); self.matches.replace(matches);
// Update child components
self.back.update(self.is_match());
self.forward.update(self.is_match());
}
// pub fn back(&self) {}
// pub fn forward(&self) {}
// Getters
pub fn is_match(&self) -> bool {
!self.matches.borrow().is_empty()
} }
} }

View File

@ -1,13 +1,31 @@
use super::MARGIN; use gtk::{gdk::Cursor, prelude::WidgetExt, Button};
use gtk::{gdk::Cursor, Button};
pub fn new() -> Button { const MARGIN: i32 = 6;
Button::builder()
pub struct Back {
pub button: Button,
}
impl Back {
// Constructors
/// Create new `Self`
pub fn new() -> Self {
Self {
button: Button::builder()
.cursor(&Cursor::from_name("default", None).unwrap()) .cursor(&Cursor::from_name("default", None).unwrap())
.icon_name("go-previous-symbolic") .icon_name("go-previous-symbolic")
.margin_bottom(MARGIN) .margin_bottom(MARGIN)
.margin_top(MARGIN) .margin_top(MARGIN)
.sensitive(false) .sensitive(false)
.tooltip_text("Back") .tooltip_text("Back")
.build() .build(),
}
}
// Actions
pub fn update(&self, is_sensitive: bool) {
self.button.set_sensitive(is_sensitive);
}
} }

View File

@ -1,13 +1,31 @@
use super::MARGIN; use gtk::{gdk::Cursor, prelude::WidgetExt, Button};
use gtk::{gdk::Cursor, Button};
pub fn new() -> Button { const MARGIN: i32 = 6;
Button::builder()
pub struct Forward {
pub button: Button,
}
impl Forward {
// Constructors
/// Create new `Self`
pub fn new() -> Self {
Self {
button: Button::builder()
.cursor(&Cursor::from_name("default", None).unwrap()) .cursor(&Cursor::from_name("default", None).unwrap())
.icon_name("go-next-symbolic") .icon_name("go-next-symbolic")
.margin_bottom(MARGIN) .margin_bottom(MARGIN)
.margin_top(MARGIN) .margin_top(MARGIN)
.sensitive(false) .sensitive(false)
.tooltip_text("Forward") .tooltip_text("Forward")
.build() .build(),
}
}
// Actions
pub fn update(&self, is_sensitive: bool) {
self.button.set_sensitive(is_sensitive);
}
} }