Setting TextBoxComponent to active doesn't set keyboard focus
fetimo opened this issue · 4 comments
Hi!
Firstly thank you for providing such a well-thought out library with personality.
I'm trying to create a textbox which receives keyboard focus when the screen is created to replace the Chat HUD. Here is the XML snippet:
<text-box id="the-text-box">
<sizing>
<horizontal method="fill">300</horizontal>
<vertical method="fill">20</vertical>
</sizing>
<active>true</active>
</text-box>
Then in the build
method I have the following (you can see my descent into madness).
TextBoxComponent theTextBox = rootComponent.childById(TextBoxComponent.class, "the-text-box");
theTextBox.setFocused(true);
theTextBox.setSelectionStart(0);
theTextBox.setSelectionEnd(0);
theTextBox.setCursorToStart(false);
theTextBox.getNavigationFocus();
theTextBox.onClick(theTextBox.x() + 10, theTextBox.y() + 5);
theTextBox.onClick(30,440);
theTextBox.mouseClicked(30,440, 1);
theTextBox.keyPress();
theTextBox.setFocused(true);
theTextBox.eraseCharacters(0);
theTextBox.getNavigationFocus();
theTextBox.update(0, theTextBox.x() + 10, theTextBox.y() + 5);
I also tried setting various values in the init method and wrapping it in a scheduled task (the ol' setTimeout trick) but the pesky textbox just doesn't want to receive keyboard focus.
I'm new to owo and Java in general so it's likely I'm missing something fundamental but my naive assumption is that between the <active>true</active>
and theTextBox.setFocused(true)
this should work. Is this a bug or is there another way to do it?
In owo-ui, focus is managed by the component tree's FocusHandler
(accessible through the .focusHandler()
) method on any component in the tree. The .setFocused(...)
method on the text box is actually a vanilla method inherited from the the vanilla TextFieldWidget
- it is unrelated to owo-ui and has no effect when used outside the vanilla UI system.
As such, to pre-focus the text field, call theTextBox.focusHandler().focus(theTextBox, FocusSource.MOUSE_CLICK)
. If you have any more questions, feel free to open another issue or join the Wisp Forest Discord server
Cheers
Thanks @gliscowo!
For some reason the focusHandler() is returning null
and shows an error java.lang.NullPointerException: Cannot invoke "io.wispforest.owo.ui.util.FocusHandler.focus(io.wispforest.owo.ui.core.Component, io.wispforest.owo.ui.core.Component$FocusSource)" because the return value of "io.wispforest.owo.ui.component.TextBoxComponent.focusHandler()" is null
.
I feel like I must've missed a step somewhere around setting up a UI provider or something. I've tried looking through the source code but can't work it out. I can log out theTextBox
and see that definitely exists as io.wispforest.owo.ui.component.TextBoxComponent@3bdf7aff
import io.wispforest.owo.ui.base.BaseUIModelScreen;
import io.wispforest.owo.ui.component.TextBoxComponent;
import io.wispforest.owo.ui.container.FlowLayout;
import io.wispforest.owo.ui.core.Component.FocusSource;
import net.minecraft.util.Identifier;
public class ChatHudInput extends BaseUIModelScreen<FlowLayout> {
public ChatHudInput() {
super(FlowLayout.class, DataSource.asset(Identifier.of("siarad", "my_ui_model")));
}
@Override
protected void build(FlowLayout rootComponent) {
TextBoxComponent theTextBox = rootComponent.childById(TextBoxComponent.class, "the-text-box");
theTextBox.focusHandler().focus(theTextBox, FocusSource.MOUSE_CLICK);
}
}
my_ui_model.xml
<owo-ui xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/wisp-forest/owo-lib/1.21/owo-ui.xsd">
<components>
<flow-layout direction="vertical">
<children>
<text-box id="the-text-box">
<sizing>
<horizontal method="fill">300</horizontal>
<vertical method="fill">20</vertical>
</sizing>
<active>true</active>
</text-box>
</children>
</flow-layout>
</components>
</owo-ui>
Ah, well, at the point you're calling this function your component tree has not been built yet - this means the text box doesn't yet know about its parent and thus also can't get the focus handler from the root node. Instead, override init()
on your screen as well and run your focusing code at the end of that method
Awesome! In retrospect that should've been obvious to me. Thanks for spelling it out :)
Just for completeness here's what I've ended up with. Thanks for your help @gliscowo
import io.wispforest.owo.ui.base.BaseUIModelScreen;
import io.wispforest.owo.ui.component.TextBoxComponent;
import io.wispforest.owo.ui.container.FlowLayout;
import io.wispforest.owo.ui.core.Component.FocusSource;
import net.minecraft.util.Identifier;
public class ChatHudInput extends BaseUIModelScreen<FlowLayout> {
TextBoxComponent theTextBox;
public ChatHudInput() {
super(FlowLayout.class, DataSource.asset(Identifier.of("siarad", "my_ui_model")));
}
@Override
protected void build(FlowLayout rootComponent) {
theTextBox = rootComponent.childById(TextBoxComponent.class, "the-text-box");
}
@Override
protected void init() {
super.init();
theTextBox.focusHandler().focus(theTextBox, FocusSource.MOUSE_CLICK);
}
}