Skip to content

String tuning follows selected chord spelling#33533

Open
pedroataidefonseca wants to merge 2 commits into
musescore:mainfrom
pedroataidefonseca:Feature/#32018
Open

String tuning follows selected chord spelling#33533
pedroataidefonseca wants to merge 2 commits into
musescore:mainfrom
pedroataidefonseca:Feature/#32018

Conversation

@pedroataidefonseca
Copy link
Copy Markdown
Contributor

Resolves: #32018

Summary

This pull request makes string tunings follow the note spelling system
selected for chord symbols.

Previously, string tunings could display note names differently from the
rest of the score. With this change, the rendered string tuning text,
accessible labels, and popup note values now all use the active
chord-symbol spelling.

Behavior

String tunings now display localized note names consistently across the
score and the editing popup. This includes spelling systems such as
German, Solfeggio, and French, while preserving the expected casing and
special note-name behavior for each system.

The popup was also updated so that note values are both displayed and
parsed according to the selected spelling. Localized note input is
accepted, and the step controls continue to work with the active note
representation.

Implementation

To support this, the popup note parsing and formatting logic was
refactored into reusable helper functions. On the engraving side,
string tuning text generation and accessibility output now derive note
names from the current chord-symbol spelling style.

Testing

The patch adds engraving and notation scene tests covering:

  • localized string tuning text
  • accessibility note names
  • popup note formatting
  • localized popup input parsing
  • invalid popup inputs
  • I signed the CLA
  • The title of the PR describes the problem it addresses
  • Each commit's message describes its purpose and effects, and references the issue it resolves
  • If changes are extensive, there is a sequence of easily reviewable commits
  • The code in the PR follows the coding rules
  • There are no unnecessary changes
  • The code compiles and runs on my machine, preferably after each commit individually
  • I created a unit test or vtest to verify the changes I made (if applicable)

Use chord symbol spelling for string tunings text,
accessible labels, and popup note values so note
names follow the selected spelling system
(e.g. "H" instead of "B" for German spelling).

Added engraving and notation scene tests covering
localized string tunings text, popup note
formatting, and popup input parsing across
supported spelling systems.

Closes musescore#32018

Co-authored-by: Rodrigo Bandeira <rodrigobandeira@tecnico.ulisboa.pt>
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR centralizes string-tuning pitch formatting and parsing into new mu::notation utilities, adds engraving::stringTuningPitchName, propagates NoteSpellingType through StringTuningsSettingsModel and StringTuningsItem, updates the QML popup to use text input plus adjust controls feeding setStringValue/setStringPitchValue, and adds unit tests and CMake updates verifying localized formatting and parsing.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: string tunings now follow the selected chord symbol spelling system, which is the primary objective of this PR.
Description check ✅ Passed The PR description is comprehensive, covering the problem, behavior changes, implementation approach, and testing. Most PR template checklist items are marked complete.
Linked Issues check ✅ Passed The PR successfully addresses issue #32018 by making string tunings follow the chord symbol spelling system, including engraving updates and popup parsing/display changes.
Out of Scope Changes check ✅ Passed All code changes are directly related to implementing the string tuning spelling synchronization feature and do not include unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml (1)

178-178: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Accessibility regression: accessible name now exposes the raw MIDI pitch instead of the spelled note.

Value %1 is interpolated with valueControl.currentValue (an integer pitch). Before this refactor the equivalent control surfaced the localized note string. Screen readers will now announce e.g. "Value 64" rather than "Value E4" / "Value Mi4". Bind this to the spelled text so screen readers reflect the same content as the visible field and stay consistent with the chord-symbol spelling that this PR is otherwise threading through.

