Add wxNativeWindow allowing to easily embed native widgets in wx.

Implement the class for wxMSW, wxGTK and wxOSX/Cocoa, show it in the widgets
sample and add documentation for it.
This commit is contained in:
Vadim Zeitlin
2015-08-03 17:47:09 +02:00
parent ce95913319
commit 9bc3ab1ea7
24 changed files with 757 additions and 7 deletions

View File

@@ -22,6 +22,7 @@ LIBS = @LIBS@
LDFLAGS_GUI = @LDFLAGS_GUI@
CXX = @CXX@
CXXFLAGS = @CXXFLAGS@
OBJCXXFLAGS = @OBJCXXFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
WX_LIB_FLAVOUR = @WX_LIB_FLAVOUR@
@@ -49,6 +50,10 @@ WIDGETS_CXXFLAGS = -D__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) \
$(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) \
-I$(srcdir) $(__DLLFLAG_p) -I$(srcdir)/../../samples $(CXXWARNINGS) \
$(SAMPLES_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS)
WIDGETS_OBJCXXFLAGS = -D__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p) \
$(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) \
$(__THREAD_DEFINE_p) -I$(srcdir) $(__DLLFLAG_p) -I$(srcdir)/../../samples \
$(CPPFLAGS) $(OBJCXXFLAGS)
WIDGETS_OBJECTS = \
widgets_activityindicator.o \
widgets_bmpcombobox.o \
@@ -68,6 +73,8 @@ WIDGETS_OBJECTS = \
widgets_hyperlnk.o \
widgets_itemcontainer.o \
widgets_listbox.o \
$(__NATIVE_OBJCPP_SRC_OBJECTS) \
widgets_native.o \
widgets_notebook.o \
widgets_odcombobox.o \
widgets_radiobox.o \
@@ -131,6 +138,8 @@ COND_PLATFORM_OS2_1___widgets___os2_emxbindcmd = $(NM) widgets$(EXEEXT) | if \
@COND_TOOLKIT_OSX_IPHONE@ = $(__widgets_app_Contents_PkgInfo___depname)
@COND_TOOLKIT_COCOA@____widgets_BUNDLE_TGT_REF_DEP = \
@COND_TOOLKIT_COCOA@ $(__widgets_app_Contents_PkgInfo___depname)
@COND_TOOLKIT_OSX_COCOA_WXUNIV_0@__NATIVE_OBJCPP_SRC_OBJECTS \
@COND_TOOLKIT_OSX_COCOA_WXUNIV_0@ = widgets_native_wrapper.o
COND_MONOLITHIC_0___WXLIB_ADV_p = \
-lwx_$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_adv-$(WX_RELEASE)$(HOST_SUFFIX)
@COND_MONOLITHIC_0@__WXLIB_ADV_p = $(COND_MONOLITHIC_0___WXLIB_ADV_p)
@@ -265,6 +274,12 @@ widgets_itemcontainer.o: $(srcdir)/itemcontainer.cpp
widgets_listbox.o: $(srcdir)/listbox.cpp
$(CXXC) -c -o $@ $(WIDGETS_CXXFLAGS) $(srcdir)/listbox.cpp
widgets_native_wrapper.o: $(srcdir)/native_wrapper.mm
$(CXXC) -c -o $@ $(WIDGETS_OBJCXXFLAGS) $(srcdir)/native_wrapper.mm
widgets_native.o: $(srcdir)/native.cpp
$(CXXC) -c -o $@ $(WIDGETS_CXXFLAGS) $(srcdir)/native.cpp
widgets_notebook.o: $(srcdir)/notebook.cpp
$(CXXC) -c -o $@ $(WIDGETS_CXXFLAGS) $(srcdir)/notebook.cpp

View File

