Merge branch 'date-picker-blank'
Allow customizing text displayed in wxDatePickerCtrl without valid value, notably not displaying anything in it in this case. See https://github.com/wxWidgets/wxWidgets/pull/2109
This commit is contained in:
@@ -32,6 +32,13 @@ public:
|
||||
// Set/get the date or time (in the latter case, time part is ignored).
|
||||
virtual void SetValue(const wxDateTime& dt) = 0;
|
||||
virtual wxDateTime GetValue() const = 0;
|
||||
|
||||
// For the controls with wxDP_ALLOWNONE style, set the string displayed
|
||||
// when the control doesn't have any valid value. Currently this is only
|
||||
// actually used under MSW, where it can be used to override the previous
|
||||
// value which is still displayed by the control in this case, and ignored
|
||||
// elsewhere.
|
||||
virtual void SetNullText(const wxString& WXUNUSED(text)) { }
|
||||
};
|
||||
|
||||
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
||||
|
@@ -13,11 +13,13 @@
|
||||
#include "wx/containr.h"
|
||||
#include "wx/compositewin.h"
|
||||
|
||||
typedef wxTimePickerCtrlCommonBase<wxDateTimePickerCtrlBase> wxTimePickerCtrlGenericBase;
|
||||
|
||||
class WXDLLIMPEXP_ADV wxTimePickerCtrlGeneric
|
||||
: public wxCompositeWindow< wxNavigationEnabled<wxTimePickerCtrlBase> >
|
||||
: public wxCompositeWindow< wxNavigationEnabled<wxTimePickerCtrlGenericBase> >
|
||||
{
|
||||
public:
|
||||
typedef wxCompositeWindow< wxNavigationEnabled<wxTimePickerCtrlBase> > Base;
|
||||
typedef wxCompositeWindow< wxNavigationEnabled<wxTimePickerCtrlGenericBase> > Base;
|
||||
|
||||
// Creating the control.
|
||||
wxTimePickerCtrlGeneric() { Init(); }
|
||||
|
@@ -26,6 +26,8 @@ public:
|
||||
virtual void SetValue(const wxDateTime& dt) wxOVERRIDE;
|
||||
virtual wxDateTime GetValue() const wxOVERRIDE;
|
||||
|
||||
virtual void SetNullText(const wxString& text) wxOVERRIDE;
|
||||
|
||||
// returns true if the platform should explicitly apply a theme border
|
||||
virtual bool CanApplyThemeBorder() const wxOVERRIDE { return false; }
|
||||
|
||||
@@ -46,43 +48,34 @@ protected:
|
||||
const wxValidator& validator,
|
||||
const wxString& name);
|
||||
|
||||
// Notice that the methods below must be overridden in all native MSW
|
||||
// classes inheriting from this one but they can't be pure virtual because
|
||||
// the generic implementations, not needing nor able to implement them, is
|
||||
// also derived from this class currently. The real problem is, of course,
|
||||
// this wrong class structure because the generic classes also inherit the
|
||||
// wrong implementations of Set/GetValue() and DoGetBestSize() but as they
|
||||
// override these methods anyhow, it does work -- but is definitely ugly
|
||||
// and need to be changed (but how?) in the future.
|
||||
|
||||
#if wxUSE_INTL
|
||||
// Override to return the date/time format used by this control.
|
||||
virtual wxLocaleInfo MSWGetFormat() const /* = 0 */
|
||||
{
|
||||
wxFAIL_MSG( "Unreachable" );
|
||||
return wxLOCALE_TIME_FMT;
|
||||
}
|
||||
virtual wxLocaleInfo MSWGetFormat() const = 0;
|
||||
#endif // wxUSE_INTL
|
||||
|
||||
// Override to indicate whether we can have no date at all.
|
||||
virtual bool MSWAllowsNone() const /* = 0 */
|
||||
{
|
||||
wxFAIL_MSG( "Unreachable" );
|
||||
return false;
|
||||
}
|
||||
virtual bool MSWAllowsNone() const = 0;
|
||||
|
||||
// Override to update m_date and send the event when the control contents
|
||||
// changes, return true if the event was handled.
|
||||
virtual bool MSWOnDateTimeChange(const tagNMDATETIMECHANGE& dtch) /* = 0 */
|
||||
{
|
||||
wxUnusedVar(dtch);
|
||||
wxFAIL_MSG( "Unreachable" );
|
||||
return false;
|
||||
}
|
||||
virtual bool MSWOnDateTimeChange(const tagNMDATETIMECHANGE& dtch) = 0;
|
||||
|
||||
|
||||
// the date currently shown by the control, may be invalid
|
||||
wxDateTime m_date;
|
||||
|
||||
private:
|
||||
// Helper setting the appropriate format depending on the passed in state.
|
||||
void MSWUpdateFormat(bool valid);
|
||||
|
||||
// Same thing, but only doing if the validity differs from the date
|
||||
// validity, i.e. avoiding useless work if nothing needs to be done.
|
||||
void MSWUpdateFormatIfNeeded(bool valid);
|
||||
|
||||
|
||||
// shown when there is no valid value (so only used with wxDP_ALLOWNONE),
|
||||
// always non-empty if SetNullText() was called, see the comments there
|
||||
wxString m_nullText;
|
||||
};
|
||||
|
||||
#endif // _WX_MSW_DATETIMECTRL_H_
|
||||
|
@@ -29,7 +29,10 @@ enum
|
||||
// wxTimePickerCtrl: Allow the user to enter the time.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class WXDLLIMPEXP_ADV wxTimePickerCtrlBase : public wxDateTimePickerCtrl
|
||||
// The template argument must be a class deriving from wxDateTimePickerCtrlBase
|
||||
// (i.e. in practice either this class itself or wxDateTimePickerCtrl).
|
||||
template <typename Base>
|
||||
class wxTimePickerCtrlCommonBase : public Base
|
||||
{
|
||||
public:
|
||||
/*
|
||||
@@ -67,7 +70,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValue(dt);
|
||||
this->SetValue(dt);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -78,7 +81,7 @@ public:
|
||||
wxCHECK_MSG( hour && min && sec, false,
|
||||
wxS("Time component pointers must be non-NULL") );
|
||||
|
||||
const wxDateTime::Tm tm = GetValue().GetTm();
|
||||
const wxDateTime::Tm tm = this->GetValue().GetTm();
|
||||
*hour = tm.hour;
|
||||
*min = tm.min;
|
||||
*sec = tm.sec;
|
||||
@@ -87,6 +90,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// This class is defined mostly for compatibility and is used as the base class
|
||||
// by native wxTimePickerCtrl implementations.
|
||||
typedef wxTimePickerCtrlCommonBase<wxDateTimePickerCtrl> wxTimePickerCtrlBase;
|
||||
|
||||
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
||||
#include "wx/msw/timectrl.h"
|
||||
|
||||
|
@@ -168,6 +168,22 @@ public:
|
||||
*/
|
||||
virtual wxDateTime GetValue() const;
|
||||
|
||||
/**
|
||||
Set the text to show when there is no valid value.
|
||||
|
||||
For the controls with @c wxDP_ALLOWNONE style, set the string displayed
|
||||
when the control doesn't have any valid value. Currently this is only
|
||||
actually used under MSW, where it can be used to override the previous
|
||||
value which is still displayed by the control in this case, and ignored
|
||||
elsewhere.
|
||||
|
||||
Notably, @a text can be empty to completely hide the date if no valid
|
||||
date is specified.
|
||||
|
||||
@since 3.1.5
|
||||
*/
|
||||
void SetNullText(const wxString& text);
|
||||
|
||||
/**
|
||||
Sets the valid range for the date selection. If @a dt1 is valid, it
|
||||
becomes the earliest date (inclusive) accepted by the control. If
|
||||
|
@@ -894,7 +894,8 @@ wxDatePickerCtrl =
|
||||
element object {
|
||||
attribute class { "wxDatePickerCtrl" } &
|
||||
stdObjectNodeAttributes &
|
||||
stdWindowProperties
|
||||
stdWindowProperties &
|
||||
[xrc:p="o"] element null-text {_, t_text }*
|
||||
}
|
||||
|
||||
|
||||
|
@@ -218,12 +218,24 @@ class MyTimeDialog : public wxDialog
|
||||
public:
|
||||
MyTimeDialog(wxWindow* parent);
|
||||
|
||||
wxDateTime GetTime() const { return m_timePicker->GetValue(); }
|
||||
wxDateTime GetTime() const
|
||||
{
|
||||
#if wxUSE_TIMEPICKCTRL_GENERIC
|
||||
if ( m_timePickerGeneric )
|
||||
return m_timePickerGeneric->GetValue();
|
||||
#endif // wxUSE_TIMEPICKCTRL_GENERIC
|
||||
|
||||
return m_timePicker->GetValue();
|
||||
}
|
||||
|
||||
private:
|
||||
void OnTimeChange(wxDateEvent& event);
|
||||
|
||||
wxTimePickerCtrlBase* m_timePicker;
|
||||
wxTimePickerCtrl* m_timePicker;
|
||||
#if wxUSE_TIMEPICKCTRL_GENERIC
|
||||
wxTimePickerCtrlGeneric* m_timePickerGeneric;
|
||||
#endif // wxUSE_TIMEPICKCTRL_GENERIC
|
||||
|
||||
wxStaticText* m_timeText;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
@@ -987,20 +999,31 @@ wxEND_EVENT_TABLE()
|
||||
MyTimeDialog::MyTimeDialog(wxWindow *parent)
|
||||
: wxDialog(parent, wxID_ANY, wxString("Calendar: Choose time"))
|
||||
{
|
||||
wxWindow* timePickerWindow = NULL;
|
||||
|
||||
#if wxUSE_TIMEPICKCTRL_GENERIC
|
||||
m_timePickerGeneric = NULL;
|
||||
m_timePicker = NULL;
|
||||
|
||||
wxFrame *frame = (wxFrame *)wxGetTopLevelParent(parent);
|
||||
if ( frame && frame->GetMenuBar()->IsChecked(Calendar_TimePicker_Generic) )
|
||||
m_timePicker = new wxTimePickerCtrlGeneric(this, wxID_ANY);
|
||||
{
|
||||
m_timePickerGeneric = new wxTimePickerCtrlGeneric(this, wxID_ANY);
|
||||
timePickerWindow = m_timePickerGeneric;
|
||||
}
|
||||
else
|
||||
#endif // wxUSE_TIMEPICKCTRL_GENERIC
|
||||
m_timePicker = new wxTimePickerCtrl(this, wxID_ANY);
|
||||
m_timeText = new wxStaticText(this, wxID_ANY,
|
||||
m_timePicker->GetValue().FormatISOTime());
|
||||
|
||||
if ( !timePickerWindow )
|
||||
timePickerWindow = m_timePicker;
|
||||
|
||||
m_timeText = new wxStaticText(this, wxID_ANY, GetTime().FormatISOTime());
|
||||
|
||||
const wxSizerFlags flags = wxSizerFlags().Centre().Border();
|
||||
wxFlexGridSizer* const sizerMain = new wxFlexGridSizer(2);
|
||||
sizerMain->Add(new wxStaticText(this, wxID_ANY, "Enter &time:"), flags);
|
||||
sizerMain->Add(m_timePicker, flags);
|
||||
sizerMain->Add(timePickerWindow, flags);
|
||||
|
||||
sizerMain->Add(new wxStaticText(this, wxID_ANY, "Time in ISO format:"),
|
||||
flags);
|
||||
|
@@ -54,6 +54,7 @@ enum
|
||||
DatePickerPage_Reset = wxID_HIGHEST,
|
||||
DatePickerPage_Set,
|
||||
DatePickerPage_SetRange,
|
||||
DatePickerPage_SetNullText,
|
||||
DatePickerPage_Picker
|
||||
};
|
||||
|
||||
@@ -78,6 +79,7 @@ protected:
|
||||
|
||||
void OnButtonSet(wxCommandEvent& event);
|
||||
void OnButtonSetRange(wxCommandEvent& event);
|
||||
void OnButtonSetNullText(wxCommandEvent& event);
|
||||
void OnButtonReset(wxCommandEvent& event);
|
||||
|
||||
// reset the date picker parameters
|
||||
@@ -96,6 +98,7 @@ protected:
|
||||
wxTextCtrl *m_textCur;
|
||||
wxTextCtrl *m_textMin;
|
||||
wxTextCtrl *m_textMax;
|
||||
wxTextCtrl *m_textNull;
|
||||
|
||||
wxRadioBox* m_radioKind;
|
||||
wxCheckBox* m_chkStyleCentury;
|
||||
@@ -117,6 +120,7 @@ wxBEGIN_EVENT_TABLE(DatePickerWidgetsPage, WidgetsPage)
|
||||
EVT_BUTTON(DatePickerPage_Reset, DatePickerWidgetsPage::OnButtonReset)
|
||||
EVT_BUTTON(DatePickerPage_Set, DatePickerWidgetsPage::OnButtonSet)
|
||||
EVT_BUTTON(DatePickerPage_SetRange, DatePickerWidgetsPage::OnButtonSetRange)
|
||||
EVT_BUTTON(DatePickerPage_SetNullText, DatePickerWidgetsPage::OnButtonSetNullText)
|
||||
|
||||
EVT_DATE_CHANGED(wxID_ANY, DatePickerWidgetsPage::OnDateChanged)
|
||||
wxEND_EVENT_TABLE()
|
||||
@@ -197,6 +201,20 @@ void DatePickerWidgetsPage::CreateContent()
|
||||
sizerMiddle->Add(new wxButton(this, DatePickerPage_SetRange, "Set &range"),
|
||||
wxSizerFlags().Centre().Border());
|
||||
|
||||
sizerMiddle->AddSpacer(10);
|
||||
|
||||
sizerMiddle->Add(CreateSizerWithTextAndLabel
|
||||
(
|
||||
"&Null text",
|
||||
wxID_ANY,
|
||||
&m_textNull
|
||||
),
|
||||
wxSizerFlags().Expand().Border());
|
||||
|
||||
sizerMiddle->Add(new wxButton(this, DatePickerPage_SetNullText,
|
||||
"Set &null text"),
|
||||
wxSizerFlags().Centre().Border());
|
||||
|
||||
|
||||
// right pane: control itself
|
||||
wxSizer *sizerRight = new wxBoxSizer(wxHORIZONTAL);
|
||||
@@ -327,11 +345,24 @@ void DatePickerWidgetsPage::OnButtonSetRange(wxCommandEvent& WXUNUSED(event))
|
||||
}
|
||||
}
|
||||
|
||||
void DatePickerWidgetsPage::OnButtonSetNullText(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
m_datePicker->SetNullText(m_textNull->GetValue());
|
||||
}
|
||||
|
||||
// Helper function which has to be used here because the controls with
|
||||
// wxDP_ALLOWNONE style can have invalid date, both in the control itself and
|
||||
// in the event it generates.
|
||||
static wxString FormatPossiblyInvalidDate(const wxDateTime& dt)
|
||||
{
|
||||
return dt.IsValid() ? dt.FormatISOCombined() : wxString("[none]");
|
||||
}
|
||||
|
||||
void DatePickerWidgetsPage::OnDateChanged(wxDateEvent& event)
|
||||
{
|
||||
wxLogMessage("Date changed, now is %s (control value is %s).",
|
||||
event.GetDate().FormatISOCombined(),
|
||||
m_datePicker->GetValue().FormatISOCombined());
|
||||
FormatPossiblyInvalidDate(event.GetDate()),
|
||||
FormatPossiblyInvalidDate(m_datePicker->GetValue()));
|
||||
}
|
||||
|
||||
#endif // wxUSE_DATEPICKCTRL
|
||||
|
@@ -99,6 +99,8 @@ void wxDateTimePickerCtrl::SetValue(const wxDateTime& dt)
|
||||
return;
|
||||
}
|
||||
|
||||
MSWUpdateFormatIfNeeded(dt.IsValid());
|
||||
|
||||
m_date = dt;
|
||||
}
|
||||
|
||||
@@ -107,6 +109,57 @@ wxDateTime wxDateTimePickerCtrl::GetValue() const
|
||||
return m_date;
|
||||
}
|
||||
|
||||
void wxDateTimePickerCtrl::MSWUpdateFormatIfNeeded(bool valid)
|
||||
{
|
||||
if ( MSWAllowsNone() && !m_nullText.empty() && valid != m_date.IsValid() )
|
||||
MSWUpdateFormat(valid);
|
||||
}
|
||||
|
||||
void wxDateTimePickerCtrl::MSWUpdateFormat(bool valid)
|
||||
{
|
||||
// We just use NULL to reset to the default format when the date is valid,
|
||||
// as the control seems to remember whichever format was used when it was
|
||||
// created, i.e. this works both with and without wxDP_SHOWCENTURY.
|
||||
|
||||
// Note: due to a bug in MinGW headers, with missing parentheses around the
|
||||
// macro argument (corrected in or before 8.2, but still existing in 5.3),
|
||||
// we have to use a temporary variable here.
|
||||
const TCHAR* const format = valid ? NULL : m_nullText.t_str();
|
||||
DateTime_SetFormat(GetHwnd(), format);
|
||||
}
|
||||
|
||||
void wxDateTimePickerCtrl::SetNullText(const wxString& text)
|
||||
{
|
||||
m_nullText = text;
|
||||
if ( m_nullText.empty() )
|
||||
{
|
||||
// Using empty format doesn't work with the native control, it just
|
||||
// uses the default short date format in this case, so set the format
|
||||
// to the single space which is more or less guaranteed to work as it's
|
||||
// the semi-official way to clear the control contents when it doesn't
|
||||
// have any valid value, according to Microsoft's old KB document
|
||||
// Q238077, which can still be found online by searching for its
|
||||
// number, even if it's not available on Microsoft web site any more.
|
||||
//
|
||||
// Coincidentally, it's also convenient for us, as we can just check if
|
||||
// null text is empty to see if we need to use it elsewhere in the code.
|
||||
m_nullText = wxS(" ");
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to quote the text, as otherwise format specifiers (e.g.
|
||||
// "d", "m" etc) would be interpreted specially by the control. To make
|
||||
// things simple, we just quote it entirely and always.
|
||||
m_nullText.Replace("'", "''");
|
||||
m_nullText.insert(0, "'");
|
||||
m_nullText.append("'");
|
||||
}
|
||||
|
||||
// Apply it immediately if we don't have any value right now.
|
||||
if ( !m_date.IsValid() )
|
||||
MSWUpdateFormat(false);
|
||||
}
|
||||
|
||||
wxSize wxDateTimePickerCtrl::DoGetBestSize() const
|
||||
{
|
||||
wxClientDC dc(const_cast<wxDateTimePickerCtrl *>(this));
|
||||
@@ -168,7 +221,12 @@ wxDateTimePickerCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
|
||||
switch ( hdr->code )
|
||||
{
|
||||
case DTN_DATETIMECHANGE:
|
||||
if ( MSWOnDateTimeChange(*(NMDATETIMECHANGE*)(hdr)) )
|
||||
const NMDATETIMECHANGE& dtch = *(NMDATETIMECHANGE*)(hdr);
|
||||
|
||||
// Update the format before showing the new date if necessary.
|
||||
MSWUpdateFormatIfNeeded(dtch.dwFlags == GDT_VALID);
|
||||
|
||||
if ( MSWOnDateTimeChange(dtch) )
|
||||
{
|
||||
*result = 0;
|
||||
return true;
|
||||
|
@@ -42,6 +42,10 @@ wxObject *wxDateCtrlXmlHandler::DoCreateResource()
|
||||
|
||||
SetupWindow(picker);
|
||||
|
||||
// Note that we want to set this one even if it's empty.
|
||||
if ( HasParam(wxS("null-text")) )
|
||||
picker->SetNullText(GetText(wxS("null-text")));
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user