From 5b2b6df9361205e38ad8daf5ba9ad477709a8641 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 6 May 2016 21:39:19 -0400 Subject: [PATCH 1/2] Accessibility for time ruler --- src/widgets/Ruler.cpp | 256 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 4a59ad801..f0fca18d9 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -1751,6 +1751,258 @@ void QuickPlayIndicatorOverlay::Draw **********************************************************************/ +#if wxUSE_ACCESSIBILITY + +class RulerAx final : public wxWindowAccessible +{ +public: + RulerAx(wxWindow * window); + + virtual ~ RulerAx(); + + // Retrieves the address of an IDispatch interface for the specified child. + // All objects must support this property. + wxAccStatus GetChild(int childId, wxAccessible** child) override; + + // Gets the number of children. + wxAccStatus GetChildCount(int* childCount) override; + + // Gets the default action for this object (0) or > 0 (the action for a child). + // Return wxACC_OK even if there is no action. actionName is the action, or the empty + // string if there is no action. + // The retrieved string describes the action that is performed on an object, + // not what the object does as a result. For example, a toolbar button that prints + // a document has a default action of "Press" rather than "Prints the current document." + wxAccStatus GetDefaultAction(int childId, wxString *actionName) override; + + // Returns the description for this object or a child. + wxAccStatus GetDescription(int childId, wxString *description) override; + + // Gets the window with the keyboard focus. + // If childId is 0 and child is NULL, no object in + // this subhierarchy has the focus. + // If this object has the focus, child should be 'this'. + wxAccStatus GetFocus(int *childId, wxAccessible **child) override; + + // Returns help text for this object or a child, similar to tooltip text. + wxAccStatus GetHelpText(int childId, wxString *helpText) override; + + // Returns the keyboard shortcut for this object or child. + // Return e.g. ALT+K + wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override; + + // Returns the rectangle for this object (id = 0) or a child element (id > 0). + // rect is in screen coordinates. + wxAccStatus GetLocation(wxRect& rect, int elementId) override; + + // Gets the name of the specified object. + wxAccStatus GetName(int childId, wxString *name) override; + + // Returns a role constant. + wxAccStatus GetRole(int childId, wxAccRole *role) override; + + // Gets a variant representing the selected children + // of this object. + // Acceptable values: + // - a null variant (IsNull() returns TRUE) + // - a list variant (GetType() == wxT("list")) + // - an integer representing the selected child element, + // or 0 if this object is selected (GetType() == wxT("long")) + // - a "void*" pointer to a wxAccessible child object + wxAccStatus GetSelections(wxVariant *selections) override; + + // Returns a state constant. + wxAccStatus GetState(int childId, long* state) override; + + // Returns a localized string representing the value for the object + // or child. + wxAccStatus GetValue(int childId, wxString* strValue) override; +}; + +RulerAx::RulerAx(wxWindow * window) : +wxWindowAccessible( window ) +{ +} + +RulerAx::~RulerAx() +{ +} + +// Retrieves the address of an IDispatch interface for the specified child. +// All objects must support this property. +wxAccStatus RulerAx::GetChild( int childId, wxAccessible** child ) +{ + if( childId == wxACC_SELF ) + { + *child = this; + } + else + { + *child = NULL; + } + + return wxACC_OK; +} + +// Gets the number of children. +wxAccStatus RulerAx::GetChildCount(int* childCount) +{ + *childCount = 4; + + return wxACC_OK; +} + +// Gets the default action for this object (0) or > 0 (the action for a child). +// Return wxACC_OK even if there is no action. actionName is the action, or the empty +// string if there is no action. +// The retrieved string describes the action that is performed on an object, +// not what the object does as a result. For example, a toolbar button that prints +// a document has a default action of "Press" rather than "Prints the current document." +wxAccStatus RulerAx::GetDefaultAction( int WXUNUSED(childId), wxString *actionName ) +{ + actionName->Clear(); + + return wxACC_OK; +} + +// Returns the description for this object or a child. +wxAccStatus RulerAx::GetDescription( int WXUNUSED(childId), wxString *description ) +{ + description->Clear(); + + return wxACC_OK; +} + +// Gets the window with the keyboard focus. +// If childId is 0 and child is NULL, no object in +// this subhierarchy has the focus. +// If this object has the focus, child should be 'this'. +wxAccStatus RulerAx::GetFocus(int* childId, wxAccessible** child) +{ + *childId = 0; + *child = this; + + return wxACC_OK; +} + +// Returns help text for this object or a child, similar to tooltip text. +wxAccStatus RulerAx::GetHelpText( int WXUNUSED(childId), wxString *helpText ) +{ + helpText->Clear(); + + return wxACC_OK; +} + +// Returns the keyboard shortcut for this object or child. +// Return e.g. ALT+K +wxAccStatus RulerAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut ) +{ + shortcut->Clear(); + + return wxACC_OK; +} + +// Returns the rectangle for this object (id = 0) or a child element (id > 0). +// rect is in screen coordinates. +wxAccStatus RulerAx::GetLocation( wxRect& rect, int WXUNUSED(elementId) ) +{ + wxWindow *w = wxDynamicCast( GetWindow() ); + + rect = w->GetRect(); + rect.SetPosition( w->GetParent()->ClientToScreen( rect.GetPosition() ) ); + + return wxACC_OK; +} + +// Gets the name of the specified object. +wxAccStatus RulerAx::GetName(int WXUNUSED(childId), wxString* name) +{ + *name = _("Time Ruler"); + + return wxACC_OK; +} + +// Returns a role constant. +wxAccStatus RulerAx::GetRole(int childId, wxAccRole* role) +{ + switch( childId ) + { + case 0: + *role = wxROLE_SYSTEM_TABLE; + break; + + default: + *role = wxROLE_SYSTEM_ROW; + break; + } + + return wxACC_OK; +} + +// Gets a variant representing the selected children +// of this object. +// Acceptable values: +// - a null variant (IsNull() returns TRUE) +// - a list variant (GetType() == wxT("list")) +// - an integer representing the selected child element, +// or 0 if this object is selected (GetType() == wxT("long")) +// - a "void*" pointer to a wxAccessible child object +wxAccStatus RulerAx::GetSelections( wxVariant * WXUNUSED(selections) ) +{ + return wxACC_NOT_IMPLEMENTED; +} + +// Returns a state constant. +wxAccStatus RulerAx::GetState(int childId, long* state) +{ + wxSlider *s = wxDynamicCast( GetWindow(), wxSlider ); + + switch( childId ) + { + case 0: + *state = wxACC_STATE_SYSTEM_FOCUSABLE; + break; + + case 1: + if( s->GetValue() == s->GetMin() ) + { + *state = wxACC_STATE_SYSTEM_INVISIBLE; + } + break; + + case 3: + if( s->GetValue() == s->GetMax() ) + { + *state = wxACC_STATE_SYSTEM_INVISIBLE; + } + break; + } + + // Do not use mSliderIsFocused is not set until after this method + // is called. + *state |= ( s == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0 ); + + return wxACC_OK; +} + +// Returns a localized string representing the value for the object +// or child. +wxAccStatus RulerAx::GetValue(int childId, wxString* strValue) +{ + wxSlider *s = wxDynamicCast( GetWindow(), wxSlider ); + + if( childId == 0 ) + { + strValue->Printf( mFmt, s->GetValue() ); + + return wxACC_OK; + } + + return wxACC_NOT_SUPPORTED; +} + +#endif // wxUSE_ACCESSIBILITY + #include "../ViewInfo.h" #include "../AColor.h" @@ -1856,6 +2108,10 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent, wxCommandEventHandler(AdornedRulerPanel::OnCapture), NULL, this); + +#if wxUSE_ACCESSIBILITY + SetAccessible( safenew RulerAx { this } ); +#endif } AdornedRulerPanel::~AdornedRulerPanel() From 34d0a5201102cbe929440b6134dfc54fb087622e Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 9 May 2016 17:56:38 -0400 Subject: [PATCH 2/2] Better updating of the white guideline for scrub, and hiding it when scrub stops --- src/tracks/ui/Scrubbing.cpp | 50 +++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index d56891363..3a420820b 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -389,6 +389,8 @@ void Scrubber::StopScrubbing() const auto ctb = mProject->GetControlToolBar(); ctb->SetPlay(false, ControlToolBar::PlayAppearance::Straight); } + + mProject->GetRulerPanel()->HideQuickPlayIndicator(); } void Scrubber::SetScrollScrubbing(bool scrollScrubbing) @@ -465,24 +467,15 @@ void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event) void Scrubber::Forwarder::OnMouse(wxMouseEvent &event) { + auto ruler = scrubber.mProject->GetRulerPanel(); auto isScrubbing = scrubber.IsScrubbing(); - if (!isScrubbing && scrubber.HasStartedScrubbing()) { - if (!event.HasAnyModifiers() && - event.GetEventType() == wxEVT_MOTION) { - - // Really start scrub if motion is far enough - auto ruler = scrubber.mProject->GetRulerPanel(); - auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x; - scrubber.MaybeStartScrubbing(xx); - } - } - else if (isScrubbing && !event.HasAnyModifiers()) { + if (isScrubbing && !event.HasAnyModifiers()) { if(event.LeftDown() || (event.LeftIsDown() && event.Dragging())) { scrubber.mScrubSeekPress = true; - auto ruler = scrubber.mProject->GetRulerPanel(); auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x; ruler->UpdateQuickPlayPos(xx); + ruler->ShowQuickPlayIndicator(); } else if (event.m_wheelRotation) { double steps = event.m_wheelRotation / @@ -567,9 +560,25 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) event.Skip(); Scrubber &scrubber = GetScrubber(); - if (!GetScrubber().IsScrubbing()) { - mNextScrubRect = wxRect(); - return; + const auto isScrubbing = scrubber.IsScrubbing(); + const auto ruler = mProject->GetRulerPanel(); + auto position = ::wxGetMousePosition(); + + { + auto xx = ruler->ScreenToClient(position).x; + ruler->UpdateQuickPlayPos(xx); + + if(!isScrubbing && scrubber.HasStartedScrubbing()) { + // Really start scrub if motion is far enough + scrubber.MaybeStartScrubbing(xx); + } + + if (!isScrubbing) { + mNextScrubRect = wxRect(); + return; + } + else + ruler->ShowQuickPlayIndicator(); } // Call ContinueScrubbing() here in the timer handler @@ -587,9 +596,7 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) trackPanel->GetSize(&panelWidth, &panelHeight); // Where's the mouse? - int xx, yy; - ::wxGetMousePosition(&xx, &yy); - trackPanel->ScreenToClient(&xx, &yy); + position = trackPanel->ScreenToClient(position); const bool seeking = scrubber.PollIsSeeking(); @@ -599,7 +606,7 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL scrubber.IsScrollScrubbing() ? scrubber.FindScrubSpeed - (seeking, mProject->GetViewInfo().PositionToTime(xx, trackPanel->GetLeftOffset())) + (seeking, mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset())) : #endif maxScrubSpeed; @@ -624,11 +631,12 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) dc.SetFont(labelFont); dc.GetTextExtent(mNextScrubSpeedText, &width, &height); } - xx = std::max(0, std::min(panelWidth - width, xx - width / 2)); + const auto xx = + std::max(0, std::min(panelWidth - width, position.x - width / 2)); // Put the text above the cursor, if it fits. enum { offset = 20 }; - yy -= height + offset; + auto yy = position.y - height + offset; if (yy < 0) yy += height + 2 * offset; yy = std::max(0, std::min(panelHeight - height, yy));