Editor Field Descriptions (#1476)

* Add description input to fields dialog

QLineEdit seems like the best option, as it saves space and motivates users to keep their descriptions concise.

* Add setDescriptions to note initialization script

Went for the extra function instead of including it in setFields to prevent potential add-on breakages.

* Add tooltip next to field name if description is set

* Refactor code according to suggestions

Set default tooltip placement to right instead of bottom

Use .get() for fld["description"]

Fix tab order in fields dialog

Swap out abbreviation "desc" for full length name to keep consistency

* Update Protobuf and Rust for description

Add description to notetypes.proto and schema11

Co-authored-by: RumovZ <RumovZ@users.noreply.github.com>

* Fix tooltips not updating with description

Remove redundant variable tooltipOptions

Update previousTooltip within reactive function

* Move LabelDescription out of LabelName

Co-authored-by: Henrik Giesel <hgiesel@users.noreply.github.com>

* Decrease icon size and fix alignment

Co-Authored-By: Henrik Giesel <hengiesel@gmail.com>

* the new key needs to be cleared from fields, not the notetype itself

Co-authored-by: RumovZ <RumovZ@users.noreply.github.com>
Co-authored-by: Henrik Giesel <hengiesel@gmail.com>
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
This commit is contained in:
Matthias Metelka 2021-11-06 00:42:48 +01:00 committed by GitHub
parent 07f3f34bf5
commit 371f731e30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 147 additions and 26 deletions

View File

@ -3,6 +3,8 @@ fields-delete-field-from = Delete field from { $val }?
fields-editing-font = Editing Font
fields-field = Field:
fields-field-name = Field name:
fields-description = Description
fields-description-placeholder = Tooltip to show next to the field's name in the editing screen
fields-fields-for = Fields for { $val }
fields-font = Font:
fields-new-position-1 = New position (1...{ $val }):

View File

@ -68,6 +68,7 @@ message Notetype {
bool rtl = 2;
string font_name = 3;
uint32 font_size = 4;
string description = 5;
bytes other = 255;
}

View File

@ -460,6 +460,10 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
(fld, self.mw.col.media.escape_media_filenames(val))
for fld, val in self.note.items()
]
flds = self.note.note_type()["flds"]
descriptions = [fld.get("description", "") for fld in flds]
self.widget.show()
note_fields_status = self.note.fields_check()
@ -478,8 +482,9 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
text_color = self.mw.pm.profile.get("lastTextColor", "#00f")
highlight_color = self.mw.pm.profile.get("lastHighlightColor", "#00f")
js = "setFields({}); setFonts({}); focusField({}); setNoteId({}); setColorButtons({}); setTags({}); ".format(
js = "setFields({}); setDescriptions({}); setFonts({}); focusField({}); setNoteId({}); setColorButtons({}); setTags({}); ".format(
json.dumps(data),
json.dumps(descriptions),
json.dumps(self.fonts()),
json.dumps(focusTo),
json.dumps(self.note.id),

View File

@ -214,6 +214,7 @@ class FieldDialog(QDialog):
f.fontSize.setValue(fld["size"])
f.sortField.setChecked(self.model["sortf"] == fld["ord"])
f.rtl.setChecked(fld["rtl"])
f.fieldDescription.setText(fld.get("description", ""))
def saveField(self) -> None:
# not initialized yet?
@ -234,6 +235,10 @@ class FieldDialog(QDialog):
if fld["rtl"] != rtl:
fld["rtl"] = rtl
self.change_tracker.mark_basic()
desc = f.fieldDescription.text()
if fld.get("description", "") != desc:
fld["description"] = desc
self.change_tracker.mark_basic()
def reject(self) -> None:
if self.change_tracker.changed():

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>483</width>
<height>352</height>
<width>586</width>
<height>376</height>
</rect>
</property>
<property name="windowTitle">
@ -84,17 +84,21 @@
</item>
<item>
<layout class="QGridLayout" name="_2">
<item row="0" column="1">
<widget class="QFontComboBox" name="fontFamily">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
<item row="1" column="0">
<widget class="QLabel" name="label_font">
<property name="text">
<string>fields_editing_font</string>
</property>
</widget>
</item>
<item row="0" column="2">
<item row="3" column="1">
<widget class="QCheckBox" name="rtl">
<property name="text">
<string>fields_reverse_text_direction_rtl</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="fontSize">
<property name="minimum">
<number>5</number>
@ -104,31 +108,47 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_18">
<item row="2" column="0">
<widget class="QLabel" name="label_sort">
<property name="text">
<string>actions_options</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QRadioButton" name="sortField">
<property name="text">
<string>fields_sort_by_this_field_in_the</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="rtl">
<item row="0" column="0">
<widget class="QLabel" name="label_description">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>fields_reverse_text_direction_rtl</string>
<string>fields_description</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>fields_editing_font</string>
<item row="1" column="1">
<widget class="QFontComboBox" name="fontFamily">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="fieldDescription">
<property name="placeholderText">
<string>fields_description_placeholder</string>
</property>
</widget>
</item>
@ -152,11 +172,11 @@
<tabstop>fieldDelete</tabstop>
<tabstop>fieldRename</tabstop>
<tabstop>fieldPosition</tabstop>
<tabstop>fieldDescription</tabstop>
<tabstop>fontFamily</tabstop>
<tabstop>fontSize</tabstop>
<tabstop>sortField</tabstop>
<tabstop>rtl</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="icons.qrc"/>

View File

@ -44,6 +44,7 @@ impl NoteField {
rtl: false,
font_name: "Arial".into(),
font_size: 20,
description: "".into(),
other: vec![],
},
}

View File

@ -165,6 +165,13 @@ impl From<Notetype> for NotetypeSchema11 {
}
}
fn clear_other_field_duplicates(other: &mut HashMap<String, Value>) {
// see `clear_other_duplicates()` in `deckconfig/schema11.rs`
for key in &["description"] {
other.remove(*key);
}
}
impl From<CardRequirementSchema11> for CardRequirement {
fn from(r: CardRequirementSchema11) -> Self {
CardRequirement {
@ -203,6 +210,13 @@ pub struct NoteFieldSchema11 {
pub(crate) rtl: bool,
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
// on downgrade/upgrade.
// NOTE: if adding new ones, make sure to update clear_other_field_duplicates()
#[serde(default, deserialize_with = "default_on_invalid")]
pub(crate) description: String,
#[serde(flatten)]
pub(crate) other: HashMap<String, Value>,
}
@ -216,6 +230,7 @@ impl Default for NoteFieldSchema11 {
rtl: false,
font: "Arial".to_string(),
size: 20,
description: String::new(),
other: Default::default(),
}
}
@ -231,6 +246,7 @@ impl From<NoteFieldSchema11> for NoteField {
rtl: f.rtl,
font_name: f.font,
font_size: f.size as u32,
description: f.description,
other: other_to_bytes(&f.other),
},
}
@ -242,6 +258,8 @@ impl From<NoteFieldSchema11> for NoteField {
impl From<NoteField> for NoteFieldSchema11 {
fn from(p: NoteField) -> Self {
let conf = p.config;
let mut other = bytes_to_other(&conf.other);
clear_other_field_duplicates(&mut other);
NoteFieldSchema11 {
name: p.name,
ord: p.ord.map(|o| o as u16),
@ -249,7 +267,8 @@ impl From<NoteField> for NoteFieldSchema11 {
rtl: conf.rtl,
font: conf.font_name,
size: conf.font_size as u16,
other: bytes_to_other(&conf.other),
description: conf.description,
other,
}
}
}

View File

@ -26,7 +26,6 @@
export let hideDelay = 0;
let tooltipObject: Tooltip;
function createTooltip(element: HTMLElement): void {
element.title = tooltip;
tooltipObject = new Tooltip(element, {
@ -39,6 +38,15 @@
}
onDestroy(() => tooltipObject?.dispose());
// hack to update field description tooltips
let previousTooltip: string = tooltip;
$: if (tooltip !== previousTooltip) {
previousTooltip = tooltip;
let element: HTMLElement = tooltipObject["_element"];
tooltipObject.dispose();
createTooltip(element);
}
</script>
<slot {createTooltip} {tooltipObject} />

View File

@ -9,6 +9,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export interface FieldData {
name: string;
description: string;
fontFamily: string;
fontSize: number;
direction: "ltr" | "rtl";
@ -29,6 +30,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts">
import EditingArea from "./EditingArea.svelte";
import LabelContainer from "./LabelContainer.svelte";
import LabelDescription from "./LabelDescription.svelte";
import LabelName from "./LabelName.svelte";
import FieldState from "./FieldState.svelte";
@ -74,7 +76,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
on:click={() => editingArea.focus?.()}
>
<LabelContainer>
<LabelName>{field.name}</LabelName>
<span>
<LabelName>
{field.name}
</LabelName>
{#if field.description}
<LabelDescription description={field.description} />
{/if}
</span>
<FieldState><slot name="field-state" /></FieldState>
</LabelContainer>
<EditingArea

View File

@ -0,0 +1,39 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { descriptionIcon } from "./icons";
import WithTooltip from "../components/WithTooltip.svelte";
import Badge from "../components/Badge.svelte";
export let description: string;
</script>
<span>
<WithTooltip
tooltip={description}
showDelay={250}
offset={[0, 20]}
placement="right"
let:createTooltip
>
<Badge
--icon-align="sub"
iconSize={65}
on:mount={(event) => createTooltip(event.detail.span)}
>
{@html descriptionIcon}
</Badge>
</WithTooltip>
</span>
<style lang="scss">
span {
opacity: 0.4;
&:hover {
opacity: 0.8;
}
}
</style>

View File

@ -2,7 +2,10 @@
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<span class="label-name"><slot /></span>
<span class="label-name">
<slot />
</span>
<style lang="scss">
.label-name {

View File

@ -115,6 +115,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
fieldNames = newFieldNames;
}
let fieldDescriptions: string[] = [];
export function setDescriptions(fs: string[]): void {
fieldDescriptions = fs;
}
let fonts: [string, number, boolean][] = [];
let richTextsHidden: boolean[] = [];
let plainTextsHidden: boolean[] = [];
@ -173,6 +178,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: fieldsData = fieldNames.map((name, index) => ({
name,
description: fieldDescriptions[index],
fontFamily: quoteFontFamily(fonts[index][0]),
fontSize: fonts[index][1],
direction: fonts[index][2] ? "rtl" : "ltr",

View File

@ -32,6 +32,8 @@ export { default as micIcon } from "bootstrap-icons/icons/mic.svg";
export { default as ellipseIcon } from "@mdi/svg/svg/contain.svg";
export { default as functionIcon } from "@mdi/svg/svg/function-variant.svg";
export { default as descriptionIcon } from "bootstrap-icons/icons/info-circle.svg";
export { default as tagIcon } from "@mdi/svg/svg/tag.svg";
export { default as addTagIcon } from "@mdi/svg/svg/tag-plus.svg";
export { default as dotsIcon } from "@mdi/svg/svg/dots-vertical.svg";

View File

@ -85,6 +85,7 @@ async function setupNoteEditor(): Promise<NoteEditorAPI> {
Object.assign(globalThis, {
setFields: noteEditor.setFields,
setDescriptions: noteEditor.setDescriptions,
setFonts: noteEditor.setFonts,
focusField: noteEditor.focusField,
setColorButtons: noteEditor.setColorButtons,