• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyclopsMC / IntegratedDynamics / 16552051255

27 Jul 2025 01:58PM UTC coverage: 53.206% (+8.0%) from 45.161%
16552051255

push

github

rubensworks
Resolve minor TODOs

2888 of 8740 branches covered (33.04%)

Branch coverage included in aggregate %.

17341 of 29280 relevant lines covered (59.22%)

3.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/org/cyclops/integrateddynamics/core/client/gui/WidgetTextFieldDropdown.java
1
package org.cyclops.integrateddynamics.core.client.gui;
2

3
import com.google.common.collect.Lists;
4
import lombok.Getter;
5
import lombok.Setter;
6
import net.minecraft.client.Minecraft;
7
import net.minecraft.client.gui.Font;
8
import net.minecraft.client.gui.GuiGraphics;
9
import net.minecraft.network.chat.Component;
10
import net.minecraft.network.chat.MutableComponent;
11
import net.minecraft.util.ARGB;
12
import net.minecraft.util.FormattedCharSequence;
13
import org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;
14
import org.cyclops.cyclopscore.helper.IModHelpers;
15
import org.lwjgl.glfw.GLFW;
16

17
import javax.annotation.Nullable;
18
import java.util.Collections;
19
import java.util.List;
20
import java.util.Objects;
21
import java.util.Set;
22

23
/**
24
 * A text field that can show a dropdown for autocomplete.
25
 * @param <T> The dropdown entry type.
26
 * @author rubensworks
27
 */
