add alt popup window style allowing to use keyboard and comboboxes in the popup window and use it for the generic date picker (patch 1582391 from Jaakko)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42247 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2006-10-22 20:25:08 +00:00
parent d945fd16d5
commit 06077aaf2c
6 changed files with 301 additions and 115 deletions

View File

@@ -664,3 +664,15 @@ Show the popup.
Undoes the last edit in the text field. Windows only.
\membersection{wxComboCtrl::UseAltPopupWindow}\label{wxcomboctrlusealtpopupwindow}
\func{void}{UseAltPopupWindow}{\param{bool }{enable = true}}
Enable or disable usage of an alternative popup window, which guarantees
ability to focus the popup control, and allows common native controls to
function normally. This alternative popup window is usually a wxDialog,
and as such, when it is shown, its parent top-level window will appear
as if the focus has been lost from it.

View File

@@ -83,7 +83,9 @@ enum
// Internal use: SetTextIndent has been called
wxCC_IFLAG_INDENT_SET = 0x0400,
// Internal use: Set wxTAB_TRAVERSAL to parent when popup is dismissed
wxCC_IFLAG_PARENT_TAB_TRAVERSAL = 0x0800
wxCC_IFLAG_PARENT_TAB_TRAVERSAL = 0x0800,
// Internal use: Secondary popup window type should be used (if available).
wxCC_IFLAG_USE_ALT_POPUP = 0x1000
};
@@ -305,6 +307,21 @@ public:
return m_tcArea;
}
// Call with enable as true to use a type of popup window that guarantees ability
// to focus the popup control, and normal function of common native controls.
// This alternative popup window is usually a wxDialog, and as such it's parent
// frame will appear as if the focus has been lost from it.
void UseAltPopupWindow( bool enable = true )
{
wxASSERT_MSG( !m_winPopup,
wxT("call this only before SetPopupControl") );
if ( enable )
m_iFlags |= wxCC_IFLAG_USE_ALT_POPUP;
else
m_iFlags &= ~wxCC_IFLAG_USE_ALT_POPUP;
}
//
// Utilies needed by the popups or native implementations
//
@@ -459,6 +476,9 @@ protected:
// this is for the control in popup
wxEvtHandler* m_popupExtraHandler;
// this is for the popup window
wxEvtHandler* m_popupWinEvtHandler;
// used to prevent immediate re-popupping incase closed popup
// by clicking on the combo control (needed because of inconsistent
// transient implementation across platforms).
@@ -527,6 +547,9 @@ private:
wxByte m_ignoreEvtText; // Number of next EVT_TEXTs to ignore
// Is popup window wxPopupTransientWindow, wxPopupWindow or wxDialog?
wxByte m_popupWinType;
DECLARE_EVENT_TABLE()
DECLARE_ABSTRACT_CLASS(wxComboCtrlBase)

View File

@@ -83,6 +83,8 @@ public:
// log wxComboCtrl events
void OnComboBoxUpdate( wxCommandEvent& event );
void OnIdle( wxIdleEvent& event );
protected:
wxTextCtrl* m_logWin;
wxLog* m_logOld;
@@ -127,6 +129,8 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ComboControl_Compare, MyFrame::OnShowComparison)
EVT_MENU(ComboControl_Quit, MyFrame::OnQuit)
EVT_MENU(ComboControl_About, MyFrame::OnAbout)
EVT_IDLE(MyFrame::OnIdle)
END_EVENT_TABLE()
// Create a new application object: this macro will allow wxWidgets to create
@@ -729,6 +733,9 @@ MyFrame::MyFrame(const wxString& title)
cc = new wxComboCtrl(panel,2,wxEmptyString,
wxDefaultPosition, wxDefaultSize);
// Make sure we use popup that allows focusing the listview.
cc->UseAltPopupWindow();
cc->SetPopupMinWidth(300);
ListViewComboPopup* iface = new ListViewComboPopup();
@@ -749,6 +756,9 @@ MyFrame::MyFrame(const wxString& title)
gcc = new wxGenericComboCtrl(panel,wxID_ANY,wxEmptyString,
wxDefaultPosition, wxDefaultSize);
// Make sure we use popup that allows focusing the treectrl.
gcc->UseAltPopupWindow();
// Set popup interface right away, otherwise some of the calls
// below may fail
TreeCtrlComboPopup* tcPopup = new TreeCtrlComboPopup();
@@ -1059,3 +1069,27 @@ void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
wxOK | wxICON_INFORMATION,
this);
}
void MyFrame::OnIdle(wxIdleEvent& event)
{
// This code is useful for debugging focus problems
// (which are plentiful when dealing with popup windows).
#if 0
static wxWindow* lastFocus = (wxWindow*) NULL;
wxWindow* curFocus = ::wxWindow::FindFocus();
if ( curFocus != lastFocus )
{
const wxChar* className = wxT("<none>");
if ( curFocus )
className = curFocus->GetClassInfo()->GetClassName();
lastFocus = curFocus;
wxLogDebug( wxT("FOCUSED: %s %X"),
className,
(unsigned int)curFocus);
}
#endif
event.Skip();
}

