public class MoveContainer extends AbstractWidget<MoveContainer>
A special kind of container that grants the ability for each direct child to be LEFT+CLICK+DRAGGED and moved around on the screen. The movable regions of these tracked children will be their content bounds, using AbstractWidget.isMouseInContentBounds()
to determine which child to move. This container is currently a full screen widget and does not adjust its children positions using the typical AbstractWidget.anchoredAt(float, float, AnchorPosition)
downward percolation (i.e. the children will not be affected by anchoring this widget somewhere else, unlike most other containers). As with any other container, it can (and should) be used to render()
and update()
, as it will pass those function calls down the hierarchy.
This widget can serialize/deserialize the current positions of the tracked widgets. This information is in JSON string form, and can be directly used within the EaselConfigHelper
to have persistent locations of widgets across game boots. You can learn more about this serialization process on the toJsonString()
and loadFromJsonString(String)
javadocs.
The following is an example that makes all children of a layout movable and leverages the EaselConfigHelper
pattern to make the moves persistent. It will SAVE the positions of the moved children when right clicking anywhere, and will attempt to LOAD these saved positions after this container is initialized. The move container is added to the widgets array at the end like any other widget might be, to be used in something like widgets.forEach(widget → widget.render(sb))
(or similarly for update()
as well) in order for everything to be rendered and updated properly.
MoveContainer mc = new MoveContainer()
.withAllChildrenOfLayout(layout)
.onRightClick(container -> {
configHelper.setString(MyStringConfigEnum.MOVE_CONTAINER_LOCATIONS, container.toJsonString());
})
.anchoredCenteredOnScreen();
// Load default settings. The MyStringConfigEnum has a default value of "" so that the first time it's used doesn't crash.
mc.loadFromJsonString(configHelper.getString(MyStringConfigEnum.MOVE_CONTAINER_LOCATIONS));
widgets.add(mc);
hasInteractivity, hb, isHovered, leftClickStarted, onLeftClick, onMouseEnter, onMouseLeave, onRightClick, rightClickStarted
Constructor and Description |
---|
MoveContainer() |
Modifier and Type | Method and Description |
---|---|
float |
getContentHeight()
The internal content height of the widget (excludes margins).
|
float |
getContentWidth()
The internal content width of the widget (excludes margins).
|
java.util.stream.Stream<AbstractWidget> |
iterator() |
<T> java.util.stream.Stream<T> |
iteratorOfType(java.lang.Class<T> clz)
Returns all children managed by this widget of a particular type.
|
boolean |
loadFromJsonString(java.lang.String jsonString)
Forcibly update all widget positions based on a previously serialized string.
|
protected void |
renderWidget(com.badlogic.gdx.graphics.g2d.SpriteBatch sb)
Custom widgets should implement this method for rendering.
|
java.lang.String |
toJsonString() |
protected void |
updateWidget()
Required for interactive components.
|
MoveContainer |
withAllChildrenOfLayout(GridLayout layout)
Transfers all children of the given layout to be managed by this move container instead.
|
MoveContainer |
withAllChildrenOfLayout(HorizontalLayout layout)
Transfers all children of the given layout to be managed by this move container instead.
|
MoveContainer |
withAllChildrenOfLayout(VerticalLayout layout)
Transfers all children of the given layout to be managed by this move container instead.
|
MoveContainer |
withChild(AbstractWidget child)
Adds a new child to be managed by this container.
|
anchoredAt, anchoredAt, anchoredAtClamped, anchoredAtClamped, anchoredCenteredOnMouse, anchoredCenteredOnMouse, anchoredCenteredOnMouseClamped, anchoredCenteredOnMouseClamped, anchoredCenteredOnScreen, anchoredCenteredOnScreen, cancelMovementQueue, cancelMovementQueueForAllChildren, delayedTranslate, getBottom, getContentBottom, getContentCenterX, getContentCenterY, getContentLeft, getContentRight, getContentTop, getHeight, getLeft, getRight, getTop, getWidth, hide, initializeInteractivity, isMouseInBounds, isMouseInContentBounds, leftMouseClick, mouseEnter, mouseLeave, onLeftClick, onMouseEnter, onMouseLeave, onRightClick, refreshAnchor, render, renderTopLevel, resolveMovementQueue, rightMouseClick, scaleHitboxToContent, setAllDelayedMovement, setChildrenDelayedMovement, show, toString, translate, update, updateInteractivity, withMargins, withMargins, withMargins
public MoveContainer withChild(AbstractWidget child)
child
- the new child to be managedwithAllChildrenOfLayout(GridLayout)
,
withAllChildrenOfLayout(VerticalLayout)
,
withAllChildrenOfLayout(HorizontalLayout)
public MoveContainer withAllChildrenOfLayout(VerticalLayout layout)
iterator()
stream, and then can be moved as if they were individually added using withChild(AbstractWidget)
. This function is intended to make it easy to layout widgets into nice default positions using a throwaway layout. I.e. do NOT continue to re-use the layout with the intent of using it to manage its original children, as this move container will become the new parent. To ensure that this restriction is followed, the layout is purposely cleared after all its children are transferred over.layout
- the layout whose children will all be made movable by this containerwithChild(AbstractWidget)
,
withAllChildrenOfLayout(HorizontalLayout)
,
withAllChildrenOfLayout(GridLayout)
public MoveContainer withAllChildrenOfLayout(HorizontalLayout layout)
iterator()
stream, and then can be moved as if they were individually added using withChild(AbstractWidget)
. This function is intended to make it easy to layout widgets into nice default positions using a throwaway layout. I.e. do NOT continue to re-use the layout with the intent of using it to manage its original children, as this move container will become the new parent. To ensure that this restriction is followed, the layout is purposely cleared after all its children are transferred over.layout
- the layout whose children will all be made movable by this containerwithChild(AbstractWidget)
,
withAllChildrenOfLayout(VerticalLayout)
,
withAllChildrenOfLayout(GridLayout)
public MoveContainer withAllChildrenOfLayout(GridLayout layout)
iterator()
stream, and then can be moved as if they were individually added using withChild(AbstractWidget)
. This function is intended to make it easy to layout widgets into nice default positions using a throwaway layout. I.e. do NOT continue to re-use the layout with the intent of managing its original children, as this move container will become the new parent. To ensure that this restriction is followed, the layout is purposely cleared after all its children are transferred over.layout
- the layout whose children will all be made movable by this containerwithChild(AbstractWidget)
,
withAllChildrenOfLayout(HorizontalLayout)
,
withAllChildrenOfLayout(VerticalLayout)
public float getContentWidth()
AbstractWidget
getContentWidth
in class AbstractWidget<MoveContainer>
AbstractWidget.getWidth()
,
AbstractWidget.getContentHeight()
public float getContentHeight()
AbstractWidget
getContentHeight
in class AbstractWidget<MoveContainer>
AbstractWidget.getHeight()
,
AbstractWidget.getContentWidth()
public java.util.stream.Stream<AbstractWidget> iterator()
public <T> java.util.stream.Stream<T> iteratorOfType(java.lang.Class<T> clz)
iterator()
has a slight performance penalty due to making sure the casts are safe.T
- the type of widget that will be in the final streamclz
- the class of widget caught by the filteriterator()
protected void updateWidget()
AbstractWidget
update()
on all children. For widgets that require some sort of updates each frame, you can do so here.updateWidget
in class AbstractWidget<MoveContainer>
protected void renderWidget(com.badlogic.gdx.graphics.g2d.SpriteBatch sb)
AbstractWidget
Custom widgets should implement this method for rendering. Use the inner content positions (e.g. AbstractWidget.getContentLeft()
, AbstractWidget.getContentWidth()
, etc.) to determine any position information necessary for rendering at a specific location. If the library is used as intended, these content locations should be accurate to where the widget needs to be rendered, as they reflect the most up to date location set by an anchoredAt call (this automatically will be interpolated if the anchorAt move is set to occur over several frames).
Note: you NEED to revert any changes you make to the SpriteBatch (e.g. setting a shader, changing the perspective matrix, etc.) by the time this function returns, as the SpriteBatch will be reused for rendering other widgets which will not expect those changes. You also don't technically need to render to this particular SpriteBatch (e.g. you can render to your own batch if you know what you're doing), as long as you follow the general intent of this function to render the widget.
renderWidget
in class AbstractWidget<MoveContainer>
sb
- the SpriteBatch the widget should be rendered onAbstractWidget.renderTopLevel(SpriteBatch)
public boolean loadFromJsonString(java.lang.String jsonString)
Forcibly update all widget positions based on a previously serialized string. Note that the serialization writes out information about widget positions to a JSON formatted string, with the ability to identify widgets based on the order they are added to this container. Thus, this deserialization step only works correctly if you've added the same widgets in the same order as the time when toJsonString()
was called - i.e. unless you're doing something very weird and fancy by adding a variable number of widgets in a dynamic way, this deserialization / serialization step should work out of the box. Be sure to call this load AFTER adding all widgets to the container that existed when the serialized string was created.
An empty jsonString
argument will be ignored, and if using a EaselConfigHelper
-style custom String enum, you can use "" as the default value for it.
In the backend, this deserialization works by looping through each saved (addOrder, left, bottom) triplet in the serialized string, and trying to match up the widgets currently in the map. If the current map contains a widget which was added with the same addOrder as the triplet, that particular widget will be AbstractWidget.anchoredAt(float, float, AnchorPosition)
with the (left, bottom) coordinates. This function will also attempt to restore the stacking order correctly - the serialization step stores information in order from bottom to top, and the deserialization loops through that same order and will bring elements to the top if they're valid. If all widgets in the map can be linked 1:1 with the elements in the serialized string, this function will return true.
Note that if you aren't using this function as intended (i.e. if you're getting anything other than true as the output as it fails to 1:1 update each widget), there are no guarantees that this function will be stable and not crash. So if you're doing something custom enough for this to return false, then you really shouldn't be using this at all and should roll your own serialization/deserialization code to fit your particular needs. Scary warning aside, if you're just using this in a predictable way (whenever a deserialize gets called, the container has the same basic structure of widgets added to it when the serialize was called), then this should be safe to use.
jsonString
- a string generated by a previous call of toJsonString()
toJsonString()
public java.lang.String toJsonString()
loadFromJsonString(String)
loadFromJsonString(String)