28
public class WidgetTextFieldDropdown<T> extends WidgetTextFieldExtended {
29

30
    private Set<IDropdownEntry<T>> possibilities;
31
    private List<IDropdownEntry<T>> visiblePossibilities = Collections.emptyList();
×
32
    private int visiblePossibilitiesIndex = -1;
×
33
    @Getter
×
34
    private IDropdownEntry<T> selectedDropdownPossibility = null;
35
    @Getter
×
36
    @Setter
×
37
    private int dropdownSize = 5;
38
    @Getter
×
39
    @Setter
×
40
    private IDropdownEntryListener<T> dropdownEntryListener;
41

42
    private int enabledColor = ARGB.opaque(14737632);
×
43
    private int disabledColor = ARGB.opaque(7368816);
×
44

45
    public WidgetTextFieldDropdown(Font fontrenderer, int x, int y, int width, int height,
46
                                   Component narrationMessage, boolean background, Set<IDropdownEntry<T>> possibilities) {
47
        super(fontrenderer, x, y, width, height, narrationMessage, background);
×
48
        setPossibilities(Objects.requireNonNull(possibilities));
×
49
    }
×
50

51
    public WidgetTextFieldDropdown(Font fontrenderer, int x, int y, int width, int height,
52
                                   Component narrationMessage, boolean background) {
53
        this(fontrenderer, x, y, width, height, narrationMessage, background, Collections.emptySet());
×
54
    }
×
55

56
    public void setPossibilities(Set<IDropdownEntry<T>> possibilities) {
57
        this.possibilities = possibilities;
×
58
        this.visiblePossibilities = Collections.emptyList();
×
59
    }
×
60

61
    public int getPossibilitiesCount() {
62
        return possibilities.size();
×
63
    }
64

65
    @Nullable
66
    public IDropdownEntry<T> getVisiblePossibility(int index) {
67
        return visiblePossibilities.get(index);
×
68
    }
69

70
    public void refreshDropdownList() {
71
        // Remove all colors and formatting when changing text
72
        if(getValue().contains("§")) {
×
73
            setValue(getValue().replaceAll("§.", ""));
×
74
        }
75
        if (!possibilities.isEmpty()) {
×
76
            visiblePossibilities = Lists.newArrayList();
×
77
            for (IDropdownEntry<T> possibility : possibilities) {
×
78
                if (possibility.getMatchString().toLowerCase().contains(getValue().toLowerCase())) {
×
79
                    visiblePossibilities.add(possibility);
×
80
                }
81
            }
×
82
            visiblePossibilitiesIndex = -1;
×
83
            if (!visiblePossibilities.isEmpty()) {
×
84
                selectedDropdownPossibility = visiblePossibilities.stream()
×
85
                        .filter(e -> e.getMatchString().equals(getValue()))
×
86
                        .findFirst()
×
87
                        .orElse(null);
×
88
            }
89
            if (dropdownEntryListener != null) {
×
90
                dropdownEntryListener.onSetDropdownPossiblity(selectedDropdownPossibility);
×
91
            }
92
        }
93
    }
×
94
    @Override
95
    public void setFocused(boolean isFocusedIn) {
96
        super.setFocused(isFocusedIn);
×
97
        if (isFocusedIn) {
×
98
            refreshDropdownList();
×
99
        }
100
    }
×
101

102
    @Override
103
    public boolean charTyped(char typedChar, int keyCode) {
104
        if (super.charTyped(typedChar, keyCode)) {
×
105
            refreshDropdownList();
×
106
            return true;
×
107
        }
108
        return false;
×
109
    }
110

111
    @Override
112
    public boolean keyPressed(int typedChar, int keyCode, int modifiers) {
113
        IDropdownEntry<T> oldPossibility = selectedDropdownPossibility;
×
114
        selectedDropdownPossibility = null;
×
115
        if (!possibilities.isEmpty()) {
×
116
            switch (typedChar) {
×
117
                case GLFW.GLFW_KEY_UP:
118
                    if (visiblePossibilitiesIndex >= 0) {
×
119
                        visiblePossibilitiesIndex--;
×
120
                    } else {
121
                        visiblePossibilitiesIndex = visiblePossibilities.size() - 1;
×
122
                    }
123
                    return true;
×
124
                case GLFW.GLFW_KEY_TAB:
125
                case GLFW.GLFW_KEY_DOWN:
126
                    if (visiblePossibilitiesIndex < visiblePossibilities.size() - 1) {
×
127
                        visiblePossibilitiesIndex++;
×
128
                    } else {
129
                        visiblePossibilitiesIndex = 0;
×
130
                    }
131
                    return true;
×
132
                case GLFW.GLFW_KEY_KP_ENTER:
133
                case GLFW.GLFW_KEY_ENTER:
134
                case GLFW.GLFW_KEY_RIGHT:
135
                    if (visiblePossibilitiesIndex >= 0
×
136
                            && visiblePossibilitiesIndex < visiblePossibilities.size()) {
×
137
                        selectVisiblePossibility(visiblePossibilitiesIndex);
×
138
                        return true;
×
139
                    }
140
            }
141
        }
142
        if (super.keyPressed(typedChar, keyCode, modifiers)) {
×
143
            refreshDropdownList();
×
144
            return true;
×
145
        }
146
        selectedDropdownPossibility = oldPossibility;
×
147
        return false;
×
148
    }
149

150
    protected void selectVisiblePossibility(int index) {
151
        visiblePossibilitiesIndex = index;
×
152
        selectPossibility(visiblePossibilities.get(visiblePossibilitiesIndex));
×
153
    }
×
154

155
    public void selectPossibility(@Nullable IDropdownEntry<T> entry) {
156
        selectedDropdownPossibility = entry;
×
157
        setValue(selectedDropdownPossibility != null ? selectedDropdownPossibility.getDisplayString().getString() : "");
×
158
        visiblePossibilities = Lists.newArrayList();
×
159
        visiblePossibilitiesIndex = -1;
×
160
        if (dropdownEntryListener != null) {
×
161
            dropdownEntryListener.onSetDropdownPossiblity(selectedDropdownPossibility);
×
162
        }
163
    }
×
164

165
    @Override
166
    public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
167
        // Display text red that is in an "invalid" state (no valid dropdrown entry selected)
168
        this.setTextColor(this.selectedDropdownPossibility == null ? IModHelpers.get().getBaseHelpers().RGBAToInt(220, 10, 10, 255) : ARGB.opaque(14737632));
×
169

170
        super.renderWidget(guiGraphics, mouseX, mouseY, partialTicks);
×
171
        if (this.isVisible() && isFocused()) {
×
172
            Font fontRenderer = Minecraft.getInstance().font;
×
173
            int yOffset = fontRenderer.lineHeight + 3;
×
174

175
            int x = this.getX();
×
176
            int y = this.getY() + yOffset;
×
177
            int width = this.getWidth() + 9;
×
178
            int startIndex = Math.max(0, Math.min(visiblePossibilitiesIndex, visiblePossibilities.size() - getDropdownSize()));
×
179
            int endIndex = Math.min(startIndex + getDropdownSize(), visiblePossibilities.size());
×
180
            int cy = y;
×
181

182
            // Draw ... if we are not at the first element
183
            if (startIndex > 0) {
×
184
                // Draw background
185
                guiGraphics.fill(x, cy - 1, x + width, cy + 11, -6250336);
×
186
                guiGraphics.fill(x - 1, cy, x + width - 1, cy + 10, -16777216);
×
187

188
                guiGraphics.drawString(fontRenderer, "...", x + 1, cy + 2, disabledColor, true);
×
189

190
                cy += 10;
×
191
            }
192

193
            for (int i = startIndex; i < endIndex; i++) {
×
194
                // Initialize entry
195
                IDropdownEntry<?> dropdownEntry = visiblePossibilities.get(i);
×
196
                MutableComponent possibility = dropdownEntry.getDisplayString();
×
197
                List<FormattedCharSequence> displayPossibility = fontRenderer.split(possibility, width);
×
198
                boolean active = visiblePossibilitiesIndex == i;
×
199
                int entryHeight = yOffset;
×
200

201
                // Optionally initialize tooltip
202
                boolean addTooltip = (active && IModHelpers.get().getMinecraftClientHelpers().isShifted())
×
203
                        || IModHelpers.get().getRenderHelpers().isPointInRegion(x, cy, getWidth(), yOffset, mouseX, mouseY);
×
204
                List<MutableComponent> tooltipLines = null;
×
205
                if (addTooltip) {
×
206
                    tooltipLines = dropdownEntry.getTooltip();
×
207
                    entryHeight += tooltipLines.size() * yOffset;
×
208
                }
209

210
                // Draw background
211
                guiGraphics.fill(x, cy - 1, x + width, cy + entryHeight + 1, -6250336);
×
212
                guiGraphics.fill(x - 1, cy, x + width - 1, cy + entryHeight, -16777216);
×
213

214
                // Draw text
215
                guiGraphics.drawString(fontRenderer, displayPossibility.get(0), x + 1, cy + 2, active ? enabledColor : disabledColor, true);
×
216
                if(addTooltip) {
×
217
                    int tooltipLineOffsetY = 2;
×
218
                    for (Component tooltipLine : tooltipLines) {
×
219
                        tooltipLineOffsetY += yOffset;
×
220
                        guiGraphics.drawString(fontRenderer, tooltipLine.getString(), x + 1, cy + tooltipLineOffsetY, enabledColor, true);
×
221
                    }
×
222
                }
223

224
                cy += entryHeight;
×
225
            }
226

227
            // Draw ... if we haven't reached the end of the list
228
            if (endIndex < visiblePossibilities.size()) {
×
229
                // Draw background
230
                guiGraphics.fill(x, cy - 1, x + width, cy + 11, -6250336);
×
231
                guiGraphics.fill(x - 1, cy, x + width - 1, cy + 10, -16777216);
×
232

233
                guiGraphics.drawString(fontRenderer, "...", x + 1, cy + 2, disabledColor, true);
×
234
            }
235
        }
236
    }
