1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-10 00:51:13 +02:00

Give the keyboard user the ability to move among all modeless windows

With realtime preview, the keyboard user will need this to get
back and forth between the effect and the project window.  Actualy,
the "problem" existed before this since the keyboard user couldn't
switch between project and Plot Spectrum windows.

The normal flags-based keyboard handling would not work since it
depends on the state of the project and requires a project window
to have the current focus.

In this new case, a modeless dialog may actually have the focus and
using the "switch window" key combo may happen in nearly any mode, like
while playing.

So, I "created" the concept of "meta" commands...ones that do not have
the restrictions mentioned above.  The should be used sparingly and
you must be careful about what happens within their handlers.
This commit is contained in:
lllucius 2014-11-26 18:17:38 +00:00
parent bc6583b62d
commit 4b756ce7d6
6 changed files with 190 additions and 6 deletions

View File

@ -1752,9 +1752,6 @@ void AudacityApp::OnKeyDown(wxKeyEvent & event)
if (!prj)
return;
if (prj != wxGetTopLevelParent(wxWindow::FindFocus()))
return;
if (prj->HandleKeyDown(event))
event.Skip(false);
}
@ -1773,9 +1770,6 @@ void AudacityApp::OnChar(wxKeyEvent & event)
if (!prj)
return;
if (prj != wxGetTopLevelParent(wxWindow::FindFocus()))
return;
if (prj->HandleChar(event))
event.Skip(false);
}

View File

