Fix wxStatusBar with field controls under wxQt

Postpone the creation of the QStatusBar fields until the first call to
SetStatusText(). This is to account for any field control added by the
user and also to avoid having to call UpdateFields() multiple times.

wxSTB_ELLIPSIZE_XXX and wxSTB_SHOW_TIPS supports are also implemented now.

The statbar sample is also updated: The OnSize() handler is removed because
it doesn't do much. that is: it is defined to keep the bitmap centered in the
status bar field it occupies if it is resized. but this is already done by the
wxStaticBitmap control, at least under wxMSW, wxGTK3 and wxQt.
This commit is contained in:
ali kettab 2023-11-20 16:11:31 +01:00
parent 02610d5d4b
commit 5c46947c57
3 changed files with 109 additions and 40 deletions

View file

@ -10,7 +10,6 @@
#include "wx/statusbr.h"
class QLabel;
class QStatusBar;
class WXDLLIMPEXP_CORE wxStatusBar : public wxStatusBarBase
@ -25,6 +24,7 @@ public:
long style = wxSTB_DEFAULT_STYLE,
const wxString& name = wxASCII_STR(wxStatusBarNameStr));
virtual void SetStatusWidths(int n, const int widths_field[]) override;
virtual bool GetFieldRect(int i, wxRect& rect) const override;
virtual void SetMinHeight(int height) override;
virtual int GetBorderX() const override;
@ -40,7 +40,7 @@ private:
void UpdateFields();
QStatusBar *m_qtStatusBar = nullptr;
wxVector<QLabel*> m_qtPanes;
std::vector<QWidget*> m_qtPanes;
wxDECLARE_DYNAMIC_CLASS(wxStatusBar);
};

View file

