Skip to content

Commit

Permalink
Rework record editing widget (remove intermediate Vec<String>)
Browse files Browse the repository at this point in the history
  • Loading branch information
andy128k committed Sep 18, 2023
1 parent 2638c35 commit 0fbc430
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 25 deletions.
29 changes: 11 additions & 18 deletions src/main_window.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::cache::Cache;
use crate::config::ConfigService;
use crate::format;
use crate::model::record::{RecordType, RECORD_TYPE_GENERIC};
use crate::model::record::{Record, RecordType, RECORD_TYPE_GENERIC};
use crate::model::tree::RecordNode;
use crate::model::tree::RecordTree;
use crate::ui;
Expand All @@ -10,7 +10,7 @@ use crate::ui::dialogs::ask_save::{ask_save, AskSave};
use crate::ui::dialogs::change_password::change_password;
use crate::ui::dialogs::file_chooser;
use crate::ui::dialogs::say::say_error;
use crate::ui::edit_record::edit_record;
use crate::ui::edit_record::dialog::edit_record;
use crate::ui::forms::entry::form_password_entry;
use crate::ui::open_file::OpenFile;
use crate::ui::search::SearchEvent;
Expand Down Expand Up @@ -526,14 +526,7 @@ impl PSMainWindow {
let record_type = RecordType::find(&record_type_name).unwrap_or(&*RECORD_TYPE_GENERIC);

let empty_record = record_type.new_record();
let Some(new_record) = edit_record(
&empty_record,
self.upcast_ref(),
"Add record",
self.get_usernames(),
)
.await
else {
let Some(new_record) = self.edit_record("Add record", &empty_record).await else {
return;
};

Expand All @@ -548,15 +541,15 @@ impl PSMainWindow {
}

impl PSMainWindow {
async fn edit_record(&self, title: &str, record: &Record) -> Option<Record> {
let result = edit_record(record, self.upcast_ref(), title, self.get_usernames()).await;
self.imp().file_pane.grab_focus_to_view();
pending().await;
result
}

async fn action_edit(&self, position: u32, record_node: RecordNode) {
let Some(new_record) = edit_record(
record_node.record(),
self.upcast_ref(),
"Edit record",
self.get_usernames(),
)
.await
else {
let Some(new_record) = self.edit_record("Edit record", record_node.record()).await else {
return;
};

Expand Down
12 changes: 6 additions & 6 deletions src/ui/edit_record.rs → src/ui/edit_record/dialog.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use super::edit_object::edit_object;
use super::forms::base::*;
use super::forms::entry::*;
use super::forms::form::*;
use super::forms::multiline::*;
use super::password_editor::PasswordEditor;
use crate::model::record::FIELD_NAME;
use crate::model::record::RECORD_TYPES;
use crate::model::record::{FieldType, Record, RecordType};
use crate::ui::edit_object::edit_object;
use crate::ui::forms::base::*;
use crate::ui::forms::entry::*;
use crate::ui::forms::form::*;
use crate::ui::forms::multiline::*;
use crate::ui::password_editor::PasswordEditor;
use crate::ui::record_type_popover::RecordTypePopoverBuilder;
use gtk::{glib, prelude::*};
use std::cell::RefCell;
Expand Down
3 changes: 3 additions & 0 deletions src/ui/edit_record/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod dialog;
mod record_form;
mod record_widget;
160 changes: 160 additions & 0 deletions src/ui/edit_record/record_form.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use crate::model::record::{Field, FieldType, Record, RecordType};
use gtk::{glib, prelude::*, subclass::prelude::*};

mod imp {
use super::*;
use crate::ui::forms::base::*;
use crate::ui::forms::entry::*;
use crate::ui::forms::multiline::*;
use crate::ui::password_editor::PasswordEditor;
use once_cell::sync::{Lazy, OnceCell};

struct FormEntry {
field: &'static Field,
widget: Box<dyn FormWidget<String>>,
}

pub struct RecordForm {
grid: gtk::Grid,
pub record_type: OnceCell<&'static RecordType>,
entries: OnceCell<Vec<FormEntry>>,
}

#[glib::object_subclass]
impl ObjectSubclass for RecordForm {
const NAME: &'static str = "PSRecordForm";
type Type = super::RecordForm;
type ParentType = gtk::Widget;

fn new() -> Self {
Self {
grid: gtk::Grid::builder()
.column_spacing(10)
.row_spacing(10)
.build(),
record_type: Default::default(),
entries: Default::default(),
}
}
}

impl ObjectImpl for RecordForm {
fn constructed(&self) {
self.parent_constructed();

let obj = self.obj();
obj.set_layout_manager(Some(gtk::BinLayout::new()));

self.grid.set_parent(&*obj);
}

fn signals() -> &'static [glib::subclass::Signal] {
static SIGNALS: Lazy<Vec<glib::subclass::Signal>> =
Lazy::new(|| vec![glib::subclass::Signal::builder("record-changed").build()]);
&SIGNALS
}

fn dispose(&self) {
while let Some(child) = self.obj().first_child() {
child.unparent();
}
}
}

impl WidgetImpl for RecordForm {}

impl RecordForm {
pub fn init(&self, record_type: &'static RecordType, names: &[String]) {
self.record_type.set(record_type).unwrap();

let mut entries: Vec<FormEntry> = Vec::new();
for (index, field) in record_type.fields.iter().enumerate() {
let mut widget: Box<dyn FormWidget<String>> = match field.field_type {
FieldType::Text => Box::new(form_entry()),
FieldType::MultiLine => Box::new(MultiLine::new()),
FieldType::Name => Box::new(form_entry_with_completion(names)),
FieldType::Password => Box::new(PasswordEditor::new()),
FieldType::Secret => Box::new(form_password_entry()),
};

let label_widget = gtk::Label::builder()
.label(field.title)
.xalign(0_f32)
.yalign(0.5_f32)
.build();
self.grid.attach(&label_widget, 0, index as i32, 1, 1);

widget.connect_changed(Box::new(glib::clone!(@weak self as this => move |_| {
this.obj().emit_by_name::<()>("record-changed", &[]);
})));
self.grid
.attach(&widget.get_widget(), 1, index as i32, 1, 1);

entries.push(FormEntry { field, widget });
}
self.entries.set(entries).ok().unwrap();
}

pub fn record(&self) -> Record {
let mut record = self.record_type.get().unwrap().new_record();
for &FormEntry { field, ref widget } in self.entries.get().unwrap().iter() {
if let Some(value) = widget.get_value() {
record.set_field(field, &value);
}
}
record
}

pub fn set_record(&self, record: &Record) {
for &FormEntry { field, ref widget } in self.entries.get().unwrap().iter() {
widget.set_value(Some(&record.get_field(field).to_string()));
}
}

pub fn grab_focus_to_editor(&self) {
if let Some(first) = self.entries.get().unwrap().first() {
first.widget.get_widget().grab_focus();
}
}
}
}

glib::wrapper! {
pub struct RecordForm(ObjectSubclass<imp::RecordForm>)
@extends gtk::Widget;
}

impl RecordForm {
pub fn new(record_type: &'static RecordType, names: &[String]) -> Self {
let obj: Self = glib::Object::builder().build();
obj.imp().init(record_type, names);
obj
}

pub fn record_type(&self) -> &'static RecordType {
self.imp().record_type.get().unwrap()
}

pub fn record(&self) -> Record {
self.imp().record()
}

pub fn set_record(&self, record: &Record) {
self.imp().set_record(record);
}

pub fn grab_focus_to_editor(&self) {
self.imp().grab_focus_to_editor();
}

pub fn connect_record_changed<F>(&self, f: F) -> glib::signal::SignalHandlerId
where
F: Fn(&Self) + 'static,
{
self.connect_closure(
"record-changed",
false,
glib::closure_local!(move |self_: &Self| (f)(self_)),
)
}
}
Loading

0 comments on commit 0fbc430

Please sign in to comment.