ProjectAudioManager doesn't use ProjectWindow, which doesn't use...

ControlToolBar, after we make a system to register functions that calculate
necessary minimum widths for status bar fields.

Also let Scrubbing.cpp register its own strings.

Also be sure to size the status field sufficiently for "Playing at Speed".
This commit is contained in:
Paul Licameli 2019-07-02 18:20:14 -04:00
parent 9f61b67965
commit 68999934e0
11 changed files with 171 additions and 90 deletions

View File

@ -23,7 +23,6 @@ Paul Licameli split from ProjectManager.cpp
#include "ProjectHistory.h"
#include "ProjectSettings.h"
#include "ProjectStatus.h"
#include "ProjectWindow.h"
#include "TimeTrack.h"
#include "UndoManager.h"
#include "toolbars/ControlToolBar.h"
@ -49,39 +48,48 @@ const ProjectAudioManager &ProjectAudioManager::Get(
return Get( const_cast< AudacityProject & >( project ) );
}
ProjectAudioManager::ProjectAudioManager( AudacityProject &project )
: mProject{ project }
{
static ProjectStatus::RegisteredStatusWidthFunction
registerStatusWidthFunction{ StatusWidthFunction };
}
ProjectAudioManager::~ProjectAudioManager() = default;
static wxString FormatRate( int rate )
{
if (rate > 0) {
return wxString::Format(_("Actual Rate: %d"), rate);
}
else
// clear the status field
return {};
}
auto ProjectAudioManager::StatusWidthFunction(
const AudacityProject &project, StatusBarField field )
-> ProjectStatus::StatusWidthResult
{
if ( field == rateStatusBarField ) {
auto &audioManager = ProjectAudioManager::Get( project );
int rate = audioManager.mDisplayedRate;
return {
{ { FormatRate( rate ) } },
50
};
}
return {};
}
void ProjectAudioManager::OnAudioIORate(int rate)
{
auto &project = mProject;
// Be careful to null-check the window. We might get to this function
// during shut-down, but a timer hasn't been told to stop sending its
// messages yet.
auto pWindow = ProjectWindow::Find( &project );
if ( !pWindow )
return;
auto &window = *pWindow;
mDisplayedRate = rate;
wxString display;
if (rate > 0) {
display = wxString::Format(_("Actual Rate: %d"), rate);
}
else
// clear the status field
;
wxString display = FormatRate( rate );
int x, y;
auto statusBar = window.GetStatusBar();
statusBar->GetTextExtent(display, &x, &y);
int widths[] = {
0,
ControlToolBar::Get( project ).WidthForStatusBar(statusBar),
-1,
x+50
};
statusBar->SetStatusWidths(4, widths);
ProjectStatus::Get( project ).Set( display, rateStatusBarField );
}

View File

@ -17,6 +17,8 @@ Paul Licameli split from ProjectManager.h
class AudacityProject;
struct AudioIOStartStreamOptions;
enum StatusBarField : int;
class ProjectAudioManager final
: public ClientData::Base
, public AudioIOListener
@ -26,9 +28,7 @@ public:
static ProjectAudioManager &Get( AudacityProject &project );
static const ProjectAudioManager &Get( const AudacityProject &project );
explicit ProjectAudioManager( AudacityProject &project )
: mProject{ project }
{}
explicit ProjectAudioManager( AudacityProject &project );
ProjectAudioManager( const ProjectAudioManager & ) PROHIBITED;
ProjectAudioManager &operator=( const ProjectAudioManager & ) PROHIBITED;
~ProjectAudioManager() override;
@ -77,6 +77,11 @@ private:
bool mLooping{ false };
bool mCutting{ false };
bool mStopping{ false };
int mDisplayedRate{ 0 };
static std::pair< std::vector< wxString >, unsigned >
StatusWidthFunction(
const AudacityProject &project, StatusBarField field);
};
AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project );

View File

@ -838,6 +838,8 @@ void ProjectManager::OnStatusChange( wxCommandEvent &evt )
return;
auto &window = *pWindow;
window.UpdateStatusWidths();
auto field = static_cast<StatusBarField>( evt.GetInt() );
const auto &msg = ProjectStatus::Get( project ).Get( field );
window.GetStatusBar()->SetStatusText(msg, field);

View File