@@ -0,0 +1,81 @@
/* XPM */
static const char *const native_xpm[] = {
/* columns rows colors chars-per-pixel */
"16 16 59 1 ",
" c #E40000",
". c #E50000",
"X c #E70000",
"o c #EA0000",
"O c #EB0000",
"+ c #ED0000",
"@ c #F10000",
"# c #F20000",
"$ c #F60000",
"% c #FA0000",
"& c #FB0000",
"* c #FC0000",
"= c #FC0001",
"- c #FE0002",
"; c #FF2C35",
": c #FF4F4F",
"> c #75ED00",
", c #76ED00",
"< c #76EE00",
"1 c #76F000",
"2 c #77F100",
"3 c #79F600",
"4 c #7AF700",
"5 c #7BF700",
"6 c #7AF900",
"7 c #7BF900",
"8 c #7CFB00",
"9 c #7DFC00",
"0 c #7EFE00",
"q c #81FF04",
"w c #82FF07",
"e c #8AFF17",
"r c #8CFF1B",
"t c #8CFF1D",
"y c #A7FF15",
"u c #92FF26",
"i c #92FF27",
"p c #93FF29",
"a c #9AFF37",
"s c #9CFF3B",
"d c #A1FF46",
"f c #A1FF4D",
"g c #AAFF56",
"h c #A4FF5C",
"j c #A2FF68",
"k c #B0FF62",
"l c #B6FF71",
"z c #B8FF73",
"x c #C5FF67",
"c c #FFA6A6",
"v c #FFB4B4",
"b c #C0FF83",
"n c #CBFF98",
"m c #E2FFB3",
"M c #B5D9FF",
"N c #D2E6FF",
"B c #D2E7FF",
"V c #DBECFF",
"C c None",
/* pixels */
"CCCCCCVVMNCCCCCC",
"CCCBBBBBBBBBCCCC",
"CNNNNNNNNNNNNNCC",
"C-MMMMMMMMMMMMVC",
"CXoMMMMMMMMMMjsC",
"Co ;MMMMMh9>>qC",
"C+X X%Mfq7>>>>iC",
"C# y>57>>>>dC",
"C$ Xy6pe>>>>gC",
"C% X$%x3>3i2>>kC",
"C:%%X y>>>>t7>lC",
"Cco y>>>>>9tbC",
"CCv+ y>>>>>2gCC",
"CCCC# y>>>9zCCCC",
"CCCCC%y>anCCCCCC",
"CCCCCCmCCCCCCCCC"
};

View File

@@ -54,6 +54,7 @@ WIDGETS_OBJECTS = \
$(OBJS)\widgets_hyperlnk.obj \
$(OBJS)\widgets_itemcontainer.obj \
$(OBJS)\widgets_listbox.obj \
$(OBJS)\widgets_native.obj \
$(OBJS)\widgets_notebook.obj \
$(OBJS)\widgets_odcombobox.obj \
$(OBJS)\widgets_radiobox.obj \
@@ -318,6 +319,9 @@ $(OBJS)\widgets_itemcontainer.obj: .\itemcontainer.cpp
$(OBJS)\widgets_listbox.obj: .\listbox.cpp
$(CXX) -q -c -P -o$@ $(WIDGETS_CXXFLAGS) .\listbox.cpp
$(OBJS)\widgets_native.obj: .\native.cpp
$(CXX) -q -c -P -o$@ $(WIDGETS_CXXFLAGS) .\native.cpp
$(OBJS)\widgets_notebook.obj: .\notebook.cpp
$(CXX) -q -c -P -o$@ $(WIDGETS_CXXFLAGS) .\notebook.cpp

View File

@@ -47,6 +47,7 @@ WIDGETS_OBJECTS = \
$(OBJS)\widgets_hyperlnk.o \
$(OBJS)\widgets_itemcontainer.o \
$(OBJS)\widgets_listbox.o \
$(OBJS)\widgets_native.o \
$(OBJS)\widgets_notebook.o \
$(OBJS)\widgets_odcombobox.o \
$(OBJS)\widgets_radiobox.o \
@@ -307,6 +308,9 @@ $(OBJS)\widgets_itemcontainer.o: ./itemcontainer.cpp
$(OBJS)\widgets_listbox.o: ./listbox.cpp
$(CXX) -c -o $@ $(WIDGETS_CXXFLAGS) $(CPPDEPS) $<
$(OBJS)\widgets_native.o: ./native.cpp
$(CXX) -c -o $@ $(WIDGETS_CXXFLAGS) $(CPPDEPS) $<
$(OBJS)\widgets_notebook.o: ./notebook.cpp
$(CXX) -c -o $@ $(WIDGETS_CXXFLAGS) $(CPPDEPS) $<

View File