@ -1163,6 +1163,9 @@ void AudacityProject::CreateMenusAndCommands()
SetMenuBar(menubar);
c->AddMetaCommand(wxT("PrevWindow"), _("Move backward thru active windows"), FN(PrevWindow), wxT("Alt+Shift+F6"));
c->AddMetaCommand(wxT("NextWindow"), _("Move forward thru active windows"), FN(NextWindow), wxT("Alt+F6"));
c->SetDefaultFlags(AlwaysEnabledFlag, AlwaysEnabledFlag);
c->AddCommand(wxT("PrevFrame"), _("Move backward from toolbars to tracks"), FN(PrevFrame), wxT("Ctrl+Shift+F6"));
@ -2713,6 +2716,132 @@ void AudacityProject::PrevFrame()
}
}
void AudacityProject::NextWindow()
{
wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
const wxWindowList & list = GetChildren();
wxWindowList::compatibility_iterator iter;
// If the project window has the current focus, start the search with the first child
if (w == this)
{
iter = list.GetFirst();
}
// Otherwise start the search with the current window's next sibling
else
{
// Find the window in this projects children. If the window with the
// focus isn't a child of this project (like when a dialog is created
// without specifying a parent), then we'll get back NULL here.
iter = list.Find(w);
if (iter)
{
iter = iter->GetNext();
}
}
// Search for the next toplevel window
while (iter)
{
// If it's a toplevel, visible (we have hidden windows) and is enabled,
// then we're done. The IsEnabled() prevents us from moving away from
// a modal dialog because all other toplevel windows will be disabled.
w = iter->GetData();
if (w->IsTopLevel() && w->IsShown() && w->IsEnabled())
{
break;
}
// Get the next sibling
iter = iter->GetNext();
}
// Ran out of siblings, so make the current project's track panel active
if (!iter)
{
#if defined(__WXGTK__)
// In wxGTK-2.8.12, focus gets lost if you don't set it to the project window
// instead of the track panel. No idea why.
w = this;
#else
w = mTrackPanel;
#endif
}
// Move focus to the window and bring it to the fore
w->SetFocus();
#if defined(__WXMAC__)
// Yes, I know...why 2 SetFocus() calls? Without the second one, focus
// gets lost on wxMac-2.8.12.
w->SetFocus();
#endif
// And make sure it's on top (only for floating windows...project window will not raise)
w->Raise();
}
void AudacityProject::PrevWindow()
{
wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
const wxWindowList & list = GetChildren();
wxWindowList::compatibility_iterator iter;
// If the project window has the current focus, start the search with the last child
if (w == this)
{
iter = list.GetLast();
}
// Otherwise start the search with the current window's previous sibling
else
{
iter = list.Find(w)->GetPrevious();
}
// Search for the previous toplevel window
while (iter)
{
// If it's a toplevel and is visible (we have come hidden windows), then we're done
w = iter->GetData();
if (w->IsTopLevel() && w->IsShown())
{
break;
}
// Find the window in this projects children. If the window with the
// focus isn't a child of this project (like when a dialog is created
// without specifying a parent), then we'll get back NULL here.
iter = list.Find(w);
if (iter)
{
iter = iter->GetPrevious();
}
}
// Ran out of siblings, so make the current project's track panel active
if (!iter)
{
#if defined(__WXGTK__)
// In wxGTK-2.8.12, focus gets lost if you don't set it to the project window
// instead of the track panel. No idea why.
w = this;
#else
w = mTrackPanel;
#endif
}
// Move focus to the window and bring it to the fore
w->SetFocus();
#if defined(__WXMAC__)
// Yes, I know...why 2 SetFocus() calls? Without the second one, focus
// gets lost on wxMac-2.8.12.
w->SetFocus();
#endif
w->Raise();
}
void AudacityProject::OnTrackPan()
{
mTrackPanel->OnTrackPan();

View File

@ -384,6 +384,9 @@ void OnSeparator();
void PrevFrame();
void NextFrame();
void PrevWindow();
void NextWindow();
void OnResample();
// Make sure we return to "public" for subsequent declarations in Project.h.

View File

@ -1779,6 +1779,14 @@ void AudacityProject::OnScroll(wxScrollEvent & WXUNUSED(event))
bool AudacityProject::HandleKeyDown(wxKeyEvent & event)
{
// Check to see if it is a meta command
if (mCommandManager.HandleMeta(event))
return true;
// Any other keypresses must be destined for this project window.
if (wxGetTopLevelParent(wxWindow::FindFocus()) != this)
return false;
if (event.GetKeyCode() == WXK_ALT)
mTrackPanel->HandleAltKey(true);
@ -1821,6 +1829,10 @@ bool AudacityProject::HandleChar(wxKeyEvent & WXUNUSED(event))
bool AudacityProject::HandleKeyUp(wxKeyEvent & event)
{
// All keypresses must be destined for this project window.
if (wxGetTopLevelParent(wxWindow::FindFocus()) != this)
return false;
if (event.GetKeyCode() == WXK_ALT)
mTrackPanel->HandleAltKey(false);

View File

@ -620,6 +620,24 @@ void CommandManager::AddCommand(const wxChar *name,
}
}
void CommandManager::AddMetaCommand(const wxChar *name,
const wxChar *label_in,
CommandFunctor *callback,
const wxChar *accel)
{
wxString label(label_in);
label += wxT("\t");
label += accel;
NewIdentifier(name, label, NULL, callback, false, 0, 0);
CommandListEntry *entry = mCommandNameHash[name];
entry->enabled = false;
entry->isMeta = true;
entry->flags = 0;
entry->mask = 0;
}
void CommandManager::AddSeparator()
{
if( mHidingLevel > 0 )
@ -697,6 +715,7 @@ int CommandManager::NewIdentifier(wxString name, wxString label, wxMenu *menu,
tmpEntry->mask = mDefaultMask;
tmpEntry->enabled = true;
tmpEntry->wantevent = (label.Find(wxT("\twantevent")) != wxNOT_FOUND);
tmpEntry->isMeta = false;
// Key from preferences overridse the default key given
gPrefs->SetPath(wxT("/NewKeys"));
@ -1065,6 +1084,27 @@ bool CommandManager::HandleKey(wxKeyEvent &evt, wxUint32 flags, wxUint32 mask)
return false;
}
bool CommandManager::HandleMeta(wxKeyEvent &evt)
{
wxString keyStr = KeyEventToKeyString(evt);
CommandListEntry *entry = mCommandKeyHash[keyStr];
// Return unhandle if it isn't a meta command
if (!entry || !entry->isMeta)
{
return false;
}
// Meta commands are always disabled so they do not interfere with the
// rest of the command handling. But, to use the common handler, we
// enable it temporarily and then disable it again after handling.
entry->enabled = true;
bool ret = HandleCommandEntry( entry, 0xffffffff, 0xffffffff, &evt );
entry->enabled = false;
return ret;
}
/// HandleTextualCommand() allows us a limitted version of script/batch
/// behavior, since we can get from a string command name to the actual
/// code to run.

View File

@ -58,6 +58,7 @@ struct CommandListEntry
int count;
bool enabled;
bool wantevent;
bool isMeta;
wxUint32 flags;
wxUint32 mask;
};
@ -146,6 +147,10 @@ class AUDACITY_DLL_API CommandManager: public XMLTagHandler
unsigned int flags = NoFlagsSpecifed,
unsigned int mask = NoFlagsSpecifed);
void AddMetaCommand(const wxChar *name,
const wxChar *label,
CommandFunctor *callback,
const wxChar *accel);
//
// Command masks
//
@ -186,6 +191,7 @@ class AUDACITY_DLL_API CommandManager: public XMLTagHandler
bool HandleCommandEntry(CommandListEntry * entry, wxUint32 flags, wxUint32 mask, const wxEvent * evt = NULL);
bool HandleMenuID(int id, wxUint32 flags, wxUint32 mask);
bool HandleKey(wxKeyEvent &evt, wxUint32 flags, wxUint32 mask);
bool HandleMeta(wxKeyEvent &evt);
bool HandleTextualCommand(wxString & Str, wxUint32 flags, wxUint32 mask);
void TellUserWhyDisallowed(wxUint32 flagsGot, wxUint32 flagsRequired);