1 // =================================
  2 // Copyright (c) 2022 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.IO;
  8 
  9 namespace System.Screen
 10 {
 11     public class MenuControlCreateParams
 12     {
 13         public nothrow MenuControlCreateParams() : 
 14             controlCreateParams()
 15             highlightColor(ConsoleColor.defaultColor)
 16             disabledColor(ConsoleColor.defaultColor)
 17             focusedItemForeColor(ConsoleColor.defaultColor)
 18             focusedItemBackColor(ConsoleColor.defaultColor)
 19             gap(-1)
 20         {
 21         }
 22         public nothrow MenuControlCreateParams& Defaults()
 23         {
 24             return *this;
 25         }
 26         public nothrow MenuControlCreateParams& HighlightColor(ConsoleColor highlightColor_)
 27         {
 28             highlightColor = highlightColor_;
 29             return *this;
 30         }
 31         public nothrow MenuControlCreateParams& DisabledColor(ConsoleColor disabledColor_)
 32         {
 33             disabledColor = disabledColor_;
 34             return *this;
 35         }
 36         public nothrow MenuControlCreateParams& FocusedItemForeColor(ConsoleColor focusedItemForeColor_)
 37         {
 38             focusedItemForeColor = focusedItemForeColor_;
 39             return *this;
 40         }
 41         public nothrow MenuControlCreateParams& FocusedItemBackColor(ConsoleColor focusedItemBackColor_)
 42         {
 43             focusedItemBackColor = focusedItemBackColor_;
 44             return *this;
 45         }
 46         public ControlCreateParams controlCreateParams;
 47         public ConsoleColor highlightColor;
 48         public ConsoleColor disabledColor;
 49         public ConsoleColor focusedItemForeColor;
 50         public ConsoleColor focusedItemBackColor;
 51         public int gap;
 52     }
 53 
 54     public enum MenuControlFlags
 55     {
 56         none = 0changed = 1 << 0menuOpen = 1 << 1
 57     }
 58 
 59     public class MenuControl : Control
 60     {
 61         public nothrow MenuControl(MenuControlCreateParams& createParams) : base(createParams.controlCreateParams)flags(MenuControlFlags.none)
 62         {
 63             if (createParams.highlightColor == ConsoleColor.defaultColor)
 64             {
 65                 highlightColor = ConsoleColor.red;
 66             }
 67             else
 68             {
 69                 highlightColor = createParams.highlightColor;
 70             }
 71             if (createParams.disabledColor == ConsoleColor.defaultColor)
 72             {
 73                 disabledColor = ConsoleColor.darkGray;
 74             }
 75             else
 76             {
 77                 disabledColor = createParams.disabledColor;
 78             }
 79             if (createParams.focusedItemForeColor == ConsoleColor.defaultColor)
 80             {
 81                 focusedItemForeColor = cast<ConsoleColor>(defaultFocusedControlForeColor);
 82             }
 83             else
 84             {
 85                 focusedItemForeColor = createParams.focusedItemForeColor;
 86             }
 87             if (createParams.focusedItemBackColor == ConsoleColor.defaultColor)
 88             {
 89                 focusedItemBackColor = cast<ConsoleColor>(defaultFocusedControlBackColor);
 90             }
 91             else
 92             {
 93                 focusedItemBackColor = createParams.focusedItemBackColor;
 94             }
 95             if (createParams.gap == -1)
 96             {
 97                 gap = 8;
 98             }
 99             else
100             {
101                 gap = createParams.gap;
102             }
103         }
104         public nothrow void SetChanged()
105         {
106             flags = cast<MenuControlFlags>(flags | MenuControlFlags.changed);
107         }
108         public nothrow bool IsChanged() const
109         {
110             return (flags & MenuControlFlags.changed) != MenuControlFlags.none;
111         }
112         public nothrow void ResetChanged()
113         {
114             flags = cast<MenuControlFlags>(flags & ~MenuControlFlags.changed);
115         }
116         public nothrow void SetOpen()
117         {
118             if (!IsOpen())
119             {
120                 flags = cast<MenuControlFlags>(flags | MenuControlFlags.menuOpen);
121                 Invalidate();
122             }
123         }
124         public virtual nothrow void ResetOpen()
125         {
126             if (IsOpen())
127             {
128                 focusedMenuItem = null;
129                 flags = cast<MenuControlFlags>(flags & ~MenuControlFlags.menuOpen);
130                 Application.Instance().MainWindow()->Invalidate();
131             }
132         }
133         public nothrow bool IsOpen() const
134         {
135             return (flags & MenuControlFlags.menuOpen) != MenuControlFlags.none;
136         }
137         public nothrow ConsoleColor HighlightColor() const
138         {
139             return highlightColor;
140         }
141         public nothrow ConsoleColor DisabledColor() const
142         {
143             return disabledColor;
144         }
145         public nothrow ConsoleColor FocusedItemForeColor() const
146         {
147             return focusedItemForeColor;
148         }
149         public nothrow ConsoleColor FocusedItemBackColor() const
150         {
151             return focusedItemBackColor;
152         }
153         public nothrow int Gap() const
154         {
155             return gap;
156         }
157         public nothrow MenuItem* FocusedMenuItem() const
158         {
159             return focusedMenuItem;
160         }
161         public nothrow void SetFocusedMenuItem(MenuItem* focusedMenuItem_)
162         {
163             focusedMenuItem = focusedMenuItem_;
164         }
165         public override void OnGotFocus()
166         {
167             focusedMenuItem = null;
168         }
169         public override void OnLostFocus()
170         {
171             focusedMenuItem = null;
172         }
173         private MenuControlFlags flags;
174         private ConsoleColor highlightColor;
175         private ConsoleColor disabledColor;
176         private ConsoleColor focusedItemForeColor;
177         private ConsoleColor focusedItemBackColor;
178         private int gap;
179         private MenuItem* focusedMenuItem;
180     }
181 
182     public class MenuBar : MenuControl
183     {
184         public nothrow MenuBar(MenuControlCreateParams& createParams) : base(createParams)menuItems(this)
185         {
186             InvalidateGuard guard(thisInvalidateKind.dontInvalidate);
187             if (Location().IsDefault())
188             {
189                 SetLocation(Point(00));
190             }
191             if (GetSize().IsDefault())
192             {
193                 SetSize(Size(TerminalWindowWidth()1));
194             }
195             if (ForeColor() == ConsoleColor.defaultColor)
196             {
197                 SetForeColor(ConsoleColor.black);
198             }
199             if (BackColor() == ConsoleColor.defaultColor)
200             {
201                 SetBackColor(ConsoleColor.gray);
202             }
203             SetChanged();
204         }
205         public void AddMenuItem(MenuItem* menuItem)
206         {
207             menuItems.AddChild(menuItem);
208             menuItem->SetAccessKey();
209         }
210         public override nothrow void ResetOpen()
211         {
212             base->ResetOpen();
213             Component* child = menuItems.FirstChild();
214             while (child != null)
215             {
216                 if (child is MenuItem*)
217                 {
218                     MenuItem* menuItem = cast<MenuItem*>(child);
219                     menuItem->SetState(MenuItemState.closed);
220                 }
221                 child = child->NextSibling();
222             }
223         }
224         public override void OnKeyPressed(KeyEventArgs& args)
225         {
226             if (args.Handled()) return;
227             if (IsOpen() && args.Key() == keyEscape)
228             {
229                 ResetOpen();
230                 SetFocusedMenuItem(null);
231                 args.SetHandled();
232                 return;
233             }
234             MenuItem* focusedMenuItem = FocusedMenuItem();
235             if (focusedMenuItem != null)
236             {
237                 focusedMenuItem->OnKeyPressed(args);
238                 if (args.Handled())
239                 {
240                     return;
241                 }
242             }
243             uchar key = args.Key();
244             if (key >= 'a' && key <= 'z')
245             {
246                 key = ToUpper(key);
247             }
248             Component* child = menuItems.FirstChild();
249             while (child != null)
250             {
251                 if (child is MenuItem*)
252                 {
253                     MenuItem* menuItem = cast<MenuItem*>(child);
254                     if (menuItem->IsEnabled())
255                     {
256                         if (menuItem->AccessKey() == key)
257                         {
258                             args.SetHandled();
259                             ResetOpen();
260                             SetOpen();
261                             menuItem->SetState(MenuItemState.open);
262                             SetFocusedMenuItem(menuItem->FirstChildItem());
263                             Invalidate();
264                             return;
265                         }
266                         else
267                         {
268                             if (menuItem->DispatchKey(key))
269                             {
270                                 args.SetHandled();
271                                 return;
272                             }
273                         }
274                     }
275                 }
276                 child = child->NextSibling();
277             }
278         }
279         public override void OnWriteScreen(WriteScreenEventArgs& args)
280         {
281             base->OnWriteScreen(args);
282             Rect rect = args.GetRect();
283             if (rect.IsDefault())
284             {
285                 rect = GetRect();
286             }
287             Clear(rectForeColor()BackColor());
288             if (IsChanged())
289             {
290                 Measure();
291             }
292             WriteMenuItems();
293             MenuItem* focusedMenuItem = FocusedMenuItem();
294             if (focusedMenuItem != null)
295             {
296                 Point location = focusedMenuItem->Location();
297                 SetCursorPos(location.x + 1location.y);
298             }
299         }
300         private void Measure()
301         {
302             Point loc = Location();
303             loc.x = loc.x + 1;
304             Component* child = menuItems.FirstChild();
305             while (child != null)
306             {
307                 if (child is MenuItem*)
308                 {
309                     MenuItem* menuItem = cast<MenuItem*>(child);
310                     menuItem->Measure(loc);
311                 }
312                 child = child->NextSibling();
313             }
314         }
315         private void WriteMenuItems()
316         {
317             Component* child = menuItems.FirstChild();
318             while (child != null)
319             {
320                 if (child is MenuItem*)
321                 {
322                     MenuItem* menuItem = cast<MenuItem*>(child);
323                     menuItem->WriteScreen();
324                 }
325                 child = child->NextSibling();
326             }
327         }
328         private Container menuItems;
329     }
330     
331     public enum MenuItemState
332     {
333         closed = 0open = 1
334     }
335 
336     public enum MenuItemFlags
337     {
338         none = 0disabled = 1 << 0
339     }
340     
341     public class MenuItem : Component
342     {
343         public nothrow MenuItem(const string& text_uchar shortcut_) : state(MenuItemState.closed)flags(MenuItemFlags.none)text(ToUtf32(text_))items(this)accessKey()shortcut(shortcut_)
344         {
345         }
346         public nothrow MenuItem(const string& text_) : this(text_uchar())
347         {
348         }
349         public nothrow void SetText(const string& text_)
350         {
351             text = ToUtf32(text_);
352             SetAccessKey();
353         }
354         public nothrow void SetDisabled()
355         {
356             flags = cast<MenuItemFlags>(flags | MenuItemFlags.disabled);
357         }
358         public nothrow void SetEnabled()
359         {
360             flags = cast<MenuItemFlags>(flags & ~MenuItemFlags.disabled);
361         }
362         public nothrow bool IsDisabled() const
363         {
364             return (flags & MenuItemFlags.disabled) != MenuItemFlags.none;
365         }
366         public nothrow bool IsEnabled() const
367         {
368             return (flags & MenuItemFlags.disabled) == MenuItemFlags.none;
369         }
370         public nothrow void SetState(MenuItemState state_)
371         {
372             if (state != state_)
373             {
374                 state = state_;
375             }
376         }
377         public nothrow bool IsTopLevel() const
378         {
379             return ParentMenuItem() == null;
380         }
381         public nothrow bool IsFocused() const
382         {
383             MenuControl* menuControl = GetMenuControl();
384             if (menuControl != null)
385             {
386                 return menuControl->FocusedMenuItem() == this;
387             }
388             else
389             {
390                 return false;
391             }
392         }
393         public nothrow MenuItem* FirstChildItem() const
394         {
395             Component* child = items.FirstChild();
396             while (child != null)
397             {
398                 if (child is MenuItem*)
399                 {
400                     return cast<MenuItem*>(child);
401                 }
402                 child = child->NextSibling();
403             }
404             return null;
405         }
406         public nothrow MenuItem* LastChildItem() const
407         {
408             Component* child = items.LastChild();
409             while (child != null)
410             {
411                 if (child is MenuItem*)
412                 {
413                     return cast<MenuItem*>(child);
414                 }
415                 child = child->PrevSibling();
416             }
417             return null;
418         }
419         public nothrow MenuItem* NextMenuItem() const
420         {
421             Component* nextSibling = NextSibling();
422             while (nextSibling != null)
423             {
424                 if (nextSibling is MenuItem*)
425                 {
426                     return cast<MenuItem*>(nextSibling);
427                 }
428                 nextSibling = nextSibling->NextSibling();
429             }
430             return null;
431         }
432         public nothrow MenuItem* PrevMenuItem() const
433         {
434             Component* prevSibling = PrevSibling();
435             while (prevSibling != null)
436             {
437                 if (prevSibling is MenuItem*)
438                 {
439                     return cast<MenuItem*>(prevSibling);
440                 }
441                 prevSibling = prevSibling->PrevSibling();
442             }
443             return null;
444         }
445         public nothrow bool HasChildItems() const
446         {
447             return !items.IsEmpty();
448         }
449         public void AddMenuItem(MenuItem* menuItem)
450         {
451             items.AddChild(menuItem);
452             menuItem->SetAccessKey();
453         }
454         public nothrow MenuItem* ParentMenuItem()
455         {
456             Container* container = GetContainer();
457             if (container != null)
458             {
459                 Component* parent = container->Parent();
460                 if (parent != null)
461                 {
462                     if (parent is MenuItem*)
463                     {
464                         return cast<MenuItem*>(parent);
465                     }
466                 }
467             }
468             return null;
469         }
470         public MenuControl* GetMenuControl()
471         {
472             Container* container = GetContainer();
473             if (container != null)
474             {
475                 Component* parent = container->Parent();
476                 if (parent is MenuControl*)
477                 {
478                     return cast<MenuControl*>(parent);
479                 }
480                 else if (parent is MenuItem*)
481                 {
482                     MenuItem* parentMenuItem = cast<MenuItem*>(parent);
483                     return parentMenuItem->GetMenuControl();
484                 }
485             }
486             else
487             {
488                 throw Exception("menu control not found");
489             }
490         }
491         public nothrow const Rect& MenuBoxRect() const
492         {
493             return menuBoxRect;
494         }
495         public nothrow const Point& Location() const
496         {
497             return location;
498         }
499         public void OnKeyPressed(KeyEventArgs& args)
500         {
501             MenuControl* menuControl = GetMenuControl();
502             if (IsFocused())
503             {
504                 switch (args.Key())
505                 {
506                     case keyNewline:
507                     {
508                         args.SetHandled();
509                         Select();
510                         menuControl->SetFocusedMenuItem(null);
511                         MenuItem* parentItem = ParentMenuItem();
512                         if (parentItem != null)
513                         {
514                             Application.Instance().MainWindow()->Invalidate(parentItem->MenuBoxRect());
515                         }
516                         break;
517                     }
518                     case keyUp:
519                     {
520                         MenuItem* prevMenuItem = PrevMenuItem();
521                         if (prevMenuItem != null)
522                         {
523                             menuControl->SetFocusedMenuItem(prevMenuItem);
524                             args.SetHandled();
525                             menuControl->Invalidate();
526                         }
527                         break;
528                     }
529                     case keyDown:
530                     {
531                         MenuItem* nextMenuItem = NextMenuItem();
532                         if (nextMenuItem != null)
533                         {
534                             menuControl->SetFocusedMenuItem(nextMenuItem);
535                             args.SetHandled();
536                             menuControl->Invalidate();
537                         }
538                         break;
539                     }
540                 }
541             }
542         }
543         public bool DispatchKey(uchar key)
544         {
545             Component* child = items.FirstChild();
546             while (child != null)
547             {
548                 if (child is MenuItem*)
549                 {
550                     MenuItem* menuItem = cast<MenuItem*>(child);
551                     if (menuItem->IsEnabled())
552                     {
553                         if (state == MenuItemState.open)
554                         {
555                             if (menuItem->AccessKey() == ToUpper(key))
556                             {
557                                 if (menuItem->HasChildItems())
558                                 {
559                                     menuItem->SetState(MenuItemState.open);
560                                     MenuControl* menuControl = GetMenuControl();
561                                     menuControl->Invalidate();
562                                     return true;
563                                 }
564                                 else
565                                 {
566                                     menuItem->Select();
567                                     Application.Instance().MainWindow()->Invalidate(menuBoxRect);
568                                     return true;
569                                 }
570                             }
571                             else if (menuItem->Shortcut() == key)
572                             {
573                                 menuItem->Select();
574                                 Application.Instance().MainWindow()->Invalidate(menuBoxRect);
575                                 return true;
576                             }
577                         }
578                         else if (menuItem->Shortcut() == key)
579                         {
580                             menuItem->Select();
581                             Application.Instance().MainWindow()->Invalidate(menuBoxRect);
582                             return true;
583                         }
584                         else
585                         {
586                             bool handled = menuItem->DispatchKey(key);
587                             if (handled)
588                             {
589                                 return true;
590                             }
591                         }
592                     }
593                 }
594                 child = child->NextSibling();
595             }
596             return false;
597         }
598         public nothrow Event<SelectEventHandler>& SelectEvent()
599         {
600             return selectEvent;
601         }
602         public void Select()
603         {
604             MenuControl* menuControl = GetMenuControl();
605             menuControl->ResetOpen();
606             MenuItem* parent = ParentMenuItem();
607             if (parent != null)
608             {
609                 parent->SetState(MenuItemState.closed);
610             }
611             selectEvent.Fire();
612         }
613         public nothrow void Measure(Point& loc)
614         {
615             if (IsTopLevel())
616             {
617                 MeasureTopLevel(loc);
618             }
619             else
620             {
621                 MeasureChild(loc);
622             }
623         }
624         public nothrow int TextLength() const
625         {
626             int ampPos = cast<int>(text.Find('&'));
627             if (ampPos != -1)
628             {
629                 int n = cast<int>(text.Length() - 1);
630                 return n;
631             }
632             else
633             {
634                 return cast<int>(text.Length());
635             }
636         }
637         public nothrow ustring ShortcutText() const
638         {
639             if (shortcut != '\0')
640             {
641                 ustring shortcutText = ToUtf32(KeyName(shortcut));
642                 return shortcutText;
643             }
644             else
645             {
646                 return ustring();
647             }
648         }
649         public nothrow uchar AccessKey() const
650         {
651             return accessKey;
652         }
653         public nothrow void SetAccessKey()
654         {
655             int ampPos = cast<int>(text.Find('&'));
656             if (ampPos != -1 && ampPos < text.Length() - 1)
657             {
658                 if (IsTopLevel())
659                 {
660                     accessKey = cast<uchar>(cast<int>(keyAltA) + (cast<int>(ToUpper(text[ampPos + 1])) - cast<int>('A')));
661                 }
662                 else
663                 {
664                     accessKey = ToUpper(text[ampPos + 1]);
665                 }
666             }
667             else
668             {
669                 accessKey = '\0';
670             }
671         }
672         public nothrow uchar Shortcut() const
673         {
674             return shortcut;
675         }
676         public void WriteScreen()
677         {
678             if (IsTopLevel())
679             {
680                 WriteTopLevel();
681             }
682             else
683             {
684                 WriteChild();
685             }
686         }
687         public nothrow Rect ItemRect() const
688         {
689             MenuItem* parent = ParentMenuItem();
690             Rect parentMenuBox = parent->MenuBoxRect();
691             Size sz(parentMenuBox.size.w - 21);
692             return Rect(locationsz);
693         }
694         private void MeasureTopLevel(Point& loc)
695         {
696             location = loc;
697             menuBoxRect.location.x = location.x - 1;
698             menuBoxRect.location.y = location.y + 1;
699             MeasureSize();
700             loc.x = loc.x + TextLength() + 2;
701             MeasureChildren();
702         }
703         private void MeasureChildren()
704         {
705             Point loc(location.xlocation.y + 2);
706             Component* child = items.FirstChild();
707             while (child != null)
708             {
709                 if (child is MenuItem*)
710                 {
711                     MenuItem* menuItem = cast<MenuItem*>(child);
712                     menuItem->MeasureChild(loc);
713                 }
714                 child = child->NextSibling();
715             }
716         }
717         private void MeasureSize()
718         {
719             MenuControl* menuControl = GetMenuControl();
720             int numChildren = 0;
721             int maxTextLength = 0;
722             int maxShortcutLength = 0;
723             Component* child = items.FirstChild();
724             while (child != null)
725             {
726                 if (child is MenuItem*)
727                 {
728                     MenuItem* menuItem = cast<MenuItem*>(child);
729                     int textLength = menuItem->TextLength();
730                     if (textLength > maxTextLength)
731                     {
732                         maxTextLength = textLength;
733                     }
734                     int shortcutTextLength = cast<int>(menuItem->ShortcutText().Length());
735                     if (shortcutTextLength > maxShortcutLength)
736                     {
737                         maxShortcutLength = shortcutTextLength;
738                     }
739                     ++numChildren;
740                 }
741                 child = child->NextSibling();
742             }
743             menuBoxRect.size = Size(maxTextLength + menuControl->Gap() + maxShortcutLength + 2numChildren + 2);
744         }
745         private void MeasureChild(Point& loc)
746         {
747             location = loc;
748             loc.y = loc.y + 1;
749         }
750         private void WriteTopLevel()
751         {
752             WriteText();
753             if (state == MenuItemState.open)
754             {
755                 WriteMenuBox();
756             }
757         }
758         private void WriteChild()
759         {
760             if (IsFocused())
761             {
762                 WriteFocused();
763             }
764             else
765             {
766                 WriteText();
767                 MenuItem* parent = ParentMenuItem();
768                 Rect parentMenuBox = parent->MenuBoxRect();
769                 ustring shortcutText = ShortcutText();
770                 int x = cast<int>(parentMenuBox.Right() - 1 - shortcutText.Length());
771                 int y = location.y;
772                 SetCursorPos(xy);
773                 Terminal.Out() << shortcutText;
774                 if (state == MenuItemState.open)
775                 {
776                     WriteMenuBox();
777                 }
778             }
779         }
780         private void WriteFocused()
781         {
782             MenuControl* menuControl = GetMenuControl();
783             Clear(ItemRect()menuControl->FocusedItemForeColor()menuControl->FocusedItemBackColor());
784             Terminal.Out() << SetColors(menuControl->FocusedItemForeColor()menuControl->FocusedItemBackColor());
785             WriteText();
786             MenuItem* parent = ParentMenuItem();
787             Rect parentMenuBox = parent->MenuBoxRect();
788             ustring shortcutText = ShortcutText();
789             int x = cast<int>(parentMenuBox.Right() - 1 - shortcutText.Length());
790             int y = location.y;
791             SetCursorPos(xy);
792             ConsoleColor textColor = menuControl->ForeColor();
793             if (IsDisabled())
794             {
795                 textColor = menuControl->DisabledColor();
796             }
797             Terminal.Out() << SetColors(menuControl->FocusedItemForeColor()menuControl->FocusedItemBackColor());
798             Terminal.Out() << shortcutText;
799         }
800         private void WriteText()
801         {
802             MenuControl* menuControl = GetMenuControl();
803             ConsoleColor textColor = menuControl->ForeColor();
804             if (IsDisabled())
805             {
806                 textColor = menuControl->DisabledColor();
807             }
808             SetCursorPos(location.x + 1location.y);
809             int ampPos = cast<int>(text.Find('&'));
810             bool isFocused = IsFocused();
811             if (ampPos != -1)
812             {
813                 ustring prefix = text.Substring(0ampPos);
814                 if (!isFocused)
815                 {
816                     Terminal.Out() << SetColors(textColormenuControl->BackColor());
817                 }
818                 else
819                 {
820                     Terminal.Out() << SetColors(menuControl->FocusedItemForeColor()menuControl->FocusedItemBackColor());
821                 }
822                 Terminal.Out() << prefix;
823                 ustring highlightStr = text.Substring(ampPos + 11);
824                 if (!isFocused)
825                 {
826                     Terminal.Out() << SetColors(menuControl->HighlightColor()menuControl->BackColor());
827                 }
828                 Terminal.Out() << highlightStr;
829                 if (!isFocused)
830                 {
831                     Terminal.Out() << SetColors(textColormenuControl->BackColor());
832                 }
833                 ustring suffix = text.Substring(ampPos + 2);
834                 Terminal.Out() << suffix;
835             }
836             else
837             {
838                 Terminal.Out() << text;
839             }
840         }
841         private void WriteMenuBox()
842         {
843             MenuControl* menuControl = GetMenuControl();
844             WriteBox(menuBoxRectmenuControl->ForeColor()menuControl->BackColor());
845             Component* child = items.FirstChild();
846             while (child != null)
847             {
848                 if (child is MenuItem*)
849                 {
850                     MenuItem* menuItem = cast<MenuItem*>(child);
851                     menuItem->WriteScreen();
852                 }
853                 child = child->NextSibling();
854             }
855         }
856         private MenuItemState state;
857         private MenuItemFlags flags;
858         private Point location;
859         private Rect menuBoxRect;
860         private ustring text;
861         private Container items;
862         private uchar accessKey;
863         private uchar shortcut;
864         private Event<SelectEventHandler> selectEvent;
865     }
866     
867     public abstract class SelectAction
868     {
869         public SelectAction(MenuItem* menuItem)
870         {
871             menuItem->SelectEvent().AddHandler(Select);
872         }
873         public virtual default ~SelectAction();
874         public abstract void Execute();
875         private void Select()
876         {
877             Execute();
878         }
879     }
880 }