There might be an easier way to do this, but I’m creating buttons in a for loop, and adding the widgets to a [widget]int map, and the OnClick() task to an []awaitable(widget_message) array.
I don’t know how I would await the tasks though. Completely expecting a compiler error I tried race(T:Tasks){T.Await()} which surprisingly didn’t give me any error messages, but when saving, the editor crashed, and crashes on startup unless I comment out the code.
Code Example
CreateUI(P:player, ButtonCount:int)<suspends>:int=
Canvas := canvas{}
var Tasks : []awaitable(widget_message) = array{}
var ButtonMap : [widget]int = map{}
if(PlayerUI:=GetPlayerUI[P]):
for(I:=0..ButtonCount-1):
Button := canvas_slot:
Anchors := anchors:
Minimum:= vector2:
X:=0.1+0.05*I
Y:=0.25
Maximum:= vector2:
X:=0.1+0.05*I
Y:=0.25
Widget := button_regular{}
if(W:=button_regular[Button.Widget], set ButtonMap[W]=I):
set Tasks += array{W.OnClick()}
Canvas.AddWidget(Button)
PlayerUI.AddWidget(Canvas, player_ui_slot{InputMode:=ui_input_mode.All})
#It would be really great if the following line worked. It doesn't give any errors in vscode, but the editor crashes
WM:=race(T:Tasks):
T.Await()
if(ButtonID:=ButtonMap[WM.Source]):
return ButtonID
return -1
I could make the Tasks array a tuple instead and do race{Tasks(0).Await(), Task(1).Await(), Task(2)...etc}, but then I would have to hardcode it, and couldn’t change how many buttons I create at runtime.
Is there a way to work around this issue currently?
Still don’t know why the editor crashes if I type race(T:Type){T.Await()}, but I figured out how to do it without an array of awaitables
Still not 100% happy with my solution, but this works:
ToMessage<localizes>(S:string):message="{S}"
MyUI := class:
var Buttons : []button_regular = array{}
Player : player
#Is there any way to put this inside AwaitButtons()?
AwaitThenSignal(A:awaitable(widget_message), S:signalable(int), I:int)<suspends>:void=
A.Await()
S.Signal(I)
#Returns the index of the pressed button
AwaitButtons()<suspends>:int=
Event := event(int){}
for(I->B:Buttons):
spawn{AwaitThenSignal(B.OnClick(), Event, I)}
return Event.Await()
CreateUI(Labels:[]string):void=
if(PlayerUI := GetPlayerUI[Player]):
for(I->L:Labels):
Button := canvas_slot:
Anchors :=anchor:
Minimum := vector2{X:=0.5, Y:=0.25+I*0.1}
Maximum := vector2{X:=0.5, Y:=0.25+I*0.1}
Widget := button_regular{DefaultText:=ToMessage(L)}
Canvas.AddWidget(Button)
if(BW := button_regular[Button.Widget]):
set Buttons += array{BW}
PlayerUI.AddWidget(Canvas, player_ui_slot{InputMode:=ui_input_mode.All})
Hmm, seems like it should work, but it might be buggy somehow since I imagine it hasn’t been used a lot yet. Have you tried iterating using regular integers instead, like race(I := 0..Tasks.Length-1)?
The intent is that race(T:Tasks){T.Await()} will eventually do what expected, but it’s only partially implemented right now. The compiler front end accepts it, but then the back end produces a compile error as the codegen part hasn’t been implemented yet.
Unfortunately, there’s a bug in 24.30 that leads to back end compile errors crashing UEFN. I’ve committed a fix that should be in the next release.