×
237

238
    @Override
239
    public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) {
240
        if (this.isVisible() && isFocused()) {
×
241
            int i = getHoveredVisiblePossibility(mouseX, mouseY);
×
242
            if (i >= 0) {
×
243
                selectVisiblePossibility(i);
×
244
                return true;
×
245
            }
246
        }
247
        return super.mouseClicked(mouseX, mouseY, mouseButton);
×
248
    }
249

250
    public int getHoveredVisiblePossibility(double mouseX, double mouseY) {
251
        Font fontRenderer = Minecraft.getInstance().gui.getFont();
×
252
        int yOffset = fontRenderer.lineHeight + 3;
×
253

254
        int x = this.getX();
×
255
        int y = this.getY() + yOffset;
×
256
        int startIndex = Math.max(0, Math.min(visiblePossibilitiesIndex, visiblePossibilities.size() - getDropdownSize()));
×
257
        int endIndex = Math.min(startIndex + getDropdownSize(), visiblePossibilities.size());
×
258
        int cy = y;
×
259

260
        // Draw ... if we are not at the first element
261
        if (startIndex > 0) {
×
262
            cy += 10;
×
263
        }
264

265
        for (int i = startIndex; i < endIndex; i++) {
×
266
            // Initialize entry
267
            IDropdownEntry<?> dropdownEntry = visiblePossibilities.get(i);
×
268
            boolean active = visiblePossibilitiesIndex == i;
×
269
            int entryHeight = yOffset;
×
270

271
            // Optionally initialize tooltip
272
            boolean addTooltip = (active && IModHelpers.get().getMinecraftClientHelpers().isShifted())
×
273
                    || IModHelpers.get().getRenderHelpers().isPointInRegion(x, cy, getWidth(), yOffset, mouseX, mouseY);
×
274
            if (IModHelpers.get().getRenderHelpers().isPointInRegion(x, cy, getWidth(), yOffset, mouseX, mouseY)) {
×
275
                return i;
×
276
            }
277
            List<MutableComponent> tooltipLines = null;
×
278
            if (addTooltip) {
×
279
                tooltipLines = dropdownEntry.getTooltip();
×
280
                entryHeight += tooltipLines.size() * yOffset;
×
281
            }
282

283
            cy += entryHeight;
×
284
        }
285

286
        return -1;
×
287
    }
288

289
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc