#include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #ifndef wxHAS_IMAGES_IN_RESOURCES #include "../sample.xpm" #endif #include "wx/dataview.h" #include #include class LIB_TREE_NODE { public: LIB_TREE_NODE() : m_Parent( nullptr ) {}; virtual ~LIB_TREE_NODE() {} typedef std::vector> PTR_VECTOR; LIB_TREE_NODE* m_Parent; // Parent node or null PTR_VECTOR m_Children; // List of child nodes wxString m_Name; // Actual name of the part wxString m_Desc; // Description to be displayed }; class LIB_TREE_NODE_LIBRARY : public LIB_TREE_NODE { public: LIB_TREE_NODE_LIBRARY( LIB_TREE_NODE* aParent, const wxString& aName, const wxString& aDesc ); }; class LIB_TREE_NODE_ROOT: public LIB_TREE_NODE { public: LIB_TREE_NODE_LIBRARY& AddLib( wxString const& aName, wxString const& aDesc ); }; LIB_TREE_NODE_LIBRARY::LIB_TREE_NODE_LIBRARY( LIB_TREE_NODE* aParent, wxString const& aName, wxString const& aDesc ) { m_Name = aName; m_Desc = aDesc; m_Parent = aParent; } LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxString const& aDesc ) { LIB_TREE_NODE_LIBRARY* lib = new LIB_TREE_NODE_LIBRARY( this, aName, aDesc ); m_Children.push_back( std::unique_ptr( lib ) ); return *lib; } class LIB_TREE_MODEL_ADAPTER: public wxDataViewModel { public: enum TREE_COLS { NAME_COL = 0, ///< Library or library item name column DESC_COL, ///< Library or library description column NUM_COLS ///< The number of default tree columns }; LIB_TREE_NODE_LIBRARY& DoAddLibraryNode( const wxString& aNodeName, const wxString& aDesc ); void AttachTo( wxDataViewCtrl* aDataViewCtrl ); unsigned int GetChildren( const wxDataViewItem& aItem, wxDataViewItemArray& aChildren ) const override; protected: wxDataViewColumn* doAddColumn( const wxString& aHeader ); void recreateColumns(); static wxDataViewItem ToItem( const LIB_TREE_NODE* aNode ); static LIB_TREE_NODE* ToNode( wxDataViewItem aItem ); bool HasContainerColumns( const wxDataViewItem& aItem ) const override; bool IsContainer( const wxDataViewItem& aItem ) const override; wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override; unsigned int GetColumnCount() const override { return m_columns.size(); } wxString GetColumnType( unsigned int WXUNUSED(aCol) ) const override { return "string"; } void GetValue( wxVariant& aVariant, const wxDataViewItem& aItem, unsigned int aCol ) const override; bool SetValue( const wxVariant& WXUNUSED(aVariant), const wxDataViewItem& WXUNUSED(aItem), unsigned int WXUNUSED(aCol) ) override { return false; } bool GetAttr( const wxDataViewItem& WXUNUSED(aItem), unsigned int WXUNUSED(aCol), wxDataViewItemAttr& WXUNUSED(aAttr) ) const override { return false; }; protected: LIB_TREE_NODE_ROOT m_tree; wxDataViewCtrl* m_widget; private: std::vector m_columns; }; class LIB_TREE_RENDERER : public wxDataViewCustomRenderer { public: LIB_TREE_RENDERER() : m_canvasItem( false ) {} wxSize GetSize() const override { int y = GetTextExtent( m_text ).y; std::cout << y << " text: " << m_text << std::endl;; return wxSize( GetOwner()->GetWidth(), 30 ); } bool GetValue( wxVariant& aValue ) const override { aValue = m_text; return true; } bool SetValue( const wxVariant& aValue ) override { m_text = aValue.GetString(); return true; } void SetAttr( const wxDataViewItemAttr& aAttr ) override { // Use strikethrough as a proxy for is-canvas-item m_canvasItem = aAttr.GetStrikethrough(); wxDataViewItemAttr realAttr = aAttr; realAttr.SetStrikethrough( false ); wxDataViewCustomRenderer::SetAttr( realAttr ); } bool Render( wxRect aRect, wxDC *dc, int aState ) override { std::cout << "Render " << aRect.GetHeight() << " text: " << m_text << std::endl;; RenderBackground( dc, aRect ); if( m_canvasItem ) { wxPoint points[6]; points[0] = aRect.GetTopLeft(); points[1] = aRect.GetTopRight() + wxPoint( -4, 0 ); points[2] = aRect.GetTopRight() + wxPoint( 0, aRect.GetHeight() / 2 ); points[3] = aRect.GetBottomRight() + wxPoint( -4, 1 ); points[4] = aRect.GetBottomLeft() + wxPoint( 0, 1 ); points[5] = aRect.GetTopLeft(); dc->SetPen( *wxBLACK_PEN ); dc->DrawLines( 6, points ); } // We should be able to pass wxDATAVIEW_CELL_SELECTED into RenderText() and have it do // the right thing -- but it picks wxSYS_COLOUR_HIGHLIGHTTEXT on MacOS and GTK (instead // of wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT). if( aState & wxDATAVIEW_CELL_SELECTED ) dc->SetTextForeground( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT ) ); // aRect.Deflate( 1 ); RenderText( m_text, 0, aRect, dc, 0 ); return true; } private: bool m_canvasItem; wxString m_text; }; wxDataViewItem LIB_TREE_MODEL_ADAPTER::ToItem( const LIB_TREE_NODE* aNode ) { return wxDataViewItem( const_cast( static_cast( aNode ) ) ); } LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ToNode( wxDataViewItem aItem ) { return static_cast( aItem.GetID() ); } LIB_TREE_NODE_LIBRARY& LIB_TREE_MODEL_ADAPTER::DoAddLibraryNode( const wxString& aNodeName, const wxString& aDesc ) { LIB_TREE_NODE_LIBRARY& lib_node = m_tree.AddLib( aNodeName, aDesc ); return lib_node; } void LIB_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl ) { m_widget = aDataViewCtrl; aDataViewCtrl->AssociateModel( this ); recreateColumns(); } void LIB_TREE_MODEL_ADAPTER::recreateColumns() { m_widget->ClearColumns(); m_columns.clear(); // The Item column is always shown doAddColumn( wxT( "Item" ) ); doAddColumn( wxT( "Description" ) ); } wxDataViewColumn* LIB_TREE_MODEL_ADAPTER::doAddColumn( const wxString& aHeader ) { int index = (int) m_columns.size(); wxDataViewColumn *col = new wxDataViewColumn( aHeader, new LIB_TREE_RENDERER(), index, 150, wxALIGN_NOT, wxDATAVIEW_CELL_INERT | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); m_widget->AppendColumn( col ); m_columns.emplace_back( col ); return col; } unsigned int LIB_TREE_MODEL_ADAPTER::GetChildren( const wxDataViewItem& aItem, wxDataViewItemArray& aChildren ) const { const LIB_TREE_NODE* node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree ); unsigned int count = 0; for( std::unique_ptr const& child : node->m_Children ) { aChildren.Add( ToItem( &*child ) ); ++count; } return count; } bool LIB_TREE_MODEL_ADAPTER::HasContainerColumns( const wxDataViewItem& aItem ) const { return IsContainer( aItem ); } bool LIB_TREE_MODEL_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const { LIB_TREE_NODE* node = ToNode( aItem ); return node ? node->m_Children.size() : true; } wxDataViewItem LIB_TREE_MODEL_ADAPTER::GetParent( const wxDataViewItem& WXUNUSED(aItem) ) const { return ToItem( nullptr ); } void LIB_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant, const wxDataViewItem& aItem, unsigned int aCol ) const { LIB_TREE_NODE* node = ToNode( aItem ); wxCHECK( node, /* void */ ); switch( aCol ) { case NAME_COL: //aVariant = wxString("Name") << aCol; aVariant = node->m_Name; break; default: // aVariant = wxString("Description") << aCol; aVariant = node->m_Desc; break; } } // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- // Define a new application type, each program should derive a class from wxApp class MyApp : public wxApp { public: virtual bool OnInit() wxOVERRIDE; }; // Define a new frame type: this is going to be our main frame class MyFrame : public wxFrame { public: // ctor(s) MyFrame(const wxString& title); private: // any class wishing to process wxWidgets events must use this macro wxDECLARE_EVENT_TABLE(); }; // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- // IDs for the controls and the menu commands enum { // menu items Minimal_Quit = wxID_EXIT, // it is important for the id corresponding to the "About" command to have // this standard value as otherwise it won't be handled properly under Mac // (where it is special and put into the "Apple" menu) Minimal_About = wxID_ABOUT }; wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) wxEND_EVENT_TABLE() wxIMPLEMENT_APP(MyApp); // ============================================================================ // implementation // ============================================================================ // ---------------------------------------------------------------------------- // the application class // ---------------------------------------------------------------------------- // 'Main program' equivalent: the program execution "starts" here bool MyApp::OnInit() { if ( !wxApp::OnInit() ) return false; MyFrame *frame = new MyFrame("Minimal wxWidgets App"); frame->Show(true); return true; } // ---------------------------------------------------------------------------- // main frame // ---------------------------------------------------------------------------- // frame constructor MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { LIB_TREE_MODEL_ADAPTER* adapter = new LIB_TREE_MODEL_ADAPTER(); for( int i = 0; i < 100; i++ ) adapter->DoAddLibraryNode( wxString( "Lib_Name" ) << i, wxString( "Description" ) << i ); wxDataViewCtrl* m_tree_ctrl = new wxDataViewCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_SINGLE ); adapter->AttachTo( m_tree_ctrl ); wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); sizer->Add( m_tree_ctrl, 1, wxEXPAND, 5 ); SetSizer( sizer ); Layout(); }