Add an option to use a dialog to enter the name of a new label

Motivation:
1. The text boxes in the label track are not fully accessible for users of screen readers, and I don't think that they can be made to be fully accessible using the accessibility API used by wxWidgets. When such an edit box becomes the focus, this is not announced, and for NVDA users typed characters are not echoed.

2. Provides a work around for bugs 1778 (cannot type diacritics into text label), and 1804 (Windows: Labels do not accept IME (Chinese/Japanese) input).

Fix: Provide an option for a dialog for entering the name. The text box in the dialog is accessible for screen readers. On windows the text box receives wm_keydown and wm_char messages and so is a work around for bug 1804. Being a standard text box, it will presumably be a work around for bug 1778.

1. There is a new option in track behaviors: "Use dialog for the name of new label", which is off by default.

2. When using the commands "Add label at selection" and "Add label at playback position", when the dialog closes, focus is returned to the track which was the focus before the dialog opened. I think this is more convenient for users of screen readers.
This commit is contained in:
David Bailes 2018-03-21 14:16:05 +00:00
parent a06e845b2f
commit a13e7191c4
7 changed files with 105 additions and 25 deletions

View File

@ -2098,10 +2098,24 @@ bool LabelTrack::OnChar(SelectedRegion &WXUNUSED(newSel), wxKeyEvent & event)
event.Skip();
return false;
}
SetSelected(true);
bool useDialog;
AudacityProject *p = GetActiveProject();
AddLabel(p->mViewInfo.selectedRegion);
p->PushState(_("Added label"), _("Label"));
gPrefs->Read(wxT("/Gui/DialogForNameNewLabel"), &useDialog, false);
if (useDialog) {
wxString title;
if (p->DialogForLabelName(charCode, title) == wxID_CANCEL) {
return false;
}
SetSelected(true);
AddLabel(p->mViewInfo.selectedRegion, title, -2);
p->PushState(_("Added label"), _("Label"));
return false;
}
else {
SetSelected(true);
AddLabel(p->mViewInfo.selectedRegion);
p->PushState(_("Added label"), _("Label"));
}
}
//

View File

