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
ConversationBox.cs
1using System.Drawing;
7using SkiaSharp;
8
10
15public sealed class ConversationBox : WidgetBase
16{
17 private const int SpeakerHeight = 30;
18 private const int ContinueIndicatorWidth = 28;
19 private const int ContinueIndicatorHeight = 22;
20 private const int ContentInset = 10;
21
22 private string _speaker;
23 private string _text;
24 private bool _continueIndicatorVisible = true;
25
29 public event Action? AdvanceRequested;
30
40 public ConversationBox(RenderSurfaceHostBase renderSurfaceHost,
41 View view,
42 Rectangle bounds,
43 string speaker,
44 string text,
45 string? nickname = null)
46 : base(renderSurfaceHost, DirectDrawingMode.View, ValidateBounds(bounds).Location, nickname)
47 {
48 ArgumentNullException.ThrowIfNull(view);
49
50 _speaker = speaker ?? string.Empty;
51 _text = text ?? string.Empty;
52
53 Panel = CreatePanel(renderSurfaceHost, view, bounds);
54 SpeakerText = CreateSpeakerText(renderSurfaceHost, view, GetSpeakerBounds(bounds), _speaker);
55 BodyText = CreateBodyText(renderSurfaceHost, view, GetBodyBounds(bounds), _text);
56 ContinueIndicator = CreateContinueIndicator(renderSurfaceHost, view, GetContinueBounds(bounds));
57
58 Add(Panel);
62
63 CompleteInitialization();
64 }
65
75 public ConversationBox(RenderSurfaceHostBase renderSurfaceHost,
76 SceneLayer sceneLayer,
77 Rectangle bounds,
78 string speaker,
79 string text,
80 string? nickname = null)
81 : base(renderSurfaceHost, DirectDrawingMode.SceneLayer, ValidateBounds(bounds).Location, nickname)
82 {
83 ArgumentNullException.ThrowIfNull(sceneLayer);
84
85 _speaker = speaker ?? string.Empty;
86 _text = text ?? string.Empty;
87
88 Panel = CreatePanel(renderSurfaceHost, sceneLayer, bounds);
89 SpeakerText = CreateSpeakerText(renderSurfaceHost, sceneLayer, GetSpeakerBounds(bounds), _speaker);
90 BodyText = CreateBodyText(renderSurfaceHost, sceneLayer, GetBodyBounds(bounds), _text);
91 ContinueIndicator = CreateContinueIndicator(renderSurfaceHost, sceneLayer, GetContinueBounds(bounds));
92
93 Add(Panel);
97
98 CompleteInitialization();
99 }
100
104 public DirectRectangle Panel { get; }
105
109 public TextBlock SpeakerText { get; }
110
114 public TextBlock BodyText { get; }
115
120
124 public string Speaker => _speaker;
125
129 public string Text => _text;
130
134 public Rectangle Bounds => Mode == DirectDrawingMode.View
135 ? Panel.ScreenBounds
136 : Panel.WorldBounds;
137
142 public int PrimaryAdvanceKey { get; set; } = 13;
143
148 public int SecondaryAdvanceKey { get; set; } = 32;
149
156 public ConversationBox SetConversation(string speaker, string text)
157 {
158 SetSpeaker(speaker);
159 SetText(text);
160 return this;
161 }
162
168 public ConversationBox SetSpeaker(string speaker)
169 {
170 _speaker = speaker ?? string.Empty;
171 SpeakerText.SetText(_speaker);
172 return this;
173 }
174
180 public ConversationBox SetText(string text)
181 {
182 _text = text ?? string.Empty;
183 BodyText.SetText(_text);
184 return this;
185 }
186
192 public ConversationBox ShowContinueIndicator(bool visible = true)
193 {
194 _continueIndicatorVisible = visible;
195 ContinueIndicator.Visible = visible;
196 return this;
197 }
198
205 public ConversationBox SetTextColors(SKColor speakerColor, SKColor bodyColor)
206 {
207 SpeakerText.SetColors(speakerColor, SKColors.Transparent);
208 BodyText.SetColors(bodyColor, SKColors.Transparent);
209 return this;
210 }
211
218 public ConversationBox SetPanelColors(Color fill, Color border)
219 {
220 Panel.SetColor(fill)
221 .SetBorderColor(border);
222 Panel.SetPosition(Panel.GetPosition());
223 return this;
224 }
225
232 {
233 Panel.ZOrder = zOrder;
234 SpeakerText.ZOrder = zOrder + 1;
235 BodyText.ZOrder = zOrder + 1;
236 ContinueIndicator.ZOrder = zOrder + 2;
237 return this;
238 }
239
243 public void Advance()
244 {
245 if (!IsInputEnabled)
246 return;
247
248 AdvanceRequested?.Invoke();
249 }
250
252 protected override void ProcessShown()
253 {
254 base.ProcessShown();
255 ContinueIndicator.Visible = _continueIndicatorVisible;
256 }
257
259 protected override void OnPointerClick(WidgetPointerEventArgs args)
260 {
261 base.OnPointerClick(args);
262
263 if (!args.IsPrimaryButton)
264 return;
265
266 args.Handled = true;
267 Advance();
268 }
269
271 protected override void OnKeyboardInput(WidgetKeyboardEventArgs args)
272 {
273 base.OnKeyboardInput(args);
274
275 if (args.KeyAction != KeyAction.Pressed ||
276 args.Key != PrimaryAdvanceKey && args.Key != SecondaryAdvanceKey)
277 {
278 return;
279 }
280
281 args.Handled = true;
282 Advance();
283 }
284
285 private void CompleteInitialization()
286 {
287 CanReceiveFocus = true;
290 }
291
292 private static DirectRectangle CreatePanel(RenderSurfaceHostBase host,
293 View view,
294 Rectangle bounds)
295 {
296 return ConfigurePanel(new DirectRectangle(Color.FromArgb(238, 24, 25, 32), host, view, bounds));
297 }
298
299 private static DirectRectangle CreatePanel(RenderSurfaceHostBase host,
300 SceneLayer layer,
301 Rectangle bounds)
302 {
303 return ConfigurePanel(new DirectRectangle(Color.FromArgb(238, 24, 25, 32), host, layer, bounds));
304 }
305
306 private static DirectRectangle ConfigurePanel(DirectRectangle panel)
307 {
308 return panel.SetFilled(true)
309 .SetBorderColor(Color.FromArgb(245, 205, 205, 215))
310 .SetStrokeWidth(2f)
311 .SetCornerRadius(7f);
312 }
313
314 private static TextBlock CreateSpeakerText(RenderSurfaceHostBase host,
315 View view,
316 Rectangle bounds,
317 string speaker)
318 {
319 return ConfigureSpeakerText(new TextBlock(host, view, bounds), speaker);
320 }
321
322 private static TextBlock CreateSpeakerText(RenderSurfaceHostBase host,
323 SceneLayer layer,
324 Rectangle bounds,
325 string speaker)
326 {
327 return ConfigureSpeakerText(new TextBlock(host, layer, view: null, worldBounds: bounds), speaker);
328 }
329
330 private static TextBlock ConfigureSpeakerText(TextBlock textBlock, string speaker)
331 {
332 return textBlock.SetText(speaker)
333 .SetFont(SKTypeface.Default, 17f, minSize: 11f)
334 .SetColors(new SKColor(255, 224, 145), SKColors.Transparent)
335 .SetAlignment(SKTextAlign.Left, TextBlock.VerticalAlign.Center)
336 .SetPadding(2f, 0f)
337 .EnableWrapping(false);
338 }
339
340 private static TextBlock CreateBodyText(RenderSurfaceHostBase host,
341 View view,
342 Rectangle bounds,
343 string text)
344 {
345 return ConfigureBodyText(new TextBlock(host, view, bounds), text);
346 }
347
348 private static TextBlock CreateBodyText(RenderSurfaceHostBase host,
349 SceneLayer layer,
350 Rectangle bounds,
351 string text)
352 {
353 return ConfigureBodyText(new TextBlock(host, layer, view: null, worldBounds: bounds), text);
354 }
355
356 private static TextBlock ConfigureBodyText(TextBlock textBlock, string text)
357 {
358 return textBlock.SetText(text)
359 .SetFont(SKTypeface.Default, 16f, minSize: 10f)
360 .SetColors(SKColors.White, SKColors.Transparent)
361 .SetAlignment(SKTextAlign.Left, TextBlock.VerticalAlign.Top)
362 .SetPadding(2f, 2f)
363 .EnableWrapping(true);
364 }
365
366 private static TextBlock CreateContinueIndicator(RenderSurfaceHostBase host,
367 View view,
368 Rectangle bounds)
369 {
370 return ConfigureContinueIndicator(new TextBlock(host, view, bounds));
371 }
372
373 private static TextBlock CreateContinueIndicator(RenderSurfaceHostBase host,
374 SceneLayer layer,
375 Rectangle bounds)
376 {
377 return ConfigureContinueIndicator(new TextBlock(host, layer, view: null, worldBounds: bounds));
378 }
379
380 private static TextBlock ConfigureContinueIndicator(TextBlock textBlock)
381 {
382 return textBlock.SetText("▼")
383 .SetFont(SKTypeface.Default, 14f, minSize: 9f)
384 .SetColors(SKColors.White, SKColors.Transparent)
385 .SetAlignment(SKTextAlign.Center, TextBlock.VerticalAlign.Center)
386 .EnableWrapping(false);
387 }
388
389 private static Rectangle GetSpeakerBounds(Rectangle bounds)
390 {
391 return new Rectangle(bounds.X + ContentInset,
392 bounds.Y + 4,
393 Math.Max(1, bounds.Width - ContentInset * 2),
394 SpeakerHeight);
395 }
396
397 private static Rectangle GetBodyBounds(Rectangle bounds)
398 {
399 int top = bounds.Y + SpeakerHeight + 4;
400 int bottom = bounds.Bottom - ContinueIndicatorHeight - 4;
401 return new Rectangle(bounds.X + ContentInset,
402 top,
403 Math.Max(1, bounds.Width - ContentInset * 2),
404 Math.Max(1, bottom - top));
405 }
406
407 private static Rectangle GetContinueBounds(Rectangle bounds)
408 {
409 return new Rectangle(bounds.Right - ContentInset - ContinueIndicatorWidth,
410 bounds.Bottom - ContinueIndicatorHeight - 4,
411 ContinueIndicatorWidth,
412 ContinueIndicatorHeight);
413 }
414
415 private static Rectangle ValidateBounds(Rectangle bounds)
416 {
417 if (bounds.Width < 120 || bounds.Height < SpeakerHeight + ContinueIndicatorHeight + 24)
418 throw new ArgumentOutOfRangeException(nameof(bounds), bounds, "Conversation-box bounds are too small.");
419
420 return bounds;
421 }
422}
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.
virtual DirectComposite Add(IDirectCompositeChild child, bool keepCurrentOffset=true, Vector2? explicitLocalOffsetPx=null)
Adds a composite-compatible direct drawable as a child.
DirectDrawingMode Mode
Gets the coordinate mode shared by the composite and its children.
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 SetPadding(float horizontal, float vertical)
Sets symmetric horizontal and vertical padding and invalidates cached layout.
Definition TextBlock.cs:317
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 ...
Represents a rendered view of a scene, combining a camera position with a viewport configuration to c...
Definition View.cs:14
Represents a single layer within a Scene, containing a grid of tiles and supporting various coordinat...
Definition SceneLayer.cs:40
ConversationBox SetText(string text)
Changes the conversation body text.
DirectRectangle Panel
Gets the panel background.
override void ProcessShown()
Performs processing when the widget is shown before notification callbacks are invoked.
ConversationBox(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle bounds, string speaker, string text, string? nickname=null)
Creates a scene-layer conversation box.
void Advance()
Programmatically requests the next conversation step.
string Text
Gets the current conversation text.
ConversationBox ShowContinueIndicator(bool visible=true)
Shows or hides the continue indicator.
ConversationBox SetConversationZOrder(int zOrder)
Sets the base Z-order used by the conversation visuals.
ConversationBox SetPanelColors(Color fill, Color border)
Sets the panel fill and border colors.
ConversationBox(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle bounds, string speaker, string text, string? nickname=null)
Creates a view-level conversation box.
ConversationBox SetTextColors(SKColor speakerColor, SKColor bodyColor)
Sets the speaker and body text colors.
TextBlock BodyText
Gets the conversation body text block.
Rectangle Bounds
Gets the conversation bounds in its native coordinate space.
TextBlock SpeakerText
Gets the speaker-name text block.
ConversationBox SetSpeaker(string speaker)
Changes the speaker name.
TextBlock ContinueIndicator
Gets the small visual indicating that the conversation can advance.
override void OnKeyboardInput(WidgetKeyboardEventArgs args)
Called when keyboard input is routed to the widget.
int SecondaryAdvanceKey
Gets or sets the secondary key used to request the next conversation step. Defaults to Space (32).
int PrimaryAdvanceKey
Gets or sets the primary key used to request the next conversation step. Defaults to Enter (13).
ConversationBox SetConversation(string speaker, string text)
Changes the speaker and body text together.
string Speaker
Gets the current speaker name.
Action? AdvanceRequested
Occurs when the player requests the next line or conversation step.
override void OnPointerClick(WidgetPointerEventArgs args)
Called when a pointer click is dispatched to the widget.
bool CanReceiveFocus
Gets or sets a value indicating whether the widget can receive keyboard focus.
WidgetBase(RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, PointF anchor=default, string? nickname=null)
Initializes a new instance of the WidgetBase class.
Definition WidgetBase.cs:95
bool IsKeyboardInputEnabled
Gets or sets a value indicating whether the widget can receive keyboard input.
bool IsInputEnabled
Gets or sets a value indicating whether the widget can participate in input routing.
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.
bool IsPrimaryButton
Gets whether the pointer event represents a primary-button interaction.
@ 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.
@ View
Performs direct drawing at the view level using screen-space coordinates.
KeyAction
Represents the type of action performed on a keyboard key, indicating whether the key was pressed,...
Definition KeyAction.cs:10