@@ -71,6 +71,7 @@ WIDGETS_OBJECTS = \
widgets_hyperlnk.o \
widgets_itemcontainer.o \
widgets_listbox.o \
widgets_native.o \
widgets_notebook.o \
widgets_odcombobox.o \
widgets_radiobox.o \
@@ -173,6 +174,9 @@ widgets_itemcontainer.o: ./itemcontainer.cpp
widgets_listbox.o: ./listbox.cpp
$(CXX) -c -o $@ $(WIDGETS_CXXFLAGS) $(CPPDEPS) $<
widgets_native.o: ./native.cpp
$(CXX) -c -o $@ $(WIDGETS_CXXFLAGS) $(CPPDEPS) $<
widgets_notebook.o: ./notebook.cpp
$(CXX) -c -o $@ $(WIDGETS_CXXFLAGS) $(CPPDEPS) $<

View File

@@ -49,6 +49,7 @@ WIDGETS_OBJECTS = \
$(OBJS)\widgets_hyperlnk.obj \
$(OBJS)\widgets_itemcontainer.obj \
$(OBJS)\widgets_listbox.obj \
$(OBJS)\widgets_native.obj \
$(OBJS)\widgets_notebook.obj \
$(OBJS)\widgets_odcombobox.obj \
$(OBJS)\widgets_radiobox.obj \
@@ -441,6 +442,9 @@ $(OBJS)\widgets_itemcontainer.obj: .\itemcontainer.cpp
$(OBJS)\widgets_listbox.obj: .\listbox.cpp
$(CXX) /c /nologo /TP /Fo$@ $(WIDGETS_CXXFLAGS) .\listbox.cpp
$(OBJS)\widgets_native.obj: .\native.cpp
$(CXX) /c /nologo /TP /Fo$@ $(WIDGETS_CXXFLAGS) .\native.cpp
$(OBJS)\widgets_notebook.obj: .\notebook.cpp
$(CXX) /c /nologo /TP /Fo$@ $(WIDGETS_CXXFLAGS) .\notebook.cpp

281
samples/widgets/native.cpp Normal file
View File

@@ -0,0 +1,281 @@
/////////////////////////////////////////////////////////////////////////////
// Program: wxWidgets Widgets Sample
// Name: native.cpp
// Purpose: Part of the widgets sample showing native control integration
// Author: Vadim Zeitlin
// Created: 2015-07-30
// Copyright: (c) 2015 Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// this file is included from native.mm which ensures that it is compiled as
// Objective C++, but it's also still compiled by the makefiles directly as C++
// source because we can't easily exclude it, so check for this and only
// compile the rest of this file once
#if !defined(__WXOSX_COCOA__) || defined(__OBJC__)
// for all others, include the necessary headers
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/checkbox.h"
#include "wx/log.h"
#include "wx/menu.h"
#include "wx/sizer.h"
#endif // !WX_PRECOMP
#include "wx/nativewin.h"
#ifdef wxHAS_NATIVE_WINDOW
#include "widgets.h"
#include "icons/native.xpm"
// Helper to create a menu to be shown in our button.
//
// The menu is supposed to be initially empty, don't call this more than once
// for the same object.
void BuildTestMenu(wxMenu *menu)
{
menu->Append(wxID_NEW, "Do it with a new file");
menu->Append(wxID_OPEN, "Do it with an existing file");
menu->AppendSeparator();
menu->Append(wxID_ABOUT);
}
// Create the native control in a -- necessarily -- platform-specific way.
#ifdef __WXMSW__
#include "wx/msw/wrapcctl.h" // for BS_SPLITBUTTON
class NativeWindow : public wxNativeWindow
{
public:
explicit NativeWindow(wxWindow* parent)
: wxNativeWindow()
{
// When creating the native window, we must specify the valid parent
// and while we don't have to specify any position if it's going to be
// laid out by sizers, we do need the size.
const wxSize size = FromDIP(wxSize(140, 30));
HWND hwnd = ::CreateWindow
(
TEXT("BUTTON"),
TEXT("Press me to do it"),
WS_CHILD | WS_VISIBLE | BS_SPLITBUTTON,
0, 0, size.x, size.y,
(HWND)parent->GetHWND(), 0, NULL, NULL
);
if ( !hwnd )
{
wxLogError("Creating split button failed.");
return;
}
(void)Create(parent, wxID_ANY, hwnd);
}
protected:
// Split buttons under MSW don't show the menu on their own, unlike their
// equivalents under the other platforms, so do it manually here. This also
// shows how to handle a native event in MSW (for the specific case of
// WM_NOTIFY, more generally MSWHandleMessage() could be overridden).
virtual bool
MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxOVERRIDE
{
const NMHDR* hdr = reinterpret_cast<NMHDR*>(lParam);
if ( hdr->code != BCN_DROPDOWN )
return wxNativeWindow::MSWOnNotify(idCtrl, lParam, result);
const NMBCDROPDOWN* dd = reinterpret_cast<NMBCDROPDOWN*>(lParam);
wxMenu menu;
BuildTestMenu(&menu);
PopupMenu(&menu, wxPoint(dd->rcButton.right, dd->rcButton.bottom));
return true;
}
};
#elif defined(__WXGTK__)
class NativeWindow : public wxNativeWindow
{
public:
explicit NativeWindow(wxWindow* parent)
: wxNativeWindow()
{
#if GTK_CHECK_VERSION(3,6,0)
BuildTestMenu(&m_menu);
GtkWidget* const widget = gtk_menu_button_new();
gtk_menu_button_set_popup(GTK_MENU_BUTTON(widget), m_menu.m_menu);
#else // GTK+ < 3.6
// Menu buttons are only available since GTK+ 3.6, so use something
// even simpler for earlier versions (including GTK+ 2) just as a
// placeholder.
GtkWidget* const widget = gtk_label_new("");
gtk_label_set_markup
(
GTK_LABEL(widget),
"<b>Sorry</b>, but your GTK+ is too old to have menu buttons."
);
#endif // GTK+ 3.6/earlier
(void)Create(parent, wxID_ANY, widget);
}
private:
wxMenu m_menu;
};
#elif defined(__WXOSX_COCOA__)
#import <Cocoa/Cocoa.h>
class NativeWindow : public wxNativeWindow
{
public:
explicit NativeWindow(wxWindow* parent)
: wxNativeWindow()
{
// Neither the position nor the size really matter: the former because
// the window will be positioned by the sizers, the latter because its
// "fitting" size will be used as the best size by default.
NSPopUpButton* const v = [[NSPopUpButton alloc]
initWithFrame:NSMakeRect(0, 0, 0, 0)
pullsDown:YES];
wxMenu menu;
BuildTestMenu(&menu);
[v setMenu:(NSMenu*)menu.GetHMenu()];
// In a pull down (but not pop up) buttons, items start with 1 and the
// 0-th one is used as title.
[v insertItemWithTitle:@"Press me to do it" atIndex:0];
// By default the control would disable the button title because it
// doesn't appear in the list of the menu items, prevent this from
// happening.
[v setAutoenablesItems:NO];
(void)Create(parent, wxID_ANY, v);
}
};
#else // some other platform
// The sample should be updated if wxNativeCtrl is implemented for some new
// platform in wx/nativectrl.h.
#error "Native control creation not implemented for this platform"
#endif // platforms
// ----------------------------------------------------------------------------
// NativeWidgetsPage
// ----------------------------------------------------------------------------
class NativeWidgetsPage : public WidgetsPage
{
public:
NativeWidgetsPage(WidgetsBookCtrl *book, wxImageList *imaglist);
virtual wxWindow *GetWidget() const wxOVERRIDE { return m_nativeWindow; }
virtual void RecreateWidget() wxOVERRIDE;
// lazy creation of the content
virtual void CreateContent() wxOVERRIDE;
private:
void OnCheckExpand(wxCommandEvent& event);
wxCheckBox* m_chkExpand;
wxNativeWindow* m_nativeWindow;
wxSizer* m_sizerCtrl;
DECLARE_WIDGETS_PAGE(NativeWidgetsPage)
};
// ============================================================================
// implementation
// ============================================================================
IMPLEMENT_WIDGETS_PAGE(NativeWidgetsPage, wxS("Native"), NATIVE_CTRLS);
NativeWidgetsPage::NativeWidgetsPage(WidgetsBookCtrl *book, wxImageList *imaglist)
: WidgetsPage(book, imaglist, native_xpm)
{
m_nativeWindow = NULL;
}
void NativeWidgetsPage::CreateContent()
{
wxSizer* const sizerTop = new wxBoxSizer(wxHORIZONTAL);
wxSizer* const sizerLeft = new wxBoxSizer(wxVERTICAL);
m_chkExpand = new wxCheckBox(this, wxID_ANY, "&Expand to all available size");
m_chkExpand->Bind(wxEVT_CHECKBOX, &NativeWidgetsPage::OnCheckExpand, this);
sizerLeft->Add(m_chkExpand, wxSizerFlags().Border());
sizerTop->Add(sizerLeft);
m_sizerCtrl = new wxBoxSizer(wxHORIZONTAL);
RecreateWidget();
sizerTop->Add(m_sizerCtrl, wxSizerFlags(1).Expand());
SetSizer(sizerTop);
}
// ----------------------------------------------------------------------------
// operations
// ----------------------------------------------------------------------------
void NativeWidgetsPage::RecreateWidget()
{
delete m_nativeWindow;
m_nativeWindow = new NativeWindow(this);
m_sizerCtrl->Clear();
if ( m_chkExpand->IsChecked() )
{
m_sizerCtrl->Add(m_nativeWindow, wxSizerFlags(1).Expand().Border());
}
else
{
m_sizerCtrl->AddStretchSpacer();
m_sizerCtrl->Add(m_nativeWindow, wxSizerFlags().Centre());
m_sizerCtrl->AddStretchSpacer();
}
m_sizerCtrl->Layout();
}
// ----------------------------------------------------------------------------
// event handlers
// ----------------------------------------------------------------------------
void NativeWidgetsPage::OnCheckExpand(wxCommandEvent& WXUNUSED(event))
{
RecreateWidget();
}
#endif // wxHAS_NATIVE_WINDOW
#endif // !OSX/Cocoa compilation as Objective C source file

