CMenu menu ; menu.CreateMenu() ; menu.AppendMenu(MF_STRING, ECmdOpen, L"") ; menu.AppendMenu(MF_STRING, ECmdClose, L"") ; menu.AppendMenu(MF_SEPARATOR) ; menu.AppendMenu(MF_STRING, ECmdTwist, L"") ; CMenu scratchMenu ; scratchMenu.CreateMenu() ; scratchMenu.AppendMenu(MF_STRING, ECmdScratchHead, L"") ; scratchMenu.AppendMenu(MF_STRING, ECmdScratchNose, L"") ; menu.AppendMenu(MF_POPUP, scratchMenu, L"") ; menu.AppendMenu(MF_STRING, ECmdLose, L"") ;
popup(L"") [command(L"", ECmdOpen)] [command(L"", ECmdClose)] [separator()] [command(L"", ECmdTwist)] [popup(L"") [command(L"", ECmdScratchHead)] [command(L"", ECmdScratchNose)] ] [command(L"", ECmdLose)]
struct node { node & operator[](...) { // ... return *this ; } } ;
node n ; n[123][456][789] ;
enum node_type { EEmpty, ESeparator, ECommand, EPopup } ;
EPopup
node can have as many children of any type as possible; the other nodes cannot have children.append()
method allows you to add children to the node, the append_to()
method is needed to create a real menu when traversing our tree. struct node_impl_base ; typedef std::auto_ptr<node_impl_base> node_impl_ptr ; struct node_impl_base { virtual ~node_impl_base() {} virtual node_type type() const = 0 ; virtual void append(node & n) = 0 ; virtual void append_to(HMENU aMenu) const = 0 ; } ; typedef std::auto_ptr<node_impl_base> node_impl_ptr ;
template <node_type Type> struct node_impl: public node_impl_base { static const node_type KType = Type ; node_type type() const { return KType; } void append(node & n) { _ASSERT(!"not allowed"); } };
_ASSERT
needed in order not to accidentally call the append()
method for a node that cannot have children. struct empty_node: node_impl<EEmpty>, boost::noncopyable { void append_to(HMENU aMenu) const { CMenuHandle(aMenu).AppendMenu(MF_STRING); } }; struct separator_node: node_impl<ESeparator>, boost::noncopyable { void append_to(HMENU aMenu) const { CMenuHandle(aMenu).AppendMenu(MF_SEPARATOR); } }; struct command_node: node_impl<ECommand>, boost::noncopyable { command_node(PCTSTR text, int id): text_(text), id_(id) {} void append_to(HMENU aMenu) const { CMenuHandle(aMenu).AppendMenu(MF_STRING, id_, text_); } private: CString text_ ; int id_ ; } ; struct popup_node: node_impl<EPopup>, boost::noncopyable { popup_node(PCTSTR text): text_(text) {} void append(node & n) { children_.push_back(new node(n)); } void append_to(HMENU aMenu) const { CMenuHandle menu ; menu.CreatePopupMenu() ; BOOST_FOREACH(const node & n, children_) { n.append_to(menu) ; } CMenuHandle(aMenu).AppendMenu(MF_STRING, menu, text_) ; } private: boost::ptr_vector<node> children_ ; CString text_ ; } ;
node
type. The idiom "pimpl" is used, that is, node contains a pointer to a specific implementation of the node. Pay attention to the semantics of copying and assignment: struct node { friend node empty() ; friend node separator() ; friend node command(PCTSTR text, int id) ; friend node popup(PCTSTR text) ; node(): impl_(new empty_node()) {} node(node & other): impl_(other.impl_) {} // move node & operator=(node & other) { impl_ = other.impl_; return *this; } // move node & operator[](node & n) { impl_->append(n); return *this; } node_type type() const { return impl_->type(); } void append_to(HMENU aMenu) const { impl_->append_to(aMenu); } private: node(node_impl_ptr impl): impl_(impl) {} // take ownership node_impl_ptr impl_ ; } ;
node
another, the implementation is transferred (the so-called move semantics). An object from the right side becomes a dummy. The copy constructor also works. ( std::auto_ptr
works in the same way).node_impl_ptr
is std::auto_ptr
, you could not explicitly define node(node & other)
and operator=(node & other)
, the compiler would generate them themselves.empty()
, they use a private constructor and are therefore declared as friend
. node empty() { return node() ; } node separator() { return node(node_impl_ptr(new separator_node())) ; } node command(PCTSTR text, int id) { return node(node_impl_ptr(new command_node(text, id))) ; } node popup(PCTSTR text) { return node(node_impl_ptr(new popup_node(text))) ; }
struct menubar { menubar(node key1, node key2) ; // ... }; SetMenuBar( menubar( command(L"Ok", IDOK), popup(L"") [command(L"", ECmdOpen)] [command(L"", ECmdClose)] [separator()] [command(L"", ECmdTwist)] [popup(L"") [command(L"", ECmdScratchHead)] [command(L"", ECmdScratchNose)] ] [command(L"", ECmdLose)] ) ) ;
SetMenuBar()
.menubar
consists of two trees, because the Windows Mobile application has two soft keys. The implementation of SetMenuBar()
is beyond the scope of this article, and so a lot of text has already happened :)Source: https://habr.com/ru/post/88844/
All Articles