1
2
3
4
5
6 using System;
7 using System.Collections;
8 using System.Windows.API;
9
10 namespace System.Windows
11 {
12 public Color DefaultMenuBackgroundColor()
13 {
14 return GetSystemColor(SystemColor.COLOR_MENU);
15 }
16
17 public Color DefaultMenuTextColor()
18 {
19 return GetSystemColor(SystemColor.COLOR_MENUTEXT);
20 }
21
22 public Color DefaultDisabledMenuTextColor()
23 {
24 Color color = GetSystemColor(SystemColor.COLOR_GRAYTEXT);
25 if (color == Color.Black())
26 {
27 return Color(195u, 195u, 198u);
28 }
29 return color;
30 }
31
32 public Color DefaultMenuMouseOverColor()
33 {
34 return Color(201u, 222u, 245u);
35 }
36
37 public Color DefaultMenuOpenColor()
38 {
39 return Color.White();
40 }
41
42 public ControlCreateParams& MenuControlControlCreateParams(ControlCreateParams& controlCreateParams)
43 {
44 return controlCreateParams.SetWindowClassBackgroundColor(SystemColor.COLOR_MENU).SetBackgroundColor(DefaultMenuBackgroundColor());
45 }
46
47 public class MenuControlCreateParams
48 {
49 public MenuControlCreateParams(ControlCreateParams& controlCreateParams_) :
50 controlCreateParams(controlCreateParams_),
51 fontFamilyName("Segoe UI"),
52 fontSize(9.000000f),
53 textColor(DefaultMenuTextColor()),
54 disabledTextColor(DefaultDisabledMenuTextColor()),
55 mouseOverColor(DefaultMenuMouseOverColor()),
56 menuOpenColor(DefaultMenuOpenColor())
57 {
58 }
59 public MenuControlCreateParams& Defaults()
60 {
61 return *this;
62 }
63 public ControlCreateParams& controlCreateParams;
64 public string fontFamilyName;
65 public float fontSize;
66 public Color textColor;
67 public Color disabledTextColor;
68 public Color mouseOverColor;
69 public Color menuOpenColor;
70 }
71
72 public abstract class MenuControl : Control
73 {
74 public MenuControl(const Font& font_, const string& windowClassName, WindowClassStyle windowClassStyle, WindowStyle style,
75 ExtendedWindowStyle exStyle,
76 const Color& backgroundColor, const string& text, const Point& location, const Size& size, Dock dock, Anchors anchors) :
77 base(windowClassName, windowClassStyle, style, exStyle, backgroundColor, text, location, size, dock, anchors),
78 font(font_), textColor(DefaultMenuTextColor()), disabledTextColor(DefaultDisabledMenuTextColor()), mouseOverColor(201u, 222u, 245u),
79 menuOpenColor(Color.White()), shadowColor(ShadowColor()), textBrush(textColor), disabledTextBrush(disabledTextColor),
80 backgroundBrush(BackgroundColor()), mouseOverBrush(mouseOverColor), menuOpenBrush(menuOpenColor),
81 shadowBrush(shadowColor), blackBrush(Color.Black()), blackPen(Color.Black()), darkPen(Color.DarkGray()),
82 format(StringAlignment.near, StringAlignment.near, HotKeyPrefix.show)
83 {
84 }
85 public MenuControl(MenuControlCreateParams& createParams) :
86 base(createParams.controlCreateParams),
87 font(FontFamily(createParams.fontFamilyName), createParams.fontSize),
88 textColor(createParams.textColor),
89 disabledTextColor(createParams.disabledTextColor),
90 mouseOverColor(createParams.mouseOverColor),
91 menuOpenColor(createParams.menuOpenColor),
92 shadowColor(ShadowColor()),
93 textBrush(textColor), disabledTextBrush(disabledTextColor),
94 backgroundBrush(BackgroundColor()), mouseOverBrush(mouseOverColor), menuOpenBrush(menuOpenColor),
95 shadowBrush(shadowColor), blackBrush(Color.Black()), blackPen(Color.Black()), darkPen(Color.DarkGray()),
96 format(StringAlignment.near, StringAlignment.near, HotKeyPrefix.show)
97 {
98 }
99 public override Padding DefaultPadding() const
100 {
101 return Padding(6, 2, 6, 2);
102 }
103 public inline const Font& GetFont() const
104 {
105 return font;
106 }
107 public inline const Color& TextColor() const
108 {
109 return textColor;
110 }
111 public inline const Color& DisabledTextColor() const
112 {
113 return disabledTextColor;
114 }
115 public inline const Color& MouseOverColor() const
116 {
117 return mouseOverColor;
118 }
119 public inline const Color& MenuOpenColor() const
120 {
121 return menuOpenColor;
122 }
123 public inline const Color& ShadowColor() const
124 {
125 return shadowColor;
126 }
127 public inline const Brush& TextBrush() const
128 {
129 return textBrush;
130 }
131 public inline const Brush& DisabledTextBrush() const
132 {
133 return disabledTextBrush;
134 }
135 public inline const Brush& BackgroundBrush() const
136 {
137 return backgroundBrush;
138 }
139 public inline const Brush& MouseOverBrush() const
140 {
141 return mouseOverBrush;
142 }
143 public inline const Brush& MenuOpenBrush() const
144 {
145 return menuOpenBrush;
146 }
147 public inline const Brush& ShadowBrush() const
148 {
149 return shadowBrush;
150 }
151 public inline const Brush& BlackBrush() const
152 {
153 return blackBrush;
154 }
155 public inline const Pen& BlackPen() const
156 {
157 return blackPen;
158 }
159 public inline const Pen& DarkPen() const
160 {
161 return darkPen;
162 }
163 public inline const StringFormat& Format() const
164 {
165 return format;
166 }
167 internal virtual MenuItem* GetMenuItemByAccessKey(wchar accessKey)
168 {
169 return null;
170 }
171 internal virtual MenuItem* GetOpenedMenuItem() const
172 {
173 return null;
174 }
175 internal virtual MenuItem* GetSelectedMenuItem() const
176 {
177 return null;
178 }
179 internal virtual void SetSelectedMenuItem(MenuItem* selectedMenuItem_)
180 {
181 }
182 internal virtual bool IsOpen() const
183 {
184 return false;
185 }
186 public virtual void SetOpen()
187 {
188 }
189 [nodiscard]
190 internal virtual Result<bool> SetClosed()
191 {
192 return Result<bool>(true);
193 }
194 [nodiscard]
195 internal virtual Result<bool> SetMenuInvalidated()
196 {
197 return Result<bool>(true);
198 }
199 internal virtual MenuItem* GetFirstMenuItem() const
200 {
201 return null;
202 }
203 internal virtual MenuItem* GetLastMenuItem() const
204 {
205 return null;
206 }
207 internal virtual MenuItem* LatestOpenedMenuItem() const
208 {
209 return null;
210 }
211 internal virtual void SetLatestOpenedMenuItem(MenuItem* menuItem)
212 {
213 }
214 internal virtual MenuItem* GetLatestMouseDownMenuItem() const
215 {
216 return null;
217 }
218 internal virtual void SetLatestMouseDownMenuItem(MenuItem* menuItem)
219 {
220 }
221 [nodiscard]
222 internal virtual Result<bool> InvalidateMenu()
223 {
224 return Result<bool>(true);
225 }
226 internal virtual void SetMenuChanged()
227 {
228 }
229 private Font font;
230 private Color textColor;
231 private Color disabledTextColor;
232 private Color mouseOverColor;
233 private Color menuOpenColor;
234 private Color shadowColor;
235 private SolidBrush textBrush;
236 private SolidBrush disabledTextBrush;
237 private SolidBrush backgroundBrush;
238 private SolidBrush mouseOverBrush;
239 private SolidBrush menuOpenBrush;
240 private SolidBrush shadowBrush;
241 private SolidBrush blackBrush;
242 private Pen blackPen;
243 private Pen darkPen;
244 private StringFormat format;
245 }
246
247 public const int initialMenuBarHeight = 20;
248
249 public ControlCreateParams& MenuBarControlCreateParams(ControlCreateParams& controlCreateParams)
250 {
251 return controlCreateParams.SetWindowClassBackgroundColor(SystemColor.COLOR_MENU).SetBackgroundColor(DefaultMenuBackgroundColor()).
252 SetWindowClassName("System.Windows.MenuBar").SetDock(Dock.top).SetSize(Size(0, initialMenuBarHeight));
253 }
254
255 public class MenuBarCreateParams
256 {
257 public MenuBarCreateParams(MenuControlCreateParams& menuControlCreateParams_) : menuControlCreateParams(menuControlCreateParams_)
258 {
259 }
260 public MenuBarCreateParams& Defaults()
261 {
262 return *this;
263 }
264 public MenuControlCreateParams& menuControlCreateParams;
265 }
266
267 public class MenuBar : MenuControl
268 {
269 private enum Flags : sbyte
270 {
271 none, open = 1 << 0, menuChanged = 1 << 1, menuBoxAdded = 1 << 2, menuInvalidated = 1 << 3, menuKeyDisabled = 1 << 4
272 }
273
274 internal Color ShadowColor()
275 {
276 Color shadowColor = GetSystemColor(SystemColor.COLOR_BTNSHADOW);
277 shadowColor.alpha = 196u;
278 return shadowColor;
279 }
280
281 public MenuBar(const Font& font_) : base(font_, "System.Windows.MenuBar", DefaultWindowClassStyle(), DefaultChildWindowStyle(),
282 DefaultExtendedWindowStyle(), DefaultMenuBackgroundColor(), "menuBar", Point(), Size(0, initialMenuBarHeight), Dock.top, Anchors.none),
283 flags(Flags.none), children(this), latestOpenedMenuItem(null), selectedMenuItem(null), latestMouseDownMenuItem(null)
284 {
285 SetMenuChanged();
286 }
287 public MenuBar() : this(Font(FontFamily("Segoe UI"), 9.000000f))
288 {
289 }
290 public MenuBar(MenuBarCreateParams& createParams) :
291 base(createParams.menuControlCreateParams),
292 flags(Flags.none), children(this), latestOpenedMenuItem(null), selectedMenuItem(null), latestMouseDownMenuItem(null)
293 {
294 SetMenuChanged();
295 }
296 [nodiscard]
297 public Result<bool> CloseMenu()
298 {
299 if (!IsOpen()) return Result<bool>(false);
300 auto result = SetClosed();
301 if (result.Error()) return result;
302 SetLatestOpenedMenuItem(null);
303 Component* child = children.FirstChild();
304 while (child != null)
305 {
306 if (child is MenuItem*)
307 {
308 MenuItem* menuItem = cast<MenuItem*>(child);
309 result = menuItem->SetState(MenuItem.State.closed, this);
310 if (result.Error()) return result;
311 }
312 child = child->NextSibling();
313 }
314 return InvalidateMenu();
315 }
316 [nodiscard]
317 public Result<bool> AddMenuItem(MenuItem* menuItem)
318 {
319 auto result = children.AddChild(menuItem);
320 if (result.Error())
321 {
322 return Result<bool>(ErrorId(result.GetErrorId()));
323 }
324 SetMenuChanged();
325 return Result<bool>(true);
326 }
327 [nodiscard]
328 public override Result<bool> PrintWindowTree(int level)
329 {
330 LogView* log = Application.GetLogView();
331 if (log != null)
332 {
333 auto handleResult = ToHexString(cast<ulong>(Handle()));
334 if (handleResult.Error())
335 {
336 return Result<bool>(ErrorId(handleResult.GetErrorId()));
337 }
338 const string& handleStr = handleResult.Value();
339 auto parentTextResult = ParentText();
340 if (parentTextResult.Error())
341 {
342 return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
343 }
344 const string& parentText = parentTextResult.Value();
345 auto result = log->WriteLine(string(' ', level) + "MenuBar." + Text() + ".handle=" + handleStr + " " + parentText + "[" +
346 Rect(Point(), GetSize()).ToString() + "]");
347 if (result.Error()) return result;
348 }
349 return Result<bool>(true);
350 }
351 [nodiscard]
352 protected override Result<bool> OnPaint(PaintEventArgs& args)
353 {
354 if (Debug.Paint())
355 {
356 auto locResult = Location();
357 if (locResult.Error())
358 {
359 return Result<bool>(ErrorId(locResult.GetErrorId()));
360 }
361 else
362 {
363 Point loc = locResult.Value();
364 Rect r(loc, GetSize());
365 LogView* log = Application.GetLogView();
366 if (log != null)
367 {
368 auto result = log->WriteLine("MenuBar.OnPaint: " + r.ToString());
369 if (result.Error()) return result;
370 }
371 }
372 }
373 if (!MenuBoxAdded())
374 {
375 SetMenuBoxAdded();
376 auto result = AddMenuBox();
377 if (result.Error())
378 {
379 return Result<bool>(ErrorId(result.GetErrorId()));
380 }
381 }
382 auto clearResult = args.graphics.Clear(BackgroundColor());
383 if (clearResult.Error())
384 {
385 return Result<bool>(ErrorId(clearResult.GetErrorId()));
386 }
387 if (MenuChanged())
388 {
389 ResetMenuChanged();
390 CollectShortcuts();
391 Size size = GetSize();
392 size.h = cast<int>(GetFont().GetHeight(args.graphics));
393 size.h = size.h + DefaultPadding().Vertical();
394 auto result = SetSize(size);
395 if (result.Error()) return result;
396 auto locateResult = LocateMenuItems(args.graphics, size);
397 if (locateResult.Error())
398 {
399 return Result<bool>(ErrorId(locateResult.GetErrorId()));
400 }
401 }
402 auto locResult = Location();
403 if (locResult.Error())
404 {
405 return Result<bool>(ErrorId(locResult.GetErrorId()));
406 }
407 Point loc = locResult.Value();
408 auto drawResult = DrawMenuItems(args, false, loc);
409 if (drawResult.Error())
410 {
411 return Result<bool>(ErrorId(drawResult.GetErrorId()));
412 }
413 auto paintResult = base->OnPaint(args);
414 if (paintResult.Error())
415 {
416 return Result<bool>(ErrorId(paintResult.GetErrorId()));
417 }
418 return Result<bool>(true);
419 }
420 private Result<bool> LocateMenuItems(Graphics& graphics, const Size& size)
421 {
422 auto locResult = Location();
423 if (locResult.Error())
424 {
425 return Result<bool>(ErrorId(locResult.GetErrorId()));
426 }
427 Point loc = locResult.Value();
428 Rect itemRect(loc, Size(0, size.h));
429 Padding padding = DefaultPadding();
430 PointF origin(0, 0);
431 Component* child = children.FirstChild();
432 while (child != null)
433 {
434 if (child is MenuItem*)
435 {
436 MenuItem* menuItem = cast<MenuItem*>(child);
437 Padding menuItemPadding = menuItem->DefaultPadding();
438 auto measureResult = graphics.MeasureStringRectF(menuItem->Text(), GetFont(), origin, Format());
439 if (measureResult.Error())
440 {
441 return Result<bool>(ErrorId(measureResult.GetErrorId()));
442 }
443 RectF r = Rvalue(measureResult.Value());
444 int w = cast<int>(r.size.w) + padding.Horizontal() + menuItemPadding.Horizontal();
445 itemRect.size.w = w;
446 menuItem->SetLocation(itemRect.location);
447 menuItem->SetSize(itemRect.size);
448 menuItem->CalculateChildRect(graphics, GetFont(), Format(), Point(itemRect.location.x, itemRect.location.y + itemRect.size.h));
449 itemRect.location.x = itemRect.location.x + w;
450 }
451 child = child->NextSibling();
452 }
453 return Result<bool>(true);
454 }
455 private Control* ParentControl() const
456 {
457 ComponentContainer* container = GetContainer();
458 if (container != null)
459 {
460 Component* parent = container->Parent();
461 if (parent != null && (parent is Control*))
462 {
463 return cast<Control*>(parent);
464 }
465 }
466 return null;
467 }
468 [nodiscard]
469 private Result<bool> InvalidateParentRect(const Rect& parentRect)
470 {
471 Control* parentControl = ParentControl();
472 if (parentControl != null)
473 {
474 auto result = parentControl->Invalidate(parentRect.ToWinRect());
475 if (result.Error()) return result;
476 }
477 return Result<bool>(true);
478 }
479 internal Result<bool> DrawMenuItems(PaintEventArgs& args, bool drawSubItems, const Point& origin)
480 {
481 Padding padding = DefaultPadding();
482 Size size = GetSize();
483 Component* child = children.FirstChild();
484 while (child != null)
485 {
486 if (child is MenuItem*)
487 {
488 MenuItem* menuItem = cast<MenuItem*>(child);
489 auto drawResult = menuItem->Draw(
490 args.graphics, padding, TextBrush(), DisabledTextBrush(), BackgroundBrush(), MouseOverBrush(), MenuOpenBrush(), ShadowBrush(),
491 BlackBrush(), BlackPen(), DarkPen(), GetFont(), Format(), MenuOpenColor(), this, drawSubItems, origin);
492 if (drawResult.Error())
493 {
494 return Result<bool>(ErrorId(drawResult.GetErrorId()));
495 }
496 }
497 child = child->NextSibling();
498 }
499 return Result<bool>(true);
500 }
501 [nodiscard]
502 internal Result<bool> DoKeyDown(KeyEventArgs& args)
503 {
504 return OnKeyDown(args);
505 }
506 [nodiscard]
507 protected override Result<bool> OnKeyDown(KeyEventArgs& args)
508 {
509 auto result = base->OnKeyDown(args);
510 if (result.Error()) return result;
511 if (!args.handled)
512 {
513 auto it = shortcuts.Find(args.key);
514 if (it != shortcuts.End())
515 {
516 MenuItem* menuItem = it->second;
517 if (menuItem->IsEnabled())
518 {
519 result = menuItem->DoClick();
520 if (result.Error()) return result;
521 args.handled = true;
522 }
523 }
524 }
525 return Result<bool>(true);
526 }
527 [nodiscard]
528 internal Result<bool> MouseEnterInternal()
529 {
530 EnterLeaveEventArgs args;
531 auto result = OnMouseEnter(args);
532 if (result.Error()) return result;
533 if (args.errorId != 0)
534 {
535 return Result<bool>(ErrorId(args.errorId));
536 }
537 return Result<bool>(true);
538 }
539 [nodiscard]
540 protected override Result<bool> OnMouseEnter(EnterLeaveEventArgs& args)
541 {
542 return base->OnMouseEnter(args);
543 }
544 [nodiscard]
545 internal Result<bool> MouseMoveInternal(MouseEventArgs& args)
546 {
547 return OnMouseMove(args);
548 }
549 [nodiscard]
550 protected override Result<bool> OnMouseMove(MouseEventArgs& args)
551 {
552 ResetMenuInvalidated();
553 auto result = base->OnMouseMove(args);
554 if (result.Error()) return result;
555 bool handled = false;
556 Component* child = children.FirstChild();
557 while (child != null)
558 {
559 if (child is MenuItem*)
560 {
561 MenuItem* menuItem = cast<MenuItem*>(child);
562 if (!menuItem->Contains(args.location))
563 {
564 result = menuItem->ResetSelected(this);
565 if (result.Error()) return result;
566 if (menuItem->MouseInClient())
567 {
568 menuItem->ResetMouseInClient();
569 result = menuItem->DoMouseLeave(this);
570 if (result.Error()) return result;
571 }
572 }
573 }
574 child = child->NextSibling();
575 }
576 child = children.FirstChild();
577 while (child != null)
578 {
579 if (child is MenuItem*)
580 {
581 MenuItem* menuItem = cast<MenuItem*>(child);
582 if (menuItem->Contains(args.location))
583 {
584 auto result = menuItem->SetSelected(this);
585 if (result.Error()) return result;
586 if (!menuItem->MouseInClient())
587 {
588 menuItem->SetMouseInClient();
589 result = menuItem->DoMouseEnter(IsOpen(), this);
590 if (result.Error()) return result;
591 }
592 else
593 {
594 result = menuItem->DoMouseMove(args);
595 if (result.Error()) return result;
596 }
597 handled = true;
598 }
599 }
600 child = child->NextSibling();
601 }
602 if (!handled)
603 {
604 if (IsOpen())
605 {
606 if (latestOpenedMenuItem != null)
607 {
608 Component* child = children.FirstChild();
609 while (child != null && !handled)
610 {
611 if (child is MenuItem*)
612 {
613 MenuItem* menuItem = cast<MenuItem*>(child);
614 if (menuItem->IsSameOrParentOf(latestOpenedMenuItem))
615 {
616 auto result = menuItem->DispatchMouseMove(args, handled, this);
617 if (result.Error()) return result;
618 }
619 }
620 child = child->NextSibling();
621 }
622 }
623 }
624 }
625 if (MenuInvalidated())
626 {
627 auto result = InvalidateMenu();
628 if (result.Error()) return result;
629 }
630 return Result<bool>(true);
631 }
632 [nodiscard]
633 internal Result<bool> MouseLeaveInternal()
634 {
635 EnterLeaveEventArgs args;
636 auto result = OnMouseLeave(args);
637 if (result.Error()) return result;
638 if (args.errorId != 0)
639 {
640 return Result<bool>(ErrorId(args.errorId));
641 }
642 return Result<bool>(true);
643 }
644 [nodiscard]
645 protected override Result<bool> OnMouseLeave(EnterLeaveEventArgs& args)
646 {
647 ResetMenuInvalidated();
648 auto result = base->OnMouseLeave(args);
649 if (result.Error()) return result;
650 Component* child = children.FirstChild();
651 while (child != null)
652 {
653 if (child is MenuItem*)
654 {
655 MenuItem* menuItem = cast<MenuItem*>(child);
656 if (menuItem->MouseInClient() || menuItem->Children().IsEmpty())
657 {
658 result = menuItem->ResetSelected(this);
659 if (result.Error()) return result;
660 menuItem->ResetMouseInClient();
661 Point mousePos;
662 WinGetMessagePos(mousePos.x, mousePos.y);
663 auto clientMousePosResult = ScreenToClient(mousePos);
664 if (clientMousePosResult.Error())
665 {
666 return Result<bool>(ErrorId(clientMousePosResult.GetErrorId()));
667 }
668 else
669 {
670 mousePos = clientMousePosResult.Value();
671 if (menuItem != latestOpenedMenuItem || !menuItem->UnionRectContains(mousePos))
672 {
673 auto result = menuItem->DoMouseLeave(this);
674 if (result.Error()) return result;
675 }
676 }
677 }
678 else
679 {
680 result = menuItem->ResetSelected(this);
681 if (result.Error()) return result;
682 menuItem->ResetMouseInClient();
683 }
684 }
685 child = child->NextSibling();
686 }
687 if (MenuInvalidated())
688 {
689 auto result = InvalidateMenu();
690 if (result.Error()) return result;
691 }
692 return Result<bool>(true);
693 }
694 [nodiscard]
695 internal override Result<bool> InvalidateMenu()
696 {
697 auto result = Invalidate();
698 if (result.Error()) return result;
699 Rect menuRect;
700 Component* child = children.FirstChild();
701 while (child != null)
702 {
703 if (child is MenuItem*)
704 {
705 MenuItem* menuItem = cast<MenuItem*>(child);
706 if (menuItem->GetState() == MenuItem.State.open)
707 {
708 menuItem->GetOpenRect(menuRect);
709 }
710 }
711 child = child->NextSibling();
712 }
713 if (IsOpen())
714 {
715 if (menuBox != null)
716 {
717 menuBox->SetPaintThisMenuBox(true);
718 auto locResult = Location();
719 if (locResult.Error())
720 {
721 return Result<bool>(ErrorId(locResult.GetErrorId()));
722 }
723 Point loc = locResult.Value();
724 auto result = menuBox->SetLocation(Point(loc.x + menuRect.location.x, loc.y + menuRect.location.y));
725 if (result.Error()) return result;
726 result = menuBox->SetSize(menuRect.size);
727 if (result.Error()) return result;
728 Control* parentControl = ParentControl();
729 Control* topControl = null;
730 if (parentControl != null)
731 {
732 topControl = parentControl->TopControl();
733 }
734 result = menuBox->BringToFront();
735 if (result.Error()) return result;
736 result = menuBox->Show();
737 if (result.Error()) return result;
738 result = menuBox->Invalidate();
739 if (result.Error()) return result;
740 menuBox->Update();
741 if (topControl != null)
742 {
743 result = topControl->BringToFront();
744 if (result.Error()) return result;
745 }
746 }
747 }
748 else
749 {
750 if (menuBox != null)
751 {
752 menuBox->SetPaintThisMenuBox(false);
753 auto result = menuBox->Hide();
754 if (result.Error()) return result;
755 }
756 auto result = InvalidateParentRect(menuRect);
757 if (result.Error()) return result;
758 Control* parentControl = ParentControl();
759 if (parentControl != null)
760 {
761 parentControl->Update();
762 }
763 }
764 return Result<bool>(true);
765 }
766 private void CollectShortcuts()
767 {
768 shortcuts.Clear();
769 Component* child = children.FirstChild();
770 while (child != null)
771 {
772 if (child is MenuItem*)
773 {
774 MenuItem* menuItem = cast<MenuItem*>(child);
775 menuItem->CollectShortcuts(shortcuts);
776 }
777 child = child->NextSibling();
778 }
779 }
780 internal Result<bool> HandleAccessKey(wchar accessKey, Keys keyCode, bool& wantsKeys)
781 {
782 ResetMenuInvalidated();
783 LogView* logView = Application.GetLogView();
784 if (logView != null)
785 {
786 auto accessKeyHexResult = ToHexString(cast<ushort>(accessKey));
787 if (accessKeyHexResult.Error())
788 {
789 return Result<bool>(ErrorId(accessKeyHexResult.GetErrorId()));
790 }
791 auto accessKeyStrResult = ToString(accessKey);
792 if (accessKeyStrResult.Error())
793 {
794 return Result<bool>(ErrorId(accessKeyStrResult.GetErrorId()));
795 }
796 string s = "MENUBAR: access key = \'" + accessKeyStrResult.Value() + "\' " + accessKeyHexResult.Value() + ", key code = " +
797 KeyCodeStrings.Instance().GetKeyCodeString(keyCode);
798 auto result = logView->WriteLine(s);
799 if (result.Error()) return result;
800 }
801 if (accessKey == '\0' && keyCode == Keys.none)
802 {
803 if (!MenuKeyDisabled())
804 {
805 if (selectedMenuItem == null)
806 {
807 MenuItem* firstMenuItem = GetFirstMenuItem();
808 if (firstMenuItem != null)
809 {
810 SetOpen();
811 SetSelectedMenuItem(firstMenuItem);
812 wantsKeys = true;
813 auto result = InvalidateMenu();
814 if (result.Error()) return result;
815 return Result<bool>(true);
816 }
817 }
818 else
819 {
820 auto result = SetClosed();
821 if (result.Error()) return result;
822 MenuItem* openedMenuItem = GetOpenedMenuItem();
823 if (openedMenuItem != null)
824 {
825 result = openedMenuItem->SetState(MenuItem.State.closed, this);
826 if (result.Error()) return result;
827 }
828 result = selectedMenuItem->ResetSelected(this);
829 if (result.Error()) return result;
830 SetSelectedMenuItem(null);
831 wantsKeys = false;
832 result = InvalidateMenu();
833 if (result.Error()) return result;
834 return Result<bool>(true);
835 }
836 }
837 }
838 else if (accessKey != '\0' && keyCode == Keys.none)
839 {
840 MenuItem* menuItem = GetMenuItemByAccessKey(accessKey);
841 if (menuItem != null)
842 {
843 MenuItem* firstMenuItem = menuItem->GetFirstMenuItem();
844 if (firstMenuItem != null)
845 {
846 if (selectedMenuItem != null)
847 {
848 MenuItem* openedMenuItem = GetOpenedMenuItem();
849 if (openedMenuItem != null)
850 {
851 auto result = openedMenuItem->SetState(MenuItem.State.closed, this);
852 if (result.Error()) return result;
853 }
854 }
855 SetOpen();
856 auto result = menuItem->SetState(MenuItem.State.open, this);
857 if (result.Error()) return result;
858 SetSelectedMenuItem(firstMenuItem);
859 wantsKeys = true;
860 result = InvalidateMenu();
861 if (result.Error()) return result;
862 return Result<bool>(true);
863 }
864 }
865 }
866 else if (accessKey == '\0' && keyCode != Keys.none)
867 {
868 if (keyCode == Keys.menu)
869 {
870 wantsKeys = true;
871 return Result<bool>(true);
872 }
873 else
874 {
875 if (selectedMenuItem != null)
876 {
877 MenuItem* parentItem = selectedMenuItem->GetParentMenuItem();
878 Result<bool> handled = selectedMenuItem->HandleKey(keyCode, wantsKeys, parentItem, this);
879 if (MenuInvalidated())
880 {
881 auto result = InvalidateMenu();
882 if (result.Error()) return result;
883 }
884 return Result<bool>(handled.Value());
885 }
886 }
887 }
888 if (MenuInvalidated())
889 {
890 auto result = InvalidateMenu();
891 if (result.Error()) return result;
892 }
893 wantsKeys = false;
894 return Result<bool>(false);
895 }
896 [nodiscard]
897 internal Result<bool> MouseDownInternal(MouseEventArgs& args)
898 {
899 return OnMouseDown(args);
900 }
901 [nodiscard]
902 protected override Result<bool> OnMouseDown(MouseEventArgs& args)
903 {
904 auto result = base->OnMouseDown(args);
905 if (result.Error()) return result;
906 SetLatestMouseDownMenuItem(null);
907 bool handled = false;
908 Component* child = children.FirstChild();
909 while (child != null)
910 {
911 if (child is MenuItem*)
912 {
913 MenuItem* menuItem = cast<MenuItem*>(child);
914 result = menuItem->DoMouseDown(args, handled, this);
915 if (result.Error()) return result;
916 if (handled)
917 {
918 auto result = InvalidateMenu();
919 if (result.Error()) return result;
920 return Result<bool>(true);
921 }
922 }
923 child = child->NextSibling();
924 }
925 if (IsOpen())
926 {
927 auto result = SetClosed();
928 if (result.Error()) return result;
929 SetLatestOpenedMenuItem(null);
930 Component* child = children.FirstChild();
931 while (child != null)
932 {
933 if (child is MenuItem*)
934 {
935 MenuItem* menuItem = cast<MenuItem*>(child);
936 result = menuItem->SetState(MenuItem.State.closed, this);
937 if (result.Error()) return result;
938 }
939 child = child->NextSibling();
940 }
941 result = InvalidateMenu();
942 if (result.Error()) return result;
943 }
944 return Result<bool>(true);
945 }
946 [nodiscard]
947 internal Result<bool> MouseUpInternal(MouseEventArgs& args)
948 {
949 return OnMouseUp(args);
950 }
951 [nodiscard]
952 protected override Result<bool> OnMouseUp(MouseEventArgs& args)
953 {
954 auto result = base->OnMouseUp(args);
955 if (result.Error()) return result;
956 bool handled = false;
957 Component* child = children.FirstChild();
958 while (child != null)
959 {
960 if (child is MenuItem*)
961 {
962 MenuItem* menuItem = cast<MenuItem*>(child);
963 if (menuItem->IsEnabled())
964 {
965 auto result = menuItem->DoMouseUp(args, handled, this);
966 if (result.Error()) return result;
967 if (handled)
968 {
969 result = InvalidateMenu();
970 if (result.Error()) return result;
971 return Result<bool>(true);
972 }
973 }
974 }
975 child = child->NextSibling();
976 }
977 return Result<bool>(true);
978 }
979 internal override void SetLatestOpenedMenuItem(MenuItem* menuItem)
980 {
981 latestOpenedMenuItem = menuItem;
982 }
983 internal override MenuItem* LatestOpenedMenuItem() const
984 {
985 return latestOpenedMenuItem;
986 }
987 internal override MenuItem* GetOpenedMenuItem() const
988 {
989 Component* child = children.FirstChild();
990 while (child != null)
991 {
992 if (child is MenuItem*)
993 {
994 MenuItem* childItem = cast<MenuItem*>(child);
995 if (childItem->GetState() == MenuItem.State.open)
996 {
997 return childItem;
998 }
999 }
1000 child = child->NextSibling();
1001 }
1002 return null;
1003 }
1004 internal override MenuItem* GetFirstMenuItem() const
1005 {
1006 Component* child = children.FirstChild();
1007 while (child != null)
1008 {
1009 if (child is MenuItem*)
1010 {
1011 MenuItem* menuItem = cast<MenuItem*>(child);
1012 return menuItem;
1013 }
1014 child = child->NextSibling();
1015 }
1016 return null;
1017 }
1018 internal override MenuItem* GetLastMenuItem() const
1019 {
1020 Component* child = children.LastChild();
1021 while (child != null)
1022 {
1023 if (child is MenuItem*)
1024 {
1025 MenuItem* menuItem = cast<MenuItem*>(child);
1026 return menuItem;
1027 }
1028 child = child->PrevSibling();
1029 }
1030 return null;
1031 }
1032 internal override MenuItem* GetMenuItemByAccessKey(wchar accessKey)
1033 {
1034 Component* child = children.FirstChild();
1035 while (child != null)
1036 {
1037 if (child is MenuItem*)
1038 {
1039 MenuItem* menuItem = cast<MenuItem*>(child);
1040 if (menuItem->AccessKey() == accessKey)
1041 {
1042 return menuItem;
1043 }
1044 }
1045 child = child->NextSibling();
1046 }
1047 return null;
1048 }
1049 internal override MenuItem* GetSelectedMenuItem() const
1050 {
1051 return selectedMenuItem;
1052 }
1053 internal override void SetSelectedMenuItem(MenuItem* selectedMenuItem_)
1054 {
1055 selectedMenuItem = selectedMenuItem_;
1056 }
1057 internal override MenuItem* GetLatestMouseDownMenuItem() const
1058 {
1059 return latestMouseDownMenuItem;
1060 }
1061 internal override void SetLatestMouseDownMenuItem(MenuItem* menuItem)
1062 {
1063 latestMouseDownMenuItem = menuItem;
1064 }
1065 public void DisableMenuKey()
1066 {
1067 flags = cast<Flags>(flags | Flags.menuKeyDisabled);
1068 }
1069 public inline bool MenuKeyDisabled() const
1070 {
1071 return (flags & Flags.menuKeyDisabled) != Flags.none;
1072 }
1073 private inline bool MenuChanged()
1074 {
1075 return (flags & Flags.menuChanged) != Flags.none;
1076 }
1077 internal override void SetMenuChanged()
1078 {
1079 flags = cast<Flags>(flags | Flags.menuChanged);
1080 }
1081 private inline void ResetMenuChanged()
1082 {
1083 flags = cast<Flags>(flags & ~Flags.menuChanged);
1084 }
1085 private inline bool MenuInvalidated() const
1086 {
1087 return (flags & Flags.menuInvalidated) != Flags.none;
1088 }
1089 internal override Result<bool> SetMenuInvalidated()
1090 {
1091 flags = cast<Flags>(flags | Flags.menuInvalidated);
1092 return Result<bool>(true);
1093 }
1094 private inline void ResetMenuInvalidated()
1095 {
1096 flags = cast<Flags>(flags & ~Flags.menuInvalidated);
1097 }
1098 internal override bool IsOpen() const
1099 {
1100 return (flags & Flags.open) != Flags.none;
1101 }
1102 public override void SetOpen()
1103 {
1104 flags = cast<Flags>(flags | Flags.open);
1105 }
1106 [nodiscard]
1107 public override Result<bool> SetClosed()
1108 {
1109 if (IsOpen())
1110 {
1111 flags = cast<Flags>(flags & ~Flags.open);
1112 auto result = InvalidateMenu();
1113 if (result.Error()) return result;
1114 }
1115 return Result<bool>(true);
1116 }
1117 private bool MenuBoxAdded() const
1118 {
1119 return (flags & Flags.menuBoxAdded) != Flags.none;
1120 }
1121 private void SetMenuBoxAdded()
1122 {
1123 flags = cast<Flags>(flags | Flags.menuBoxAdded);
1124 }
1125 private Result<bool> AddMenuBox()
1126 {
1127 Control* parentControl = ParentControl();
1128 if (parentControl != null && parentControl is ContainerControl*)
1129 {
1130 menuBox = new MenuBox(GetFont(), this, null);
1131 if (menuBox->Error())
1132 {
1133 return Result<bool>(ErrorId(menuBox->GetErrorId()));
1134 }
1135 ContainerControl* containerControl = cast<ContainerControl*>(parentControl);
1136 auto result = containerControl->InsertChildAfter(menuBox, this);
1137 if (result.Error())
1138 {
1139 return Result<bool>(ErrorId(result.GetErrorId()));
1140 }
1141 }
1142 return Result<bool>(true);
1143 }
1144 private Flags flags;
1145 private ComponentContainer children;
1146 private MenuItem* latestOpenedMenuItem;
1147 private MenuItem* selectedMenuItem;
1148 private MenuItem* latestMouseDownMenuItem;
1149 private HashMap<Keys, MenuItem*> shortcuts;
1150 private MenuBox* menuBox;
1151 }
1152
1153 public ControlCreateParams& MenuBoxControlCreateParams(ControlCreateParams& controlCreateParams)
1154 {
1155 return controlCreateParams.SetWindowStyle(HiddenChildWindowStyle()).
1156 SetWindowClassBackgroundColor(SystemColor.COLOR_MENU).SetBackgroundColor(DefaultMenuBackgroundColor()).
1157 SetWindowClassName("System.Windows.MenuBox");
1158 }
1159
1160 public class MenuBoxCreateParams
1161 {
1162 public MenuBoxCreateParams(MenuControlCreateParams& menuControlCreateParams_) : menuControlCreateParams(menuControlCreateParams_)
1163 {
1164 }
1165 public MenuBoxCreateParams& Defaults()
1166 {
1167 return *this;
1168 }
1169 public MenuControlCreateParams& menuControlCreateParams;
1170 }
1171
1172 public class MenuBox : MenuControl
1173 {
1174 public MenuBox(const Font& font, MenuBar* menuBar_, MenuItem* rootItem_) :
1175 base(font, "System.Windows.MenuBox", DefaultWindowClassStyle(), HiddenChildWindowStyle(), DefaultExtendedWindowStyle(),
1176 Color.White(), "menuBox", Point(), Size(), Dock.none, Anchors.none), menuBar(menuBar_), rootItem(rootItem_), paintThisMenuBox(false)
1177 {
1178 if (rootItem != null && rootItem->Error())
1179 {
1180 SetErrorId(rootItem->GetErrorId());
1181 return;
1182 }
1183 }
1184 public MenuBox(MenuBoxCreateParams& createParams, MenuBar* menuBar_, MenuItem* rootItem_) :
1185 base(createParams.menuControlCreateParams), menuBar(menuBar_), rootItem(rootItem_), paintThisMenuBox(false)
1186 {
1187 if (rootItem != null && rootItem->Error())
1188 {
1189 SetErrorId(rootItem->GetErrorId());
1190 return;
1191 }
1192 }
1193 public void SetPaintThisMenuBox(bool paintThisMenuBox_)
1194 {
1195 paintThisMenuBox = paintThisMenuBox_;
1196 }
1197 public inline bool PaintThisMenuBox() const
1198 {
1199 return paintThisMenuBox;
1200 }
1201 [nodiscard]
1202 public override Result<bool> PrintWindowTree(int level)
1203 {
1204 LogView* log = Application.GetLogView();
1205 if (log != null)
1206 {
1207 auto handleResult = ToHexString(cast<ulong>(Handle()));
1208 if (handleResult.Error())
1209 {
1210 return Result<bool>(ErrorId(handleResult.GetErrorId()));
1211 }
1212 const string& handleStr = handleResult.Value();
1213 auto parentTextResult = ParentText();
1214 if (parentTextResult.Error())
1215 {
1216 return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
1217 }
1218 const string& parentText = parentTextResult.Value();
1219 auto result = log->WriteLine(string(' ', level) + "MenuBox." + Text() + ".handle=" + handleStr + " " + parentText +
1220 "[" + Rect(Point(), GetSize()).ToString() + "]");
1221 if (result.Error()) return result;
1222 }
1223 return Result<bool>(true);
1224 }
1225 protected virtual Result<Point> GetBoxLocation() const
1226 {
1227 return Location();
1228 }
1229 [nodiscard]
1230 protected override Result<bool> OnPaint(PaintEventArgs& args)
1231 {
1232 if (Debug.Paint())
1233 {
1234 Rect r(Point(), GetSize());
1235 LogView* log = Application.GetLogView();
1236 if (log != null)
1237 {
1238 auto result = log->WriteLine("MenuBox.OnPaint: " + r.ToString());
1239 if (result.Error()) return result;
1240 }
1241 }
1242 auto locResult = Location();
1243 if (locResult.Error())
1244 {
1245 return Result<bool>(ErrorId(locResult.GetErrorId()));
1246 }
1247 Point loc = locResult.Value();
1248 if (!paintMenu)
1249 {
1250 if (paintThisMenuBox)
1251 {
1252 Control* parentControl = ParentControl();
1253 if (parentControl != null)
1254 {
1255 Bitmap menuBoxBitmap(args.clipRect.size.w, args.clipRect.size.h, args.graphics);
1256 auto menuBoxBitmapResult = Graphics.FromImage(menuBoxBitmap);
1257 if (menuBoxBitmapResult.Error())
1258 {
1259 return Result<bool>(ErrorId(menuBoxBitmapResult.GetErrorId()));
1260 }
1261 Graphics& menuBoxGraphics = menuBoxBitmapResult.Value();
1262 Rect menuBoxClipRect(Point(0, 0), Size(args.clipRect.size.w, args.clipRect.size.h));
1263 auto setClipResult = menuBoxGraphics.SetClip(menuBoxClipRect);
1264 if (setClipResult.Error())
1265 {
1266 return Result<bool>(ErrorId(setClipResult.GetErrorId()));
1267 }
1268 auto translateResult = menuBoxGraphics.TranslateTransform(-loc.x, -loc.y);
1269 if (translateResult.Error())
1270 {
1271 return Result<bool>(ErrorId(translateResult.GetErrorId()));
1272 }
1273 PaintEventArgs paintMenuBoxArgs(menuBoxGraphics, menuBoxClipRect);
1274 bool prevPaintMenu = paintMenu;
1275 paintMenu = true;
1276 bool skipMenuBar = this is ContextMenu*;
1277 auto paintResult = parentControl->PaintAll(paintMenuBoxArgs, skipMenuBar);
1278 if (paintResult.Error())
1279 {
1280 return Result<bool>(ErrorId(paintResult.GetErrorId()));
1281 }
1282 paintMenu = prevPaintMenu;
1283 auto drawImageResult = args.graphics.DrawImage(menuBoxBitmap, PointF(0, 0));
1284 if (drawImageResult.Error())
1285 {
1286 return Result<bool>(ErrorId(drawImageResult.GetErrorId()));
1287 }
1288 }
1289 }
1290 }
1291 else
1292 {
1293 if (menuBar != null)
1294 {
1295 auto drawResult = menuBar->DrawMenuItems(args, true, loc);
1296 if (drawResult.Error())
1297 {
1298 return Result<bool>(ErrorId(drawResult.GetErrorId()));
1299 }
1300 }
1301 else if (rootItem != null)
1302 {
1303 auto drawResult = rootItem->Draw(args.graphics, DefaultPadding(), TextBrush(), DisabledTextBrush(), BackgroundBrush(), MouseOverBrush(),
1304 MenuOpenBrush(), ShadowBrush(), BlackBrush(), BlackPen(),
1305 DarkPen(), GetFont(), Format(), MenuOpenColor(), this, true, Point());
1306 if (drawResult.Error())
1307 {
1308 return Result<bool>(ErrorId(drawResult.GetErrorId()));
1309 }
1310 }
1311 }
1312 return Result<bool>(true);
1313 }
1314 private Control* ParentControl() const
1315 {
1316 ComponentContainer* container = GetContainer();
1317 if (container != null)
1318 {
1319 Component* parent = container->Parent();
1320 if (parent != null && (parent is Control*))
1321 {
1322 return cast<Control*>(parent);
1323 }
1324 }
1325 return null;
1326 }
1327 [nodiscard]
1328 protected override Result<bool> OnMouseEnter(EnterLeaveEventArgs& args)
1329 {
1330 auto result = base->OnMouseEnter(args);
1331 if (result.Error()) return result;
1332 if (menuBar != null)
1333 {
1334 result = menuBar->MouseEnterInternal();
1335 if (result.Error()) return result;
1336 }
1337 return Result<bool>(true);
1338 }
1339 [nodiscard]
1340 protected override Result<bool> OnMouseMove(MouseEventArgs& args)
1341 {
1342 auto result = base->OnMouseMove(args);
1343 if (result.Error()) return result;
1344 if (menuBar != null)
1345 {
1346 auto locResult = Location();
1347 if (locResult.Error())
1348 {
1349 return Result<bool>(ErrorId(locResult.GetErrorId()));
1350 }
1351 Point loc = locResult.Value();
1352 args.location.x = args.location.x + loc.x;
1353 args.location.y = args.location.y + loc.y;
1354 auto result = menuBar->MouseMoveInternal(args);
1355 if (result.Error()) return result;
1356 }
1357 else if (rootItem != null)
1358 {
1359 bool handled = false;
1360 auto result = rootItem->DispatchMouseMove(args, handled, this);
1361 if (result.Error()) return result;
1362 }
1363 return Result<bool>(true);
1364 }
1365 [nodiscard]
1366 protected override Result<bool> OnMouseLeave(EnterLeaveEventArgs& args)
1367 {
1368 auto result = base->OnMouseLeave(args);
1369 if (result.Error()) return result;
1370 if (menuBar != null)
1371 {
1372 result = menuBar->MouseLeaveInternal();
1373 if (result.Error()) return result;
1374 }
1375 else if (rootItem != null)
1376 {
1377 result = rootItem->LeaveChildren(this);
1378 if (result.Error()) return result;
1379 }
1380 return Result<bool>(true);
1381 }
1382 [nodiscard]
1383 protected override Result<bool> OnMouseDown(MouseEventArgs& args)
1384 {
1385 auto result = base->OnMouseDown(args);
1386 if (result.Error()) return result;
1387 if (menuBar != null)
1388 {
1389 auto locResult = Location();
1390 if (locResult.Error())
1391 {
1392 return Result<bool>(ErrorId(locResult.GetErrorId()));
1393 }
1394 Point loc = locResult.Value();
1395 args.location.x = args.location.x + loc.x;
1396 args.location.y = args.location.y + loc.y;
1397 result = menuBar->MouseDownInternal(args);
1398 if (result.Error()) return result;
1399 }
1400 else if (rootItem != null)
1401 {
1402 SetLatestMouseDownMenuItem(null);
1403 bool handled = false;
1404 auto result = rootItem->DoMouseDown(args, handled, this);
1405 if (result.Error()) return result;
1406 if (handled)
1407 {
1408 result = Invalidate();
1409 if (result.Error()) return result;
1410 }
1411 }
1412 return Result<bool>(true);
1413 }
1414 [nodiscard]
1415 protected override Result<bool> OnMouseUp(MouseEventArgs& args)
1416 {
1417 auto result = base->OnMouseUp(args);
1418 if (result.Error()) return result;
1419 if (menuBar != null)
1420 {
1421 auto locResult = Location();
1422 if (locResult.Error())
1423 {
1424 return Result<bool>(ErrorId(locResult.GetErrorId()));
1425 }
1426 Point loc = locResult.Value();
1427 args.location.x = args.location.x + loc.x;
1428 args.location.y = args.location.y + loc.y;
1429 result = menuBar->MouseUpInternal(args);
1430 if (result.Error()) return result;
1431 }
1432 else if (rootItem != null)
1433 {
1434 bool handled = false;
1435 auto result = rootItem->DoMouseUp(args, handled, this);
1436 if (result.Error()) return result;
1437 if (handled)
1438 {
1439 result = Invalidate();
1440 if (result.Error()) return result;
1441 }
1442 }
1443 return Result<bool>(true);
1444 }
1445 protected inline MenuItem* GetRootItem() const
1446 {
1447 return rootItem;
1448 }
1449 private MenuBar* menuBar;
1450 private MenuItem* rootItem;
1451 private bool paintThisMenuBox;
1452 private bool paintMenu;
1453 }
1454
1455 public abstract class ClickAction
1456 {
1457 public ClickAction()
1458 {
1459 clickEventHandler = Click;
1460 }
1461 public virtual default ~ClickAction();
1462 public void AddHandlerTo(MenuItem& menuItem)
1463 {
1464 menuItem.ClickEvent().AddHandler(clickEventHandler);
1465 }
1466 private void Click(ClickEventArgs& args)
1467 {
1468 auto result = Execute();
1469 if (result.Error())
1470 {
1471 args.errorId = result.GetErrorId();
1472 }
1473 }
1474 [nodiscard]
1475 protected abstract Result<bool> Execute();
1476 private ClickEventHandler clickEventHandler;
1477 }
1478
1479 public class ClickActions
1480 {
1481 public void Add(ClickAction* action)
1482 {
1483 clickActions.Add(UniquePtr<ClickAction>(action));
1484 }
1485 private List<UniquePtr<ClickAction>> clickActions;
1486 }
1487
1488 public ControlCreateParams& ContextMenuControlCreateParams(ControlCreateParams& controlCreateParams)
1489 {
1490 return controlCreateParams.SetWindowStyle(HiddenChildWindowStyle()).
1491 SetWindowClassBackgroundColor(SystemColor.COLOR_MENU).SetBackgroundColor(DefaultMenuBackgroundColor()).
1492 SetWindowClassName("System.Windows.ContextMenu");
1493 }
1494
1495 public MenuBoxCreateParams& ContextMenuMenuBoxCreateParams(MenuBoxCreateParams& menuBoxCreateParams)
1496 {
1497 return menuBoxCreateParams;
1498 }
1499
1500 public class ContextMenuCreateParams
1501 {
1502 public ContextMenuCreateParams(MenuBoxCreateParams& menuBoxCreateParams_) : menuBoxCreateParams(menuBoxCreateParams_)
1503 {
1504 }
1505 public ContextMenuCreateParams& Defaults()
1506 {
1507 return *this;
1508 }
1509 public MenuBoxCreateParams& menuBoxCreateParams;
1510 }
1511
1512 public class ContextMenu : MenuBox
1513 {
1514 public ContextMenu(const Font& font) :
1515 base(font, null, new MenuItem("root")),
1516 rootItemPtr(GetRootItem()), latestOpenedMenuItem(null), selectedMenuItem(null), latestMouseDownMenuItem(null)
1517 {
1518 }
1519 public ContextMenu() : this(Font(FontFamily("Segoe UI"), 9.000000f))
1520 {
1521 }
1522 public ContextMenu(ContextMenuCreateParams& createParams) :
1523 base(createParams.menuBoxCreateParams, null, new MenuItem("root")),
1524 rootItemPtr(GetRootItem()), latestOpenedMenuItem(null), selectedMenuItem(null), latestMouseDownMenuItem(null)
1525 {
1526 }
1527 [nodiscard]
1528 public Result<bool> AddMenuItem(MenuItemBase* menuItem)
1529 {
1530 return rootItemPtr->AddMenuItem(menuItem);
1531 }
1532 [nodiscard]
1533 public Result<bool> AddMenuItemAction(MenuItem* menuItem, ClickAction* action)
1534 {
1535 auto result = rootItemPtr->AddMenuItem(menuItem);
1536 if (result.Error()) return result;
1537 action->AddHandlerTo(*menuItem);
1538 clickActions.Add(action);
1539 return Result<bool>(true);
1540 }
1541 [nodiscard]
1542 public Result<bool> CalculateSize()
1543 {
1544 auto result = rootItemPtr->SetState(MenuItem.State.open, this);
1545 if (result.Error()) return result;
1546 auto windowHandleGraphicsResult = Graphics.FromWindowHandle(Handle());
1547 if (windowHandleGraphicsResult.Error())
1548 {
1549 return Result<bool>(ErrorId(windowHandleGraphicsResult.GetErrorId()));
1550 }
1551 Graphics& graphics = windowHandleGraphicsResult.Value();
1552 rootItemPtr->CalculateChildRect(graphics, GetFont(), Format(), Point(0, 0));
1553 Rect menuRect;
1554 rootItemPtr->GetOpenRect(menuRect);
1555 result = SetSize(menuRect.size);
1556 if (result.Error()) return result;
1557 return Result<bool>(true);
1558 }
1559 public bool HasMenuItems()
1560 {
1561 return !rootItemPtr->Children().IsEmpty();
1562 }
1563 protected override Result<Point> GetBoxLocation() const
1564 {
1565 return Result<Point>(Point(0, 0));
1566 }
1567 [nodiscard]
1568 protected override Result<bool> OnPaint(PaintEventArgs& args)
1569 {
1570 SetPaintThisMenuBox(true);
1571 return base->OnPaint(args);
1572 }
1573 [nodiscard]
1574 protected override Result<bool> OnVisibleChanged()
1575 {
1576 auto result = base->OnVisibleChanged();
1577 if (result.Error()) return result;
1578 if (!IsVisible())
1579 {
1580 result = rootItemPtr->SetState(MenuItem.State.closed, this);
1581 if (result.Error()) return result;
1582 SetPaintThisMenuBox(false);
1583 }
1584 return Result<bool>(true);
1585 }
1586 [nodiscard]
1587 internal override Result<bool> SetMenuInvalidated()
1588 {
1589 return Invalidate();
1590 }
1591 internal override bool IsOpen() const
1592 {
1593 return rootItemPtr->GetState() == MenuItem.State.open;
1594 }
1595 internal override MenuItem* GetSelectedMenuItem() const
1596 {
1597 return selectedMenuItem;
1598 }
1599 internal override void SetSelectedMenuItem(MenuItem* selectedMenuItem_)
1600 {
1601 selectedMenuItem = selectedMenuItem_;
1602 }
1603 internal override MenuItem* LatestOpenedMenuItem() const
1604 {
1605 return latestOpenedMenuItem;
1606 }
1607 internal override void SetLatestOpenedMenuItem(MenuItem* menuItem)
1608 {
1609 latestOpenedMenuItem = menuItem;
1610 }
1611 internal override MenuItem* GetLatestMouseDownMenuItem() const
1612 {
1613 return latestMouseDownMenuItem;
1614 }
1615 internal override void SetLatestMouseDownMenuItem(MenuItem* menuItem)
1616 {
1617 latestMouseDownMenuItem = menuItem;
1618 }
1619 internal override MenuItem* GetOpenedMenuItem() const
1620 {
1621 return rootItemPtr.Get();
1622 }
1623 private UniquePtr<MenuItem> rootItemPtr;
1624 private MenuItem* latestOpenedMenuItem;
1625 private MenuItem* selectedMenuItem;
1626 private MenuItem* latestMouseDownMenuItem;
1627 private ClickActions clickActions;
1628 }
1629
1630 public abstract class MenuItemBase : Component
1631 {
1632 public MenuItemBase() : location(), size()
1633 {
1634 }
1635 public abstract Result<bool> Draw(Graphics& graphics, const Padding& parentPadding, const Brush& textBrush, const Brush& disabledTextBrush,
1636 const Brush& backgroundBrush, const Brush& mouseOverBrush, const Brush& menuOpenBrush, const Brush& shadowBrush, const Brush& blackBrush,
1637 const Pen& blackPen, const Pen& darkPen, const Font& font, const StringFormat& format, const Color& menuOpenColor,
1638 MenuControl* menuControl, bool drawSubItems, const Point& origin);
1639 internal MenuItem* GetParentMenuItem() const
1640 {
1641 ComponentContainer* container = GetContainer();
1642 if (container != null)
1643 {
1644 Component* parent = container->Parent();
1645 if (parent != null)
1646 {
1647 if (parent is MenuItem*)
1648 {
1649 MenuItem* parentMenuItem = cast<MenuItem*>(parent);
1650 return parentMenuItem;
1651 }
1652 }
1653 }
1654 return null;
1655 }
1656 public virtual Padding DefaultPadding() const
1657 {
1658 return Padding(0, 0, 0, 0);
1659 }
1660 public int Level() const
1661 {
1662 MenuItem* parent = GetParentMenuItem();
1663 if (parent == null)
1664 {
1665 return 0;
1666 }
1667 else
1668 {
1669 return parent->Level() + 1;
1670 }
1671 }
1672 public const Point& Location() const
1673 {
1674 return location;
1675 }
1676 public void SetLocation(const Point& location_)
1677 {
1678 location = location_;
1679 }
1680 public const Size& GetSize() const
1681 {
1682 return size;
1683 }
1684 public void SetSize(const Size& size_)
1685 {
1686 size = size_;
1687 }
1688 public abstract Size MeasureItem(Graphics& graphics, const Font& font, const StringFormat& format, int& maxShortcutWidth, int& childIndicatorWidth);
1689 private Point location;
1690 private Size size;
1691 }
1692
1693 public class MenuItem : MenuItemBase
1694 {
1695 public enum State : sbyte
1696 {
1697 closed = 0, open = 1
1698 }
1699 private enum Flags : sbyte
1700 {
1701 none = 0, disabled = 1 << 0, selected = 1 << 1, mouseInClient = 1 << 2, lbuttonPressed = 1 << 3
1702 }
1703 public explicit MenuItem(const string& text_) :
1704 base(), text(text_), children(this), state(State.closed), childRect(), unionRect(), flags(Flags.none), accessKey('\0'), shortcut(Keys.none), shortcutFieldWidth(0)
1705 {
1706 SetAccessKey();
1707 }
1708 [nodiscard]
1709 public Result<bool> AddMenuItem(MenuItemBase* menuItem)
1710 {
1711 return children.AddChild(menuItem);
1712 }
1713 public MenuItem* GetFirstMenuItem() const
1714 {
1715 Component* child = children.FirstChild();
1716 while (child != null)
1717 {
1718 if (child is MenuItem*)
1719 {
1720 MenuItem* childMenuItem = cast<MenuItem*>(child);
1721 return childMenuItem;
1722 }
1723 child = child->NextSibling();
1724 }
1725 return null;
1726 }
1727 public MenuItem* GetLastMenuItem() const
1728 {
1729 Component* child = children.LastChild();
1730 while (child != null)
1731 {
1732 if (child is MenuItem*)
1733 {
1734 MenuItem* childMenuItem = cast<MenuItem*>(child);
1735 return childMenuItem;
1736 }
1737 child = child->PrevSibling();
1738 }
1739 return null;
1740 }
1741 public MenuItem* GetNextMenuItem() const
1742 {
1743 Component* next = NextSibling();
1744 while (next != null)
1745 {
1746 if (next is MenuItem*)
1747 {
1748 MenuItem* nextMenuItem = cast<MenuItem*>(next);
1749 return nextMenuItem;
1750 }
1751 next = next->NextSibling();
1752 }
1753 return null;
1754 }
1755 public MenuItem* GetPrevMenuItem() const
1756 {
1757 Component* prev = PrevSibling();
1758 while (prev != null)
1759 {
1760 if (prev is MenuItem*)
1761 {
1762 MenuItem* prevMenuItem = cast<MenuItem*>(prev);
1763 return prevMenuItem;
1764 }
1765 prev = prev->PrevSibling();
1766 }
1767 return null;
1768 }
1769 public MenuItem* GetParentMenuItem() const
1770 {
1771 ComponentContainer* container = GetContainer();
1772 if (container != null)
1773 {
1774 Component* parent = container->Parent();
1775 if (parent != null)
1776 {
1777 if (parent is MenuItem*)
1778 {
1779 MenuItem* parentItem = cast<MenuItem*>(parent);
1780 return parentItem;
1781 }
1782 }
1783 }
1784 return null;
1785 }
1786 private MenuControl* GetMenuControl() const
1787 {
1788 ComponentContainer* container = GetContainer();
1789 if (container != null)
1790 {
1791 Component* parent = container->Parent();
1792 if (parent != null)
1793 {
1794 if (parent is MenuItem*)
1795 {
1796 MenuItem* parentItem = cast<MenuItem*>(parent);
1797 return parentItem->GetMenuControl();
1798 }
1799 else if (parent is MenuControl*)
1800 {
1801 MenuControl* menuControl = cast<MenuControl*>(parent);
1802 return menuControl;
1803 }
1804 }
1805 }
1806 return null;
1807 }
1808 [nodiscard]
1809 internal Result<bool> HandleKey(Keys key, bool& wantsKeys, MenuItem* parentMenuItem, MenuControl* menuControl)
1810 {
1811 if (key >= Keys.a && key <= Keys.z || key >= Keys.d0 && key <= Keys.d9)
1812 {
1813 wchar accessKey = cast<wchar>(cast<int>(key));
1814 MenuItem* childItem = null;
1815 if (parentMenuItem != null)
1816 {
1817 childItem = parentMenuItem->GetChildItemByAccessKey(accessKey);
1818 }
1819 else
1820 {
1821 childItem = menuControl->GetMenuItemByAccessKey(accessKey);
1822 }
1823 if (childItem != null && childItem->IsEnabled())
1824 {
1825 auto result = childItem->Execute(parentMenuItem, wantsKeys, menuControl);
1826 if (result.Error()) return result;
1827 return Result<bool>(true);
1828 }
1829 else
1830 {
1831 wantsKeys = true;
1832 return Result<bool>(false);
1833 }
1834 }
1835 else
1836 {
1837 switch (key)
1838 {
1839 case Keys.enter:
1840 {
1841 if (IsEnabled())
1842 {
1843 auto result = Execute(parentMenuItem, wantsKeys, menuControl);
1844 if (result.Error()) return result;
1845 return Result<bool>(true);
1846 }
1847 else
1848 {
1849 wantsKeys = true;
1850 return Result<bool>(false);
1851 }
1852 }
1853 case Keys.escape:
1854 {
1855 MenuItem* openedMenuItem = menuControl->GetOpenedMenuItem();
1856 if (openedMenuItem != null)
1857 {
1858 auto result = openedMenuItem->SetState(State.closed, menuControl);
1859 if (result.Error()) return result;
1860 }
1861 wantsKeys = false;
1862 menuControl->SetSelectedMenuItem(null);
1863 auto result = menuControl->SetClosed();
1864 if (result.Error()) return result;
1865 result = menuControl->SetMenuInvalidated();
1866 if (result.Error()) return result;
1867 return Result<bool>(true);
1868 }
1869 case Keys.home:
1870 {
1871 if (Level() == 0)
1872 {
1873 MenuItem* firstMenuItem = menuControl->GetFirstMenuItem();
1874 if (firstMenuItem != null)
1875 {
1876 menuControl->SetSelectedMenuItem(firstMenuItem);
1877 wantsKeys = true;
1878 auto result = menuControl->SetMenuInvalidated();
1879 if (result.Error()) return result;
1880 return Result<bool>(true);
1881 }
1882 }
1883 else
1884 {
1885 MenuItem* parentMenuItem = GetParentMenuItem();
1886 if (parentMenuItem != null)
1887 {
1888 MenuItem* firstMenuItem = parentMenuItem->GetFirstMenuItem();
1889 if (firstMenuItem != null)
1890 {
1891 menuControl->SetSelectedMenuItem(firstMenuItem);
1892 wantsKeys = true;
1893 auto result = menuControl->SetMenuInvalidated();
1894 if (result.Error()) return result;
1895 return Result<bool>(true);
1896 }
1897 }
1898 }
1899 break;
1900 }
1901 case Keys.end:
1902 {
1903 if (Level() == 0)
1904 {
1905 MenuItem* lastMenuItem = menuControl->GetLastMenuItem();
1906 if (lastMenuItem != null)
1907 {
1908 menuControl->SetSelectedMenuItem(lastMenuItem);
1909 wantsKeys = true;
1910 auto result = menuControl->SetMenuInvalidated();
1911 if (result.Error()) return result;
1912 return Result<bool>(true);
1913 }
1914 }
1915 else
1916 {
1917 MenuItem* parentMenuItem = GetParentMenuItem();
1918 if (parentMenuItem != null)
1919 {
1920 MenuItem* lastMenuItem = parentMenuItem->GetLastMenuItem();
1921 if (lastMenuItem != null)
1922 {
1923 menuControl->SetSelectedMenuItem(lastMenuItem);
1924 wantsKeys = true;
1925 auto result = menuControl->SetMenuInvalidated();
1926 if (result.Error()) return result;
1927 return Result<bool>(true);
1928 }
1929 }
1930 }
1931 break;
1932 }
1933 case Keys.down:
1934 {
1935 if (Level() == 0)
1936 {
1937 auto result = SetState(state.open, menuControl);
1938 if (result.Error()) return result;
1939 MenuItem* firstChild = GetFirstMenuItem();
1940 if (firstChild != null)
1941 {
1942 menuControl->SetSelectedMenuItem(firstChild);
1943 wantsKeys = true;
1944 result = menuControl->SetMenuInvalidated();
1945 if (result.Error()) return result;
1946 return Result<bool>(true);
1947 }
1948 }
1949 else
1950 {
1951 MenuItem* nextMenuItem = GetNextMenuItem();
1952 if (nextMenuItem != null)
1953 {
1954 menuControl->SetSelectedMenuItem(nextMenuItem);
1955 wantsKeys = true;
1956 auto result = menuControl->SetMenuInvalidated();
1957 if (result.Error()) return result;
1958 return Result<bool>(true);
1959 }
1960 else
1961 {
1962 if (parentMenuItem != null)
1963 {
1964 MenuItem* firstMenuItem = parentMenuItem->GetFirstMenuItem();
1965 if (firstMenuItem != null)
1966 {
1967 menuControl->SetSelectedMenuItem(firstMenuItem);
1968 wantsKeys = true;
1969 auto result = menuControl->SetMenuInvalidated();
1970 if (result.Error()) return result;
1971 return Result<bool>(true);
1972 }
1973 }
1974 }
1975 }
1976 break;
1977 }
1978 case Keys.up:
1979 {
1980 if (Level() == 0)
1981 {
1982 auto result = SetState(state.open, menuControl);
1983 if (result.Error()) return result;
1984 MenuItem* lastChild = GetLastMenuItem();
1985 if (lastChild != null)
1986 {
1987 menuControl->SetSelectedMenuItem(lastChild);
1988 wantsKeys = true;
1989 result = menuControl->SetMenuInvalidated();
1990 if (result.Error()) return result;
1991 return Result<bool>(true);
1992 }
1993 }
1994 else
1995 {
1996 MenuItem* prevMenuItem = GetPrevMenuItem();
1997 if (prevMenuItem != null)
1998 {
1999 menuControl->SetSelectedMenuItem(prevMenuItem);
2000 wantsKeys = true;
2001 auto result = menuControl->SetMenuInvalidated();
2002 if (result.Error()) return result;
2003 return Result<bool>(true);
2004 }
2005 else
2006 {
2007 if (parentMenuItem != null)
2008 {
2009 MenuItem* lastMenuItem = parentMenuItem->GetLastMenuItem();
2010 if (lastMenuItem != null)
2011 {
2012 menuControl->SetSelectedMenuItem(lastMenuItem);
2013 wantsKeys = true;
2014 auto result = menuControl->SetMenuInvalidated();
2015 if (result.Error()) return result;
2016 return Result<bool>(true);
2017 }
2018 }
2019 }
2020 }
2021 break;
2022 }
2023 case Keys.right:
2024 {
2025 if (Level() == 0)
2026 {
2027 MenuItem* nextMenuItem = GetNextMenuItem();
2028 if (nextMenuItem != null)
2029 {
2030 menuControl->SetSelectedMenuItem(nextMenuItem);
2031 wantsKeys = true;
2032 auto result = menuControl->SetMenuInvalidated();
2033 if (result.Error()) return result;
2034 return Result<bool>(true);
2035 }
2036 else
2037 {
2038 MenuItem* firstMenuItem = menuControl->GetFirstMenuItem();
2039 if (firstMenuItem != null)
2040 {
2041 menuControl->SetSelectedMenuItem(firstMenuItem);
2042 wantsKeys = true;
2043 auto result = menuControl->SetMenuInvalidated();
2044 if (result.Error()) return result;
2045 return Result<bool>(true);
2046 }
2047 }
2048 }
2049 else
2050 {
2051 MenuItem* firstChild = GetFirstMenuItem();
2052 if (firstChild != null)
2053 {
2054 auto result = SetState(State.open, menuControl);
2055 if (result.Error()) return result;
2056 menuControl->SetSelectedMenuItem(firstChild);
2057 wantsKeys = true;
2058 result = menuControl->SetMenuInvalidated();
2059 if (result.Error()) return result;
2060 return Result<bool>(true);
2061 }
2062 else
2063 {
2064 while (parentMenuItem != null)
2065 {
2066 if (parentMenuItem->Level() == 0)
2067 {
2068 MenuItem* nextMenuItem = parentMenuItem->GetNextMenuItem();
2069 if (nextMenuItem != null)
2070 {
2071 auto result = parentMenuItem->SetState(State.closed, menuControl);
2072 if (result.Error()) return result;
2073 result = nextMenuItem->SetState(State.open, menuControl);
2074 if (result.Error()) return result;
2075 MenuItem* firstChild = nextMenuItem->GetFirstMenuItem();
2076 if (firstChild != null)
2077 {
2078 menuControl->SetSelectedMenuItem(firstChild);
2079 wantsKeys = true;
2080 result = menuControl->SetMenuInvalidated();
2081 if (result.Error()) return result;
2082 return Result<bool>(true);
2083 }
2084 }
2085 else
2086 {
2087 MenuItem* firstMenuItem = menuControl->GetFirstMenuItem();
2088 if (firstMenuItem != null)
2089 {
2090 auto result = parentMenuItem->SetState(State.closed, menuControl);
2091 if (result.Error()) return result;
2092 result = firstMenuItem->SetState(State.open, menuControl);
2093 if (result.Error()) return result;
2094 MenuItem* firstChild = firstMenuItem->GetFirstMenuItem();
2095 if (firstChild != null)
2096 {
2097 menuControl->SetSelectedMenuItem(firstChild);
2098 wantsKeys = true;
2099 result = menuControl->SetMenuInvalidated();
2100 if (result.Error()) return result;
2101 return Result<bool>(true);
2102 }
2103 }
2104 }
2105 }
2106 else
2107 {
2108 MenuItem* grandParentMenuItem = parentMenuItem->GetParentMenuItem();
2109 MenuItem* nextMenuItem = grandParentMenuItem->GetNextMenuItem();
2110 if (nextMenuItem != null)
2111 {
2112 auto result = parentMenuItem->SetState(State.closed, menuControl);
2113 if (result.Error()) return result;
2114 result = grandParentMenuItem->SetState(State.closed, menuControl);
2115 if (result.Error()) return result;
2116 result = nextMenuItem->SetState(State.open, menuControl);
2117 if (result.Error()) return result;
2118 MenuItem* firstChild = nextMenuItem->GetFirstMenuItem();
2119 if (firstChild != null)
2120 {
2121 menuControl->SetSelectedMenuItem(firstChild);
2122 wantsKeys = true;
2123 result = menuControl->SetMenuInvalidated();
2124 if (result.Error()) return result;
2125 return Result<bool>(true);
2126 }
2127 }
2128 else
2129 {
2130 parentMenuItem = grandParentMenuItem;
2131 }
2132 }
2133 }
2134 }
2135 }
2136 break;
2137 }
2138 case Keys.left:
2139 {
2140 if (Level() == 0)
2141 {
2142 MenuItem* prevMenuItem = GetPrevMenuItem();
2143 if (prevMenuItem != null)
2144 {
2145 menuControl->SetSelectedMenuItem(prevMenuItem);
2146 wantsKeys = true;
2147 auto result = menuControl->SetMenuInvalidated();
2148 if (result.Error()) return result;
2149 return Result<bool>(true);
2150 }
2151 else
2152 {
2153 MenuItem* lastMenuItem = menuControl->GetLastMenuItem();
2154 if (lastMenuItem != null)
2155 {
2156 menuControl->SetSelectedMenuItem(lastMenuItem);
2157 wantsKeys = true;
2158 auto result = menuControl->SetMenuInvalidated();
2159 if (result.Error()) return result;
2160 return Result<bool>(true);
2161 }
2162 }
2163 }
2164 else
2165 {
2166 if (parentMenuItem != null)
2167 {
2168 MenuItem* prevMenuItem = parentMenuItem->GetPrevMenuItem();
2169 if (prevMenuItem != null)
2170 {
2171 auto result = parentMenuItem->SetState(State.closed, menuControl);
2172 if (result.Error()) return result;
2173 result = prevMenuItem->SetState(State.open, menuControl);
2174 if (result.Error()) return result;
2175 MenuItem* firstChild = prevMenuItem->GetFirstMenuItem();
2176 if (firstChild != null)
2177 {
2178 menuControl->SetSelectedMenuItem(firstChild);
2179 wantsKeys = true;
2180 result = menuControl->SetMenuInvalidated();
2181 if (result.Error()) return result;
2182 return Result<bool>(true);
2183 }
2184 }
2185 else
2186 {
2187 MenuItem* grandParentMenuItem = parentMenuItem->GetParentMenuItem();
2188 if (grandParentMenuItem != null)
2189 {
2190 auto result = grandParentMenuItem->SetState(State.open, menuControl);
2191 if (result.Error()) return result;
2192 MenuItem* firstMenuItem = grandParentMenuItem->GetFirstMenuItem();
2193 if (firstMenuItem != null)
2194 {
2195 result = firstMenuItem->SetState(State.closed, menuControl);
2196 if (result.Error()) return result;
2197 menuControl->SetSelectedMenuItem(firstMenuItem);
2198 }
2199 }
2200 else
2201 {
2202 MenuItem* lastMenuItem = menuControl->GetLastMenuItem();
2203 if (lastMenuItem != null)
2204 {
2205 auto result = parentMenuItem->SetState(State.closed, menuControl);
2206 if (result.Error()) return result;
2207 result = lastMenuItem->SetState(State.open, menuControl);
2208 if (result.Error()) return result;
2209 MenuItem* firstChild = lastMenuItem->GetFirstMenuItem();
2210 if (firstChild != null)
2211 {
2212 menuControl->SetSelectedMenuItem(firstChild);
2213 wantsKeys = true;
2214 result = menuControl->SetMenuInvalidated();
2215 if (result.Error()) return result;
2216 return Result<bool>(true);
2217 }
2218 }
2219 }
2220 }
2221 }
2222 }
2223 break;
2224 }
2225 }
2226 }
2227 wantsKeys = true;
2228 return Result<bool>(false);
2229 }
2230 [nodiscard]
2231 private Result<bool> Close(MenuControl* menuControl)
2232 {
2233 auto result = SetState(State.closed, menuControl);
2234 if (result.Error()) return result;
2235 MenuItem* parentItem = GetParentMenuItem();
2236 while (parentItem != null)
2237 {
2238 result = parentItem->SetState(State.closed, menuControl);
2239 if (result.Error()) return result;
2240 parentItem = parentItem->GetParentMenuItem();
2241 }
2242 menuControl->SetSelectedMenuItem(null);
2243 result = menuControl->SetClosed();
2244 if (result.Error()) return result;
2245 result = menuControl->SetMenuInvalidated();
2246 if (result.Error()) return result;
2247 return Result<bool>(true);
2248 }
2249 [nodiscard]
2250 private Result<bool> Execute(MenuItem* parentMenuItem, bool& wantsKeys, MenuControl* menuControl)
2251 {
2252 if (children.IsEmpty())
2253 {
2254 if (IsEnabled())
2255 {
2256 auto result = Close(menuControl);
2257 if (result.Error()) return result;
2258 wantsKeys = false;
2259 result = DoClick();
2260 if (result.Error()) return result;
2261 }
2262 }
2263 else
2264 {
2265 if (IsEnabled())
2266 {
2267 auto result = SetState(State.open, menuControl);
2268 if (result.Error()) return result;
2269 MenuItem* firstMenuItem = GetFirstMenuItem();
2270 if (firstMenuItem != null)
2271 {
2272 menuControl->SetSelectedMenuItem(firstMenuItem);
2273 wantsKeys = true;
2274 result = menuControl->SetMenuInvalidated();
2275 if (result.Error()) return result;
2276 }
2277 }
2278 }
2279 return Result<bool>(true);
2280 }
2281 [nodiscard]
2282 internal Result<bool> DoMouseDown(MouseEventArgs& args, bool& handled, MenuControl* menuControl)
2283 {
2284 if (Level() == 0)
2285 {
2286 if (Contains(args.location))
2287 {
2288 SetMouseInClient();
2289 if (menuControl->IsOpen())
2290 {
2291 auto result = menuControl->SetClosed();
2292 if (result.Error()) return result;
2293 menuControl->SetLatestOpenedMenuItem(null);
2294 result = SetState(State.closed, menuControl);
2295 if (result.Error()) return result;
2296 result = DoMouseDown(args, menuControl);
2297 if (result.Error()) return result;
2298 handled = true;
2299 }
2300 else
2301 {
2302 menuControl->SetOpen();
2303 auto result = SetState(State.open, menuControl);
2304 if (result.Error()) return result;
2305 menuControl->SetLatestOpenedMenuItem(this);
2306 result = DoMouseDown(args, menuControl);
2307 if (result.Error()) return result;
2308 handled = true;
2309 }
2310 }
2311 else
2312 {
2313 Component* child = children.FirstChild();
2314 while (child != null)
2315 {
2316 if (child is MenuItem*)
2317 {
2318 MenuItem* childItem = cast<MenuItem*>(child);
2319 auto result = childItem->DoMouseDown(args, handled, menuControl);
2320 if (result.Error()) return result;
2321 if (handled) return Result<bool>(true);
2322 }
2323 child = child->NextSibling();
2324 }
2325 }
2326 }
2327 else
2328 {
2329 if (menuControl->IsOpen())
2330 {
2331 MenuItem* parentMenuItem = GetParentMenuItem();
2332 if (parentMenuItem != null)
2333 {
2334 if (parentMenuItem->state == State.open)
2335 {
2336 if (Contains(args.location))
2337 {
2338 if (!children.IsEmpty())
2339 {
2340 if (state == State.closed)
2341 {
2342 auto result = SetState(State.open, menuControl);
2343 if (result.Error()) return result;
2344 menuControl->SetLatestOpenedMenuItem(this);
2345 result = DoMouseDown(args, menuControl);
2346 if (result.Error()) return result;
2347 handled = true;
2348 }
2349 else if (state == State.open)
2350 {
2351 auto result = SetState(State.closed, menuControl);
2352 if (result.Error()) return result;
2353 menuControl->SetLatestOpenedMenuItem(null);
2354 result = DoMouseDown(args, menuControl);
2355 if (result.Error()) return result;
2356 handled = true;
2357 }
2358 }
2359 else
2360 {
2361 auto result = DoMouseDown(args, menuControl);
2362 if (result.Error()) return result;
2363 if ((args.buttons & MouseButtons.lbutton) != 0)
2364 {
2365 SetLButtonPressed();
2366 }
2367 handled = true;
2368 }
2369 }
2370 }
2371 }
2372 Component* child = children.FirstChild();
2373 while (child != null)
2374 {
2375 if (child is MenuItem*)
2376 {
2377 MenuItem* childItem = cast<MenuItem*>(child);
2378 auto result = childItem->DoMouseDown(args, handled, menuControl);
2379 if (result.Error()) return result;
2380 if (handled) return Result<bool>(true);
2381 }
2382 child = child->NextSibling();
2383 }
2384 }
2385 }
2386 return Result<bool>(true);
2387 }
2388 [nodiscard]
2389 internal Result<bool> DoMouseUp(MouseEventArgs& args, bool& handled, MenuControl* menuControl)
2390 {
2391 if (Level() == 0)
2392 {
2393 if (Contains(args.location))
2394 {
2395 auto result = DoMouseUp(args, menuControl);
2396 if (result.Error()) return result;
2397 handled = true;
2398 }
2399 else
2400 {
2401 Component* child = children.FirstChild();
2402 while (child != null)
2403 {
2404 if (child is MenuItem*)
2405 {
2406 MenuItem* childItem = cast<MenuItem*>(child);
2407 if (childItem->IsEnabled())
2408 {
2409 auto result = childItem->DoMouseUp(args, handled, menuControl);
2410 if (result.Error()) return result;
2411 if (handled) return Result<bool>(true);
2412 }
2413 }
2414 child = child->NextSibling();
2415 }
2416 }
2417 }
2418 else
2419 {
2420 if (menuControl->IsOpen())
2421 {
2422 MenuItem* parentMenuItem = GetParentMenuItem();
2423 if (parentMenuItem != null)
2424 {
2425 if (parentMenuItem->state == State.open)
2426 {
2427 if (Contains(args.location))
2428 {
2429 auto result = DoMouseUp(args, menuControl);
2430 if (result.Error()) return result;
2431 handled = true;
2432 if (handled) return Result<bool>(true);
2433 }
2434 }
2435 }
2436 Component* child = children.FirstChild();
2437 while (child != null)
2438 {
2439 if (child is MenuItem*)
2440 {
2441 MenuItem* childItem = cast<MenuItem*>(child);
2442 if (childItem->IsEnabled())
2443 {
2444 auto result = childItem->DoMouseUp(args, handled, menuControl);
2445 if (result.Error()) return result;
2446 if (handled) return Result<bool>(true);
2447 }
2448 }
2449 child = child->NextSibling();
2450 }
2451 }
2452 }
2453 return Result<bool>(true);
2454 }
2455 [nodiscard]
2456 private Result<bool> DoMouseDown(MouseEventArgs& args, MenuControl* menuControl)
2457 {
2458 menuControl->SetLatestMouseDownMenuItem(this);
2459 ResetLButtonPressed();
2460 return OnMouseDown(args);
2461 }
2462 [nodiscard]
2463 protected virtual Result<bool> OnMouseDown(MouseEventArgs& args)
2464 {
2465 mouseDownEvent.Fire(args);
2466 return Result<bool>(true);
2467 }
2468 [nodiscard]
2469 private Result<bool> DoMouseUp(MouseEventArgs& args, MenuControl* menuControl)
2470 {
2471 if (!IsEnabled()) return Result<bool>(false);
2472 OnMouseUp(args);
2473 bool isMenuBar = menuControl is MenuBar*;
2474 MenuItem* latestMouseDownMenuItem = menuControl->GetLatestMouseDownMenuItem();
2475 if ((args.buttons & MouseButtons.lbutton) != 0)
2476 {
2477 if (LButtonPressed())
2478 {
2479 ResetLButtonPressed();
2480 if (latestMouseDownMenuItem == this && children.IsEmpty())
2481 {
2482 auto result = ResetSelected(menuControl);
2483 if (result.Error()) return result;
2484 ResetMouseInClient();
2485 result = this->DoMouseLeave(menuControl);
2486 if (result.Error()) return result;
2487 result = LeaveChildren(menuControl);
2488 if (result.Error()) return result;
2489 result = Close(menuControl);
2490 if (result.Error()) return result;
2491 result = DoClick();
2492 if (result.Error()) return result;
2493 }
2494 if (isMenuBar)
2495 {
2496 MenuItem* openedMenuItem = menuControl->GetOpenedMenuItem();
2497 if (openedMenuItem != null)
2498 {
2499 auto result = openedMenuItem->SetState(State.closed, menuControl);
2500 if (result.Error()) return result;
2501 }
2502 auto result = menuControl->SetClosed();
2503 if (result.Error()) return result;
2504 result = menuControl->SetMenuInvalidated();
2505 if (result.Error()) return result;
2506 }
2507 }
2508 }
2509 return Result<bool>(true);
2510 }
2511 protected virtual void OnMouseUp(MouseEventArgs& args)
2512 {
2513 mouseUpEvent.Fire(args);
2514 }
2515 [nodiscard]
2516 internal Result<bool> DoMouseEnter(bool parentIsOpen, MenuControl* menuControl)
2517 {
2518 if (parentIsOpen)
2519 {
2520 Component* prev = PrevSibling();
2521 while (prev != null)
2522 {
2523 if (prev is MenuItem*)
2524 {
2525 MenuItem* prevItem = cast<MenuItem*>(prev);
2526 auto result = prevItem->DoMouseLeave(menuControl);
2527 if (result.Error()) return result;
2528 }
2529 prev = prev->PrevSibling();
2530 }
2531 Component* next = NextSibling();
2532 while (next != null)
2533 {
2534 if (next is MenuItem*)
2535 {
2536 MenuItem* nextItem = cast<MenuItem*>(next);
2537 auto result = nextItem->DoMouseLeave(menuControl);
2538 if (result.Error()) return result;
2539 }
2540 next = next->NextSibling();
2541 }
2542 Component* child = children.FirstChild();
2543 while (child != null)
2544 {
2545 if (child is MenuItem*)
2546 {
2547 MenuItem* childMenuItem = cast<MenuItem*>(child);
2548 auto result = childMenuItem->ResetSelected(menuControl);
2549 if (result.Error()) return result;
2550 if (childMenuItem->MouseInClient())
2551 {
2552 childMenuItem->ResetMouseInClient();
2553 result = childMenuItem->DoMouseLeave(menuControl);
2554 if (result.Error()) return result;
2555 }
2556 }
2557 child = child->NextSibling();
2558 }
2559 }
2560 if (parentIsOpen)
2561 {
2562 auto result = SetState(State.open, menuControl);
2563 if (result.Error()) return result;
2564 menuControl->SetLatestOpenedMenuItem(this);
2565 }
2566 EnterLeaveEventArgs args;
2567 auto result = OnMouseEnter(args);
2568 if (result.Error()) return result;
2569 result = menuControl->SetMenuInvalidated();
2570 if (result.Error()) return result;
2571 return Result<bool>(true);
2572 }
2573 [nodiscard]
2574 internal Result<bool> DoMouseMove(MouseEventArgs& args)
2575 {
2576 auto result = OnMouseMove(args);
2577 if (result.Error()) return result;
2578 return Result<bool>(true);
2579 }
2580 [nodiscard]
2581 protected virtual Result<bool> OnMouseEnter(EnterLeaveEventArgs& args)
2582 {
2583 mouseEnterEvent.Fire(args);
2584 if (args.errorId != 0)
2585 {
2586 return Result<bool>(ErrorId(args.errorId));
2587 }
2588 return Result<bool>(true);
2589 }
2590 [nodiscard]
2591 internal Result<bool> LeaveChildren(MenuControl* menuControl)
2592 {
2593 Component* child = children.FirstChild();
2594 while (child != null)
2595 {
2596 if (child is MenuItem*)
2597 {
2598 MenuItem* childMenuItem = cast<MenuItem*>(child);
2599 auto result = childMenuItem->ResetSelected(menuControl);
2600 if (result.Error()) return result;
2601 childMenuItem->ResetMouseInClient();
2602 result = childMenuItem->DoMouseLeave(menuControl);
2603 if (result.Error()) return result;
2604 result = childMenuItem->LeaveChildren(menuControl);
2605 if (result.Error()) return result;
2606 }
2607 child = child->NextSibling();
2608 }
2609 return Result<bool>(true);
2610 }
2611 [nodiscard]
2612 internal Result<bool> DoMouseLeave(MenuControl* menuControl)
2613 {
2614 ResetLButtonPressed();
2615 if (state == State.open)
2616 {
2617 auto result = SetState(State.closed, menuControl);
2618 if (result.Error()) return result;
2619 result = LeaveChildren(menuControl);
2620 if (result.Error()) return result;
2621 EnterLeaveEventArgs args;
2622 result = OnMouseLeave(args);
2623 if (result.Error()) return result;
2624 if (args.errorId != 0)
2625 {
2626 return Result<bool>(ErrorId(args.errorId));
2627 }
2628 }
2629 else if (Level() == 0)
2630 {
2631 EnterLeaveEventArgs args;
2632 auto result = OnMouseLeave(args);
2633 if (result.Error()) return result;
2634 if (args.errorId != 0)
2635 {
2636 return Result<bool>(ErrorId(args.errorId));
2637 }
2638 }
2639 return Result<bool>(true);
2640 }
2641 [nodiscard]
2642 protected virtual Result<bool> OnMouseLeave(EnterLeaveEventArgs& args)
2643 {
2644 mouseLeaveEvent.Fire(args);
2645 if (args.errorId != 0)
2646 {
2647 return Result<bool>(ErrorId(args.errorId));
2648 }
2649 return Result<bool>(true);
2650 }
2651 [nodiscard]
2652 internal Result<bool> DispatchMouseMove(MouseEventArgs& args, bool& handled, MenuControl* menuControl)
2653 {
2654 if (unionRect.Contains(args.location))
2655 {
2656 if (Contains(args.location))
2657 {
2658 auto result = SetSelected(menuControl);
2659 if (result.Error()) return result;
2660 }
2661 else
2662 {
2663 auto result = ResetSelected(menuControl);
2664 if (result.Error()) return result;
2665 ResetMouseInClient();
2666 }
2667 Component* child = children.FirstChild();
2668 while (child != null)
2669 {
2670 if (child is MenuItem*)
2671 {
2672 MenuItem* childMenuItem = cast<MenuItem*>(child);
2673 if (childMenuItem->Contains(args.location))
2674 {
2675 auto result = childMenuItem->SetSelected(menuControl);
2676 if (result.Error()) return result;
2677 if (!childMenuItem->MouseInClient())
2678 {
2679 childMenuItem->SetMouseInClient();
2680 result = childMenuItem->DoMouseEnter(true, menuControl);
2681 if (result.Error()) return result;
2682 handled = true;
2683 }
2684 else
2685 {
2686 result = childMenuItem->DoMouseMove(args);
2687 if (result.Error()) return result;
2688 }
2689 }
2690 else
2691 {
2692 auto result = childMenuItem->ResetSelected(menuControl);
2693 if (result.Error()) return result;
2694 if (childMenuItem->MouseInClient())
2695 {
2696 childMenuItem->ResetMouseInClient();
2697 if (!childMenuItem->IsSameOrParentOf(menuControl->LatestOpenedMenuItem()))
2698 {
2699 result = childMenuItem->DoMouseLeave(menuControl);
2700 if (result.Error()) return result;
2701 }
2702 }
2703 }
2704 }
2705 child = child->NextSibling();
2706 }
2707 }
2708 else
2709 {
2710 Component* child = children.FirstChild();
2711 while (child != null)
2712 {
2713 if (child is MenuItem*)
2714 {
2715 MenuItem* childMenuItem = cast<MenuItem*>(child);
2716 if (childMenuItem->IsSameOrParentOf(menuControl->LatestOpenedMenuItem()))
2717 {
2718 auto result = childMenuItem->DispatchMouseMove(args, handled, menuControl);
2719 if (result.Error()) return result;
2720 if (handled) return Result<bool>(true);
2721 }
2722 }
2723 child = child->NextSibling();
2724 }
2725 }
2726 return Result<bool>(true);
2727 }
2728 [nodiscard]
2729 protected virtual Result<bool> OnMouseMove(MouseEventArgs& args)
2730 {
2731 mouseMoveEvent.Fire(args);
2732 return Result<bool>(true);
2733 }
2734 [nodiscard]
2735 internal Result<bool> DoClick()
2736 {
2737 LogView* logView = Application.GetLogView();
2738 if (logView != null)
2739 {
2740 auto result = logView->WriteLine("MENUITEM: " + text + " CLICK!");
2741 if (result.Error()) return result;
2742 }
2743 ClickEventArgs args;
2744 auto result = OnClick(args);
2745 if (result.Error()) return result;
2746 if (args.errorId != 0)
2747 {
2748 return Result<bool>(ErrorId(args.errorId));
2749 }
2750 return Result<bool>(true);
2751 }
2752 [nodiscard]
2753 protected virtual Result<bool> OnClick(ClickEventArgs& args)
2754 {
2755 clickEvent.Fire(args);
2756 return Result<bool>(true);
2757 }
2758 public void CalculateChildRect(Graphics& graphics, const Font& font, const StringFormat& format, const Point& location)
2759 {
2760 childRect = Rect();
2761 childRect.location = location;
2762 Component* child = children.FirstChild();
2763 shortcutFieldWidth = 0;
2764 childIndicatorFieldWidth = 0;
2765 while (child != null)
2766 {
2767 if (child is MenuItemBase*)
2768 {
2769 MenuItemBase* item = cast<MenuItemBase*>(child);
2770 Size size = item->MeasureItem(graphics, font, format, shortcutFieldWidth, childIndicatorFieldWidth);
2771 childRect.size.w = Max(childRect.size.w, size.w);
2772 childRect.size.h = childRect.size.h + size.h;
2773 }
2774 child = child->NextSibling();
2775 }
2776 childRect.size.w = childRect.size.w + shortcutFieldWidth + childIndicatorFieldWidth;
2777 Rect itemRect(childRect.location, Size(childRect.size.w, 0));
2778 child = children.FirstChild();
2779 while (child != null)
2780 {
2781 if (child is MenuItemBase*)
2782 {
2783 MenuItemBase* item = cast<MenuItemBase*>(child);
2784 item->SetLocation(itemRect.location);
2785 itemRect.size.h = item->GetSize().h;
2786 item->SetSize(itemRect.size);
2787 itemRect.location.y = itemRect.location.y + itemRect.size.h;
2788 }
2789 child = child->NextSibling();
2790 }
2791 int shadowWidth = ShadowWidth();
2792 childRect.size.h = childRect.size.h + shadowWidth;
2793 childRect.size.w = childRect.size.w + shadowWidth;
2794 child = children.FirstChild();
2795 while (child != null)
2796 {
2797 if (child is MenuItem*)
2798 {
2799 MenuItem* item = cast<MenuItem*>(child);
2800 if (!item->Children().IsEmpty())
2801 {
2802 Point itemLocation = item->Location();
2803 Size itemSize = item->GetSize();
2804 item->CalculateChildRect(graphics, font, format, Point(itemLocation.x + itemSize.w - shadowWidth, itemLocation.y));
2805 }
2806 }
2807 child = child->NextSibling();
2808 }
2809 Rect r(Location(), GetSize());
2810 unionRect = Rect.Union(r, childRect);
2811 }
2812 [nodiscard]
2813 public override Result<bool> Draw(Graphics& graphics, const Padding& parentPadding, const Brush& textBrush, const Brush& disabledTextBrush,
2814 const Brush& backgroundBrush, const Brush& mouseOverBrush, const Brush& menuOpenBrush, const Brush& shadowBrush, const Brush& blackBrush,
2815 const Pen& blackPen, const Pen& darkPen, const Font& font, const StringFormat& format,
2816 const Color& menuOpenColor, MenuControl* menuControl, bool drawSubItems, const Point& origin)
2817 {
2818 switch (state)
2819 {
2820 case State.closed:
2821 {
2822 auto drawResult = DrawClosed(graphics, parentPadding, textBrush, disabledTextBrush, mouseOverBrush, backgroundBrush, menuOpenBrush,
2823 blackBrush, blackPen, font, format, menuOpenColor, menuControl, drawSubItems, origin);
2824 if (drawResult.Error())
2825 {
2826 return Result<bool>(ErrorId(drawResult.GetErrorId()));
2827 }
2828 break;
2829 }
2830 case State.open:
2831 {
2832 auto drawResult = DrawOpen(graphics, parentPadding, textBrush, disabledTextBrush, backgroundBrush, mouseOverBrush, menuOpenBrush,
2833 shadowBrush, blackBrush, blackPen, darkPen, font, format, menuOpenColor, menuControl, drawSubItems, origin);
2834 if (drawResult.Error())
2835 {
2836 return Result<bool>(ErrorId(drawResult.GetErrorId()));
2837 }
2838 break;
2839 }
2840 }
2841 return Result<bool>(true);
2842 }
2843 public const string& Text() const
2844 {
2845 return text;
2846 }
2847 [nodiscard]
2848 public Result<bool> SetText(const string& text_, MenuControl* menuControl)
2849 {
2850 text = text_;
2851 SetAccessKey();
2852 auto result = menuControl->InvalidateMenu();
2853 if (result.Error()) return result;
2854 return Result<bool>(true);
2855 }
2856 public inline wchar AccessKey() const
2857 {
2858 return accessKey;
2859 }
2860 private Result<bool> SetAccessKey()
2861 {
2862 auto utf16Result = ToUtf16(text);
2863 if (utf16Result.Error())
2864 {
2865 return Result<bool>(ErrorId(utf16Result.GetErrorId()));
2866 }
2867 wstring s = Rvalue(utf16Result.Value());
2868 long ampPos = s.Find('&');
2869 if (ampPos != -1 && ampPos < s.Length() - 1)
2870 {
2871 auto upperResult = ToUpper(cast<uchar>(s[ampPos + 1]));
2872 if (upperResult.Error())
2873 {
2874 return Result<bool>(ErrorId(upperResult.GetErrorId()));
2875 }
2876 accessKey = cast<wchar>(upperResult.Value());
2877 }
2878 else
2879 {
2880 accessKey = '\0';
2881 }
2882 return Result<bool>(true);
2883 }
2884 public void SetShortcut(Keys shortcut_)
2885 {
2886 shortcut = shortcut_;
2887 MenuControl* menuControl = GetMenuControl();
2888 if (menuControl != null)
2889 {
2890 menuControl->SetMenuChanged();
2891 }
2892 }
2893 private MenuItem* GetChildItemByAccessKey(wchar accessKey)
2894 {
2895 Component* child = children.FirstChild();
2896 while (child != null)
2897 {
2898 if (child is MenuItem*)
2899 {
2900 MenuItem* menuItem = cast<MenuItem*>(child);
2901 if (menuItem->AccessKey() == accessKey)
2902 {
2903 return menuItem;
2904 }
2905 }
2906 child = child->NextSibling();
2907 }
2908 return null;
2909 }
2910 internal void CollectShortcuts(HashMap<Keys, MenuItem*>& shortcuts)
2911 {
2912 if (shortcut != Keys.none)
2913 {
2914 shortcuts[shortcut] = this;
2915 }
2916 Component* child = children.FirstChild();
2917 while (child != null)
2918 {
2919 if (child is MenuItem*)
2920 {
2921 MenuItem* menuItem = cast<MenuItem*>(child);
2922 menuItem->CollectShortcuts(shortcuts);
2923 }
2924 child = child->NextSibling();
2925 }
2926 }
2927 public const ComponentContainer& Children() const
2928 {
2929 return children;
2930 }
2931 public override Padding DefaultPadding() const
2932 {
2933 if (Level() == 0)
2934 {
2935 return Padding(4, 0, 4, 0);
2936 }
2937 else
2938 {
2939 return Padding(32, 4, 32, 4);
2940 }
2941 }
2942 public Padding ShortcutPadding() const
2943 {
2944 if (Level() == 0)
2945 {
2946 return Padding(0, 0, 0, 0);
2947 }
2948 else
2949 {
2950 return Padding(4, 0, 4, 0);
2951 }
2952 }
2953 public virtual int ShadowWidth() const
2954 {
2955 return 3;
2956 }
2957 public virtual int ChildIndicatorIndent() const
2958 {
2959 return 9;
2960 }
2961 public virtual int ChildIndicatorWidth() const
2962 {
2963 return 6;
2964 }
2965 public bool Contains(const Point& p)
2966 {
2967 Rect r(Location(), GetSize());
2968 if (r.Contains(p))
2969 {
2970 return true;
2971 }
2972 return false;
2973 }
2974 public bool UnionRectContains(const Point& p)
2975 {
2976 return unionRect.Contains(p);
2977 }
2978 public inline const Rect& UnionRect() const
2979 {
2980 return unionRect;
2981 }
2982 public void GetOpenRect(Rect& parentRect)
2983 {
2984 if (!childRect.IsEmpty())
2985 {
2986 if (parentRect.IsEmpty())
2987 {
2988 parentRect = childRect;
2989 }
2990 else
2991 {
2992 parentRect = Rect.Union(parentRect, childRect);
2993 }
2994 }
2995 Component* child = children.FirstChild();
2996 while (child != null)
2997 {
2998 if (child is MenuItem*)
2999 {
3000 MenuItem* menuItem = cast<MenuItem*>(child);
3001 if (menuItem->GetState() == MenuItem.State.open)
3002 {
3003 menuItem->GetOpenRect(parentRect);
3004 }
3005 }
3006 child = child->NextSibling();
3007 }
3008 }
3009 public inline State GetState() const
3010 {
3011 return state;
3012 }
3013 [nodiscard]
3014 internal Result<bool> SetState(State state_, MenuControl* menuControl)
3015 {
3016 if (state != state_)
3017 {
3018 state = state_;
3019 auto result = menuControl->SetMenuInvalidated();
3020 if (result.Error()) return result;
3021 }
3022 return Result<bool>(true);
3023 }
3024 public override Size MeasureItem(Graphics& graphics, const Font& font, const StringFormat& format, int& shortcutFieldWidth, int& childIndicatorFieldWidth)
3025 {
3026 Padding padding = DefaultPadding();
3027 auto measureStringResult = graphics.MeasureStringRectF(text, font, PointF(0, 0), format);
3028 if (measureStringResult.Error())
3029 {
3030 SetErrorId(measureStringResult.GetErrorId());
3031 return Size();
3032 }
3033 RectF rect = Rvalue(measureStringResult.Value());
3034 int w = cast<int>(rect.size.w);
3035 w = w + padding.Horizontal();
3036 if (shortcut != Keys.none)
3037 {
3038 string shortcutText = ToString(shortcut);
3039 auto shortcutTextResult = graphics.MeasureStringRectF(shortcutText, font, PointF(0, 0), format);
3040 if (shortcutTextResult.Error())
3041 {
3042 SetErrorId(measureStringResult.GetErrorId());
3043 return Size();
3044 }
3045 RectF shortcutRect = Rvalue(shortcutTextResult.Value());
3046 Padding shortcutPadding = ShortcutPadding();
3047 int shortcutWidth = cast<int>(shortcutRect.size.w + shortcutPadding.Horizontal());
3048 shortcutFieldWidth = Max(shortcutFieldWidth, shortcutWidth);
3049 }
3050 if (Level() > 0 && !children.IsEmpty())
3051 {
3052 childIndicatorFieldWidth = Max(childIndicatorFieldWidth, ChildIndicatorIndent());
3053 }
3054 int h = cast<int>(rect.size.h);
3055 h = h + padding.Vertical();
3056 Size size(w, h);
3057 SetSize(size);
3058 return size;
3059 }
3060 private Result<bool> DrawClosed(Graphics& graphics, const Padding& parentPadding, const Brush& textBrush, const Brush& disabledTextBrush,
3061 const Brush& mouseOverBrush, const Brush& backgroundBrush,
3062 const Brush& menuOpenBrush, const Brush& blackBrush, const Pen& blackPen, const Font& font, const StringFormat& format, const Color& menuOpenColor,
3063 MenuControl* menuControl, bool drawSubItems, const Point& origin)
3064 {
3065 Point loc = Location();
3066 loc.x = loc.x - origin.x;
3067 loc.y = loc.y - origin.y;
3068 Size size = GetSize();
3069 Rect r(loc, size);
3070 r.size.w = r.size.w - 1;
3071 r.size.h = r.size.h - 1;
3072 MenuItem* selectedMenuItem = menuControl->GetSelectedMenuItem();
3073 if (Selected() || this == selectedMenuItem)
3074 {
3075 auto fillRectangleResult = graphics.FillRectangle(mouseOverBrush, r);
3076 if (fillRectangleResult.Error())
3077 {
3078 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3079 }
3080 auto drawRectangleResult = graphics.DrawRectangle(blackPen, r);
3081 if (drawRectangleResult.Error())
3082 {
3083 return Result<bool>(ErrorId(drawRectangleResult.GetErrorId()));
3084 }
3085 }
3086 if (Level() == 0)
3087 {
3088 if (!drawSubItems)
3089 {
3090 if (!Selected() && this != selectedMenuItem)
3091 {
3092 auto fillRectangleResult = graphics.FillRectangle(backgroundBrush, r);
3093 if (fillRectangleResult.Error())
3094 {
3095 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3096 }
3097 }
3098 StringFormat textFormat(format);
3099 auto setAlignmentResult = textFormat.SetAlignment(StringAlignment.center);
3100 if (setAlignmentResult.Error())
3101 {
3102 return Result<bool>(ErrorId(setAlignmentResult.GetErrorId()));
3103 }
3104 RectF rect(PointF(loc.x, loc.y), SizeF(size.w, size.h));
3105 if (IsEnabled())
3106 {
3107 auto drawStringResult = graphics.DrawString(text, font, rect, textFormat, textBrush);
3108 if (drawStringResult.Error())
3109 {
3110 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3111 }
3112 }
3113 else
3114 {
3115 auto drawStringResult = graphics.DrawString(text, font, rect, textFormat, disabledTextBrush);
3116 if (drawStringResult.Error())
3117 {
3118 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3119 }
3120 }
3121 }
3122 }
3123 else
3124 {
3125 if (drawSubItems)
3126 {
3127 Padding padding = DefaultPadding();
3128 if (IsEnabled())
3129 {
3130 auto drawStringResult = graphics.DrawString(text, font, PointF(loc.x + padding.left, loc.y + padding.top), format, textBrush);
3131 if (drawStringResult.Error())
3132 {
3133 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3134 }
3135 DrawShortcut(graphics, textBrush, font, format, origin);
3136 }
3137 else
3138 {
3139 auto drawStringResult = graphics.DrawString(text, font, PointF(loc.x + padding.left, loc.y + padding.top), format, disabledTextBrush);
3140 if (drawStringResult.Error())
3141 {
3142 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3143 }
3144 DrawShortcut(graphics, disabledTextBrush, font, format, origin);
3145 }
3146 DrawChildIndicator(graphics, blackBrush, origin);
3147 }
3148 }
3149 return Result<bool>(true);
3150 }
3151 private Result<bool> DrawOpen(Graphics& graphics, const Padding& parentPadding, const Brush& textBrush,
3152 const Brush& disabledTextBrush, const Brush& backgroundBrush, const Brush& mouseOverBrush,
3153 const Brush& menuOpenBrush, const Brush& shadowBrush, const Brush& blackBrush, const Pen& blackPen, const Pen& darkPen, const Font& font,
3154 const StringFormat& format, const Color& menuOpenColor, MenuControl* menuControl, bool drawSubItems, const Point& origin)
3155 {
3156 Point loc = Location();
3157 loc.x = loc.x - origin.x;
3158 loc.y = loc.y - origin.y;
3159 Size size = GetSize();
3160 Rect r(loc, size);
3161 r.size.w = r.size.w - 1;
3162 r.size.h = r.size.h - 1;
3163 int shadowWidth = ShadowWidth();
3164 if (Level() == 0)
3165 {
3166 if (!drawSubItems)
3167 {
3168 auto fillRectangleResult = graphics.FillRectangle(menuOpenBrush, r);
3169 if (fillRectangleResult.Error())
3170 {
3171 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3172 }
3173 auto drawLineResult = graphics.DrawLine(blackPen, r.location, Point(r.location.x + r.size.w, r.location.y));
3174 if (drawLineResult.Error())
3175 {
3176 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3177 }
3178 drawLineResult = graphics.DrawLine(blackPen, r.location, Point(r.location.x, r.location.y + r.size.h));
3179 if (drawLineResult.Error())
3180 {
3181 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3182 }
3183 drawLineResult = graphics.DrawLine(blackPen, Point(r.location.x + r.size.w, r.location.y), Point(r.location.x + r.size.w, r.location.y + r.size.h));
3184 if (drawLineResult.Error())
3185 {
3186 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3187 }
3188 if (children.IsEmpty())
3189 {
3190 drawLineResult = graphics.DrawLine(blackPen, Point(r.location.x, r.location.y + r.size.h), Point(r.location.x + r.size.w, r.location.y + r.size.h));
3191 if (drawLineResult.Error())
3192 {
3193 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3194 }
3195 }
3196 StringFormat textFormat(format);
3197 if (textFormat.Error()) return Result<bool>(ErrorId(textFormat.GetErrorId()));
3198 auto result = textFormat.SetAlignment(StringAlignment.center);
3199 if (result.Error()) return result;
3200 RectF rect(PointF(loc.x, loc.y), SizeF(size.w, size.h));
3201 if (IsEnabled())
3202 {
3203 auto drawStringResult = graphics.DrawString(text, font, rect, textFormat, textBrush);
3204 if (drawStringResult.Error())
3205 {
3206 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3207 }
3208 }
3209 else
3210 {
3211 auto drawStringResult = graphics.DrawString(text, font, rect, textFormat, disabledTextBrush);
3212 if (drawStringResult.Error())
3213 {
3214 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3215 }
3216 }
3217 }
3218 }
3219 else
3220 {
3221 if (drawSubItems)
3222 {
3223 MenuItem* selectedMenuItem = menuControl->GetSelectedMenuItem();
3224 if (Selected() || this == selectedMenuItem)
3225 {
3226 auto fillRectangleResult = graphics.FillRectangle(mouseOverBrush, r);
3227 if (fillRectangleResult.Error())
3228 {
3229 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3230 }
3231 auto drawRectangleResult = graphics.DrawRectangle(blackPen, r);
3232 if (drawRectangleResult.Error())
3233 {
3234 return Result<bool>(ErrorId(drawRectangleResult.GetErrorId()));
3235 }
3236 }
3237 else
3238 {
3239 Rect inside = r;
3240 inside.Inflate(-1, -1);
3241 auto fillRectangleResult = graphics.FillRectangle(menuOpenBrush, inside);
3242 if (fillRectangleResult.Error())
3243 {
3244 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3245 }
3246 }
3247 Padding padding = DefaultPadding();
3248 if (IsEnabled())
3249 {
3250 auto drawStringResult = graphics.DrawString(text, font, PointF(loc.x + padding.left, loc.y + padding.top), format, textBrush);
3251 if (drawStringResult.Error())
3252 {
3253 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3254 }
3255 auto drawShortcutResult = DrawShortcut(graphics, textBrush, font, format, origin);
3256 if (drawShortcutResult.Error())
3257 {
3258 return Result<bool>(ErrorId(drawShortcutResult.GetErrorId()));
3259 }
3260 }
3261 else
3262 {
3263 auto drawStringResult = graphics.DrawString(text, font, PointF(loc.x + padding.left, loc.y + padding.top), format, disabledTextBrush);
3264 if (drawStringResult.Error())
3265 {
3266 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3267 }
3268 auto drawShortcutResult = DrawShortcut(graphics, disabledTextBrush, font, format, origin);
3269 if (drawShortcutResult.Error())
3270 {
3271 return Result<bool>(ErrorId(drawShortcutResult.GetErrorId()));
3272 }
3273 }
3274 auto drawIndicatorResult = DrawChildIndicator(graphics, blackBrush, origin);
3275 if (drawIndicatorResult.Error())
3276 {
3277 return Result<bool>(ErrorId(drawIndicatorResult.GetErrorId()));
3278 }
3279 }
3280 }
3281 if (!childRect.IsEmpty())
3282 {
3283 if (drawSubItems)
3284 {
3285 auto getClipResult = graphics.GetClip();
3286 if (getClipResult.Error())
3287 {
3288 return Result<bool>(ErrorId(getClipResult.GetErrorId()));
3289 }
3290 Region prevClipRegion = getClipResult.Value();
3291 Rect menuBox = childRect;
3292 menuBox.location.x = menuBox.location.x - origin.x;
3293 menuBox.location.y = menuBox.location.y - origin.y;
3294 menuBox.size.h = menuBox.size.h - shadowWidth;
3295 menuBox.size.w = menuBox.size.w - shadowWidth;
3296 auto setClipResult = graphics.SetClip(menuBox);
3297 if (setClipResult.Error())
3298 {
3299 return Result<bool>(ErrorId(setClipResult.GetErrorId()));
3300 }
3301 auto clearResult = graphics.Clear(menuOpenColor);
3302 if (clearResult.Error())
3303 {
3304 return Result<bool>(ErrorId(clearResult.GetErrorId()));
3305 }
3306 Rect cr(childRect);
3307 cr.location.x = cr.location.x - origin.x;
3308 cr.location.y = cr.location.y - origin.y;
3309 setClipResult = graphics.SetClip(cr);
3310 if (setClipResult.Error())
3311 {
3312 return Result<bool>(ErrorId(setClipResult.GetErrorId()));
3313 }
3314 Rect bottomShadowRect(
3315 Point(cr.location.x + shadowWidth, cr.location.y + cr.size.h - shadowWidth),
3316 Size(cr.size.w - shadowWidth, shadowWidth));
3317 auto fillRectangleResult = graphics.FillRectangle(shadowBrush, bottomShadowRect);
3318 if (fillRectangleResult.Error())
3319 {
3320 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3321 }
3322 Rect rightShadowRect(
3323 Point(cr.location.x + cr.size.w - shadowWidth, cr.location.y + shadowWidth),
3324 Size(shadowWidth, cr.size.h - shadowWidth));
3325 fillRectangleResult = graphics.FillRectangle(shadowBrush, rightShadowRect);
3326 if (fillRectangleResult.Error())
3327 {
3328 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
3329 }
3330 Rect rect = childRect;
3331 rect.location.x = rect.location.x - origin.x;
3332 rect.location.y = rect.location.y - origin.y;
3333 rect.size.w = rect.size.w - 1;
3334 rect.size.h = rect.size.h - 1;
3335 if (Level() == 0)
3336 {
3337 auto drawLineResult = graphics.DrawLine(blackPen, rect.location, Point(rect.location.x, rect.location.y + rect.size.h - shadowWidth));
3338 if (drawLineResult.Error())
3339 {
3340 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3341 }
3342 drawLineResult = graphics.DrawLine(blackPen, Point(rect.location.x, rect.location.y + rect.size.h - shadowWidth),
3343 Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y + rect.size.h - shadowWidth));
3344 if (drawLineResult.Error())
3345 {
3346 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3347 }
3348 drawLineResult = graphics.DrawLine(blackPen, Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y + rect.size.h - shadowWidth),
3349 Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y));
3350 if (drawLineResult.Error())
3351 {
3352 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3353 }
3354 drawLineResult = graphics.DrawLine(blackPen,
3355 Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y), Point(r.location.x + r.size.w, rect.location.y));
3356 if (drawLineResult.Error())
3357 {
3358 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3359 }
3360 }
3361 else
3362 {
3363 auto drawLineResult = graphics.DrawLine(blackPen, rect.location, Point(rect.location.x, rect.location.y + rect.size.h - shadowWidth));
3364 if (drawLineResult.Error())
3365 {
3366 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3367 }
3368 drawLineResult = graphics.DrawLine(blackPen, Point(rect.location.x, r.location.y + rect.size.h - shadowWidth),
3369 Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y + rect.size.h - shadowWidth));
3370 if (drawLineResult.Error())
3371 {
3372 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3373 }
3374 drawLineResult = graphics.DrawLine(blackPen, Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y + rect.size.h - shadowWidth),
3375 Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y));
3376 if (drawLineResult.Error())
3377 {
3378 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3379 }
3380 drawLineResult = graphics.DrawLine(blackPen, Point(rect.location.x + rect.size.w - shadowWidth, rect.location.y),
3381 Point(rect.location.x, rect.location.y));
3382 if (drawLineResult.Error())
3383 {
3384 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3385 }
3386 }
3387 Component* child = children.FirstChild();
3388 while (child != null)
3389 {
3390 if (child is MenuItemBase*)
3391 {
3392 MenuItemBase* menuItem = cast<MenuItem*>(child);
3393 auto drawResult = menuItem->Draw(graphics, parentPadding, textBrush, disabledTextBrush, backgroundBrush, mouseOverBrush,
3394 menuOpenBrush, shadowBrush, blackBrush, blackPen, darkPen, font, format, menuOpenColor, menuControl, drawSubItems, origin);
3395 if (drawResult.Error())
3396 {
3397 return Result<bool>(ErrorId(drawResult.GetErrorId()));
3398 }
3399 }
3400 child = child->NextSibling();
3401 }
3402 setClipResult = graphics.SetClip(prevClipRegion);
3403 if (setClipResult.Error())
3404 {
3405 return Result<bool>(ErrorId(setClipResult.GetErrorId()));
3406 }
3407 }
3408 }
3409 return Result<bool>(true);
3410 }
3411 private Result<bool> DrawShortcut(Graphics& graphics, const Brush& textBrush, const Font& font, const StringFormat& format, const Point& origin)
3412 {
3413 if (shortcut != Keys.none)
3414 {
3415 string shortcutText = ToString(shortcut);
3416 Padding shortcutPadding = ShortcutPadding();
3417 int shortcutFieldWidth = 0;
3418 int childIndicatorFieldWidth = 0;
3419 MenuItem* parent = GetParentMenuItem();
3420 if (parent != null)
3421 {
3422 shortcutFieldWidth = parent->shortcutFieldWidth;
3423 childIndicatorFieldWidth = parent->childIndicatorFieldWidth;
3424 }
3425 Padding padding = DefaultPadding();
3426 int shadowWidth = ShadowWidth();
3427 Point loc = Location();
3428 loc.x = loc.x - origin.x;
3429 loc.y = loc.y - origin.y;
3430 Size size = GetSize();
3431 auto drawStringResult = graphics.DrawString(shortcutText, font,
3432 PointF(loc.x + size.w - shadowWidth - shortcutFieldWidth - childIndicatorFieldWidth + shortcutPadding.left, loc.y + padding.top), format, textBrush);
3433 if (drawStringResult.Error())
3434 {
3435 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
3436 }
3437 }
3438 return Result<bool>(true);
3439 }
3440 private Result<bool> DrawChildIndicator(Graphics& graphics, const Brush& blackBrush, const Point& origin)
3441 {
3442 if (children.IsEmpty()) return Result<bool>(true);
3443 Point loc = Location();
3444 loc.x = loc.x - origin.x;
3445 loc.y = loc.y - origin.y;
3446 Size size = GetSize();
3447 int childIndicatorIndent = ChildIndicatorIndent();
3448 int childIndicatorWidth = ChildIndicatorWidth();
3449 int shadowWidth = ShadowWidth();
3450 Point up(loc.x + size.w - childIndicatorIndent - shadowWidth, loc.y + size.h / 2 - childIndicatorWidth / 2);
3451 Point down(loc.x + size.w - childIndicatorIndent - shadowWidth, loc.y + size.h / 2 + childIndicatorWidth / 2);
3452 Point right(loc.x + size.w - childIndicatorIndent + cast<int>((Sqrt(3) / 2.000000) * childIndicatorWidth) - shadowWidth, loc.y + size.h / 2);
3453 List<Point> triangle;
3454 triangle.Add(up);
3455 triangle.Add(down);
3456 triangle.Add(right);
3457 auto fillPolygonResult = graphics.FillPolygon(blackBrush, 3, triangle.Begin().Ptr());
3458 if (fillPolygonResult.Error())
3459 {
3460 return Result<bool>(ErrorId(fillPolygonResult.GetErrorId()));
3461 }
3462 return Result<bool>(true);
3463 }
3464 internal bool IsSameOrParentOf(MenuItem* menuItem) const
3465 {
3466 if (this == menuItem) return true;
3467 MenuItem* parent = menuItem->GetParentMenuItem();
3468 if (parent != null)
3469 {
3470 return IsSameOrParentOf(parent);
3471 }
3472 return false;
3473 }
3474 internal inline bool Selected() const
3475 {
3476 return (flags & Flags.selected) != 0;
3477 }
3478 [nodiscard]
3479 internal Result<bool> SetSelected(MenuControl* menuControl)
3480 {
3481 if ((flags & Flags.selected) == 0)
3482 {
3483 flags = cast<Flags>(flags | Flags.selected);
3484 auto result = menuControl->SetMenuInvalidated();
3485 if (result.Error()) return result;
3486 }
3487 return Result<bool>(true);
3488 }
3489 [nodiscard]
3490 internal Result<bool> ResetSelected(MenuControl* menuControl)
3491 {
3492 if ((flags & Flags.selected) != 0)
3493 {
3494 flags = cast<Flags>(flags & ~Flags.selected);
3495 auto result = menuControl->SetMenuInvalidated();
3496 if (result.Error()) return result;
3497 }
3498 return Result<bool>(true);
3499 }
3500 public inline bool IsEnabled() const
3501 {
3502 return cast<Flags>(flags & Flags.disabled) == Flags.none;
3503 }
3504 public inline bool IsDisabled() const
3505 {
3506 return cast<Flags>(flags & Flags.disabled) != Flags.none;
3507 }
3508 [nodiscard]
3509 public Result<bool> Enable()
3510 {
3511 if (!IsEnabled())
3512 {
3513 flags = cast<Flags>(flags & ~Flags.disabled);
3514 MenuControl* menuControl = GetMenuControl();
3515 if (menuControl != null)
3516 {
3517 auto result = menuControl->SetMenuInvalidated();
3518 if (result.Error()) return result;
3519 menuControl->SetMenuChanged();
3520 }
3521 }
3522 return Result<bool>(true);
3523 }
3524 [nodiscard]
3525 public Result<bool> Disable()
3526 {
3527 if (IsEnabled())
3528 {
3529 flags = cast<Flags>(flags | Flags.disabled);
3530 MenuControl* menuControl = GetMenuControl();
3531 if (menuControl != null)
3532 {
3533 auto result = menuControl->SetMenuInvalidated();
3534 if (result.Error()) return result;
3535 menuControl->SetMenuChanged();
3536 }
3537 }
3538 return Result<bool>(true);
3539 }
3540 internal inline bool MouseInClient() const
3541 {
3542 return (flags & Flags.mouseInClient) != 0;
3543 }
3544 internal void SetMouseInClient()
3545 {
3546 flags = cast<Flags>(flags | Flags.mouseInClient);
3547 }
3548 internal void ResetMouseInClient()
3549 {
3550 flags = cast<Flags>(flags & ~Flags.mouseInClient);
3551 }
3552 private inline bool LButtonPressed() const
3553 {
3554 return (flags & Flags.lbuttonPressed) != 0;
3555 }
3556 private void SetLButtonPressed()
3557 {
3558 flags = cast<Flags>(flags | Flags.lbuttonPressed);
3559 }
3560 private void ResetLButtonPressed()
3561 {
3562 flags = cast<Flags>(flags & ~Flags.lbuttonPressed);
3563 }
3564 public Event<MouseEventHandler, MouseEventArgs>& MouseDownEvent()
3565 {
3566 return mouseDownEvent;
3567 }
3568 public Event<MouseEventHandler, MouseEventArgs>& MouseUpEvent()
3569 {
3570 return mouseUpEvent;
3571 }
3572 public Event<MouseEnterEventHandler, EnterLeaveEventArgs>& MouseEnterEvent()
3573 {
3574 return mouseEnterEvent;
3575 }
3576 public Event<MouseEventHandler, MouseEventArgs>& MouseMoveEvent()
3577 {
3578 return mouseMoveEvent;
3579 }
3580 public Event<MouseLeaveEventHandler, EnterLeaveEventArgs>& MouseLeaveEvent()
3581 {
3582 return mouseLeaveEvent;
3583 }
3584 public Event<ClickEventHandler, ClickEventArgs>& ClickEvent()
3585 {
3586 return clickEvent;
3587 }
3588 private string text;
3589 private ComponentContainer children;
3590 private Rect childRect;
3591 private Rect unionRect;
3592 private Event<MouseEventHandler, MouseEventArgs> mouseDownEvent;
3593 private Event<MouseEventHandler, MouseEventArgs> mouseUpEvent;
3594 private Event<MouseEnterEventHandler, EnterLeaveEventArgs> mouseEnterEvent;
3595 private Event<MouseEventHandler, MouseEventArgs> mouseMoveEvent;
3596 private Event<MouseLeaveEventHandler, EnterLeaveEventArgs> mouseLeaveEvent;
3597 private Event<ClickEventHandler, ClickEventArgs> clickEvent;
3598 private State state;
3599 private Flags flags;
3600 private wchar accessKey;
3601 private Keys shortcut;
3602 private int shortcutFieldWidth;
3603 private int childIndicatorFieldWidth;
3604 }
3605
3606 public class MenuItemSeparator : MenuItemBase
3607 {
3608 [nodiscard]
3609 public override Result<bool> Draw(Graphics& graphics, const Padding& parentPadding, const Brush& textBrush, const Brush& disabledTextBrush,
3610 const Brush& backgroundBrush, const Brush& mouseOverBrush, const Brush& menuOpenBrush, const Brush& shadowBrush, const Brush& blackBrush,
3611 const Pen& blackPen, const Pen& darkPen, const Font& font, const StringFormat& format,
3612 const Color& menuOpenColor, MenuControl* menuControl, bool drawSubItems, const Point& origin)
3613 {
3614 Point loc = Location();
3615 loc.x = loc.x - origin.x;
3616 loc.y = loc.y - origin.y;
3617 Rect rect(loc, GetSize());
3618 Padding padding = DefaultPadding();
3619 auto drawLineResult = graphics.DrawLine(darkPen,
3620 Point(rect.location.x + padding.left, rect.location.y + rect.size.h / 2),
3621 Point(rect.location.x + rect.size.w - padding.right, rect.location.y + rect.size.h / 2));
3622 if (drawLineResult.Error())
3623 {
3624 return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
3625 }
3626 return Result<bool>(true);
3627 }
3628 public override Padding DefaultPadding() const
3629 {
3630 return Padding(32, 4, 8, 4);
3631 }
3632 public override Size MeasureItem(Graphics& graphics, const Font& font, const StringFormat& format, int& shortcutFieldWidth, int& childIndicatorFieldWidth)
3633 {
3634 Padding padding = DefaultPadding();
3635 RectF rect;
3636 int w = cast<int>(rect.size.w);
3637 w = w + padding.Horizontal();
3638 int h = cast<int>(rect.size.h);
3639 h = h + padding.Vertical();
3640 Size size(w, h);
3641 SetSize(size);
3642 return size;
3643 }
3644 }