Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
DialogBox.cs
1using System.Drawing;
2using System.Numerics;
9using SkiaSharp;
10
12
17public abstract class DialogBox : DraggableContainerWidget
18{
22 protected const int DefaultTitleBarHeight = 36;
26 protected const int DefaultCloseButtonSize = 28;
27
31 protected readonly static Color DefaultPanelColor = Color.FromArgb(245, 36, 36, 44);
35 protected readonly static Color DefaultPanelBorderColor = Color.FromArgb(255, 140, 140, 155);
39 protected readonly static Color DefaultTitleBarColor = Color.FromArgb(255, 57, 57, 72);
40
41 private const int DialogZOrderStart = 100_000;
42 private const int DialogZOrderGap = 100;
43
44 private static readonly object _zOrderSyncRoot = new();
45 private static int _nextDialogZOrder = DialogZOrderStart;
46
47 private bool _disposed;
48
52 public event Action<DialogResult>? Closed;
53
54 #region constructors
55
65 protected DialogBox(RenderSurfaceHostBase renderSurfaceHost,
66 View view,
67 Rectangle bounds,
68 string title,
69 bool showCloseButton = true,
70 string? nickname = null)
71 : base(renderSurfaceHost, DirectDrawingMode.View, bounds.Location, nickname)
72 {
73 ValidateBounds(bounds);
74
75 DialogSize = bounds.Size;
76
77 Panel = CreatePanel(renderSurfaceHost, view, bounds);
78 TitleBar = CreateTitleBar(renderSurfaceHost, view, bounds);
79 TitleText = CreateTitleText(renderSurfaceHost, view, bounds, title);
80
81 Add(Panel);
84
85 if (showCloseButton)
86 {
87 CloseButton = CreateCloseButton(renderSurfaceHost, view, bounds);
88 Add(CloseButton, GetCloseButtonOffset(bounds.Size));
89
90 CloseButton.Clicked += OnCloseButtonClicked;
91 }
92
93 CompleteInitialization();
94 }
95
105 protected DialogBox(RenderSurfaceHostBase renderSurfaceHost,
106 SceneLayer sceneLayer,
107 Rectangle bounds,
108 string title,
109 bool showCloseButton = true,
110 string? nickname = null)
111 : base(renderSurfaceHost, DirectDrawingMode.SceneLayer, bounds.Location, nickname)
112 {
113 ValidateBounds(bounds);
114
115 DialogSize = bounds.Size;
116
117 Panel = CreatePanel(renderSurfaceHost, sceneLayer, bounds);
118 TitleBar = CreateTitleBar(renderSurfaceHost, sceneLayer, bounds);
119 TitleText = CreateTitleText(renderSurfaceHost, sceneLayer, bounds, title);
120
121 Add(Panel);
122 Add(TitleBar);
123 Add(TitleText);
124
125 if (showCloseButton)
126 {
127 CloseButton = CreateCloseButton(renderSurfaceHost, sceneLayer, bounds);
128 Add(CloseButton, GetCloseButtonOffset(bounds.Size));
129
130 CloseButton.Clicked += OnCloseButtonClicked;
131 }
132
133 CompleteInitialization();
134 }
135
136 #endregion constructors
137
138 #region public properties
139
143 public DirectRectangle Panel { get; }
144
148 public DirectRectangle TitleBar { get; }
149
153 public TextBlock TitleText { get; }
154
158 public ButtonWidget? CloseButton { get; }
159
163 public Size DialogSize { get; }
164
168 public bool IsClosed { get; private set; }
169
173 public DialogResult Result { get; private set; }
174
178 public bool DisposeOnClose { get; set; } = true;
179
186 public int AcceptKey { get; set; } = 13;
187
194 public int CancelKey { get; set; } = 27;
195
196 #endregion public properties
197
198 #region exposed methods and hooks
199
204 public void Close(DialogResult result = DialogResult.Close)
205 {
206 if (IsClosed)
207 return;
208
209 IsClosed = true;
210 Result = result;
211
212 Hide();
213
214 OnClosed(result);
215 Closed?.Invoke(result);
216
217 if (DisposeOnClose)
218 Dispose();
219 }
220
225 protected virtual void OnClosed(DialogResult result)
226 {
227 }
228
232 protected virtual void OnAcceptRequested()
233 {
235 }
236
238 protected override void ProcessActivated()
239 {
240 BringVisualTreeToFront();
241 base.ProcessActivated();
242 }
243
244 private void BringVisualTreeToFront()
245 {
246 DirectDrawingBase[] visuals = GetVisualDescendants(this).ToArray();
247 if (visuals.Length == 0)
248 return;
249
250 int minimumZOrder = visuals.Min(static visual => visual.ZOrder);
251 int maximumZOrder = visuals.Max(static visual => visual.ZOrder);
252 int span = maximumZOrder - minimumZOrder;
253
254 int targetMinimum;
255 lock (_zOrderSyncRoot)
256 {
257 targetMinimum = _nextDialogZOrder;
258 _nextDialogZOrder = checked(targetMinimum + span + DialogZOrderGap);
259 }
260
261 int delta = targetMinimum - minimumZOrder;
262 foreach (DirectDrawingBase visual in visuals)
263 visual.ZOrder += delta;
264 }
265
266 private static IEnumerable<DirectDrawingBase> GetVisualDescendants(IDirectCompositeContainer container)
267 {
268 foreach (IDirectCompositeChild child in container.Children)
269 {
270 if (child is DirectDrawingBase visual)
271 yield return visual;
272
273 if (child is not IDirectCompositeContainer childContainer)
274 continue;
275
276 foreach (DirectDrawingBase descendant in GetVisualDescendants(childContainer))
277 yield return descendant;
278 }
279 }
280
282 protected override bool CanStartDrag(WidgetPointerEventArgs args)
283 {
284 if (!base.CanStartDrag(args))
285 return false;
286
287 RectangleF titleBarBounds = TitleBar.GetDrawLocationScreen(args.View);
288 titleBarBounds.Offset(args.WrappedOffsetWorldPx.X * args.View.Viewport.Zoom, args.WrappedOffsetWorldPx.Y * args.View.Viewport.Zoom);
289
290 return titleBarBounds.Contains(args.ScreenPositionPx.X, args.ScreenPositionPx.Y);
291 }
292
294 protected override void OnKeyboardInput(WidgetKeyboardEventArgs args)
295 {
296 base.OnKeyboardInput(args);
297
298 if (args.Handled || args.KeyAction != KeyAction.Pressed)
299 return;
300
301 if (args.Key == CancelKey)
302 {
303 args.Handled = true;
304 Close(DialogResult.Cancel);
305 return;
306 }
307
308 if (args.Key == AcceptKey)
309 {
310 args.Handled = true;
312 }
313 }
314
316 protected override void OnCancelled()
317 {
318 base.OnCancelled();
319 Close(DialogResult.Cancel);
320 }
321
323 public override void Dispose()
324 {
325 if (_disposed)
326 return;
327
328 _disposed = true;
329
330 if (CloseButton is not null)
331 {
332 CloseButton.Clicked -= OnCloseButtonClicked;
333 }
334
335 base.Dispose();
336 GC.SuppressFinalize(this);
337 }
338
339 #endregion exposed methods and hooks
340
341 #region private methods
342
343 private void CompleteInitialization()
344 {
345 CanReceiveFocus = true;
348 }
349
350 private void OnCloseButtonClicked()
351 {
352 Close(DialogResult.Cancel);
353 }
354
355 private static void ValidateBounds(Rectangle bounds)
356 {
357 if (bounds.Width <= 0 || bounds.Height <= DefaultTitleBarHeight)
358 {
359 throw new ArgumentOutOfRangeException(
360 nameof(bounds),
361 bounds,
362 "Dialog bounds must have positive width and enough height for the title bar.");
363 }
364 }
365
366 private static Vector2 GetCloseButtonOffset(Size size)
367 {
368 return new Vector2(size.Width - DefaultCloseButtonSize - 4, 4);
369 }
370
371 private static DirectRectangle CreatePanel(RenderSurfaceHostBase host,
372 View view,
373 Rectangle bounds)
374 {
375 return ConfigurePanel(new DirectRectangle(DefaultPanelColor,
376 host,
377 view,
378 bounds));
379 }
380
381 private static DirectRectangle CreatePanel(RenderSurfaceHostBase host,
382 SceneLayer layer,
383 Rectangle bounds)
384 {
385 return ConfigurePanel(new DirectRectangle(DefaultPanelColor,
386 host,
387 layer,
388 bounds));
389 }
390
391 private static DirectRectangle ConfigurePanel(DirectRectangle panel)
392 {
393 panel.SetFilled(true)
395 .SetStrokeWidth(2f)
396 .SetCornerRadius(8f);
397
398 panel.ZOrder = 10_000;
399
400 return panel;
401 }
402
403 private static DirectRectangle CreateTitleBar(RenderSurfaceHostBase host,
404 View view,
405 Rectangle bounds)
406 {
407 return ConfigureTitleBar(new DirectRectangle(DefaultTitleBarColor,
408 host,
409 view,
410 GetTitleBarBounds(bounds)));
411 }
412
413 private static DirectRectangle CreateTitleBar(RenderSurfaceHostBase host,
414 SceneLayer layer,
415 Rectangle bounds)
416 {
417 return ConfigureTitleBar(new DirectRectangle(DefaultTitleBarColor,
418 host,
419 layer,
420 GetTitleBarBounds(bounds)));
421 }
422
423 private static DirectRectangle ConfigureTitleBar(DirectRectangle titleBar)
424 {
425 titleBar.SetFilled(true)
426 .SetCornerRadius(8f);
427
428 titleBar.ZOrder = 10_001;
429
430 return titleBar;
431 }
432
433 private static TextBlock CreateTitleText(RenderSurfaceHostBase host,
434 View view,
435 Rectangle bounds,
436 string title)
437 {
438 return ConfigureTitleText(new TextBlock(host, view, GetTitleTextBounds(bounds)), title);
439 }
440
441 private static TextBlock CreateTitleText(RenderSurfaceHostBase host,
442 SceneLayer layer,
443 Rectangle bounds,
444 string title)
445 {
446 return ConfigureTitleText(new TextBlock(host, layer, view: null, worldBounds: GetTitleTextBounds(bounds)), title);
447 }
448
449 private static TextBlock ConfigureTitleText(TextBlock titleText, string title)
450 {
451 titleText.SetText(title)
452 .SetFont(SKTypeface.Default, 18f, minSize: 12f)
453 .SetColors(SKColors.White, SKColors.Transparent)
454 .SetAlignment(SKTextAlign.Left, TextBlock.VerticalAlign.Center)
455 .EnableWrapping(false);
456
457 titleText.HorizontalPadding = 12f;
458 titleText.ZOrder = 10_002;
459
460 return titleText;
461 }
462
463 private static ButtonWidget CreateCloseButton(RenderSurfaceHostBase host,
464 View view,
465 Rectangle bounds)
466 {
467 return ConfigureCloseButton(new ButtonWidget(host, view, GetCloseButtonBounds(bounds), "×"));
468 }
469
470 private static ButtonWidget CreateCloseButton(RenderSurfaceHostBase host,
471 SceneLayer layer,
472 Rectangle bounds)
473 {
474 return ConfigureCloseButton(new ButtonWidget(host, layer, GetCloseButtonBounds(bounds), "×"));
475 }
476
477 private static ButtonWidget ConfigureCloseButton(ButtonWidget closeButton)
478 {
479 closeButton.SetBackgroundColors(Color.FromArgb(0, 0, 0, 0),
480 Color.FromArgb(255, 110, 55, 62),
481 Color.FromArgb(255, 145, 45, 55))
482 .SetTextColor(Color.White)
483 .SetButtonZOrder(10_003);
484
485 return closeButton;
486 }
487
488 private static Rectangle GetTitleBarBounds(Rectangle bounds)
489 {
490 return new Rectangle(bounds.X, bounds.Y, bounds.Width, DefaultTitleBarHeight);
491 }
492
493 private static Rectangle GetTitleTextBounds(
494 Rectangle bounds)
495 {
496 return new Rectangle(bounds.X, bounds.Y, bounds.Width - DefaultCloseButtonSize - 12, DefaultTitleBarHeight);
497 }
498
499 private static Rectangle GetCloseButtonBounds(
500 Rectangle bounds)
501 {
502 return new Rectangle(bounds.Right - DefaultCloseButtonSize - 4, bounds.Y + 4, DefaultCloseButtonSize, DefaultCloseButtonSize);
503 }
504
505 #endregion private methods
506}
SceneLayer? SceneLayer
Gets the scene layer shared by all children in scene-layer mode.
View? View
Gets the view shared by all children in view mode.
Provides the base implementation for direct drawing objects that can be rendered to the backbuffer wi...
Renders a configurable rectangle with support for fill, border, rounded corners, dash patterns,...
DirectRectangle SetFilled(bool isFilled)
Sets whether the rectangle interior is filled with the base color (or pattern).
DirectRectangle SetBorderColor(Color color)
Sets a distinct border (stroke) color, independent of the fill color.
DirectRectangle SetStrokeWidth(float width)
Sets the width (thickness) of the rectangle's stroke (outline) in pixels.
DirectRectangle SetCornerRadius(float radius)
Sets the radius for rounded corners in pixels.
A retained-mode text drawable with wrapping, multi-line ( ) support, horizontal/vertical alignment,...
Definition TextBlock.cs:44
TextBlock SetFont(SKTypeface typeface, float size, float? minSize=null)
Sets the font face and size (and optional minimum size for auto-shrink).
Definition TextBlock.cs:394
TextBlock EnableWrapping(bool enable=true)
Enables or disables word wrapping when laying out paragraphs.
Definition TextBlock.cs:503
TextBlock SetAlignment(SKTextAlign h, VerticalAlign v)
Sets the horizontal and vertical alignment used when drawing the laid-out lines within the bounds.
Definition TextBlock.cs:433
TextBlock SetColors(SKColor fg, SKColor bg)
Sets the foreground (text) and background colors using Skia colors.
Definition TextBlock.cs:410
TextBlock SetText(string text)
Sets the current text content and rebuilds layout as needed. If no reveal animation is active,...
Definition TextBlock.cs:358
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
Viewport Viewport
Gets the viewport that defines the screen-space rectangle, zoom level, and other rendering parameters...
Definition View.cs:44
float Zoom
Zoom factor applied to the world when rendering this view. Values greater than 1 zoom in,...
Definition Viewport.cs:67
override DirectComposite Add(IDirectCompositeChild child, bool keepCurrentOffset=true, Vector2? explicitLocalOffsetPx=null)
Adds a composite-compatible direct drawable as a child.The current composite.
Represents a basic text button composed from a rectangle and text block.
ButtonWidget SetTextColor(Color color)
Sets the button text color.
ButtonWidget SetBackgroundColors(Color normal, Color hover, Color pressed)
Sets the normal, hover, and pressed background colors.
ButtonWidget SetButtonZOrder(int zOrder)
Sets the background Z-order and places the label immediately above it.
const int DefaultTitleBarHeight
The default title bar height.
Definition DialogBox.cs:22
DialogResult Result
Gets the result selected when the dialog closed.
Definition DialogBox.cs:173
override void OnKeyboardInput(WidgetKeyboardEventArgs args)
Called when keyboard input is routed to the widget.
Definition DialogBox.cs:294
override void ProcessActivated()
Performs processing when the widget is activated before notification callbacks are invoked.
Definition DialogBox.cs:238
static readonly Color DefaultPanelBorderColor
The default panel border color.
Definition DialogBox.cs:35
ButtonWidget? CloseButton
Gets the optional close button.
Definition DialogBox.cs:158
DialogBox(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle bounds, string title, bool showCloseButton=true, string? nickname=null)
Initializes a scene-layer dialog.
Definition DialogBox.cs:105
DialogBox(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle bounds, string title, bool showCloseButton=true, string? nickname=null)
Initializes a view-level dialog.
Definition DialogBox.cs:65
static readonly Color DefaultTitleBarColor
The default title bar color.
Definition DialogBox.cs:39
virtual void OnAcceptRequested()
Called when the accept key is pressed.
Definition DialogBox.cs:232
Size DialogSize
Gets the dialog size.
Definition DialogBox.cs:163
static readonly Color DefaultPanelColor
The default panel color.
Definition DialogBox.cs:31
int AcceptKey
Gets or sets the key value used to accept the dialog.
Definition DialogBox.cs:186
const int DefaultCloseButtonSize
The default close button size.
Definition DialogBox.cs:26
bool DisposeOnClose
Gets or sets whether closing the dialog disposes it.
Definition DialogBox.cs:178
void Close(DialogResult result=DialogResult.Close)
Closes the dialog with the specified result.
Definition DialogBox.cs:204
virtual void OnClosed(DialogResult result)
Called after the dialog closes.
Definition DialogBox.cs:225
override bool CanStartDrag(WidgetPointerEventArgs args)
Determines whether a pointer-down event may begin drag tracking.true if the pointer event can begin a...
Definition DialogBox.cs:282
override void Dispose()
Disposes the composite and every child it owns.
Definition DialogBox.cs:323
bool IsClosed
Gets whether the dialog has closed.
Definition DialogBox.cs:168
DirectRectangle Panel
Gets the dialog panel.
Definition DialogBox.cs:143
Action< DialogResult >? Closed
Occurs when the dialog closes.
Definition DialogBox.cs:52
TextBlock TitleText
Gets the title text block.
Definition DialogBox.cs:153
DirectRectangle TitleBar
Gets the title-bar rectangle.
Definition DialogBox.cs:148
override void OnCancelled()
Called when the widget is cancelled.
Definition DialogBox.cs:316
int CancelKey
Gets or sets the key value used to cancel the dialog.
Definition DialogBox.cs:194
DraggableContainerWidget(RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, PointF anchor=default, string? nickname=null)
Initializes a new instance of the DraggableContainerWidget class.
bool CanReceiveFocus
Gets or sets a value indicating whether the widget can receive keyboard focus.
bool IsPointerInputEnabled
Gets or sets a value indicating whether the widget can receive pointer input.
WidgetBase Hide()
Hides the widget and releases any routed input state.
bool IsKeyboardInputEnabled
Gets or sets a value indicating whether the widget can receive keyboard input.
bool Handled
Gets or sets whether this widget interaction was handled.
Provides data for widget keyboard input events routed by WidgetInputRouter.
int Key
Gets the integer key identifier.
KeyAction KeyAction
Gets the action associated with this key event.
Event arguments for pointer-based widget callbacks.
PointF WrappedOffsetWorldPx
World-space translation of the selected periodic widget instance. Retained throughout pointer capture...
View View
Gets the view through which the pointer interaction was routed.
PointF ScreenPositionPx
Gets the pointer position in screen pixels.
Defines a movable direct drawable that may be owned by a DirectComposite.
Defines a container that can hold and manage a collection of direct composite children.
IReadOnlyCollection< IDirectCompositeChild > Children
Gets the read-only collection of child elements in this container.
@ Rectangle
Particles are spawned uniformly within a rectangular area. This is the default distribution.
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
KeyAction
Represents the type of action performed on a keyboard key, indicating whether the key was pressed,...
Definition KeyAction.cs:10
DialogResult
Represents the result produced when a dialog closes.
@ Close
The dialog was closed without a more specific result.