♻️ Proposed fix
-                    navigation.accessible.name: visibleBox.navigation.accessible.name + " " + qsTrc("notation", "Value %1").arg(valueControl.currentValue)
+                    navigation.accessible.name: visibleBox.navigation.accessible.name + " " + qsTrc("notation", "Value %1").arg(valueControl.currentText)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml`
at line 178, The accessible name currently interpolates the raw integer pitch
via valueControl.currentValue; change that to use the spelled/localized note
string from the control (e.g., valueControl.spelledText or
valueControl.spelledNote) so screen readers announce the note name (like "E4")
not the MIDI number; update the expression assigned to
navigation.accessible.name (which currently builds on
visibleBox.navigation.accessible.name + " " + qsTrc("notation", "Value
%1").arg(...)) to pass the spelled-note property instead of
valueControl.currentValue.
🧹 Nitpick comments (1)
src/engraving/dom/stringtunings.cpp (1)

40-64: ⚖️ Poor tradeoff

Consider extracting a shared pitch-name helper to avoid drift with stringTuningPitchToString.

tuningPitchName here and mu::notation::stringTuningPitchToString in stringtuningsutils.cpp implement effectively the same algorithm (pitch validation, pitch2tpc with flat/sharp preference, tpc2name, the GERMAN + TPC_B_B accidental-clear special case, and Unicode conversion). Any future fix (e.g., additional spelling rules, more accidental special cases) has to be applied in two places or the engraving text and the popup formatting will silently disagree.

A small engraving-level helper such as engraving::stringTuningPitchName(int pitch, bool useFlat, NoteSpellingType spelling, bool withOctave) could be the single implementation; the notationscene utility would just forward to it (wrapping the result in QString).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/dom/stringtunings.cpp` around lines 40 - 64, Extract the
duplicate pitch-name logic into a single helper (suggested signature:
engraving::stringTuningPitchName(int pitch, bool useFlat, NoteSpellingType
spelling, bool withOctave)) that performs pitchIsValid, pitch2tpc using
useFlat/Prefer::FLATS vs Prefer::SHARPS, tpc2name, the NoteSpellingType::GERMAN
+ TPC_B_B accidental.clear() special-case,
convertPitchStringFlatsAndSharpsToUnicode, and the octave math using
PITCH_DELTA_OCTAVE; then replace tuningPitchName in stringtunings.cpp and
mu::notation::stringTuningPitchToString in stringtuningsutils.cpp to call this
new helper (wrapping/convert to QString in the notation utility as needed) so
all logic lives in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml`:
- Line 221: The suppressNextTextCommit flag (property suppressNextTextCommit) is
being set unconditionally in onIncreaseButtonClicked/onDecreaseButtonClicked and
can persist if valueInput does not have focus, causing the next user text commit
to be dropped in onTextEditingFinished; fix by gating or resetting the flag:
either only set suppressNextTextCommit when valueInput.focus is true (i.e. check
valueInput.focus before arming the flag in
onIncreaseButtonClicked/onDecreaseButtonClicked) or add a focus handler on
valueInput (onFocusChanged / onActiveFocusChanged) that clears
suppressNextTextCommit when the field gains focus so it cannot leak between
interactions, and ensure currentText/setStringValue behavior in
onTextEditingFinished remains unchanged except for the guarded flag.

---

Outside diff comments:
In
`@src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml`:
- Line 178: The accessible name currently interpolates the raw integer pitch via
valueControl.currentValue; change that to use the spelled/localized note string
from the control (e.g., valueControl.spelledText or valueControl.spelledNote) so
screen readers announce the note name (like "E4") not the MIDI number; update
the expression assigned to navigation.accessible.name (which currently builds on
visibleBox.navigation.accessible.name + " " + qsTrc("notation", "Value
%1").arg(...)) to pass the spelled-note property instead of
valueControl.currentValue.

---

Nitpick comments:
In `@src/engraving/dom/stringtunings.cpp`:
- Around line 40-64: Extract the duplicate pitch-name logic into a single helper
(suggested signature: engraving::stringTuningPitchName(int pitch, bool useFlat,
NoteSpellingType spelling, bool withOctave)) that performs pitchIsValid,
pitch2tpc using useFlat/Prefer::FLATS vs Prefer::SHARPS, tpc2name, the
NoteSpellingType::GERMAN + TPC_B_B accidental.clear() special-case,
convertPitchStringFlatsAndSharpsToUnicode, and the octave math using
PITCH_DELTA_OCTAVE; then replace tuningPitchName in stringtunings.cpp and
mu::notation::stringTuningPitchToString in stringtuningsutils.cpp to call this
new helper (wrapping/convert to QString in the notation utility as needed) so
all logic lives in one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 930684f7-468a-4edb-99ca-ea2ec2ebb5ec

📥 Commits

Reviewing files that changed from the base of the PR and between 526b45f and f33cc95.

📒 Files selected for processing (11)
  • src/engraving/dom/stringtunings.cpp
  • src/engraving/tests/CMakeLists.txt
  • src/engraving/tests/stringtunings_tests.cpp
  • src/notationscene/qml/MuseScore/NotationScene/CMakeLists.txt
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/stringtuningssettingsmodel.cpp
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/stringtuningssettingsmodel.h
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/stringtuningsutils.cpp
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/stringtuningsutils.h
  • src/notationscene/qml/MuseScore/NotationScene/tests/CMakeLists.txt
  • src/notationscene/qml/MuseScore/NotationScene/tests/stringtuningsutils_tests.cpp

Use spelled note names in the string tunings popup
accessibility text so screen readers announce
note values instead of raw MIDI numbers.

Move string tuning pitch-name formatting into a
shared engraving helper and reuse it from the
popup utility code to keep spelling behavior in
one place.

Only suppress the next text commit after
arrow-button stepping when the text field had
focus, preventing the flag from leaking into
later manual edits.

Closes musescore#32018

Co-authored-by: Rodrigo Bandeira <rodrigobandeira@tecnico.ulisboa.pt>
@pedroataidefonseca
Copy link
Copy Markdown
Contributor Author

This latest commit addresses the issues raised by @coderabbitai, that should now be fixed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get all review comments from coderabbitai on this PR
gh pr view 33533 --repo musescore/MuseScore --json reviews,comments

# Get review comments (inline)
gh api repos/musescore/MuseScore/pulls/33533/comments --paginate | jq '[.[] | select(.user.login == "coderabbitai") | {id: .id, path: .path, line: .line, body: .body, resolved: .resolved}]'

Length of output: 36855


@pedroataidefonseca Thanks for the heads-up! I'll kick off a fresh review of the latest commit to verify all three issues have been addressed:

  1. The suppressNextTextCommit flag leaking across interactions when valueInput doesn't have focus
  2. The accessibility regression where the accessible name exposed the raw MIDI pitch instead of the localized note string
  3. The duplicated pitch-name logic between tuningPitchName and stringTuningPitchToString
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/engraving/dom/stringtunings.cpp (1)

50-50: ⚡ Quick win

NoteCaseType::CAPITAL preserves SOLFEGGIO/FRENCH base casing (it won’t force uppercase).

tpc2name builds SOLFEGGIO/FRENCH names as {"Fa","Do","Sol","Re","La","Mi","Si"} (with French Re) and then applies casing changes only for LOWER/UPPER; NoteCaseType::CAPITAL (and AUTO) leaves the constructed casing unchanged. So this stringtunings.cpp call won’t break Solfeggio/French casing by uppercasing—use NoteCaseType::LOWER only if the PR specifically requires lowercase "do"/"ré".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/dom/stringtunings.cpp` at line 50, The call to tpc2name(tpc,
spelling, NoteCaseType::CAPITAL, step, accidental) is leaving Solfeggio/French
base casing intact (CAPITAL does not force uppercase), so if the PR intended to
produce lowercase names (e.g., "do"/"ré") change the NoteCaseType to
NoteCaseType::LOWER in this call; otherwise keep CAPITAL as-is to preserve the
original Solfeggio/French capitalization. Ensure you update the call site
referencing tpc2name and the NoteCaseType enum value accordingly.
src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml (1)

