16 private readonly Action? _action;
17 private readonly Action<bool>? _checkedAction;
18 private readonly MenuIndicatorDrawing _marker;
19 private readonly MenuIndicatorDrawing _arrow;
20 private bool _disposed;
21 private bool _isPointerOver;
22 private bool _isSelected;
23 private bool _isEnabled =
true;
25 internal event Action<MenuItemWidget>? Hovered;
27 public event Action<MenuItemWidget>?
Invoked;
30 string text,
string? shortcutText, Action? action,
MenuBarTheme theme,
32 char? mnemonic, SKImage? icon,
bool checkable,
string? radioGroup,
39 _checkedAction = checkedAction;
51 Label = CreateText(host, view, bounds,
"label", SKTextAlign.Left).SetText(
Text);
53 _marker =
new MenuIndicatorDrawing(host, view, bounds,
54 radioGroup is
null ? MenuIndicatorDrawing.Shape.Check : MenuIndicatorDrawing.Shape.Radio,
55 theme, $
"{Nickname}.marker");
56 _arrow =
new MenuIndicatorDrawing(host, view, bounds, MenuIndicatorDrawing.Shape.Arrow,
57 theme, $
"{Nickname}.arrow");
76 public string?
Key {
get; }
78 public string Text {
get; }
86 public SKImage?
Icon {
get; }
111 internal bool CanInvoke => !_disposed && _isEnabled && _owner.CanInvoke;
118 ObjectDisposedException.ThrowIf(_disposed,
this);
119 _isEnabled = enabled;
120 ApplyInputState(_owner.IsOpen);
121 if (!enabled) _owner.OnItemDisabled(
this);
131 ObjectDisposedException.ThrowIf(_disposed,
this);
132 if (!
IsCheckable)
throw new InvalidOperationException(
"This menu item is not checkable.");
133 if (isChecked &&
RadioGroup is not
null) _owner.UncheckRadioPeers(
this);
143 if (!CanInvoke)
return;
146 _owner.OpenSubMenu(
this, selectFirst:
true);
151 _owner.NotifyInvoked(
this);
153 _checkedAction?.Invoke(checkedState);
157 internal void SetSelected(
bool selected)
159 _isSelected = selected;
160 if (!selected) _isPointerOver =
false;
164 internal void ApplyInputState(
bool menuAcceptsInput)
166 bool enabled = menuAcceptsInput && _isEnabled && !_disposed;
173 internal void SetItemZOrder(
int zOrder)
175 Background.ZOrder = zOrder;
177 drawing.ZOrder = zOrder + 1;
178 if (
IconDrawing is not
null) IconDrawing.ZOrder = zOrder + 1;
181 internal void SetBounds(Rectangle bounds,
int markerWidth,
int iconWidth,
int shortcutWidth,
int arrowWidth)
184 Background.ScreenBounds = bounds;
185 int x = bounds.X + _theme.ItemHorizontalPadding;
186 Place(
Marker,
new Rectangle(x, bounds.Y, Math.Max(1, markerWidth), bounds.Height));
189 Place(
IconDrawing,
new Rectangle(x, bounds.Y + (bounds.Height - _theme.IconSize) / 2, _theme.IconSize, _theme.IconSize));
191 int right = bounds.Right - _theme.ItemHorizontalPadding;
192 Place(
Arrow,
new Rectangle(right - arrowWidth, bounds.Y, Math.Max(1, arrowWidth), bounds.Height));
194 Place(
ShortcutLabel,
new Rectangle(right - shortcutWidth, bounds.Y, Math.Max(1, shortcutWidth), bounds.Height));
195 if (shortcutWidth > 0) right -= shortcutWidth + _theme.ShortcutGap;
196 Place(
Label,
new Rectangle(x, bounds.Y, Math.Max(1, right - x), bounds.Height));
197 Label.HorizontalPadding = ShortcutLabel.HorizontalPadding = 0;
200 private void Place(DirectDrawingBase drawing, Rectangle bounds)
203 drawing.ScreenBounds = bounds;
206 private TextBlock CreateText(RenderSurfaceHostBase host,
View view, Rectangle bounds,
string name, SKTextAlign alignment) =>
207 new TextBlock(host, view, bounds, $
"{Nickname}.{name}")
208 .SetFont(SKTypeface.Default, _theme.FontSize, _theme.MinimumFontSize)
209 .SetAlignment(alignment, TextBlock.VerticalAlign.Center).EnableWrapping(
false);
214 if (_disposed)
return;
216 _owner.UnregisterItem(
this);
225 base.OnPointerEnter(args);
226 if (!_isEnabled)
return;
227 _isPointerOver =
true;
228 Hovered?.Invoke(
this);
235 base.OnPointerLeave(args);
236 _isPointerOver =
false;
243 base.OnPointerDown(args);
244 if (_isEnabled && args.
IsPrimaryButton) SetBackgroundColor(_theme.ItemPressedColor);
250 base.OnPointerUp(args);
257 base.OnPointerClick(args);
266 base.OnKeyboardInput(args);
269 if (!CanInvoke)
return;
274 private void UpdateVisualState()
276 SetBackgroundColor(_isEnabled && (_isPointerOver || _isSelected) ? _theme.ItemHoverColor : _theme.ItemNormalColor);
277 Label.
SetColors(ToSkColor(_isEnabled ? _theme.TextColor : _theme.DisabledTextColor), SKColors.Transparent);
278 ShortcutLabel.
SetColors(ToSkColor(_isEnabled ? _theme.ShortcutTextColor : _theme.DisabledTextColor), SKColors.Transparent);
279 var indicator = ToSkColor(_isEnabled ? _theme.IndicatorColor : _theme.DisabledTextColor);
281 _arrow.SetState(
SubMenu is not
null, indicator);
282 IconDrawing?.
SetTint(_isEnabled ? SKColors.White : ToSkColor(_theme.DisabledTextColor));
285 private void SetBackgroundColor(Color color)
291 private static SKColor ToSkColor(Color color) =>
new(color.R, color.G, color.B, color.A);
A retained-mode text drawable with wrapping, multi-line ( ) support, horizontal/vertical alignment,...
TextBlock SetColors(SKColor fg, SKColor bg)
Sets the foreground (text) and background colors using Skia colors.
@ Rectangle
Particles are spawned uniformly within a rectangular area. This is the default distribution.