From 1e37c806e5bd54fcbc2081f8ef42f99d5100ce04 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 17 Sep 2023 13:55:37 +1000 Subject: [PATCH] Add support for tagging + protecting fields --- ftl/core/fields.ftl | 1 + proto/anki/notetypes.proto | 3 +++ qt/aqt/fields.py | 19 +++++++++++-------- rslib/src/notetype/fields.rs | 2 ++ rslib/src/notetype/schema11.rs | 16 +++++++++++++++- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/ftl/core/fields.ftl b/ftl/core/fields.ftl index df2c34a75..af8d45758 100644 --- a/ftl/core/fields.ftl +++ b/ftl/core/fields.ftl @@ -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, # but is when searching for a specific field, eg 'field:text'. fields-exclude-from-search = Exclude from unqualified searches (slower) +fields-field-is-required = This is a required field, and can not be deleted. diff --git a/proto/anki/notetypes.proto b/proto/anki/notetypes.proto index 60c1f5eb1..6163229bd 100644 --- a/proto/anki/notetypes.proto +++ b/proto/anki/notetypes.proto @@ -87,6 +87,9 @@ message Notetype { bool exclude_from_search = 8; // used for merging notetypes on import (Anki 23.10) optional int64 id = 9; + // Can be used to uniquely identify required fields. + optional uint32 tag = 10; + bool prevent_deletion = 11; bytes other = 255; } diff --git a/qt/aqt/fields.py b/qt/aqt/fields.py index 14b581ee5..9c1ddfc70 100644 --- a/qt/aqt/fields.py +++ b/qt/aqt/fields.py @@ -21,7 +21,7 @@ from aqt.utils import ( disable_help_button, getOnlyText, openHelp, - showWarning, + show_warning, tooltip, tr, ) @@ -128,17 +128,17 @@ class FieldDialog(QDialog): if not txt: return None if txt[0] in "#^/": - showWarning(tr.fields_name_first_letter_not_valid()) + show_warning(tr.fields_name_first_letter_not_valid()) return None for letter in """:{"}""": if letter in txt: - showWarning(tr.fields_name_invalid_letter()) + show_warning(tr.fields_name_invalid_letter()) return None for f in self.model["flds"]: if ignoreOrd is not None and f["ord"] == ignoreOrd: continue 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 txt @@ -174,7 +174,11 @@ class FieldDialog(QDialog): def onDelete(self) -> None: 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 count = self.mm.use_count(self.model) c = tr.browsing_note_count(count=count) @@ -182,9 +186,8 @@ class FieldDialog(QDialog): return if not self.change_tracker.mark_schema(): return - f = self.model["flds"][self.form.fieldList.currentRow()] - self.mm.remove_field(self.model, f) - gui_hooks.fields_did_delete_field(self, f) + self.mm.remove_field(self.model, field) + gui_hooks.fields_did_delete_field(self, field) self.fillFields() self.form.fieldList.setCurrentRow(0) diff --git a/rslib/src/notetype/fields.rs b/rslib/src/notetype/fields.rs index 6aaebb784..2d3419d73 100644 --- a/rslib/src/notetype/fields.rs +++ b/rslib/src/notetype/fields.rs @@ -47,6 +47,8 @@ impl NoteField { description: "".into(), collapsed: false, exclude_from_search: false, + tag: None, + prevent_deletion: false, other: vec![], }, } diff --git a/rslib/src/notetype/schema11.rs b/rslib/src/notetype/schema11.rs index 669af7ab7..044c405ef 100644 --- a/rslib/src/notetype/schema11.rs +++ b/rslib/src/notetype/schema11.rs @@ -239,7 +239,7 @@ pub struct NoteFieldSchema11 { pub(crate) font: String, 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. #[serde(default, deserialize_with = "default_on_invalid")] pub(crate) description: String, @@ -256,6 +256,12 @@ pub struct NoteFieldSchema11 { #[serde(default, deserialize_with = "default_on_invalid")] pub(crate) id: Option, + #[serde(default, deserialize_with = "default_on_invalid")] + pub(crate) tag: Option, + + #[serde(default, deserialize_with = "default_on_invalid")] + pub(crate) prevent_deletion: bool, + #[serde(flatten)] pub(crate) other: HashMap, } @@ -274,6 +280,8 @@ impl Default for NoteFieldSchema11 { collapsed: false, exclude_from_search: false, id: None, + tag: None, + prevent_deletion: false, other: Default::default(), } } @@ -294,6 +302,8 @@ impl From for NoteField { collapsed: f.collapsed, exclude_from_search: f.exclude_from_search, id: f.id, + tag: f.tag, + prevent_deletion: f.prevent_deletion, other: other_to_bytes(&f.other), }, } @@ -315,6 +325,8 @@ impl From for NoteFieldSchema11 { collapsed: conf.collapsed, exclude_from_search: conf.exclude_from_search, id: conf.id, + tag: conf.tag, + prevent_deletion: conf.prevent_deletion, other: parse_other_fields(&conf.other, &RESERVED_FIELD_KEYS), } } @@ -332,6 +344,8 @@ static RESERVED_FIELD_KEYS: Set<&'static str> = phf_set! { "description", "excludeFromSearch", "id", + "tag", + "preventDeletion", }; #[derive(Serialize, Deserialize, Debug, Default, Clone)]