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:
parent
bc6583b62d
commit
4b756ce7d6
@ -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);
|
||||
}
|
||||
|
129
src/Menus.cpp
129
src/Menus.cpp
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user