Add support for tagging + protecting fields

This commit is contained in:
Damien Elmes 2023-09-17 13:55:37 +10:00
parent 64e40033e0
commit 1e37c806e5
5 changed files with 32 additions and 9 deletions

View file

@ -20,3 +20,4 @@ fields-name-invalid-letter = The field name should not contain :, ", { "{" } or
# If enabled, the field is not included when searching for 'text', 're:text' and so on, # If enabled, the field is not included when searching for 'text', 're:text' and so on,
# but is when searching for a specific field, eg 'field:text'. # but is when searching for a specific field, eg 'field:text'.
fields-exclude-from-search = Exclude from unqualified searches (slower) fields-exclude-from-search = Exclude from unqualified searches (slower)
fields-field-is-required = This is a required field, and can not be deleted.

View file

@ -87,6 +87,9 @@ message Notetype {
bool exclude_from_search = 8; bool exclude_from_search = 8;
// used for merging notetypes on import (Anki 23.10) // used for merging notetypes on import (Anki 23.10)
optional int64 id = 9; optional int64 id = 9;
// Can be used to uniquely identify required fields.
optional uint32 tag = 10;
bool prevent_deletion = 11;
bytes other = 255; bytes other = 255;
} }

View file

@ -21,7 +21,7 @@ from aqt.utils import (
disable_help_button, disable_help_button,
getOnlyText, getOnlyText,
openHelp, openHelp,
showWarning, show_warning,
tooltip, tooltip,
tr, tr,
) )
@ -128,17 +128,17 @@ class FieldDialog(QDialog):
if not txt: if not txt:
return None return None
if txt[0] in "#^/": if txt[0] in "#^/":
showWarning(tr.fields_name_first_letter_not_valid()) show_warning(tr.fields_name_first_letter_not_valid())
return None return None
for letter in """:{"}""": for letter in """:{"}""":
if letter in txt: if letter in txt:
showWarning(tr.fields_name_invalid_letter()) show_warning(tr.fields_name_invalid_letter())
return None return None
for f in self.model["flds"]: for f in self.model["flds"]:
if ignoreOrd is not None and f["ord"] == ignoreOrd: if ignoreOrd is not None and f["ord"] == ignoreOrd:
continue continue
if f["name"] == txt: if f["name"] == txt:
showWarning(tr.fields_that_field_name_is_already_used()) show_warning(tr.fields_that_field_name_is_already_used())
return None return None
return txt return txt
@ -174,7 +174,11 @@ class FieldDialog(QDialog):
def onDelete(self) -> None: def onDelete(self) -> None:
if len(self.model["flds"]) < 2: if len(self.model["flds"]) < 2:
showWarning(tr.fields_notes_require_at_least_one_field()) show_warning(tr.fields_notes_require_at_least_one_field())
return
field = self.model["flds"][self.form.fieldList.currentRow()]
if field["preventDeletion"]:
show_warning(tr.fields_field_is_required())
return return
count = self.mm.use_count(self.model) count = self.mm.use_count(self.model)
c = tr.browsing_note_count(count=count) c = tr.browsing_note_count(count=count)
@ -182,9 +186,8 @@ class FieldDialog(QDialog):
return return
if not self.change_tracker.mark_schema(): if not self.change_tracker.mark_schema():
return return
f = self.model["flds"][self.form.fieldList.currentRow()] self.mm.remove_field(self.model, field)
self.mm.remove_field(self.model, f) gui_hooks.fields_did_delete_field(self, field)
gui_hooks.fields_did_delete_field(self, f)
self.fillFields() self.fillFields()
self.form.fieldList.setCurrentRow(0) self.form.fieldList.setCurrentRow(0)

View file

@ -47,6 +47,8 @@ impl NoteField {
description: "".into(), description: "".into(),
collapsed: false, collapsed: false,
exclude_from_search: false, exclude_from_search: false,
tag: None,
prevent_deletion: false,
other: vec![], other: vec![],
}, },
} }

View file

@ -239,7 +239,7 @@ pub struct NoteFieldSchema11 {
pub(crate) font: String, pub(crate) font: String,
pub(crate) size: u16, pub(crate) size: u16,
// This was not in schema 11, but needs to be listed here so that the setting is not lost // These were not in schema 11, but need to be listed here so that the setting is not lost
// on downgrade/upgrade. // on downgrade/upgrade.
#[serde(default, deserialize_with = "default_on_invalid")] #[serde(default, deserialize_with = "default_on_invalid")]
pub(crate) description: String, pub(crate) description: String,
@ -256,6 +256,12 @@ pub struct NoteFieldSchema11 {
#[serde(default, deserialize_with = "default_on_invalid")] #[serde(default, deserialize_with = "default_on_invalid")]
pub(crate) id: Option<i64>, pub(crate) id: Option<i64>,
#[serde(default, deserialize_with = "default_on_invalid")]
pub(crate) tag: Option<u32>,
#[serde(default, deserialize_with = "default_on_invalid")]
pub(crate) prevent_deletion: bool,
#[serde(flatten)] #[serde(flatten)]
pub(crate) other: HashMap<String, Value>, pub(crate) other: HashMap<String, Value>,
} }
@ -274,6 +280,8 @@ impl Default for NoteFieldSchema11 {
collapsed: false, collapsed: false,
exclude_from_search: false, exclude_from_search: false,
id: None, id: None,
tag: None,
prevent_deletion: false,
other: Default::default(), other: Default::default(),
} }
} }
@ -294,6 +302,8 @@ impl From<NoteFieldSchema11> for NoteField {
collapsed: f.collapsed, collapsed: f.collapsed,
exclude_from_search: f.exclude_from_search, exclude_from_search: f.exclude_from_search,
id: f.id, id: f.id,
tag: f.tag,
prevent_deletion: f.prevent_deletion,
other: other_to_bytes(&f.other), other: other_to_bytes(&f.other),
}, },
} }
@ -315,6 +325,8 @@ impl From<NoteField> for NoteFieldSchema11 {
collapsed: conf.collapsed, collapsed: conf.collapsed,
exclude_from_search: conf.exclude_from_search, exclude_from_search: conf.exclude_from_search,
id: conf.id, id: conf.id,
tag: conf.tag,
prevent_deletion: conf.prevent_deletion,
other: parse_other_fields(&conf.other, &RESERVED_FIELD_KEYS), other: parse_other_fields(&conf.other, &RESERVED_FIELD_KEYS),
} }
} }
@ -332,6 +344,8 @@ static RESERVED_FIELD_KEYS: Set<&'static str> = phf_set! {
"description", "description",
"excludeFromSearch", "excludeFromSearch",
"id", "id",
"tag",
"preventDeletion",
}; };
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]