@ -8461,8 +8461,54 @@ void AudacityProject::OnRescanDevices(const CommandContext &WXUNUSED(context) )
DeviceManager::Instance()->Rescan();
}
int AudacityProject::DialogForLabelName(const wxString& initialValue, wxString& value)
{
wxPoint position = mTrackPanel->FindTrackRect(mTrackPanel->GetFocusedTrack(), false).GetBottomLeft();
// the start of the text in the text box will be roughly in line with Audacity's edit cursor
position.x += mTrackPanel->GetLabelWidth() + mViewInfo.TimeToPosition(mViewInfo.selectedRegion.t0()) - 40;
position.y += 2; // just below the bottom of the track
position = mTrackPanel->ClientToScreen(position);
AudacityTextEntryDialog dialog{ this,
_("Name:"),
_("New label"),
initialValue,
wxOK | wxCANCEL,
position };
// keep the dialog within Audacity's window, so that the dialog is always fully visible
wxRect dialogScreenRect = dialog.GetScreenRect();
wxRect projScreenRect = GetScreenRect();
wxPoint max = projScreenRect.GetBottomRight() + wxPoint{ -dialogScreenRect.width, -dialogScreenRect.height };
if (dialogScreenRect.x > max.x) {
position.x = max.x;
dialog.Move(position);
}
if (dialogScreenRect.y > max.y) {
position.y = max.y;
dialog.Move(position);
}
dialog.SetInsertionPointEnd(); // because, by default, initial text is selected
int status = dialog.ShowModal();
if (status != wxID_CANCEL) {
value = dialog.GetValue();
value.Trim(true).Trim(false);
}
return status;
}
int AudacityProject::DoAddLabel(const SelectedRegion &region, bool preserveFocus)
{
wxString title; // of label
bool useDialog;
gPrefs->Read(wxT("/GUI/DialogForNameNewLabel"), &useDialog, false);
if (useDialog) {
if (DialogForLabelName(wxEmptyString, title) == wxID_CANCEL)
return -1; // index
}
LabelTrack *lt = NULL;
// If the focused track is a label track, use that
@ -8502,28 +8548,36 @@ int AudacityProject::DoAddLabel(const SelectedRegion &region, bool preserveFocus
// SelectNone();
lt->SetSelected(true);
int focusTrackNumber = -1;
if (pFocusedTrack && preserveFocus) {
// Must remember the track to re-focus after finishing a label edit.
// do NOT identify it by a pointer, which might dangle! Identify
// by position.
TrackListIterator iter(GetTracks());
Track *track = iter.First();
do
++focusTrackNumber;
while (track != pFocusedTrack &&
NULL != (track = iter.Next()));
if (!track)
// How could we not find it?
focusTrackNumber = -1;
int focusTrackNumber;
if (useDialog) {
focusTrackNumber = -2;
}
else {
focusTrackNumber = -1;
if (pFocusedTrack && preserveFocus) {
// Must remember the track to re-focus after finishing a label edit.
// do NOT identify it by a pointer, which might dangle! Identify
// by position.
TrackListIterator iter(GetTracks());
Track *track = iter.First();
do
++focusTrackNumber;
while (track != pFocusedTrack &&
NULL != (track = iter.Next()));
if (!track)
// How could we not find it?
focusTrackNumber = -1;
}
}
int index = lt->AddLabel(region, wxString(), focusTrackNumber);
int index = lt->AddLabel(region, title, focusTrackNumber);
PushState(_("Added label"), _("Label"));
RedrawProject();
mTrackPanel->EnsureVisible((Track *)lt);
if (!useDialog) {
mTrackPanel->EnsureVisible((Track *)lt);
}
mTrackPanel->SetFocus();
return index;

View File

@ -49,6 +49,7 @@ CommandFlag GetUpdateFlags(bool checkActive = false);
//Adds label and returns index of label in labeltrack.
int DoAddLabel(const SelectedRegion& region, bool preserveFocus = false);
int DialogForLabelName(const wxString& initialValue, wxString& value);
private:
double NearestZeroCrossing(double t0);

View File

@ -356,6 +356,11 @@ public:
void MakeParentRedrawScrollbars();
// If label, rectangle includes track control panel only.
// If !label, rectangle includes all of that, and the vertical ruler, and
// the proper track area.
wxRect FindTrackRect( const Track * target, bool label );
protected:
void MakeParentModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
@ -372,19 +377,14 @@ protected:
void HandleMotion
( const TrackPanelMouseState &tpmState, bool doHit = true );
// If label, rectangle includes track control panel only.
// If !label, rectangle includes all of that, and the vertical ruler, and
// the proper track area.
wxRect FindTrackRect( const Track * target, bool label );
int GetVRulerWidth() const;
int GetVRulerOffset() const { return mTrackInfo.GetTrackInfoWidth(); }
public:
int GetLabelWidth() const { return mTrackInfo.GetTrackInfoWidth() + GetVRulerWidth(); }
// JKC Nov-2011: These four functions only used from within a dll such as mod-track-panel
// They work around some messy problems with constructors.
public:
const TrackList * GetTracks() const { return mTracks.get(); }
TrackList * GetTracks() { return mTracks.get(); }
ViewInfo * GetViewInfo(){ return mViewInfo;}

View File

@ -82,6 +82,9 @@ void TracksBehaviorsPrefs::PopulateOrExchange(ShuttleGui & S)
S.TieCheckBox(_("&Type to create a label"),
wxT("/GUI/TypeToCreateLabel"),
true);
S.TieCheckBox(_("Use dialog for the &name of a new label"),
wxT("/GUI/DialogForNameNewLabel"),
false);
#ifdef EXPERIMENTAL_SCROLLING_LIMITS
S.TieCheckBox(_("Enable scrolling left of &zero"),
ScrollingPreferenceKey(),

View File

@ -237,3 +237,9 @@ extern wxString AudacityMessageBoxCaptionStr()
{
return _("Message");
}
void AudacityTextEntryDialog::SetInsertionPointEnd()
{
// m_textctrl is protected member of wxTextEntryDialog
m_textctrl->SetInsertionPointEnd();
}

View File

@ -118,6 +118,8 @@ public:
: wxTabTraversalWrapper< wxTextEntryDialog>
( parent, message, caption, value, style, pos )
{}
void SetInsertionPointEnd();
};
/**************************************************************************//**