diff options
author | Juuxel <6596629+Juuxel@users.noreply.github.com> | 2020-09-24 12:02:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-24 12:02:38 +0300 |
commit | 016eb1efda675e676e5cde655ba382a77b1552b5 (patch) | |
tree | 453a69a08aabd3b234e7179a7ce6884d95781768 | |
parent | 283db1edc77209344888210c4b618595874707fe (diff) | |
download | LibGui-3.0.0.tar.gz LibGui-3.0.0.tar.bz2 LibGui-3.0.0.zip |
Tabs, card panels and showing/hiding widgets (#74)3.0.0
* Add beta API for hiding and showing widget peers, add default implementation for slots
* Add WCardPanel
* Tab thingies
* Improve WPanel.toString
* Fix tabs, add dark mode
* Add box fillers
* Tabs again
* Tab go brrr
* Revert modmenu changes
* Fix card panels not initialising hidden widgets properly
* Fix slots not being hidden when they should be
* Things
* Revert "Add box fillers"
This reverts commit 1ea1bfbb
* foo
* revert more modmenu changes
* Add tab titles and switch to a builder model for adding tabs
* Document tab builders
* Make hidden widgets release their focus
* Replace outdated since tags with TAB_VERSION
* Fix compilation of WTabPanel
* TAB_VERSION => 3.0.0
* Add focusing support to tabs
18 files changed, 732 insertions, 11 deletions
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java b/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java index a45fee6..69acc85 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java @@ -3,6 +3,7 @@ package io.github.cottonmc.cotton.gui; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import io.github.cottonmc.cotton.gui.widget.WItemSlot; +import io.github.cottonmc.cotton.gui.impl.access.SlotAccessor; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; @@ -16,15 +17,20 @@ import java.util.function.Predicate; public class ValidatedSlot extends Slot { private static final Logger LOGGER = LogManager.getLogger(); private final int slotNumber; + // Original positions that will be restored when this slot is reshown + private final int originalX, originalY; private boolean insertingAllowed = true; private boolean takingAllowed = true; private Predicate<ItemStack> filter; protected final Multimap<WItemSlot, WItemSlot.ChangeListener> listeners = HashMultimap.create(); + private boolean visible = true; public ValidatedSlot(Inventory inventory, int index, int x, int y) { super(inventory, index, x, y); if (inventory==null) throw new IllegalArgumentException("Can't make an itemslot from a null inventory!"); this.slotNumber = index; + this.originalX = x; + this.originalY = y; } @Override @@ -142,4 +148,35 @@ public class ValidatedSlot extends Slot { Objects.requireNonNull(listener, "listener"); listeners.put(owner, listener); } -}
\ No newline at end of file + + /** + * Tests whether this slot is visible. + * + * @return true if this slot is visible, false otherwise + * @since 3.0.0 + */ + public boolean isVisible() { + return visible; + } + + /** + * Sets whether this slot is visible. + * + * @param visible true if this slot if visible, false otherwise + * @since 3.0.0 + */ + public void setVisible(boolean visible) { + if (this.visible != visible) { + this.visible = visible; + + SlotAccessor accessor = (SlotAccessor) this; + if (visible) { + accessor.setX(originalX); + accessor.setY(originalY); + } else { + accessor.setX(-100000); + accessor.setY(-100000); + } + } + } +} 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 01b0c70..8fc53e1 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 @@ -52,6 +52,8 @@ public class CottonClientScreen extends Screen implements TextHoverRendererScree super.init(client, screenWidth, screenHeight); client.keyboard.setRepeatEvents(true); + WPanel root = description.getRootPanel(); + if (root != null) root.addPainters(); description.addPainters(); reposition(screenWidth, screenHeight); } 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 210470d..e3a61a8 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 @@ -65,6 +65,8 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl super.init(client, screenWidth, screenHeight); client.keyboard.setRepeatEvents(true); + WPanel root = description.getRootPanel(); + if (root != null) root.addPainters(); description.addPainters(); reposition(screenWidth, screenHeight); diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java b/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java index ccc9679..cd3fe0a 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java @@ -17,7 +17,7 @@ public class ConfigGui extends LightweightGuiDescription { public ConfigGui(Screen previous) { WGridPanel root = new WGridPanel(); setRootPanel(root); - + WToggleButton darkmodeButton = new WToggleButton(new TranslatableText("option.libgui.darkmode")) { @Override public void onToggle(boolean on) { diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ModMenuSupport.java b/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ModMenuSupport.java index 5cec105..29ea6bf 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ModMenuSupport.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ModMenuSupport.java @@ -21,11 +21,6 @@ public class ModMenuSupport implements ModMenuApi { public void onClose() { this.client.openScreen(screen); } - - protected void init() { - super.init(); - this.description.getRootPanel().validate(null); - }; }; } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/access/SlotAccessor.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/access/SlotAccessor.java new file mode 100644 index 0000000..954b4a8 --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/access/SlotAccessor.java @@ -0,0 +1,14 @@ +package io.github.cottonmc.cotton.gui.impl.access; + +import net.minecraft.screen.slot.Slot; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Slot.class) +public interface SlotAccessor { + @Accessor("x") + void setX(int x); + + @Accessor("y") + void setY(int y); +} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WCardPanel.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WCardPanel.java new file mode 100644 index 0000000..6c6449a --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WCardPanel.java @@ -0,0 +1,189 @@ +package io.github.cottonmc.cotton.gui.widget; + +import io.github.cottonmc.cotton.gui.GuiDescription; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Similar to the CardLayout in AWT, this panel displays one widget at a time from a list of widgets. + * + * @since 3.0.0 + */ +public class WCardPanel extends WPanel { + private final List<WWidget> cards = new ArrayList<>(); + private int selectedIndex = 0; + + /** + * Adds a card to this panel without resizing it. + * + * @param card the added card + */ + public void add(WWidget card) { + add(cards.size(), card); + } + + /** + * Adds a card to this panel without resizing it. + * + * @param index the index of the card + * @param card the added card + */ + public void add(int index, WWidget card) { + cards.add(index, card); + + card.setParent(this); + card.setLocation(0, 0); + expandToFit(card); + } + + /** + * Adds a card to this panel and resizes it. + * + * @param card the added card + * @param width the new width + * @param height the new height + */ + public void add(WWidget card, int width, int height) { + add(cards.size(), card, width, height); + } + + /** + * Adds a card to this panel and resizes it. + * + * @param index the index of the card + * @param card the added card + * @param width the new width + * @param height the new height + */ + public void add(int index, WWidget card, int width, int height) { + if (card.canResize()) { + card.setSize(width, height); + } + + add(index, card); + } + + /** + * Gets the index of the selected card in this panel. + * + * @return the selected card's index + */ + public int getSelectedIndex() { + return selectedIndex; + } + + /** + * Sets the selected index of this panel. + * + * @param selectedIndex the new selected index + * @return this panel + * @throws IndexOutOfBoundsException if this panel does not contain the card index + */ + public WCardPanel setSelectedIndex(int selectedIndex) { + if (selectedIndex < 0 || selectedIndex >= cards.size()) { + throw new IndexOutOfBoundsException("Card index " + selectedIndex + " out of bounds: 0 <= index <" + cards.size()); + } + + if (this.selectedIndex != selectedIndex) { + this.selectedIndex = selectedIndex; + layout(); + } + + return this; + } + + /** + * Gets the selected card of this panel. + * + * @return the selected card + */ + public WWidget getSelectedCard() { + return cards.get(getSelectedIndex()); + } + + /** + * Sets the selected card of this panel. + * + * @param selectedCard the new selected card + * @return this panel + * @throws NoSuchElementException if this panel does not contain the card + */ + public WCardPanel setSelectedCard(WWidget selectedCard) { + if (!cards.contains(selectedCard)) { + throw new NoSuchElementException("Widget " + selectedCard + " is not a card in this panel!"); + } + + return setSelectedIndex(cards.indexOf(selectedCard)); + } + + @Override + public void setSize(int x, int y) { + super.setSize(x, y); + for (WWidget card : cards) { + card.setSize(x, y); + } + } + + @Override + public void layout() { + children.clear(); + + for (WWidget child : cards) { + if (child instanceof WPanel) ((WPanel) child).layout(); + expandToFit(child); + + if (child == getSelectedCard()) { + child.onShown(); + } else { + child.onHidden(); + } + } + + for (WWidget child : cards) { + child.setSize(getWidth(), getHeight()); + } + + children.add(getSelectedCard()); + } + + /** + * {@inheritDoc} + * + * @param c the host GUI description + * @throws IllegalStateException if this panel has no cards + */ + @Override + public void validate(GuiDescription c) { + if (cards.isEmpty()) { + throw new IllegalStateException("No children in card panel"); + } + + layout(); + for (WWidget card : cards) { + card.validate(c); + if (getSelectedCard() != card) card.onHidden(); + } + + if (c != null) createPeers(c); + } + + @SuppressWarnings("deprecation") + @Override + public void createPeers(GuiDescription c) { + for (WWidget card : cards) { + card.createPeers(c); + } + } + + @Environment(EnvType.CLIENT) + @Override + public void addPainters() { + for (WWidget card : cards) { + card.addPainters(); + } + } +} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java index 4ef0056..15e6916 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java @@ -61,7 +61,6 @@ public class WItemSlot extends WWidget { private final List<ValidatedSlot> peers = new ArrayList<>(); @Nullable @Environment(EnvType.CLIENT) - // TODO: Set the background painter to SLOT in a new method that sets a widget's default painter. private BackgroundPainter backgroundPainter = null; private Inventory inventory; private int startIndex = 0; @@ -328,7 +327,9 @@ public class WItemSlot extends WWidget { @Environment(EnvType.CLIENT) @Override public void paint(MatrixStack matrices, int x, int y, int mouseX, int mouseY) { - (backgroundPainter != null ? backgroundPainter : BackgroundPainter.SLOT).paintBackground(x, y, this); + if (backgroundPainter != null) { + backgroundPainter.paintBackground(x, y, this); + } } @Nullable @@ -370,6 +371,28 @@ public class WItemSlot extends WWidget { } } + @Override + public void onShown() { + for (ValidatedSlot peer : peers) { + peer.setVisible(true); + } + } + + @Override + public void onHidden() { + super.onHidden(); + + for (ValidatedSlot peer : peers) { + peer.setVisible(false); + } + } + + @Environment(EnvType.CLIENT) + @Override + public void addPainters() { + backgroundPainter = BackgroundPainter.SLOT; + } + /** * A listener for changes in an item slot. * diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WPanel.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WPanel.java index 10725ee..7265e8d 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WPanel.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WPanel.java @@ -279,7 +279,38 @@ public abstract class WPanel extends WWidget { } @Override + public void onShown() { + for (WWidget child : children) { + child.onShown(); + } + } + + @Override + public void onHidden() { + super.onHidden(); + + for (WWidget child : children) { + child.onHidden(); + } + } + + /** + * {@inheritDoc} + * + * <p>Subclasses should call {@code super.addPainters()} to ensure that children have proper default painters. + * + * @since 3.0.0 + */ + @Environment(EnvType.CLIENT) + @Override + public void addPainters() { + for (WWidget child : children) { + child.addPainters(); + } + } + + @Override public String toString() { - return "WPanel{ children: [\n" + children.stream().map(Objects::toString).flatMap(x -> Stream.of(x.split("\n")).map(y -> "\t" + y)).collect(Collectors.joining(",\n")) + "] }"; + return getClass().getSimpleName() + " {\n" + children.stream().map(Object::toString).map(x -> x + ",").flatMap(x -> Stream.of(x.split("\n")).filter(y -> !y.isEmpty()).map(y -> "\t" + y)).collect(Collectors.joining("\n")) + "\n}"; } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java new file mode 100644 index 0000000..039f2bd --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java @@ -0,0 +1,370 @@ +package io.github.cottonmc.cotton.gui.widget; + +import io.github.cottonmc.cotton.gui.client.BackgroundPainter; +import io.github.cottonmc.cotton.gui.client.LibGuiClient; +import io.github.cottonmc.cotton.gui.client.ScreenDrawing; +import io.github.cottonmc.cotton.gui.widget.data.Axis; +import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment; +import io.github.cottonmc.cotton.gui.widget.icon.Icon; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +// TODO: Different tab positions + +/** + * A panel that contains creative inventory-style tabs on the top. + * + * @since 3.0.0 + */ +public class WTabPanel extends WPanel { + private static final int TAB_PADDING = 4; + private static final int TAB_WIDTH = 28; + private static final int TAB_HEIGHT = 30; + private static final int PANEL_PADDING = 8; // The padding of BackgroundPainter.VANILLA + private static final int ICON_SIZE = 16; + private final WBox tabRibbon = new WBox(Axis.HORIZONTAL).setSpacing(1); + private final List<WTab> tabWidgets = new ArrayList<>(); + private final WCardPanel mainPanel = new WCardPanel(); + + /** + * Constructs a new tab panel. + */ + public WTabPanel() { + add(tabRibbon, 0, 0); + add(mainPanel, PANEL_PADDING, TAB_HEIGHT + PANEL_PADDING); + } + + private void add(WWidget widget, int x, int y) { + children.add(widget); + widget.setParent(this); + widget.setLocation(x, y); + expandToFit(widget); + } + + /** + * Adds a tab to this panel. + * + * @param tab the added tab + */ + public void add(Tab tab) { + WTab tabWidget = new WTab(tab); + + if (tabWidgets.isEmpty()) { + tabWidget.selected = true; + } + + tabWidgets.add(tabWidget); + tabRibbon.add(tabWidget, TAB_WIDTH, TAB_HEIGHT + TAB_PADDING); + mainPanel.add(tab.getWidget()); + } + + /** + * Configures and adds a tab to this panel. + * + * @param widget the contained widget + * @param configurator the tab configurator + */ + public void add(WWidget widget, Consumer<Tab.Builder> configurator) { + Tab.Builder builder = new Tab.Builder(widget); + configurator.accept(builder); + add(builder.build()); + } + + @Override + public void setSize(int x, int y) { + super.setSize(x, y); + tabRibbon.setSize(x, TAB_HEIGHT); + } + + @Environment(EnvType.CLIENT) + @Override + public void addPainters() { + super.addPainters(); + mainPanel.setBackgroundPainter(BackgroundPainter.VANILLA); + } + + /** + * The data of a tab. + */ + public static class Tab { + @Nullable + private final Text title; + @Nullable + private final Icon icon; + private final WWidget widget; + private final Consumer<TooltipBuilder> tooltip; + + /** + * Constructs a tab. + * + * @param title the tab title + * @param icon the tab icon + * @param widget the widget contained in the tab + * @param tooltip the tab tooltip + * @throws IllegalArgumentException if both the title and the icon are null + * @throws NullPointerException if either the widget or the tooltip is null + */ + public Tab(@Nullable Text title, @Nullable Icon icon, WWidget widget, Consumer<TooltipBuilder> tooltip) { + if (title == null && icon == null) { + throw new IllegalArgumentException("A tab must have a title or an icon"); + } + + this.title = title; + this.icon = icon; + this.widget = Objects.requireNonNull(widget, "widget"); + this.tooltip = Objects.requireNonNull(tooltip, "tooltip"); + } + + /** + * Gets the title of this tab. + * + * @return the title, or null if there's no title + */ + @Nullable + public Text getTitle() { + return title; + } + + /** + * Gets the icon of this tab. + * + * @return the icon, or null if there's no title + */ + @Nullable + public Icon getIcon() { + return icon; + } + + /** + * Gets the contained widget of this tab. + * + * @return the contained widget + */ + public WWidget getWidget() { + return widget; + } + + /** + * Adds this widget's tooltip to the {@code tooltip} builder. + * + * @param tooltip the tooltip builder + */ + public void addTooltip(TooltipBuilder tooltip) { + this.tooltip.accept(tooltip); + } + + /** + * A builder for tab data. + */ + public static final class Builder { + @Nullable + private Text title; + @Nullable + private Icon icon; + private final WWidget widget; + private final List<Consumer<TooltipBuilder>> tooltip = new ArrayList<>(); + private static final Consumer<TooltipBuilder> DEFAULT_TOOLTIP = builder -> {}; + + /** + * Constructs a new tab data builder. + * + * @param widget the contained widget + * @throws NullPointerException if the widget is null + */ + public Builder(WWidget widget) { + this.widget = Objects.requireNonNull(widget, "widget"); + } + + /** + * Sets the tab title. + * + * @param title the new title + * @return this builder + * @throws NullPointerException if the title is null + */ + public Builder title(Text title) { + this.title = Objects.requireNonNull(title, "title"); + return this; + } + + /** + * Sets the tab icon. + * + * @param icon the new icon + * @return this builder + * @throws NullPointerException if the icon is null + */ + public Builder icon(Icon icon) { + this.icon = Objects.requireNonNull(icon, "icon"); + return this; + } + + /** + * Adds lines to the tab's tooltip. + * + * @param lines the added lines + * @return this builder + * @throws NullPointerException if the line array is null + */ + public Builder tooltip(Text... lines) { + Objects.requireNonNull(lines, "lines"); + tooltip.add(builder -> builder.add(lines)); + + return this; + } + + /** + * Adds lines to the tab's tooltip. + * + * @param lines the added lines + * @return this builder + * @throws NullPointerException if the line collection is null + */ + public Builder tooltip(Collection<? extends Text> lines) { + Objects.requireNonNull(lines, "lines"); + tooltip.add(builder -> builder.add(lines.toArray(new Text[0]))); + return this; + } + + /** + * Builds a tab from this builder. + * + * @return the built tab + * @see Tab#Tab(Text, Icon, WWidget, Consumer) + */ + public Tab build() { + Consumer<TooltipBuilder> tooltip = DEFAULT_TOOLTIP; + + if (!this.tooltip.isEmpty()) { + tooltip = builder -> { + for (Consumer<TooltipBuilder> entry : this.tooltip) { + entry.accept(builder); + } + }; + } + + return new Tab(title, icon, widget, tooltip); + } + } + } + + private final class WTab extends WWidget { + private final Tab data; + boolean selected = false; + + WTab(Tab data) { + this.data = data; + } + + @Override + public boolean canFocus() { + return true; + } + + @Environment(EnvType.CLIENT) + @Override + public void onClick(int x, int y, int button) { + super.onClick(x, y, button); + + MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + + for (WTab tab : tabWidgets) { + tab.selected = (tab == this); + } + + mainPanel.setSelectedCard(data.getWidget()); + WTabPanel.this.layout(); + } + + @Environment(EnvType.CLIENT) + @Override + public void onKeyPressed(int ch, int key, int modifiers) { + if (isActivationKey(ch)) { + onClick(0, 0, 0); + } + } + + @Environment(EnvType.CLIENT) + @Override + public void paint(MatrixStack matrices, int x, int y, int mouseX, int mouseY) { + TextRenderer renderer = MinecraftClient.getInstance().textRenderer; + Text title = data.getTitle(); + Icon icon = data.getIcon(); + + if (title != null) { + int width = TAB_WIDTH + renderer.getWidth(title); + if (icon == null) width = Math.max(TAB_WIDTH, width - ICON_SIZE); + + if (this.width != width) { + setSize(width, this.height); + getParent().layout(); + } + } + + (selected ? Painters.SELECTED_TAB : Painters.UNSELECTED_TAB).paintBackground(x, y, this); + if (isFocused()) { + (selected ? Painters.SELECTED_TAB_FOCUS_BORDER : Painters.UNSELECTED_TAB_FOCUS_BORDER).paintBackground(x, y, this); + } + + int iconX = 6; + + if (title != null) { + int titleX = (icon != null) ? iconX + ICON_SIZE + 1 : 0; + int titleY = (height - TAB_PADDING - renderer.fontHeight) / 2 + 1; + int width = (icon != null) ? this.width - iconX - ICON_SIZE : this.width; + HorizontalAlignment align = (icon != null) ? HorizontalAlignment.LEFT : HorizontalAlignment.CENTER; + + int color; + if (LibGuiClient.config.darkMode) { + color = WLabel.DEFAULT_DARKMODE_TEXT_COLOR; + } else { + color = selected ? WLabel.DEFAULT_TEXT_COLOR : 0xEEEEEE; + } + + ScreenDrawing.drawString(matrices, title.asOrderedText(), align, x + titleX, y + titleY, width, color); + } + + if (icon != null) { + icon.paint(matrices, x + iconX, y + (height - TAB_PADDING - ICON_SIZE) / 2, ICON_SIZE); + } + } + + @Override + public void addTooltip(TooltipBuilder tooltip) { + data.addTooltip(tooltip); + } + } + + /** + * Internal background painter instances for tabs. + */ + @Environment(EnvType.CLIENT) + final static class Painters { + static final BackgroundPainter SELECTED_TAB = BackgroundPainter.createLightDarkVariants( + BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/selected_light.png")).setTopPadding(2), + BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/selected_dark.png")).setTopPadding(2) + ); + + static final BackgroundPainter UNSELECTED_TAB = BackgroundPainter.createLightDarkVariants( + BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/unselected_light.png")), + BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/unselected_dark.png")) + ); + + static final BackgroundPainter SELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/focus.png")).setTopPadding(2); + static final BackgroundPainter UNSELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/focus.png")); + } +} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java index 789560a..ee9c1e7 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java @@ -3,6 +3,7 @@ package io.github.cottonmc.cotton.gui.widget; import java.util.ArrayList; import java.util.List; +import com.google.common.annotations.Beta; import io.github.cottonmc.cotton.gui.GuiDescription; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -372,7 +373,11 @@ public class WWidget { * @param host the host GUI description */ public void validate(GuiDescription host) { - this.host = host; + if (host != null) { + this.host = host; + } else { + LOGGER.warn("Validating {} with a null host", this); + } } /** @@ -435,6 +440,42 @@ public class WWidget { } /** + * Notifies this widget that it is visible and + * shows any hidden peers of itself and its children. + * + * @since 3.0.0 + */ + @Beta + public void onShown() { + } + + /** + * Notifies this widget that it won't be drawn and + * hides any visible peers of itself and its children. + * + * <p>The default implementation releases this widget's + * focus if it is focused. Overriding implementations + * might want to do this as well. + * + * @since 3.0.0 + */ + @Beta + public void onHidden() { + releaseFocus(); + } + + /** + * Adds the default background painters to this widget and all children. + * + * <p>Always called before {@link GuiDescription#addPainters()} to allow users to modify painters. + * + * @since 3.0.0 + */ + @Environment(EnvType.CLIENT) + public void addPainters() { + } + + /** * Tests if the provided key code is an activation key for widgets. * * <p>The activation keys are Enter, keypad Enter, and Space. diff --git a/src/main/resources/assets/libgui/textures/widget/tab/focus.png b/src/main/resources/assets/libgui/textures/widget/tab/focus.png Binary files differnew file mode 100644 index 0000000..6d25685 --- /dev/null +++ b/src/main/resources/assets/libgui/textures/widget/tab/focus.png diff --git a/src/main/resources/assets/libgui/textures/widget/tab/selected_dark.png b/src/main/resources/assets/libgui/textures/widget/tab/selected_dark.png Binary files differnew file mode 100644 index 0000000..24ba766 --- /dev/null +++ b/src/main/resources/assets/libgui/textures/widget/tab/selected_dark.png diff --git a/src/main/resources/assets/libgui/textures/widget/tab/selected_light.png b/src/main/resources/assets/libgui/textures/widget/tab/selected_light.png Binary files differnew file mode 100644 index 0000000..ce7cbf2 --- /dev/null +++ b/src/main/resources/assets/libgui/textures/widget/tab/selected_light.png diff --git a/src/main/resources/assets/libgui/textures/widget/tab/unselected_dark.png b/src/main/resources/assets/libgui/textures/widget/tab/unselected_dark.png Binary files differnew file mode 100644 index 0000000..ae1d16e --- /dev/null +++ b/src/main/resources/assets/libgui/textures/widget/tab/unselected_dark.png diff --git a/src/main/resources/assets/libgui/textures/widget/tab/unselected_light.png b/src/main/resources/assets/libgui/textures/widget/tab/unselected_light.png Binary files differnew file mode 100644 index 0000000..21ab044 --- /dev/null +++ b/src/main/resources/assets/libgui/textures/widget/tab/unselected_light.png diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 22d9990..df75753 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -21,6 +21,9 @@ "client": ["io.github.cottonmc.cotton.gui.client.LibGuiClient"], "modmenu": ["io.github.cottonmc.cotton.gui.client.modmenu.ModMenuSupport"] }, + "mixins": [ + "mixins.libgui.accessors.json" + ], "depends": { "fabricloader": ">=0.8.8", "fabric": "*", diff --git a/src/main/resources/mixins.libgui.accessors.json b/src/main/resources/mixins.libgui.accessors.json new file mode 100644 index 0000000..9ec55f2 --- /dev/null +++ b/src/main/resources/mixins.libgui.accessors.json @@ -0,0 +1,14 @@ +{ + "compatibilityLevel": "JAVA_8", + "minVersion": "0.7.11", + "package": "io.github.cottonmc.cotton.gui.impl.access", + "required": true, + + "mixins": [ + "SlotAccessor" + ], + + "injectors": { + "defaultRequire": 1 + } +} |