1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Fixes some focus issues under wxGTK and bug #1116

This commit is contained in:
Leland Lucius 2015-08-14 16:15:28 -05:00
parent 1a54947e92
commit 13c7484458
2 changed files with 109 additions and 90 deletions

View File

@ -1009,24 +1009,27 @@ ProgressDialog::ProgressDialog(const wxString & title,
//
ProgressDialog::~ProgressDialog()
{
// Delete the window disabler before hiding the dialog to allow
// focus to return to the original window.
if (mDisable)
{
delete mDisable;
mDisable = NULL;
}
if (IsShown())
else if (mTopParent)
{
Show(false);
Beep();
mTopParent->Enable(true);
}
Beep();
}
void ProgressDialog::Init()
{
mLastValue = 0;
mDisable = NULL;
mTopParent = NULL;
mIsTransparent = true;
}
bool ProgressDialog::Create(const wxString & title,
@ -1041,7 +1044,8 @@ bool ProgressDialog::Create(const wxString & title,
wxDefaultPosition,
wxDefaultSize,
wxDEFAULT_DIALOG_STYLE |
wxFRAME_FLOAT_ON_PARENT);
wxFRAME_FLOAT_ON_PARENT |
wxTRANSPARENT_WINDOW);
if (!success)
{
return false;
@ -1166,23 +1170,37 @@ bool ProgressDialog::Create(const wxString & title,
mCancel = false;
mStop = false;
// Even though we won't necessarily show the dialog due to the 500ms
// delay, we MUST disable other windows/menus anyway since we run the risk
// of allowing other tasks to run before this one is complete.
//
// Reviewed this code per Proposed Features #1, at
// http://wiki.audacityteam.org/wiki/Proposal_Timer_Record_Improvements.
// Note that this causes a problem for Timer Record wait dialog
// (see TimerRecordDialog::RunWaitDialog()), because it makes it
// impossible to do any editing, even in other open projects,
// while waiting for Timer Record to start -- and then also
// while it's recording, it has a ProgressDialog, so really,
// no editing in any project until Timer Record finishes.
mDisable = new wxWindowDisabler(this);
// Because wxGTK is very sensitive about maintaining focus when
// this window is not shown, we always show it. But, since we
// want a 500ms delay before it's actually visible for those
// quick tasks, we show it as transparent. If the initial
// delay is exceeded, then we reset the dialog to full opacity.
SetTransparent(0);
mIsTransparent = true;
// On OSX at least, creating the disabler causes the dialog to show, so
// just hide it again until the delay has been reached.
Show(false);
wxDialog::Show(true);
if (flags & pdlgAppModal)
{
// Even though we won't necessarily show the dialog due to the 500ms
// delay, we MUST disable other windows/menus anyway since we run the risk
// of allowing other tasks to run before this one is complete.
//
// Reviewed this code per Proposed Features #1, at
// http://wiki.audacityteam.org/wiki/Proposal_Timer_Record_Improvements.
// Note that this causes a problem for Timer Record wait dialog
// (see TimerRecordDialog::RunWaitDialog()), because it makes it
// impossible to do any editing, even in other open projects,
// while waiting for Timer Record to start -- and then also
// while it's recording, it has a ProgressDialog, so really,
// no editing in any project until Timer Record finishes.
mDisable = new wxWindowDisabler(this);
}
else
{
mTopParent = wxGetTopLevelParent(GetParent());
mTopParent->Enable(false);
}
return true;
}
@ -1190,8 +1208,7 @@ bool ProgressDialog::Create(const wxString & title,
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(int value, const wxString & message)
int ProgressDialog::Update(int value, const wxString & message)
{
if (mCancel)
{
@ -1203,7 +1220,19 @@ ProgressDialog::Update(int value, const wxString & message)
return eProgressStopped;
}
SetMessage(message);
wxLongLong_t now = wxGetLocalTimeMillis().GetValue();
wxLongLong_t elapsed = now - mStartTime;
if (elapsed < 500)
{
return eProgressSuccess;
}
if (mIsTransparent)
{
SetTransparent(255);
mIsTransparent = false;
}
if (value <= 0)
{
@ -1215,16 +1244,10 @@ ProgressDialog::Update(int value, const wxString & message)
value = 1000;
}
wxLongLong_t now = wxGetLocalTimeMillis().GetValue();
wxLongLong_t elapsed = now - mStartTime;
wxLongLong_t estimate = elapsed * 1000ll / value;
wxLongLong_t remains = (estimate + mStartTime) - now;
if (!IsShown() && elapsed > 500)
{
Show(true);
wxDialog::Update();
}
SetMessage(message);
if (value != mLastValue)
{
@ -1233,7 +1256,7 @@ ProgressDialog::Update(int value, const wxString & message)
mLastValue = value;
}
// Only update if a full second has passed.or track progress is complete
// Only update if a full second has passed or track progress is complete
if ((now - mLastUpdate > 1000) || (value == 1000))
{
wxTimeSpan tsElapsed(0, 0, 0, elapsed);
@ -1270,8 +1293,7 @@ ProgressDialog::Update(int value, const wxString & message)
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(double current, const wxString & message)
int ProgressDialog::Update(double current, const wxString & message)
{
return Update((int)(current * 1000), message);
}
@ -1279,8 +1301,7 @@ ProgressDialog::Update(double current, const wxString & message)
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(wxULongLong_t current, wxULongLong_t total, const wxString & message)
int ProgressDialog::Update(wxULongLong_t current, wxULongLong_t total, const wxString & message)
{
if (total != 0)
{
@ -1295,8 +1316,7 @@ ProgressDialog::Update(wxULongLong_t current, wxULongLong_t total, const wxStrin
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(wxLongLong current, wxLongLong total, const wxString & message)
int ProgressDialog::Update(wxLongLong current, wxLongLong total, const wxString & message)
{
if (total.GetValue() != 0)
{
@ -1311,8 +1331,7 @@ ProgressDialog::Update(wxLongLong current, wxLongLong total, const wxString & me
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(wxLongLong_t current, wxLongLong_t total, const wxString & message)
int ProgressDialog::Update(wxLongLong_t current, wxLongLong_t total, const wxString & message)
{
if (total != 0)
{
@ -1327,8 +1346,7 @@ ProgressDialog::Update(wxLongLong_t current, wxLongLong_t total, const wxString
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(int current, int total, const wxString & message)
int ProgressDialog::Update(int current, int total, const wxString & message)
{
if (total != 0)
{
@ -1343,8 +1361,7 @@ ProgressDialog::Update(int current, int total, const wxString & message)
//
// Update the time and, optionally, the message
//
int
ProgressDialog::Update(double current, double total, const wxString & message)
int ProgressDialog::Update(double current, double total, const wxString & message)
{
if (total != 0)
{
@ -1356,12 +1373,10 @@ ProgressDialog::Update(double current, double total, const wxString & message)
}
}
//
// Update the message text
//
void
ProgressDialog::SetMessage(const wxString & message)
void ProgressDialog::SetMessage(const wxString & message)
{
if (!message.IsEmpty())
{
@ -1400,33 +1415,28 @@ ProgressDialog::SetMessage(const wxString & message)
SetClientSize(ds);
wxDialog::Update();
}
}
}
void
ProgressDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
void ProgressDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
{
FindWindowById(wxID_CANCEL, this)->Disable();
mCancel = true;
}
void
ProgressDialog::OnStop(wxCommandEvent & WXUNUSED(event))
void ProgressDialog::OnStop(wxCommandEvent & WXUNUSED(event))
{
FindWindowById(wxID_OK, this)->Disable();
mCancel = false;
mStop = true;
}
void
ProgressDialog::OnCloseWindow(wxCloseEvent & WXUNUSED(event))
void ProgressDialog::OnCloseWindow(wxCloseEvent & WXUNUSED(event))
{
mCancel = true;
}
void
ProgressDialog::Beep()
void ProgressDialog::Beep() const
{
int after;
bool should;
@ -1441,10 +1451,12 @@ ProgressDialog::Beep()
wxBusyCursor busy;
wxSound s;
if (name.IsEmpty()) {
if (name.IsEmpty())
{
s.Create(sizeof(beep), beep);
}
else {
else
{
s.Create(name);
}
@ -1476,28 +1488,24 @@ int TimerProgressDialog::Update(const wxString & message /*= wxEmptyString*/)
return eProgressStopped;
}
// Copied from wx 3.0.2 generic progress dialog
//
// we have to yield because not only we want to update the display but
// also to process the clicks on the cancel and skip buttons
// NOTE: using YieldFor() this call shouldn't give re-entrancy problems
// for event handlers not interested to UI/user-input events.
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT);
wxDialog::Update();
wxLongLong_t now = wxGetLocalTimeMillis().GetValue();
wxLongLong_t elapsed = now - mStartTime;
if (elapsed < 500)
{
return eProgressSuccess;
}
if (mIsTransparent)
{
SetTransparent(255);
mIsTransparent = false;
}
SetMessage(message);
wxLongLong_t now = wxGetLocalTimeMillis().GetValue();
wxLongLong_t elapsed = now - mStartTime;
wxLongLong_t remains = mStartTime + mDuration - now;
if (!IsShown() && elapsed > 500)
{
Show(true);
wxDialog::Update();
}
int nGaugeValue = (1000 * elapsed) / mDuration; // range = [0,1000]
// Running in TimerRecordDialog::RunWaitDialog(), for some unknown reason,
// nGaugeValue here is often a little over 1000.
@ -1528,10 +1536,16 @@ int TimerProgressDialog::Update(const wxString & message /*= wxEmptyString*/)
// Copied from wx 3.0.2 generic progress dialog
//
// allow the window to repaint:
// NOTE: since we yield only for UI events with this call, there
// should be no side-effects
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
// we have to yield because not only we want to update the display but
// also to process the clicks on the cancel and skip buttons
// NOTE: using YieldFor() this call shouldn't give re-entrancy problems
// for event handlers not interested to UI/user-input events.
//
// LL: Added timer category to prevent extreme delays when processing effects
// (and probably other things). I do not yet know why this happens and
// I'm not too keen on having timer events processed here, but you do
// what you have to do.
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT | wxEVT_CATEGORY_TIMER);
return eProgressSuccess;
}

View File

@ -40,8 +40,9 @@ enum ProgressDialogFlags
pdlgEmptyFlags = 0x00000000,
pdlgHideStopButton = 0x00000001,
pdlgHideCancelButton = 0x00000002,
pdlgAppModal = 0x00000004,
pdlgDefaultFlags = pdlgEmptyFlags
pdlgDefaultFlags = pdlgAppModal
};
////////////////////////////////////////////////////////////
@ -53,9 +54,9 @@ class AUDACITY_DLL_API ProgressDialog:public wxDialog
public:
ProgressDialog();
ProgressDialog(const wxString & title, const wxString & message = wxEmptyString, ProgressDialogFlags flags = pdlgDefaultFlags);
~ProgressDialog();
virtual ~ProgressDialog();
bool Create(const wxString & title, const wxString & message = wxEmptyString, ProgressDialogFlags flags = pdlgDefaultFlags);
virtual bool Create(const wxString & title, const wxString & message = wxEmptyString, ProgressDialogFlags flags = pdlgDefaultFlags);
int Update(int value, const wxString & message = wxEmptyString);
int Update(double current, const wxString & message = wxEmptyString);
@ -78,19 +79,23 @@ protected:
bool mCancel;
bool mStop;
bool mIsTransparent;
private:
void Init();
void OnCancel(wxCommandEvent & e);
void OnStop(wxCommandEvent & e);
void OnCloseWindow(wxCloseEvent & e);
void Beep();
void Beep() const;
private:
// This guarantees we have an active event loop...possible during OnInit()
wxEventLoopGuarantor mLoop;
wxStaticText *mMessage;
wxWindow *mTopParent;
wxWindowDisabler *mDisable;
wxStaticText *mMessage;
int mLastW;
int mLastH;
@ -99,14 +104,14 @@ private:
class AUDACITY_DLL_API TimerProgressDialog : public ProgressDialog
{
public:
public:
TimerProgressDialog(const wxLongLong_t duration,
const wxString & title,
const wxString & message = wxEmptyString,
ProgressDialogFlags flags = pdlgDefaultFlags);
const wxString & title,
const wxString & message = wxEmptyString,
ProgressDialogFlags flags = pdlgEmptyFlags);
int Update(const wxString & message = wxEmptyString);
protected:
protected:
wxLongLong_t mDuration;
};