diff options
27 files changed, 893 insertions, 30 deletions
diff --git a/build.gradle b/build.gradle index 9f04998..4e6d927 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,10 @@ archivesBaseName = project.archives_base_name version = "$project.mod_version+$project.minecraft_version" group = project.maven_group +configurations { + javadocClasspath +} + repositories { maven { url "https://server.bbkr.space/artifactory/libs-release" } /*maven { @@ -47,6 +51,8 @@ dependencies { modRuntime(modCompileOnly("com.terraformersmc:modmenu:$project.modmenu_version") { exclude group: 'net.fabricmc.fabric-api' }) + + javadocClasspath project(':javadoc') } processResources { @@ -79,9 +85,16 @@ checkstyle { toolVersion = '8.36.2' } +evaluationDependsOn(':javadoc') + javadoc { + dependsOn project(':javadoc').tasks.jar + options { links("https://maven.fabricmc.net/docs/yarn-$project.yarn_mappings") + taglets 'io.github.cottonmc.cotton.gui.jd.ExperimentalTaglet' + taglets 'io.github.cottonmc.cotton.gui.jd.PropertyTaglet' + tagletPath project(':javadoc').tasks.jar.outputs.files.singleFile } exclude("**/impl/**") diff --git a/javadoc/build.gradle b/javadoc/build.gradle new file mode 100644 index 0000000..e8300bb --- /dev/null +++ b/javadoc/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'java' +} + +sourceCompatibility = rootProject.sourceCompatibility +targetCompatibility = rootProject.targetCompatibility diff --git a/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/ExperimentalTaglet.java b/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/ExperimentalTaglet.java new file mode 100644 index 0000000..dbd2391 --- /dev/null +++ b/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/ExperimentalTaglet.java @@ -0,0 +1,30 @@ +package io.github.cottonmc.cotton.gui.jd; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.doclet.Taglet; + +import java.util.List; +import java.util.Set; +import javax.lang.model.element.Element; + +public class ExperimentalTaglet implements Taglet { + @Override + public Set<Location> getAllowedLocations() { + return Set.of(Location.values()); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "experimental"; + } + + @Override + public String toString(List<? extends DocTree> tags, Element element) { + return "<dt>Experimental API:</dt><dd>Might be modified or removed without prior notice until stabilised.</dd>"; + } +} diff --git a/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java b/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java new file mode 100644 index 0000000..163f1d6 --- /dev/null +++ b/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java @@ -0,0 +1,174 @@ +package io.github.cottonmc.cotton.gui.jd; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.TextTree; +import com.sun.source.util.DocTrees; +import com.sun.source.util.SimpleDocTreeVisitor; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Taglet; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeVisitor; +import javax.lang.model.util.ElementKindVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; + +public class PropertyTaglet implements Taglet { + private static final Pattern PROPERTY_METHOD = Pattern.compile("^(.+)Property$"); + private static final String OBSERVABLE_PROPERTY = "io.github.cottonmc.cotton.gui.widget.data.ObservableProperty"; + private DocTrees docTrees; + + @Override + public void init(DocletEnvironment env, Doclet doclet) { + docTrees = env.getDocTrees(); + } + + @Override + public Set<Location> getAllowedLocations() { + return Set.of(Location.TYPE); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "properties"; + } + + @Override + public String toString(List<? extends DocTree> tags, Element element) { + if (!((element.getKind().isClass() || element.getKind().isInterface()) && element instanceof TypeElement type)) { + throw new IllegalArgumentException("Not a type: " + element); + } + + List<PropertyEntry> myEntries = scan(type); + StringBuilder builder = new StringBuilder(); + + if (!myEntries.isEmpty()) { + builder.append("<div class=\"caption\"><span>Properties</span></div>"); + builder.append("<div class=\"summary-table two-column-summary\">"); + builder.append("<div class=\"table-header col-first\">Property</div>"); + builder.append("<div class=\"table-header col-last\">Description</div>"); + + for (int i = 0; i < myEntries.size(); i++) { + PropertyEntry entry = myEntries.get(i); + String rowClass = (i % 2 == 0) ? "even-row-color" : "odd-row-color"; + builder.append("<div class=\"col-first ").append(rowClass).append("\"><code><span class=\"member-name-link\">"); + builder.append("<a href=\"#").append(entry.name).append("Property()\">").append(entry.name).append("</a>"); + builder.append("</span></code></div>"); + builder.append("<div class=\"col-last ").append(rowClass).append("\">").append(entry.doc).append("</div>"); + } + + builder.append("</div>"); + } + + Map<String, List<PropertyEntry>> inheritedProperties = new LinkedHashMap<>(); + scanParents(type, inheritedProperties); + + inheritedProperties.forEach((name, entries) -> { + if (!entries.isEmpty()) { + builder.append("<dt>Properties from <code>").append(name).append("</code></dt>"); + builder.append("<dd>"); + builder.append(entries.stream().map(entry -> "<code>" + entry.name + "</code>").collect(Collectors.joining(", "))); + builder.append("</dd>"); + } + }); + + return builder.toString(); + } + + private static String getClassName(TypeElement cl) { + return cl.getQualifiedName().toString(); + } + + private List<PropertyEntry> scan(TypeElement cl) { + // Java classes don't have LibGui properties (yet? 😳) + if (getClassName(cl).startsWith("java.")) return List.of(); + + return cl.getEnclosedElements().stream() + .filter(el -> el.getKind() == ElementKind.METHOD) + .map(el -> (ExecutableElement) el) + .filter(el -> el.getReturnType().accept(new ObservableTypeFilter(), null)) + .map(el -> { + Matcher matcher = PROPERTY_METHOD.matcher(el.getSimpleName()); + + if (matcher.matches()) { + String doc = docTrees.getDocCommentTree(el).getFirstSentence().stream() + .map(tree -> tree.accept(new SimpleDocTreeVisitor<String, Void>() { + @Override + public String visitText(TextTree node, Void o) { + return node.getBody(); + } + }, null)) + .filter(Objects::nonNull) + .findAny().orElse(""); + + return new PropertyEntry(matcher.group(1), doc); + } + + return null; + }) + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList()); + } + + private void scanParents(TypeElement cl, Map<String, List<PropertyEntry>> inheritedProperties) { + TypeVisitor<Void, Void> typeVisitor = new SimpleTypeVisitor8<>() { + @Override + public Void visitDeclared(DeclaredType t, Void o) { + return t.asElement().accept(new ElementKindVisitor8<>() { + @Override + public Void visitType(TypeElement e, Object o) { + String fqn = e.getQualifiedName().toString(); + inheritedProperties.put(fqn, scan(e)); + scanParents(e, inheritedProperties); + return null; + } + }, null); + } + }; + + cl.getSuperclass().accept(typeVisitor, null); + cl.getInterfaces().forEach(itf -> itf.accept(typeVisitor, null)); + } + + private static final class ObservableTypeFilter extends SimpleTypeVisitor8<Boolean, Void> { + ObservableTypeFilter() { + super(false); + } + + @Override + public Boolean visitDeclared(DeclaredType t, Void v) { + Element type = t.asElement(); + + if (type.getKind() == ElementKind.CLASS) { + return ((TypeElement) type).getQualifiedName().contentEquals(OBSERVABLE_PROPERTY); + } + + return false; + } + } + + private record PropertyEntry(String name, String doc) implements Comparable<PropertyEntry> { + @Override + public int compareTo(PropertyEntry o) { + return name.compareTo(o.name); + } + } +} diff --git a/settings.gradle b/settings.gradle index 1dfe6ed..4e731fa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,3 +15,4 @@ pluginManagement { rootProject.name = 'LibGui' include ':GuiTest' +include 'javadoc' diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java index cb80707..331f68b 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java @@ -1,6 +1,7 @@ package io.github.cottonmc.cotton.gui.client; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.LiteralText; import net.minecraft.text.Style; @@ -10,6 +11,7 @@ import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.impl.VisualLogger; import io.github.cottonmc.cotton.gui.impl.client.CottonScreenImpl; import io.github.cottonmc.cotton.gui.impl.client.MouseInputHandler; +import io.github.cottonmc.cotton.gui.impl.client.NarrationHelper; import io.github.cottonmc.cotton.gui.widget.WPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; import org.jetbrains.annotations.Nullable; @@ -36,6 +38,8 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { @Nullable protected WWidget lastResponder = null; + + private final MouseInputHandler<CottonClientScreen> mouseInputHandler = new MouseInputHandler<>(this); public CottonClientScreen(GuiDescription description) { this(new LiteralText(""), description); @@ -46,7 +50,8 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { this.description = description; description.getRootPanel().validate(description); } - + + @Override public GuiDescription getDescription() { return description; } @@ -176,7 +181,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return true; - MouseInputHandler.onMouseDown(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseDown(containerX, containerY, mouseButton); return true; } @@ -187,7 +192,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { super.mouseReleased(mouseX, mouseY, mouseButton); int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseUp(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseUp(containerX, containerY, mouseButton); return true; } @@ -199,7 +204,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseDrag(description, this, containerX, containerY, mouseButton, deltaX, deltaY); + mouseInputHandler.onMouseDrag(containerX, containerY, mouseButton, deltaX, deltaY); return true; } @@ -210,7 +215,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseScroll(description, containerX, containerY, amount); + mouseInputHandler.onMouseScroll(containerX, containerY, amount); return true; } @@ -221,7 +226,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseMove(description, containerX, containerY); + mouseInputHandler.onMouseMove(containerX, containerY); } @Override @@ -264,4 +269,9 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { return true; } + + @Override + protected void addElementNarrations(NarrationMessageBuilder builder) { + if (description != null) NarrationHelper.addNarrations(description.getRootPanel(), builder); + } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java index 87d9453..953563c 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java @@ -1,6 +1,7 @@ package io.github.cottonmc.cotton.gui.client; import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; import net.minecraft.client.render.DiffuseLighting; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerEntity; @@ -8,12 +9,15 @@ import net.minecraft.text.LiteralText; import net.minecraft.text.Style; import net.minecraft.text.Text; +import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.SyncedGuiDescription; import io.github.cottonmc.cotton.gui.impl.VisualLogger; import io.github.cottonmc.cotton.gui.impl.client.CottonScreenImpl; import io.github.cottonmc.cotton.gui.impl.client.MouseInputHandler; +import io.github.cottonmc.cotton.gui.impl.client.NarrationHelper; import io.github.cottonmc.cotton.gui.widget.WPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; @@ -26,6 +30,7 @@ import org.lwjgl.opengl.GL11; public class CottonInventoryScreen<T extends SyncedGuiDescription> extends HandledScreen<T> implements CottonScreenImpl { protected SyncedGuiDescription description; @Nullable protected WWidget lastResponder = null; + private final MouseInputHandler<CottonInventoryScreen<T>> mouseInputHandler = new MouseInputHandler<>(this); /** * Constructs a new screen without a title. @@ -83,6 +88,12 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl VisualLogger.reset(); } + @ApiStatus.Internal + @Override + public GuiDescription getDescription() { + return description; + } + @Nullable @Override public WWidget getLastResponder() { @@ -187,7 +198,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return result; - MouseInputHandler.onMouseDown(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseDown(containerX, containerY, mouseButton); return true; } @@ -197,7 +208,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl super.mouseReleased(mouseX, mouseY, mouseButton); int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseUp(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseUp(containerX, containerY, mouseButton); return true; } @@ -208,7 +219,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseDrag(description, this, containerX, containerY, mouseButton, deltaX, deltaY); + mouseInputHandler.onMouseDrag(containerX, containerY, mouseButton, deltaX, deltaY); return true; } @@ -219,7 +230,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseScroll(description, containerX, containerY, amount); + mouseInputHandler.onMouseScroll(containerX, containerY, amount); return true; } @@ -230,7 +241,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseMove(description, containerX, containerY); + mouseInputHandler.onMouseMove(containerX, containerY); } @Override @@ -304,4 +315,9 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl return true; } + + @Override + protected void addElementNarrations(NarrationMessageBuilder builder) { + if (description != null) NarrationHelper.addNarrations(description.getRootPanel(), builder); + } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java index 2ef9632..cd215b3 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java @@ -5,11 +5,14 @@ import net.fabricmc.api.Environment; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Style; +import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.widget.WWidget; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public interface CottonScreenImpl { + GuiDescription getDescription(); + @Nullable WWidget getLastResponder(); diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java index 45fc17f..055df75 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java @@ -2,9 +2,9 @@ package io.github.cottonmc.cotton.gui.impl.client; import net.minecraft.client.gui.screen.Screen; -import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.widget.WWidget; import io.github.cottonmc.cotton.gui.widget.data.InputResult; +import io.github.cottonmc.cotton.gui.widget.data.ObservableProperty; import org.jetbrains.annotations.Nullable; import java.util.function.Function; @@ -12,10 +12,21 @@ import java.util.function.Function; /** * The implementation for mouse inputs. */ -public final class MouseInputHandler { - public static void onMouseDown(GuiDescription description, CottonScreenImpl screen, int containerX, int containerY, int mouseButton) { +public final class MouseInputHandler<S extends Screen & CottonScreenImpl> { + private final S screen; + private final ObservableProperty<WWidget> hovered = ObservableProperty.of(null); + + public MouseInputHandler(S screen) { + this.screen = screen; + hovered.addListener((property, from, to) -> { + if (from != null) from.setHovered(false); + if (to != null) to.setHovered(true); + }); + } + + public void onMouseDown(int containerX, int containerY, int mouseButton) { if (screen.getLastResponder() == null) { - WWidget lastResponder = description.getRootPanel().hit(containerX, containerY); + WWidget lastResponder = screen.getDescription().getRootPanel().hit(containerX, containerY); screen.setLastResponder(lastResponder); if (lastResponder != null) { runTree( @@ -28,7 +39,7 @@ public final class MouseInputHandler { } } - public static <S extends Screen & CottonScreenImpl> void onMouseUp(GuiDescription description, S screen, int containerX, int containerY, int mouseButton) { + public void onMouseUp(int containerX, int containerY, int mouseButton) { WWidget lastResponder = screen.getLastResponder(); if (lastResponder != null) { @@ -48,7 +59,7 @@ public final class MouseInputHandler { } } else { runTree( - description.getRootPanel().hit(containerX, containerY), + screen.getDescription().getRootPanel().hit(containerX, containerY), widget -> widget.onMouseUp(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY(), mouseButton) ); } @@ -56,7 +67,7 @@ public final class MouseInputHandler { screen.setLastResponder(null); } - public static <S extends Screen & CottonScreenImpl> void onMouseDrag(GuiDescription description, S screen, int containerX, int containerY, int mouseButton, double deltaX, double deltaY) { + public void onMouseDrag(int containerX, int containerY, int mouseButton, double deltaX, double deltaY) { WWidget lastResponder = screen.getLastResponder(); if (lastResponder != null) { @@ -68,22 +79,25 @@ public final class MouseInputHandler { if (containerX < 0 || containerY < 0 || containerX >= width || containerY >= height) return; runTree( - description.getRootPanel().hit(containerX, containerY), + screen.getDescription().getRootPanel().hit(containerX, containerY), widget -> widget.onMouseDrag(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY(), mouseButton, deltaX, deltaY) ); } } - public static void onMouseScroll(GuiDescription description, int containerX, int containerY, double amount) { + public void onMouseScroll(int containerX, int containerY, double amount) { runTree( - description.getRootPanel().hit(containerX, containerY), + screen.getDescription().getRootPanel().hit(containerX, containerY), widget -> widget.onMouseScroll(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY(), amount) ); } - public static void onMouseMove(GuiDescription description, int containerX, int containerY) { + public void onMouseMove(int containerX, int containerY) { + WWidget hit = screen.getDescription().getRootPanel().hit(containerX, containerY); + hovered.set(hit); + runTree( - description.getRootPanel().hit(containerX, containerY), + hit, widget -> widget.onMouseMove(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY()) ); } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java new file mode 100644 index 0000000..54567be --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java @@ -0,0 +1,49 @@ +package io.github.cottonmc.cotton.gui.impl.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.text.TranslatableText; + +import io.github.cottonmc.cotton.gui.widget.WPanel; +import io.github.cottonmc.cotton.gui.widget.WWidget; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Environment(EnvType.CLIENT) +public final class NarrationHelper { + public static void addNarrations(WPanel rootPanel, NarrationMessageBuilder builder) { + List<WWidget> narratableWidgets = getAllWidgets(rootPanel) + .filter(WWidget::isNarratable) + .collect(Collectors.toList()); + + for (int i = 0, childCount = narratableWidgets.size(); i < childCount; i++) { + WWidget child = narratableWidgets.get(i); + if (!child.isFocused() && !child.isHovered()) continue; + + // replicates Screen.addElementNarrations + if (narratableWidgets.size() > 1) { + builder.put(NarrationPart.POSITION, new TranslatableText(NarrationMessages.Vanilla.SCREEN_POSITION_KEY, i + 1, childCount)); + + if (child.isFocused()) { + builder.put(NarrationPart.USAGE, NarrationMessages.Vanilla.COMPONENT_LIST_USAGE); + } + } + + child.addNarrations(builder.nextMessage()); + } + } + + private static Stream<WWidget> getAllWidgets(WPanel panel) { + return Stream.concat(Stream.of(panel), panel.streamChildren().flatMap(widget -> { + if (widget instanceof WPanel nested) { + return getAllWidgets(nested); + } + + return Stream.of(widget); + })); + } +} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationMessages.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationMessages.java new file mode 100644 index 0000000..bc9ee48 --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationMessages.java @@ -0,0 +1,29 @@ +package io.github.cottonmc.cotton.gui.impl.client; + +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; + +public final class NarrationMessages { + public static final String ITEM_SLOT_TITLE_KEY = "widget.libgui.item_slot.narration.title"; + public static final String LABELED_SLIDER_TITLE_KEY = "widget.libgui.labeled_slider.narration.title"; + public static final Text PLAYER_INVENTORY_HOTBAR = new TranslatableText("widget.libgui.player_inventory.narration.hotbar"); + public static final Text SCROLL_BAR_TITLE = new TranslatableText("widget.libgui.scroll_bar.narration.title"); + public static final String SLIDER_MESSAGE_KEY = "widget.libgui.slider.narration.title"; + public static final Text SLIDER_USAGE = new TranslatableText("widget.libgui.slider.narration.usage"); + public static final String TAB_TITLE_KEY = "widget.libgui.tab.narration.title"; + public static final String TAB_POSITION_KEY = "widget.libgui.tab.narration.position"; + public static final String TEXT_FIELD_TITLE_KEY = "widget.libgui.text_field.narration.title"; + public static final String TEXT_FIELD_SUGGESTION_KEY = "widget.libgui.text_field.narration.suggestion"; + public static final String TOGGLE_BUTTON_NAMED_KEY = "widget.libgui.toggle_button.narration.named"; + public static final Text TOGGLE_BUTTON_OFF = new TranslatableText("widget.libgui.toggle_button.narration.off"); + public static final Text TOGGLE_BUTTON_ON = new TranslatableText("widget.libgui.toggle_button.narration.on"); + public static final String TOGGLE_BUTTON_UNNAMED_KEY = "widget.libgui.toggle_button.narration.unnamed"; + + public static final class Vanilla { + public static final Text BUTTON_USAGE_FOCUSED = new TranslatableText("narration.button.usage.focused"); + public static final Text BUTTON_USAGE_HOVERED = new TranslatableText("narration.button.usage.hovered"); + public static final Text COMPONENT_LIST_USAGE = new TranslatableText("narration.component_list.usage"); + public static final Text INVENTORY = new TranslatableText("container.inventory"); + public static final String SCREEN_POSITION_KEY = "narrator.position.screen"; + } +} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java index 7fda1d9..2af6092 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java @@ -2,8 +2,12 @@ package io.github.cottonmc.cotton.gui.widget; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.text.TranslatableText; import net.minecraft.util.math.MathHelper; +import io.github.cottonmc.cotton.gui.impl.client.NarrationMessages; import io.github.cottonmc.cotton.gui.widget.data.Axis; import io.github.cottonmc.cotton.gui.widget.data.InputResult; import org.jetbrains.annotations.Nullable; @@ -342,6 +346,13 @@ public abstract class WAbstractSlider extends WWidget { return dragging; } + @Environment(EnvType.CLIENT) + @Override + public void addNarrations(NarrationMessageBuilder builder) { + builder.put(NarrationPart.TITLE, new TranslatableText(NarrationMessages.SLIDER_MESSAGE_KEY, value, min, max)); + builder.put(NarrationPart.USAGE, NarrationMessages.SLIDER_USAGE); + } + /** * Tests if the key should decrease sliders with the specified direction. * diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java index 15cd6bb..533e436 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/wid |
