├── jitpack.yml ├── .git-blame-ignore-revs ├── CODEOWNERS ├── repositories.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── modularui │ │ │ ├── textures │ │ │ └── gui │ │ │ │ ├── slot │ │ │ │ ├── item.png │ │ │ │ ├── fluid.png │ │ │ │ └── fluid_tank.png │ │ │ │ ├── icons │ │ │ │ ├── cross.png │ │ │ │ ├── arrow_up.png │ │ │ │ ├── arrow_down.png │ │ │ │ ├── arrow_left.png │ │ │ │ ├── cross_gray.png │ │ │ │ ├── arrow_gray_up.png │ │ │ │ ├── arrow_right.png │ │ │ │ ├── arrow_gray_down.png │ │ │ │ ├── arrow_gray_left.png │ │ │ │ └── arrow_gray_right.png │ │ │ │ ├── tab │ │ │ │ ├── tabs_left.png │ │ │ │ ├── tabs_right.png │ │ │ │ ├── tabs_top.png │ │ │ │ └── tabs_bottom.png │ │ │ │ ├── background │ │ │ │ ├── display.png │ │ │ │ ├── background.png │ │ │ │ ├── vanilla_background.png │ │ │ │ └── background_border_1px.png │ │ │ │ └── widgets │ │ │ │ ├── base_button.png │ │ │ │ ├── information.png │ │ │ │ ├── cycle_button_demo.png │ │ │ │ ├── progress_bar_arrow.png │ │ │ │ ├── progress_bar_mixer.png │ │ │ │ ├── vanilla_button_normal.png │ │ │ │ ├── vanilla_button_disabled.png │ │ │ │ └── vanilla_button_hovered.png │ │ │ └── lang │ │ │ └── en_US.lang │ ├── mixins.modularui.json │ ├── mixins.modularui.late.json │ ├── mixins.modularui.early.json │ └── mcmod.info │ └── java │ └── com │ └── gtnewhorizons │ └── modularui │ ├── api │ ├── widget │ │ ├── scroll │ │ │ ├── ScrollType.java │ │ │ ├── IVerticalScrollable.java │ │ │ └── IHorizontalScrollable.java │ │ ├── IHasStackUnderMouse.java │ │ ├── IWidgetDrawable.java │ │ ├── IDragAndDropHandler.java │ │ ├── IVanillaSlot.java │ │ ├── IDraggable.java │ │ ├── ISyncedWidget.java │ │ └── IWidgetBuilder.java │ ├── animation │ │ ├── IEase.java │ │ ├── Eases.java │ │ └── Interpolator.java │ ├── screen │ │ ├── IWindowCreator.java │ │ ├── ITileWithModularUI.java │ │ ├── IItemWithModularUI.java │ │ ├── IContainerCreator.java │ │ ├── IGuiCreator.java │ │ ├── DraggableWindowWrapper.java │ │ └── UIBuildContext.java │ ├── math │ │ ├── CrossAxisAlignment.java │ │ ├── MainAxisAlignment.java │ │ ├── Alignment.java │ │ └── Size.java │ ├── forge │ │ ├── IItemHandlerModifiable.java │ │ ├── INBTSerializable.java │ │ ├── IItemHandler.java │ │ ├── PlayerMainInvWrapper.java │ │ ├── RangedWrapper.java │ │ ├── ListItemHandler.java │ │ ├── CraftingHelper.java │ │ └── SlotItemHandler.java │ ├── drawable │ │ ├── SizedDrawable.java │ │ ├── OffsetDrawable.java │ │ ├── FluidDrawable.java │ │ ├── RotatedDrawable.java │ │ ├── RepeatingDrawable.java │ │ ├── ItemDrawable.java │ │ ├── FallbackableUITexture.java │ │ └── shapes │ │ │ └── Circle.java │ ├── KeyboardUtil.java │ ├── fluids │ │ ├── IFluidTanksHandler.java │ │ ├── FluidTanksHandler.java │ │ ├── IFluidTankLong.java │ │ ├── FluidTankLongDelegate.java │ │ └── ListFluidHandler.java │ ├── ModularUITextures.java │ └── UIInfos.java │ ├── common │ ├── fluid │ │ ├── IOverflowableTank.java │ │ └── FluidStackTank.java │ ├── widget │ │ ├── textfield │ │ │ └── TextEditorWidget.java │ │ ├── SingleChildWidget.java │ │ ├── WidgetJsonRegistry.java │ │ ├── TabButton.java │ │ ├── SyncedWidget.java │ │ ├── DrawableWidget.java │ │ ├── TabContainer.java │ │ ├── MultiChildWidget.java │ │ ├── VanillaButtonWidget.java │ │ ├── DynamicPositionedRow.java │ │ ├── DynamicPositionedColumn.java │ │ ├── DynamicTextWidget.java │ │ ├── PageControlWidget.java │ │ ├── Row.java │ │ ├── Column.java │ │ └── ButtonWidget.java │ ├── internal │ │ ├── network │ │ │ ├── IPacket.java │ │ │ ├── CWidgetUpdate.java │ │ │ ├── SWidgetUpdate.java │ │ │ └── NetworkHandler.java │ │ ├── wrapper │ │ │ ├── FluidTankHandler.java │ │ │ └── MultiList.java │ │ ├── InternalUIMapper.java │ │ ├── Theme.java │ │ └── JsonLoader.java │ ├── builder │ │ ├── UIBuilder.java │ │ └── UIInfo.java │ └── peripheral │ │ └── ModularUIPeripheralInputHandler.java │ ├── config │ ├── GuiFactory.java │ └── ModularUIGuiConfig.java │ ├── mixinplugin │ ├── LateMixinLoader.java │ └── EarlyMixinLoader.java │ ├── test │ └── TestBlock.java │ ├── mixins │ ├── early │ │ ├── GuiContainerAccessor.java │ │ ├── NetHandlerPlayClientMixin.java │ │ └── PacketBufferMixin.java │ └── late │ │ └── NEIControllerMixin.java │ ├── CommonProxy.java │ ├── ClientProxy.java │ ├── integration │ └── nei │ │ └── ModularUIContainerObjectHandler.java │ └── ModularUI.java ├── addon.gradle ├── .github └── workflows │ ├── release-tags.yml │ └── build-and-test.yml ├── README.MD ├── .editorconfig ├── settings.gradle ├── .gitignore ├── dependencies.gradle ├── .gitattributes └── gradlew.bat /jitpack.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - ./gradlew setupCIWorkspace -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Ignore spotlessApply reformat 2 | 407f1f4b07ca2183b2f46e95992a5c5822af817f 3 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Any Github changes require admin approval 2 | /.github/** @GTNewHorizons/admin 3 | 4 | -------------------------------------------------------------------------------- /repositories.gradle: -------------------------------------------------------------------------------- 1 | // Add any additional repositories for your dependencies here 2 | 3 | repositories { 4 | } 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/slot/item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/slot/item.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/cross.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/slot/fluid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/slot/fluid.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_up.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/tab/tabs_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/tab/tabs_left.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/tab/tabs_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/tab/tabs_right.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/tab/tabs_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/tab/tabs_top.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_down.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_left.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/cross_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/cross_gray.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/slot/fluid_tank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/slot/fluid_tank.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/tab/tabs_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/tab/tabs_bottom.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/background/display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/background/display.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_up.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_right.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/base_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/base_button.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/information.png -------------------------------------------------------------------------------- /addon.gradle: -------------------------------------------------------------------------------- 1 | compileJava { 2 | options.encoding = "UTF-8" 3 | } 4 | test { 5 | useJUnitPlatform() 6 | testLogging { 7 | events "passed", "skipped", "failed" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/background/background.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_down.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_left.png -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/scroll/ScrollType.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget.scroll; 2 | 3 | public enum ScrollType { 4 | HORIZONTAL, 5 | VERTICAL 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/icons/arrow_gray_right.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/cycle_button_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/cycle_button_demo.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/progress_bar_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/progress_bar_arrow.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/progress_bar_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/progress_bar_mixer.png -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/fluid/IOverflowableTank.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.fluid; 2 | 3 | public interface IOverflowableTank { 4 | 5 | int getRealCapacity(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/background/vanilla_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/background/vanilla_background.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/vanilla_button_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/vanilla_button_normal.png -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/animation/IEase.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.animation; 2 | 3 | @FunctionalInterface 4 | public interface IEase { 5 | 6 | float interpolate(float t); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/vanilla_button_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/vanilla_button_disabled.png -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/widgets/vanilla_button_hovered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/widgets/vanilla_button_hovered.png -------------------------------------------------------------------------------- /src/main/resources/mixins.modularui.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8.5-GTNH", 4 | "refmap": "mixins.modularui.refmap.json", 5 | "target": "@env(DEFAULT)", 6 | "compatibilityLevel": "JAVA_8" 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/textures/gui/background/background_border_1px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTNewHorizons/ModularUI/HEAD/src/main/resources/assets/modularui/textures/gui/background/background_border_1px.png -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/IHasStackUnderMouse.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | import net.minecraft.item.ItemStack; 4 | 5 | public interface IHasStackUnderMouse { 6 | 7 | ItemStack getStackUnderMouse(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/IWindowCreator.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | 5 | public interface IWindowCreator { 6 | 7 | ModularWindow create(EntityPlayer player); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/mixins.modularui.late.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8.5-GTNH", 4 | "package": "com.gtnewhorizons.modularui.mixins.late", 5 | "refmap": "mixins.modularui.refmap.json", 6 | "target": "@env(DEFAULT)", 7 | "compatibilityLevel": "JAVA_8" 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/mixins.modularui.early.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8.5-GTNH", 4 | "package": "com.gtnewhorizons.modularui.mixins.early", 5 | "refmap": "mixins.modularui.refmap.json", 6 | "target": "@env(DEFAULT)", 7 | "compatibilityLevel": "JAVA_8" 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/workflows/release-tags.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Release tagged build 3 | 4 | on: 5 | push: 6 | tags: [ '*' ] 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | release-tags: 13 | uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master 14 | secrets: inherit 15 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # ModularUI - GUI library mod for Minecraft 1.7.10 2 | 3 | This mod is a GUI library to ease the process of creating GUIs. Backported from 1.12.2 [ModularUI](https://github.com/CleanroomMC/ModularUI). 4 | Requires [GTNHMixins](https://github.com/GTNewHorizons/GTNHMixins) to work. 5 | 6 | This mod is under LGPL-3 license. 7 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Build and test 3 | 4 | on: 5 | pull_request: 6 | branches: [ master, main ] 7 | push: 8 | branches: [ master, main ] 9 | 10 | jobs: 11 | build-and-test: 12 | uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/ITileWithModularUI.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | /** 4 | * Implement this interface for your {@link net.minecraft.tileentity.TileEntity} to display UI 5 | */ 6 | public interface ITileWithModularUI { 7 | 8 | ModularWindow createWindow(UIBuildContext buildContext); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/math/CrossAxisAlignment.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.math; 2 | 3 | /** 4 | * Alignment for "sub" axis, i.e. horizontal for {@link com.gtnewhorizons.modularui.common.widget.Column}, vertical for 5 | * {@link com.gtnewhorizons.modularui.common.widget.Row}. 6 | */ 7 | public enum CrossAxisAlignment { 8 | START, 9 | CENTER, 10 | END; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/IItemWithModularUI.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | import net.minecraft.item.ItemStack; 4 | 5 | /** 6 | * Implement this interface for your {@link net.minecraft.item.Item} to display UI 7 | */ 8 | public interface IItemWithModularUI { 9 | 10 | ModularWindow createWindow(UIBuildContext buildContext, ItemStack heldStack); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/IItemHandlerModifiable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | // 3 | // Source code recreated from a .class file by IntelliJ IDEA 4 | // (powered by FernFlower decompiler) 5 | // 6 | 7 | import net.minecraft.item.ItemStack; 8 | 9 | public interface IItemHandlerModifiable extends IItemHandler { 10 | 11 | void setStackInSlot(int var1, ItemStack var2); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/IContainerCreator.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraft.inventory.Container; 5 | import net.minecraft.world.World; 6 | 7 | @FunctionalInterface 8 | public interface IContainerCreator { 9 | 10 | Container create(EntityPlayer player, World world, int x, int y, int z); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/INBTSerializable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | 3 | // 4 | // Source code recreated from a .class file by IntelliJ IDEA 5 | // (powered by FernFlower decompiler) 6 | // 7 | 8 | import net.minecraft.nbt.NBTBase; 9 | 10 | public interface INBTSerializable { 11 | 12 | T serializeNBT(); 13 | 14 | void deserializeNBT(T var1); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/math/MainAxisAlignment.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.math; 2 | 3 | /** 4 | * Alignment for "main" axis, i.e. vertical for {@link com.gtnewhorizons.modularui.common.widget.Column}, horizontal for 5 | * {@link com.gtnewhorizons.modularui.common.widget.Row}. 6 | */ 7 | public enum MainAxisAlignment { 8 | START, 9 | CENTER, 10 | END, 11 | SPACE_BETWEEN; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/scroll/IVerticalScrollable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget.scroll; 2 | 3 | public interface IVerticalScrollable { 4 | 5 | void setVerticalScrollOffset(int offset); 6 | 7 | int getVerticalScrollOffset(); 8 | 9 | default int getVerticalBarWidth() { 10 | return 2; 11 | } 12 | 13 | int getVisibleHeight(); 14 | 15 | int getActualHeight(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/textfield/TextEditorWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget.textfield; 2 | 3 | /** 4 | * A non syncable, multiline text input widget. Meant for client only screens to edit large amounts of text. 5 | */ 6 | public class TextEditorWidget extends BaseTextFieldWidget { 7 | 8 | public TextEditorWidget() { 9 | this.handler.setMaxLines(10000); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/IWidgetDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | @FunctionalInterface 4 | public interface IWidgetDrawable { 5 | 6 | /** 7 | * Is called before any other draw calls in gui 8 | * 9 | * @param widget widget to draw 10 | * @param partialTicks ticks since last draw 11 | */ 12 | void drawWidgetCustom(Widget widget, float partialTicks); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/IGuiCreator.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraft.world.World; 5 | 6 | import cpw.mods.fml.relauncher.Side; 7 | import cpw.mods.fml.relauncher.SideOnly; 8 | 9 | @FunctionalInterface 10 | public interface IGuiCreator { 11 | 12 | @SideOnly(Side.CLIENT) 13 | Object create(EntityPlayer player, World world, int x, int y, int z); 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This is the universal Text Editor Configuration 2 | # for all GTNewHorizons projects 3 | # See: https://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 4 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.{bat,ini}] 16 | end_of_line = crlf 17 | 18 | [*.{dtd,json,info,mcmeta,md,sh,svg,xml,xsd,xsl,yaml,yml}] 19 | indent_size = 2 20 | 21 | [*.lang] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/scroll/IHorizontalScrollable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget.scroll; 2 | 3 | import org.jetbrains.annotations.Range; 4 | 5 | public interface IHorizontalScrollable { 6 | 7 | void setHorizontalScrollOffset(int offset); 8 | 9 | int getHorizontalScrollOffset(); 10 | 11 | default @Range(from = 1, to = 20) int getHorizontalBarHeight() { 12 | return 2; 13 | } 14 | 15 | int getVisibleWidth(); 16 | 17 | int getActualWidth(); 18 | } 19 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | pluginManagement { 3 | repositories { 4 | maven { 5 | // RetroFuturaGradle 6 | name "GTNH Maven" 7 | url "https://nexus.gtnewhorizons.com/repository/public/" 8 | mavenContent { 9 | includeGroup("com.gtnewhorizons") 10 | includeGroupByRegex("com\\.gtnewhorizons\\..+") 11 | } 12 | } 13 | gradlePluginPortal() 14 | mavenCentral() 15 | mavenLocal() 16 | } 17 | } 18 | 19 | plugins { 20 | id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.43' 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .settings 3 | /.idea/ 4 | /.vscode/ 5 | /run/ 6 | /build/ 7 | /eclipse/ 8 | .classpath 9 | .project 10 | /bin/ 11 | /config/ 12 | /crash-reports/ 13 | /logs/ 14 | options.txt 15 | /saves/ 16 | usernamecache.json 17 | banned-ips.json 18 | banned-players.json 19 | eula.txt 20 | ops.json 21 | server.properties 22 | servers.dat 23 | usercache.json 24 | whitelist.json 25 | /out/ 26 | *.iml 27 | *.ipr 28 | *.iws 29 | src/main/resources/mixins.*([!.]).json 30 | *.bat 31 | *.DS_Store 32 | !gradlew.bat 33 | .factorypath 34 | addon.local.gradle 35 | addon.local.gradle.kts 36 | addon.late.local.gradle 37 | addon.late.local.gradle.kts 38 | layout.json 39 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/IDragAndDropHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | import net.minecraft.item.ItemStack; 4 | 5 | /** 6 | * Implement this interface for your widget to handle drag-and-drop from NEI. 7 | */ 8 | public interface IDragAndDropHandler { 9 | 10 | /** 11 | * Implement your drag-and-drop behavior here. The held stack will be deleted if draggedStack.stackSize == 0. 12 | * 13 | * @param draggedStack Item dragged from NEI 14 | * @param button 0 = left click, 1 = right click 15 | * @return True if success 16 | */ 17 | boolean handleDragAndDrop(ItemStack draggedStack, int button); 18 | } 19 | -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | // Add your dependencies here 2 | 3 | dependencies { 4 | api("org.jetbrains:annotations:23.0.0") 5 | 6 | api("com.github.GTNewHorizons:NotEnoughItems:2.8.9-GTNH:dev") 7 | 8 | implementation("com.github.GTNewHorizons:GTNHLib:0.7.0:dev") { transitive = false } 9 | compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.2:dev") { transitive = false } 10 | compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.24:dev") { 11 | transitive = false 12 | exclude group:"com.github.GTNewHorizons", module:"ModularUI" 13 | } 14 | 15 | testImplementation(platform('org.junit:junit-bom:5.9.2')) 16 | testImplementation('org.junit.jupiter:junit-jupiter') 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/animation/Eases.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.animation; 2 | 3 | public enum Eases implements IEase { 4 | 5 | EaseLinear(input -> input), 6 | EaseQuadIn(input -> input * input), 7 | EaseQuadInOut(input -> { 8 | if ((input /= 0.5f) < 1) { 9 | return 0.5f * input * input; 10 | } 11 | return -0.5f * ((--input) * (input - 2) - 1); 12 | }), 13 | EaseQuadOut(input -> -input * (input - 2)); 14 | 15 | IEase ease; 16 | 17 | Eases(IEase ease) { 18 | this.ease = ease; 19 | } 20 | 21 | @Override 22 | public float interpolate(float t) { 23 | return ease.interpolate(t); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/IVanillaSlot.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.function.Function; 6 | 7 | import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; 8 | 9 | public interface IVanillaSlot { 10 | 11 | BaseSlot getMcSlot(); 12 | 13 | /** 14 | * Called when slot has actual ItemStack. If slot is empty, {@link Widget#getTooltip()} is called instead. 15 | */ 16 | default List getExtraTooltip() { 17 | return Collections.emptyList(); 18 | } 19 | 20 | default Function, List> getOverwriteItemStackTooltip() { 21 | return list -> list; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | { 2 | "modListVersion": 2, 3 | "modList": [{ 4 | "modid": "${modId}", 5 | "name": "${modName}", 6 | "description": "A GUI library to ease the process of creating GUIs. Backported from 1.12.2.", 7 | "version": "${modVersion}", 8 | "mcversion": "${minecraftVersion}", 9 | "url": "https://github.com/GTNewHorizons/ModularUI", 10 | "updateUrl": "", 11 | "authorList": [ 12 | "brachy84", 13 | "Rongmario", 14 | "CleanroomMC", 15 | "Quarri6343 (1.7.10 port)", 16 | "miozune (1.7.10 port)" 17 | ], 18 | "credits": "CleanroomMC for implementing the original mod. Link: https://github.com/CleanroomMC/ModularUI", 19 | "logoFile": "", 20 | "screenshots": [], 21 | "parent": "", 22 | "requiredMods": [], 23 | "dependencies": [], 24 | "dependants": [] 25 | }] 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/config/GuiFactory.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.config; 2 | 3 | import java.util.Set; 4 | 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.GuiScreen; 7 | 8 | import cpw.mods.fml.client.IModGuiFactory; 9 | 10 | @SuppressWarnings("unused") 11 | public class GuiFactory implements IModGuiFactory { 12 | 13 | @Override 14 | public void initialize(Minecraft minecraftInstance) {} 15 | 16 | @Override 17 | public Class mainConfigGuiClass() { 18 | return ModularUIGuiConfig.class; 19 | } 20 | 21 | @Override 22 | public Set runtimeGuiCategories() { 23 | return null; 24 | } 25 | 26 | @Override 27 | public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) { 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/mixinplugin/LateMixinLoader.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.mixinplugin; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import com.gtnewhorizon.gtnhmixins.ILateMixinLoader; 8 | import com.gtnewhorizon.gtnhmixins.LateMixin; 9 | 10 | import cpw.mods.fml.relauncher.FMLLaunchHandler; 11 | 12 | @LateMixin 13 | public class LateMixinLoader implements ILateMixinLoader { 14 | 15 | @Override 16 | public String getMixinConfig() { 17 | return "mixins.modularui.late.json"; 18 | } 19 | 20 | @Override 21 | public List getMixins(Set loadedMods) { 22 | final List mixins = new ArrayList<>(); 23 | if (FMLLaunchHandler.side().isClient()) { 24 | if (loadedMods.contains("NotEnoughItems")) mixins.add("NEIControllerMixin"); 25 | } 26 | return mixins; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/network/IPacket.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal.network; 2 | 3 | import net.minecraft.client.network.NetHandlerPlayClient; 4 | import net.minecraft.network.NetHandlerPlayServer; 5 | import net.minecraft.network.PacketBuffer; 6 | 7 | import cpw.mods.fml.common.network.simpleimpl.IMessage; 8 | import cpw.mods.fml.relauncher.Side; 9 | import cpw.mods.fml.relauncher.SideOnly; 10 | import io.netty.buffer.ByteBuf; 11 | 12 | /** 13 | * Joinked from Multiblocked 14 | */ 15 | public interface IPacket extends IMessage { 16 | 17 | void encode(PacketBuffer buf); 18 | 19 | void decode(PacketBuffer buf); 20 | 21 | @Override 22 | default void fromBytes(ByteBuf buf) { 23 | decode(new PacketBuffer(buf)); 24 | } 25 | 26 | @Override 27 | default void toBytes(ByteBuf buf) { 28 | encode(new PacketBuffer(buf)); 29 | } 30 | 31 | @SideOnly(Side.CLIENT) 32 | default IPacket executeClient(NetHandlerPlayClient handler) { 33 | return null; 34 | } 35 | 36 | default IPacket executeServer(NetHandlerPlayServer handler) { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/SizedDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | /** 4 | * Draws a {@link IDrawable} with a fixed size to a offset position 5 | */ 6 | public class SizedDrawable implements IDrawable { 7 | 8 | private final IDrawable drawable; 9 | private final float fixedWidth, fixedHeight; 10 | private final float offsetX, offsetY; 11 | 12 | public SizedDrawable(IDrawable drawable, float fixedWidth, float fixedHeight, float offsetX, float offsetY) { 13 | this.drawable = drawable; 14 | this.fixedWidth = fixedWidth; 15 | this.fixedHeight = fixedHeight; 16 | this.offsetX = offsetX; 17 | this.offsetY = offsetY; 18 | } 19 | 20 | public SizedDrawable(IDrawable drawable, float fixedWidth, float fixedHeight) { 21 | this(drawable, fixedWidth, fixedHeight, 0, 0); 22 | } 23 | 24 | @Override 25 | public void applyThemeColor(int color) { 26 | drawable.applyThemeColor(color); 27 | } 28 | 29 | @Override 30 | public void draw(float x, float y, float width, float height, float partialTicks) { 31 | drawable.draw(x + offsetX, y + offsetY, fixedWidth, fixedHeight, partialTicks); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/OffsetDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | /** 4 | * Draws a {@link IDrawable} to a offset pos with a offset size 5 | */ 6 | public class OffsetDrawable implements IDrawable { 7 | 8 | private final IDrawable drawable; 9 | private final float offsetX, offsetY; 10 | private final float widthOffset, heightOffset; 11 | 12 | public OffsetDrawable(IDrawable drawable, float offsetX, float offsetY, float widthOffset, float heightOffset) { 13 | this.drawable = drawable; 14 | this.offsetX = offsetX; 15 | this.offsetY = offsetY; 16 | this.widthOffset = widthOffset; 17 | this.heightOffset = heightOffset; 18 | } 19 | 20 | public OffsetDrawable(IDrawable drawable, float offsetX, float offsetY) { 21 | this(drawable, offsetX, offsetY, 0, 0); 22 | } 23 | 24 | @Override 25 | public void applyThemeColor(int color) { 26 | drawable.applyThemeColor(color); 27 | } 28 | 29 | @Override 30 | public void draw(float x, float y, float width, float height, float partialTicks) { 31 | drawable.draw(x + offsetX, y + offsetY, width + widthOffset, height + heightOffset, partialTicks); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/config/ModularUIGuiConfig.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Locale; 6 | 7 | import net.minecraft.client.gui.GuiScreen; 8 | import net.minecraftforge.common.config.ConfigElement; 9 | 10 | import com.gtnewhorizons.modularui.ModularUI; 11 | 12 | import cpw.mods.fml.client.config.GuiConfig; 13 | import cpw.mods.fml.client.config.IConfigElement; 14 | 15 | @SuppressWarnings("rawtypes") 16 | public class ModularUIGuiConfig extends GuiConfig { 17 | 18 | public ModularUIGuiConfig(GuiScreen parentScreen) { 19 | super( 20 | parentScreen, 21 | getConfigElements(), 22 | ModularUI.MODID, 23 | false, 24 | false, 25 | GuiConfig.getAbridgedConfigPath(Config.config.toString())); 26 | } 27 | 28 | private static List getConfigElements() { 29 | List list = new ArrayList<>(); 30 | 31 | for (String category : Config.CATEGORIES) { 32 | list.add(new ConfigElement(Config.config.getCategory(category.toLowerCase(Locale.US)))); 33 | } 34 | 35 | return list; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/builder/UIBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.builder; 2 | 3 | import com.gtnewhorizons.modularui.api.screen.IContainerCreator; 4 | import com.gtnewhorizons.modularui.api.screen.IGuiCreator; 5 | 6 | /** 7 | * Builder for {@link UIInfo} 8 | */ 9 | public class UIBuilder { 10 | 11 | private static final IContainerCreator DUMMY_CONTAINER_CREATOR = (player, world, x, y, z) -> null; 12 | private static final IGuiCreator DUMMY_GUI_CREATOR = (player, world, x, y, z) -> null; 13 | 14 | public static UIBuilder of() { 15 | return new UIBuilder(); 16 | } 17 | 18 | private IContainerCreator containerCreator = DUMMY_CONTAINER_CREATOR; 19 | private IGuiCreator guiCreator = DUMMY_GUI_CREATOR; 20 | 21 | private UIBuilder() {} 22 | 23 | public UIBuilder container(IContainerCreator containerCreator) { 24 | this.containerCreator = containerCreator; 25 | return this; 26 | } 27 | 28 | public UIBuilder gui(IGuiCreator guiCreator) { 29 | this.guiCreator = guiCreator; 30 | return this; 31 | } 32 | 33 | public UIInfo build() { 34 | return new UIInfo<>(this.containerCreator, this.guiCreator); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/FluidDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import net.minecraftforge.fluids.Fluid; 6 | import net.minecraftforge.fluids.FluidStack; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | @SuppressWarnings("unused") 12 | public class FluidDrawable implements IDrawable { 13 | 14 | @NotNull 15 | private Supplier fluid = () -> null; 16 | 17 | @Override 18 | public void applyThemeColor(int color) {} 19 | 20 | @Override 21 | public void draw(float x, float y, float width, float height, float partialTicks) { 22 | GuiHelper.drawFluidTexture(fluid.get(), x, y, width, height, 0); 23 | } 24 | 25 | public @NotNull Supplier getFluid() { 26 | return fluid; 27 | } 28 | 29 | public FluidDrawable setFluid(@NotNull Fluid fluid) { 30 | return setFluid(new FluidStack(fluid, 0)); 31 | } 32 | 33 | public FluidDrawable setFluid(@Nullable FluidStack fluid) { 34 | return setFluid(() -> fluid); 35 | } 36 | 37 | public FluidDrawable setFluid(@NotNull Supplier fluid) { 38 | this.fluid = fluid; 39 | return this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | 3 | *.[jJ][aA][rR] binary 4 | 5 | *.[pP][nN][gG] binary 6 | *.[jJ][pP][gG] binary 7 | *.[jJ][pP][eE][gG] binary 8 | *.[gG][iI][fF] binary 9 | *.[tT][iI][fF] binary 10 | *.[tT][iI][fF][fF] binary 11 | *.[iI][cC][oO] binary 12 | *.[sS][vV][gG] text 13 | *.[eE][pP][sS] binary 14 | *.[xX][cC][fF] binary 15 | 16 | *.[kK][aA][rR] binary 17 | *.[mM]4[aA] binary 18 | *.[mM][iI][dD] binary 19 | *.[mM][iI][dD][iI] binary 20 | *.[mM][pP]3 binary 21 | *.[oO][gG][gG] binary 22 | *.[rR][aA] binary 23 | 24 | *.7[zZ] binary 25 | *.[gG][zZ] binary 26 | *.[tT][aA][rR] binary 27 | *.[tT][gG][zZ] binary 28 | *.[zZ][iI][pP] binary 29 | 30 | *.[tT][cC][nN] binary 31 | *.[sS][oO] binary 32 | *.[dD][lL][lL] binary 33 | *.[dD][yY][lL][iI][bB] binary 34 | *.[pP][sS][dD] binary 35 | *.[tT][tT][fF] binary 36 | *.[oO][tT][fF] binary 37 | 38 | *.[pP][aA][tT][cC][hH] -text 39 | 40 | *.[bB][aA][tT] text eol=crlf 41 | *.[cC][mM][dD] text eol=crlf 42 | *.[pP][sS]1 text eol=crlf 43 | 44 | *[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary 45 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/test/TestBlock.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.test; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import net.minecraft.block.Block; 6 | import net.minecraft.block.ITileEntityProvider; 7 | import net.minecraft.block.material.Material; 8 | import net.minecraft.entity.player.EntityPlayer; 9 | import net.minecraft.tileentity.TileEntity; 10 | import net.minecraft.util.Vec3; 11 | import net.minecraft.world.World; 12 | 13 | import com.gtnewhorizons.modularui.api.UIInfos; 14 | 15 | public class TestBlock extends Block implements ITileEntityProvider { 16 | 17 | public TestBlock(Material p_i45394_1_) { 18 | super(p_i45394_1_); 19 | } 20 | 21 | @Nullable 22 | @Override 23 | public TileEntity createNewTileEntity(World worldIn, int meta) { 24 | return new TestTile(); 25 | } 26 | 27 | @Override 28 | public boolean onBlockActivated(World p_149727_1_, int p_149727_2_, int p_149727_3_, int p_149727_4_, 29 | EntityPlayer p_149727_5_, int p_149727_6_, float p_149727_7_, float p_149727_8_, float p_149727_9_) { 30 | if (!p_149727_1_.isRemote) { 31 | UIInfos.TILE_MODULAR_UI 32 | .open(p_149727_5_, p_149727_1_, Vec3.createVectorHelper(p_149727_2_, p_149727_3_, p_149727_4_)); 33 | } 34 | return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/math/Alignment.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.math; 2 | 3 | public class Alignment { 4 | 5 | public final int x, y; 6 | 7 | public static final Alignment TopLeft = new Alignment(-1, -1); 8 | public static final Alignment TopCenter = new Alignment(0, -1); 9 | public static final Alignment TopRight = new Alignment(1, -1); 10 | public static final Alignment CenterLeft = new Alignment(-1, 0); 11 | public static final Alignment Center = new Alignment(0, 0); 12 | public static final Alignment CenterRight = new Alignment(1, 0); 13 | public static final Alignment BottomLeft = new Alignment(-1, 1); 14 | public static final Alignment BottomCenter = new Alignment(0, 1); 15 | public static final Alignment BottomRight = new Alignment(1, 1); 16 | 17 | public static final Alignment[] ALL = { TopLeft, TopCenter, TopRight, CenterLeft, Center, CenterRight, BottomLeft, 18 | BottomCenter, BottomRight }; 19 | 20 | public static final Alignment[] CORNERS = { TopLeft, TopRight, BottomLeft, BottomRight }; 21 | 22 | public Alignment(int x, int y) { 23 | this.x = x; 24 | this.y = y; 25 | } 26 | 27 | public Pos2d getAlignedPos(Size parent, Size child) { 28 | float x = (this.x + 1) * 1f / 2, y = (this.y + 1) * 1f / 2; 29 | return new Pos2d(parent.width * x - child.width * x, parent.height * y - child.height * y); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/SingleChildWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import com.gtnewhorizons.modularui.ModularUI; 9 | import com.gtnewhorizons.modularui.api.math.Pos2d; 10 | import com.gtnewhorizons.modularui.api.math.Size; 11 | import com.gtnewhorizons.modularui.api.widget.IWidgetParent; 12 | import com.gtnewhorizons.modularui.api.widget.Widget; 13 | 14 | public class SingleChildWidget extends Widget implements IWidgetParent { 15 | 16 | private Widget child; 17 | 18 | public SingleChildWidget() {} 19 | 20 | public SingleChildWidget(Size size) { 21 | super(size); 22 | } 23 | 24 | public SingleChildWidget(Size size, Pos2d pos) { 25 | super(size, pos); 26 | } 27 | 28 | public final SingleChildWidget setChild(Widget widget) { 29 | if (this.child != null) { 30 | ModularUI.logger.error("Child is already set!"); 31 | } else if (MultiChildWidget.checkEditable(this)) { 32 | this.child = widget; 33 | } 34 | return this; 35 | } 36 | 37 | @Override 38 | public List getChildren() { 39 | return Collections.singletonList(child); 40 | } 41 | 42 | public Widget getChild() { 43 | return child; 44 | } 45 | 46 | @Override 47 | protected @NotNull Size determineSize(int maxWidth, int maxHeight) { 48 | return child.getSize(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/RotatedDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | import com.gtnewhorizons.modularui.api.GlStateManager; 4 | 5 | @SuppressWarnings("unused") 6 | public class RotatedDrawable implements IDrawable { 7 | 8 | private final IDrawable drawable; 9 | private float rotation; 10 | 11 | public RotatedDrawable(IDrawable drawable) { 12 | this.drawable = drawable; 13 | } 14 | 15 | /** 16 | * @param rotation 0° - 360°, clockwise 17 | */ 18 | public RotatedDrawable setRotationDegree(float rotation) { 19 | this.rotation = rotation; 20 | return this; 21 | } 22 | 23 | /** 24 | * @param rotation 0 rad - 2π rad, clockwise 25 | */ 26 | public RotatedDrawable setRotationRadian(float rotation) { 27 | return setRotationDegree(180f / (float) Math.PI * rotation); 28 | } 29 | 30 | /** 31 | * @param rotation 0 rad - 2π rad, clockwise 32 | */ 33 | public RotatedDrawable setRotationRadian(double rotation) { 34 | return setRotationRadian((float) rotation); 35 | } 36 | 37 | @Override 38 | public void applyThemeColor(int color) { 39 | drawable.applyThemeColor(color); 40 | } 41 | 42 | @Override 43 | public void draw(float x, float y, float width, float height, float partialTicks) { 44 | GlStateManager.pushMatrix(); 45 | drawable.rotate(rotation, width, height); 46 | drawable.draw(x, y, width, height, partialTicks); 47 | GlStateManager.popMatrix(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/mixinplugin/EarlyMixinLoader.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.mixinplugin; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader; 9 | 10 | import cpw.mods.fml.relauncher.FMLLaunchHandler; 11 | import cpw.mods.fml.relauncher.IFMLLoadingPlugin; 12 | 13 | @IFMLLoadingPlugin.MCVersion("1.7.10") 14 | public class EarlyMixinLoader implements IFMLLoadingPlugin, IEarlyMixinLoader { 15 | 16 | @Override 17 | public String[] getASMTransformerClass() { 18 | return null; 19 | } 20 | 21 | @Override 22 | public String getModContainerClass() { 23 | return null; 24 | } 25 | 26 | @Override 27 | public String getSetupClass() { 28 | return null; 29 | } 30 | 31 | @Override 32 | public void injectData(Map data) {} 33 | 34 | @Override 35 | public String getAccessTransformerClass() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public String getMixinConfig() { 41 | return "mixins.modularui.early.json"; 42 | } 43 | 44 | @Override 45 | public List getMixins(Set loadedCoreMods) { 46 | final List mixins = new ArrayList<>(); 47 | if (FMLLaunchHandler.side().isClient()) { 48 | mixins.add("GuiContainerAccessor"); 49 | mixins.add("NetHandlerPlayClientMixin"); 50 | } 51 | mixins.add("PacketBufferMixin"); 52 | return mixins; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/mixins/early/GuiContainerAccessor.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.mixins.early; 2 | 3 | import java.util.Set; 4 | 5 | import net.minecraft.client.gui.inventory.GuiContainer; 6 | import net.minecraft.inventory.Slot; 7 | import net.minecraft.item.ItemStack; 8 | 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.gen.Accessor; 11 | import org.spongepowered.asm.mixin.gen.Invoker; 12 | 13 | @Mixin(GuiContainer.class) 14 | public interface GuiContainerAccessor { 15 | 16 | @Accessor("theSlot") 17 | void setHoveredSlot(Slot slot); 18 | 19 | @Accessor 20 | Slot getClickedSlot(); 21 | 22 | @Accessor 23 | ItemStack getDraggedStack(); 24 | 25 | @Accessor 26 | boolean getIsRightMouseClick(); 27 | 28 | @Accessor("field_146987_F") 29 | int getDragSplittingLimit(); 30 | 31 | @Invoker("func_146980_g") 32 | void invokeUpdateDragSplitting(); 33 | 34 | @Accessor("field_147008_s") 35 | Set getDragSplittingSlots(); 36 | 37 | @Accessor("field_147007_t") 38 | boolean isDragSplittingInternal(); 39 | 40 | @Accessor("field_146996_I") 41 | int getDragSplittingRemnant(); 42 | 43 | @Accessor 44 | ItemStack getReturningStack(); 45 | 46 | @Accessor 47 | void setReturningStack(ItemStack stack); 48 | 49 | @Accessor 50 | Slot getReturningStackDestSlot(); 51 | 52 | @Accessor("field_147011_y") 53 | int getTouchUpX(); 54 | 55 | @Accessor("field_147010_z") 56 | int getTouchUpY(); 57 | 58 | @Accessor 59 | long getReturningStackTime(); 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/IItemHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | // 3 | // Source code recreated from a .class file by IntelliJ IDEA 4 | // (powered by FernFlower decompiler) 5 | // 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import javax.annotation.Nullable; 11 | 12 | import net.minecraft.inventory.IInventory; 13 | import net.minecraft.item.ItemStack; 14 | 15 | public interface IItemHandler { 16 | 17 | int getSlots(); 18 | 19 | ItemStack getStackInSlot(int var1); 20 | 21 | @Nullable 22 | ItemStack insertItem(int var1, ItemStack var2, boolean var3); 23 | 24 | @Nullable 25 | ItemStack extractItem(int var1, int var2, boolean var3); 26 | 27 | int getSlotLimit(int var1); 28 | 29 | default boolean isItemValid(int slot, ItemStack stack) { 30 | return true; 31 | } 32 | 33 | default List getStacks() { 34 | List ret = new ArrayList<>(); 35 | for (int i = 0; i < getSlots(); i++) { 36 | ret.add(getStackInSlot(i)); 37 | } 38 | return ret; 39 | } 40 | 41 | /** 42 | * Get the inventory this slot originates from. Only supposed to be an identifier. Avoid callings methods on this 43 | * inventory directly beyond comparing its identity. 44 | * 45 | * @return the source inventory, or null if not from any {@link IInventory}, or the source inventory information has 46 | * been lost. 47 | */ 48 | @Nullable 49 | default IInventory getSourceInventory() { 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/KeyboardUtil.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api; 2 | 3 | import net.minecraft.client.gui.GuiScreen; 4 | 5 | import org.lwjgl.input.Keyboard; 6 | 7 | import cpw.mods.fml.relauncher.Side; 8 | import cpw.mods.fml.relauncher.SideOnly; 9 | 10 | @SideOnly(Side.CLIENT) 11 | public class KeyboardUtil { 12 | 13 | /** 14 | * Returns true if either windows ctrl key is down or if either mac meta key is down 15 | */ 16 | public static boolean isCtrlKeyDown() { 17 | return GuiScreen.isCtrlKeyDown(); 18 | } 19 | 20 | /** 21 | * Returns true if either shift key is down 22 | */ 23 | public static boolean isShiftKeyDown() { 24 | return GuiScreen.isShiftKeyDown(); 25 | } 26 | 27 | /** 28 | * Returns true if either alt key is down 29 | */ 30 | public static boolean isAltKeyDown() { 31 | return Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU); 32 | } 33 | 34 | public static boolean isKeyComboCtrlX(int keyID) { 35 | return keyID == Keyboard.KEY_X && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); 36 | } 37 | 38 | public static boolean isKeyComboCtrlV(int keyID) { 39 | return keyID == Keyboard.KEY_V && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); 40 | } 41 | 42 | public static boolean isKeyComboCtrlC(int keyID) { 43 | return keyID == Keyboard.KEY_C && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); 44 | } 45 | 46 | public static boolean isKeyComboCtrlA(int keyID) { 47 | return keyID == Keyboard.KEY_A && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/fluids/IFluidTanksHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.fluids; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | import net.minecraftforge.fluids.Fluid; 9 | import net.minecraftforge.fluids.FluidStack; 10 | 11 | public interface IFluidTanksHandler { 12 | 13 | int getTanks(); 14 | 15 | @Nullable 16 | FluidStack getFluidStackInTank(int tank); 17 | 18 | Fluid getFluidInTank(int tank); 19 | 20 | IFluidTankLong getFluidTank(int tank); 21 | 22 | @Nullable 23 | FluidStack fill(int tank, @Nullable Fluid fluid, long amount, boolean simulate); 24 | 25 | @Nullable 26 | FluidStack drain(int tank, long amount, boolean simulate); 27 | 28 | long getTankCapacity(int tank); 29 | 30 | long getRealTankCapacity(int tank); 31 | 32 | long getTankStoredAmount(int tank); 33 | 34 | default boolean isFluidValid(int slot, FluidStack stack) { 35 | return true; 36 | } 37 | 38 | default List getFluids() { 39 | List ret = new ArrayList<>(); 40 | 41 | for (int i = 0; i < this.getTanks(); ++i) { 42 | ret.add(this.getFluidStackInTank(i)); 43 | } 44 | 45 | return ret; 46 | } 47 | 48 | void setFluidInTank(int tank, Fluid fluid, long amount); 49 | 50 | default void setFluidInTank(int tank, Fluid fluid) { 51 | setFluidInTank(tank, fluid, 0); 52 | } 53 | 54 | default void setFluidInTank(int tank, IFluidTankLong fluid) { 55 | setFluidInTank(tank, fluid != null ? fluid.getStoredFluid() : null, fluid != null ? fluid.getFluidAmount() : 0); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/mixins/early/NetHandlerPlayClientMixin.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.mixins.early; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.network.NetHandlerPlayClient; 5 | import net.minecraft.inventory.Container; 6 | import net.minecraft.network.play.server.S2FPacketSetSlot; 7 | 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; 15 | 16 | @Mixin(NetHandlerPlayClient.class) 17 | public class NetHandlerPlayClientMixin { 18 | 19 | @Shadow 20 | private Minecraft gameController; 21 | 22 | /** 23 | * @reason Prevent client-only GUI from accepting slot updates from server, e.g. armor taking damage 24 | */ 25 | @Inject( 26 | method = "handleSetSlot", 27 | at = @At( 28 | value = "INVOKE", 29 | target = "Lnet/minecraft/inventory/Container;putStackInSlot(ILnet/minecraft/item/ItemStack;)V", 30 | ordinal = 1), 31 | cancellable = true) 32 | public void modularui$beforeHandleSetSlot(S2FPacketSetSlot p_147266_1_, CallbackInfo ci) { 33 | Container container = gameController.thePlayer.openContainer; 34 | if (container instanceof ModularUIContainer) { 35 | if (((ModularUIContainer) container).getContext().isClientOnly()) { 36 | ci.cancel(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/IDraggable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | import java.awt.Rectangle; 4 | 5 | import org.jetbrains.annotations.ApiStatus; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | @ApiStatus.Experimental 9 | public interface IDraggable { 10 | 11 | /** 12 | * Get's called from the cursor Usually you just call {@link Widget#drawInternal(float)} 13 | * 14 | * @param partialTicks difference from last from 15 | */ 16 | void renderMovingState(float partialTicks); 17 | 18 | /** 19 | * @param button the mouse button that's holding down 20 | * @return false if the action should be canceled 21 | */ 22 | boolean onDragStart(int button); 23 | 24 | /** 25 | * The dragging has ended and getState == IDLE 26 | * 27 | * @param successful is false if this returned to it's old position 28 | */ 29 | void onDragEnd(boolean successful); 30 | 31 | void onDrag(int mouseButton, long timeSinceLastClick); 32 | 33 | /** 34 | * Gets called when the mouse is released 35 | * 36 | * @param widget current top most widget below the mouse 37 | * @param isInBounds if the mouse is in the gui bounds 38 | * @return if the location is valid 39 | */ 40 | default boolean canDropHere(@Nullable Widget widget, boolean isInBounds) { 41 | return isInBounds; 42 | } 43 | 44 | /** 45 | * @return the size and pos during move 46 | */ 47 | @Nullable 48 | Rectangle getArea(); 49 | 50 | default boolean shouldRenderChildren() { 51 | return true; 52 | } 53 | 54 | boolean isMoving(); 55 | 56 | void setMoving(boolean moving); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/CommonProxy.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.material.Material; 5 | import net.minecraft.creativetab.CreativeTabs; 6 | import net.minecraftforge.common.MinecraftForge; 7 | 8 | import com.gtnewhorizons.modularui.config.Config; 9 | import com.gtnewhorizons.modularui.test.TestBlock; 10 | import com.gtnewhorizons.modularui.test.TestTile; 11 | 12 | import cpw.mods.fml.client.event.ConfigChangedEvent; 13 | import cpw.mods.fml.common.FMLCommonHandler; 14 | import cpw.mods.fml.common.event.FMLPreInitializationEvent; 15 | import cpw.mods.fml.common.eventhandler.SubscribeEvent; 16 | import cpw.mods.fml.common.registry.GameRegistry; 17 | 18 | public class CommonProxy { 19 | 20 | public static Block testBlock; 21 | 22 | public void preInit(FMLPreInitializationEvent event) { 23 | Config.init(event.getSuggestedConfigurationFile()); 24 | 25 | if (ModularUI.isDevEnv || Config.forceEnableDebugBlock) { 26 | testBlock = new TestBlock(Material.rock).setBlockName("testBlock").setCreativeTab(CreativeTabs.tabBlock) 27 | .setBlockTextureName("stone"); 28 | GameRegistry.registerBlock(testBlock, "testBlock"); 29 | GameRegistry.registerTileEntity(TestTile.class, "TestTileEntity"); 30 | } 31 | 32 | FMLCommonHandler.instance().bus().register(this); 33 | MinecraftForge.EVENT_BUS.register(this); 34 | } 35 | 36 | public void postInit() {} 37 | 38 | @SubscribeEvent 39 | public void onConfigChange(ConfigChangedEvent.OnConfigChangedEvent event) { 40 | if (event.modID.equals(ModularUI.MODID)) { 41 | Config.syncConfig(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/PlayerMainInvWrapper.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | 3 | import net.minecraft.entity.player.EntityPlayerMP; 4 | import net.minecraft.entity.player.InventoryPlayer; 5 | import net.minecraft.inventory.IInventory; 6 | import net.minecraft.item.ItemStack; 7 | 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * Exposes the player inventory WITHOUT the armor inventory as IItemHandler. Also takes care of inserting/extracting 12 | * having the same logic as picking up items. 13 | */ 14 | public class PlayerMainInvWrapper extends RangedWrapper { 15 | 16 | private final InventoryPlayer inventoryPlayer; 17 | 18 | public PlayerMainInvWrapper(InventoryPlayer inv) { 19 | super(new InvWrapper(inv), 0, inv.mainInventory.length); 20 | inventoryPlayer = inv; 21 | } 22 | 23 | @Override 24 | public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { 25 | ItemStack rest = super.insertItem(slot, stack, simulate); 26 | if (rest.stackSize != stack.stackSize) { 27 | // the stack in the slot changed, animate it 28 | ItemStack inSlot = getStackInSlot(slot); 29 | if (inSlot != null) { 30 | if (getInventoryPlayer().player.worldObj.isRemote) { 31 | inSlot.animationsToGo = 5; 32 | } else if (getInventoryPlayer().player instanceof EntityPlayerMP) { 33 | getInventoryPlayer().player.openContainer.detectAndSendChanges(); 34 | } 35 | } 36 | } 37 | return rest; 38 | } 39 | 40 | public InventoryPlayer getInventoryPlayer() { 41 | return inventoryPlayer; 42 | } 43 | 44 | @Nullable 45 | @Override 46 | public IInventory getSourceInventory() { 47 | return inventoryPlayer; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/peripheral/ModularUIPeripheralInputHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.peripheral; 2 | 3 | import net.minecraft.client.gui.inventory.GuiContainer; 4 | 5 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 6 | 7 | import codechicken.nei.guihook.IContainerInputHandler; 8 | 9 | public class ModularUIPeripheralInputHandler implements IContainerInputHandler { 10 | 11 | @Override 12 | public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) { 13 | return false; 14 | } 15 | 16 | @Override 17 | public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {} 18 | 19 | @Override 20 | public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) { 21 | return false; 22 | } 23 | 24 | @Override 25 | public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) { 26 | return false; 27 | } 28 | 29 | @Override 30 | public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {} 31 | 32 | @Override 33 | public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {} 34 | 35 | @Override 36 | public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { 37 | if (gui instanceof ModularGui && scrolled != 0) { 38 | return ((ModularGui) gui).mouseScrolled(scrolled); 39 | } 40 | return false; 41 | } 42 | 43 | @Override 44 | public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { 45 | if (gui instanceof ModularGui && scrolled != 0) { 46 | ((ModularGui) gui).onMouseScrolled(scrolled); 47 | } 48 | } 49 | 50 | @Override 51 | public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {} 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/WidgetJsonRegistry.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.function.Supplier; 6 | 7 | import javax.annotation.Nullable; 8 | 9 | import net.minecraft.entity.player.EntityPlayer; 10 | 11 | import com.gtnewhorizons.modularui.ModularUI; 12 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 13 | import com.gtnewhorizons.modularui.api.drawable.Text; 14 | import com.gtnewhorizons.modularui.api.drawable.UITexture; 15 | import com.gtnewhorizons.modularui.api.widget.Widget; 16 | 17 | public class WidgetJsonRegistry { 18 | 19 | public static void init() { 20 | registerWidget("text", TextWidget::new); 21 | registerWidget("image", DrawableWidget::new); 22 | registerWidget("cycle_button", CycleButtonWidget::new); 23 | registerWidget("button", ButtonWidget::new); 24 | registerWidgetSpecial("player_inventory", SlotGroup::playerInventoryGroup); 25 | 26 | IDrawable.JSON_DRAWABLE_MAP.put("text", Text::ofJson); 27 | IDrawable.JSON_DRAWABLE_MAP.put("image", UITexture::ofJson); 28 | } 29 | 30 | private static final Map REGISTRY = new HashMap<>(); 31 | 32 | public static void registerWidgetSpecial(String id, WidgetFactory factory) { 33 | ModularUI.logger.info("Register type {}", id); 34 | REGISTRY.put(id, factory); 35 | } 36 | 37 | public static void registerWidget(String id, Supplier factory) { 38 | ModularUI.logger.info("Register type {}", id); 39 | REGISTRY.put(id, player -> factory.get()); 40 | } 41 | 42 | @Nullable 43 | public static WidgetFactory getFactory(String id) { 44 | return REGISTRY.get(id); 45 | } 46 | 47 | public interface WidgetFactory { 48 | 49 | Widget create(EntityPlayer player); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/ClientProxy.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.resources.IResourceManager; 5 | import net.minecraft.client.resources.IResourceManagerReloadListener; 6 | import net.minecraft.client.resources.SimpleReloadableResourceManager; 7 | 8 | import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture; 9 | import com.gtnewhorizons.modularui.common.internal.JsonLoader; 10 | import com.gtnewhorizons.modularui.common.peripheral.ModularUIPeripheralInputHandler; 11 | import com.gtnewhorizons.modularui.integration.nei.ModularUIContainerObjectHandler; 12 | 13 | import codechicken.nei.guihook.GuiContainerManager; 14 | import cpw.mods.fml.common.event.FMLPreInitializationEvent; 15 | import cpw.mods.fml.relauncher.Side; 16 | import cpw.mods.fml.relauncher.SideOnly; 17 | 18 | @SuppressWarnings("unused") 19 | @SideOnly(Side.CLIENT) 20 | public class ClientProxy extends CommonProxy { 21 | 22 | @Override 23 | public void preInit(FMLPreInitializationEvent event) { 24 | super.preInit(event); 25 | GuiContainerManager.addInputHandler(new ModularUIPeripheralInputHandler()); 26 | GuiContainerManager.addObjectHandler(new ModularUIContainerObjectHandler()); 27 | } 28 | 29 | public void postInit() { 30 | super.postInit(); 31 | ((SimpleReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()) 32 | .registerReloadListener(new ResourceManagerReloadListener()); 33 | } 34 | 35 | private static class ResourceManagerReloadListener implements IResourceManagerReloadListener { 36 | 37 | @Override 38 | public void onResourceManagerReload(IResourceManager p_110549_1_) { 39 | ModularUI.logger.info("Reloading GUIs"); 40 | JsonLoader.loadJson(); 41 | FallbackableUITexture.reload(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/TabButton.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 6 | import com.gtnewhorizons.modularui.api.widget.Interactable; 7 | import com.gtnewhorizons.modularui.api.widget.Widget; 8 | 9 | /** 10 | * Tab-styled button widget. Can be switched between other tab buttons by {@link TabContainer}. 11 | */ 12 | public class TabButton extends Widget implements Interactable { 13 | 14 | private final int page; 15 | private TabContainer tabController; 16 | private IDrawable[] activeBackground; 17 | 18 | /** 19 | * @param page Should be unique within {@link TabContainer} 20 | */ 21 | public TabButton(int page) { 22 | this.page = page; 23 | } 24 | 25 | @Override 26 | public ClickResult onClick(int buttonId, boolean doubleClick) { 27 | if (page != tabController.getCurrentPage()) { 28 | tabController.setActivePage(page); 29 | } 30 | return ClickResult.ACCEPT; 31 | } 32 | 33 | protected void setTabController(TabContainer tabController) { 34 | this.tabController = tabController; 35 | } 36 | 37 | public int getPage() { 38 | return page; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public IDrawable[] getBackground() { 44 | if (isSelected() && activeBackground != null) { 45 | return activeBackground; 46 | } 47 | return super.getBackground(); 48 | } 49 | 50 | public boolean isSelected() { 51 | return page == tabController.getCurrentPage(); 52 | } 53 | 54 | public TabButton setBackground(boolean active, IDrawable... drawables) { 55 | if (active) { 56 | this.activeBackground = drawables; 57 | } else { 58 | setBackground(drawables); 59 | } 60 | return this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/network/CWidgetUpdate.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal.network; 2 | 3 | import java.io.IOException; 4 | 5 | import net.minecraft.inventory.Container; 6 | import net.minecraft.network.NetHandlerPlayServer; 7 | import net.minecraft.network.PacketBuffer; 8 | 9 | import com.gtnewhorizons.modularui.ModularUI; 10 | import com.gtnewhorizons.modularui.api.screen.ModularUIContext; 11 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; 12 | 13 | public class CWidgetUpdate implements IPacket { 14 | 15 | public int widgetId; 16 | public PacketBuffer packet; 17 | 18 | public CWidgetUpdate(PacketBuffer packet, int widgetId) { 19 | this.packet = packet; 20 | this.widgetId = widgetId; 21 | } 22 | 23 | public CWidgetUpdate() {} 24 | 25 | @Override 26 | public void decode(PacketBuffer buf) { 27 | this.widgetId = buf.readVarIntFromBuffer(); 28 | this.packet = NetworkUtils.readPacketBuffer(buf); 29 | } 30 | 31 | @Override 32 | public void encode(PacketBuffer buf) { 33 | buf.writeVarIntToBuffer(widgetId); 34 | NetworkUtils.writePacketBuffer(buf, packet); 35 | } 36 | 37 | @Override 38 | public IPacket executeServer(NetHandlerPlayServer handler) { 39 | Container container = handler.playerEntity.openContainer; 40 | if (container instanceof ModularUIContainer) { 41 | ModularUIContext context = ((ModularUIContainer) container).getContext(); 42 | try { 43 | context.readClientPacket(packet, widgetId); 44 | } catch (IOException e) { 45 | ModularUI.logger.error("Error reading client packet: "); 46 | e.printStackTrace(); 47 | } 48 | } else { 49 | // hopefully harmless error, caused by some kind of network lag 50 | // ModularUI.logger.error("Expected ModularUIContainer on server, but got {}", container); 51 | } 52 | return null; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/mixins/early/PacketBufferMixin.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.mixins.early; 2 | 3 | import net.minecraft.item.ItemStack; 4 | import net.minecraft.network.PacketBuffer; 5 | 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 13 | 14 | @Mixin(PacketBuffer.class) 15 | public abstract class PacketBufferMixin { 16 | 17 | @Shadow 18 | public abstract int readVarIntFromBuffer(); 19 | 20 | @Shadow 21 | public abstract void writeVarIntToBuffer(int input); 22 | 23 | /** 24 | * @reason Use integer for stack size 25 | */ 26 | @Inject( 27 | method = "writeItemStackToBuffer", 28 | at = @At( 29 | value = "INVOKE", 30 | target = "Lnet/minecraft/network/PacketBuffer;writeNBTTagCompoundToBuffer(Lnet/minecraft/nbt/NBTTagCompound;)V", 31 | shift = At.Shift.AFTER)) 32 | public void modularui$writeItemStackSizeInteger(ItemStack stack, CallbackInfo ci) { 33 | writeVarIntToBuffer(stack.stackSize); 34 | } 35 | 36 | /** 37 | * @reason Use integer for stack size 38 | */ 39 | @Inject( 40 | method = "readItemStackFromBuffer", 41 | at = @At( 42 | value = "INVOKE", 43 | target = "Lnet/minecraft/network/PacketBuffer;readNBTTagCompoundFromBuffer()Lnet/minecraft/nbt/NBTTagCompound;", 44 | shift = At.Shift.AFTER), 45 | locals = LocalCapture.CAPTURE_FAILEXCEPTION) 46 | public void modularui$readItemStackSizeInteger(CallbackInfoReturnable cir, ItemStack itemstack) { 47 | itemstack.stackSize = readVarIntFromBuffer(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/network/SWidgetUpdate.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal.network; 2 | 3 | import java.io.IOException; 4 | 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.GuiScreen; 7 | import net.minecraft.client.network.NetHandlerPlayClient; 8 | import net.minecraft.network.PacketBuffer; 9 | 10 | import com.gtnewhorizons.modularui.ModularUI; 11 | import com.gtnewhorizons.modularui.api.screen.ModularUIContext; 12 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 13 | 14 | public class SWidgetUpdate implements IPacket { 15 | 16 | public int widgetId; 17 | public PacketBuffer packet; 18 | 19 | public SWidgetUpdate(PacketBuffer packet, int widgetId) { 20 | this.packet = packet; 21 | this.widgetId = widgetId; 22 | } 23 | 24 | public SWidgetUpdate() {} 25 | 26 | @Override 27 | public void decode(PacketBuffer buf) { 28 | this.widgetId = buf.readVarIntFromBuffer(); 29 | this.packet = NetworkUtils.readPacketBuffer(buf); 30 | } 31 | 32 | @Override 33 | public void encode(PacketBuffer buf) { 34 | buf.writeVarIntToBuffer(widgetId); 35 | NetworkUtils.writePacketBuffer(buf, packet); 36 | } 37 | 38 | @Override 39 | public IPacket executeClient(NetHandlerPlayClient handler) { 40 | GuiScreen screen = Minecraft.getMinecraft().currentScreen; 41 | if (screen instanceof ModularGui) { 42 | ModularUIContext context = ((ModularGui) screen).getContext(); 43 | try { 44 | context.readServerPacket(packet, widgetId); 45 | } catch (IOException e) { 46 | ModularUI.logger.error("Error reading server packet: "); 47 | e.printStackTrace(); 48 | } 49 | } else { 50 | // no-op 51 | // This can legitimately happen when: 52 | // - client opens NEI GuiRecipe 53 | // - client closes GUI (until server receives packet of closing GUI server-side) 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/wrapper/FluidTankHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal.wrapper; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import net.minecraftforge.common.util.ForgeDirection; 6 | import net.minecraftforge.fluids.Fluid; 7 | import net.minecraftforge.fluids.FluidStack; 8 | import net.minecraftforge.fluids.FluidTankInfo; 9 | import net.minecraftforge.fluids.IFluidHandler; 10 | import net.minecraftforge.fluids.IFluidTank; 11 | 12 | public class FluidTankHandler implements IFluidHandler { 13 | 14 | public static IFluidHandler getTankFluidHandler(IFluidTank tank) { 15 | if (tank instanceof IFluidHandler) { 16 | return (IFluidHandler) tank; 17 | } 18 | return new FluidTankHandler(tank); 19 | } 20 | 21 | private final IFluidTank fluidTank; 22 | 23 | public FluidTankHandler(IFluidTank tank) { 24 | this.fluidTank = tank; 25 | } 26 | 27 | @Override 28 | public FluidTankInfo[] getTankInfo(ForgeDirection from) { 29 | return new FluidTankInfo[] { fluidTank.getInfo() }; 30 | } 31 | 32 | @Override 33 | public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { 34 | return fluidTank.fill(resource, doFill); 35 | } 36 | 37 | @Nullable 38 | @Override 39 | public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { 40 | FluidStack currentFluid = fluidTank.getFluid(); 41 | if (currentFluid == null || currentFluid.amount <= 0 || !currentFluid.isFluidEqual(resource)) { 42 | return null; 43 | } 44 | return fluidTank.drain(resource.amount, doDrain); 45 | } 46 | 47 | @Nullable 48 | @Override 49 | public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { 50 | return fluidTank.drain(maxDrain, doDrain); 51 | } 52 | 53 | @Override 54 | public boolean canFill(ForgeDirection from, Fluid fluid) { 55 | return true; 56 | } 57 | 58 | @Override 59 | public boolean canDrain(ForgeDirection from, Fluid fluid) { 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/mixins/late/NEIControllerMixin.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.mixins.late; 2 | 3 | import java.awt.Point; 4 | 5 | import net.minecraft.client.gui.inventory.GuiContainer; 6 | import net.minecraft.inventory.Slot; 7 | 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 13 | 14 | import com.gtnewhorizons.modularui.api.widget.Widget; 15 | import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; 16 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 17 | import com.gtnewhorizons.modularui.common.widget.SlotWidget; 18 | 19 | import codechicken.nei.NEIController; 20 | 21 | @Mixin(NEIController.class) 22 | public class NEIControllerMixin { 23 | 24 | /** 25 | * @reason Prevent client-only window from sending slot click packet via NEI fast transfer 26 | */ 27 | @Inject( 28 | method = "mouseScrolled", 29 | at = @At( 30 | value = "INVOKE", 31 | shift = At.Shift.BEFORE, 32 | target = "Lcodechicken/nei/NEIClientConfig;shouldInvertMouseScrollTransfer()Z"), 33 | locals = LocalCapture.CAPTURE_FAILSOFT, 34 | cancellable = true, 35 | remap = false) 36 | public void modularui$beforeMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled, 37 | CallbackInfoReturnable ci, Point mousePos, Slot mouseover) { 38 | if (gui instanceof ModularGui) { 39 | Widget hovered = ((ModularGui) gui).getCursor().getHovered(); 40 | if (!(hovered instanceof SlotWidget) || mouseover != ((SlotWidget) hovered).getMcSlot()) { 41 | ci.setReturnValue(false); 42 | return; 43 | } 44 | if (((BaseSlot) mouseover).getParentWidget().getWindow().isClientOnly()) { 45 | ci.setReturnValue(false); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/network/NetworkHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal.network; 2 | 3 | import net.minecraft.client.network.NetHandlerPlayClient; 4 | import net.minecraft.entity.player.EntityPlayerMP; 5 | import net.minecraft.network.NetHandlerPlayServer; 6 | import net.minecraft.world.World; 7 | 8 | import com.gtnewhorizons.modularui.ModularUI; 9 | 10 | import cpw.mods.fml.common.network.NetworkRegistry; 11 | import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; 12 | import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; 13 | import cpw.mods.fml.relauncher.Side; 14 | 15 | /** 16 | * Joinked from Multiblocked 17 | */ 18 | public class NetworkHandler { 19 | 20 | public static final SimpleNetworkWrapper network = NetworkRegistry.INSTANCE.newSimpleChannel(ModularUI.MODID); 21 | private static int packetId = 0; 22 | 23 | public static void init() { 24 | registerS2C(SWidgetUpdate.class); 25 | registerC2S(CWidgetUpdate.class); 26 | } 27 | 28 | private static void registerC2S(Class clazz) { 29 | network.registerMessage(C2SHandler, clazz, packetId++, Side.SERVER); 30 | } 31 | 32 | private static void registerS2C(Class clazz) { 33 | network.registerMessage(S2CHandler, clazz, packetId++, Side.CLIENT); 34 | } 35 | 36 | public static void sendToServer(IPacket packet) { 37 | network.sendToServer(packet); 38 | } 39 | 40 | public static void sendToWorld(IPacket packet, World world) { 41 | network.sendToDimension(packet, world.provider.dimensionId); 42 | } 43 | 44 | public static void sendToPlayer(IPacket packet, EntityPlayerMP player) { 45 | network.sendTo(packet, player); 46 | } 47 | 48 | static final IMessageHandler S2CHandler = (message, ctx) -> { 49 | NetHandlerPlayClient handler = ctx.getClientHandler(); 50 | return message.executeClient(handler); 51 | }; 52 | 53 | static final IMessageHandler C2SHandler = (message, ctx) -> { 54 | NetHandlerPlayServer handler = ctx.getServerHandler(); 55 | return message.executeServer(handler); 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/SyncedWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.gtnewhorizons.modularui.api.widget.ISyncedWidget; 5 | import com.gtnewhorizons.modularui.api.widget.Widget; 6 | import com.gtnewhorizons.modularui.common.internal.JsonHelper; 7 | 8 | /** 9 | * An optional base class for synced widgets. 10 | */ 11 | public abstract class SyncedWidget extends Widget implements ISyncedWidget { 12 | 13 | private boolean needsUpdate; 14 | 15 | private boolean syncsToServer = true; 16 | private boolean syncsToClient = true; 17 | 18 | @Override 19 | public void readJson(JsonObject json, String type) { 20 | super.readJson(json, type); 21 | this.syncsToServer = JsonHelper.getBoolean(json, true, "syncToClient", "handlesServer"); 22 | this.syncsToClient = JsonHelper.getBoolean(json, true, "syncToServer", "handlesClient"); 23 | } 24 | 25 | /** 26 | * @return if this widget should operate on the server side. For example detecting and sending changes to client. 27 | */ 28 | public boolean syncsToClient() { 29 | return syncsToClient; 30 | } 31 | 32 | /** 33 | * @return if this widget should operate on the client side. For example, sending a changed value to the server. 34 | */ 35 | public boolean syncsToServer() { 36 | return syncsToServer; 37 | } 38 | 39 | /** 40 | * Determines how this widget should sync values 41 | * 42 | * @param syncsToClient if this widget should sync changes to the server 43 | * @param syncsToServer if this widget should detect changes on server and sync them to client 44 | */ 45 | public SyncedWidget setSynced(boolean syncsToClient, boolean syncsToServer) { 46 | this.syncsToClient = syncsToClient; 47 | this.syncsToServer = syncsToServer; 48 | return this; 49 | } 50 | 51 | @Override 52 | public void markForUpdate() { 53 | needsUpdate = true; 54 | } 55 | 56 | @Override 57 | public void unMarkForUpdate() { 58 | needsUpdate = false; 59 | } 60 | 61 | @Override 62 | public boolean isMarkedForUpdate() { 63 | return needsUpdate; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/RepeatingDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | import com.gtnewhorizons.modularui.api.GlStateManager; 4 | import com.gtnewhorizons.modularui.api.math.Size; 5 | 6 | @SuppressWarnings("unused") 7 | public class RepeatingDrawable implements IDrawable { 8 | 9 | private IDrawable drawable; 10 | private Size drawableSize; 11 | private int spaceX, spaceY; 12 | 13 | public RepeatingDrawable setDrawable(IDrawable drawable) { 14 | this.drawable = drawable; 15 | return this; 16 | } 17 | 18 | public RepeatingDrawable setDrawableSize(Size size) { 19 | this.drawableSize = size; 20 | return this; 21 | } 22 | 23 | public RepeatingDrawable setDrawableSize(int width, int height) { 24 | return setDrawableSize(new Size(width, height)); 25 | } 26 | 27 | public RepeatingDrawable setSpaceX(int spaceX) { 28 | this.spaceX = spaceX; 29 | return this; 30 | } 31 | 32 | public RepeatingDrawable setSpaceY(int spaceY) { 33 | this.spaceY = spaceY; 34 | return this; 35 | } 36 | 37 | @Override 38 | public void applyThemeColor(int color) { 39 | if (drawable != null) { 40 | drawable.applyThemeColor(color); 41 | } 42 | } 43 | 44 | @Override 45 | public void draw(float x0, float y0, float width, float height, float partialTicks) { 46 | if (drawable == null) return; 47 | if (width <= 0 || height <= 0) return; 48 | if (drawableSize.width <= 0 || drawableSize.height <= 0) return; 49 | double[] translation = GlStateManager.getTranslation(); 50 | 51 | GuiHelper 52 | .useScissor((int) (x0 + translation[0]), (int) (y0 + translation[1]), (int) width, (int) height, () -> { 53 | float x = x0, y = y0; 54 | final float x1 = x0 + width, y1 = y0 + height; 55 | while (y < y1) { 56 | while (x < x1) { 57 | drawable.draw(x, y, drawableSize.width, drawableSize.height, partialTicks); 58 | x += drawableSize.width + spaceX; 59 | } 60 | y += drawableSize.height + spaceY; 61 | x = x0; 62 | } 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/DrawableWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import com.google.gson.JsonObject; 9 | import com.gtnewhorizons.modularui.ModularUI; 10 | import com.gtnewhorizons.modularui.api.GlStateManager; 11 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 12 | import com.gtnewhorizons.modularui.api.drawable.Text; 13 | import com.gtnewhorizons.modularui.api.drawable.UITexture; 14 | import com.gtnewhorizons.modularui.api.math.Pos2d; 15 | import com.gtnewhorizons.modularui.api.widget.Widget; 16 | 17 | /** 18 | * Uses {@link IDrawable} to draw widget. 19 | */ 20 | public class DrawableWidget extends Widget { 21 | 22 | @NotNull 23 | private Supplier drawable = () -> IDrawable.EMPTY; 24 | 25 | @Override 26 | public void readJson(JsonObject json, String type) { 27 | super.readJson(json, type); 28 | if (type.equals("image")) { 29 | setDrawable(UITexture.ofJson(json)); 30 | } 31 | } 32 | 33 | @Override 34 | public void onScreenUpdate() { 35 | if (drawable.get() != null) { 36 | drawable.get().tick(); 37 | } 38 | } 39 | 40 | @Override 41 | public void draw(float partialTicks) { 42 | if (drawable.get() != null) { 43 | GlStateManager.pushMatrix(); 44 | // so that item z levels are properly ordered 45 | // todo: probably need better solution so that more windows can work 46 | GlStateManager.translate(0, 0, 150 * getWindowLayer()); 47 | drawable.get().draw(Pos2d.ZERO, getSize(), partialTicks); 48 | GlStateManager.popMatrix(); 49 | } 50 | } 51 | 52 | @Nullable 53 | public IDrawable getDrawable() { 54 | return drawable.get(); 55 | } 56 | 57 | public DrawableWidget setDrawable(@Nullable IDrawable drawable) { 58 | if (drawable instanceof Text) { 59 | ModularUI.logger.warn("Please use TextWidget for Text"); 60 | } 61 | this.drawable = () -> drawable; 62 | return this; 63 | } 64 | 65 | public DrawableWidget setDrawable(@NotNull Supplier drawable) { 66 | this.drawable = drawable; 67 | return this; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/integration/nei/ModularUIContainerObjectHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.integration.nei; 2 | 3 | import net.minecraft.client.gui.inventory.GuiContainer; 4 | import net.minecraft.item.ItemStack; 5 | 6 | import com.gtnewhorizons.modularui.api.screen.ModularWindow; 7 | import com.gtnewhorizons.modularui.api.widget.IHasStackUnderMouse; 8 | import com.gtnewhorizons.modularui.api.widget.Widget; 9 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 10 | import com.gtnewhorizons.modularui.common.widget.SlotWidget; 11 | 12 | import codechicken.nei.guihook.IContainerObjectHandler; 13 | 14 | public class ModularUIContainerObjectHandler implements IContainerObjectHandler { 15 | 16 | @Override 17 | public void guiTick(GuiContainer gui) {} 18 | 19 | @Override 20 | public void refresh(GuiContainer gui) {} 21 | 22 | @Override 23 | public void load(GuiContainer gui) {} 24 | 25 | @Override 26 | public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) { 27 | if (gui instanceof ModularGui) { 28 | Widget hovered = ((ModularGui) gui).getCursor().getHovered(); 29 | if (hovered instanceof IHasStackUnderMouse) { 30 | return ((IHasStackUnderMouse) hovered).getStackUnderMouse(); 31 | } 32 | } 33 | return null; 34 | } 35 | 36 | @Override 37 | public boolean objectUnderMouse(GuiContainer gui, int mousex, int mousey) { 38 | if (gui instanceof ModularGui) { 39 | ModularGui modularGui = (ModularGui) gui; 40 | ModularWindow hoveredWindow = modularGui.getCursor().findHoveredWindow(); 41 | 42 | boolean foundLowerSlot = false; 43 | for (Object hovered : modularGui.getCursor().getAllHovered()) { 44 | if (hovered instanceof SlotWidget) { 45 | SlotWidget slot = (SlotWidget) hovered; 46 | if (slot.getWindow() == hoveredWindow) { 47 | return false; 48 | } else { 49 | foundLowerSlot = true; 50 | } 51 | } 52 | } 53 | return foundLowerSlot; 54 | } 55 | return false; 56 | } 57 | 58 | @Override 59 | public boolean shouldShowTooltip(GuiContainer gui) { 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/fluids/FluidTanksHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.fluids; 2 | 3 | import static com.google.common.primitives.Ints.saturatedCast; 4 | 5 | import java.util.Arrays; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | import net.minecraftforge.fluids.Fluid; 10 | import net.minecraftforge.fluids.FluidStack; 11 | 12 | public class FluidTanksHandler implements IFluidTanksHandler { 13 | 14 | protected final List fluids; 15 | 16 | public FluidTanksHandler(IFluidTankLong tank) { 17 | this.fluids = Collections.singletonList(tank); 18 | } 19 | 20 | public FluidTanksHandler(int tankAmount, long capacity) { 21 | FluidTankLong[] fluids = new FluidTankLong[tankAmount]; 22 | for (int i = 0; i < fluids.length; i++) { 23 | fluids[i] = new FluidTankLong(capacity); 24 | } 25 | this.fluids = Arrays.asList(fluids); 26 | } 27 | 28 | @Override 29 | public int getTanks() { 30 | return fluids.size(); 31 | } 32 | 33 | @Override 34 | public FluidStack getFluidStackInTank(int tank) { 35 | return fluids.get(tank).getFluidStack(); 36 | } 37 | 38 | @Override 39 | public FluidStack fill(int tank, Fluid fluid, long amount, boolean simulate) { 40 | return fluid == null ? null 41 | : new FluidStack(fluid, saturatedCast(fluids.get(tank).fill(fluid, amount, !simulate))); 42 | } 43 | 44 | @Override 45 | public FluidStack drain(int tank, long amount, boolean simulate) { 46 | return fluids.get(tank).drain(amount, !simulate); 47 | } 48 | 49 | @Override 50 | public long getTankCapacity(int tank) { 51 | return fluids.get(tank).getCapacity(); 52 | } 53 | 54 | @Override 55 | public long getRealTankCapacity(int tank) { 56 | return fluids.get(tank).getRealCapacityLong(); 57 | } 58 | 59 | @Override 60 | public long getTankStoredAmount(int tank) { 61 | return fluids.get(tank).getFluidAmountLong(); 62 | } 63 | 64 | @Override 65 | public void setFluidInTank(int tank, Fluid fluid, long amount) { 66 | fluids.get(tank).setFluid(fluid, amount); 67 | } 68 | 69 | @Override 70 | public IFluidTankLong getFluidTank(int tank) { 71 | return fluids.get(tank); 72 | } 73 | 74 | @Override 75 | public Fluid getFluidInTank(int tank) { 76 | return fluids.get(tank).getStoredFluid(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/builder/UIInfo.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.builder; 2 | 3 | import java.util.function.Function; 4 | 5 | import net.minecraft.entity.player.EntityPlayer; 6 | import net.minecraft.util.Vec3; 7 | import net.minecraft.world.World; 8 | 9 | import com.gtnewhorizons.modularui.ModularUI; 10 | import com.gtnewhorizons.modularui.api.UIInfos; 11 | import com.gtnewhorizons.modularui.api.screen.IContainerCreator; 12 | import com.gtnewhorizons.modularui.api.screen.IGuiCreator; 13 | import com.gtnewhorizons.modularui.common.internal.InternalUIMapper; 14 | import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; 15 | 16 | import cpw.mods.fml.common.network.internal.FMLNetworkHandler; 17 | 18 | /** 19 | * Responsible for registering actual Container and GuiContainer, and opening them. 20 | */ 21 | public class UIInfo { 22 | 23 | private final int id; 24 | private final CC containerCreator; 25 | private final GC guiCreator; 26 | 27 | /** 28 | * @param containerCreator {@link IContainerCreator} 29 | * @param guiCreator {@link IGuiCreator} 30 | */ 31 | UIInfo(CC containerCreator, GC guiCreator) { 32 | this.id = InternalUIMapper.getInstance().register(containerCreator, guiCreator); 33 | this.containerCreator = containerCreator; 34 | this.guiCreator = guiCreator; 35 | } 36 | 37 | /** 38 | * Open GUI of TileEntity at given position. This should be called only by logical server. For client-only GUI, use 39 | * {@link UIInfos#openClientUI(EntityPlayer, Function)} 40 | */ 41 | public void open(EntityPlayer player, World world, int x, int y, int z) { 42 | if (NetworkUtils.isClient()) { 43 | ModularUI.logger.warn("Please use UIInfos.openClientUI to open a client only ui!"); 44 | } 45 | FMLNetworkHandler.openGui(player, ModularUI.INSTANCE, id, world, x, y, z); 46 | } 47 | 48 | /** 49 | * Open GUI of TileEntity at given position. This should be called only by logical server. For client-only GUI, use 50 | * {@link UIInfos#openClientUI(EntityPlayer, Function)} 51 | */ 52 | public void open(EntityPlayer player, World world, Vec3 pos) { 53 | open(player, world, (int) pos.xCoord, (int) pos.yCoord, (int) pos.zCoord); 54 | } 55 | 56 | public void open(EntityPlayer player) { 57 | open(player, player.getEntityWorld(), (int) player.posX, (int) player.posY, (int) player.posZ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/TabContainer.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.gtnewhorizons.modularui.api.math.Size; 7 | import com.gtnewhorizons.modularui.api.widget.Widget; 8 | 9 | /** 10 | * Widget holding a list of {@link TabButton}. Add tab buttons with {@link #addTabButton(Widget)} and add pages with 11 | * {@link #addPage(Widget)} in corresponding order. At least one child is required. Number of {@link TabButton}s and 12 | * pages should match. 13 | */ 14 | public class TabContainer extends PageControlWidget { 15 | 16 | private final List tabButtons = new ArrayList<>(); 17 | private final List allChildren = new ArrayList<>(); 18 | private Size buttonSize = null; 19 | 20 | @Override 21 | public void initChildren() { 22 | allChildren.clear(); 23 | allChildren.addAll(getPages()); 24 | allChildren.addAll(tabButtons); 25 | } 26 | 27 | @Override 28 | public void onInit() { 29 | super.onInit(); 30 | for (TabButton tabButton : tabButtons) { 31 | tabButton.setTabController(this); 32 | if (tabButton.getPage() < 0 || tabButton.getPage() >= getPages().size()) { 33 | throw new IndexOutOfBoundsException( 34 | String.format( 35 | "TabButton page is %s, but must be 0 - %s", 36 | tabButton.getPage(), 37 | getPages().size() - 1)); 38 | } 39 | if (buttonSize != null && tabButton.isAutoSized()) { 40 | tabButton.setSize(buttonSize); 41 | } 42 | } 43 | } 44 | 45 | @Override 46 | public List getChildren() { 47 | return allChildren; 48 | } 49 | 50 | /** 51 | * @param tabButton Must extend {@link TabButton} 52 | */ 53 | public TabContainer addTabButton(Widget tabButton) { 54 | if (!(tabButton instanceof TabButton)) { 55 | throw new IllegalArgumentException("Tab button must be instance of TabButton"); 56 | } 57 | this.tabButtons.add((TabButton) tabButton); 58 | return this; 59 | } 60 | 61 | public TabContainer setButtonSize(Size buttonSize) { 62 | this.buttonSize = buttonSize; 63 | return this; 64 | } 65 | 66 | public TabContainer setButtonSize(int width, int height) { 67 | return setButtonSize(new Size(width, height)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/MultiChildWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import com.gtnewhorizons.modularui.ModularUI; 10 | import com.gtnewhorizons.modularui.api.math.Size; 11 | import com.gtnewhorizons.modularui.api.widget.IWidgetParent; 12 | import com.gtnewhorizons.modularui.api.widget.Widget; 13 | 14 | public class MultiChildWidget extends Widget implements IWidgetParent { 15 | 16 | protected final List children = new ArrayList<>(); 17 | 18 | public MultiChildWidget addChild(Widget widget) { 19 | if (checkChild(this, widget)) { 20 | children.add(widget); 21 | checkNeedsRebuild(); 22 | } 23 | return this; 24 | } 25 | 26 | public void removeChild(Widget widget) { 27 | if (checkEditable(this)) { 28 | children.remove(widget); 29 | checkNeedsRebuild(); 30 | } 31 | } 32 | 33 | public void removeChild(int index) { 34 | if (checkEditable(this)) { 35 | children.remove(index); 36 | checkNeedsRebuild(); 37 | } 38 | } 39 | 40 | @Override 41 | public List getChildren() { 42 | return Collections.unmodifiableList(children); 43 | } 44 | 45 | @Override 46 | protected @NotNull Size determineSize(int maxWidth, int maxHeight) { 47 | if (!getChildren().isEmpty()) { 48 | return IWidgetParent.getSizeOf(getChildren()); 49 | } 50 | return new Size(maxWidth, maxHeight); 51 | } 52 | 53 | @Deprecated 54 | public static Size getSizeOf(List widgets) { 55 | return IWidgetParent.getSizeOf(widgets); 56 | } 57 | 58 | public static boolean checkChild(Widget parent, Widget widget) { 59 | if (widget == null) { 60 | ModularUI.logger.error("Tried adding null widget to " + parent.getClass().getSimpleName()); 61 | return false; 62 | } 63 | if (widget == parent) { 64 | ModularUI.logger.error("Can't add self!"); 65 | return false; 66 | } 67 | return checkEditable(parent); 68 | } 69 | 70 | public static boolean checkEditable(Widget parent) { 71 | if (parent.isInitialised() && !parent.getContext().isClientOnly()) { 72 | throw new IllegalStateException("Can only dynamically add/remove widgets when the ui is client only!"); 73 | } 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/InternalUIMapper.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import net.minecraft.client.gui.GuiScreen; 7 | import net.minecraft.entity.player.EntityPlayer; 8 | import net.minecraft.world.World; 9 | 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import com.gtnewhorizons.modularui.ModularUI; 13 | import com.gtnewhorizons.modularui.api.screen.IContainerCreator; 14 | import com.gtnewhorizons.modularui.api.screen.IGuiCreator; 15 | 16 | import cpw.mods.fml.common.network.IGuiHandler; 17 | import cpw.mods.fml.common.network.NetworkRegistry; 18 | import cpw.mods.fml.relauncher.Side; 19 | import cpw.mods.fml.relauncher.SideOnly; 20 | 21 | public class InternalUIMapper implements IGuiHandler { 22 | 23 | private static InternalUIMapper INSTANCE; 24 | 25 | public static InternalUIMapper getInstance() { 26 | if (INSTANCE == null) { 27 | INSTANCE = new InternalUIMapper(); 28 | } 29 | return INSTANCE; 30 | } 31 | 32 | private int id; 33 | private final List serverContainers; 34 | private final List clientGuis; 35 | 36 | public InternalUIMapper() { 37 | if (ModularUI.INSTANCE == null) { 38 | throw new NullPointerException("Something went wrong! Mod instance should not be null!"); 39 | } 40 | NetworkRegistry.INSTANCE.registerGuiHandler(ModularUI.INSTANCE, this); 41 | this.serverContainers = new ArrayList<>(); 42 | this.clientGuis = new ArrayList<>(); 43 | } 44 | 45 | public int register(CC containerCreator, GC guiCreator) { 46 | this.serverContainers.add(containerCreator); 47 | this.clientGuis.add(guiCreator); 48 | return id++; 49 | } 50 | 51 | @Nullable 52 | @Override 53 | public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { 54 | return serverContainers.get(id).create(player, world, x, y, z); 55 | } 56 | 57 | @SideOnly(Side.CLIENT) 58 | @Nullable 59 | @Override 60 | public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { 61 | Object screen = clientGuis.get(id).create(player, world, x, y, z); 62 | if (screen != null && !(screen instanceof GuiScreen)) { 63 | throw new RuntimeException("The returned Object of IGuiCreator must be a instance of GuiScreen!"); 64 | } 65 | return screen; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/DraggableWindowWrapper.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | import java.awt.Rectangle; 4 | 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import com.gtnewhorizons.modularui.api.GlStateManager; 8 | import com.gtnewhorizons.modularui.api.math.Pos2d; 9 | import com.gtnewhorizons.modularui.api.widget.IDraggable; 10 | import com.gtnewhorizons.modularui.api.widget.Widget; 11 | 12 | public class DraggableWindowWrapper implements IDraggable { 13 | 14 | private final ModularWindow window; 15 | private final Pos2d relativeClickPos; 16 | private boolean moving; 17 | private final Rectangle area = new Rectangle(); 18 | 19 | public DraggableWindowWrapper(ModularWindow window, Pos2d relativeClickPos) { 20 | this.window = window; 21 | this.relativeClickPos = relativeClickPos; 22 | this.area.setSize(window.getSize().width, window.getSize().height); 23 | } 24 | 25 | @Override 26 | public void renderMovingState(float delta) { 27 | Cursor cursor = window.getContext().getCursor(); 28 | GlStateManager.pushMatrix(); 29 | GlStateManager.translate(-window.getPos().x, -window.getPos().y, 0); 30 | GlStateManager.translate(cursor.getX() - relativeClickPos.x, cursor.getY() - relativeClickPos.y, 0); 31 | window.drawWidgets(delta, false); 32 | GlStateManager.popMatrix(); 33 | } 34 | 35 | @Override 36 | public boolean onDragStart(int button) { 37 | return button == 0; 38 | } 39 | 40 | @Override 41 | public void onDragEnd(boolean successful) { 42 | if (successful) { 43 | window.setPos(window.getContext().getCursor().getPos().subtract(relativeClickPos)); 44 | window.markNeedsRebuild(); 45 | } 46 | } 47 | 48 | @Override 49 | public void onDrag(int mouseButton, long timeSinceLastClick) {} 50 | 51 | @Override 52 | public boolean canDropHere(@Nullable Widget widget, boolean isInBounds) { 53 | return true; 54 | } 55 | 56 | @Override 57 | public @Nullable Rectangle getArea() { 58 | Pos2d cursor = window.getContext().getCursor().getPos(); 59 | this.area.setLocation(cursor.x - relativeClickPos.x, cursor.y - relativeClickPos.y); 60 | return this.area; 61 | } 62 | 63 | @Override 64 | public boolean isMoving() { 65 | return moving; 66 | } 67 | 68 | @Override 69 | public void setMoving(boolean moving) { 70 | this.moving = moving; 71 | this.window.setEnabled(!moving); 72 | } 73 | 74 | public ModularWindow getWindow() { 75 | return window; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/RangedWrapper.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | 3 | import net.minecraft.item.ItemStack; 4 | 5 | import com.google.common.base.Preconditions; 6 | 7 | /** 8 | * A wrapper that composes another IItemHandlerModifiable, exposing only a range of the composed slots. Shifting of slot 9 | * indices is handled automatically for you. 10 | */ 11 | public class RangedWrapper implements IItemHandlerModifiable { 12 | 13 | private final IItemHandlerModifiable compose; 14 | private final int minSlot; 15 | private final int maxSlot; 16 | 17 | public RangedWrapper(IItemHandlerModifiable compose, int minSlot, int maxSlotExclusive) { 18 | Preconditions.checkArgument(maxSlotExclusive > minSlot, "Max slot must be greater than min slot"); 19 | this.compose = compose; 20 | this.minSlot = minSlot; 21 | this.maxSlot = maxSlotExclusive; 22 | } 23 | 24 | @Override 25 | public int getSlots() { 26 | return maxSlot - minSlot; 27 | } 28 | 29 | @Override 30 | public ItemStack getStackInSlot(int slot) { 31 | if (checkSlot(slot)) { 32 | return compose.getStackInSlot(slot + minSlot); 33 | } 34 | 35 | return null; 36 | } 37 | 38 | @Override 39 | public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { 40 | if (checkSlot(slot)) { 41 | return compose.insertItem(slot + minSlot, stack, simulate); 42 | } 43 | 44 | return stack; 45 | } 46 | 47 | @Override 48 | public ItemStack extractItem(int slot, int amount, boolean simulate) { 49 | if (checkSlot(slot)) { 50 | return compose.extractItem(slot + minSlot, amount, simulate); 51 | } 52 | 53 | return null; 54 | } 55 | 56 | @Override 57 | public void setStackInSlot(int slot, ItemStack stack) { 58 | if (checkSlot(slot)) { 59 | compose.setStackInSlot(slot + minSlot, stack); 60 | } 61 | } 62 | 63 | @Override 64 | public int getSlotLimit(int slot) { 65 | if (checkSlot(slot)) { 66 | return compose.getSlotLimit(slot + minSlot); 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | @Override 73 | public boolean isItemValid(int slot, ItemStack stack) { 74 | if (checkSlot(slot)) { 75 | return compose.isItemValid(slot + minSlot, stack); 76 | } 77 | 78 | return false; 79 | } 80 | 81 | private boolean checkSlot(int localSlot) { 82 | return localSlot + minSlot < maxSlot; 83 | } 84 | 85 | public IItemHandlerModifiable getCompose() { 86 | return compose; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/Theme.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal; 2 | 3 | import java.util.HashMap; 4 | 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import com.google.gson.JsonObject; 8 | import com.gtnewhorizons.modularui.ModularUI; 9 | import com.gtnewhorizons.modularui.api.math.Color; 10 | 11 | public class Theme { 12 | 13 | public static final Theme INSTANCE = new Theme(); 14 | 15 | public static final String KEY_BACKGROUND = "bg"; 16 | public static final String KEY_BUTTON = "button"; 17 | public static final String KEY_TEXT = "text"; 18 | public static final String KEY_ITEM_SLOT = "itemslot"; 19 | public static final String KEY_FLUID_SLOT = "fluidslot"; 20 | public static final String KEY_SLOT_HIGHLIGHT = "slothighlight"; 21 | 22 | private final HashMap colors = new HashMap(32); 23 | 24 | private Theme() { 25 | registerThemeColor(KEY_BACKGROUND); 26 | registerThemeColor(KEY_BUTTON); 27 | registerThemeColor(KEY_TEXT, 0x404040); 28 | registerThemeColor(KEY_ITEM_SLOT); 29 | registerThemeColor(KEY_FLUID_SLOT); 30 | registerThemeColor(KEY_SLOT_HIGHLIGHT, Color.withAlpha(Color.WHITE.normal, 0x80)); 31 | } 32 | 33 | public void registerThemeColor(String name) { 34 | registerThemeColor(name, 0xFFFFFFFF); 35 | } 36 | 37 | public void registerThemeColor(String name, int value) { 38 | if (colors.containsKey(name)) { 39 | ModularUI.logger.error("Theme already has a color with key {}", name); 40 | return; 41 | } 42 | colors.put(name, value); 43 | } 44 | 45 | public int getColor(@Nullable String key) { 46 | return key == null ? 0xFFFFFFFF : colors.getOrDefault(key, 0xFFFFFFFF); 47 | } 48 | 49 | public int getBackground() { 50 | return getColor(KEY_BACKGROUND); 51 | } 52 | 53 | public int getButton() { 54 | return getColor(KEY_BUTTON); 55 | } 56 | 57 | public int getText() { 58 | return getColor(KEY_TEXT); 59 | } 60 | 61 | public int getItemSlot() { 62 | return getColor(KEY_ITEM_SLOT); 63 | } 64 | 65 | public int getFluidSlot() { 66 | return getColor(KEY_FLUID_SLOT); 67 | } 68 | 69 | public int getSlotHighlight() { 70 | return getColor(KEY_SLOT_HIGHLIGHT); 71 | } 72 | 73 | public JsonObject readTheme(JsonObject json) { 74 | for (String key : colors.keySet()) { 75 | if (json.has(key)) { 76 | colors.put(key, json.get(key).getAsInt()); 77 | } else { 78 | json.addProperty(key, colors.get(key)); 79 | } 80 | } 81 | return json; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/wrapper/MultiList.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal.wrapper; 2 | 3 | import java.util.AbstractList; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.NoSuchElementException; 9 | 10 | public class MultiList extends AbstractList { 11 | 12 | private final List> lists = new ArrayList<>(); 13 | private final List defaultList = new ArrayList<>(); 14 | 15 | public MultiList() { 16 | this.lists.add(defaultList); 17 | } 18 | 19 | public void addList(List list) { 20 | this.lists.add(list); 21 | } 22 | 23 | public void addElements(T... ts) { 24 | addList(Arrays.asList(ts)); 25 | } 26 | 27 | public void clearLists() { 28 | lists.clear(); 29 | defaultList.clear(); 30 | lists.add(defaultList); 31 | } 32 | 33 | @Override 34 | public void clear() { 35 | for (List list : lists) { 36 | list.clear(); 37 | } 38 | } 39 | 40 | @Override 41 | public boolean add(T element) { 42 | defaultList.add(element); 43 | return true; 44 | } 45 | 46 | @Override 47 | public T get(int index) { 48 | if (index < 0) throw new IndexOutOfBoundsException("Index out of bounds: " + index); 49 | for (List list : lists) { 50 | if (index >= list.size()) { 51 | index -= list.size(); 52 | continue; 53 | } 54 | return list.get(index); 55 | } 56 | throw new IndexOutOfBoundsException("Index out of bounds: " + index); 57 | } 58 | 59 | @Override 60 | public int size() { 61 | return lists.stream().mapToInt(List::size).sum(); 62 | } 63 | 64 | @Override 65 | public Iterator iterator() { 66 | return new MultiListIterator(); 67 | } 68 | 69 | private class MultiListIterator implements Iterator { 70 | 71 | private int listCursor = 0, cursor = 0; 72 | private List currentLists; 73 | 74 | @Override 75 | public boolean hasNext() { 76 | return listCursor < lists.size() || cursor < currentLists.size(); 77 | } 78 | 79 | @Override 80 | public T next() { 81 | try { 82 | while (currentLists == null || cursor == currentLists.size()) { 83 | currentLists = lists.get(listCursor++); 84 | cursor = 0; 85 | } 86 | return currentLists.get(cursor++); 87 | } catch (IndexOutOfBoundsException e) { 88 | throw new NoSuchElementException(); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/fluids/IFluidTankLong.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.fluids; 2 | 3 | import static com.google.common.primitives.Ints.saturatedCast; 4 | 5 | import java.io.IOException; 6 | 7 | import net.minecraft.nbt.NBTTagCompound; 8 | import net.minecraft.network.PacketBuffer; 9 | import net.minecraftforge.fluids.Fluid; 10 | import net.minecraftforge.fluids.FluidStack; 11 | import net.minecraftforge.fluids.FluidTankInfo; 12 | import net.minecraftforge.fluids.IFluidTank; 13 | 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | public interface IFluidTankLong extends IFluidTank { 17 | 18 | long fill(Fluid fluid, long amount, boolean doFill); 19 | 20 | FluidStack drain(long amount, boolean doFill); 21 | 22 | long getCapacityLong(); 23 | 24 | long getRealCapacityLong(); 25 | 26 | long getFluidAmountLong(); 27 | 28 | FluidStack getFluidStack(); 29 | 30 | Fluid getStoredFluid(); 31 | 32 | @Override 33 | default int getFluidAmount() { 34 | return saturatedCast(getFluidAmountLong()); 35 | } 36 | 37 | @Override 38 | default FluidTankInfo getInfo() { 39 | return new FluidTankInfo(this); 40 | } 41 | 42 | @Override 43 | default int fill(FluidStack resource, boolean doFill) { 44 | return saturatedCast(fill(resource.getFluid(), resource.amount, doFill)); 45 | } 46 | 47 | @Override 48 | default FluidStack getFluid() { 49 | return getFluidStack(); 50 | } 51 | 52 | @Override 53 | default int getCapacity() { 54 | return saturatedCast(getCapacityLong()); 55 | } 56 | 57 | @Override 58 | default FluidStack drain(int maxDrain, boolean doDrain) { 59 | return drain((long) maxDrain, doDrain); 60 | } 61 | 62 | void setFluid(Fluid fluid, long amount); 63 | 64 | @Nullable 65 | IFluidTankLong copy(); 66 | 67 | boolean isFluidEqual(@Nullable IFluidTankLong cached); 68 | 69 | void saveToNBT(NBTTagCompound fluidTag); 70 | 71 | void loadFromNBT(NBTTagCompound fluidTag); 72 | 73 | public static void writeToBuffer(PacketBuffer buffer, IFluidTankLong currentFluid) { 74 | if (currentFluid == null) { 75 | buffer.writeBoolean(true); 76 | } else { 77 | buffer.writeBoolean(false); 78 | NBTTagCompound fluidTag = new NBTTagCompound(); 79 | currentFluid.saveToNBT(fluidTag); 80 | 81 | try { 82 | buffer.writeNBTTagCompoundToBuffer(fluidTag); 83 | } catch (IOException ignored) {} 84 | } 85 | } 86 | 87 | public static void readFromBuffer(PacketBuffer buffer, IFluidTankLong currentTank) throws IOException { 88 | currentTank.loadFromNBT(buffer.readBoolean() ? null : buffer.readNBTTagCompoundFromBuffer()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/ISyncedWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | import java.io.IOException; 4 | import java.util.function.Consumer; 5 | 6 | import net.minecraft.network.PacketBuffer; 7 | 8 | import com.gtnewhorizons.modularui.api.screen.ModularWindow; 9 | 10 | import cpw.mods.fml.relauncher.Side; 11 | import cpw.mods.fml.relauncher.SideOnly; 12 | 13 | /** 14 | * Implement this to let them synchronize data between server and client. 15 | */ 16 | public interface ISyncedWidget { 17 | 18 | /** 19 | * Receive packet sent from server. 20 | */ 21 | @SideOnly(Side.CLIENT) 22 | void readOnClient(int id, PacketBuffer buf) throws IOException; 23 | 24 | /** 25 | * Receive packet sent from client. 26 | */ 27 | void readOnServer(int id, PacketBuffer buf) throws IOException; 28 | 29 | /** 30 | * Called AT LEAST each tick on server. Use it to detect and sync changes 31 | * 32 | * @param init true if it is called the first time after init 33 | */ 34 | default void detectAndSendChanges(boolean init) {} 35 | 36 | /** 37 | * Mark this widget as "updated". Call this on appropriate timing on {@link #detectAndSendChanges} or 38 | * {@link #readOnServer} etc. invocation. 39 | */ 40 | void markForUpdate(); 41 | 42 | void unMarkForUpdate(); 43 | 44 | boolean isMarkedForUpdate(); 45 | 46 | /** 47 | * Sends the written data to {@link #readOnServer(int, PacketBuffer)} 48 | * 49 | * @param id helper to determine the type. Must not be -1! 50 | * @param bufBuilder data builder 51 | */ 52 | @SideOnly(Side.CLIENT) 53 | default void syncToServer(int id, Consumer bufBuilder) { 54 | if (!(this instanceof Widget)) { 55 | throw new IllegalStateException("Tried syncing a non Widget ISyncedWidget"); 56 | } 57 | if (id == -1) { 58 | throw new IllegalArgumentException("Id -1 is already reserved for syncing!"); 59 | } 60 | getWindow().getContext().sendClientPacket(id, this, getWindow(), bufBuilder); 61 | } 62 | 63 | /** 64 | * Sends the written data to {@link #readOnClient(int, PacketBuffer)} 65 | * 66 | * @param id helper to determine the type. Must not be -1! 67 | * @param bufBuilder data builder 68 | */ 69 | default void syncToClient(int id, Consumer bufBuilder) { 70 | if (!(this instanceof Widget)) { 71 | throw new IllegalStateException("Tried syncing a non Widget ISyncedWidget"); 72 | } 73 | if (id == -1) { 74 | throw new IllegalArgumentException("Id -1 is already reserved for syncing!"); 75 | } 76 | getWindow().getContext().sendServerPacket(id, this, getWindow(), bufBuilder); 77 | } 78 | 79 | ModularWindow getWindow(); 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/animation/Interpolator.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.animation; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public class Interpolator { 6 | 7 | private final float from; 8 | private final float to; 9 | private final int duration; 10 | private final IEase ease; 11 | private final Consumer interpolate; 12 | private Consumer callback; 13 | 14 | private int runs = 0; 15 | private int progress = 0; 16 | 17 | public Interpolator(float from, float to, int duration, IEase ease, Consumer interpolate) { 18 | this(from, to, duration, ease, interpolate, null); 19 | } 20 | 21 | public Interpolator(float from, float to, int duration, IEase ease, Consumer interpolate, 22 | Consumer callback) { 23 | this.from = from; 24 | this.to = to; 25 | this.duration = duration; 26 | this.ease = ease; 27 | this.interpolate = interpolate; 28 | this.callback = callback; 29 | } 30 | 31 | public Interpolator getReversed(int duration, IEase ease) { 32 | return new Interpolator(to, from, duration, ease, interpolate, callback); 33 | } 34 | 35 | public void setCallback(Consumer callback) { 36 | this.callback = callback; 37 | } 38 | 39 | public void stop() { 40 | runs = 0; 41 | } 42 | 43 | public Interpolator forward() { 44 | progress = 0; 45 | runs = 1; 46 | return this; 47 | } 48 | 49 | public void backwards() { 50 | progress = duration; 51 | runs = -1; 52 | } 53 | 54 | public boolean isAtStart() { 55 | return progress == 0; 56 | } 57 | 58 | public boolean isAtEnd() { 59 | return progress >= duration; 60 | } 61 | 62 | public boolean isRunning() { 63 | return runs != 0 && progress > 0 && progress < duration; 64 | } 65 | 66 | public void update(float partialTicks) { 67 | if (runs != 0) { 68 | if (runs == -1 && progress <= 0) { 69 | progress = 0; 70 | if (callback != null) { 71 | callback.accept(ease.interpolate(progress * 1.0f / duration) * (to - from) + from); 72 | } 73 | stop(); 74 | return; 75 | } else if (runs == 1 && progress >= duration) { 76 | progress = duration; 77 | if (callback != null) { 78 | callback.accept(ease.interpolate(progress * 1.0f / duration) * (to - from) + from); 79 | } 80 | stop(); 81 | return; 82 | } else { 83 | interpolate.accept(ease.interpolate(progress * 1.0f / duration) * (to - from) + from); 84 | } 85 | progress += partialTicks * 50 * runs; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/fluids/FluidTankLongDelegate.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.fluids; 2 | 3 | import static com.google.common.primitives.Ints.saturatedCast; 4 | 5 | import net.minecraft.nbt.NBTTagCompound; 6 | import net.minecraftforge.fluids.Fluid; 7 | import net.minecraftforge.fluids.FluidStack; 8 | import net.minecraftforge.fluids.IFluidTank; 9 | 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import com.gtnewhorizons.modularui.common.fluid.IOverflowableTank; 13 | 14 | public class FluidTankLongDelegate implements IFluidTankLong { 15 | 16 | private final IFluidTank tank; 17 | 18 | public FluidTankLongDelegate(IFluidTank tank) { 19 | this.tank = tank; 20 | } 21 | 22 | @Override 23 | public long fill(Fluid fluid, long amount, boolean doFill) { 24 | return tank.fill(new FluidStack(fluid, saturatedCast(amount)), doFill); 25 | } 26 | 27 | @Override 28 | public FluidStack drain(long amount, boolean doDrain) { 29 | return tank.drain(saturatedCast(amount), doDrain); 30 | } 31 | 32 | @Override 33 | public long getCapacityLong() { 34 | return tank.getCapacity(); 35 | } 36 | 37 | @Override 38 | public long getRealCapacityLong() { 39 | if (tank instanceof IOverflowableTank) { 40 | return ((IOverflowableTank) tank).getRealCapacity(); 41 | } 42 | return tank.getCapacity(); 43 | } 44 | 45 | @Override 46 | public long getFluidAmountLong() { 47 | return tank.getFluidAmount(); 48 | } 49 | 50 | @Override 51 | public FluidStack getFluidStack() { 52 | return tank.getFluid(); 53 | } 54 | 55 | @Override 56 | public Fluid getStoredFluid() { 57 | return tank.getFluid() != null ? tank.getFluid().getFluid() : null; 58 | } 59 | 60 | @Override 61 | public void setFluid(Fluid fluid, long amount) { 62 | tank.drain(Integer.MAX_VALUE, true); 63 | tank.fill(new FluidStack(fluid, saturatedCast(amount)), true); 64 | } 65 | 66 | @Override 67 | public @Nullable IFluidTankLong copy() { 68 | return new FluidTankLong(getStoredFluid(), getCapacityLong(), getFluidAmountLong()); 69 | } 70 | 71 | @Override 72 | public boolean isFluidEqual(@Nullable IFluidTankLong cached) { 73 | return (cached == null && tank.getFluid() == null) 74 | || (cached != null && cached.getFluidStack() == null && tank.getFluid() == null) 75 | || (cached != null && tank.getFluid() != null && tank.getFluid().isFluidEqual(cached.getFluidStack())); 76 | } 77 | 78 | @Override 79 | public void saveToNBT(NBTTagCompound fluidTag) { 80 | if (tank.getFluid() == null) return; 81 | tank.getFluid().writeToNBT(fluidTag); 82 | } 83 | 84 | @Override 85 | public void loadFromNBT(NBTTagCompound fluidTag) { 86 | tank.drain(Integer.MAX_VALUE, true); 87 | tank.fill(FluidStack.loadFluidStackFromNBT(fluidTag), true); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/ItemDrawable.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.function.Supplier; 6 | 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.renderer.RenderHelper; 9 | import net.minecraft.item.ItemStack; 10 | 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import com.gtnewhorizons.modularui.api.GlStateManager; 14 | import com.gtnewhorizons.modularui.api.widget.Widget; 15 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 16 | import com.gtnewhorizons.modularui.common.widget.DrawableWidget; 17 | 18 | /** 19 | * Draws item. Can also be used for {@link com.gtnewhorizons.modularui.api.widget.Widget} 20 | */ 21 | public class ItemDrawable implements IDrawable { 22 | 23 | @NotNull 24 | private Supplier item; 25 | 26 | public ItemDrawable() { 27 | this((ItemStack) null); 28 | } 29 | 30 | public ItemDrawable(ItemStack item) { 31 | this.item = () -> item; 32 | } 33 | 34 | public ItemDrawable(@NotNull Supplier itemGetter) { 35 | this.item = itemGetter; 36 | } 37 | 38 | @Override 39 | public void applyThemeColor(int color) {} 40 | 41 | @Override 42 | public void draw(float x, float y, float width, float height, float partialTicks) { 43 | final ItemStack item = this.item.get(); 44 | if (item == null || item.getItem() == null) return; 45 | GlStateManager.pushMatrix(); 46 | GlStateManager.enableRescaleNormal(); 47 | RenderHelper.enableGUIStandardItemLighting(); 48 | GlStateManager.enableDepth(); 49 | GlStateManager.scale(width / 16, height / 16, 1); 50 | ModularGui.getItemRenderer().renderItemAndEffectIntoGUI( 51 | GuiHelper.getFontRenderer(item), 52 | Minecraft.getMinecraft().getTextureManager(), 53 | item, 54 | (int) x, 55 | (int) y); 56 | GuiHelper.afterRenderItemAndEffectIntoGUI(item); 57 | 58 | GlStateManager.disableRescaleNormal(); 59 | GlStateManager.disableDepth(); 60 | RenderHelper.enableStandardItemLighting(); 61 | GlStateManager.disableLighting(); 62 | GlStateManager.popMatrix(); 63 | } 64 | 65 | @Override 66 | public DrawableWidget asWidget() { 67 | return (DrawableWidget) IDrawable.super.asWidget().setSize(16, 16); 68 | } 69 | 70 | public DrawableWidget asWidgetWithTooltip() { 71 | Widget widget = asWidget(); 72 | return (DrawableWidget) widget.setUpdateTooltipEveryTick(true).dynamicTooltip(() -> { 73 | if (item.get() == null) return Collections.emptyList(); 74 | return new ArrayList<>(GuiHelper.getItemTooltip(item.get())); 75 | }); 76 | } 77 | 78 | public ItemStack getItem() { 79 | return item.get(); 80 | } 81 | 82 | public ItemDrawable setItem(ItemStack item) { 83 | this.item = () -> item; 84 | return this; 85 | } 86 | 87 | public ItemDrawable setItem(@NotNull Supplier itemGetter) { 88 | this.item = itemGetter; 89 | return this; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/internal/JsonLoader.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.internal; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import net.minecraft.util.ResourceLocation; 14 | 15 | import com.google.gson.Gson; 16 | import com.google.gson.GsonBuilder; 17 | import com.google.gson.JsonObject; 18 | import com.google.gson.JsonParseException; 19 | import com.google.gson.JsonParser; 20 | import com.gtnewhorizons.modularui.ModularUI; 21 | import com.gtnewhorizons.modularui.api.forge.CraftingHelper; 22 | import com.gtnewhorizons.modularui.config.Config; 23 | 24 | import cpw.mods.fml.common.Loader; 25 | import cpw.mods.fml.common.ModContainer; 26 | 27 | public class JsonLoader { 28 | 29 | public static final Map GUIS = new HashMap<>(); 30 | public static final JsonParser jsonParser = new JsonParser(); 31 | public static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); 32 | 33 | public static void loadJson() { 34 | if (!Config.useJson) return; 35 | GUIS.clear(); 36 | List mods = Loader.instance().getActiveModList(); 37 | mods.forEach((mod) -> { 38 | String id = mod.getModId(); 39 | CraftingHelper 40 | .findFiles(mod, String.format("assets/%s/guis", id), (path) -> Files.exists(path), (path, file) -> { 41 | if (file.toString().endsWith(".json")) { 42 | JsonObject json = tryExtractFromFile(file); 43 | if (json != null) { 44 | String fileStr = file.toString().replaceAll("\\\\", "/"); 45 | String guiName = fileStr.substring(fileStr.indexOf("guis/") + 5, fileStr.length() - 5); 46 | 47 | ResourceLocation bookId = new ResourceLocation(id, guiName); 48 | GUIS.put(bookId, json); 49 | } 50 | } 51 | return true; 52 | }, false, true); 53 | }); 54 | ModularUI.logger.info("Loaded {} guis from json", GUIS.size()); 55 | } 56 | 57 | public static JsonObject tryExtractFromFile(Path filePath) { 58 | try (InputStream fileStream = Files.newInputStream(filePath)) { 59 | InputStreamReader streamReader = new InputStreamReader(fileStream, StandardCharsets.UTF_8); 60 | return jsonParser.parse(streamReader).getAsJsonObject(); 61 | } catch (IOException exception) { 62 | ModularUI.logger.error("Failed to read file on path {}", filePath, exception); 63 | } catch (JsonParseException exception) { 64 | ModularUI.logger.error("Failed to extract json from file", exception); 65 | } catch (Exception exception) { 66 | ModularUI.logger.error("Failed to extract json from file on path {}", filePath, exception); 67 | } 68 | 69 | return null; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/FallbackableUITexture.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import net.minecraft.client.Minecraft; 8 | 9 | import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; 10 | 11 | /** 12 | * Wrapper for {@link UITexture} that you can specify candidate and fallback. If resource path for candidate doesn't 13 | * exist, fallback is automatically selected. 14 | */ 15 | public class FallbackableUITexture { 16 | 17 | private final UITexture candidate; 18 | private final Object fallback; 19 | private Boolean useFallback = null; 20 | 21 | private static final List ALL_INSTANCES = new ArrayList<>(); 22 | 23 | /** 24 | * @param candidate Texture to look for first. 25 | * @param fallback Texture to look for second. 26 | */ 27 | public FallbackableUITexture(UITexture candidate, UITexture fallback) { 28 | this(candidate, (Object) fallback); 29 | } 30 | 31 | /** 32 | * @param candidate Texture to look for first. 33 | * @param fallback Fallback object to look for second. You can chain multiple fallbacks. 34 | */ 35 | public FallbackableUITexture(UITexture candidate, FallbackableUITexture fallback) { 36 | this(candidate, (Object) fallback); 37 | } 38 | 39 | /** 40 | * @param fallback Texture to always fall back to. 41 | */ 42 | public FallbackableUITexture(UITexture fallback) { 43 | this(null, fallback); 44 | } 45 | 46 | private FallbackableUITexture(UITexture candidate, Object fallback) { 47 | this.candidate = candidate; 48 | this.fallback = fallback; 49 | ALL_INSTANCES.add(this); 50 | } 51 | 52 | public UITexture get() { 53 | verifyCandidate(); 54 | if (useFallback) { 55 | return castFallback(); 56 | } else { 57 | return candidate; 58 | } 59 | } 60 | 61 | private void verifyCandidate() { 62 | if (useFallback == null) { 63 | if (NetworkUtils.isDedicatedClient()) { 64 | if (candidate == null) { 65 | useFallback = true; 66 | } else { 67 | try { 68 | Minecraft.getMinecraft().getResourceManager().getResource(candidate.location); 69 | useFallback = false; 70 | } catch (IOException e) { 71 | useFallback = true; 72 | } 73 | } 74 | } else { 75 | useFallback = true; 76 | } 77 | } 78 | } 79 | 80 | private UITexture castFallback() { 81 | if (fallback instanceof UITexture) { 82 | return (UITexture) fallback; 83 | } else if (fallback instanceof FallbackableUITexture) { 84 | return ((FallbackableUITexture) fallback).get(); 85 | } else { 86 | throw new RuntimeException("Unexpected type found for fallback: " + fallback.getClass()); 87 | } 88 | } 89 | 90 | public static void reload() { 91 | for (FallbackableUITexture t : ALL_INSTANCES) { 92 | t.useFallback = null; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/screen/UIBuildContext.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.screen; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | import java.util.function.Consumer; 9 | import java.util.function.Supplier; 10 | 11 | import javax.annotation.Nullable; 12 | 13 | import net.minecraft.entity.player.EntityPlayer; 14 | 15 | import com.google.common.collect.ImmutableMap; 16 | import com.gtnewhorizons.modularui.ModularUI; 17 | import com.gtnewhorizons.modularui.api.widget.Widget; 18 | 19 | public class UIBuildContext { 20 | 21 | protected final EntityPlayer player; 22 | private final Map jsonWidgets = new HashMap<>(); 23 | protected final ImmutableMap.Builder syncedWindows = new ImmutableMap.Builder<>(); 24 | protected final List closeListeners = new ArrayList<>(); 25 | protected Supplier validator; 26 | protected boolean showNEI = true; 27 | 28 | public UIBuildContext(EntityPlayer player) { 29 | this.player = player; 30 | } 31 | 32 | public EntityPlayer getPlayer() { 33 | return player; 34 | } 35 | 36 | /** 37 | * Sets validator that will be called on every tick on server. If validator returns false, it means GUI is in 38 | * invalid state, and GUI will be closed. 39 | */ 40 | public void setValidator(Supplier validator) { 41 | this.validator = validator; 42 | } 43 | 44 | public void addJsonWidgets(String name, Widget widget) { 45 | if (jsonWidgets.containsKey(name)) { 46 | ModularUI.logger.warn("Widget {} is already registered from json", name); 47 | } 48 | jsonWidgets.put(name, widget); 49 | } 50 | 51 | public void addCloseListener(Runnable runnable) { 52 | closeListeners.add(runnable); 53 | } 54 | 55 | @Nullable 56 | public Widget getJsonWidget(String name) { 57 | return jsonWidgets.get(name); 58 | } 59 | 60 | @Nullable 61 | public T getJsonWidget(String name, Class clazz) { 62 | Widget widget = getJsonWidget(name); 63 | if (widget != null && widget.getClass().isAssignableFrom(clazz)) { 64 | return (T) widget; 65 | } 66 | return null; 67 | } 68 | 69 | public void applyToWidget(String name, Class clazz, Consumer consumer) { 70 | T t = getJsonWidget(name, clazz); 71 | if (t != null) { 72 | consumer.accept(t); 73 | } else { 74 | ModularUI.logger 75 | .error("Expected Widget with name {}, of class {}, but was not found!", name, clazz.getName()); 76 | } 77 | } 78 | 79 | /** 80 | * Registers synced window that can be displayed on top of main window. Call 81 | * {@link ModularUIContext#openSyncedWindow} to actually open the window. 82 | */ 83 | public void addSyncedWindow(int id, IWindowCreator windowCreator) { 84 | if (id <= 0) { 85 | ModularUI.logger.error("Window id must be > 0"); 86 | return; 87 | } 88 | syncedWindows.put(id, Objects.requireNonNull(windowCreator)); 89 | } 90 | 91 | public void setShowNEI(boolean showNEI) { 92 | this.showNEI = showNEI; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/widget/IWidgetBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.widget; 2 | 3 | import java.util.Collection; 4 | 5 | import net.minecraft.entity.player.EntityPlayer; 6 | import net.minecraft.util.ResourceLocation; 7 | 8 | import org.jetbrains.annotations.ApiStatus; 9 | 10 | import com.google.gson.JsonObject; 11 | import com.gtnewhorizons.modularui.ModularUI; 12 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 13 | import com.gtnewhorizons.modularui.api.math.Pos2d; 14 | import com.gtnewhorizons.modularui.api.screen.UIBuildContext; 15 | import com.gtnewhorizons.modularui.common.internal.JsonHelper; 16 | import com.gtnewhorizons.modularui.common.internal.JsonLoader; 17 | import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; 18 | import com.gtnewhorizons.modularui.common.widget.SlotGroup; 19 | import com.gtnewhorizons.modularui.common.widget.SlotWidget; 20 | 21 | @SuppressWarnings("unchecked") 22 | public interface IWidgetBuilder> { 23 | 24 | @ApiStatus.Internal 25 | void addWidgetInternal(Widget widget); 26 | 27 | /** 28 | * Add Widget 29 | */ 30 | default T widget(Widget widget) { 31 | if (widget != null) { 32 | addWidgetInternal(widget); 33 | } 34 | return (T) this; 35 | } 36 | 37 | /** 38 | * Add Widgets 39 | */ 40 | default T widgets(Widget... widgets) { 41 | for (Widget widget : widgets) { 42 | if (widget != null) { 43 | addWidgetInternal(widget); 44 | } 45 | } 46 | return (T) this; 47 | } 48 | 49 | /** 50 | * Add Widgets 51 | */ 52 | default T widgets(Collection widgets) { 53 | return widgets(widgets.toArray(new Widget[0])); 54 | } 55 | 56 | /** 57 | * Add Widget only when {@code doAdd} is true 58 | */ 59 | default T widgetWhen(boolean doAdd, Widget widget) { 60 | if (doAdd) widget(widget); 61 | return (T) this; 62 | } 63 | 64 | default T drawable(IDrawable drawable) { 65 | return widget(drawable.asWidget()); 66 | } 67 | 68 | default T slot(BaseSlot slot) { 69 | return widget(new SlotWidget(slot)); 70 | } 71 | 72 | default T bindPlayerInventory(EntityPlayer player, Pos2d pos, IDrawable background) { 73 | return widget(SlotGroup.playerInventoryGroup(player, background).setPos(pos)); 74 | } 75 | 76 | default T bindPlayerInventory(EntityPlayer player, int x, int y) { 77 | return widget(SlotGroup.playerInventoryGroup(player).setPos(new Pos2d(x, y))); 78 | } 79 | 80 | default T addFromJson(String mod, String location, UIBuildContext buildContext) { 81 | return addFromJson(new ResourceLocation(mod, location), buildContext); 82 | } 83 | 84 | default T addFromJson(String location, UIBuildContext buildContext) { 85 | return addFromJson(new ResourceLocation(location), buildContext); 86 | } 87 | 88 | default T addFromJson(ResourceLocation location, UIBuildContext buildContext) { 89 | JsonObject json = JsonLoader.GUIS.get(location); 90 | if (json == null) { 91 | ModularUI.logger.error("Couldn't not find json file {}", location); 92 | return (T) this; 93 | } 94 | JsonHelper.parseJson(this, json, buildContext); 95 | return (T) this; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/ListItemHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import net.minecraft.item.ItemStack; 6 | 7 | import org.apache.commons.lang3.tuple.ImmutablePair; 8 | import org.apache.commons.lang3.tuple.Pair; 9 | 10 | /** 11 | * Wraps list of {@link IItemHandlerModifiable} into one handler. 12 | */ 13 | @SuppressWarnings("unused") 14 | public class ListItemHandler implements IItemHandlerModifiable { 15 | 16 | protected final Iterable listItemHandler; 17 | 18 | public ListItemHandler(Iterable listItemHandler) { 19 | this.listItemHandler = listItemHandler; 20 | } 21 | 22 | @Override 23 | public int getSlots() { 24 | int ret = 0; 25 | for (IItemHandlerModifiable itemHandler : listItemHandler) { 26 | ret += itemHandler.getSlots(); 27 | } 28 | return ret; 29 | } 30 | 31 | @Override 32 | public ItemStack getStackInSlot(int slot) { 33 | Pair result = findItemHandler(slot); 34 | return result.getLeft().getStackInSlot(result.getRight()); 35 | } 36 | 37 | @Nullable 38 | @Override 39 | public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { 40 | Pair result = findItemHandler(slot); 41 | return result.getLeft().insertItem(result.getRight(), stack, simulate); 42 | } 43 | 44 | @Nullable 45 | @Override 46 | public ItemStack extractItem(int slot, int amount, boolean simulate) { 47 | Pair result = findItemHandler(slot); 48 | return result.getLeft().extractItem(result.getRight(), amount, simulate); 49 | } 50 | 51 | @Override 52 | public int getSlotLimit(int slot) { 53 | Pair result = findItemHandler(slot); 54 | return result.getLeft().getSlotLimit(result.getRight()); 55 | } 56 | 57 | @Override 58 | public void setStackInSlot(int slot, ItemStack stack) { 59 | Pair result = findItemHandler(slot); 60 | result.getLeft().setStackInSlot(result.getRight(), stack); 61 | } 62 | 63 | @Override 64 | public boolean isItemValid(int slot, ItemStack stack) { 65 | Pair result = findItemHandler(slot); 66 | return result.getLeft().isItemValid(result.getRight(), stack); 67 | } 68 | 69 | /** 70 | * Searches all item handlers and find one matching handler that contains specified slot index 71 | * 72 | * @param slot Index to search 73 | * @return Pair of item handler and actual index for it 74 | */ 75 | protected Pair findItemHandler(int slot) { 76 | int searching = 0; 77 | for (IItemHandlerModifiable itemHandler : listItemHandler) { 78 | int numSlots = itemHandler.getSlots(); 79 | if (slot >= searching && slot < searching + numSlots) { 80 | return new ImmutablePair<>(itemHandler, slot - searching); 81 | } 82 | searching += numSlots; 83 | } 84 | throw new RuntimeException("Slot " + slot + " not in valid range - [0," + getSlots() + ")"); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/CraftingHelper.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URI; 6 | import java.net.URISyntaxException; 7 | import java.nio.file.FileSystem; 8 | import java.nio.file.FileSystems; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.util.Iterator; 12 | import java.util.function.BiFunction; 13 | import java.util.function.Function; 14 | 15 | import net.minecraft.item.crafting.CraftingManager; 16 | 17 | import org.apache.commons.io.IOUtils; 18 | 19 | import com.gtnewhorizons.modularui.ModularUI; 20 | 21 | import cpw.mods.fml.common.ModContainer; 22 | 23 | public class CraftingHelper { 24 | 25 | private static final boolean DEBUG_LOAD_MINECRAFT = false; 26 | 27 | public static boolean findFiles(ModContainer mod, String base, Function preprocessor, 28 | BiFunction processor, boolean defaultUnfoundRoot, boolean visitAllFiles) { 29 | 30 | File source = mod.getSource(); 31 | 32 | if ("minecraft".equals(mod.getModId())) { 33 | if (!DEBUG_LOAD_MINECRAFT) return true; 34 | 35 | try { 36 | URI tmp = CraftingManager.class.getResource("/assets/.mcassetsroot").toURI(); 37 | source = new File(tmp.resolve("..").getPath()); 38 | } catch (URISyntaxException e) { 39 | ModularUI.logger.error("Error finding Minecraft jar: ", e); 40 | return false; 41 | } 42 | } 43 | 44 | FileSystem fs = null; 45 | boolean success = true; 46 | 47 | try { 48 | Path root = null; 49 | 50 | if (source.isFile()) { 51 | try { 52 | fs = FileSystems.newFileSystem(source.toPath(), null); 53 | root = fs.getPath("/" + base); 54 | } catch (IOException e) { 55 | ModularUI.logger.error("Error loading FileSystem from jar: ", e); 56 | return false; 57 | } 58 | } else if (source.isDirectory()) { 59 | root = source.toPath().resolve(base); 60 | } 61 | 62 | if (root == null || !Files.exists(root)) return defaultUnfoundRoot; 63 | 64 | if (preprocessor != null) { 65 | Boolean cont = preprocessor.apply(root); 66 | if (cont == null || !cont.booleanValue()) return false; 67 | } 68 | 69 | if (processor != null) { 70 | Iterator itr = null; 71 | try { 72 | itr = Files.walk(root).iterator(); 73 | } catch (IOException e) { 74 | ModularUI.logger.error("Error iterating filesystem for: {}", mod.getModId(), e); 75 | return false; 76 | } 77 | 78 | while (itr != null && itr.hasNext()) { 79 | Boolean cont = processor.apply(root, itr.next()); 80 | 81 | if (visitAllFiles) { 82 | success &= cont != null && cont; 83 | } else if (cont == null || !cont) { 84 | return false; 85 | } 86 | } 87 | } 88 | } finally { 89 | IOUtils.closeQuietly(fs); 90 | } 91 | 92 | return success; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/drawable/shapes/Circle.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.drawable.shapes; 2 | 3 | import net.minecraft.client.renderer.Tessellator; 4 | 5 | import org.jetbrains.annotations.Contract; 6 | import org.lwjgl.opengl.GL11; 7 | 8 | import com.gtnewhorizons.modularui.api.GlStateManager; 9 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 10 | import com.gtnewhorizons.modularui.api.math.Color; 11 | 12 | public class Circle implements IDrawable { 13 | 14 | public static final double PI2 = Math.PI * 2; 15 | 16 | private int colorInner, colorOuter, segments; 17 | 18 | public Circle() { 19 | this.colorInner = 0; 20 | this.colorOuter = 0; 21 | this.segments = 40; 22 | } 23 | 24 | @Contract("_ -> this") 25 | public Circle setColorInner(int colorInner) { 26 | this.colorInner = colorInner; 27 | return this; 28 | } 29 | 30 | public Circle setColorOuter(int colorOuter) { 31 | this.colorOuter = colorOuter; 32 | return this; 33 | } 34 | 35 | public Circle setColor(int inner, int outer) { 36 | this.colorInner = inner; 37 | this.colorOuter = outer; 38 | return this; 39 | } 40 | 41 | public Circle setSegments(int segments) { 42 | this.segments = segments; 43 | return this; 44 | } 45 | 46 | @Override 47 | public void applyThemeColor(int color) { 48 | if (colorInner == 0 && colorOuter == 0) { 49 | IDrawable.super.applyThemeColor(color == 0 ? 0xFFFFFFFF : color); 50 | } 51 | } 52 | 53 | @Override 54 | public void draw(float x0, float y0, float width, float height, float partialTicks) { 55 | GlStateManager.disableTexture2D(); 56 | GlStateManager.enableBlend(); 57 | GlStateManager.enableAlpha(); 58 | GlStateManager.disableLighting(); 59 | GlStateManager.tryBlendFuncSeparate( 60 | GlStateManager.SourceFactor.SRC_ALPHA, 61 | GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, 62 | GlStateManager.SourceFactor.ONE, 63 | GlStateManager.DestFactor.ZERO); 64 | GlStateManager.shadeModel(7425); 65 | Tessellator tessellator = Tessellator.instance; 66 | float x_2 = x0 + width / 2, y_2 = y0 + height / 2; 67 | tessellator.startDrawing(GL11.GL_TRIANGLE_FAN); 68 | tessellator.setColorRGBA( 69 | Color.getRed(colorInner), 70 | Color.getGreen(colorInner), 71 | Color.getBlue(colorInner), 72 | Color.getAlpha(colorInner)); 73 | tessellator.addVertex(x_2, y_2, 0.0f); 74 | float incr = (float) (PI2 / segments); 75 | for (int i = 0; i <= segments; i++) { 76 | float angle = incr * i; 77 | float x = (float) (Math.sin(angle) * (width / 2) + x_2); 78 | float y = (float) (Math.cos(angle) * (height / 2) + y_2); 79 | tessellator.setColorRGBA( 80 | Color.getRed(colorOuter), 81 | Color.getGreen(colorOuter), 82 | Color.getBlue(colorOuter), 83 | Color.getAlpha(colorOuter)); 84 | tessellator.addVertex(x, y, 0.0f); 85 | } 86 | tessellator.draw(); 87 | GlStateManager.shadeModel(7424); 88 | GlStateManager.disableBlend(); 89 | GlStateManager.enableAlpha(); 90 | GlStateManager.enableTexture2D(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/VanillaButtonWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import com.gtnewhorizons.modularui.api.ModularUITextures; 8 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 9 | import com.gtnewhorizons.modularui.api.drawable.TextRenderer; 10 | import com.gtnewhorizons.modularui.api.math.Alignment; 11 | 12 | /** 13 | * {@link net.minecraft.client.gui.GuiButton}-alike button widget. 14 | */ 15 | public class VanillaButtonWidget extends ButtonWidget { 16 | 17 | private String displayString; 18 | private Supplier clickableGetter; 19 | private final TextRenderer textRenderer = new TextRenderer(); 20 | private IDrawable[] normalBackground = new IDrawable[] { ModularUITextures.VANILLA_BUTTON_NORMAL }; 21 | private IDrawable[] hoveredBackground = new IDrawable[] { ModularUITextures.VANILLA_BUTTON_HOVERED }; 22 | private IDrawable[] disabledBackground = new IDrawable[] { ModularUITextures.VANILLA_BUTTON_DISABLED }; 23 | 24 | @Override 25 | public @Nullable IDrawable[] getBackground() { 26 | if (!isClickable()) return disabledBackground; 27 | if (isHovering()) return hoveredBackground; 28 | return normalBackground; 29 | } 30 | 31 | @Override 32 | public ClickResult onClick(int buttonId, boolean doubleClick) { 33 | if (!isClickable()) return ClickResult.REJECT; 34 | return super.onClick(buttonId, doubleClick); 35 | } 36 | 37 | @Override 38 | public void draw(float partialTicks) { 39 | if (displayString != null) { 40 | textRenderer.setPos(0, 0); 41 | textRenderer.setShadow(true); 42 | textRenderer.setAlignment(Alignment.Center, size.width, size.height); 43 | textRenderer.setColor(!isClickable() ? 0xa0a0a0 : isHovering() ? 0xffffa0 : 0xffffff); 44 | textRenderer.draw(displayString); 45 | } 46 | } 47 | 48 | public boolean isClickable() { 49 | return clickableGetter != null ? clickableGetter.get() : true; 50 | } 51 | 52 | public VanillaButtonWidget setDisplayString(String displayString) { 53 | this.displayString = displayString; 54 | return this; 55 | } 56 | 57 | public VanillaButtonWidget setClickableGetter(Supplier clickableGetter) { 58 | this.clickableGetter = clickableGetter; 59 | return this; 60 | } 61 | 62 | public VanillaButtonWidget setBackground(IDrawable[] normalBackground, IDrawable[] hoveredBackground, 63 | IDrawable[] disabledBackground) { 64 | this.normalBackground = normalBackground; 65 | this.hoveredBackground = hoveredBackground; 66 | this.disabledBackground = disabledBackground; 67 | return this; 68 | } 69 | 70 | public VanillaButtonWidget setOverlay(IDrawable normalOverlay, IDrawable hoveredOverlay, 71 | IDrawable disabledOverlay) { 72 | return setBackground( 73 | new IDrawable[] { ModularUITextures.VANILLA_BUTTON_NORMAL, normalOverlay }, 74 | new IDrawable[] { ModularUITextures.VANILLA_BUTTON_HOVERED, hoveredOverlay }, 75 | new IDrawable[] { ModularUITextures.VANILLA_BUTTON_DISABLED, disabledOverlay }); 76 | } 77 | 78 | public VanillaButtonWidget setOverlay(IDrawable overlay) { 79 | return setOverlay(overlay, overlay, overlay); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/DynamicPositionedRow.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import net.minecraft.network.PacketBuffer; 8 | 9 | import com.gtnewhorizons.modularui.api.widget.ISyncedWidget; 10 | import com.gtnewhorizons.modularui.api.widget.Widget; 11 | 12 | public class DynamicPositionedRow extends Row implements ISyncedWidget { 13 | 14 | private final List childrenEnabledFlags = new ArrayList<>(); 15 | 16 | private boolean needsUpdate; 17 | private boolean syncsToClient = true; 18 | 19 | public DynamicPositionedRow() { 20 | this.skipDisabledChild = true; 21 | } 22 | 23 | @Override 24 | public void initChildren() { 25 | for (Widget child : getChildren()) { 26 | childrenEnabledFlags.add(child.isEnabled()); 27 | } 28 | } 29 | 30 | @Override 31 | public void onScreenUpdate() { 32 | boolean needsReposition = false; 33 | for (int i = 0; i < getChildren().size(); i++) { 34 | if (childrenEnabledFlags.get(i) != getChildren().get(i).isEnabled()) { 35 | needsReposition = true; 36 | childrenEnabledFlags.set(i, getChildren().get(i).isEnabled()); 37 | } 38 | } 39 | if (needsReposition) { 40 | layoutChildren(getWindow().getSize().width, getWindow().getSize().height); 41 | } 42 | } 43 | 44 | @Override 45 | public void detectAndSendChanges(boolean init) { 46 | if (syncsToClient) { 47 | boolean needsReposition = false; 48 | for (int i = 0; i < getChildren().size(); i++) { 49 | if (childrenEnabledFlags.get(i) != getChildren().get(i).isEnabled()) { 50 | needsReposition = true; 51 | childrenEnabledFlags.set(i, getChildren().get(i).isEnabled()); 52 | } 53 | } 54 | if (init || needsReposition) { 55 | syncToClient(0, buffer -> { 56 | buffer.writeVarIntToBuffer(getChildren().size()); 57 | for (boolean flag : childrenEnabledFlags) { 58 | buffer.writeBoolean(flag); 59 | } 60 | }); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void readOnClient(int id, PacketBuffer buf) throws IOException { 67 | if (id == 0) { 68 | int length = buf.readVarIntFromBuffer(); 69 | for (int i = 0; i < length && i < childrenEnabledFlags.size(); i++) { 70 | boolean flag = buf.readBoolean(); 71 | childrenEnabledFlags.set(i, flag); 72 | getChildren().get(i).setEnabledForce(flag); 73 | } 74 | layoutChildren(getWindow().getSize().width, getWindow().getSize().height); 75 | } 76 | } 77 | 78 | @Override 79 | public void readOnServer(int id, PacketBuffer buf) throws IOException {} 80 | 81 | @Override 82 | public void markForUpdate() { 83 | needsUpdate = true; 84 | } 85 | 86 | @Override 87 | public void unMarkForUpdate() { 88 | needsUpdate = false; 89 | } 90 | 91 | @Override 92 | public boolean isMarkedForUpdate() { 93 | return needsUpdate; 94 | } 95 | 96 | public DynamicPositionedRow setSynced(boolean syncsToClient) { 97 | this.syncsToClient = syncsToClient; 98 | return this; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/DynamicPositionedColumn.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import net.minecraft.network.PacketBuffer; 8 | 9 | import com.gtnewhorizons.modularui.api.widget.ISyncedWidget; 10 | import com.gtnewhorizons.modularui.api.widget.Widget; 11 | 12 | public class DynamicPositionedColumn extends Column implements ISyncedWidget { 13 | 14 | private final List childrenEnabledFlags = new ArrayList<>(); 15 | 16 | private boolean needsUpdate; 17 | private boolean syncsToClient = true; 18 | 19 | public DynamicPositionedColumn() { 20 | this.skipDisabledChild = true; 21 | } 22 | 23 | @Override 24 | public void initChildren() { 25 | for (Widget child : getChildren()) { 26 | childrenEnabledFlags.add(child.isEnabled()); 27 | } 28 | } 29 | 30 | @Override 31 | public void onScreenUpdate() { 32 | boolean needsReposition = false; 33 | for (int i = 0; i < getChildren().size(); i++) { 34 | if (childrenEnabledFlags.get(i) != getChildren().get(i).isEnabled()) { 35 | needsReposition = true; 36 | childrenEnabledFlags.set(i, getChildren().get(i).isEnabled()); 37 | } 38 | } 39 | if (needsReposition) { 40 | layoutChildren(getWindow().getSize().width, getWindow().getSize().height); 41 | } 42 | } 43 | 44 | @Override 45 | public void detectAndSendChanges(boolean init) { 46 | if (syncsToClient) { 47 | boolean needsReposition = false; 48 | for (int i = 0; i < getChildren().size(); i++) { 49 | if (childrenEnabledFlags.get(i) != getChildren().get(i).isEnabled()) { 50 | needsReposition = true; 51 | childrenEnabledFlags.set(i, getChildren().get(i).isEnabled()); 52 | } 53 | } 54 | if (init || needsReposition) { 55 | syncToClient(0, buffer -> { 56 | buffer.writeVarIntToBuffer(getChildren().size()); 57 | for (boolean flag : childrenEnabledFlags) { 58 | buffer.writeBoolean(flag); 59 | } 60 | }); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void readOnClient(int id, PacketBuffer buf) throws IOException { 67 | if (id == 0) { 68 | int length = buf.readVarIntFromBuffer(); 69 | for (int i = 0; i < length && i < childrenEnabledFlags.size(); i++) { 70 | boolean flag = buf.readBoolean(); 71 | childrenEnabledFlags.set(i, flag); 72 | getChildren().get(i).setEnabledForce(flag); 73 | } 74 | layoutChildren(getWindow().getSize().width, getWindow().getSize().height); 75 | } 76 | } 77 | 78 | @Override 79 | public void readOnServer(int id, PacketBuffer buf) throws IOException {} 80 | 81 | @Override 82 | public void markForUpdate() { 83 | needsUpdate = true; 84 | } 85 | 86 | @Override 87 | public void unMarkForUpdate() { 88 | needsUpdate = false; 89 | } 90 | 91 | @Override 92 | public boolean isMarkedForUpdate() { 93 | return needsUpdate; 94 | } 95 | 96 | public DynamicPositionedColumn setSynced(boolean syncsToClient) { 97 | this.syncsToClient = syncsToClient; 98 | return this; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/ModularUITextures.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api; 2 | 3 | import com.gtnewhorizons.modularui.ModularUI; 4 | import com.gtnewhorizons.modularui.api.drawable.AdaptableUITexture; 5 | import com.gtnewhorizons.modularui.api.drawable.UITexture; 6 | 7 | public class ModularUITextures { 8 | 9 | public static final UITexture ICON_INFO = UITexture.fullImage(ModularUI.MODID, "gui/widgets/information"); 10 | public static final UITexture VANILLA_BACKGROUND = AdaptableUITexture 11 | .of(ModularUI.MODID, "gui/background/vanilla_background", 195, 136, 4); 12 | public static final AdaptableUITexture BACKGROUND_BORDER_1PX = AdaptableUITexture 13 | .of(ModularUI.MODID, "gui/background/background_border_1px", 8, 8, 1); 14 | public static final AdaptableUITexture BASE_BUTTON = AdaptableUITexture 15 | .of(ModularUI.MODID, "gui/widgets/base_button", 18, 18, 1); 16 | public static final AdaptableUITexture ITEM_SLOT = AdaptableUITexture 17 | .of(ModularUI.MODID, "gui/slot/item", 18, 18, 1); 18 | public static final AdaptableUITexture FLUID_SLOT = AdaptableUITexture 19 | .of(ModularUI.MODID, "gui/slot/fluid", 18, 18, 1); 20 | 21 | public static final UITexture ARROW_LEFT = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_left"); 22 | public static final UITexture ARROW_RIGHT = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_right"); 23 | public static final UITexture ARROW_UP = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_up"); 24 | public static final UITexture ARROW_DOWN = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_down"); 25 | public static final UITexture CROSS = UITexture.fullImage(ModularUI.MODID, "gui/icons/cross"); 26 | public static final UITexture ARROW_GRAY_LEFT = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_gray_left"); 27 | public static final UITexture ARROW_GRAY_RIGHT = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_gray_right"); 28 | public static final UITexture ARROW_GRAY_UP = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_gray_up"); 29 | public static final UITexture ARROW_GRAY_DOWN = UITexture.fullImage(ModularUI.MODID, "gui/icons/arrow_gray_down"); 30 | public static final UITexture CROSS_GRAY = UITexture.fullImage(ModularUI.MODID, "gui/icons/cross_gray"); 31 | 32 | public static final UITexture VANILLA_TAB_TOP = UITexture.fullImage(ModularUI.MODID, "gui/tab/tabs_top"); 33 | public static final UITexture VANILLA_TAB_BOTTOM = UITexture.fullImage(ModularUI.MODID, "gui/tab/tabs_bottom"); 34 | public static final UITexture VANILLA_TAB_LEFT = UITexture.fullImage(ModularUI.MODID, "gui/tab/tabs_left"); 35 | public static final UITexture VANILLA_TAB_RIGHT = UITexture.fullImage(ModularUI.MODID, "gui/tab/tabs_right"); 36 | 37 | public static final UITexture VANILLA_TAB_TOP_START = VANILLA_TAB_TOP.getSubArea(0f, 0f, 1 / 3f, 1f); 38 | public static final UITexture VANILLA_TAB_TOP_MIDDLE = VANILLA_TAB_TOP.getSubArea(1 / 3f, 0f, 2 / 3f, 1f); 39 | public static final UITexture VANILLA_TAB_TOP_END = VANILLA_TAB_TOP.getSubArea(2 / 3f, 0f, 1f, 1f); 40 | 41 | public static final AdaptableUITexture VANILLA_BUTTON_DISABLED = AdaptableUITexture 42 | .of(ModularUI.MODID, "gui/widgets/vanilla_button_disabled", 20, 20, 2); 43 | public static final AdaptableUITexture VANILLA_BUTTON_NORMAL = AdaptableUITexture 44 | .of(ModularUI.MODID, "gui/widgets/vanilla_button_normal", 20, 20, 2); 45 | public static final AdaptableUITexture VANILLA_BUTTON_HOVERED = AdaptableUITexture 46 | .of(ModularUI.MODID, "gui/widgets/vanilla_button_hovered", 20, 20, 2); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/fluids/ListFluidHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.fluids; 2 | 3 | import net.minecraftforge.fluids.Fluid; 4 | import net.minecraftforge.fluids.FluidStack; 5 | 6 | import org.apache.commons.lang3.tuple.Pair; 7 | 8 | public class ListFluidHandler implements IFluidTanksHandler { 9 | 10 | protected final Iterable fluidHandlers; 11 | 12 | public ListFluidHandler(Iterable fluidHandlers) { 13 | this.fluidHandlers = fluidHandlers; 14 | } 15 | 16 | @Override 17 | public int getTanks() { 18 | int tanks = 0; 19 | 20 | for (IFluidTanksHandler fluidHandler : fluidHandlers) { 21 | tanks += fluidHandler.getTanks(); 22 | } 23 | return tanks; 24 | } 25 | 26 | @Override 27 | public FluidStack getFluidStackInTank(int tank) { 28 | Pair result = findFluidHandler(tank); 29 | return result.getLeft().getFluidStackInTank(result.getRight()); 30 | } 31 | 32 | @Override 33 | public FluidStack fill(int tank, Fluid fluid, long amount, boolean simulate) { 34 | Pair result = findFluidHandler(tank); 35 | return result.getLeft().fill(result.getRight(), fluid, amount, simulate); 36 | } 37 | 38 | @Override 39 | public FluidStack drain(int tank, long amount, boolean simulate) { 40 | Pair result = findFluidHandler(tank); 41 | return result.getLeft().drain(result.getRight(), amount, simulate); 42 | } 43 | 44 | @Override 45 | public long getTankCapacity(int tank) { 46 | Pair result = findFluidHandler(tank); 47 | return result.getLeft().getTankCapacity(result.getRight()); 48 | } 49 | 50 | @Override 51 | public long getRealTankCapacity(int tank) { 52 | Pair result = findFluidHandler(tank); 53 | return result.getLeft().getRealTankCapacity(result.getRight()); 54 | } 55 | 56 | @Override 57 | public long getTankStoredAmount(int tank) { 58 | Pair result = findFluidHandler(tank); 59 | return result.getLeft().getTankStoredAmount(result.getRight()); 60 | } 61 | 62 | protected Pair findFluidHandler(int tank) { 63 | int searching = 0; 64 | int amountOfTanks; 65 | 66 | for (IFluidTanksHandler fluidHandler : fluidHandlers) { 67 | amountOfTanks = fluidHandler.getTanks(); 68 | if (tank >= searching && tank < searching + amountOfTanks) { 69 | return Pair.of(fluidHandler, tank - searching); 70 | } 71 | } 72 | 73 | throw new RuntimeException("Tank " + tank + " not in valid range - [0," + this.getTanks() + ")"); 74 | } 75 | 76 | @Override 77 | public void setFluidInTank(int tank, Fluid fluid, long amount) { 78 | Pair result = findFluidHandler(tank); 79 | result.getLeft().setFluidInTank(result.getRight(), fluid, amount); 80 | } 81 | 82 | @Override 83 | public IFluidTankLong getFluidTank(int tank) { 84 | Pair result = findFluidHandler(tank); 85 | return result.getLeft().getFluidTank(tank); 86 | } 87 | 88 | @Override 89 | public Fluid getFluidInTank(int tank) { 90 | Pair result = findFluidHandler(tank); 91 | return result.getLeft().getFluidInTank(tank); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/DynamicTextWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.io.IOException; 4 | import java.util.function.Supplier; 5 | 6 | import net.minecraft.network.PacketBuffer; 7 | 8 | import com.gtnewhorizons.modularui.api.drawable.Text; 9 | import com.gtnewhorizons.modularui.api.widget.ISyncedWidget; 10 | import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; 11 | 12 | /** 13 | * Allows changing text dynamically. Syncs text from server to client. 14 | */ 15 | public class DynamicTextWidget extends TextWidget implements ISyncedWidget { 16 | 17 | private final Supplier textSupplier; 18 | 19 | private boolean syncsToClient = true; 20 | private Text lastText; 21 | 22 | private static final int MAX_PACKET_LENGTH = Short.MAX_VALUE; 23 | 24 | public DynamicTextWidget(Supplier text) { 25 | this.textSupplier = text; 26 | this.isDynamic = true; 27 | } 28 | 29 | @Override 30 | public void onInit() { 31 | lastText = new Text(""); 32 | } 33 | 34 | @Override 35 | public Text getText() { 36 | return syncsToClient() ? lastText : updateText(); 37 | } 38 | 39 | /** 40 | * Executed only on server 41 | */ 42 | private Text updateText() { 43 | Text ret = textSupplier.get(); 44 | if (defaultColor != null) { 45 | ret.color(defaultColor); 46 | } 47 | if (defaultFormat != null) { 48 | ret.format(defaultFormat); 49 | } 50 | return ret; 51 | } 52 | 53 | public DynamicTextWidget setSynced(boolean synced) { 54 | this.syncsToClient = synced; 55 | return this; 56 | } 57 | 58 | /** 59 | * @return if this widget should operate on the server side. For example detecting and sending changes to client. 60 | */ 61 | protected boolean syncsToClient() { 62 | return syncsToClient; 63 | } 64 | 65 | @Override 66 | public void readOnClient(int id, PacketBuffer buf) throws IOException { 67 | if (id == 0) { 68 | Text newText = new Text(NetworkUtils.readStringSafe(buf)); 69 | newText.color(buf.readVarIntFromBuffer()); 70 | newText.setFormatting(NetworkUtils.readStringSafe(buf)); 71 | newText.shadow(buf.readBoolean()); 72 | lastText = newText; 73 | checkNeedsRebuild(); 74 | } 75 | } 76 | 77 | @Override 78 | public void readOnServer(int id, PacketBuffer buf) throws IOException {} 79 | 80 | @Override 81 | public void detectAndSendChanges(boolean init) { 82 | if (!syncsToClient()) return; 83 | Text newText = updateText(); 84 | if (init || needsSync(newText)) { 85 | this.lastText = newText; 86 | syncToClient(0, buffer -> { 87 | NetworkUtils.writeStringSafe(buffer, newText.getRawText()); 88 | buffer.writeVarIntToBuffer(newText.getColor()); 89 | NetworkUtils.writeStringSafe(buffer, newText.getFormatting()); 90 | buffer.writeBoolean(newText.hasShadow()); 91 | }); 92 | } 93 | } 94 | 95 | @Override 96 | public void markForUpdate() {} 97 | 98 | @Override 99 | public void unMarkForUpdate() {} 100 | 101 | @Override 102 | public boolean isMarkedForUpdate() { 103 | // assume update was handled somewhere else 104 | return false; 105 | } 106 | 107 | private boolean needsSync(Text newText) { 108 | if (lastText == null && newText == null) return false; 109 | else if (lastText == null) return false; 110 | return !lastText.getRawText().equals(newText.getRawText()) || lastText.getColor() != newText.getColor() 111 | || !lastText.getFormatting().equals(newText.getFormatting()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/PageControlWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import com.gtnewhorizons.modularui.ModularUI; 10 | import com.gtnewhorizons.modularui.api.math.Size; 11 | import com.gtnewhorizons.modularui.api.widget.IWidgetParent; 12 | import com.gtnewhorizons.modularui.api.widget.Widget; 13 | 14 | /** 15 | * Parent widget that can contain children and switch them with pagination. At least one child is required. Number of 16 | * children and pages should match. 17 | */ 18 | public class PageControlWidget extends Widget implements IWidgetParent { 19 | 20 | private int currentPage = 0; 21 | private final List pages = new ArrayList<>(); 22 | 23 | @Override 24 | public void onInit() { 25 | if (pages.isEmpty()) { 26 | throw new IllegalStateException("PageControlWidget must have at least one child!"); 27 | } 28 | for (int i = 0; i < pages.size(); i++) { 29 | if (i != currentPage) { 30 | setPage(i, false); 31 | } 32 | } 33 | } 34 | 35 | @Override 36 | protected @NotNull Size determineSize(int maxWidth, int maxHeight) { 37 | return pages.isEmpty() ? super.determineSize(maxWidth, maxHeight) : IWidgetParent.getSizeOf(pages); 38 | } 39 | 40 | protected List getPages() { 41 | return pages; 42 | } 43 | 44 | @Override 45 | public List getChildren() { 46 | return Collections.unmodifiableList(pages); 47 | } 48 | 49 | public int getCurrentPage() { 50 | return currentPage; 51 | } 52 | 53 | public void nextPage() { 54 | if (currentPage + 1 >= pages.size()) { 55 | setActivePage(0); 56 | } else { 57 | setActivePage(currentPage + 1); 58 | } 59 | } 60 | 61 | public void prevPage() { 62 | if (currentPage == 0) { 63 | setActivePage(pages.size() - 1); 64 | } else { 65 | setActivePage(currentPage - 1); 66 | } 67 | } 68 | 69 | public void setActivePage(int page) { 70 | if (page > pages.size() - 1 || page < 0) { 71 | throw new IndexOutOfBoundsException( 72 | "Tried setting active page to " + page + " while only 0 - " + (pages.size() - 1) + " is allowed"); 73 | } 74 | if (!isInitialised()) { 75 | this.currentPage = page; 76 | return; 77 | } 78 | setPage(currentPage, false); 79 | this.currentPage = page; 80 | setPage(currentPage, true); 81 | } 82 | 83 | private void setPage(int page, boolean active) { 84 | Widget widget = pages.get(page); 85 | widget.setEnabled(active); 86 | setEnabledAllChildren(active, widget); 87 | if (active) { 88 | for (Widget pageWidget : pages) { 89 | if (pageWidget != widget) { 90 | pageWidget.setEnabled(false); 91 | setEnabledAllChildren(false, pageWidget); 92 | } 93 | } 94 | } 95 | } 96 | 97 | private void setEnabledAllChildren(boolean active, Widget parent) { 98 | if (parent instanceof IWidgetParent) { 99 | IWidgetParent.forEachByLayer(parent, forEach -> { 100 | forEach.setEnabled(active); 101 | return false; 102 | }); 103 | } 104 | } 105 | 106 | public PageControlWidget addPage(Widget page) { 107 | if (page == this) { 108 | ModularUI.logger.error("Can't add self!"); 109 | return this; 110 | } 111 | if (isInitialised()) { 112 | ModularUI.logger.error("Can't add child after initialised!"); 113 | } else { 114 | pages.add(page); 115 | } 116 | return this; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/Row.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import com.gtnewhorizons.modularui.api.math.CrossAxisAlignment; 6 | import com.gtnewhorizons.modularui.api.math.MainAxisAlignment; 7 | import com.gtnewhorizons.modularui.api.math.Pos2d; 8 | import com.gtnewhorizons.modularui.api.math.Size; 9 | import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder; 10 | import com.gtnewhorizons.modularui.api.widget.IWidgetParent; 11 | import com.gtnewhorizons.modularui.api.widget.Widget; 12 | 13 | public class Row extends MultiChildWidget implements IWidgetBuilder { 14 | 15 | private MainAxisAlignment maa = MainAxisAlignment.START; 16 | private CrossAxisAlignment caa = CrossAxisAlignment.START; 17 | private int maxWidth = -1, maxHeight = 0; 18 | private Integer space; 19 | 20 | protected boolean skipDisabledChild = false; 21 | 22 | @Override 23 | public void addWidgetInternal(Widget widget) { 24 | addChild(widget); 25 | } 26 | 27 | @Override 28 | protected @NotNull Size determineSize(int maxWidth, int maxHeight) { 29 | if (maa == MainAxisAlignment.START) { 30 | return IWidgetParent.getSizeOf(children); 31 | } 32 | return new Size(this.maxWidth, this.maxHeight); 33 | } 34 | 35 | @Override 36 | public void layoutChildren(int maxWidthC, int maxHeightC) { 37 | if (maxWidth < 0 && maa != MainAxisAlignment.START) { 38 | if (isAutoSized()) { 39 | maxWidth = maxWidthC - getPos().x; 40 | } else { 41 | maxWidth = getSize().width; 42 | } 43 | } 44 | 45 | this.maxHeight = 0; 46 | int totalWidth = 0; 47 | 48 | for (Widget widget : getChildren()) { 49 | if (skipDisabledChild && !widget.isEnabled()) continue; 50 | totalWidth += widget.getSize().width; 51 | maxHeight = Math.max(maxHeight, widget.getSize().height); 52 | } 53 | 54 | int lastX = 0; 55 | if (maa == MainAxisAlignment.CENTER) { 56 | lastX = (int) (maxWidth / 2f - totalWidth / 2f); 57 | } else if (maa == MainAxisAlignment.END) { 58 | lastX = maxWidth - totalWidth; 59 | } 60 | 61 | for (Widget widget : getChildren()) { 62 | if (skipDisabledChild && !widget.isEnabled()) continue; 63 | int y = widget.getPos().y; 64 | if (caa == CrossAxisAlignment.CENTER) { 65 | y = (int) (maxHeight / 2f - widget.getSize().height / 2f); 66 | } else if (caa == CrossAxisAlignment.END) { 67 | y = maxHeight - widget.getSize().height; 68 | } 69 | widget.setPosSilent(new Pos2d(lastX, y)); 70 | lastX += widget.getSize().width; 71 | if (maa == MainAxisAlignment.SPACE_BETWEEN) { 72 | if (space != null) { 73 | lastX += space; 74 | } else { 75 | lastX += (maxWidth - totalWidth) / (getChildren().size() - 1); 76 | } 77 | } 78 | } 79 | } 80 | 81 | public Row setAlignment(MainAxisAlignment maa) { 82 | return setAlignment(maa, caa); 83 | } 84 | 85 | public Row setAlignment(CrossAxisAlignment caa) { 86 | return setAlignment(maa, caa); 87 | } 88 | 89 | public Row setAlignment(MainAxisAlignment maa, CrossAxisAlignment caa) { 90 | this.maa = maa; 91 | this.caa = caa; 92 | return this; 93 | } 94 | 95 | public Row setMaxWidth(int maxWidth) { 96 | this.maxWidth = maxWidth; 97 | return this; 98 | } 99 | 100 | /** 101 | * Specifies space between children. Use together with {@link MainAxisAlignment#SPACE_BETWEEN}. If you don't call 102 | * this, this widget will automatically calculate space based on {@link #maxHeight}. 103 | */ 104 | public Row setSpace(int space) { 105 | this.space = space; 106 | return this; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/Column.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import com.gtnewhorizons.modularui.api.math.CrossAxisAlignment; 6 | import com.gtnewhorizons.modularui.api.math.MainAxisAlignment; 7 | import com.gtnewhorizons.modularui.api.math.Pos2d; 8 | import com.gtnewhorizons.modularui.api.math.Size; 9 | import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder; 10 | import com.gtnewhorizons.modularui.api.widget.IWidgetParent; 11 | import com.gtnewhorizons.modularui.api.widget.Widget; 12 | 13 | public class Column extends MultiChildWidget implements IWidgetBuilder { 14 | 15 | private MainAxisAlignment maa = MainAxisAlignment.START; 16 | private CrossAxisAlignment caa = CrossAxisAlignment.START; 17 | private int maxHeight = -1, maxWidth = 0; 18 | private Integer space; 19 | 20 | protected boolean skipDisabledChild = false; 21 | 22 | @Override 23 | public void addWidgetInternal(Widget widget) { 24 | addChild(widget); 25 | } 26 | 27 | @Override 28 | protected @NotNull Size determineSize(int maxWidth, int maxHeight) { 29 | if (maa == MainAxisAlignment.START) { 30 | return IWidgetParent.getSizeOf(children); 31 | } 32 | return new Size(this.maxWidth, this.maxHeight); 33 | } 34 | 35 | @Override 36 | public void layoutChildren(int maxWidthC, int maxHeightC) { 37 | if (maxHeight < 0 && maa != MainAxisAlignment.START) { 38 | if (isAutoSized()) { 39 | maxHeight = maxHeightC - getPos().x; 40 | } else { 41 | maxHeight = getSize().height; 42 | } 43 | } 44 | 45 | this.maxWidth = 0; 46 | int totalHeight = 0; 47 | 48 | for (Widget widget : getChildren()) { 49 | if (skipDisabledChild && !widget.isEnabled()) continue; 50 | totalHeight += widget.getSize().height; 51 | maxWidth = Math.max(maxWidth, widget.getSize().width); 52 | } 53 | 54 | int lastY = 0; 55 | if (maa == MainAxisAlignment.CENTER) { 56 | lastY = (int) (maxHeight / 2f - totalHeight / 2f); 57 | } else if (maa == MainAxisAlignment.END) { 58 | lastY = maxHeight - totalHeight; 59 | } 60 | 61 | for (Widget widget : getChildren()) { 62 | if (skipDisabledChild && !widget.isEnabled()) continue; 63 | int x = widget.getPos().x; 64 | if (caa == CrossAxisAlignment.CENTER) { 65 | x = (int) (maxWidth / 2f - widget.getSize().width / 2f); 66 | } else if (caa == CrossAxisAlignment.END) { 67 | x = maxWidth - widget.getSize().width; 68 | } 69 | widget.setPosSilent(new Pos2d(x, lastY)); 70 | lastY += widget.getSize().height; 71 | if (maa == MainAxisAlignment.SPACE_BETWEEN) { 72 | if (space != null) { 73 | lastY += space; 74 | } else { 75 | lastY += (maxHeight - totalHeight) / (getChildren().size() - 1); 76 | } 77 | } 78 | } 79 | } 80 | 81 | public Column setAlignment(MainAxisAlignment maa) { 82 | return setAlignment(maa, caa); 83 | } 84 | 85 | public Column setAlignment(CrossAxisAlignment caa) { 86 | return setAlignment(maa, caa); 87 | } 88 | 89 | public Column setAlignment(MainAxisAlignment maa, CrossAxisAlignment caa) { 90 | this.maa = maa; 91 | this.caa = caa; 92 | return this; 93 | } 94 | 95 | public Column setMaxHeight(int maxHeight) { 96 | this.maxHeight = maxHeight; 97 | return this; 98 | } 99 | 100 | /** 101 | * Specifies space between children. Use together with {@link MainAxisAlignment#SPACE_BETWEEN}. If you don't call 102 | * this, this widget will automatically calculate space based on {@link #maxHeight}. 103 | */ 104 | public Column setSpace(int space) { 105 | this.space = space; 106 | return this; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/resources/assets/modularui/lang/en_US.lang: -------------------------------------------------------------------------------- 1 | modularui.test=Test Text\n2. Line 2 | modularui.tooltip.shift=§bHold Shift for more info 3 | modularui.increment.tooltip=§7Click to increase the number by §6%d§7\nHold shift[§6%d§7], ctrl[§6%d§7] or shift+ctrl[§6%d§7] 4 | modularui.decrement.tooltip=§7Click to decrease the number by §6%d§7\nHold shift[§6%d§7], ctrl[§6%d§7] or shift+ctrl[§6%d§7] 5 | modularui.amount=§9Amount: %,d 6 | modularui.fluid.empty=Empty 7 | modularui.fluid.none=None 8 | modularui.fluid.amount=§9Amount: %,d/%,d L 9 | modularui.fluid.capacity=§9Capacity: %,d L 10 | modularui.fluid.phantom.amount=§9Amount: %,d L 11 | modularui.fluid.phantom.control=§7Scroll wheel up increases amount, down decreases.\nShift[§6x10§7],Ctrl[§ex100§7],Shift+Ctrl[§ax1000§7]\nRight click increases amount, left click decreases.\nShift + left click to clear. 12 | modularui.item.phantom.control=§7Scroll wheel up increases amount, down decreases.\n§7Shift[§6x8§7],Ctrl[§ex64§7],Shift+Ctrl[§ax512§7]\n§7Right click increases amount, left click decreases.\n§7Shift + left click to clear. 13 | modularui.fluid.registry=§7Registry: %s 14 | modularui.fluid.unique_registry=§7Unique registry key: %s 15 | modularui.fluid.temperature=§cTemperature: %,d K 16 | modularui.fluid.state=§aState: %s 17 | modularui.fluid.gas=Gas 18 | modularui.fluid.liquid=Liquid 19 | modularui.phantom.single.clear=§8left click to remove 20 | modularui.dropdown.select=Select... 21 | 22 | modularui.fluid.click_to_fill=§7Left Click with a Fluid Container to fill the tank.\nRight Click to process 1 item per click. 23 | modularui.fluid.click_combined=§7Left Click with a Fluid Container to fill/empty the tank.\nRight Click to process 1 item per click. 24 | modularui.fluid.click_to_empty=§7Left Click with a Fluid Container to empty the tank.\nRight Click to process 1 item per click. 25 | 26 | modularui.config.animations=Animations 27 | modularui.config.rendering=Rendering 28 | modularui.config.keyboard=Keyboard 29 | modularui.config.localization=Localization 30 | modularui.config.json=Json 31 | modularui.config.debug=Debug 32 | 33 | modularui.config.animations.openCloseDurationMs=Duration of animations 34 | modularui.config.animations.openCloseDurationMs.tooltip=How many milliseconds will it take to draw open/close animation 35 | modularui.config.animations.openCloseFade=Enable GUI fade in/out 36 | modularui.config.animations.openCloseFade.tooltip=Whether to draw fade in/out animation on GUI open/close 37 | modularui.config.animations.openCloseScale=Enable GUI scale in/out 38 | modularui.config.animations.openCloseScale.tooltip=Whether to draw scale in/out animation on GUI open/close 39 | modularui.config.animations.openCloseTranslateFromBottom=Enable GUI slide 40 | modularui.config.animations.openCloseTranslateFromBottom.tooltip=Whether to draw GUI coming out of / going out to the bottom of the screen on GUI open/close 41 | modularui.config.animations.openCloseRotateFast=Enable GUI rotation 42 | modularui.config.animations.openCloseRotateFast.tooltip=Whether to draw GUI rotating fast on GUI open/close 43 | modularui.config.rendering.smoothProgressbar=Enable smooth progress bar 44 | modularui.config.rendering.smoothProgressbar.tooltip=Draw progress bar smoothly 45 | modularui.config.rendering.textCursor=Cursor style 46 | modularui.config.rendering.textCursor.tooltip=Select: underscore, vertical 47 | modularui.config.keyboard.escRestoreLastText=Restore last text with esc key 48 | modularui.config.keyboard.escRestoreLastText.tooltip=Whether to restore last text if esc key is pressed in the text field 49 | modularui.config.keyboard.closeWindowsAtOnce=Close windows at once 50 | modularui.config.keyboard.closeWindowsAtOnce.tooltip=Whether to close all the opened windows at once 51 | modularui.config.localization.locale=Locale 52 | modularui.config.localization.locale.tooltip=Locale to use to display GUI elements. Primarily used to display numbers in your regional format. 53 | modularui.config.json.useJson=Use Json 54 | modularui.config.json.useJson.tooltip=Whether to enable Json. Enabling this will increase loading time. 55 | modularui.config.debug.debug=Enable debug 56 | modularui.config.debug.debug.tooltip=Enable Debug information display 57 | modularui.config.debug.forceEnableDebugBlock=Enable debug block 58 | modularui.config.debug.forceEnableDebugBlock.tooltip=Add debug block even in non-dev env 59 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/math/Size.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.math; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Rectangle; 5 | import java.util.Objects; 6 | 7 | import com.google.gson.JsonElement; 8 | import com.google.gson.JsonObject; 9 | import com.gtnewhorizons.modularui.ModularUI; 10 | import com.gtnewhorizons.modularui.common.internal.JsonHelper; 11 | 12 | public class Size { 13 | 14 | public static final Size ZERO = zero(); 15 | 16 | public static Size zero() { 17 | return new Size(0, 0); 18 | } 19 | 20 | public final int width, height; 21 | 22 | public Size(int width, int height) { 23 | this.width = Math.max(0, width); 24 | this.height = Math.max(0, height); 25 | } 26 | 27 | public Size(double width, double height) { 28 | this((int) width, (int) height); 29 | } 30 | 31 | public Size(Size widthComponent, Size heightComponent) { 32 | this(widthComponent.width, heightComponent.height); 33 | } 34 | 35 | public Size(int width, Size heightComponent) { 36 | this(width, heightComponent.height); 37 | } 38 | 39 | public Size(Size widthComponent, int height) { 40 | this(widthComponent.width, height); 41 | } 42 | 43 | public static Size ofDimension(Dimension dimension) { 44 | return new Size(dimension.width, dimension.height); 45 | } 46 | 47 | public boolean isLargerThan(Size size) { 48 | return (size.width * size.height) < (width * height); 49 | } 50 | 51 | public boolean hasLargerDimensionsThan(Size size) { 52 | return width > size.width && height > size.height; 53 | } 54 | 55 | public Size shrink(int width, int height) { 56 | return new Size(this.width - width, this.height - height); 57 | } 58 | 59 | public Size grow(int width, int height) { 60 | return new Size(this.width + width, this.height + height); 61 | } 62 | 63 | public Size scale(float widthScale, float heightScale) { 64 | return new Size(this.width * widthScale, this.height * heightScale); 65 | } 66 | 67 | public Size scale(float scale) { 68 | return scale(scale, scale); 69 | } 70 | 71 | public boolean isZero() { 72 | return width == 0 && height == 0; 73 | } 74 | 75 | @Override 76 | public boolean equals(Object o) { 77 | if (this == o) return true; 78 | if (o == null || getClass() != o.getClass()) return false; 79 | Size size = (Size) o; 80 | return Float.compare(size.width, width) == 0 && Float.compare(size.height, height) == 0; 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | return Objects.hash(width, height); 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return "[" + width + ", " + height + "]"; 91 | } 92 | 93 | public Dimension asDimension() { 94 | return new Dimension(width, height); 95 | } 96 | 97 | public Rectangle asRectangle() { 98 | return new Rectangle(width, height); 99 | } 100 | 101 | public static Size ofJson(JsonElement jsonElement) { 102 | int width = 0, height = 0; 103 | if (jsonElement.isJsonObject()) { 104 | JsonObject json = jsonElement.getAsJsonObject(); 105 | width = JsonHelper.getInt(json, 0, "width", "w"); 106 | height = JsonHelper.getInt(json, 0, "height", "h"); 107 | } else { 108 | String raw = jsonElement.getAsString(); 109 | if (raw.contains(",")) { 110 | String[] parts = raw.split(","); 111 | try { 112 | if (!parts[0].isEmpty()) { 113 | width = Integer.parseInt(parts[0]); 114 | } 115 | if (parts.length > 1 && !parts[1].isEmpty()) { 116 | height = Integer.parseInt(parts[1]); 117 | } 118 | } catch (NumberFormatException e) { 119 | ModularUI.logger.error("Error parsing JSON pos: {}", raw); 120 | e.printStackTrace(); 121 | } 122 | } 123 | } 124 | return new Size(width, height); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/UIInfos.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api; 2 | 3 | import java.util.function.Function; 4 | 5 | import net.minecraft.client.gui.GuiScreen; 6 | import net.minecraft.entity.player.EntityPlayer; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.tileentity.TileEntity; 9 | 10 | import com.gtnewhorizons.modularui.ModularUI; 11 | import com.gtnewhorizons.modularui.api.screen.IItemWithModularUI; 12 | import com.gtnewhorizons.modularui.api.screen.ITileWithModularUI; 13 | import com.gtnewhorizons.modularui.api.screen.ModularUIContext; 14 | import com.gtnewhorizons.modularui.api.screen.ModularWindow; 15 | import com.gtnewhorizons.modularui.api.screen.UIBuildContext; 16 | import com.gtnewhorizons.modularui.common.builder.UIBuilder; 17 | import com.gtnewhorizons.modularui.common.builder.UIInfo; 18 | import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; 19 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 20 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; 21 | 22 | import cpw.mods.fml.common.FMLCommonHandler; 23 | import cpw.mods.fml.relauncher.Side; 24 | import cpw.mods.fml.relauncher.SideOnly; 25 | 26 | public class UIInfos { 27 | 28 | public static void init() {} 29 | 30 | public static final UIInfo TILE_MODULAR_UI = UIBuilder.of().gui(((player, world, x, y, z) -> { 31 | if (!world.isRemote) return null; 32 | TileEntity te = world.getTileEntity(x, y, z); 33 | if (te instanceof ITileWithModularUI) { 34 | return ModularUI.createGuiScreen(player, ((ITileWithModularUI) te)::createWindow); 35 | } 36 | return null; 37 | })).container((player, world, x, y, z) -> { 38 | TileEntity te = world.getTileEntity(x, y, z); 39 | if (te instanceof ITileWithModularUI) { 40 | return ModularUI.createContainer(player, ((ITileWithModularUI) te)::createWindow, te::markDirty); 41 | } 42 | return null; 43 | }).build(); 44 | 45 | public static final UIInfo PLAYER_HELD_ITEM_UI = UIBuilder.of().container((player, world, x, y, z) -> { 46 | ItemStack heldItem = player.getHeldItem(); 47 | if (heldItem.getItem() instanceof IItemWithModularUI) { 48 | UIBuildContext buildContext = new UIBuildContext(player); 49 | ModularWindow window = ((IItemWithModularUI) heldItem.getItem()).createWindow(buildContext, heldItem); 50 | return new ModularUIContainer( 51 | new ModularUIContext(buildContext, () -> player.inventoryContainer.detectAndSendChanges()), 52 | window, 53 | player.inventory.currentItem); 54 | } 55 | return null; 56 | }).gui((player, world, x, y, z) -> { 57 | ItemStack heldItem = player.getHeldItem(); 58 | if (heldItem.getItem() instanceof IItemWithModularUI) { 59 | UIBuildContext buildContext = new UIBuildContext(player); 60 | ModularWindow window = ((IItemWithModularUI) heldItem.getItem()).createWindow(buildContext, heldItem); 61 | return new ModularGui( 62 | new ModularUIContainer( 63 | new ModularUIContext(buildContext, null), 64 | window, 65 | player.inventory.currentItem)); 66 | } 67 | return null; 68 | }).build(); 69 | 70 | @SideOnly(Side.CLIENT) 71 | public static void openClientUI(EntityPlayer player, Function uiCreator) { 72 | if (!NetworkUtils.isClient()) { 73 | ModularUI.logger.info("Tried opening client ui on server!"); 74 | return; 75 | } 76 | UIBuildContext buildContext = new UIBuildContext(player); 77 | ModularWindow window = uiCreator.apply(buildContext); 78 | GuiScreen screen = new ModularGui( 79 | new ModularUIContainer(new ModularUIContext(buildContext, null, true), window)); 80 | FMLCommonHandler.instance().showGuiScreen(screen); 81 | } 82 | 83 | /** 84 | * Call this if you want to draw widgets in other mods' GUI. Don't call if you're using {@link UIInfo#open} or 85 | * {@link #openClientUI}. 86 | */ 87 | public static void initializeWindow(EntityPlayer player, ModularWindow window) { 88 | UIBuildContext buildContext = new UIBuildContext(player); 89 | new ModularGui(new ModularUIContainer(new ModularUIContext(buildContext, null, true), window)); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/ModularUI.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui; 2 | 3 | import java.util.function.Function; 4 | 5 | import net.minecraft.entity.player.EntityPlayer; 6 | import net.minecraft.launchwrapper.Launch; 7 | 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | import com.gtnewhorizons.modularui.api.UIInfos; 12 | import com.gtnewhorizons.modularui.api.screen.ModularUIContext; 13 | import com.gtnewhorizons.modularui.api.screen.ModularWindow; 14 | import com.gtnewhorizons.modularui.api.screen.UIBuildContext; 15 | import com.gtnewhorizons.modularui.common.internal.JsonLoader; 16 | import com.gtnewhorizons.modularui.common.internal.network.NetworkHandler; 17 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; 18 | import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; 19 | import com.gtnewhorizons.modularui.common.widget.WidgetJsonRegistry; 20 | 21 | import cpw.mods.fml.common.FMLCommonHandler; 22 | import cpw.mods.fml.common.Loader; 23 | import cpw.mods.fml.common.Mod; 24 | import cpw.mods.fml.common.SidedProxy; 25 | import cpw.mods.fml.common.event.FMLInitializationEvent; 26 | import cpw.mods.fml.common.event.FMLPostInitializationEvent; 27 | import cpw.mods.fml.common.event.FMLPreInitializationEvent; 28 | import cpw.mods.fml.relauncher.Side; 29 | import cpw.mods.fml.relauncher.SideOnly; 30 | 31 | @Mod( 32 | modid = ModularUI.MODID, 33 | version = Tags.VERSION, 34 | name = "ModularUI", 35 | acceptedMinecraftVersions = "[1.7.10]", 36 | dependencies = ModularUI.DEPENDENCIES, 37 | guiFactory = ModularUI.GUI_FACTORY) 38 | public class ModularUI { 39 | 40 | public static final String MODID = "modularui"; 41 | public static final String DEPENDENCIES = "required-after:gtnhmixins@[2.0.1,); " 42 | + "required-after:NotEnoughItems@[2.3.50-GTNH,);" 43 | + "after:hodgepodge@[2.0.0,);" 44 | + "after:gtnhlib@[0.2.7,);" 45 | + "before:gregtech"; 46 | public static final String GUI_FACTORY = "com.gtnewhorizons.modularui.config.GuiFactory"; 47 | 48 | public static final Logger logger = LogManager.getLogger(MODID); 49 | 50 | public static final String MODID_GT5U = "gregtech"; 51 | public static final String MODID_GT6 = "gregapi_post"; 52 | public static final boolean isGT5ULoaded = Loader.isModLoaded(MODID_GT5U) && !Loader.isModLoaded(MODID_GT6); 53 | public static final boolean isHodgepodgeLoaded = Loader.isModLoaded("hodgepodge"); 54 | public static final boolean isAE2Loaded = Loader.isModLoaded("appliedenergistics2"); 55 | public static final boolean isGTNHLibLoaded = Loader.isModLoaded("gtnhlib"); 56 | 57 | public static final boolean isDevEnv = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); 58 | 59 | @Mod.Instance(ModularUI.MODID) 60 | public static ModularUI INSTANCE; 61 | 62 | @SidedProxy( 63 | modId = MODID, 64 | clientSide = "com.gtnewhorizons.modularui.ClientProxy", 65 | serverSide = "com.gtnewhorizons.modularui.CommonProxy") 66 | public static CommonProxy proxy; 67 | 68 | @Mod.EventHandler 69 | public void preInit(FMLPreInitializationEvent event) { 70 | proxy.preInit(event); 71 | NetworkHandler.init(); 72 | UIInfos.init(); 73 | WidgetJsonRegistry.init(); 74 | } 75 | 76 | @Mod.EventHandler 77 | public void init(FMLInitializationEvent event) { 78 | if (FMLCommonHandler.instance().getSide() == Side.SERVER) { 79 | JsonLoader.loadJson(); 80 | } 81 | } 82 | 83 | @Mod.EventHandler 84 | public void onPostInit(FMLPostInitializationEvent event) { 85 | proxy.postInit(); 86 | } 87 | 88 | public static ModularUIContainer createContainer(EntityPlayer player, 89 | Function windowCreator, Runnable onWidgetUpdate) { 90 | UIBuildContext buildContext = new UIBuildContext(player); 91 | ModularWindow window = windowCreator.apply(buildContext); 92 | return new ModularUIContainer(new ModularUIContext(buildContext, onWidgetUpdate), window); 93 | } 94 | 95 | @SideOnly(Side.CLIENT) 96 | public static ModularGui createGuiScreen(EntityPlayer player, 97 | Function windowCreator) { 98 | return new ModularGui(createContainer(player, windowCreator, null)); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/fluid/FluidStackTank.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.fluid; 2 | 3 | import java.util.function.Consumer; 4 | import java.util.function.IntSupplier; 5 | import java.util.function.Supplier; 6 | 7 | import net.minecraftforge.fluids.FluidStack; 8 | import net.minecraftforge.fluids.FluidTankInfo; 9 | import net.minecraftforge.fluids.IFluidTank; 10 | 11 | @SuppressWarnings("unused") 12 | public class FluidStackTank implements IFluidTank, IOverflowableTank { 13 | 14 | private final Supplier getter; 15 | private final Consumer setter; 16 | private final IntSupplier capacityGetter; 17 | private boolean allowOverflow; 18 | private boolean preventDraining; 19 | 20 | public FluidStackTank(Supplier getter, Consumer setter, int capacity) { 21 | this(getter, setter, () -> capacity); 22 | } 23 | 24 | public FluidStackTank(Supplier getter, Consumer setter, IntSupplier capacityGetter) { 25 | this.getter = getter; 26 | this.setter = setter; 27 | this.capacityGetter = capacityGetter; 28 | } 29 | 30 | public void setAllowOverflow(boolean allowOverflow) { 31 | this.allowOverflow = allowOverflow; 32 | } 33 | 34 | public void setPreventDraining(boolean preventDraining) { 35 | this.preventDraining = preventDraining; 36 | } 37 | 38 | @Override 39 | public FluidStack getFluid() { 40 | return getter.get(); 41 | } 42 | 43 | @Override 44 | public int getFluidAmount() { 45 | FluidStack fluidStack = getter.get(); 46 | return fluidStack != null ? fluidStack.amount : 0; 47 | } 48 | 49 | @Override 50 | public int getCapacity() { 51 | return allowOverflow ? Integer.MAX_VALUE : getRealCapacity(); 52 | } 53 | 54 | @Override 55 | public FluidTankInfo getInfo() { 56 | return new FluidTankInfo(this); 57 | } 58 | 59 | @Override 60 | public int fill(FluidStack resource, boolean doFill) { 61 | if (resource == null) { 62 | return 0; 63 | } 64 | FluidStack fluid = getter.get(); 65 | 66 | if (!doFill) { 67 | if (fluid == null) { 68 | return Math.min(getCapacity(), resource.amount); 69 | } 70 | 71 | if (!fluid.isFluidEqual(resource)) { 72 | return 0; 73 | } 74 | 75 | return Math.min(getCanFillAmount(), resource.amount); 76 | } 77 | 78 | if (fluid == null) { 79 | fluid = new FluidStack(resource, Math.min(getCapacity(), resource.amount)); 80 | setter.accept(fluid); 81 | int retAmount = fluid.amount; 82 | validateFluid(); 83 | return retAmount; 84 | } 85 | 86 | if (!fluid.isFluidEqual(resource)) { 87 | return 0; 88 | } 89 | 90 | int canFillAmount = getCanFillAmount(); 91 | 92 | if (resource.amount < canFillAmount) { 93 | fluid.amount += resource.amount; 94 | canFillAmount = resource.amount; 95 | } else { 96 | fluid.amount = getRealCapacity(); 97 | } 98 | validateFluid(); 99 | 100 | return canFillAmount; 101 | } 102 | 103 | @Override 104 | public FluidStack drain(int maxDrain, boolean doDrain) { 105 | FluidStack fluid = getter.get(); 106 | if (fluid == null) { 107 | return null; 108 | } 109 | 110 | int drained = maxDrain; 111 | if (fluid.amount < drained) { 112 | drained = fluid.amount; 113 | } 114 | 115 | FluidStack stack = new FluidStack(fluid, drained); 116 | if (doDrain) { 117 | fluid.amount -= drained; 118 | if (fluid.amount <= 0 && !preventDraining) { 119 | setter.accept(null); 120 | } 121 | 122 | } 123 | return stack; 124 | } 125 | 126 | @Override 127 | public int getRealCapacity() { 128 | return capacityGetter.getAsInt(); 129 | } 130 | 131 | public int getCanFillAmount() { 132 | FluidStack fluid = getter.get(); 133 | if (fluid == null) return 0; 134 | return allowOverflow ? Integer.MAX_VALUE : (getRealCapacity() - fluid.amount); 135 | } 136 | 137 | /** 138 | * Shrinks fluid amount if it exceeds real capacity 139 | */ 140 | public void validateFluid() { 141 | FluidStack fluid = getter.get(); 142 | if (fluid == null) return; 143 | fluid.amount = Math.min(fluid.amount, getRealCapacity()); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/api/forge/SlotItemHandler.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.api.forge; 2 | // 3 | // Source code recreated from a .class file by IntelliJ IDEA 4 | // (powered by FernFlower decompiler) 5 | // 6 | 7 | import javax.annotation.Nullable; 8 | 9 | import net.minecraft.entity.player.EntityPlayer; 10 | import net.minecraft.inventory.IInventory; 11 | import net.minecraft.inventory.InventoryBasic; 12 | import net.minecraft.inventory.Slot; 13 | import net.minecraft.item.ItemStack; 14 | 15 | public class SlotItemHandler extends Slot { 16 | 17 | private static IInventory emptyInventory = new InventoryBasic("[Null]", true, 0); 18 | private final IItemHandler itemHandler; 19 | private final int index; 20 | 21 | public SlotItemHandler(IItemHandler itemHandler, int index, int xPosition, int yPosition) { 22 | super( 23 | itemHandler.getSourceInventory() == null ? emptyInventory : itemHandler.getSourceInventory(), 24 | index, 25 | xPosition, 26 | yPosition); 27 | this.itemHandler = itemHandler; 28 | this.index = index; 29 | } 30 | 31 | @Override 32 | public boolean isItemValid(ItemStack stack) { 33 | if (stack != null && this.itemHandler.isItemValid(this.index, stack)) { 34 | IItemHandler handler = this.getItemHandler(); 35 | ItemStack remainder; 36 | if (handler instanceof IItemHandlerModifiable) { 37 | IItemHandlerModifiable handlerModifiable = (IItemHandlerModifiable) handler; 38 | ItemStack currentStack = handlerModifiable.getStackInSlot(this.index); 39 | handlerModifiable.setStackInSlot(this.index, null); 40 | remainder = handlerModifiable.insertItem(this.index, stack, true); 41 | handlerModifiable.setStackInSlot(this.index, currentStack); 42 | } else { 43 | remainder = handler.insertItem(this.index, stack, true); 44 | } 45 | 46 | return remainder != null ? remainder.stackSize < stack.stackSize : stack.stackSize > 0; 47 | } else { 48 | return false; 49 | } 50 | } 51 | 52 | @Override 53 | public ItemStack getStack() { 54 | return this.getItemHandler().getStackInSlot(this.index); 55 | } 56 | 57 | @Override 58 | public void putStack(ItemStack stack) { 59 | ((IItemHandlerModifiable) this.getItemHandler()).setStackInSlot(this.index, stack); 60 | this.onSlotChanged(); 61 | } 62 | 63 | @Override 64 | public void onSlotChange(ItemStack p_75220_1_, ItemStack p_75220_2_) {} 65 | 66 | @Override 67 | public int getSlotStackLimit() { 68 | return this.itemHandler.getSlotLimit(this.index); 69 | } 70 | 71 | public int getItemStackLimit(ItemStack stack) { 72 | ItemStack maxAdd = stack.copy(); 73 | int maxInput = stack.getMaxStackSize(); 74 | maxAdd.stackSize = maxInput; 75 | IItemHandler handler = this.getItemHandler(); 76 | ItemStack currentStack = handler.getStackInSlot(this.index); 77 | if (handler instanceof IItemHandlerModifiable) { 78 | IItemHandlerModifiable handlerModifiable = (IItemHandlerModifiable) handler; 79 | handlerModifiable.setStackInSlot(this.index, null); 80 | ItemStack remainder = handlerModifiable.insertItem(this.index, maxAdd, true); 81 | handlerModifiable.setStackInSlot(this.index, currentStack); 82 | return remainder != null ? maxInput - remainder.stackSize : maxInput; 83 | } else { 84 | ItemStack remainder = handler.insertItem(this.index, maxAdd, true); 85 | int current = currentStack.stackSize; 86 | int added = remainder != null ? maxInput - remainder.stackSize : maxInput; 87 | return current + added; 88 | } 89 | } 90 | 91 | @Override 92 | public boolean canTakeStack(EntityPlayer playerIn) { 93 | // make a best effort guess at checking whether this handler allows extraction 94 | return this.getItemHandler().getStackInSlot(this.index) == null 95 | || this.getItemHandler().extractItem(this.index, 1, true) != null; 96 | } 97 | 98 | @Override 99 | @Nullable 100 | public ItemStack decrStackSize(int amount) { 101 | return this.getItemHandler().extractItem(this.index, amount, false); 102 | } 103 | 104 | public IItemHandler getItemHandler() { 105 | return this.itemHandler; 106 | } 107 | 108 | public boolean isSameInventory(Slot other) { 109 | return other instanceof SlotItemHandler && ((SlotItemHandler) other).getItemHandler() == this.itemHandler; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/gtnewhorizons/modularui/common/widget/ButtonWidget.java: -------------------------------------------------------------------------------- 1 | package com.gtnewhorizons.modularui.common.widget; 2 | 3 | import java.util.function.BiConsumer; 4 | import java.util.function.Supplier; 5 | 6 | import net.minecraft.network.PacketBuffer; 7 | import net.minecraft.util.ResourceLocation; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import com.gtnewhorizons.modularui.api.ModularUITextures; 13 | import com.gtnewhorizons.modularui.api.drawable.IDrawable; 14 | import com.gtnewhorizons.modularui.api.drawable.Text; 15 | import com.gtnewhorizons.modularui.api.math.Size; 16 | import com.gtnewhorizons.modularui.api.widget.Interactable; 17 | import com.gtnewhorizons.modularui.api.widget.Widget; 18 | import com.gtnewhorizons.modularui.common.internal.Theme; 19 | 20 | /** 21 | * Clickable button widget. 22 | */ 23 | public class ButtonWidget extends SyncedWidget implements Interactable { 24 | 25 | private IDrawable[] hoveredBackground; 26 | 27 | public static ButtonWidget openSyncedWindowButton(int id) { 28 | return (ButtonWidget) new ButtonWidget() 29 | .setOnClick( 30 | (clickData, widget) -> { if (!widget.isClient()) widget.getContext().openSyncedWindow(id); }) 31 | .setBackground(ModularUITextures.VANILLA_BACKGROUND, new Text("Window")); 32 | } 33 | 34 | public static ButtonWidget closeWindowButton(boolean syncedWindow) { 35 | return (ButtonWidget) new ButtonWidget().setOnClick((clickData, widget) -> { 36 | if (!syncedWindow || !widget.isClient()) { 37 | widget.getWindow().closeWindow(); 38 | } 39 | }).setBackground(ModularUITextures.VANILLA_BACKGROUND, new Text("x")).setSize(12, 12); 40 | } 41 | 42 | private BiConsumer clickAction; 43 | private boolean playClickSound = true; 44 | private Supplier playClickSoundResource; 45 | 46 | /** 47 | * Set callback that will be invoked when button is clicked. 48 | */ 49 | public ButtonWidget setOnClick(BiConsumer clickAction) { 50 | this.clickAction = clickAction; 51 | return this; 52 | } 53 | 54 | public ButtonWidget setPlayClickSound(boolean playClickSound) { 55 | this.playClickSound = playClickSound; 56 | return this; 57 | } 58 | 59 | public ButtonWidget setPlayClickSoundResource(ResourceLocation sound) { 60 | return setPlayClickSoundResource(() -> sound); 61 | } 62 | 63 | public ButtonWidget setPlayClickSoundResource(Supplier soundSupplier) { 64 | this.playClickSoundResource = soundSupplier; 65 | return setPlayClickSound(true); 66 | } 67 | 68 | public ButtonWidget setHoveredBackground(IDrawable... hoveredBackground) { 69 | this.hoveredBackground = hoveredBackground; 70 | return this; 71 | } 72 | 73 | @Override 74 | protected @NotNull Size determineSize(int maxWidth, int maxHeight) { 75 | return new Size(20, 20); 76 | } 77 | 78 | @Override 79 | public @Nullable IDrawable[] getBackground() { 80 | if (hoveredBackground != null && isHovering()) { 81 | return hoveredBackground; 82 | } 83 | return super.getBackground(); 84 | } 85 | 86 | @Override 87 | public @Nullable String getBackgroundColorKey() { 88 | return Theme.KEY_BUTTON; 89 | } 90 | 91 | @Override 92 | public ClickResult onClick(int buttonId, boolean doubleClick) { 93 | if (clickAction != null) { 94 | Widget.ClickData clickData = Widget.ClickData.create(buttonId, doubleClick); 95 | clickAction.accept(clickData, this); 96 | if (syncsToServer()) { 97 | syncToServer(1, clickData::writeToPacket); 98 | } 99 | if (playClickSound) { 100 | if (playClickSoundResource != null && playClickSoundResource.get() != null) { 101 | Interactable.playButtonClickSound(playClickSoundResource.get()); 102 | } else { 103 | Interactable.playButtonClickSound(); 104 | } 105 | } 106 | return ClickResult.ACCEPT; 107 | } 108 | return ClickResult.ACKNOWLEDGED; 109 | } 110 | 111 | @Override 112 | public void readOnClient(int id, PacketBuffer buf) {} 113 | 114 | @Override 115 | public void readOnServer(int id, PacketBuffer buf) { 116 | if (id == 1) { 117 | Widget.ClickData data = Widget.ClickData.readPacket(buf); 118 | clickAction.accept(data, this); 119 | markForUpdate(); 120 | } 121 | } 122 | } 123 | --------------------------------------------------------------------------------