Skip to content

Commit 9504ec8

Browse files
committed
docs(dialogs): add label capitalization guideline + cleanup backlog
New src/widgets/dialogs/AGENTS.md codifies the form-label capitalization convention surfaced during the OpenNotebookDialog2 refinement: - Form labels (multi-word, ending in ":"): sentence case (e.g., "Local root folder:", "Output directory:"). - Form labels for proper nouns / established technical terms: preserve Title Case (e.g., "Personal Access Token:"). - Initialisms (URL, PAT, JSON, HTTP): preserve canonical casing. - Placeholders: sentence case, no trailing period. - Buttons: Title Case. - Window / dialog titles: Title Case. - Tooltips and banner messages: sentence form ending in a period. - Radio/checkbox labels: Title Case. Also documents: - The banner-suppression pattern (RemoteValidation::surfaceInBanner) introduced in OpenNotebookDialog2 for quiet-while-typing UX. - The test-discovery rule: tests find dialog widgets via findChild<>("objectName"), NOT by label text. Label text is a UX concern; objectName is a test concern. They stay decoupled. - A "Known Inconsistencies" cleanup backlog table listing the 9 existing labels that violate the convention (newnotebookdialog2.cpp:72,107,169; managenotebooksdialog2.cpp:89; openvnote3notebookdialog2.cpp:35,41,43; notebooksyncinfodialog2.cpp: 220,226). These are deferred to a separate normalization plan per user direction; documented here so contributors don't accidentally copy the wrong style going forward. Cross-references the broader widgets, controllers, and architecture AGENTS.md docs.
1 parent e349f29 commit 9504ec8

1 file changed

Lines changed: 90 additions & 0 deletions

File tree

src/widgets/dialogs/AGENTS.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Dialogs Module
2+
3+
Dialog widgets (`*Dialog2`) for VNote. Each dialog is paired with a controller in `../../controllers/` that owns business logic; the dialog is the View layer and receives `ServiceLocator&` via constructor.
4+
5+
## Label Capitalization Convention
6+
7+
VNote dialogs use a mixed capitalization scheme. The rules below are the project standard going forward; older dialogs that diverge are tracked under [Known Inconsistencies](#known-inconsistencies-cleanup-backlog) and will be normalized in a follow-up pass.
8+
9+
| Element | Style | Examples |
10+
|---|---|---|
11+
| **Form labels** (multi-word, ending in `:`) | **sentence case** | `"Local root folder:"`, `"Remote URL:"`, `"Output directory:"`, `"Cursor mark:"` |
12+
| **Form labels** that are a proper noun or established technical term | **Title Case** preserved | `"Personal Access Token:"` |
13+
| **Initialisms** (URL, PAT, JSON, HTTP) | preserve their canonical casing inside any label | `"Remote URL:"`, `"JSON path:"` |
14+
| **Placeholders** (the gray helper text inside `QLineEdit`) | **sentence case**, no trailing period | `"Folder to clone into (must not exist or be empty)"`, `"Optional (empty to open as read-only)"` |
15+
| **Buttons** (`QPushButton`, `QDialogButtonBox` buttons) | **Title Case** | `"Open"`, `"Browse"`, `"Disable Sync"`, `"Close Notebook"` |
16+
| **Window / dialog titles** (`setWindowTitle`, `QFileDialog` title) | **Title Case** | `"Open Notebook"`, `"Select Local Root Folder"`, `"Manage Notebooks"` |
17+
| **Tooltips** | sentence form, **end with a period** | `"Remote git URL. Only HTTPS and file:// schemes are supported."` |
18+
| **Banner / info-text messages** (`setInformationText`) | sentence form, **end with a period** | `"Local root folder must be empty (contains 3 item(s))."`, `"Cloning..."` |
19+
| **Radio button / checkbox labels** | **Title Case** | `"Local Folder"`, `"Remote URL"` |
20+
21+
### Why these rules
22+
23+
- **Sentence-case form labels** match the OS-native conventions on macOS and modern GNOME/KDE, and they read faster in dense forms. They also visually disambiguate from buttons.
24+
- **Title-Case buttons + titles** match Windows and Qt's built-in widgets (`QDialogButtonBox` ships with `"Open"`, `"Cancel"`, `"Save"`, etc., already in Title Case).
25+
- **Tooltip periods** make tooltip strings reusable as `qInfo()` log lines and as accessible-name / accessible-description sources.
26+
- **Initialism preservation** prevents the OWASP-style "Personal access token" stripping that confuses GitHub/GitLab users searching for "PAT".
27+
28+
### When in doubt
29+
30+
1. Look at `src/widgets/dialogs/opennotebookdialog2.cpp` and `src/widgets/dialogs/exportdialog2.cpp` — those are the reference dialogs for sentence-case labels.
31+
2. If your new dialog is dominated by proper nouns or domain-specific multi-word terms (sync state, credentials), match the convention of the closest sibling dialog rather than mechanically forcing sentence case.
32+
3. Never change a label string without also grep'ing the codebase for the old string and the translation `.ts` files — labels are user-visible and may be referenced in tests via `findChild<>(...)` (use **object names**, not label text, for test lookups).
33+
34+
### Test-discovery rule
35+
36+
Tests find dialog widgets via `findChild<>("objectName")` — NOT by label text. Every interactive widget in a `*Dialog2` MUST have an `objectName` set via `setObjectName(QLatin1String(kFooName))` where `kFooName` is a top-of-file constant in the dialog's `.cpp`. Changing label TEXT is a UX change; changing an `objectName` is a TEST change. Keep them decoupled.
37+
38+
## Banner Suppression Pattern (Quiet Dialog UX)
39+
40+
Some dialogs (currently `opennotebookdialog2` post `refine-open-notebook-dialog`) intentionally suppress the `ScrollDialog::setInformationText` banner on certain field changes to keep the dialog quiet and stable in size while the user is typing.
41+
42+
Pattern:
43+
1. The validation result struct (`RemoteValidation` inside the dialog) carries an extra `bool surfaceInBanner = false;` flag alongside `valid` and `message`.
44+
2. Each validator branch decides whether the message is "actionable enough to surface". URL-scheme errors are silent (the user is still typing — they don't want a banner blinking). Folder-content errors (non-empty existing dir) surface immediately because the user has stopped typing and clicked a folder.
45+
3. `updateOpenButtonState` reads `surfaceInBanner` and either calls `setInformationText(message, Error)` (surface) or `setInformationText(QString(), Info)` (clear).
46+
4. Clone start / progress / failure / cancel events ALWAYS surface, regardless of `surfaceInBanner`. These are not "while typing" events.
47+
48+
Use this pattern when the dialog has both keystroke-driven validation (noisy) and discrete-action validation (e.g., folder selection). Do NOT use it when every validation message is equally actionable — the regular `setInformationText` flow is simpler.
49+
50+
## Dialog Inventory
51+
52+
| Dialog | Controller | Purpose |
53+
|---|---|---|
54+
| `NewNoteDialog2` | `NewNoteController` | Create a new note |
55+
| `NewFolderDialog2` | `NewFolderController` | Create a new folder |
56+
| `NewNotebookDialog2` | `NewNotebookController` | Create a new notebook |
57+
| `OpenNotebookDialog2` | `OpenNotebookController` | Open an existing notebook (local OR remote clone) |
58+
| `ManageNotebooksDialog2` | `ManageNotebooksController` | Notebook management |
59+
| `ImportFolderDialog2` | `ImportFolderController` | Import an external folder as a notebook |
60+
| `OpenVNote3NotebookDialog2` | (legacy migration) | Import a VNote3 notebook |
61+
| `NotebookSyncInfoDialog2` | `NotebookSyncInfoController` | View / edit notebook sync configuration |
62+
| `ExportDialog2` | (`export` controller / inline) | Export notes |
63+
| `NewQuickAccessItemDialog` | (inline) | Add a quick-access entry (used inside Settings) |
64+
| `SnippetInfoWidget2` / snippet dialogs | `SnippetController` | Snippet metadata |
65+
66+
## Known Inconsistencies (Cleanup Backlog)
67+
68+
These labels violate the sentence-case convention and SHOULD be normalized in a focused follow-up plan. Listed here so a future contributor doesn't accidentally copy the wrong style:
69+
70+
| File | Line | Current | Target |
71+
|---|---|---|---|
72+
| `newnotebookdialog2.cpp` | 72 | `"Root Folder:"` | `"Root folder:"` |
73+
| `newnotebookdialog2.cpp` | 107 | `"Sync Method:"` | `"Sync method:"` |
74+
| `newnotebookdialog2.cpp` | 169 | `"Assets Folder:"` | `"Assets folder:"` |
75+
| `managenotebooksdialog2.cpp` | 89 | `"Root Folder:"` | `"Root folder:"` |
76+
| `openvnote3notebookdialog2.cpp` | 35 | `"Source Folder:"` | `"Source folder:"` |
77+
| `openvnote3notebookdialog2.cpp` | 41 | `"Destination Folder:"` | `"Destination folder:"` |
78+
| `openvnote3notebookdialog2.cpp` | 43 | `"Notebook Name:"` | `"Notebook name:"` |
79+
| `notebooksyncinfodialog2.cpp` | 220 | `"Last Sync:"` | `"Last sync:"` |
80+
| `notebooksyncinfodialog2.cpp` | 226 | `"Current State:"` | `"Current state:"` |
81+
82+
`"Personal Access Token:"` and `"Remote URL:"` are NOT in this list — they are correctly preserved per the proper-noun / initialism rules above.
83+
84+
The cleanup should also update each dialog's translation `.ts` entries (`translations/`) and re-run `lupdate`. A single PR is fine because each change is a one-line label edit with no behavior change.
85+
86+
## Related Modules
87+
88+
- [`../AGENTS.md`](../AGENTS.md) — Widget module overview, ViewArea2 framework, `2` suffix convention
89+
- [`../../controllers/AGENTS.md`](../../controllers/AGENTS.md) — Controllers paired with these dialogs
90+
- [`../../../AGENTS.md`](../../../AGENTS.md) — Project-level architecture, MVC rules, code style

0 commit comments

Comments
 (0)