219-222: 💤 Low value

Redundant spelledNote property duplicates currentText.

Both currentText and spelledNote are bound to the same expression delegateItem.modelData["valueStr"]. The accessibility label at line 178 could reference valueControl.currentText directly, and spelledNote could be removed to reduce surface area. If the intent is to later differentiate (e.g., spelled-without-octave for screen readers vs. visible text with octave), consider documenting that intent; otherwise this is dead duplication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml`
around lines 219 - 222, spelledNote duplicates currentText by binding both to
delegateItem.modelData["valueStr"]; remove the redundant property spelledNote
and update any references (e.g. the accessibility label that currently could use
spelledNote) to use valueControl.currentText or currentText directly; if you
intended different semantics later, add a clear comment above currentText
explaining the planned differentiation before reintroducing a separate
spelledNote property.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/engraving/dom/stringtunings.cpp`:
- Line 50: The call to tpc2name(tpc, spelling, NoteCaseType::CAPITAL, step,
accidental) is leaving Solfeggio/French base casing intact (CAPITAL does not
force uppercase), so if the PR intended to produce lowercase names (e.g.,
"do"/"ré") change the NoteCaseType to NoteCaseType::LOWER in this call;
otherwise keep CAPITAL as-is to preserve the original Solfeggio/French
capitalization. Ensure you update the call site referencing tpc2name and the
NoteCaseType enum value accordingly.

In
`@src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml`:
- Around line 219-222: spelledNote duplicates currentText by binding both to
delegateItem.modelData["valueStr"]; remove the redundant property spelledNote
and update any references (e.g. the accessibility label that currently could use
spelledNote) to use valueControl.currentText or currentText directly; if you
intended different semantics later, add a clear comment above currentText
explaining the planned differentiation before reintroducing a separate
spelledNote property.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 028f4062-4019-42e9-8308-cbbc3ca58c74

📥 Commits

Reviewing files that changed from the base of the PR and between f33cc95 and c5464aa.

📒 Files selected for processing (4)
  • src/engraving/dom/stringtunings.cpp
  • src/engraving/dom/stringtunings.h
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/StringTuningsPopup.qml
  • src/notationscene/qml/MuseScore/NotationScene/elementpopups/stringtuningsutils.cpp

@Jojo-Schmitz
Copy link
Copy Markdown
Contributor

Would this also fix #20673 ?

@pedroataidefonseca
Copy link
Copy Markdown
Contributor Author

@Jojo-Schmitz No, the primary focus of this implementation was to display note tunings in the same language as the selected chord spelling for consistency, as mentioned in the corresponding feature (#32018).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

String tuning (and other elements displaying note names) should use the same note name spelling as chords

5 participants