{"id":13407824,"url":"https://github.com/IPVP-MC/canvas","last_synced_at":"2025-03-14T12:31:40.617Z","repository":{"id":20857716,"uuid":"91110446","full_name":"IPVP-MC/canvas","owner":"IPVP-MC","description":"Canvas is a java library built for Bukkit to manage custom inventory based menus","archived":false,"fork":false,"pushed_at":"2024-02-26T21:40:49.000Z","size":143,"stargazers_count":145,"open_issues_count":1,"forks_count":21,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-07-31T20:28:24.104Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IPVP-MC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-12T16:31:35.000Z","updated_at":"2024-07-27T20:25:49.000Z","dependencies_parsed_at":"2024-02-26T22:46:31.506Z","dependency_job_id":"9df89e01-f5fe-4b7e-94c2-69fbca62e744","html_url":"https://github.com/IPVP-MC/canvas","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IPVP-MC%2Fcanvas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IPVP-MC%2Fcanvas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IPVP-MC%2Fcanvas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IPVP-MC%2Fcanvas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IPVP-MC","download_url":"https://codeload.github.com/IPVP-MC/canvas/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243578102,"owners_count":20313758,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:00:48.864Z","updated_at":"2025-03-14T12:31:38.902Z","avatar_url":"https://github.com/IPVP-MC.png","language":"Java","funding_links":[],"categories":["Inventories"],"sub_categories":[],"readme":"# canvas [![Build Status](https://travis-ci.org/IPVP-MC/canvas.svg?branch=master)](https://travis-ci.org/IPVP-MC/canvas)\n\nA highly advanced and effective inventory management library for Bukkit plugins. The primary goal of canvas is to enable creation of elegant inventory systems without the quirks of existing libraries.\n\n## Feature Overview\n* [Menus](#menus) - the basics of GUI creation\n    * [Close Handlers](#close-handlers) - handling close behavior\n    * [Redrawing](#redrawing) - preventing cursor position resets\n    * [Pagination](#pagination) - menu pages made easy\n* [Slots](#slots) - controlling what GUI slots do\n* [Templates](#templates) - rendering non-static items on a per-player basis\n* [Masks](#masks) - inventory slot IDs made easy!\n    * [Recipe Masks](#recipe-masks) - multiple item masks\n\n## Using canvas\n\ncanvas is integrated into plugins through the use of Maven.\n\n#### Requirements\n* [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html)\n* [Maven 3](http://maven.apache.org/download.html)\n* [Git](https://git-scm.com/downloads)\n* Spigot 1.8.8 or newer\n\nThen use the following command to install canvas to your local maven repository\n```\ngit clone https://github.com/IPVP-MC/canvas.git\ncd canvas/\nmvn clean install\n```\n\nYou will now be able to add canvas as a dependency in your pom.xml files with the following\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.ipvp\u003c/groupId\u003e\n    \u003cartifactId\u003ecanvas\u003c/artifactId\u003e\n    \u003cversion\u003e1.7.0-SNAPSHOT\u003c/version\u003e\n    \u003cscope\u003ecompile\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n**Note**: You will need to use the Maven shade plugin in order to package your final `.jar` file. Add the following to your maven plugins section:\n```\n\u003cplugin\u003e\n    \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n    \u003cartifactId\u003emaven-shade-plugin\u003c/artifactId\u003e\n    \u003cversion\u003e3.2.4\u003c/version\u003e\n    \u003cexecutions\u003e\n        \u003cexecution\u003e\n            \u003cphase\u003epackage\u003c/phase\u003e\n            \u003cgoals\u003e\n                \u003cgoal\u003eshade\u003c/goal\u003e\n            \u003c/goals\u003e\n        \u003c/execution\u003e\n    \u003c/executions\u003e\n\u003c/plugin\u003e   \n```\nsee [here](https://maven.apache.org/plugins/maven-shade-plugin/) for additional documentation on the shade plugin.\n\nOnce the dependency is registered, the only thing left to do is to register the [MenuFunctionListener](src/main/java/org/ipvp/canvas/MenuFunctionListener.java) with the Bukkit event dispatcher.\n\n```java\nBukkit.getPluginManager().registerEvents(new MenuFunctionListener(), plugin);\n```\n\n## Features\n\n### Menus\nOut of the box, canvas supports the following inventory types as menus:\n* [Chest menus](src/main/java/org/ipvp/canvas/type/ChestMenu.java)\n* [Hopper menus](src/main/java/org/ipvp/canvas/type/HopperMenu.java)\n* [Box menus](src/main/java/org/ipvp/canvas/type/BoxMenu.java) (ie. 3x3 inventories such as workbench, dispenser, dropper)\n  * Note: Due to an error in internal Minecraft code, shift clicking is disabled in Hopper and Box menus.\n\nThe above menus can be created by using the Builder pattern available in their respective classes. Creating a standard [ChestMenu](src/main/java/org/ipvp/canvas/type/ChestMenu.java) with 4 rows would look as such:\n\n```java\npublic Menu createMenu() {\n    return ChestMenu.builder(4)\n            .title(\"Menu\")\n            .build();\n}\n```\n\nDisplaying the menu to a player is made simple with `Menu#open(Player)`\n```java\npublic void displayMenu(Player player) {\n    Menu menu = createMenu();\n    menu.open(player);\n}\n```\n\nSimple yet effective, our result looks like this:\n\n![](http://i.imgur.com/LXnCkLv.png)\n\n#### Close handlers\nFunctionality when a Menu is closed can be added to Menus through the Menu.CloseHandler interface. The interface is meant to be used as a functional interface, and functionality is added elegantly with Java 8 lambda expressions.\n\nLet's say we want to send the player some messages when they leave the inventory:\n```java\npublic void addCloseHandler(Menu menu) {\n    menu.setCloseHandler((player, menu1) -\u003e {\n            player.sendMessage(\"You just closed the menu...\");\n            player.sendMessage(\"See you next time!\");\n    });\n}\n```\n\n#### Redrawing\nWhen switching between different Menus for players, by default the cursor will reset to the middle of the screen. \nThis behavior can be changed to preserve cursor location at the cost of not being able to update Menu titles by enabling\nthe `redraw` property of a Menu. When building a Menu via `MenuBuilder`, passing a value of `true` to `MenuBuilder#redraw(boolean)` \nenabled this functionality.\n\n**Note**: If switching to a menu that has different dimensions, the `redraw` flag will be ignored and a new Inventory will\nbe opened for the player, resetting their cursor.\n\n#### Pagination\nCreating connected pages of Menus to display a catalog of items is made easy with the \n[PaginatedMenuBuilder](src/main/java/org/ipvp/canvas/paginate/PaginatedMenuBuilder.java) class. The utility is able to be\nconfigured to set the proper previous and next page icons, and any necessary functions for items that are added.\n\nIn the basic example below, we create a simple menu displaying various static items.\n```java\nMenu.Builder pageTemplate = ChestMenu.builder(3).title(\"Items\").redraw(true);\nMask itemSlots = BinaryMask.builder(pageTemplate.getDimensions())\n        .pattern(\"011111110\").build();\nList\u003cMenu\u003e pages = PaginatedMenuBuilder.builder(pageTemplate)\n        .slots(itemSlots)\n        .nextButton(new ItemStack(Material.ARROW))\n        .nextButtonEmpty(new ItemStack(Material.ARROW)) // Icon when no next page available\n        .nextButtonSlot(23)\n        .previousButton(new ItemStack(Material.ARROW))\n        .previousButtonEmpty(new ItemStack(Material.ARROW)) // Icon when no previous page available\n        .previousButtonSlot(21)\n        .addItem(new ItemStack(Material.DIRT))\n        .addItem(new ItemStack(Material.GRASS))\n        .addItem(new ItemStack(Material.COBBLESTONE))\n        .addItem(new ItemStack(Material.STONE))\n        // ...\n        .build();\n```\nPer-player items and click handlers are supported for added items as well, via the `PaginatedMenuBuilder.addItem(ItemStackTemplate)`\nor `PaginatedMenuBuilder.addItem(SlotSettings)` methods.\n\nIf additional modifications need to be made to any newly created page that the builder doesn't support, adding functionality \nto modify a freshly created page is available by adding a `Consumer\u003cMenu\u003e` with the `PaginatedMenuBuilder.newMenuModifier(Consumer\u003cMenu\u003e)` method. \n\n### Slots\nA [Slot](src/main/java/org/ipvp/canvas/slot/Slot.java) is exactly what you'd expect it to be, however canvas allows \nincredible customization of what they can do. Menus grant access to their slots through the `Menu#getSlot(int)` method.\n\nThere are 3 major pieces to Slot functionality:\n* [ClickOptions](src/main/java/org/ipvp/canvas/slot/ClickOptions.java)\n* [ClickInformation](src/main/java/org/ipvp/canvas/ClickInformation.java)\n* [ClickHandler](src/main/java/org/ipvp/canvas/slot/Slot.java)\n\nAdditionally, Slots grant the ability to render non-static items within their parent Menu via \n[ItemStackTemplate](src/main/java/org/ipvp/canvas/template/ItemStackTemplate.java) (see below).\n\n#### ClickOptions\nClick options are the primary method of controlling what actions and click types can be performed on the raw item contents of the holding inventory. Two basic sets are provided with the library, which are `ClickOptions.ALLOW_ALL` and `ClickOptions.DENY_ALL`. By default, slots carry the DENY_ALL trait, denying all pickup and dropping off of items in the respective inventory. These behaviors are easily modified with the `Slot#setClickOptions(ClickOptions)` method.\n\nCreation of custom options is done through the `ClickOptions.Builder` class. In the following example, we show you how to only allow dropping off of items into a specific slot, but not picking it up.\n\n```java\npublic void addClickOptions(Slot slot) {\n    ClickOptions options = ClickOptions.builder()\n            .allow(ClickType.LEFT, ClickType.RIGHT)\n            .allow(InventoryAction.PLACE_ALL, InventoryAction.PLACE_ONE, InventoryAction.PLACE_SOME)\n            .build();\n    slot.setClickOptions(options);\n}\n```\n\n#### ClickInformation\n[ClickInformation](src/main/java/org/ipvp/canvas/ClickInformation.java) is a class constructed to provide the ClickHandler of a Slot with all available information about a click performed on the Slot. Also available is the possibility to change the resulting outcome of the click (whether interaction in the raw inventory occurs).\n\n#### ClickHandler\nClick handlers are where most of the logic of a slot will occur. As a slot is clicked, the click handler (if present) is triggered with information about who clicked as well as the click performed. The handler of a slot will always be triggered, regardless of whether or not the options of a slot forbid interaction with it. Keep in mind that the result of the click will be set by the options before the handler is triggered and as such the ClickInformation will represent this result.\n\nAdding a handler is made simple with `Slot#setClickHandler(ClickHandler)`:\n\n```java\npublic void addClickHandler(Slot slot) {\n    slot.setClickHandler((player, info) -\u003e {\n        player.sendMessage(\"You clicked the slot at index \" + info.getClickedSlot().getIndex());\n        // Additional functionality goes here\n    });\n}\n```\n\n### Templates\nItem templates are used to render non-static items on a per-player basis. In certain situations, users of canvas may\nrequire a Menu to be updated because state has changed. For example, if an icon in a Menu displays the level of a player\nand the player has levelled up then the icon must be redrawn to reflect the changes. Item templates allow this functionality\nto happen without requiring a completely new Menu for each player.\n\nItem templates are assigned to slots via the `Slot#setItemTemplate(ItemStackTemplate)` method. Alternatively,  \n`Slot#setItem(ItemStack)` can be used to set a static item that will render the same for every player.\n\nUsing the scenario above, where a player may be levelling up, code for an item may look something like the following:\n```java\nMenu menu = ChestMenu.builder(1).title(\"Level\").build();\nSlot slot = menu.getSlot(4);\nslot.setItemTemplate(p -\u003e {\n    int level = p.getLevel();\n    ItemStack item = new ItemStack(Material.EXP_BOTTLE);\n    ItemMeta itemMeta = item.getItemMeta();\n    itemMeta.setDisplayName(\"Level: \" + level);\n    item.setItemMeta(itemMeta);\n    return item;\n});\n```\nWith the item template set in place, every time the Menu is updated for the player using `Menu.update(Player)`, the EXP bottle \nwill be updated with the players current level and will be rendered in the inventory the player has open. \n\n### Masks\nMasks create a layer of abstraction over raw inventory slot IDs. Through the usage of masks, populating specific slots inside an inventory has never been easier. Let's start with an example.\n\nSuppose we begin with the previously created menu:\n\n![](http://i.imgur.com/LXnCkLv.png)\n\nIf we wanted to create a basic border of white glass on the outer slots, we would normally have to figure out which \nvalues reference those slots. These 22 slot IDs are 0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33, \n34, 35. If we didn't already store these values inside an `int[]` array, some general code might look something like this:\n\n```java\npublic void addWhiteBorder(Menu menu) {\n    ItemStack glass = new ItemStack(Material.STAINED_GLASS_PANE);\n    for (int i = 0 ; i \u003c 9 ; i++) { // Setting first row\n        menu.getSlot(i).setItem(glass);\n    }\n    menu.getSlot(17).setItem(glass);\n    menu.getSlot(18).setItem(glass);\n    for (int i = 26 ; i \u003c 36 ; i++) {\n        menu.getSlot(i).setItem(glass);\n    }\n}\n```\n\nAs you can see, the code is fairly unintuitive and not at all friendly for refactoring or bug fixing and it only gets \nworse as the inventory size grows or we want to add more slots. What if we _accidentally_ missed or forgot a slot id? \nFinding it would be a nuisance! For your benefit (or not) we've purposely excluded a single slot in the above example \nso that our inventory would have a gaping hole, feel free to see how long it would take to find the missing number.\n\nHere is where Masks come in play. For the above inventory, masking the slots is made simple using a \n[BinaryMask](src/main/java/org/ipvp/canvas/mask/BinaryMask.java).\n\n```java\npublic void addWhiteBorder(Inventory inventory) {\n    Mask mask = BinaryMask.builder(menu)\n                    .item(new ItemStack(Material.STAINED_GLASS_PANE))\n                    .pattern(\"111111111\") // First row\n                    .pattern(\"100000001\") // Second row\n                    .pattern(\"100000001\") // Third row\n                    .pattern(\"111111111\").build(); // Fourth row\n    mask.apply(menu);\n}\n```\n\nMasks provide an incredibly simple interface for labelling slots. In the case of \n[BinaryMask](src/main/java/org/ipvp/canvas/mask/BinaryMask.java), each character represents a boolean value of whether or \nnot the slot should be selected. A character value of '1' represents yes and all other characters the opposite. \nThis model provides a semi-visual view of what the inventory will look like and is easy to add or remove specific slots.\n\nThe final product we end up with is:\n\n![](http://i.imgur.com/BHt65l6.png)\n\n#### Recipe Masks\nA [RecipeMask](src/main/java/org/ipvp/canvas/mask/RecipeMask.java) is another abstraction of mask which enabled assigning\nmultiple types of items to slots. In the above image suppose we want every second item to be a red stained glass pane, we\ncould use a RecipeMask in the following way:\n```java\npublic void addRedWhiteBorder(Inventory inventory) {\n    Mask mask = RecipeMask.builder(menu)\n            .pattern(\"wrwrwrwrw\") // First row\n            .pattern(\"r0000000r\") // Second row\n            .pattern(\"w0000000w\") // Third row\n            .pattern(\"rwrwrwrwr\").build(); // Fourth row\n    mask.apply(menu);\n}\n```\nIn the above, we use the `w` and `r` characters in the pattern wherever we want and then assign an item\nto that character. If no item is assigned, the character will default to `AIR` in the final product.   \n\nThe final product we end up with is:\n\n![](https://i.imgur.com/eWU3BuG.png)\n\n## License\ncanvas is open source and is available under the [MIT license](LICENSE.txt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIPVP-MC%2Fcanvas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FIPVP-MC%2Fcanvas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIPVP-MC%2Fcanvas/lists"}