Skip to content

Commit 16c1f9d

Browse files
committed
asynchronicity improvements for ui system
- Added UiSystem.EnqueueAction to do work synchronously next time the ui system updates - Fixed TextField EnterReceiver not allowing element addition or removal in
1 parent 430424f commit 16c1f9d

File tree

3 files changed

+25
-2
lines changed

3 files changed

+25
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Additions
2929
- Added the ability to set a selected texture and color for buttons
3030
- Added the ability for tooltips to snap to a specified selection-independent element
3131
- Added Image.UseImagePivot to allow disabling texture region pivot being taken into account
32+
- Added UiSystem.EnqueueAction to do work synchronously next time the ui system updates
3233

3334
Improvements
3435
- Explicitly return the element type from Dropdown.AddElement overloads
@@ -41,6 +42,7 @@ Fixes
4142
- Fixed UiParser ImageExceptionHandler being ignored when an exception occurs during texture construction
4243
- Fixed images displaying the initial texture when the texture callback starts returning null
4344
- Fixed UiStyle copy constructor not copying all style properties
45+
- Fixed TextField EnterReceiver not allowing element addition or removal in OnPressed
4446

4547
Removals
4648
- Marked RootElement.OnAddedToUi as obsolete in favor of UiSystem.OnRootAdded

MLEM.Ui/Elements/TextField.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,10 @@ public TextField(Anchor anchor, Vector2 size, Rule rule = null, GenericFont font
209209
}
210210
};
211211
this.OnTextInput += (element, key, character) => {
212-
if (this.IsSelectedActive && !this.IsHidden && !this.textInput.OnTextInput(key, character) && key == Keys.Enter && !this.Multiline)
213-
this.InvokeOnEnter();
212+
if (this.IsSelectedActive && !this.IsHidden && !this.textInput.OnTextInput(key, character) && key == Keys.Enter && !this.Multiline) {
213+
// enqueue the press to be executed next update since all other presses are executed in the regular update loop
214+
this.System.EnqueueAction((_, t) => ((TextField) t).InvokeOnEnter(), this);
215+
}
214216
};
215217
}
216218

MLEM.Ui/UiSystem.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ public UiStyle Style {
169169
/// </summary>
170170
public event RootCallback OnRootRemoved;
171171

172+
private readonly Queue<(Action<UiSystem, object> Action, object ActionObject)> nextUpdateActions = new Queue<(Action<UiSystem, object> Action, object ActionObject)>();
173+
private readonly object nextUpdateActionsLock = new object();
172174
private readonly List<RootElement> rootElements = new List<RootElement>();
173175
private readonly Stopwatch stopwatch = new Stopwatch();
174176
private float globalScale = 1;
@@ -249,6 +251,12 @@ public override void Update(GameTime time) {
249251
this.stopwatch.Restart();
250252

251253
this.Controls.Update();
254+
lock (this.nextUpdateActionsLock) {
255+
while (this.nextUpdateActions.Count > 0) {
256+
var next = this.nextUpdateActions.Dequeue();
257+
next.Action.Invoke(this, next.ActionObject);
258+
}
259+
}
252260
for (var i = this.rootElements.Count - 1; i >= 0; i--)
253261
this.rootElements[i].Element.Update(time);
254262

@@ -332,6 +340,17 @@ public RootElement Get(string name) {
332340
return index < 0 ? null : this.rootElements[index];
333341
}
334342

343+
/// <summary>
344+
/// Enqueues the given <paramref name="action"/> (optionally with the given <paramref name="actionObject"/> attached) to be executed the next time <see cref="Update"/> is called, before all of this ui system's elements are updated.
345+
/// This method is thread-safe, meaning actions can be enqueued from other threads (for example, after downloading content to display) and they will be executed on the main thread.
346+
/// </summary>
347+
/// <param name="action">The action to enqueue.</param>
348+
/// <param name="actionObject">An optional object to attach to the action, which will be passed as the second parameter to <paramref name="action"/>.</param>
349+
public void EnqueueAction(Action<UiSystem, object> action, object actionObject = null) {
350+
lock (this.nextUpdateActionsLock)
351+
this.nextUpdateActions.Enqueue((action, actionObject));
352+
}
353+
335354
private int IndexOf(string name) {
336355
return this.rootElements.FindIndex(element => element.Name == name);
337356
}

0 commit comments

Comments
 (0)