@ -94,7 +94,6 @@ public:
#if wxUSE_TIMER
void OnTimer(wxTimerEvent& WXUNUSED(event)) { UpdateClock(); }
#endif
void OnSize(wxSizeEvent& event);
void OnToggleClock(wxCommandEvent& event);
void OnIdle(wxIdleEvent& event);
@ -270,7 +269,6 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
wxEND_EVENT_TABLE()
wxBEGIN_EVENT_TABLE(MyStatusBar, wxStatusBar)
EVT_SIZE(MyStatusBar::OnSize)
#if wxUSE_CHECKBOX
EVT_CHECKBOX(StatusBar_Checkbox, MyStatusBar::OnToggleClock)
#endif
@ -939,6 +937,7 @@ MyStatusBar::MyStatusBar(wxWindow *parent, long style)
#endif
m_statbmp = new wxStaticBitmap(this, wxID_ANY, wxIcon(green_xpm));
AddFieldControl(Field_Bitmap, m_statbmp);
#if wxUSE_TIMER
m_timer.Start(1000);
@ -964,18 +963,6 @@ MyStatusBar::~MyStatusBar()
#endif
}
void MyStatusBar::OnSize(wxSizeEvent& event)
{
wxRect rect;
GetFieldRect(Field_Bitmap, rect);
wxSize size = m_statbmp->GetSize();
m_statbmp->Move(rect.x + (rect.width - size.x) / 2,
rect.y + (rect.height - size.y) / 2);
event.Skip();
}
void MyStatusBar::OnToggleClock(wxCommandEvent& WXUNUSED(event))
{
DoToggle();

View file

@ -50,17 +50,56 @@ bool wxStatusBar::Create(wxWindow *parent, wxWindowID WXUNUSED(winid),
SetFieldsCount(1);
// Notice that child controls, if any, will be added using addWidget() in
// UpdateFields() function. So Unbind the base class handler which is not
// needed here. And more importantely, it won't work properly either.
Unbind(wxEVT_SIZE, &wxStatusBar::OnSize, static_cast<wxStatusBarBase*>(this));
Bind(wxEVT_SIZE, [this](wxSizeEvent& event)
{
const int n = this->GetFieldsCount();
for ( int i = 0; i < n; ++i )
{
// Update ellipsized status texts.
this->DoUpdateStatusText(i);
}
event.Skip();
});
return true;
}
void wxStatusBar::SetStatusWidths(int number, const int widths[])
{
if ( number != (int)m_panes.GetCount() )
return;
if ( !m_qtPanes.empty() )
{
int i = 0;
for ( auto pane : m_qtPanes )
{
m_qtStatusBar->removeWidget(pane);
// Do not delete user-added controls.
if ( !m_panes[i++].GetFieldControl() )
{
delete pane;
}
}
m_qtPanes.clear();
}
wxStatusBarBase::SetStatusWidths(number, widths);
}
bool wxStatusBar::GetFieldRect(int i, wxRect& rect) const
{
wxCHECK_MSG( (i >= 0) && ((size_t)i < m_panes.GetCount()), false,
"invalid statusbar field index" );
if ( m_qtPanes.size() != m_panes.GetCount() )
const_cast<wxStatusBar*>(this)->UpdateFields();
rect = wxQtConvertRect(m_qtPanes[i]->geometry());
return true;
}
@ -82,42 +121,85 @@ int wxStatusBar::GetBorderY() const
void wxStatusBar::DoUpdateStatusText(int number)
{
if ( m_qtPanes.size() != m_panes.GetCount() )
UpdateFields();
UpdateFields();
m_qtPanes[number]->setText( wxQtConvertString( m_panes[number].GetText() ) );
const auto pane = dynamic_cast<QLabel*>(m_qtPanes[number]);
if ( !pane )
{
// do nothing if this pane is a field control.
return;
}
QString text = wxQtConvertString( m_panes[number].GetText() );
// do we need to ellipsize this string?
Qt::TextElideMode ellmode = Qt::ElideNone;
if ( HasFlag(wxSTB_ELLIPSIZE_START) ) ellmode = Qt::ElideLeft;
else if ( HasFlag(wxSTB_ELLIPSIZE_MIDDLE) ) ellmode = Qt::ElideMiddle;
else if ( HasFlag(wxSTB_ELLIPSIZE_END) ) ellmode = Qt::ElideRight;
if ( ellmode != Qt::ElideNone )
{
QFontMetrics metrics(pane->font());
QString elidedText = metrics.elidedText(text, ellmode, pane->width());
if ( HasFlag(wxSTB_SHOW_TIPS) )
{
elidedText != text ? pane->setToolTip(text) : pane->setToolTip(QString());
}
text = elidedText;
}
pane->setText(text);
}
void wxStatusBar::UpdateFields()
{
// is it a good idea to recreate all the panes every update?
if ( !m_qtPanes.empty() )
return;
for ( wxVector<QLabel*>::const_iterator it = m_qtPanes.begin();
it != m_qtPanes.end(); ++it )
{
//Remove all panes
delete *it;
}
m_qtPanes.clear();
for (size_t i = 0; i < m_panes.GetCount(); i++)
for ( size_t i = 0; i < m_panes.GetCount(); ++i )
{
//Set sizes
int width = m_panes[i].GetWidth();
const int width = m_bSameWidthForAllPanes ? -1 : m_panes[i].GetWidth();
QLabel *pane = new QLabel( m_qtStatusBar );
m_qtPanes.push_back(pane);
QWidget* pane;
wxWindow* win = m_panes[i].GetFieldControl();
if ( width >= 0 )
if ( win )
{
//Fixed width field
pane->setMinimumSize( QSize(width, 0) );
m_qtStatusBar->addWidget( pane );
pane = win->GetHandle();
}
else
{
m_qtStatusBar->addWidget( pane, -width );
pane = new QLabel;
if ( width >= 0 )
pane->setMinimumWidth(width);
int style;
switch( m_panes[i].GetStyle() )
{
case wxSB_RAISED:
style = QFrame::Panel | QFrame::Raised;
break;
case wxSB_SUNKEN:
style = QFrame::Panel | QFrame::Sunken;
break;
case wxSB_NORMAL:
case wxSB_FLAT:
default:
style = QFrame::Plain | QFrame::NoFrame;
break;
}
static_cast<QLabel*>(pane)->setFrameStyle(style);
}
m_qtPanes.push_back(pane);
m_qtStatusBar->addWidget(pane, width < 0 ? -width : 0);
}
}