Try out different autosuggestion methods

This commit is contained in:
Julian Mutter 2024-05-26 14:29:16 +02:00
parent e0feae0546
commit c3e5db6889
4 changed files with 207 additions and 2 deletions

View File

@ -0,0 +1,32 @@
use gtk::prelude::*;
use relm4::prelude::*;
pub struct AutosuggestionElementModel {
pub label: String,
}
#[relm4::factory(pub)]
impl FactoryComponent for AutosuggestionElementModel {
type Init = String;
type ParentWidget = gtk::ListBox;
type CommandOutput = ();
type Input = ();
type Output = ();
view! {
#[root]
gtk::ListBoxRow {
gtk::Label {
set_label: &self.label,
set_halign: gtk::Align::Start,
set_margin_all: 0,
}
}
}
fn init_model(label: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
AutosuggestionElementModel { label }
}
fn update(&mut self, msg: Self::Input, _sender: FactorySender<Self>) {}
}

View File

@ -0,0 +1,104 @@
use gtk::prelude::*;
use relm4::component::{AsyncComponent, AsyncComponentParts};
use relm4::factory::FactoryVecDeque;
use relm4::gtk;
use relm4::{AsyncComponentSender, RelmListBoxExt};
use super::autosuggestion_element::AutosuggestionElementModel;
pub struct AutosuggestionPopoverModel {
hidden: bool,
suggestions: Vec<String>,
suggestion_rows: FactoryVecDeque<AutosuggestionElementModel>,
}
#[derive(Debug)]
pub enum AutosuggestionPopoverInput {
Show,
Hide,
SetSuggestions(Vec<String>),
SuggestionIndexSelected(i32),
None,
}
#[derive(Debug)]
pub enum AutosuggestionPopoverOutput {
SuggestionSelected(String),
}
#[relm4::component(pub, async)]
impl AsyncComponent for AutosuggestionPopoverModel {
type Init = ();
type Input = AutosuggestionPopoverInput;
type Output = AutosuggestionPopoverOutput;
type CommandOutput = ();
view! {
gtk::Popover {
#[watch]
set_visible: !model.hidden,
set_has_arrow: false,
model.suggestion_rows.widget() -> &relm4::gtk::ListBox {
set_hexpand: true,
// set_orientation: gtk::Orientation::Vertical,
// set_spacing: 5,
connect_row_activated[sender] => move |list_box, row| {
let index = list_box.index_of_child(row).unwrap();
sender.input(AutosuggestionPopoverInput::SuggestionIndexSelected(index));
},
},
}
}
async fn init(
_params: Self::Init,
root: Self::Root,
sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
let suggestion_rows = FactoryVecDeque::builder()
.launch(gtk::ListBox::default())
.forward(sender.input_sender(), |_| AutosuggestionPopoverInput::None);
let model = AutosuggestionPopoverModel {
hidden: true,
suggestions: Vec::new(),
suggestion_rows,
};
let widgets = view_output!();
AsyncComponentParts { model, widgets }
}
// TODO: init_loading_widgets
async fn update(
&mut self,
msg: Self::Input,
_sender: AsyncComponentSender<Self>,
_root: &Self::Root,
) {
match msg {
AutosuggestionPopoverInput::Show => self.hidden = false,
AutosuggestionPopoverInput::Hide => self.hidden = true,
AutosuggestionPopoverInput::SetSuggestions(suggestions) => {
self.suggestion_rows.guard().clear();
for suggestion in suggestions.iter() {
self.suggestion_rows
.guard()
.push_back(suggestion.to_string());
}
self.suggestions = suggestions;
}
AutosuggestionPopoverInput::None => {}
AutosuggestionPopoverInput::SuggestionIndexSelected(index) => {
let suggestion = self.suggestions.get(index as usize).unwrap();
_sender
.output(AutosuggestionPopoverOutput::SuggestionSelected(
suggestion.to_string(),
))
.unwrap();
}
}
}
}

View File

@ -1,4 +1,6 @@
pub mod app;
pub mod autosuggestion_element;
pub mod autosuggestion_popover;
pub mod mcdu;
pub mod sheet_edit_dialog;
pub mod sheet_listing;

View File

@ -3,7 +3,11 @@ use std::sync::Arc;
use relm4::{
component::{AsyncComponent, AsyncComponentParts, Connector},
gtk::EntryBuffer,
gtk::{
gio::ListStore,
glib::{self, GString, Type, Value},
EntryBuffer, EntryCompletion,
},
prelude::*,
AsyncComponentSender,
};
@ -11,15 +15,19 @@ use relm4_components::alert::{Alert, AlertMsg, AlertSettings};
use crate::{database::Database, sheet::Sheet, sheet_dao};
use super::autosuggestion_popover::{AutosuggestionPopoverInput, AutosuggestionPopoverModel};
pub struct SheetEditDialogModel {
database: Arc<Database>,
hidden: bool,
sheet: Option<Sheet>,
name_entry_buffer: EntryBuffer,
composer_entry_buffer: EntryBuffer,
composer_entry_completion: EntryCompletion,
is_book: bool,
book_sheets: Vec<(String, String, i64)>,
alert_empty_fields: Connector<Alert>,
autosuggestion_popover: AsyncController<AutosuggestionPopoverModel>,
}
pub struct SheetEditDialogInit {
@ -31,6 +39,8 @@ pub struct SheetEditDialogInit {
pub enum SheetEditDialogInput {
Accept,
Cancel,
ComposerTextChanged(String),
ComposerSuggestionSelected(String),
}
#[derive(Debug)]
@ -74,7 +84,9 @@ impl AsyncComponent for SheetEditDialogModel {
},
gtk::Entry {
set_buffer: &model.composer_entry_buffer,
set_completion: Some(&model.composer_entry_completion),
set_hexpand: true,
connect_changed[sender] => move |entry| sender.input(SheetEditDialogInput::ComposerTextChanged(entry.text().to_string())),
},
},
gtk::Box {
@ -103,7 +115,8 @@ impl AsyncComponent for SheetEditDialogModel {
set_label : "Confirm",
connect_clicked[sender] => move |_| sender.input(SheetEditDialogInput::Accept)
},
}
},
// model.autosuggestion_popover.widget(),
}
}
}
@ -148,12 +161,39 @@ impl AsyncComponent for SheetEditDialogModel {
}
};
let composer_entry_completion = EntryCompletion::new();
let data = [
"France".to_string(),
"Italy".to_string(),
"Sweden".to_string(),
"Switzerland".to_string(),
];
let store = gtk::ListStore::new(&[glib::Type::STRING]);
for d in data.iter() {
store.set(&store.append(), &[(0, &d)]);
}
composer_entry_completion.set_model(Some(&store));
// Use the first (and only) column available to set the autocompletion text
composer_entry_completion.set_text_column(0);
// how many keystrokes to wait before attempting to autocomplete?
composer_entry_completion.set_minimum_key_length(1);
// whether the completions should be presented in a popup window
composer_entry_completion.set_popup_completion(true);
let autosuggestion_popover = AutosuggestionPopoverModel::builder()
.launch(())
.forward(sender.input_sender(), |output| match output {
crate::ui::autosuggestion_popover::AutosuggestionPopoverOutput::SuggestionSelected(suggestion) => SheetEditDialogInput::ComposerSuggestionSelected(suggestion),
});
let model = SheetEditDialogModel {
database: params.database,
hidden: false,
sheet: Some(sheet),
name_entry_buffer: EntryBuffer::new(Some(sheet_name)),
composer_entry_buffer: EntryBuffer::new(Some(sheet_composer)),
composer_entry_completion,
is_book,
book_sheets: Vec::new(),
alert_empty_fields: Alert::builder().transient_for(&root).launch(AlertSettings {
@ -165,6 +205,7 @@ impl AsyncComponent for SheetEditDialogModel {
cancel_label: None,
option_label: None,
}),
autosuggestion_popover,
};
let widgets = view_output!();
@ -224,6 +265,32 @@ impl AsyncComponent for SheetEditDialogModel {
self.hidden = true;
self.sheet = None;
}
SheetEditDialogInput::ComposerTextChanged(composer_name) => {
// let suggestions = vec![
// "Hello".into(),
// "World".into(),
// "Seattle".into(),
// "Salzburg".into(),
// ];
// self.autosuggestion_popover
// .emit(AutosuggestionPopoverInput::SetSuggestions(suggestions));
// self.autosuggestion_popover
// .emit(AutosuggestionPopoverInput::Show);
// self.model.filtered_suggestions = self
// .model
// .suggestions
// .iter()
// .filter(|suggestion| suggestion.starts_with(&text))
// .cloned()
// .collect();
// self.update_suggestions();
}
SheetEditDialogInput::ComposerSuggestionSelected(composer_name) => {
self.composer_entry_buffer.set_text(composer_name);
self.autosuggestion_popover
.emit(AutosuggestionPopoverInput::Hide);
}
}
}
}