View File

@@ -62,6 +62,9 @@
#if defined(__WXMSW__)
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
// native controls work on it like normal.
#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
#define TEXTCTRL_TEXT_CENTERED 0 // 1 if text in textctrl is vertically centered
//#undef wxUSE_POPUPWIN
@@ -69,17 +72,32 @@
#elif defined(__WXGTK__)
// NB: It is not recommended to use wxDialog as popup on wxGTK, because of
// this bug: If wxDialog is hidden, its position becomes corrupt
// between hide and next show, but without internal coordinates being
// reflected (or something like that - atleast commenting out ->Hide()
// seemed to eliminate the position change).
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
// native controls work on it like normal.
#define POPUPWIN_IS_PERFECT 1 // Same, but for non-transient popup window.
#define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
#elif defined(__WXMAC__)
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
// native controls work on it like normal.
#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
#define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
#else
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
// native controls work on it like normal.
#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
#define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
#endif
@@ -102,24 +120,77 @@
#endif
// Define different types of popup windows
enum
{
POPUPWIN_NONE = 0,
POPUPWIN_WXPOPUPTRANSIENTWINDOW = 1,
POPUPWIN_WXPOPUPWINDOW = 2,
POPUPWIN_WXDIALOG = 3
};
#if USE_TRANSIENT_POPUP
// wxPopupTransientWindow is implemented
#define wxComboPopupWindowBase wxPopupTransientWindow
#define INSTALL_TOPLEV_HANDLER 0
#define PRIMARY_POPUP_TYPE POPUPWIN_WXPOPUPTRANSIENTWINDOW
#define USES_WXPOPUPTRANSIENTWINDOW 1
#if TRANSIENT_POPUPWIN_IS_PERFECT
//
#elif POPUPWIN_IS_PERFECT
#define wxComboPopupWindowBase2 wxPopupWindow
#define SECONDARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW
#define USES_WXPOPUPWINDOW 1
#else
#define wxComboPopupWindowBase2 wxDialog
#define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG
#define USES_WXDIALOG 1
#endif
#elif wxUSE_POPUPWIN
// wxPopupWindow (but not wxPopupTransientWindow) is properly implemented
#define wxComboPopupWindowBase wxPopupWindow
#define INSTALL_TOPLEV_HANDLER 1
#define PRIMARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW
#define USES_WXPOPUPWINDOW 1
#if !POPUPWIN_IS_PERFECT
#define wxComboPopupWindowBase2 wxDialog
#define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG
#define USES_WXDIALOG 1
#endif
#else
// wxPopupWindow is not implemented
#define wxComboPopupWindowBase wxDialog
#define INSTALL_TOPLEV_HANDLER 0 // Doesn't need since can monitor active event
#define PRIMARY_POPUP_TYPE POPUPWIN_WXDIALOG
#define USES_WXDIALOG 1
#endif
#ifndef USES_WXPOPUPTRANSIENTWINDOW
#define USES_WXPOPUPTRANSIENTWINDOW 0
#endif
#ifndef USES_WXPOPUPWINDOW
#define USES_WXPOPUPWINDOW 0
#endif
#ifndef USES_WXDIALOG
#define USES_WXDIALOG 0
#endif
#if USES_WXPOPUPWINDOW
#define INSTALL_TOPLEV_HANDLER 1
#else
#define INSTALL_TOPLEV_HANDLER 0
#endif
//
// ** TODO **
@@ -252,7 +323,7 @@ void wxComboFrameEventHandler::OnMove( wxMoveEvent& event )
#endif // INSTALL_TOPLEV_HANDLER
// ----------------------------------------------------------------------------
// wxComboPopupWindow is wxPopupWindow customized for
// wxComboPopupWindow is, in essence, wxPopupWindow customized for
// wxComboCtrl.
// ----------------------------------------------------------------------------
@@ -260,43 +331,9 @@ class wxComboPopupWindow : public wxComboPopupWindowBase
{
public:
wxComboPopupWindow( wxComboCtrlBase *parent, int style = wxBORDER_NONE );
#if USE_TRANSIENT_POPUP
virtual bool ProcessLeftDown(wxMouseEvent& event);
#endif
void OnKeyEvent(wxKeyEvent& event);
void OnMouseEvent( wxMouseEvent& event );
#if !wxUSE_POPUPWIN
void OnActivate( wxActivateEvent& event );
#endif
protected:
#if USE_TRANSIENT_POPUP
virtual void OnDismiss();
#endif
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxComboPopupWindow, wxComboPopupWindowBase)
EVT_MOUSE_EVENTS(wxComboPopupWindow::OnMouseEvent)
#if !wxUSE_POPUPWIN
EVT_ACTIVATE(wxComboPopupWindow::OnActivate)
#endif
EVT_KEY_DOWN(wxComboPopupWindow::OnKeyEvent)
EVT_KEY_UP(wxComboPopupWindow::OnKeyEvent)
END_EVENT_TABLE()
wxComboPopupWindow::wxComboPopupWindow( wxComboCtrlBase *parent,
wxComboPopupWindow( wxComboCtrlBase *parent,
int style )
#if wxUSE_POPUPWIN
#if USES_WXPOPUPWINDOW || USES_WXPOPUPTRANSIENTWINDOW
: wxComboPopupWindowBase(parent,style)
#else
: wxComboPopupWindowBase(parent,
@@ -309,46 +346,20 @@ wxComboPopupWindow::wxComboPopupWindow( wxComboCtrlBase *parent,
{
}
void wxComboPopupWindow::OnKeyEvent( wxKeyEvent& event )
{
// Relay keyboard event to the main child controls
// (just skipping may just cause the popup to close)
wxWindowList children = GetChildren();
wxWindowList::iterator node = children.begin();
wxWindow* child = (wxWindow*)*node;
child->AddPendingEvent(event);
}
void wxComboPopupWindow::OnMouseEvent( wxMouseEvent& event )
{
event.Skip();
}
#if !wxUSE_POPUPWIN
void wxComboPopupWindow::OnActivate( wxActivateEvent& event )
{
if ( !event.GetActive() )
{
// Tell combo control that we are dismissed.
wxComboCtrl* combo = (wxComboCtrl*) GetParent();
wxASSERT( combo );
wxASSERT( combo->IsKindOf(CLASSINFO(wxComboCtrl)) );
combo->HidePopup();
event.Skip();
}
}
#if USES_WXPOPUPTRANSIENTWINDOW
virtual bool ProcessLeftDown(wxMouseEvent& event);
virtual void OnDismiss();
#endif
#if USE_TRANSIENT_POPUP
};
#if USES_WXPOPUPTRANSIENTWINDOW
bool wxComboPopupWindow::ProcessLeftDown(wxMouseEvent& event )
{
return wxComboPopupWindowBase::ProcessLeftDown(event);
}
#endif
#if USE_TRANSIENT_POPUP
// First thing that happens when a transient popup closes is that this method gets called.
void wxComboPopupWindow::OnDismiss()
{
@@ -358,8 +369,68 @@ void wxComboPopupWindow::OnDismiss()
combo->OnPopupDismiss();
}
#endif // USES_WXPOPUPTRANSIENTWINDOW
// ----------------------------------------------------------------------------
// wxComboPopupWindowEvtHandler does bulk of the custom event handling
// of a popup window. It is separate so we can have different types
// of popup windows.
// ----------------------------------------------------------------------------
class wxComboPopupWindowEvtHandler : public wxEvtHandler
{
public:
wxComboPopupWindowEvtHandler( wxComboCtrlBase *parent )
{
m_combo = parent;
}
void OnKeyEvent(wxKeyEvent& event);
#if USES_WXDIALOG
void OnActivate( wxActivateEvent& event );
#endif
private:
wxComboCtrlBase* m_combo;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxComboPopupWindowEvtHandler, wxEvtHandler)
EVT_KEY_DOWN(wxComboPopupWindowEvtHandler::OnKeyEvent)
EVT_KEY_UP(wxComboPopupWindowEvtHandler::OnKeyEvent)
#if USES_WXDIALOG
EVT_ACTIVATE(wxComboPopupWindowEvtHandler::OnActivate)
#endif
END_EVENT_TABLE()
void wxComboPopupWindowEvtHandler::OnKeyEvent( wxKeyEvent& event )
{
// Relay keyboard event to the main child controls
wxWindowList children = m_combo->GetPopupWindow()->GetChildren();
wxWindowList::iterator node = children.begin();
wxWindow* child = (wxWindow*)*node;
child->AddPendingEvent(event);
}
#if USES_WXDIALOG
void wxComboPopupWindowEvtHandler::OnActivate( wxActivateEvent& event )
{
if ( !event.GetActive() )
{
// Tell combo control that we are dismissed.
m_combo->HidePopup();
event.Skip();
}
}
#endif
// ----------------------------------------------------------------------------
// wxComboPopup
//
@@ -639,6 +710,7 @@ void wxComboCtrlBase::Init()
m_btnWidDefault = 0;
m_blankButtonBg = false;
m_ignoreEvtText = 0;
m_popupWinType = POPUPWIN_NONE;
m_btnWid = m_btnHei = -1;
m_btnSide = wxRIGHT;
m_btnSpacingX = 0;
@@ -1354,13 +1426,16 @@ bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event,
wxLongLong t = ::wxGetLocalTimeMillis();
int evtType = event.GetEventType();
#if !USE_TRANSIENT_POPUP
#if USES_WXPOPUPWINDOW || USES_WXDIALOG
if ( m_popupWinType != POPUPWIN_WXPOPUPTRANSIENTWINDOW )
{
if ( m_isPopupShown &&
( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) )
{
HidePopup();
return true;
}
}
#endif
// Filter out clicks on button immediately after popup dismiss (Windows like behaviour)
@@ -1382,10 +1457,9 @@ void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event )
{
if ( m_isPopupShown )
{
#if !wxUSE_POPUPWIN
// Normally do nothing - evt handler should close it for us
#elif !USE_TRANSIENT_POPUP
#if USES_WXPOPUPWINDOW
// Click here always hides the popup.
if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW )
HidePopup();
#endif
}
@@ -1495,7 +1569,28 @@ void wxComboCtrlBase::CreatePopup()
wxWindow* popup;
if ( !m_winPopup )
{
#ifdef wxComboPopupWindowBase2
if ( m_iFlags & wxCC_IFLAG_USE_ALT_POPUP )
{
#if !USES_WXDIALOG
m_winPopup = new wxComboPopupWindowBase2( this, wxNO_BORDER );
#else
m_winPopup = new wxComboPopupWindowBase2( this, wxID_ANY, wxEmptyString,
wxPoint(-21,-21), wxSize(20, 20),
wxNO_BORDER );
#endif
m_popupWinType = SECONDARY_POPUP_TYPE;
}
else
#endif
{
m_winPopup = new wxComboPopupWindow( this, wxNO_BORDER );
m_popupWinType = PRIMARY_POPUP_TYPE;
}
m_popupWinEvtHandler = new wxComboPopupWindowEvtHandler(this);
m_winPopup->PushEventHandler(m_popupWinEvtHandler);
}
popupInterface->Create(m_winPopup);
m_popup = popup = popupInterface->GetControl();
@@ -1524,7 +1619,12 @@ void wxComboCtrlBase::DestroyPopup()
delete m_popupInterface;
if ( m_winPopup )
{
m_winPopup->RemoveEventHandler(m_popupWinEvtHandler);
delete m_popupWinEvtHandler;
m_popupWinEvtHandler = NULL;
m_winPopup->Destroy();
}
m_popupExtraHandler = (wxEvtHandler*) NULL;
m_popupInterface = (wxComboPopup*) NULL;
@@ -1728,14 +1828,17 @@ void wxComboCtrlBase::ShowPopup()
m_isPopupShown = true;
// Show it
#if USE_TRANSIENT_POPUP
#if USES_WXPOPUPTRANSIENTWINDOW
if ( m_popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW )
((wxPopupTransientWindow*)winPopup)->Popup(popup);
#else
winPopup->Show();
else
#endif
winPopup->Show();
#if INSTALL_TOPLEV_HANDLER
// Put top level window event handler into place
if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW )
{
if ( !m_toplevEvtHandler )
m_toplevEvtHandler = new wxComboFrameEventHandler(this);
@@ -1743,6 +1846,7 @@ void wxComboCtrlBase::ShowPopup()
wxASSERT( toplev );
((wxComboFrameEventHandler*)m_toplevEvtHandler)->OnPopup();
toplev->PushEventHandler( m_toplevEvtHandler );
}
#endif
}
@@ -1753,7 +1857,18 @@ void wxComboCtrlBase::OnPopupDismiss()
if ( !m_isPopupShown )
return;
// *Must* set this before focus etc.
// NB: Focus setting is really funny, atleast on wxMSW. First of all,
// we need to have SetFocus at the end. Otherwise wxTextCtrl may
// freeze until focus goes somewhere else. Second, wxTreeCtrl as
// popup, when dismissing, "steals" focus back to itself unless
// SetFocus is called also here, exactly before m_isPopupShown
// is set to false. Which is truly weird since SetFocus is just
// wxWindowMSW method and does not call event handler or anything like
// that (ie. does not care about m_isPopupShown).
SetFocus();
// This should preferably be set before focus.
m_isPopupShown = false;
// Inform popup control itself
@@ -1791,10 +1906,7 @@ void wxComboCtrlBase::OnPopupDismiss()
// refresh control (necessary even if m_text)
Refresh();
#if !wxUSE_POPUPWIN
SetFocus();
#endif
}
void wxComboCtrlBase::HidePopup()
@@ -1807,11 +1919,12 @@ void wxComboCtrlBase::HidePopup()
// transfer value and show it in textctrl, if any
SetValue( m_popupInterface->GetStringValue() );
#if USE_TRANSIENT_POPUP
#if USES_WXPOPUPTRANSIENTWINDOW
if ( m_popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW )
((wxPopupTransientWindow*)m_winPopup)->Dismiss();
#else
m_winPopup->Hide();
else
#endif
m_winPopup->Hide();
OnPopupDismiss();
}

View File

@@ -561,7 +561,7 @@ void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
void wxPopupFocusHandler::OnKeyDown(wxKeyEvent& event)
{
// let the window have it first, it might process the keys
if ( !m_popup->ProcessEvent(event) )
if ( !m_popup->GetEventHandler()->ProcessEvent(event) )
{
// by default, dismiss the popup
m_popup->DismissAndNotify();

View File

@@ -414,6 +414,10 @@ bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
m_popup = new wxCalendarComboPopup();
#if defined(__WXMSW__)
// without this keyboard navigation in month control doesn't work
m_combo->UseAltPopupWindow();
#endif
m_combo->SetPopupControl(m_popup);
m_cal = m_popup;