Add obisidianUI text field interop

This commit is contained in:
cryy
2025-04-22 23:57:31 +02:00
parent 33845e111b
commit 24e58027b2
2 changed files with 130 additions and 46 deletions

View File

@@ -7,7 +7,6 @@ import net.minecraft.client.gui.ParentElement;
import net.minecraft.client.gui.screen.ChatScreen; import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.util.ArrayList; import java.util.ArrayList;
@@ -23,25 +22,25 @@ public class DefaultScreenClickHandler extends AbstractScreenClickHandler<Screen
@Override @Override
public void handle(Screen screen, double mouseX, double mouseY) { public void handle(Screen screen, double mouseX, double mouseY) {
var textField = findClickedTextField(screen, mouseX, mouseY); var textField = findClickedTextField(screen.children(), mouseX, mouseY);
if (textField == null) { if (textField == null) {
return; return;
} }
this.parentScreen = screen; this.parentScreen = screen;
this.textFieldElementPath = calculatePathToElement(screen, textField); this.textFieldElementPath = calculatePathToElement(screen, textField.asElement());
var virtualKeyboardScreen = new VirtualKeyboardScreen(textField.getText(), this::handleKeyboardClose, false); var virtualKeyboardScreen = new VirtualKeyboardScreen(textField.getText(), this::handleKeyboardClose, false);
client.setScreen(virtualKeyboardScreen); client.setScreen(virtualKeyboardScreen);
} }
private void handleKeyboardClose(String newText) { private void handleKeyboardClose(String newText) {
if(this.parentScreen == null || this.textFieldElementPath == null) { if (this.parentScreen == null || this.textFieldElementPath == null) {
return; return;
} }
client.setScreen(this.parentScreen); client.setScreen(this.parentScreen);
TextFieldWidget textField = findTextFieldByPath(this.parentScreen, this.textFieldElementPath); TextFieldWrapper textField = findTextFieldByPath(this.parentScreen, this.textFieldElementPath);
if (textField == null) { if (textField == null) {
return; return;
} }
@@ -57,20 +56,29 @@ public class DefaultScreenClickHandler extends AbstractScreenClickHandler<Screen
// send the chat message // send the chat message
chatScreen.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0); chatScreen.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
} }
default -> {} default -> {
}
} }
} }
private TextFieldWidget findClickedTextField(Screen screen, double mouseX, double mouseY) {
for (Element element : screen.children()) { private TextFieldWrapper findClickedTextField(List<? extends Element> elements, double mouseX, double mouseY) {
if (element instanceof TextFieldWidget textField) { for (Element element : elements) {
if (TextFieldWrapper.isValidTextField(element)) {
TextFieldWrapper textField = new TextFieldWrapper(element);
if (textField.isMouseOver(mouseX, mouseY) && textField.isFocused()) { if (textField.isMouseOver(mouseX, mouseY) && textField.isFocused()) {
return textField; return textField;
} }
} }
if (element instanceof ParentElement parentElement) {
TextFieldWrapper found = findClickedTextField(parentElement.children(), mouseX, mouseY);
if (found != null) {
return found;
}
}
} }
// not hovering over a text field
return null; return null;
} }
@@ -78,58 +86,57 @@ public class DefaultScreenClickHandler extends AbstractScreenClickHandler<Screen
* Calculates the path between a parent and a target in the UI hierarchy * Calculates the path between a parent and a target in the UI hierarchy
*/ */
protected List<Integer> calculatePathToElement(Element parent, Element target) { protected List<Integer> calculatePathToElement(Element parent, Element target) {
if (parent instanceof ParentElement parentElement) { if (!(parent instanceof ParentElement parentElement)) {
List<? extends Element> children = parentElement.children(); return null;
}
// check direct children first List<? extends Element> children = parentElement.children();
for (int i = 0; i < children.size(); i++) {
if (children.get(i) == target) { for (int i = 0; i < children.size(); i++) {
// found it, return the path to this element Element child = children.get(i);
return Collections.singletonList(i);
} if (child == target) {
return Collections.singletonList(i);
} }
// check each child's children if (child instanceof ParentElement) {
for (int i = 0; i < children.size(); i++) { List<Integer> subPath = calculatePathToElement(child, target);
if (children.get(i) instanceof ParentElement childParent) { if (subPath != null) {
List<Integer> subPath = calculatePathToElement(childParent, target); List<Integer> fullPath = new ArrayList<>(subPath.size() + 1);
if (subPath != null) { fullPath.add(i);
// found in this subtree, prepend current index fullPath.addAll(subPath);
List<Integer> fullPath = new ArrayList<>(); return fullPath;
fullPath.add(i);
fullPath.addAll(subPath);
return fullPath;
}
} }
} }
} }
// Not found
return null; return null;
} }
protected TextFieldWidget findTextFieldByPath(Element parent, List<Integer> path) { protected TextFieldWrapper findTextFieldByPath(Element parent, List<Integer> path) {
if (path == null || path.isEmpty()) { if (path == null || path.isEmpty()) {
return null; return null;
} }
if (parent instanceof ParentElement parentElement) { if (!(parent instanceof ParentElement parentElement)) {
List<? extends Element> children = parentElement.children(); return null;
int index = path.getFirst(); }
if (index >= 0 && index < children.size()) { List<? extends Element> children = parentElement.children();
Element child = children.get(index); int index = path.get(0);
if (path.size() == 1) { if (index < 0 || index >= children.size()) {
// This should be our target return null;
return (child instanceof TextFieldWidget) ? (TextFieldWidget) child : null; }
} else {
// Continue traversing Element child = children.get(index);
if (child instanceof ParentElement) {
return findTextFieldByPath(child, path.subList(1, path.size())); if (path.size() == 1) {
} return TextFieldWrapper.isValidTextField(child) ? new TextFieldWrapper(child) : null;
} }
}
if (child instanceof ParentElement) {
return findTextFieldByPath(child, path.subList(1, path.size()));
} }
return null; return null;

View File

@@ -0,0 +1,77 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.thinkingstudio.obsidianui.widget.text.SpruceTextFieldWidget;
public record TextFieldWrapper(Object textField) {
public TextFieldWrapper {
if (!isValidTextField(textField)) {
throw new IllegalArgumentException("Type " + textField.getClass() + " is not marked as a valid text field");
}
}
Element asElement() {
return (Element) textField;
}
String getText() {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
return spruceTextField.getText();
}
case TextFieldWidget vanillaTextField -> {
return vanillaTextField.getText();
}
default -> {
return null;
}
}
}
void setText(String text) {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
spruceTextField.setText(text);
}
case TextFieldWidget vanillaTextField -> {
vanillaTextField.setText(text);
}
default -> {
}
}
}
boolean isMouseOver(double mouseX, double mouseY) {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
return spruceTextField.isMouseOver(mouseX, mouseY);
}
case TextFieldWidget vanillaTextField -> {
return vanillaTextField.isMouseOver(mouseX, mouseY);
}
default -> {
return false;
}
}
}
boolean isFocused() {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
return spruceTextField.isFocused();
}
case TextFieldWidget vanillaTextField -> {
return vanillaTextField.isFocused();
}
default -> {
return false;
}
}
}
static boolean isValidTextField(Object textField) {
return textField instanceof TextFieldWidget || textField instanceof SpruceTextFieldWidget;
}
}