View File

@@ -0,0 +1,15 @@
/////////////////////////////////////////////////////////////////////////////
// Program: wxWidgets Widgets Sample
// Name: native_wrapper.mm
// Purpose: Helper to compile native.cpp as Objective C++ code
// Author: Vadim Zeitlin
// Created: 2015-08-02
// Copyright: (c) 2015 Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// As explained in widgets.bkl, we need to compile native.cpp using Objective
// C++ compiler, but there is no simple way to do it at the build system level,
// so we just include that file from this file, which has .mm extension
// ensuring that the correct compiler is used.
#include "native.cpp"

View File

@@ -3,6 +3,23 @@
<include file="../../build/bakefiles/common_samples.bkl"/>
<!--
The source file using native controls uses Cocoa under OS X, so it must
be compiled as Objective C++ which means it must have .mm extension.
But this would make it uncompilable under the other platforms and we
don't want to have two files with identical contents. Hence this hack:
we have native.mm which just includes native.cpp under OS X, while
elsewhere we just compile native.cpp directly.
-->
<set var="NATIVE_OBJCPP_SRC">
<if cond="OUT_OF_TREE_MAKEFILES=='1'">
<!-- We can't determine if we're using Cocoa or not, assume we don't -->
</if>
<if cond="TOOLKIT=='OSX_COCOA' and WXUNIV=='0'">
native_wrapper.mm
</if>
</set>
<exe id="widgets" template="wx_sample" template_append="wx_append">
<sources>
activityindicator.cpp
@@ -23,6 +40,8 @@
hyperlnk.cpp
itemcontainer.cpp
listbox.cpp
$(NATIVE_OBJCPP_SRC)
native.cpp
notebook.cpp
odcombobox.cpp
radiobox.cpp

View File

@@ -337,6 +337,9 @@
<File
RelativePath=".\listbox.cpp">
</File>
<File
RelativePath=".\native.cpp">
</File>
<File
RelativePath=".\notebook.cpp">
</File>

View File

@@ -878,6 +878,10 @@
RelativePath=".\listbox.cpp"
>
</File>
<File
RelativePath=".\native.cpp"
>
</File>
<File
RelativePath=".\notebook.cpp"
>

View File

@@ -850,6 +850,10 @@
RelativePath=".\listbox.cpp"
>
</File>
<File
RelativePath=".\native.cpp"
>
</File>
<File
RelativePath=".\notebook.cpp"
>