diff --git a/src/main/java/fzmm/zailer/me/client/gui/components/SuggestionTextBox.java b/src/main/java/fzmm/zailer/me/client/gui/components/SuggestionTextBox.java index f01c395f..aaa24a01 100644 --- a/src/main/java/fzmm/zailer/me/client/gui/components/SuggestionTextBox.java +++ b/src/main/java/fzmm/zailer/me/client/gui/components/SuggestionTextBox.java @@ -8,118 +8,119 @@ import fzmm.zailer.me.client.gui.components.style.StyledContainers; import fzmm.zailer.me.client.gui.components.style.container.StyledFlowLayout; import io.wispforest.owo.config.ui.component.ConfigTextBox; +import io.wispforest.owo.ui.component.DropdownComponent; import io.wispforest.owo.ui.component.LabelComponent; +import io.wispforest.owo.ui.container.Containers; import io.wispforest.owo.ui.container.FlowLayout; -import io.wispforest.owo.ui.container.OverlayContainer; import io.wispforest.owo.ui.container.ScrollContainer; import io.wispforest.owo.ui.core.*; -import io.wispforest.owo.ui.event.KeyPress; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; +import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -public class SuggestionTextBox extends StyledFlowLayout { +@SuppressWarnings("UnstableApiUsage") +public class SuggestionTextBox extends ConfigTextBox { private static final int SUGGESTION_HEIGHT = 16; - private static final int TEXT_BOX_HEIGHT = 22; - private static final int SUGGESTION_MATCH_COLOR = Formatting.YELLOW.getColorValue() == null ? 0xFFFF55 : Formatting.YELLOW.getColorValue(); - private static final int SUGGESTION_NEW_COLOR = Formatting.GRAY.getColorValue() == null ? 0xAAAAAA : Formatting.GRAY.getColorValue(); - private SuggestionPosition suggestionPosition; + private final SuggestionPosition suggestionPosition; private SuggestionProvider suggestionProvider; - @SuppressWarnings("UnstableApiUsage") - private final ConfigTextBox textBox; - private final FlowLayout suggestionsLayout; - private final ScrollContainer suggestionsContainer; - private boolean mouseHoverSuggestion; private int maxSuggestionLines; - private int selectedSuggestionIndex; + private int selectedSuggestionIndex = -1; + private boolean disableCallback = false; + @Nullable + private Runnable suggestionSelectedCallback = null; + @Nullable + private DropdownComponent suggestionsContextMenu; @Nullable - private Runnable suggestionSelectedCallback; + private ScrollContainer suggestionsContainer = null; + @Nullable + private FlowLayout suggestionsLayout = null; @SuppressWarnings("UnstableApiUsage") - public SuggestionTextBox(Sizing horizontalSizing, SuggestionPosition position, int maxSuggestionLines, @Nullable KeyPress onKeyPress) { - super(horizontalSizing, Sizing.content(), Algorithm.VERTICAL); + public SuggestionTextBox(Sizing horizontalSizing, SuggestionPosition position, int maxSuggestionLines) { + super(); + this.horizontalSizing(horizontalSizing); this.suggestionProvider = (nul, builder) -> CompletableFuture.completedFuture(builder.build()); - this.textBox = new ConfigTextBox() { - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_TAB) - return false; - return super.keyPressed(keyCode, scanCode, modifiers); - } - }; - this.mouseHoverSuggestion = false; - this.textBox.horizontalSizing(Sizing.fill(100)); - this.suggestionsLayout = StyledContainers.verticalFlow(Sizing.fill(100), Sizing.content()); - this.suggestionsContainer = StyledContainers.verticalScroll(horizontalSizing, Sizing.fixed(0), this.suggestionsLayout); - this.child(this.textBox); - this.selectedSuggestionIndex = -1; - this.suggestionSelectedCallback = null; + this.suggestionPosition = position; this.setMaxSuggestionLines(maxSuggestionLines); - this.setSuggestionPosition(position); + this.onChanged().subscribe(this::updateSuggestions); + } - this.textBox.onChanged().subscribe(this::updateSuggestions); - this.removeSuggestionOnLostFocusEvents(); + private void openContextMenu() { + Screen screen = MinecraftClient.getInstance().currentScreen; + FlowLayout parent = (FlowLayout) this.root(); + if (this.contextMenuIsOpen() || screen == null || parent == null) { + return; + } - this.textBox.keyPress().subscribe((keyCode, scanCode, modifiers) -> { - boolean result = this.onTextBoxKeyPress(keyCode); - if (!result && onKeyPress != null) - result = onKeyPress.onKeyPress(keyCode, scanCode, modifiers); - return result; - }); - } + this.suggestionsContextMenu = DropdownComponent.openContextMenu(screen, parent, FlowLayout::child, this.x(), this.y(), categoryDropdown -> { + categoryDropdown.clearChildren(); - private boolean onTextBoxKeyPress(int keyCode) { - if (keyCode == GLFW.GLFW_KEY_TAB && this.suggestionsLayout.children().isEmpty()) { - this.updateSuggestions(this.textBox.getText()); - return !this.suggestionsLayout.children().isEmpty(); + this.suggestionsLayout = StyledContainers.verticalFlow(Sizing.fill(100), Sizing.content()); + this.suggestionsContainer = Containers.verticalScroll(this.horizontalSizing().get(), + Sizing.expand(100), this.suggestionsLayout); - } else if (keyCode == GLFW.GLFW_KEY_TAB || keyCode == GLFW.GLFW_KEY_DOWN) { - return this.changeSelectedSuggestionIndex(this.selectedSuggestionIndex, this.selectedSuggestionIndex + 1); - } else if (keyCode == GLFW.GLFW_KEY_UP) { - return this.changeSelectedSuggestionIndex(this.selectedSuggestionIndex, this.selectedSuggestionIndex - 1); + categoryDropdown.child(this.suggestionsContainer); + }); - } else if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + this.suggestionsContextMenu.zIndex(200); + this.updateSuggestions(this.getText()); + } - List children = this.suggestionsLayout.children(); - if (this.selectedSuggestionIndex >= 0 && this.selectedSuggestionIndex < children.size()) { - Component selectedComponent = children.get(this.selectedSuggestionIndex); - selectedComponent.onMouseDown(selectedComponent.x(), selectedComponent.y(), GLFW.GLFW_MOUSE_BUTTON_1); - this.textBox.onFocusGained(FocusSource.MOUSE_CLICK); - return true; - } else { - return false; - } + private void closeContextMenu() { + if (this.suggestionsLayout != null) { + this.suggestionsLayout.clearChildren(); + } + + if (this.suggestionsContextMenu != null) { + this.suggestionsContextMenu.remove(); } - return false; + this.suggestionsLayout = null; + this.suggestionsContainer = null; + this.suggestionsContextMenu = null; + } + + private boolean contextMenuIsOpen() { + return this.suggestionsContextMenu != null && + this.suggestionsContainer != null && + this.suggestionsLayout != null && + this.suggestionsContextMenu.hasParent(); + } + + private boolean updateSelectedSuggestionIndex(int addIndex) { + return this.updateSelectedSuggestionIndex(this.selectedSuggestionIndex, this.selectedSuggestionIndex + addIndex); } - private boolean changeSelectedSuggestionIndex(int currentIndex, int newIndex) { + private boolean updateSelectedSuggestionIndex(int currentIndex, int newIndex) { + if (this.suggestionsLayout == null || this.suggestionsContainer == null) { + return false; + } + List children = this.suggestionsLayout.children(); int childrenSize = children.size(); - currentIndex = Math.max(0, currentIndex); - - if (childrenSize > currentIndex) + if (currentIndex > 0 && childrenSize > currentIndex) { children.get(currentIndex).onFocusLost(); + } - if (newIndex < 0) + // cycles when it moves out of bounds + if (newIndex < 0) { newIndex = childrenSize - 1; - else if (newIndex >= childrenSize) + } else if (newIndex >= childrenSize) { newIndex = 0; + } if (childrenSize > newIndex) { Component selectedComponent = children.get(newIndex); @@ -131,76 +132,52 @@ else if (newIndex >= childrenSize) return true; } - private void removeSuggestionOnLostFocusEvents() { - this.textBox.focusGained().subscribe(source -> { - // if you put the suggestionContainer in the root when there is a container everything seems - // to work correctly, but it does not let you write in the text field of SuggestionTextBox, - // so you have to do black magic, and if I do not put the suggestionContainer in the root - // it overlaps other components making it not visible or selectable - this.getSuggestionsContainerParent().ifPresent(flowLayout -> flowLayout.child(this.suggestionsContainer)); - - // is necessary because otherwise the suggestion stays in the - // wrong position because a memento was executed too early - this.updateSuggestionsPos(); - }); - this.textBox.focusLost().subscribe(() -> { - // we live in a society where if you click on a component first the focusLost is - // sent and then the mouseDown so if that focusLost removes your component with - // mouseDown it will never be called - if (!this.mouseHoverSuggestion) - ((FlowLayout) this.root()).removeChild(this.suggestionsContainer); - this.mouseHoverSuggestion = false; - }); - this.suggestionsContainer.focusGained().subscribe(source -> this.clearSuggestions()); - } - - private Optional getSuggestionsContainerParent() { - FlowLayout root = (FlowLayout) this.root(); - if (root == null) - return Optional.empty(); - - for (var child : root.children()) { - if (child instanceof OverlayContainer overlayContainer && overlayContainer.child() instanceof FlowLayout flowLayout) - return Optional.of(flowLayout); + private void updateSuggestions(String newMessage) { + if (!this.contextMenuIsOpen()) { + this.openContextMenu(); } - return Optional.of(root); - } + assert this.suggestionsLayout != null; - private void updateSuggestions(String newMessage) { - try { - int messageLength = newMessage.length(); - String newMessageToLowerCase = newMessage.toLowerCase(); - List suggestions = this.suggestionProvider.getSuggestions(null, new SuggestionsBuilder(newMessage, messageLength)).get().getList(); - this.suggestionsLayout.clearChildren(); - int maxHorizontalSizing = this.suggestionsLayout.width() - 10; + String newMessageToLowerCase = newMessage.toLowerCase(); + List suggestions = this.getSuggestions(newMessage); + this.suggestionsLayout.clearChildren(); + int maxHorizontalSizing = this.suggestionsLayout.width() - 10; + + for (int i = 0; i != suggestions.size(); i++) { + String suggestion = suggestions.get(i).getText(); + int matchIndex = suggestion.toLowerCase().indexOf(newMessageToLowerCase); - for (int i = 0; i != suggestions.size(); i++) { - this.tryAddSuggestion(suggestions.get(i).getText(), newMessageToLowerCase, maxHorizontalSizing); + if (matchIndex >= 0 && this.suggestionsLayout != null) { + Text suggestionMessage = this.getSuggestionMessage(suggestion, newMessageToLowerCase, matchIndex, maxHorizontalSizing); + this.suggestionsLayout.child(this.getSuggestionComponent(suggestion, suggestionMessage)); } + } - this.updateSuggestionsPos(); + if (this.suggestionsContextMenu != null) { + this.suggestionsContextMenu.verticalSizing(Sizing.fixed(this.getMaxSuggestionsHeight(suggestions.size()))); + } + this.updateSuggestionsPos(); + } + private List getSuggestions(String message) { + try { + return this.suggestionProvider.getSuggestions(null, new SuggestionsBuilder(message, 0)).get().getList(); } catch (Exception e) { FzmmClient.LOGGER.error("[SuggestionTextBox] Failed to get suggestions", e); + assert this.suggestionsLayout != null; + this.suggestionsLayout.clearChildren(); this.suggestionsLayout.child(StyledComponents.label(Text.literal("Failed to get suggestions"))); } - } - - private void tryAddSuggestion(String suggestion, String textBoxMessageToLowerCase, int maxHorizontalSizing) { - int matchIndex = suggestion.toLowerCase().indexOf(textBoxMessageToLowerCase); - if (matchIndex >= 0) { - this.suggestionsLayout.child(this.getSuggestionComponent(suggestion, this.getSuggestionMessage(suggestion, textBoxMessageToLowerCase, matchIndex, maxHorizontalSizing))); - } + return new ArrayList<>(); } private Text getSuggestionMessage(String suggestion, String textBoxMessageToLowerCase, int matchIndex, int maxHorizontalSizing) { TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - int suggestionWidth = textRenderer.getWidth(suggestion); int startNewColorIndex = matchIndex + textBoxMessageToLowerCase.length(); - if (suggestionWidth > maxHorizontalSizing) { + if (textRenderer.getWidth(suggestion) > maxHorizontalSizing) { int suggestionLength = suggestion.length(); String ellipsis = "..."; maxHorizontalSizing -= textRenderer.getWidth(ellipsis); @@ -213,78 +190,47 @@ private Text getSuggestionMessage(String suggestion, String textBoxMessageToLowe } return Text.literal(suggestion.substring(0, matchIndex)) - .setStyle(Style.EMPTY.withColor(SUGGESTION_NEW_COLOR)) + .setStyle(Style.EMPTY.withColor(Formatting.GRAY)) .append( Text.literal(suggestion.substring(matchIndex, startNewColorIndex)) - .setStyle(Style.EMPTY.withColor(SUGGESTION_MATCH_COLOR)) - ) - .append( + .setStyle(Style.EMPTY.withColor(Formatting.YELLOW)) + ).append( Text.literal(suggestion.substring(startNewColorIndex)) - .setStyle(Style.EMPTY.withColor(SUGGESTION_NEW_COLOR)) + .setStyle(Style.EMPTY.withColor(Formatting.GRAY)) ); } - // we live in a society where if you click on a component first the focusLost is - // sent and then the mouseDown so if that focusLost removes your component with - // mouseDown it will never be called - // - // in other words, I can't call this.textBox.onFocusGained(FocusSource.MOUSE_CLICK); here - private void clearSuggestions() { - this.update(0, 0, 0); - this.mouseHoverSuggestion = false; - this.suggestionsLayout.clearChildren(); - this.textBox.setCursorToStart(false); - this.textBox.onFocusLost(); - } - private Component getSuggestionComponent(String suggestion, Text suggestionText) { - // ButtonComponent does not allow the text to be left aligned LabelComponent labelComponent = StyledComponents.label(suggestionText); - FlowLayout layout = StyledContainers.verticalFlow(Sizing.fill(100), Sizing.fixed(SUGGESTION_HEIGHT)); - layout.mouseDown().subscribe((mouseX, mouseY, button) -> { - this.textBox.text(suggestion); - if (this.suggestionSelectedCallback != null) - this.suggestionSelectedCallback.run(); - - this.clearSuggestions(); - return true; - }); + StyledFlowLayout layout = StyledContainers.verticalFlow(Sizing.fill(100), Sizing.fixed(SUGGESTION_HEIGHT)); - int backgroundColor = 0xA0000000; - Consumer setSurfaceConsumer = flowLayout -> flowLayout.surface(Surface.flat(0xE0000000).and(Surface.outline(0xA0FFFFFF))); - Consumer removeSurfaceConsumer = flowLayout -> flowLayout.surface(Surface.flat(backgroundColor)); - AtomicBoolean isFocusedAtomic = new AtomicBoolean(false); + Surface selectedSurface = Surface.flat(0xE0000000).and(Surface.outline(0xA0FFFFFF)); + Surface unselectedSurface = Surface.flat(0xA0000000); - layout.mouseEnter().subscribe(() -> { - setSurfaceConsumer.accept(layout); - this.mouseHoverSuggestion = true; - }); - layout.focusGained().subscribe(source -> { - setSurfaceConsumer.accept(layout); - isFocusedAtomic.set(true); - }); + layout.focusGained().subscribe(source -> layout.surface(Surface.BLANK)); + layout.hoveredSurface(selectedSurface); + layout.focusLost().subscribe(() -> layout.surface(unselectedSurface)); + layout.mouseDown().subscribe((mouseX, mouseY, button) -> { + this.text(suggestion); + if (this.suggestionSelectedCallback != null && !this.disableCallback) { + this.suggestionSelectedCallback.run(); + } - layout.mouseLeave().subscribe(() -> { - if (!isFocusedAtomic.get()) - removeSurfaceConsumer.accept(layout); - this.mouseHoverSuggestion = false; - }); - layout.focusLost().subscribe(() -> { - removeSurfaceConsumer.accept(layout); - isFocusedAtomic.set(false); + this.closeContextMenu(); + return true; }); - - layout.surface(Surface.flat(backgroundColor)) - .padding(Insets.horizontal(4)) + layout.surface(unselectedSurface) .verticalAlignment(VerticalAlignment.CENTER) - .zIndex(300) .cursorStyle(CursorStyle.HAND); - labelComponent.cursorStyle(CursorStyle.HAND); - layout.child(labelComponent); + labelComponent.cursorStyle(CursorStyle.HAND) + .margins(Insets.horizontal(4)); + return layout.child(labelComponent); + } - return layout; + private int getMaxSuggestionsHeight(int lines) { + return Math.min(this.getSuggestionsHeight(lines), this.getSuggestionsHeight(this.maxSuggestionLines)); } private int getSuggestionsHeight(int lines) { @@ -292,35 +238,36 @@ private int getSuggestionsHeight(int lines) { } private void updateSuggestionsPos() { + if (this.suggestionsLayout == null || this.suggestionsContextMenu == null) { + return; + } + int offset = switch (this.suggestionPosition) { - case TOP -> - -this.getSuggestionsHeight(Math.min(this.suggestionsLayout.children().size(), this.maxSuggestionLines)); - case BOTTOM -> TEXT_BOX_HEIGHT; + case TOP -> -this.getMaxSuggestionsHeight(this.suggestionsLayout.children().size()); + case BOTTOM -> this.height(); }; - this.getSuggestionsContainerParent().ifPresent(flowLayout -> { - Insets padding = flowLayout.padding().get(); - int x = this.x - flowLayout.x() - padding.left(); - int y = this.y - flowLayout.y() - padding.top(); - this.suggestionsContainer.positioning(Positioning.absolute(x, y + offset)); - }); + assert this.suggestionsContextMenu != null; + this.suggestionsContextMenu.positioning(Positioning.absolute(this.x(), this.y() + offset)); } - @Override - public void layout(Size space) { - super.layout(space); + public void updateX(int x) { + super.updateX(x); this.updateSuggestionsPos(); } - public void setSuggestionPosition(SuggestionPosition position) { - this.suggestionPosition = position; + @Override + public void updateY(int y) { + super.updateY(y); this.updateSuggestionsPos(); } public void setMaxSuggestionLines(int maxSuggestionLines) { this.maxSuggestionLines = maxSuggestionLines; - this.suggestionsContainer.verticalSizing(Sizing.fixed(this.getSuggestionsHeight(maxSuggestionLines))); + if (this.suggestionsContextMenu != null) { + this.suggestionsContextMenu.verticalSizing(Sizing.fixed(this.getMaxSuggestionsHeight(maxSuggestionLines))); + } } /** @@ -328,17 +275,9 @@ public void setMaxSuggestionLines(int maxSuggestionLines) { */ public void setSuggestionProvider(SuggestionProvider provider) { this.suggestionProvider = provider; - this.suggestionsLayout.clearChildren(); - } - - @Override - public boolean isInBoundingBox(double x, double y) { - return super.isInBoundingBox(x, y) || this.suggestionsContainer.isInBoundingBox(x, y); - } - - @SuppressWarnings("UnstableApiUsage") - public ConfigTextBox getTextBox() { - return this.textBox; + if (this.suggestionsLayout != null) { + this.suggestionsLayout.clearChildren(); + } } public void setSuggestionSelectedCallback(@Nullable Runnable suggestionSelectedCallback) { @@ -346,13 +285,45 @@ public void setSuggestionSelectedCallback(@Nullable Runnable suggestionSelectedC } @Override - public Component horizontalSizing(Sizing horizontalSizing) { - this.suggestionsContainer.horizontalSizing(horizontalSizing); - return super.horizontalSizing(horizontalSizing); - } + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + boolean result = switch (keyCode) { + case GLFW.GLFW_KEY_TAB: + if (!this.contextMenuIsOpen()) { + this.openContextMenu(); + yield true; + } + assert this.suggestionsLayout != null; + if (this.suggestionsLayout.children().isEmpty()) { + this.updateSuggestions(this.getText()); + yield !this.suggestionsLayout.children().isEmpty(); + } else { + yield this.updateSelectedSuggestionIndex(1); + } + case GLFW.GLFW_KEY_DOWN: + yield this.updateSelectedSuggestionIndex(1); + case GLFW.GLFW_KEY_UP: + yield this.updateSelectedSuggestionIndex(-1); + case GLFW.GLFW_KEY_ENTER, GLFW.GLFW_KEY_KP_ENTER: + if (this.suggestionsLayout == null) { + yield false; + } + + List children = this.suggestionsLayout.children(); + if (this.selectedSuggestionIndex >= 0 && this.selectedSuggestionIndex < children.size()) { + this.disableCallback = true; + Component selectedComponent = children.get(this.selectedSuggestionIndex); + selectedComponent.onMouseDown(selectedComponent.x(), selectedComponent.y(), GLFW.GLFW_MOUSE_BUTTON_1); + this.disableCallback = false; + yield true; + } + case GLFW.GLFW_KEY_ESCAPE: + this.closeContextMenu(); + yield true; + default: + yield false; + }; - public void visible(boolean visible) { - this.textBox.visible = visible; + return result || super.keyPressed(keyCode, scanCode, modifiers); } public enum SuggestionPosition { diff --git a/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageButtonRow.java b/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageButtonRow.java index 5d8788bf..513e3c8b 100644 --- a/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageButtonRow.java +++ b/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageButtonRow.java @@ -40,14 +40,15 @@ public Component[] getComponents(String id, String tooltipId) { Math.abs(textRenderer.getWidth(loadImageButtonText) - textRenderer.getWidth(resetButton.getMessage())) + 2 ); - SuggestionTextBox textField = (SuggestionTextBox) new SuggestionTextBox(textFieldSizing, SuggestionTextBox.SuggestionPosition.BOTTOM, 5, - (keyCode, scanCode, modifiers) -> { - boolean isEnter = keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER; - if (isEnter) - imageButton.onPress(); + SuggestionTextBox textField = new SuggestionTextBox(textFieldSizing, SuggestionTextBox.SuggestionPosition.BOTTOM, 5); + textField.id(getImageValueFieldId(id)); + textField.keyPress().subscribe((keyCode, scanCode, modifiers) -> { + boolean isEnter = keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER; + if (isEnter) + imageButton.onPress(); - return isEnter; - }).id(getImageValueFieldId(id)); + return isEnter; + }); return new Component[]{ textField, @@ -71,7 +72,7 @@ public static void setup(FlowLayout rootComponent, String id, IImageGetter defau BaseFzmmScreen.checkNull(imageButtonComponent, "image-option", getImageButtonId(id)); BaseFzmmScreen.checkNull(suggestionTextBox, "suggestion-text-option", getImageValueFieldId(id)); - imageButtonComponent.onPress(button -> imageButtonComponent.loadImage(suggestionTextBox.getTextBox().getText())); + imageButtonComponent.onPress(button -> imageButtonComponent.loadImage(suggestionTextBox.getText())); imageButtonComponent.setSourceType(defaultMode); imageButtonComponent.horizontalSizing(Sizing.fixed(textRenderer.getWidth(imageButtonComponent.getMessage()) + BaseFzmmScreen.BUTTON_TEXT_PADDING)); @@ -81,13 +82,13 @@ public static void setup(FlowLayout rootComponent, String id, IImageGetter defau @SuppressWarnings("UnstableApiUsage") public static void setupSuggestionTextBox(SuggestionTextBox suggestionTextBox, IImageGetter imageGetter) { if (imageGetter instanceof IImageLoaderFromText imageLoaderFromText) - suggestionTextBox.getTextBox().applyPredicate(imageLoaderFromText::predicate); + suggestionTextBox.applyPredicate(imageLoaderFromText::predicate); suggestionTextBox.setSuggestionProvider(imageGetter instanceof IImageSuggestion imageSuggestion ? imageSuggestion.getSuggestionProvider() : (context, builder) -> CompletableFuture.completedFuture(builder.build()) ); - suggestionTextBox.visible(imageGetter.hasTextField()); + suggestionTextBox.setVisible(imageGetter.hasTextField()); } } diff --git a/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageRows.java b/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageRows.java index 89197850..f1c98b56 100644 --- a/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageRows.java +++ b/src/main/java/fzmm/zailer/me/client/gui/components/row/image/ImageRows.java @@ -92,7 +92,7 @@ public static ImageRowsElements setup(FlowLayout rootComponent, String buttonId, imageModeButtons.get(defaultValue).onPress(); suggestionTextBox.setSuggestionSelectedCallback(imageWidget::onPress); - return new ImageRowsElements(imageWidget, suggestionTextBox.getTextBox(), selectedMode, imageModeButtons, suggestionTextBox); + return new ImageRowsElements(imageWidget, suggestionTextBox, selectedMode, imageModeButtons, suggestionTextBox); } public static ImageRows parse(Element element) { diff --git a/src/main/java/fzmm/zailer/me/client/gui/components/style/container/StyledFlowLayout.java b/src/main/java/fzmm/zailer/me/client/gui/components/style/container/StyledFlowLayout.java index ea565b4a..5125bedd 100644 --- a/src/main/java/fzmm/zailer/me/client/gui/components/style/container/StyledFlowLayout.java +++ b/src/main/java/fzmm/zailer/me/client/gui/components/style/container/StyledFlowLayout.java @@ -19,6 +19,7 @@ public class StyledFlowLayout extends FlowLayout { @Nullable private Surface hoveredSurface = null; + private boolean isFocused = false; public StyledFlowLayout(Sizing horizontalSizing, Sizing verticalSizing, Algorithm algorithm) { super(horizontalSizing, verticalSizing, algorithm); @@ -74,11 +75,25 @@ public void parseProperties(UIModel model, Element element, Map @Override public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partialTicks, float delta) { - if (this.isInBoundingBox(mouseX, mouseY) && this.hoveredSurface != null) { + this.surface.draw(context, this); + + if (this.hoveredSurface != null && (this.isInBoundingBox(mouseX, mouseY) || this.isFocused)) { this.hoveredSurface.draw(context, this); } - super.draw(context, mouseX, mouseY, partialTicks, delta); + this.drawChildren(context, mouseX, mouseY, partialTicks, delta, this.children); + } + + @Override + public void onFocusGained(FocusSource source) { + super.onFocusGained(source); + this.isFocused = true; + } + + @Override + public void onFocusLost() { + super.onFocusLost(); + this.isFocused = false; } public static FlowLayout parse(Element element) {