@ -37,6 +37,26 @@ ProjectStatus::ProjectStatus( AudacityProject &project )
ProjectStatus::~ProjectStatus() = default;
namespace
{
ProjectStatus::StatusWidthFunctions &statusWidthFunctions()
{
static ProjectStatus::StatusWidthFunctions theFunctions;
return theFunctions;
}
}
ProjectStatus::RegisteredStatusWidthFunction::RegisteredStatusWidthFunction(
const StatusWidthFunction &function )
{
statusWidthFunctions().emplace_back( function );
}
auto ProjectStatus::GetStatusWidthFunctions() -> const StatusWidthFunctions &
{
return statusWidthFunctions();
}
const wxString &ProjectStatus::Get( StatusBarField field ) const
{
return mLastStatusMessages[ field - 1 ];

View File

@ -12,10 +12,13 @@ Paul Licameli
#define __AUDACITY_PROJECT_STATUS__
#endif
#include <utility>
#include <vector>
#include <wx/event.h> // to declare custom event type
#include "ClientData.h" // to inherit
class AudacityProject;
class wxString;
class wxWindow;
enum StatusBarField : int {
@ -43,6 +46,24 @@ public:
ProjectStatus &operator= ( const ProjectStatus & ) = delete;
~ProjectStatus() override;
// Type of a function to report translated strings, and also report an extra
// margin, to request that the corresponding field of the status bar should
// be wide enough to contain any of those strings plus the margin.
using StatusWidthResult = std::pair< std::vector<wxString>, unsigned >;
using StatusWidthFunction = std::function<
StatusWidthResult( const AudacityProject &, StatusBarField )
>;
using StatusWidthFunctions = std::vector< StatusWidthFunction >;
// Typically a static instance of this struct is used.
struct RegisteredStatusWidthFunction
{
explicit
RegisteredStatusWidthFunction( const StatusWidthFunction &function );
};
static const StatusWidthFunctions &GetStatusWidthFunctions();
const wxString &Get( StatusBarField field = mainStatusBarField ) const;
void Set(const wxString &msg,
StatusBarField field = mainStatusBarField);

View File

@ -27,7 +27,6 @@ Paul Licameli split from AudacityProject.cpp
#include "WaveTrack.h"
#include "prefs/ThemePrefs.h"
#include "prefs/TracksPrefs.h"
#include "toolbars/ControlToolBar.h"
#include "toolbars/ToolManager.h"
#include "tracks/ui/Scrubbing.h"
#include "tracks/ui/TrackView.h"
@ -815,13 +814,7 @@ void ProjectWindow::Init()
#endif
mIconized = false;
int widths[] = {
0,
ControlToolBar::Get( project ).WidthForStatusBar(statusBar),
-1,
150
};
statusBar->SetStatusWidths(4, widths);
UpdateStatusWidths();
wxString msg = wxString::Format(_("Welcome to Audacity version %s"),
AUDACITY_VERSION_STRING);
statusBar->SetStatusText(msg, mainStatusBarField);
@ -1341,6 +1334,33 @@ bool ProjectWindow::IsIconized() const
return mIconized;
}
void ProjectWindow::UpdateStatusWidths()
{
enum { nWidths = nStatusBarFields + 1 };
int widths[ nWidths ]{ 0 };
widths[ rateStatusBarField ] = 150;
const auto statusBar = GetStatusBar();
const auto &functions = ProjectStatus::GetStatusWidthFunctions();
// Start from 1 not 0
// Specifying a first column always of width 0 was needed for reasons
// I forget now
for ( int ii = 1; ii <= nStatusBarFields; ++ii ) {
int &width = widths[ ii ];
for ( const auto &function : functions ) {
auto results =
function( mProject, static_cast< StatusBarField >( ii ) );
for ( const auto &string : results.first ) {
int w;
statusBar->GetTextExtent(string, &w, nullptr);
width = std::max<int>( width, w + results.second );
}
}
}
// The main status field is not fixed width
widths[ mainStatusBarField ] = -1;
statusBar->SetStatusWidths( nWidths, widths );
}
void ProjectWindow::OnIconize(wxIconizeEvent &event)
{
//JKC: On Iconizing we get called twice. Don't know

View File

@ -53,6 +53,8 @@ public:
wxWindow *GetMainPage() { return mMainPage; }
wxPanel *GetTopPanel() { return mTopPanel; }
void UpdateStatusWidths();
class PlaybackScroller final : public wxEvtHandler
{
public:

View File

@ -103,6 +103,13 @@ BEGIN_EVENT_TABLE(ControlToolBar, ToolBar)
EVT_IDLE(ControlToolBar::OnIdle)
END_EVENT_TABLE()
static const wxString
sStatePlay = XO("Playing")
, sStateStop = XO("Stopped")
, sStateRecord = XO("Recording")
, sStatePause = XO("Paused")
;
//Standard constructor
// This was called "Control" toolbar in the GUI before - now it is "Transport".
// Note that we use the legacy "Control" string as the section because this
@ -118,10 +125,6 @@ ControlToolBar::ControlToolBar( AudacityProject &project )
/* i18n-hint: These are strings for the status bar, and indicate whether Audacity
is playing or recording or stopped, and whether it is paused. */
mStatePlay = XO("Playing");
mStateStop = XO("Stopped");
mStateRecord = XO("Recording");
mStatePause = XO("Paused");
}
ControlToolBar::~ControlToolBar()
@ -1375,30 +1378,28 @@ void ControlToolBar::ClearCutPreviewTracks()
}
// works out the width of the field in the status bar needed for the state (eg play, record pause)
int ControlToolBar::WidthForStatusBar(wxStatusBar* const sb)
{
int xMax = 0;
const auto pauseString = wxT(" ") + wxGetTranslation(mStatePause);
static ProjectStatus::RegisteredStatusWidthFunction
registeredStatusWidthFunction{
[]( const AudacityProject &, StatusBarField field )
-> ProjectStatus::StatusWidthResult
{
if ( field == stateStatusBarField ) {
const auto pauseString = wxT(" ") + GetCustomTranslation(sStatePause);
auto update = [&] (const wxString &state) {
int x, y;
sb->GetTextExtent(
wxGetTranslation(state) + pauseString + wxT("."),
&x, &y
);
xMax = std::max(x, xMax);
};
std::vector<wxString> strings;
for ( auto pString :
{ &sStatePlay, &sStateStop, &sStateRecord } )
{
strings.push_back(
GetCustomTranslation(*pString) + pauseString + wxT(".") );
}
update(mStatePlay);
update(mStateStop);
update(mStateRecord);
// Note that Scrubbing + Paused is not allowed.
for(const auto &state : Scrubber::GetAllUntranslatedStatusStrings())
update(state);
return xMax + 30; // added constant needed because xMax isn't large enough for some reason, plus some space.
}
// added constant needed because xMax isn't large enough for some reason, plus some space.
return { std::move( strings ), 30 };
}
return {};
}
};
wxString ControlToolBar::StateForStatusBar()
{
@ -1412,16 +1413,16 @@ wxString ControlToolBar::StateForStatusBar()
if (!scrubState.empty())
state = wxGetTranslation(scrubState);
else if (mPlay->IsDown())
state = wxGetTranslation(mStatePlay);
state = wxGetTranslation(sStatePlay);
else if (projectAudioManager.Recording())
state = wxGetTranslation(mStateRecord);
state = wxGetTranslation(sStateRecord);
else
state = wxGetTranslation(mStateStop);
state = wxGetTranslation(sStateStop);
if (mPause->IsDown())
{
state.Append(wxT(" "));
state.Append(wxGetTranslation(mStatePause));
state.Append(wxGetTranslation(sStatePause));
}
state.Append(wxT("."));

View File

@ -125,8 +125,6 @@ class ControlToolBar final : public ToolBar {
void ReCreateButtons() override;
void RegenerateTooltips() override;
int WidthForStatusBar(wxStatusBar* const);
// Starting and stopping of scrolling display
void StartScrollingIfPreferred();
void StartScrolling();
@ -188,12 +186,6 @@ class ControlToolBar final : public ToolBar {
std::shared_ptr<TrackList> mCutPreviewTracks;
// strings for status bar
wxString mStatePlay;
wxString mStateStop;
wxString mStateRecord;
wxString mStatePause;
PlayMode mLastPlayMode{ PlayMode::normalPlay };
public:

View File

@ -23,6 +23,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../ProjectAudioIO.h"
#include "../../ProjectAudioManager.h"
#include "../../ProjectSettings.h"
#include "../../ProjectStatus.h"
#include "../../Track.h"
#include "../../TrackPanel.h"
#include "../../ViewInfo.h"
@ -1139,13 +1140,14 @@ END_EVENT_TABLE()
static_assert(nMenuItems == 3, "wrong number of items");
static wxString sPlayAtSpeedStatus = XO("Playing at Speed");
const wxString &Scrubber::GetUntranslatedStateString() const
{
static wxString empty;
if (IsSpeedPlaying()) {
static wxString result = XO("Playing at Speed");
return result;
return sPlayAtSpeedStatus;
}
else if (HasMark()) {
auto &item = FindMenuItem(Seeks() || TemporarilySeeks());
@ -1168,17 +1170,28 @@ wxString Scrubber::StatusMessageForWave() const
std::vector<wxString> Scrubber::GetAllUntranslatedStatusStrings()
{
using namespace std;
vector<wxString> results;
for (const auto &item : menuItems) {
const auto &status = item.GetStatus();
if (!status.empty())
results.push_back(status);
static ProjectStatus::RegisteredStatusWidthFunction
registeredStatusWidthFunction{
[]( const AudacityProject &, StatusBarField field )
-> ProjectStatus::StatusWidthResult
{
if ( field == stateStatusBarField ) {
std::vector< wxString > strings;
// Note that Scrubbing + Paused is not allowed.
for (const auto &item : menuItems)
strings.push_back( GetCustomTranslation( item.GetStatus() ) );
strings.push_back(
GetCustomTranslation( sPlayAtSpeedStatus ) +
wxT(" ") +
GetCustomTranslation( XO("Paused") ) +
wxT(".")
);
// added constant needed because xMax isn't large enough for some reason, plus some space.
return { std::move( strings ), 30 };
}
return {};
}
return results;
}
};
bool Scrubber::CanScrub() const
{

View File

@ -123,9 +123,6 @@ public:
const wxString &GetUntranslatedStateString() const;
wxString StatusMessageForWave() const;
// All possible status strings.
static std::vector<wxString> GetAllUntranslatedStatusStrings();
void Pause(bool paused);
bool IsPaused() const;
void CheckMenuItems();