Facepunch / Rust.Community

Community Entity to fill Server Side modder requests
MIT License
46 stars 21 forks source link

Adding Drag & Drop Support via Draggables & Slots #55

Open Kulltero opened 1 year ago

Kulltero commented 1 year ago

Implementing Drag & Drop into our UIs

drag & drop allows us to mark a panel as draggable & listening to its movements when dragged by the player, opening up a whole new way of player interaction

The Breakdown

this PR adds 2 components, a Draggable component and a Slot component. the Draggable Component is responsible for the dragging interaction, sending RPCs to the server, & keeping itself within the constraints. the Slot Component's purpose is to allow draggables to be attached to the panel, with an optional primitive string filter to only allow draggables with a matching filter in.

Demo:

https://github.com/Facepunch/Rust.Community/assets/33698270/8747ba06-2b71-49cd-b171-b20fe4a799ae

Receiving RPCs

Draggable's will send 1 of 2 RPC calls on the CommunityEntity depending on which action is performed:

the position Type

the Draggable Component comes with 4 seperate position send types, you might prefer one over the other depending on your scenario:

public enum PositionSendType : byte{
   // the Position normalized to the Screen resolution
   NormalizedScreen,
   // the Position normalized to the panel's limitParent (even if the setting is not used)*
   NormalizedParent,
   // the offset from the Draggable's last position
   Relative,
   // the offset from the anchor
   RelativeAnchor
}

* if the limitParent setting is not used, it still calculates the parent to use for the NormalizedParent setting. dragging the panel outside of the panel will produce values greater or lower than 0 - 1. the limitParentIndex is also supported for this usecase

the JSON Implementation - Draggable

{
  "type":"Draggable",  // All thats required to make the panel draggable

   "maxDistance": -1, // how far the draggable is allowed to be dragged from its parent
   "anchorOffset": "0 0", // if the anchor should be offset from the draggable's initial position the offset can be specified here

   "limitToParent": false, // Limits the Draggable to the closest non-slot parent, does not work combined with maxDistance
   "parentLimitIndex": 1, // if limitToParent is true, this index allows you to specify how many non slot parents it should traverse before finding the limit
   "parentPadding": "0 0", // if limitToParent is true, specifies additional padding that the draggable should be contained. can be negative to allow the draggable to slightly travel outside the parent

   "dropAnywhere": true, // if false, the draggable will return to its last valid position (its initial position by default) unless swapped or attached to a slot
   "keepOnTop": false, // if true the draggable will stay detached from its parent, reccomended for unrestricted draggables
   "allowSwapping": false, // if true the draggable can be swapped with other draggables if dragged ontop of eachother
   "dragAlpha": 1, // if the alpha of the draggable's graphics should be lowered while being dragged.
   "positionRPC": "NormalizedScreen", // how the position should be processed before being sent via RPCs*

   "filter": null // the filter string this draggable should carry, see #Slots & Filtering
}

* Depending on other settings the default value for the positionRPC field may change.

the JSON Implementation - Slot

{
  "type":"Draggable",  // All thats required to make the panel a slot
   "filter": null, // if not null it will only allow draggables with a matching filter string
}

Slots & Filtering

Slots come with a built in filter system that allows you to specify what draggables can be attached.

this is how the filtering is evaluated:

public static bool FitsIntoSlot(Draggable drag, Slot slot){
   if(slot.filter == null)
      return true;

   return drag.filter == slot.filter;
}

if the slot has no filter, any draggable can be inserted. if the slot has a filter it only allows Draggables with a matching filter property. in the demo video this is shown with the green Draggable & Slots, the gray slots will accept any draggable while the green slot will only accept the matching Draggable. this is accomplished by setting the filter string

when building a system arround slots & the swapping of Draggables, its reccomended to set the dropAnywhere setting to false to make Draggables return to their last position if a