1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-11 17:41:15 +02:00

Bug 2318 - Linux: Time Toolbar is needlessly resized too large when accommodating longer time format

This commit is contained in:
Leland Lucius 2020-04-12 01:14:08 -05:00
parent 1fa86ee868
commit a5db7bbb2b
6 changed files with 402 additions and 414 deletions

View File

@ -2,7 +2,7 @@
Audacity: A Digital Audio Editor Audacity: A Digital Audio Editor
TimerToolBar.cpp TimeToolBar.cpp
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -11,14 +11,7 @@
*//*******************************************************************/ *//*******************************************************************/
#include "../Audacity.h" #include "../Audacity.h"
#include "../Experimental.h"
#include "SelectionBar.h"
#include "SelectionBarListener.h"
#include "ToolManager.h"
// For compilers that support precompilation, includes "wx/wx.h". // For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h> #include <wx/wxprec.h>
@ -26,393 +19,331 @@
#include <wx/setup.h> // for wxUSE_* macros #include <wx/setup.h> // for wxUSE_* macros
#ifndef WX_PRECOMP #ifndef WX_PRECOMP
#include <wx/choice.h>
#include <wx/intl.h> #include <wx/intl.h>
#include <wx/settings.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/valtext.h>
#include <wx/stattext.h>
#endif #endif
#include <wx/statline.h>
#include "SelectionBarListener.h"
#include "SelectionBar.h"
#include "TimerToolBar.h" #include "TimerToolBar.h"
#include "ToolManager.h"
#include "SelectionBarListener.h"
//#include "../widgets/AButton.h"
#include "../AudioIO.h" #include "../AudioIO.h"
#include "../AColor.h"
#include "../KeyboardCapture.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../ProjectAudioIO.h" #include "../ProjectAudioIO.h"
#include "../ProjectSettings.h"
#include "../Snap.h"
#include "../ViewInfo.h" #include "../ViewInfo.h"
#include "../widgets/NumericTextCtrl.h"
#include "../AllThemeResources.h"
#if wxUSE_ACCESSIBILITY
#include "../widgets/WindowAccessible.h"
#endif
IMPLEMENT_CLASS(TimerToolBar, ToolBar); IMPLEMENT_CLASS(TimerToolBar, ToolBar);
enum {
TimerToolBarFirstID = 2700,
AudioTimeID,
};
BEGIN_EVENT_TABLE(TimerToolBar, ToolBar) BEGIN_EVENT_TABLE(TimerToolBar, ToolBar)
EVT_SIZE(TimerToolBar::OnSize)
EVT_IDLE( TimerToolBar::OnIdle )
EVT_COMMAND(wxID_ANY, EVT_TIMETEXTCTRL_UPDATED, TimerToolBar::OnUpdate) EVT_COMMAND(wxID_ANY, EVT_TIMETEXTCTRL_UPDATED, TimerToolBar::OnUpdate)
EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, TimerToolBar::OnCaptureKey) EVT_SIZE(TimerToolBar::OnSize)
EVT_IDLE(TimerToolBar::OnIdle)
END_EVENT_TABLE() END_EVENT_TABLE()
TimerToolBar::TimerToolBar( AudacityProject &project ) TimerToolBar::TimerToolBar(AudacityProject &project)
: ToolBar(project, TimeBarID, XO("Time"), wxT("Time"),true), : ToolBar(project, TimeBarID, XO("Time"), wxT("Time"), true),
mListener(NULL), mAudioTime(NULL) mListener(NULL),
mAudioTime(NULL)
{ {
mMinWidth = 50;
mDigitHeight = 48;
mbReady = false;
mbPreserveHeight = false;
mbPreserveWidth = false;
mRate = (double) gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"),
AudioIO::GetOptimalSupportedSampleRate());
} }
TimerToolBar::~TimerToolBar() TimerToolBar::~TimerToolBar()
{ {
} }
TimerToolBar &TimerToolBar::Get( AudacityProject &project ) TimerToolBar &TimerToolBar::Get(AudacityProject &project)
{ {
auto &toolManager = ToolManager::Get( project ); auto &toolManager = ToolManager::Get(project);
return *static_cast<TimerToolBar*>( toolManager.GetToolBar(TimeBarID) ); return *static_cast<TimerToolBar*>(toolManager.GetToolBar(TimeBarID));
} }
const TimerToolBar &TimerToolBar::Get( const AudacityProject &project ) const TimerToolBar &TimerToolBar::Get(const AudacityProject &project)
{ {
return Get( const_cast<AudacityProject&>( project )) ; return Get(const_cast<AudacityProject&>(project)) ;
}
void TimerToolBar::Create(wxWindow * parent)
{
mbIsCreating = true;
ToolBar::Create(parent);
UpdatePrefs();
mbIsCreating = false;
}
NumericTextCtrl * TimerToolBar::AddTime(
const TranslatableString &Name, int id, wxSizer * pSizer)
{
auto formatName = mListener ? mListener->TT_GetAudioTimeFormat()
: NumericConverter::HoursMinsSecondsFormat();
auto pCtrl = safenew NumericTextCtrl(
this, id, NumericConverter::TIME, formatName, 0.0, mRate);
pCtrl->SetName(Name);
pCtrl->SetReadOnly(true);
pCtrl->SetDigitSize( mDigitHeight * 0.66,mDigitHeight );
pSizer->Add(pCtrl, 0, wxALIGN_CENTER , 0);
return pCtrl;
} }
void TimerToolBar::Populate() void TimerToolBar::Populate()
{ {
// Get the default sample rate
auto rate = gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"),
AudioIO::GetOptimalSupportedSampleRate());
wxSizer *mainSizer = GetSizer(); // Get the default time format
auto format = NumericConverter::HoursMinsSecondsFormat();
mAudioTime = AddTime(XO("Audio Position"), AudioTimeID, mainSizer); // Create the read-only time control
mAudioTime = safenew NumericTextCtrl(this, wxID_ANY, NumericConverter::TIME, format, 0.0, rate);
mAudioTime->SetName(XO("Audio Position"));
mAudioTime->SetReadOnly(true);
mainSizer->Layout(); // Add it to the toolbar
RegenerateTooltips(); Add(mAudioTime, 0, wxALIGN_CENTER, 0);
SetMinSize(GetSizer()->GetMinSize());
// Calculate the width to height ratio
wxSize digitSize = mAudioTime->GetDigitSize();
mDigitRatio = (float)digitSize.x / digitSize.y;
// Establish initial resizing limits
// SetResizingLimits();
}
void TimerToolBar::UpdatePrefs()
{
// Since the language may have changed, we need to force an update to accommodate
// different length text
wxCommandEvent e;
e.SetInt(mAudioTime->GetFormatIndex());
OnUpdate(e);
// Language may have changed so reset label
SetLabel(XO("Time"));
// Give the toolbar a chance
ToolBar::UpdatePrefs();
}
void TimerToolBar::SetToDefaultSize()
{
// Reset
SetMaxSize(wxDefaultSize);
SetMinSize(wxDefaultSize);
// Set the default time format
SetAudioTimeFormat(NumericConverter::HoursMinsSecondsFormat());
// Set the default size
SetSize(GetInitialWidth(), 48);
// Inform others the toobar has changed
Updated();
}
wxSize TimerToolBar::GetDockedSize()
{
wxSize sz = GetSize();
// Anything less than a single height bar becomes single height
if (sz.y <= toolbarSingle) {
sz.y = toolbarSingle;
}
// Otherwise it will be a double height bar
else {
sz.y = 2 * toolbarSingle + toolbarGap;
}
return sz;
}
void TimerToolBar::SetDocked(ToolDock *dock, bool pushed)
{
// It's important to call this FIRST since it unhides the resizer control.
// Not doing so causes the calculated best size to be off by the width
// of the resizer.
ToolBar::SetDocked(dock, pushed);
// Recalculate the min and max limits
SetResizingLimits();
// When moving from floater to dock, fit to toolbar since the resizer will
// be mispositioned
if (dock) {
// Fit() while retaining height
SetSize(GetBestSize().x, GetSize().y);
// Inform others the toolbar has changed
Updated();
}
} }
void TimerToolBar::SetListener(TimerToolBarListener *l) void TimerToolBar::SetListener(TimerToolBarListener *l)
{ {
mbPreserveWidth = true; // Remember the listener
mListener = l; mListener = l;
// Get (and set) the saved time format
SetAudioTimeFormat(mListener->TT_GetAudioTimeFormat()); SetAudioTimeFormat(mListener->TT_GetAudioTimeFormat());
mbPreserveWidth = false;
}; };
void TimerToolBar::UpdatePrefs()
{
mbPreserveWidth = true;
wxCommandEvent e;
e.SetInt(mAudioTime->GetFormatIndex());
OnUpdate(e);
mbPreserveWidth = false;
SetLabel(XO("Time"));
ToolBar::UpdatePrefs();
}
void TimerToolBar::SetAudioTimeFormat(const NumericFormatSymbol & format) void TimerToolBar::SetAudioTimeFormat(const NumericFormatSymbol & format)
{ {
bool changed = // Set the format if it's different from previous
mAudioTime->SetFormatString(mAudioTime->GetBuiltinFormat(format)); if (mAudioTime->SetFormatString(mAudioTime->GetBuiltinFormat(format))) {
// Simulate an update since the format has changed.
// Test first whether changed, to avoid infinite recursion from OnUpdate
if ( changed || !mbReady ) {
if (!mbReady) {
//wxLogDebug("Ready!");
mbReady = true;
}
wxCommandEvent e; wxCommandEvent e;
e.SetInt(mAudioTime->GetFormatIndex()); e.SetInt(mAudioTime->GetFormatIndex());
OnUpdate(e); OnUpdate(e);
} }
} }
// The intention of this is to get the resize handle in the
// correct position, after we've let go in dragging.
void TimerToolBar::ResizingDone()
{
// Fit() while retaining height
SetSize(GetBestSize().x, GetSize().y);
// Inform others the toobar has changed
Updated();
}
void TimerToolBar::SetResizingLimits()
{
// Reset limits
SetMinSize(wxDefaultSize);
SetMaxSize(wxDefaultSize);
// If docked we use the current bar height since it's always a single or double height
// toolbar. For floaters, single height toolbar is the minimum height.
int minH = IsDocked() ? GetSize().y : toolbarSingle;
// Get the content size given the smallest digit height we allow
wxSize minSize = ComputeSizing(17);
// Account for any borders added by the window manager
minSize.x += (mAudioTime->GetSize().x - mAudioTime->GetClientSize().x);
// Calculate the space used by other controls and sizer borders with this toolbar
wxSize outer = (GetSize() - GetSizer()->GetSize());
// And account for them in the width
minSize.x += outer.x;
// Override the height
minSize.y = minH;
// Get the maximum digit height we can use. This is restricted to the toolbar's
// current height minus any control borders
int digH = minH - (mAudioTime->GetSize().y - mAudioTime->GetClientSize().y);
// Get the content size using the digit height, if docked. Otherwise use the
// maximum digit height we allow.
wxSize maxSize = ComputeSizing(IsDocked() ? digH : 100);
// Account for the other controls and sizer borders within this toolbar
maxSize.x += outer.x;
// Account for any borders added by the window manager and +1 to keep toolbar
// from dropping to next smaller size when grabbing the resizer.
maxSize.x += (mAudioTime->GetSize().x - mAudioTime->GetClientSize().x) + 1;
// Override the height
maxSize.y = IsDocked() ? minH : wxDefaultCoord;
// And finally set them both
SetMinSize(minSize);
SetMaxSize(maxSize);
}
// Called when the format drop downs is changed. // Called when the format drop downs is changed.
// This causes recreation of the toolbar contents. // This causes recreation of the toolbar contents.
void TimerToolBar::OnUpdate(wxCommandEvent &evt) void TimerToolBar::OnUpdate(wxCommandEvent &evt)
{ {
//wxLogDebug("OnUpdate");
int index = evt.GetInt();
wxWindow *w = FindFocus();
bool bHasFocus = (w == mAudioTime);
mbPreserveHeight = true;
// If docked, font height is determined by toolbar height.
// If undocked, determined by last resize.
if (IsDocked())
{
mDigitHeight = GetSize().GetHeight() - 6;
mDigitHeight = wxMin(mDigitHeight, 25);
}
evt.Skip(false); evt.Skip(false);
// Reset to allow resizing to work
SetMinSize(wxDefaultSize);
SetMaxSize(wxDefaultSize);
// Save format name before recreating the controls so they resize properly // Save format name before recreating the controls so they resize properly
{ if (mListener) {
auto format = mAudioTime->GetBuiltinName(index); mListener->TT_SetAudioTimeFormat(mAudioTime->GetBuiltinName(evt.GetInt()));
if (mListener)
mListener->TT_SetAudioTimeFormat(format);
} }
wxSize sz = GetSize();
RegenerateTooltips();
// ReCreateButtons() will get rid of our sizers and controls
// so reset pointer first.
mAudioTime = NULL;
ReCreateButtons();
auto x = GetSizer()->GetMinSize().GetX();
SetMinSize(wxSize(x, sz.GetHeight()));
// Go set the new size limits
SetResizingLimits(); SetResizingLimits();
if( bHasFocus ) // Fit() while retaining height
mAudioTime->SetFocus(); SetSize(GetBestSize().x, GetSize().y);
// Inform others the toobar has changed
Updated(); Updated();
mbPreserveHeight = false;
} }
void TimerToolBar::SetDocked(ToolDock *dock, bool pushed) void TimerToolBar::OnSize(wxSizeEvent &evt)
{ {
//wxLogDebug("SetDocked"); evt.Skip();
ToolBar::SetDocked(dock, pushed);
if (!mbReady) // Can fire before we're ready
if (!mAudioTime) {
return; return;
// Do not use IsDocked() here. It's not valid!
if ( dock == nullptr) {
// The min height when undocked is always 22
wxSize sz = GetMinSize();
sz.y = 22;
SetMinSize(sz);
}
else {
//SetResizingLimits();
ResizingDone();
} }
} // Make sure everything is where it's supposed to be
Layout();
void TimerToolBar::SetToDefaultSize(){ // Get the sizer's size and remove any borders the time control might have.
wxSize sz; wxSize sizerBR = GetSizer()->GetSize() - (mAudioTime->GetSize() - mAudioTime->GetClientSize());
sz.SetHeight( 48 );
sz.SetWidth( GetInitialWidth());
SetSize( sz );
}
void TimerToolBar::SetResizingLimits() { // Get the content size of the time control. This can be different than the
// control itself due to borders and sizer enduced changes.
wxSize timeBR = mAudioTime->GetDimensions();
//wxLogDebug("Set Resizing Limits"); // Get the current digit box height
if (!IsDocked()) { int h = mAudioTime->GetDigitSize().y;
wxWindow * pWnd = GetParent();
ToolFrame * pFrame = dynamic_cast<ToolFrame*>(pWnd);
Layout();
if (pFrame) {
pFrame->Layout();
// Set smallest conceivable min size
pFrame->SetMinSize(wxSize(80, 24));
// Fit frame around the toolbar
pFrame->Fit();
// The resize handle takes 39 pixels. // Increase current size to find the best fit within the new size
// And was not accounted for in the Fit(). if (sizerBR.x > timeBR.x && sizerBR.y > timeBR.y) {
wxSize sz = pFrame->GetSize(); do {
sz.x += 39; h++;
sz.y += 2; timeBR = ComputeSizing(h);
pFrame->SetSize(sz); } while (h < 150 && sizerBR.x > timeBR.x && sizerBR.y > timeBR.y);
// Now compute and lock in the min frame size
// using aspect ratio, so that dragging won't go
// smaller than this.
pFrame->LockInMinSize(this);
}
}
else
{
Fit(); // Toolbar size to fit around minimum sizer.
Layout();
//GetSizer()->RecalcSizes();
wxSize sz1 = GetSizer()->GetMinSize();
int minHeight = 21;
if (sz1.y > minHeight) {
sz1.x = (sz1.x * minHeight) / sz1.y;
GetSizer()->SetMinSize(sz1);
SetMinSize(sz1);
}
}
}
// We are given the size of the toolbar.
// Compute digit Height and layout the Numeric Control.
void TimerToolBar::ResizeTime( const wxSize & sz) {
int mx = sz.GetWidth() - 25;
int my = sz.GetHeight();
//wxLogDebug("ResizeTime %i,%i",mx,my);
// Digit height is 2 less than y
// OR fits to width, if width is a bit low.
int h = my-2;
h = wxMax(17, h);
h = wxMin(h, 77);
//wxLogDebug("Try h=%i dimensions(%i,%i)", h,mx,my);
h++;
do {
h--; h--;
mAudioTime->SetDigitSize(h*0.66, h); }
} while ((h > 17) && mAudioTime->IsTooBig(mx, my)); // In all other cases, we need to decrease current size to fit within new size
mAudioTime->Layout(); else if (sizerBR.x < timeBR.x || sizerBR.y < timeBR.y) {
//wxLogDebug(" accept height:%i", h); do {
h--;
mDigitHeight = h; timeBR = ComputeSizing(h);
} } while (h > 8 && (sizerBR.x < timeBR.x || sizerBR.y < timeBR.y));
// This 'OnSize' function is also called during moving the
// toolbar.
void TimerToolBar::OnSize(wxSizeEvent & ev)
{
//wxLogDebug("OnSize");
if (!mbReady)
return;
ev.Skip();
if (!mAudioTime)
return;
// If we are changing the time format, then we
// preserve the height. Otherwise we size the font to fit
// the space given.
if (mbPreserveWidth || !mbPreserveHeight) {
ResizeTime( ev.GetSize() );
} }
// Refresh and update immediately, so that we don't get if (h != mAudioTime->GetDigitSize().y) {
// to see grot from partly redrawn toolbar during mAudioTime->SetDigitSize(h * mDigitRatio, h);
// the resizing. }
Refresh(true);
// Redraw the display immediately to smooth out resizing
Update(); Update();
} }
// The intention of this is to get the resize handle in the void TimerToolBar::OnIdle(wxIdleEvent &evt)
// correct position, after we've let go in dragging.
void TimerToolBar::ResizingDone() {
if (!mbReady)
return;
wxSize sz = GetSize();
//wxLogDebug("ReSizingDone %i,%i",sz.x,sz.y);
Fit();
sz.x = GetSize().GetX();
//wxLogDebug("Fitted %i,%i",sz.x,sz.y);
SetSize(sz);
Updated();
}
void TimerToolBar::SetTimes(double audio)
{
mAudioTime->SetValue(wxMax( 0.0, audio));
}
void TimerToolBar::OnFocus(wxFocusEvent &event)
{
KeyboardCapture::OnFocus(*this, event);
}
void TimerToolBar::OnCaptureKey(wxCommandEvent &event)
{
wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
wxWindow *w = FindFocus();
int keyCode = kevent->GetKeyCode();
// Convert numeric keypad entries.
if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) {
keyCode -= WXK_NUMPAD0 - '0';
}
if (keyCode >= '0' && keyCode <= '9') {
return;
}
event.Skip();
}
void TimerToolBar::OnIdle( wxIdleEvent &evt )
{ {
evt.Skip(); evt.Skip();
auto &project = mProject;
double audioTime; double audioTime;
auto &projectAudioIO = ProjectAudioIO::Get( project ); auto &projectAudioIO = ProjectAudioIO::Get(mProject);
if ( projectAudioIO.IsAudioActive() ){ if (projectAudioIO.IsAudioActive()) {
auto gAudioIO = AudioIOBase::Get(); auto gAudioIO = AudioIOBase::Get();
audioTime = gAudioIO->GetStreamTime(); audioTime = gAudioIO->GetStreamTime();
} }
else { else {
const auto &playRegion = ViewInfo::Get( project ).playRegion; const auto &playRegion = ViewInfo::Get(mProject).playRegion;
audioTime = playRegion.GetStart(); audioTime = playRegion.GetStart();
} }
SetTimes( audioTime); mAudioTime->SetValue(wxMax(0.0, audioTime));
} }
static RegisteredToolbarFactory factory{ TimeBarID, static RegisteredToolbarFactory factory
[]( AudacityProject &project ){ {
return ToolBar::Holder{ safenew TimerToolBar{ project } }; } TimeBarID,
[]( AudacityProject &project )
{
return ToolBar::Holder{ safenew TimerToolBar{ project } };
}
}; };
#ifdef EXPERIMENTAL_TIMER_TOOLBAR
namespace { namespace {
AttachedToolBarMenuItem sAttachment{ AttachedToolBarMenuItem sAttachment
{
TimeBarID,
wxT("ShowTimeTB"),
/* i18n-hint: Clicking this menu item shows the toolbar /* i18n-hint: Clicking this menu item shows the toolbar
for viewing actual time of the cursor */ for viewing actual time of the cursor */
TimeBarID, wxT("ShowTimeTB"), XXO("&Time Toolbar"), XXO("&Time Toolbar"),
{ Registry::OrderingHint::After, "ShowSelectionTB" } {
Registry::OrderingHint::After,
"ShowSelectionTB"
}
}; };
} }
#endif

