cobuura
(babanimoaa)
May 10, 2026, 9:38pm
1
Hello guys i have a custom ui where i assembled a button and scrollbox widget
When player click on a button it shows the corresponding text with a scrollbox, the interaction with the buttons works perfectly but i cant get any interaction with the scrollbox using a controller
It works with my keyboard but not with the controller
Thanks and have a nice day
kkintaro
(kintaro)
May 10, 2026, 9:53pm
2
Hey! To add controller support enable this in your widget.
cobuura
(babanimoaa)
May 15, 2026, 6:51pm
3
Thanks for your answer !
Unfortunately it doesnt work, when i click on my button, the text shows up but its not scrollable
tldr_hoverable_button := class:
Widget : Resume_Button = Resume_Button{}
CloseEvent<public> : event()
var Owner<public> : ?player_rulebook_canvas = false
GetButton<public>() : button =
B := button:
Slot := button_slot:
HorizontalAlignment := horizontal_alignment.Fill
VerticalAlignment := vertical_alignment.Fill
Widget := Widget
spawn{HandleButton(B)}
B
HandleButton(B : button)<suspends> : void =
race:
CloseEvent.Await()
loop:
B.HighlightEvent().Await()
if (Canvas := Owner?):
Canvas.ResetAllHoverStates()
set Widget.IsHovering = true
loop:
B.UnhighlightEvent().Await()
set Widget.IsHovering = false
bonus_hoverable_button := class:
Widget : Bonus_Button = Bonus_Button{}
CloseEvent<public> : event()
var Owner<public> : ?player_rulebook_canvas = false
GetButton<public>() : button =
B := button:
Slot := button_slot:
HorizontalAlignment := horizontal_alignment.Fill
VerticalAlignment := vertical_alignment.Fill
Widget := Widget
spawn{HandleButton(B)}
B
HandleButton(B : button)<suspends> : void =
race:
CloseEvent.Await()
loop:
B.HighlightEvent().Await()
if (Canvas := Owner?):
Canvas.ResetAllHoverStates()
set Widget.IsHovering = true
loop:
B.UnhighlightEvent().Await()
set Widget.IsHovering = false
rulebook_quit_hoverable_button := class:
Widget : QUIT_Button = QUIT_Button{}
CloseEvent<public> : event()
GetButton<public>() : button =
B := button:
Slot := button_slot:
HorizontalAlignment := horizontal_alignment.Fill
VerticalAlignment := vertical_alignment.Fill
Widget := Widget
spawn{HandleButton(B)}
B
HandleButton(B : button)<suspends> : void =
race:
CloseEvent.Await()
loop:
B.HighlightEvent().Await()
set Widget.isHovering = true
loop:
B.UnhighlightEvent().Await()
set Widget.isHovering = false
rulebook_category<public> := enum:
TLDR
Bonus
rulebook_ui_device<public> := class(creative_device):
@editable
RulebookOpenTrigger : input_trigger_device = input_trigger_device{}
@editable
RulebookCloseTrigger : input_trigger_device = input_trigger_device{}
@editable
RulebookBackgroundTexture : ?texture = false
@editable
BackgroundMaterial : ?material = false
@editable
BackgroundColor : color = color{R := 0.0, G := 0.0, B := 0.0}
@editable
BorderColorStyle : color = color{R := 0.502, G := 0.0, B := 0.502}
var PlayerCanvasMap : [player]player_rulebook_canvas = map{}
OnBegin<override>()<suspends> : void =
Print("=== Rulebook System Initializing ===")
RulebookOpenTrigger.PressedEvent.Subscribe(OnOpenPressed)
RulebookCloseTrigger.PressedEvent.Subscribe(OnClosePressed)
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
for (Player : GetPlayspace().GetPlayers()):
if (Agent := agent[Player]):
RulebookOpenTrigger.Register(Agent)
RulebookCloseTrigger.Register(Agent)
Print("=== Rulebook System Ready ===")
# === EVENT HANDLERS ===
OnPlayerAdded(Player : player) : void =
if (Agent := agent[Player]):
RulebookOpenTrigger.Register(Agent)
RulebookCloseTrigger.Register(Agent)
OnPlayerRemoved(Player : player) : void =
if (Canvas := PlayerCanvasMap[Player]):
Canvas.CloseRulebook()
var NewMap : [player]player_rulebook_canvas = map{}
for (Key -> Value : PlayerCanvasMap, Key <> Player):
if (set NewMap[Key] = Value) {}
set PlayerCanvasMap = NewMap
OnOpenPressed(Agent : agent) : void =
if (Player := player[Agent]):
if (Canvas := PlayerCanvasMap[Player]):
Canvas.OpenRulebook()
else:
NewCanvas := player_rulebook_canvas{
Player := Player
RulebookDevice := Self
}
if (set PlayerCanvasMap[Player] = NewCanvas):
NewCanvas.OpenRulebook()
OnClosePressed(Agent : agent) : void =
if (Player := player[Agent]):
if (Canvas := PlayerCanvasMap[Player]):
Canvas.CloseRulebook()
player_rulebook_canvas<public> := class:
Player<public> : player
RulebookDevice<public> : rulebook_ui_device
var MainCanvas : ?canvas = false
var MainOverlay : ?overlay = false
var TextContainer : ?overlay = false
var ActiveTextWidget : ?widget = false
var SidebarWidget : ?widget = false
var CurrentCategory : rulebook_category = rulebook_category.TLDR
var CategoryButtons : [rulebook_category]button = map{}
var CloseBtn : ?button = false
var IsOpen : logic = false
ButtonCloseEvent : event() = event(){}
# Button widgets from Regles module — toggled via Is[X]Clicked
TLDRBtnWidget : Resume_Button = Resume_Button{}
BonusBtnWidget : Bonus_Button = Bonus_Button{}
QuitBtnWidget : QUIT_Button = QUIT_Button{}
# Text widgets from Regles module — toggled via Show[X]Text
TLDRTextWidget : TLDR_Text = TLDR_Text{}
BonusTextWidget : Bonus_Text = Bonus_Text{}
RoadmapTextWidget : Roadmap_Text = Roadmap_Text{}
ToggleRulebook<public>() : void =
if (IsOpen = true):
CloseRulebook()
else:
OpenRulebook()
OpenRulebook<public>() : void =
if (IsOpen = true):
return
if (PlayerUI := GetPlayerUI[Player]):
Canvas := BuildRulebookCanvas()
set MainCanvas = option{Canvas}
set IsOpen = true
PlayerUI.AddWidget(Canvas, player_ui_slot{InputMode := ui_input_mode.All, ZOrder := 100})
FocusActiveTextWidget()
spawn{HandleButtonClicks()}
CloseRulebook<public>() : void =
if (IsOpen = false):
return
ButtonCloseEvent.Signal()
if (PlayerUI := GetPlayerUI[Player]):
if (Canvas := MainCanvas?):
PlayerUI.RemoveWidget(Canvas)
set MainCanvas = false
set MainOverlay = false
set TextContainer = false
set ActiveTextWidget = false
set IsOpen = false
set CategoryButtons = map{}
# ==================== UI BUILDING ====================
BuildRulebookCanvas() : canvas =
MainOvl := BuildMainOverlay()
canvas{
Slots := array{
canvas_slot{
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.5}, Maximum := vector2{X := 0.5, Y := 0.5}}
Alignment := vector2{X := 0.5, Y := 0.5}
SizeToContent := true
Widget := MainOvl
}
}
}
BuildMainOverlay() : overlay =
CurrentWidth := 1012.0
CurrentHeight := 805.0
BgWidth := CurrentWidth
BgHeight := CurrentHeight
BackgroundWidget := if (BgMaterial := RulebookDevice.BackgroundMaterial?):
material_block{
DefaultImage := BgMaterial
DefaultDesiredSize := vector2{X := BgWidth, Y := BgHeight}
}
else:
color_block{
DefaultColor := RulebookDevice.BackgroundColor
DefaultDesiredSize := vector2{X := BgWidth, Y := BgHeight}
DefaultOpacity := 1.0
}
PanelBorder := color_block{
DefaultColor := RulebookDevice.BorderColorStyle
DefaultOpacity := 1.0
DefaultDesiredSize := vector2{X := BgWidth + 10.0, Y := BgHeight + 10.0}
}
Header := BuildHeader()
SeparatorLine := color_block{
DefaultColor := RulebookDevice.BorderColorStyle
DefaultOpacity := 1.0
DefaultDesiredSize := vector2{X := BgWidth + 10.0, Y := 5.0}
}
ButtonBar := BuildCategorySidebar()
set SidebarWidget = option{ButtonBar}
InitTextWidgets()
TextOverlay := BuildTextOverlay()
set TextContainer = option{TextOverlay}
CloseBtnWidget := BuildCloseButton()
set CloseBtn = option{CloseBtnWidget}
CompactPanel := overlay{
Slots := array{
overlay_slot{
Widget := PanelBorder
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Center
},
overlay_slot{
Widget := BackgroundWidget
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Center
},
overlay_slot{
Widget := Header
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Top
Padding := margin{Left:= 15.0, Top := 15.0}
},
overlay_slot{
Widget := CloseBtnWidget
HorizontalAlignment := horizontal_alignment.Right
VerticalAlignment := vertical_alignment.Top
Padding := margin{ Right := 10.0}
},
overlay_slot{
Widget := SeparatorLine
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Top
Padding := margin{Top := 90.0}
},
overlay_slot{
Widget := ButtonBar
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Top
Padding := margin{Top := 130.0}
},
overlay_slot{
Widget := TextOverlay
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Top
Padding := margin{Top := 310.0}
}
}
}
MainOvl := overlay{
Slots := array{
overlay_slot{
Widget := CompactPanel
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Center
}
}
}
set MainOverlay = option{CompactPanel}
MainOvl
BuildHeader() : Rulebook_text =
Rulebook_text{}
BuildCategorySidebar() : stack_box =
TLDRHandler := tldr_hoverable_button{Widget := TLDRBtnWidget, CloseEvent := ButtonCloseEvent}
TLDRBtn := TLDRHandler.GetButton()
if (set CategoryButtons[rulebook_category.TLDR] = TLDRBtn) {}
BonusHandler := bonus_hoverable_button{Widget := BonusBtnWidget, CloseEvent := ButtonCloseEvent}
BonusBtn := BonusHandler.GetButton()
if (set CategoryButtons[rulebook_category.Bonus] = BonusBtn) {}
RoadmapHandler := roadmap_hoverable_button{Widget := RoadmapBtnWidget, CloseEvent := ButtonCloseEvent}
RoadmapBtn := RoadmapHandler.GetButton()
if (set CategoryButtons[rulebook_category.Roadmap] = RoadmapBtn) {}
Spacing := margin{Left := 3.0, Right := 3.0}
Row1 := stack_box{
Orientation := orientation.Horizontal
Slots := array{
stack_box_slot{Widget := TLDRBtn, HorizontalAlignment := horizontal_alignment.Center, VerticalAlignment := vertical_alignment.Center, Padding := Spacing}
}
Row2 := stack_box{
Orientation := orientation.Horizontal
Slots := array{
stack_box_slot{Widget := BonusBtn, HorizontalAlignment := horizontal_alignment.Center, VerticalAlignment := vertical_alignment.Center, Padding := Spacing}
}
}
stack_box{
Orientation := orientation.Vertical
Slots := array{
stack_box_slot{
Widget := Row1
HorizontalAlignment := horizontal_alignment.Center
Padding := margin{Bottom := 3.0}
}
stack_box_slot{
Widget := Row2
HorizontalAlignment := horizontal_alignment.Center
}
}
}
BuildCloseButton() : button =
QuitHandler := rulebook_quit_hoverable_button{Widget := QuitBtnWidget, CloseEvent := ButtonCloseEvent}
QuitHandler.GetButton()
InitTextWidgets() : void =
ResetAllButtons()
ActivateButtonForCategory(CurrentCategory)
ResetAllTextWidgets()
ActivateTextForCategory(CurrentCategory)
BuildTextOverlay() : overlay =
ActiveText := GetTextWidgetForCategory(CurrentCategory)
set ActiveTextWidget = option{ActiveText}
overlay{
Slots := array{
overlay_slot{Widget := ActiveText}
}
}
ResetAllButtons() : void =
set TLDRBtnWidget.IsTLDRClicked = 0.0
set BonusBtnWidget.IsBonusClicked = 0.0
ResetAllHoverStates()
ResetAllHoverStates() : void =
set TLDRBtnWidget.IsHovering = false
set BonusBtnWidget.IsHovering = false
set QuitBtnWidget.isHovering = false
ActivateButtonForCategory(Category : rulebook_category) : void =
case (Category):
rulebook_category.TLDR =>
set TLDRBtnWidget.IsTLDRClicked = 1.0
rulebook_category.Bonus =>
set BonusBtnWidget.IsBonusClicked = 1.0
GetTextWidgetForCategory(Category : rulebook_category) : widget =
case (Category):
rulebook_category.TLDR => TLDRTextWidget
rulebook_category.Bonus => BonusTextWidget
ResetAllTextWidgets() : void =
set TLDRTextWidget.ShowTLDRText = 0.0
set BonusTextWidget.ShowBonusText = 0.0
ActivateTextForCategory(Category : rulebook_category) : void =
case (Category):
rulebook_category.TLDR =>
set TLDRTextWidget.ShowTLDRText = 1.0
rulebook_category.Bonus =>
set BonusTextWidget.ShowBonusText = 1.0
SwapActiveText() : void =
if (Container := TextContainer?):
if (OldText := ActiveTextWidget?):
Container.RemoveWidget(OldText)
ResetAllTextWidgets()
ActivateTextForCategory(CurrentCategory)
NewText := GetTextWidgetForCategory(CurrentCategory)
Container.AddWidget(overlay_slot{Widget := NewText})
set ActiveTextWidget = option{NewText}
FocusActiveTextWidget()
FocusWidgetDelayed(PlayerUI : player_ui, Target : widget)<suspends> : void =
Sleep(0.2)
PlayerUI.SetFocus(Target)
FocusActiveTextWidget() : void =
if (PlayerUI := GetPlayerUI[Player]):
Print("text focused")
Target := GetTextWidgetForCategory(CurrentCategory)
spawn{FocusWidgetDelayed(PlayerUI, Target)}
HandleButtonClicks()<suspends> : void =
if:
TLDRBtn := CategoryButtons[rulebook_category.TLDR]
BonusBtn := CategoryButtons[rulebook_category.Bonus]
CloseBtnRef := CloseBtn?
then:
race:
ButtonCloseEvent.Await()
loop:
CloseBtnRef.OnClick().Await()
CloseRulebook()
loop:
TLDRBtn.OnClick().Await()
set CurrentCategory = rulebook_category.TLDR
ResetAllButtons()
ActivateButtonForCategory(CurrentCategory)
SwapActiveText()
loop:
BonusBtn.OnClick().Await()
set CurrentCategory = rulebook_category.Bonus
ResetAllButtons()
ActivateButtonForCategory(CurrentCategory)
SwapActiveText()
This my code (i have more buttons, i just deleted 7 of them for better lisibility