diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java
index 6d786e46c4..3dc8a3a4c2 100644
--- a/runelite-api/src/main/java/net/runelite/api/Client.java
+++ b/runelite-api/src/main/java/net/runelite/api/Client.java
@@ -412,15 +412,6 @@ public interface Client extends GameEngine
*/
Widget getWidget(WidgetInfo widget);
- /**
- * Gets an array of widgets that correspond to the passed group ID.
- *
- * @param groupId the group ID
- * @return the widget group
- * @see net.runelite.api.widgets.WidgetID
- */
- Widget[] getGroup(int groupId);
-
/**
* Gets a widget by its raw group ID and child ID.
*
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/reorderprayers/ReorderPrayersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/reorderprayers/ReorderPrayersPlugin.java
index 86615d1c5c..8d7690579a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/reorderprayers/ReorderPrayersPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/reorderprayers/ReorderPrayersPlugin.java
@@ -36,14 +36,13 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.HashTable;
-import net.runelite.api.Node;
import net.runelite.api.Prayer;
import net.runelite.api.WidgetNode;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.GameStateChanged;
-import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.events.WidgetLoaded;
+import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.widgets.Widget;
import static net.runelite.api.widgets.WidgetConfig.DRAG;
import static net.runelite.api.widgets.WidgetConfig.DRAG_ON;
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
index ae63a28ae8..48b37398e1 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
@@ -35,6 +35,7 @@ import net.runelite.api.Friend;
import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GraphicsObject;
+import net.runelite.api.HashTable;
import net.runelite.api.HintArrowType;
import net.runelite.api.IndexedSprite;
import net.runelite.api.InventoryID;
@@ -61,6 +62,7 @@ import net.runelite.api.SpritePixels;
import net.runelite.api.Tile;
import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
+import net.runelite.api.WidgetNode;
import net.runelite.api.WorldType;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
@@ -321,9 +323,9 @@ public abstract class RSClientMixin implements RSClient
{
int topGroup = getWidgetRoot();
List widgets = new ArrayList();
- for (Widget widget : getWidgets()[topGroup])
+ for (RSWidget widget : getWidgets()[topGroup])
{
- if (widget != null && widget.getParentId() == -1)
+ if (widget != null && widget.getRSParentId() == -1)
{
widgets.add(widget);
}
@@ -343,7 +345,7 @@ public abstract class RSClientMixin implements RSClient
@Inject
@Override
- public Widget[] getGroup(int groupId)
+ public RSWidget[] getGroup(int groupId)
{
RSWidget[][] widgets = getWidgets();
@@ -352,15 +354,7 @@ public abstract class RSClientMixin implements RSClient
return null;
}
- List w = new ArrayList();
- for (Widget widget : widgets[groupId])
- {
- if (widget != null)
- {
- w.add(widget);
- }
- }
- return w.toArray(new Widget[w.size()]);
+ return widgets[groupId];
}
@Inject
@@ -564,7 +558,7 @@ public abstract class RSClientMixin implements RSClient
for (Node node = head.getNext(); node != head; node = node.getNext())
{
- graphicsObjects.add((GraphicsObject)node);
+ graphicsObjects.add((GraphicsObject) node);
}
return graphicsObjects;
@@ -1099,4 +1093,45 @@ public abstract class RSClientMixin implements RSClient
{
callbacks.clientMainLoop();
}
+
+ @MethodHook("gameDraw")
+ @Inject
+ public static void gameDraw(Widget[] widgets, int parentId, int var2, int var3, int var4, int var5, int x, int y, int var8)
+ {
+ for (Widget rlWidget : widgets)
+ {
+ RSWidget widget = (RSWidget) rlWidget;
+ if (widget == null)
+ {
+ continue;
+ }
+
+ if (widget.getRSParentId() == parentId)
+ {
+ if (parentId != -1)
+ {
+ widget.setRenderParentId(parentId);
+ }
+ widget.setRenderX(x + widget.getRelativeX());
+ widget.setRenderY(y + widget.getRelativeY());
+ }
+
+ HashTable componentTable = client.getComponentTable();
+ WidgetNode childNode = componentTable.get(widget.getId());
+ if (childNode != null)
+ {
+ int widgetId = widget.getId();
+ int groupId = childNode.getId();
+ RSWidget[] children = client.getWidgets()[groupId];
+
+ for (RSWidget child : children)
+ {
+ if (child.getRSParentId() == -1)
+ {
+ child.setRenderParentId(widgetId);
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java
index 14a44ec564..3d226d3840 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java
@@ -28,7 +28,7 @@ import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import net.runelite.api.Node;
+import net.runelite.api.HashTable;
import net.runelite.api.Point;
import net.runelite.api.WidgetNode;
import net.runelite.api.events.WidgetHiddenChanged;
@@ -42,8 +42,6 @@ import static net.runelite.api.widgets.WidgetInfo.TO_CHILD;
import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
import net.runelite.api.widgets.WidgetItem;
import net.runelite.rs.api.RSClient;
-import net.runelite.rs.api.RSHashTable;
-import net.runelite.rs.api.RSNode;
import net.runelite.rs.api.RSWidget;
@Mixin(RSWidget.class)
@@ -57,6 +55,44 @@ public abstract class RSWidgetMixin implements RSWidget
@Inject
private static int rl$widgetLastPosChanged;
+ @Inject
+ private int rl$parentId;
+
+ @Inject
+ private int rl$x;
+
+ @Inject
+ private int rl$y;
+
+ @Inject
+ RSWidgetMixin()
+ {
+ rl$parentId = -1;
+ rl$x = -1;
+ rl$y = -1;
+ }
+
+ @Inject
+ @Override
+ public void setRenderParentId(int parentId)
+ {
+ rl$parentId = parentId;
+ }
+
+ @Inject
+ @Override
+ public void setRenderX(int x)
+ {
+ rl$x = x;
+ }
+
+ @Inject
+ @Override
+ public void setRenderY(int y)
+ {
+ rl$y = y;
+ }
+
@Inject
@Override
public Widget getParent()
@@ -74,34 +110,27 @@ public abstract class RSWidgetMixin implements RSWidget
@Override
public int getParentId()
{
- int parentId = getRSParentId();
- if (parentId != -1)
- {
- return parentId;
- }
+ int parentId = rl$parentId;
- int groupId = TO_GROUP(getId());
- RSHashTable componentTable = client.getComponentTable();
- RSNode[] buckets = componentTable.getBuckets();
- for (int i = 0; i < buckets.length; ++i)
+ if (parentId != -1 && getRSParentId() == -1)
{
- Node node = buckets[i];
+ // if this happens, the widget is or was nested.
+ // rl$parentId is updated when drawing, but will not be updated when
+ // the widget is no longer reachable in the tree, leaving
+ // parent id potentially incorrect
- // It looks like the first node in the bucket is always
- // a sentinel
- Node cur = node.getNext();
- while (cur != node)
+ // check the parent in the component table
+ HashTable componentTable = client.getComponentTable();
+ WidgetNode widgetNode = componentTable.get(parentId);
+ if (widgetNode == null || widgetNode.getId() != TO_GROUP(getId()))
{
- WidgetNode wn = (WidgetNode) cur;
-
- if (groupId == wn.getId())
- {
- return (int) wn.getHash();
- }
- cur = cur.getNext();
+ // invalidate parent
+ rl$parentId = -1;
+ return -1;
}
}
- return -1;
+
+ return parentId;
}
@Inject
@@ -153,42 +182,7 @@ public abstract class RSWidgetMixin implements RSWidget
@Override
public Point getCanvasLocation()
{
- int x = 0;
- int y = 0;
- RSWidget cur;
-
- for (cur = this; cur.getParent() != null; cur = (RSWidget) cur.getParent())
- {
- x += cur.getRelativeX();
- y += cur.getRelativeY();
-
- x -= cur.getScrollX();
- y -= cur.getScrollY();
- }
-
- // cur is now the root
- int[] widgetBoundsWidth = client.getWidgetPositionsX();
- int[] widgetBoundsHeight = client.getWidgetPositionsY();
-
- int boundsIndex = cur.getBoundsIndex();
- if (boundsIndex != -1)
- {
- x += widgetBoundsWidth[boundsIndex];
- y += widgetBoundsHeight[boundsIndex];
-
- if (cur.getType() > 0)
- {
- x += cur.getRelativeX();
- y += cur.getRelativeY();
- }
- }
- else
- {
- x += cur.getRelativeX();
- y += cur.getRelativeY();
- }
-
- return new Point(x, y);
+ return new Point(rl$x, rl$y);
}
@Inject
@@ -285,9 +279,9 @@ public abstract class RSWidgetMixin implements RSWidget
}
List widgets = new ArrayList();
- for (Widget widget : children)
+ for (RSWidget widget : children)
{
- if (widget != null && widget.getParentId() == getId())
+ if (widget != null && widget.getRSParentId() == getId())
{
widgets.add(widget);
}
@@ -300,50 +294,39 @@ public abstract class RSWidgetMixin implements RSWidget
public Widget[] getStaticChildren()
{
List widgets = new ArrayList();
- for (Widget widget : client.getGroup(TO_GROUP(getId())))
+ for (RSWidget widget : client.getGroup(TO_GROUP(getId())))
{
- if (widget != null && widget.getParentId() == getId())
+ if (widget != null && widget.getRSParentId() == getId())
{
widgets.add(widget);
}
}
- return widgets.toArray(new Widget[widgets.size()]);
+ return widgets.toArray(new RSWidget[widgets.size()]);
}
@Inject
@Override
public Widget[] getNestedChildren()
{
- RSHashTable componentTable = client.getComponentTable();
- int group = -1;
+ HashTable componentTable = client.getComponentTable();
- // XXX can actually use hashtable lookup instead of table
- // iteration here...
- for (Node node : componentTable.getNodes())
+ WidgetNode wn = componentTable.get(getId());
+ if (wn == null)
{
- WidgetNode wn = (WidgetNode) node;
-
- if (wn.getHash() == getId())
- {
- group = wn.getId();
- break;
- }
+ return new RSWidget[0];
}
- if (group == -1)
- {
- return new Widget[0];
- }
+ int group = wn.getId();
- List widgets = new ArrayList();
- for (Widget widget : client.getGroup(group))
+ List widgets = new ArrayList();
+ for (RSWidget widget : client.getGroup(group))
{
- if (widget != null && widget.getParentId() == getId())
+ if (widget != null && widget.getRSParentId() == -1)
{
widgets.add(widget);
}
}
- return widgets.toArray(new Widget[widgets.size()]);
+ return widgets.toArray(new RSWidget[widgets.size()]);
}
@Inject
@@ -373,7 +356,9 @@ public abstract class RSWidgetMixin implements RSWidget
{
// if the widget is hidden it will not magically unhide from its parent changing
if (child == null || child.isSelfHidden())
+ {
continue;
+ }
child.broadcastHidden(hidden);
}
@@ -386,7 +371,9 @@ public abstract class RSWidgetMixin implements RSWidget
for (Widget nestedChild : nestedChildren)
{
if (nestedChild == null || nestedChild.isSelfHidden())
+ {
continue;
+ }
((RSWidget) nestedChild).broadcastHidden(hidden);
}
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java
index c73c8c16dd..fa7cae893e 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java
@@ -156,6 +156,15 @@ public interface RSClient extends RSGameEngine, Client
@Import("widgets")
RSWidget[][] getWidgets();
+ /**
+ * Gets an array of widgets that correspond to the passed group ID.
+ *
+ * @param groupId the group ID
+ * @return the widget group
+ * @see net.runelite.api.widgets.WidgetID
+ */
+ RSWidget[] getGroup(int groupId);
+
@Import("region")
@Override
RSRegion getRegion();
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java
index d0db093204..53bfa46cfd 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java
@@ -40,6 +40,12 @@ public interface RSWidget extends Widget
@Override
int getId();
+ void setRenderParentId(int parentId);
+
+ void setRenderX(int x);
+
+ void setRenderY(int y);
+
@Import("parentId")
int getRSParentId();