View File

@ -2,26 +2,25 @@
Audacity: A Digital Audio Editor Audacity: A Digital Audio Editor
TimerToolBar.h TimeToolBar.h
Jonatã Bolzan Loss Jonatã Bolzan Loss
**********************************************************************/ **********************************************************************/
#ifndef __AUDACITY_BIG_COUNTER__ #ifndef __AUDACITY_TIME_TOOLBAR__
#define __AUDACITY_BIG_COUNTER__ #define __AUDACITY_TIME_TOOLBAR__
#include <wx/defs.h> #include <wx/defs.h>
#include "ToolBar.h" #include "ToolBar.h"
#include "ToolManager.h" #include "../widgets/NumericTextCtrl.h"
class SelectionBarListener;
class NumericTextCtrl; class NumericTextCtrl;
class wxSize; class TimerToolBarListener;
class TimerToolBar final : public ToolBar {
class TimerToolBar final : public ToolBar
{
public: public:
TimerToolBar(AudacityProject &project); TimerToolBar(AudacityProject &project);
virtual ~TimerToolBar(); virtual ~TimerToolBar();
@ -29,51 +28,31 @@ public:
static TimerToolBar &Get(AudacityProject &project); static TimerToolBar &Get(AudacityProject &project);
static const TimerToolBar &Get(const AudacityProject &project); static const TimerToolBar &Get(const AudacityProject &project);
void Create(wxWindow *parent) override;
void Populate() override; void Populate() override;
void Repaint(wxDC * WXUNUSED(dc)) override {}; void Repaint(wxDC * WXUNUSED(dc)) override {};
void EnableDisableButtons() override {}; void EnableDisableButtons() override {};
void UpdatePrefs() override; void UpdatePrefs() override;
void OnUpdate(wxCommandEvent &evt); void RegenerateTooltips() override {};
void SetTimes(double audio); int GetInitialWidth() override {return 250;}
int GetMinToolbarWidth() override {return 50;}
void SetToDefaultSize() override;
wxSize GetDockedSize() override;
void SetDocked(ToolDock *dock, bool pushed) override;
void SetListener(TimerToolBarListener *l); void SetListener(TimerToolBarListener *l);
void SetAudioTimeFormat(const NumericFormatSymbol & format); void SetAudioTimeFormat(const NumericFormatSymbol & format);
void RegenerateTooltips() override {};
int GetInitialWidth() override {return 250;}
int GetMinToolbarWidth() override { return mMinWidth; }
void SetToDefaultSize() override;
wxSize GetDockedSize() override {
return GetSmartDockedSize();
};
void SetDocked(ToolDock *dock, bool pushed) override;
void ResizeTime( const wxSize & sz );
void SetResizingLimits();
void ResizingDone() override; void ResizingDone() override;
private: private:
NumericTextCtrl * AddTime( const TranslatableString &Name, int id, void SetResizingLimits();
wxSizer * pSizer); wxSize ComputeSizing(int digitH);
void OnFocus(wxFocusEvent &event); void OnUpdate(wxCommandEvent &evt);
void OnCaptureKey(wxCommandEvent &event);
void OnSize(wxSizeEvent &evt); void OnSize(wxSizeEvent &evt);
void OnIdle( wxIdleEvent &evt ); void OnIdle(wxIdleEvent &evt);
TimerToolBarListener * mListener; TimerToolBarListener *mListener;
double mRate; NumericTextCtrl *mAudioTime;
double mAudio; float mDigitRatio;
int mMinWidth;
int mDigitHeight;
bool mbReady;
bool mbIsCreating;
bool mbPreserveWidth;
bool mbPreserveHeight;
NumericTextCtrl *mAudioTime;
wxChoice *mSnapTo;
public: public:
@ -81,4 +60,9 @@ public:
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };
inline wxSize TimerToolBar::ComputeSizing(int digitH)
{
return mAudioTime->ComputeSizing(false, digitH * mDigitRatio, digitH);
}
#endif #endif

View File

@ -228,17 +228,31 @@ void ToolBarResizer::OnMotion( wxMouseEvent & event )
wxPoint pos = wxGetMousePosition(); wxPoint pos = wxGetMousePosition();
wxRect r = mBar->GetRect(); wxRect r = mBar->GetRect();
wxSize msz = mBar->GetMinSize(); wxSize minsz = mBar->GetMinSize();
wxSize maxsz = mBar->GetMaxSize();
wxSize psz = mBar->GetParent()->GetClientSize(); wxSize psz = mBar->GetParent()->GetClientSize();
// Adjust the size based on updated mouse position. // Adjust the size based on updated mouse position.
r.width = ( pos.x - mResizeOffset.x ) - r.x; r.width = ( pos.x - mResizeOffset.x ) - r.x;
// Keep it within max size, if specificed
if( maxsz != wxDefaultSize )
{
if( r.width > maxsz.x )
{
r.width = maxsz.x;
}
if( r.height > maxsz.y )
{
r.height = maxsz.y;
}
}
// Constrain // Constrain
if( r.width < msz.x ) if( r.width < minsz.x )
{ {
// Don't allow resizing to go too small // Don't allow resizing to go too small
r.width = msz.x; r.width = minsz.x;
} }
else if( r.GetRight() > psz.x - 3 ) else if( r.GetRight() > psz.x - 3 )
{ {
@ -538,6 +552,7 @@ void ToolBar::ReCreateButtons()
// Set dock after possibly creating resizer. // Set dock after possibly creating resizer.
// (Re)Establish dock state // (Re)Establish dock state
SetDocked(GetDock(), false); SetDocked(GetDock(), false);
// Set the sizer // Set the sizer
SetSizerAndFit(ms.release()); SetSizerAndFit(ms.release());
} }

View File

@ -231,6 +231,21 @@ void ToolFrame::OnMotion( wxMouseEvent & event )
wxRect rect = GetRect(); wxRect rect = GetRect();
rect.SetBottomRight( pos ); rect.SetBottomRight( pos );
// Keep it within max size, if specificed
wxSize maxsz = mBar->GetMaxSize();
if (maxsz != wxDefaultSize)
{
if (maxsz.x != wxDefaultCoord && rect.width > maxsz.x)
{
rect.width = maxsz.x;
}
if (maxsz.y != wxDefaultCoord && rect.height > maxsz.y)
{
rect.height = maxsz.y;
}
}
if( rect.width < mMinSize.x ) if( rect.width < mMinSize.x )
{ {
rect.width = mMinSize.x; rect.width = mMinSize.x;

View File

@ -1318,12 +1318,17 @@ NumericTextCtrl::NumericTextCtrl(wxWindow *parent, wxWindowID id,
{ {
mAllowInvalidValue = false; mAllowInvalidValue = false;
mDigitBoxW = 10; mDigitBoxW = 11;
mDigitBoxH = 16; mDigitBoxH = 19;
mBorderLeft = 1;
mBorderTop = 1;
mBorderRight = 1;
mBorderBottom = 1;
mReadOnly = options.readOnly; mReadOnly = options.readOnly;
mMenuEnabled = options.menuEnabled; mMenuEnabled = options.menuEnabled;
mButtonWidth = 9; mButtonWidth = mMenuEnabled ? 9 : 0;
SetLayoutDirection(wxLayout_LeftToRight); SetLayoutDirection(wxLayout_LeftToRight);
Layout(); Layout();
@ -1458,78 +1463,119 @@ void NumericTextCtrl::SetInvalidValue(double invalidValue)
SetValue(invalidValue); SetValue(invalidValue);
} }
wxSize NumericTextCtrl::ComputeSizing(bool update, wxCoord boxW, wxCoord boxH)
void NumericTextCtrl::ComputeSizing()
{ {
unsigned int i, j; // Get current box size
int x, pos; if (boxW == 0) {
boxW = mDigitBoxW;
}
wxMemoryDC memDC; if (boxH == 0) {
boxH = mDigitBoxH;
}
boxH -= (mBorderTop + mBorderBottom);
// Placeholder bitmap so the memDC has something to reference // We can use the screen device context since we're not drawing to it
mBackgroundBitmap = std::make_unique<wxBitmap>(1, 1, 24); wxScreenDC dc;
memDC.SelectObject(*mBackgroundBitmap);
mDigits.clear(); // First calculate a rough point size
wxFont pf(wxSize(boxW, boxH), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
int fontSize = pf.GetPointSize();
wxCoord strW;
wxCoord strH;
mBorderLeft = 1; // Now decrease it until we fit within our digit box
mBorderTop = 1; dc.SetFont(pf);
mBorderRight = 1; dc.GetTextExtent(wxT("0"), &strW, &strH);
mBorderBottom = 1; while (strW > boxW || strH > boxH) {
dc.SetFont(wxFont(--fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
int fontSize = 4; dc.GetTextExtent(wxT("0"), &strW, &strH);
wxCoord strW, strH;
wxString exampleText = wxT("0");
// Keep making the font bigger until it's too big, then subtract one.
memDC.SetFont(wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
memDC.GetTextExtent(exampleText, &strW, &strH);
while (strW <= mDigitBoxW && strH <= mDigitBoxH) {
fontSize++;
memDC.SetFont(wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
memDC.GetTextExtent(exampleText, &strW, &strH);
} }
fontSize--; fontSize--;
mDigitFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); // Create the digit font with the new point size
memDC.SetFont(*mDigitFont); if (update) {
memDC.GetTextExtent(exampleText, &strW, &strH); mDigitFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
mDigitW = strW; dc.SetFont(*mDigitFont);
mDigitH = strH;
// Remember the actual digit width and height using the new font
dc.GetTextExtent(wxT("0"), &mDigitW, &mDigitH);
}
// The label font should be a little smaller // The label font should be a little smaller
fontSize--; std::unique_ptr<wxFont> labelFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
mLabelFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
// Figure out the x-position of each field and label in the box // Use the label font for all remaining measurements since only non-digit text is left
x = mBorderLeft; dc.SetFont(*labelFont);
pos = 0;
memDC.SetFont(*mLabelFont); // Remember the pointer if updating
memDC.GetTextExtent(mPrefix, &strW, &strH); if (update) {
x += strW; mLabelFont = std::move(labelFont);
pos += mPrefix.length();
for(i = 0; i < mFields.size(); i++) {
mFields[i].fieldX = x;
for(j=0; j<(unsigned int)mFields[i].digits; j++) {
mDigits.push_back(DigitInfo(i, j, pos, wxRect(x, mBorderTop,
mDigitBoxW, mDigitBoxH)));
x += mDigitBoxW;
pos++;
}
mFields[i].labelX = x;
memDC.GetTextExtent(mFields[i].label, &strW, &strH);
pos += mFields[i].label.length();
x += strW;
mFields[i].fieldW = x;
} }
mWidth = x + mBorderRight; // Get the width of the prefix, if any
mHeight = mDigitBoxH + mBorderTop + mBorderBottom; dc.GetTextExtent(mPrefix, &strW, &strH);
}
// Bump x-position to the end of the prefix
int x = mBorderLeft + strW;
if (update) {
// Set the character position past the prefix
int pos = mPrefix.length();
// Reset digits array
mDigits.clear();
// Figure out the x-position of each field and label in the box
for (int i = 0, fcnt = mFields.size(); i < fcnt; ++i) {
// Get the size of the label
dc.GetTextExtent(mFields[i].label, &strW, &strH);
// Remember this field's x-position
mFields[i].fieldX = x;
// Remember metrics for each digit
for (int j = 0, dcnt = mFields[i].digits; j < dcnt; ++j) {
mDigits.push_back(DigitInfo(i, j, pos, wxRect(x, mBorderTop, boxW, boxH)));
x += boxW;
pos++;
}
// Remember the label's x-position
mFields[i].labelX = x;
// Bump to end of label
x += strW;
// Remember the label's width
mFields[i].fieldW = x;
// Bump character position to end of label
pos += mFields[i].label.length();
}
}
else {
// Determine the maximum x-position (length) of the remaining fields
for (int i = 0, fcnt = mFields.size(); i < fcnt; ++i) {
// Get the size of the label
dc.GetTextExtent(mFields[i].label, &strW, &strH);
// Just bump to next field
x += (boxW * mFields[i].digits) + strW;
}
}
// Calculate the maximum dimensions
wxSize dim(x + mBorderRight, boxH + mBorderTop + mBorderBottom);
// Save maximumFinally, calculate the minimum dimensions
if (update) {
mWidth = dim.x;
mHeight = dim.y;
}
return wxSize(dim.x + mButtonWidth, dim.y);
}
bool NumericTextCtrl::Layout() bool NumericTextCtrl::Layout()
{ {

View File

@ -192,8 +192,8 @@ class NumericTextCtrl final : public wxControl, public NumericConverter
// Hide the inherited function that takes wxString // Hide the inherited function that takes wxString
void SetName( const TranslatableString &name ); void SetName( const TranslatableString &name );
wxSize ComputeSizing(bool update = true, wxCoord digitW = 0, wxCoord digitH = 0);
bool Layout() override; bool Layout() override;
void ComputeSizing();
void Fit() override; void Fit() override;
void SetSampleRate(double sampleRate); void SetSampleRate(double sampleRate);
@ -207,6 +207,8 @@ class NumericTextCtrl final : public wxControl, public NumericConverter
void SetFieldFocus(int /* digit */); void SetFieldFocus(int /* digit */);
wxSize GetDimensions() { return wxSize(mWidth + mButtonWidth, mHeight); }
wxSize GetDigitSize() { return wxSize(mDigitBoxW, mDigitBoxH); }
void SetDigitSize(int width, int height); void SetDigitSize(int width, int height);
void SetReadOnly(bool readOnly = true); void SetReadOnly(bool readOnly = true);
void EnableMenu(bool enable = true); void EnableMenu(bool enable = true);
@ -219,11 +221,6 @@ class NumericTextCtrl final : public wxControl, public NumericConverter
int GetFocusedField() { return mLastField; } int GetFocusedField() { return mLastField; }
int GetFocusedDigit() { return mFocusedDigit; } int GetFocusedDigit() { return mFocusedDigit; }
// give a sane aspect ratio even if zero height.
bool IsTooBig(int width, int height) {
ComputeSizing();
return (mWidth > width) || (mHeight > height);
}
private: private: