From b269a75c451c2a209dea1ade1bce6616206f4e24 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sun, 10 Jun 2018 04:39:57 -0600 Subject: [PATCH 1/5] runelite-client: Allow ChatboxInputManager to callback early --- .../main/java/net/runelite/api/ScriptID.java | 24 ++++++++++ .../client/game/ChatboxInputManager.java | 46 ++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java index 0299da0472..08b48cdc0d 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -26,6 +26,30 @@ package net.runelite.api; public final class ScriptID { + /** + * Sends a chat message + * + */ public static final int CHATBOX_INPUT = 96; + + /** + * Closes the chatbox input + * + */ + public static final int CLOSE_CHATBOX_INPUT = 299; + + /** + * Initializes the chatbox input to use RuneLite callbacks + * + */ public static final int RUNELITE_CHATBOX_INPUT_INIT = 10001; } diff --git a/runelite-client/src/main/java/net/runelite/client/game/ChatboxInputManager.java b/runelite-client/src/main/java/net/runelite/client/game/ChatboxInputManager.java index bb907e1cbf..3839d40a80 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ChatboxInputManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ChatboxInputManager.java @@ -29,6 +29,7 @@ import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; import java.util.function.Consumer; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.ScriptID; @@ -39,13 +40,17 @@ import net.runelite.client.callback.ClientThread; @Slf4j public class ChatboxInputManager { - private static final int NO_LIMIT = Integer.MAX_VALUE; + public static final int NO_LIMIT = Integer.MAX_VALUE; private final Client client; private final ClientThread clientThread; private Consumer done; + private Consumer changed; private int characterLimit = NO_LIMIT; + @Getter + private boolean open = false; + @Inject public ChatboxInputManager(Client client, ClientThread clientThread, EventBus eventBus) { @@ -67,9 +72,16 @@ public class ChatboxInputManager } public void openInputWindow(String text, String defaul, int characterLimit, Consumer done) + { + openInputWindow(text, defaul, characterLimit, null, done); + } + + public void openInputWindow(String text, String defaul, int characterLimit, Consumer changed, Consumer done) { this.done = done; + this.changed = changed; this.characterLimit = characterLimit; + this.open = true; clientThread.invokeLater(() -> client.runScript( ScriptID.RUNELITE_CHATBOX_INPUT_INIT, text, @@ -77,6 +89,23 @@ public class ChatboxInputManager )); } + /** + * Closes the RuneScape-style chatbox input + */ + public void closeInputWindow() + { + if (!this.open) + { + return; + } + this.open = false; + clientThread.invokeLater(() -> client.runScript( + ScriptID.CLOSE_CHATBOX_INPUT, + 1, + 1 + )); + } + @Subscribe public void scriptCallback(ScriptCallbackEvent ev) { @@ -93,9 +122,17 @@ public class ChatboxInputManager { case 27: // Escape str = ""; + if (changed != null) + { + changed.accept(str); + } // fallthrough case '\n': - done.accept(str); + if (done != null) + { + done.accept(str); + } + this.open = false; retval = 1; break; case '\b': @@ -111,6 +148,11 @@ public class ChatboxInputManager } } + if (changed != null) + { + changed.accept(str); + } + client.getStringStack()[stringStackSize++] = str; client.getIntStack()[intStackSize++] = retval; client.setIntStackSize(intStackSize); From e67561c3555dd35a4d8a2a3cfdc2eed14ea868b6 Mon Sep 17 00:00:00 2001 From: Tyler Hardy Date: Sat, 10 Feb 2018 18:15:59 -0600 Subject: [PATCH 2/5] runelite-api: Add/Correct fairy ring panel widgets --- .../main/java/net/runelite/api/widgets/WidgetID.java | 11 ++++++++++- .../java/net/runelite/api/widgets/WidgetInfo.java | 10 ++++++++-- .../client/plugins/fairyring/FairyRingPlugin.java | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 9a824216d9..6549bf397c 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -36,7 +36,7 @@ package net.runelite.api.widgets; */ public class WidgetID { - public static final int FAIRY_RING_CODE_GROUP_ID = 381; + public static final int FAIRY_RING_PANEL_GROUP_ID = 381; public static final int FAIRY_RING_GROUP_ID = 398; public static final int LOGOUT_PANEL_ID = 182; public static final int BANK_GROUP_ID = 12; @@ -510,6 +510,15 @@ public class WidgetID static final int BOTTOM_BAR = 14; } + static class FairyRingPanel + { + static final int HEADER = 2; + static final int LIST = 7; + static final int FAVORITES = 8; + static final int SEPARATOR = 9; + static final int SCROLLBAR = 152; + } + static class FairyRing { static final int LEFT_ORB_CLOCKWISE = 19; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 7378e54774..da31af9b92 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -31,8 +31,6 @@ package net.runelite.api.widgets; */ public enum WidgetInfo { - FAIRY_QUEEN_HIDEOUT_CODE(WidgetID.FAIRY_RING_CODE_GROUP_ID, WidgetID.FairyRingCode.FAIRY_QUEEN_HIDEOUT), - FAIRY_RING_LEFT_ORB_CLOCKWISE(WidgetID.FAIRY_RING_GROUP_ID, WidgetID.FairyRing.LEFT_ORB_CLOCKWISE), FAIRY_RING_LEFT_ORB_COUNTER_CLOCKWISE(WidgetID.FAIRY_RING_GROUP_ID, WidgetID.FairyRing.LEFT_ORB_COUNTER_CLOCKWISE), FAIRY_RING_MIDDLE_ORB_CLOCKWISE(WidgetID.FAIRY_RING_GROUP_ID, WidgetID.FairyRing.MIDDLE_ORB_CLOCKWISE), @@ -346,6 +344,14 @@ public enum WidgetInfo MTA_ENCHANTMENT_BONUS_TEXT(WidgetID.MTA_ENCHANTMENT_GROUP_ID, WidgetID.MTA.BONUS_TEXT_COMPONENT), MTA_ENCHANTMENT_BONUS(WidgetID.MTA_ENCHANTMENT_GROUP_ID, WidgetID.MTA.BONUS_COMPONENT), + FAIRY_RING(WidgetID.FAIRY_RING_GROUP_ID, 0), + + FAIRY_RING_HEADER(WidgetID.FAIRY_RING_PANEL_GROUP_ID, WidgetID.FairyRingPanel.HEADER), + FAIRY_RING_LIST(WidgetID.FAIRY_RING_PANEL_GROUP_ID, WidgetID.FairyRingPanel.LIST), + FAIRY_RING_FAVORITES(WidgetID.FAIRY_RING_PANEL_GROUP_ID, WidgetID.FairyRingPanel.FAVORITES), + FAIRY_RING_LIST_SEPARATOR(WidgetID.FAIRY_RING_PANEL_GROUP_ID, WidgetID.FairyRingPanel.SEPARATOR), + FAIRY_RING_LIST_SCROLLBAR(WidgetID.FAIRY_RING_PANEL_GROUP_ID, WidgetID.FairyRingPanel.SCROLLBAR), + DESTROY_ITEM(WidgetID.DESTROY_ITEM_GROUP_ID, 0), DESTROY_ITEM_NAME(WidgetID.DESTROY_ITEM_GROUP_ID, WidgetID.DestroyItem.DESTROY_ITEM_NAME), DESTROY_ITEM_YES(WidgetID.DESTROY_ITEM_GROUP_ID, WidgetID.DestroyItem.DESTROY_ITEM_YES), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java index 9c23437522..f09938f751 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java @@ -61,7 +61,7 @@ public class FairyRingPlugin extends Plugin @Subscribe public void onWidgetLoaded(WidgetLoaded widgetLoaded) { - if (widgetLoaded.getGroupId() == WidgetID.FAIRY_RING_CODE_GROUP_ID) + if (widgetLoaded.getGroupId() == WidgetID.FAIRY_RING_PANEL_GROUP_ID) { setWidgetTextToDestination(); } From 6b278d48a5aa3e2d0737e47e876f87ece2f5196c Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 15 Jun 2018 06:09:57 -0600 Subject: [PATCH 3/5] Add API to create new widgets --- .../main/java/net/runelite/api/ScriptID.java | 8 ++ .../java/net/runelite/api/WidgetType.java | 30 +++++++ .../java/net/runelite/api/widgets/Widget.java | 86 +++++++++++++++++++ .../devtools/WidgetInfoTableModel.java | 8 +- .../net/runelite/mixins/RSWidgetMixin.java | 58 +++++++++++++ runelite-scripts/scripts/null.rs2asm | 37 ++++++++ .../java/net/runelite/rs/api/RSClient.java | 9 ++ .../java/net/runelite/rs/api/RSWidget.java | 51 +++++++++++ 8 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/WidgetType.java create mode 100644 runelite-scripts/scripts/null.rs2asm diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java index 08b48cdc0d..ce33b5f98e 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -52,4 +52,12 @@ public final class ScriptID * */ public static final int RUNELITE_CHATBOX_INPUT_INIT = 10001; + + /** + * Does nothing + * + * This is used to eat events when you want a menu action attached to it + * because you need an op listener attached to it for it to work + */ + public static final int NULL = 10003; } diff --git a/runelite-api/src/main/java/net/runelite/api/WidgetType.java b/runelite-api/src/main/java/net/runelite/api/WidgetType.java new file mode 100644 index 0000000000..67cb44c590 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/WidgetType.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +public final class WidgetType +{ + public static final int GRAPHIC = 5; +} diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java index 0b296d3e61..15ead6cad1 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java @@ -267,6 +267,11 @@ public interface Widget */ void setHidden(boolean hidden); + /** + * The index of this widget in it's parent's children array + */ + int getIndex(); + /** * Gets the location the widget is being drawn on the canvas. *

@@ -354,14 +359,46 @@ public interface Widget */ boolean contains(Point point); + /** + * Gets the amount of pixels the widget is scrolled in the X axis + */ int getScrollX(); + /** + * Sets the amount of pixels the widget is scrolled in the X axis + */ void setScrollX(int scrollX); + /** + * Gets the amount of pixels the widget is scrolled in the Y axis + */ int getScrollY(); + /** + * sets the amount of pixels the widget is scrolled in the Y axis + */ void setScrollY(int scrollY); + /** + * Gets the size of the widget's viewport in the X axis + */ + int getScrollWidth(); + + /** + * Sets the size of the widget's viewport in the X axis + */ + void setScrollWidth(int width); + + /** + * Gets the size of the widget's viewport in the Y axis + */ + int getScrollHeight(); + + /** + * Sets the size of the widget's viewport in the Y axis + */ + void setScrollHeight(int height); + /** * Gets the original x-axis coordinate. * @@ -452,4 +489,53 @@ public interface Widget * @return the actions */ String[] getActions(); + + /** + * Creates a dynamic widget child + * + * @param index the index of the new widget in the children list or -1 to append to the back + * @param type the type of the widget + */ + Widget createChild(int index, int type); + + /** + * Creates a menu action on the widget + * + * @param index The index of the menu + * @param action The string to be displayed next to the widget's name in the context menu + */ + void setAction(int index, String action); + + /** + * Sets a script to be ran when the a menu action is clicked. + * hasListener must be true for this to take effect + * + * @param args A ScriptID, then the args for the script + */ + void setOnOpListener(Object ...args); + + /** + * If this widget has any listeners on it + */ + boolean hasListener(); + + /** + * Sets if the widget has any listeners. This should be called whenever a setXListener function is called + */ + void setHasListener(boolean hasListener); + + /** + * This is true if the widget is from an if3 interface, or is dynamically created + */ + boolean isIf3(); + + /** + * Recomputes this widget's x/y/w/h, excluding scroll + */ + void revalidate(); + + /** + * Recomputes this widget's group's x/y/w/h including scroll + */ + void revalidateScroll(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java index 0c26c71dcf..79a261dfff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java @@ -134,12 +134,16 @@ public class WidgetInfoTableModel extends AbstractTableModel out.add(new WidgetField<>("RelativeY", Widget::getRelativeY, Widget::setRelativeY, Integer.class)); out.add(new WidgetField<>("CanvasLocation", Widget::getCanvasLocation)); out.add(new WidgetField<>("Bounds", Widget::getBounds)); - out.add(new WidgetField<>("ScrollX", Widget::getScrollX)); - out.add(new WidgetField<>("ScrollY", Widget::getScrollY)); + out.add(new WidgetField<>("ScrollX", Widget::getScrollX, Widget::setScrollX, Integer.class)); + out.add(new WidgetField<>("ScrollY", Widget::getScrollY, Widget::setScrollY, Integer.class)); + out.add(new WidgetField<>("ScrollWidth", Widget::getScrollWidth, Widget::setScrollWidth, Integer.class)); + out.add(new WidgetField<>("ScrollHeight", Widget::getScrollHeight, Widget::setScrollHeight, Integer.class)); out.add(new WidgetField<>("OriginalX", Widget::getOriginalX)); out.add(new WidgetField<>("OriginalY", Widget::getOriginalY)); out.add(new WidgetField<>("PaddingX", Widget::getPaddingX)); out.add(new WidgetField<>("PaddingY", Widget::getPaddingY)); + out.add(new WidgetField<>("IsIf3", Widget::isIf3)); + out.add(new WidgetField<>("HasListener", Widget::hasListener, Widget::setHasListener, Boolean.class)); return out; } 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 a4d2dc8cc8..3b197f10ed 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java @@ -477,4 +477,62 @@ public abstract class RSWidgetMixin implements RSWidget WidgetPositioned widgetPositioned = new WidgetPositioned(); client.getCallbacks().postDeferred(widgetPositioned); } + + @Inject + @Override + public Widget createChild(int index, int type) + { + RSWidget w = client.createWidget(); + w.setType(type); + w.setParentId(getId()); + w.setId(getId()); + w.setIsIf3(true); + + RSWidget[] siblings = getChildren(); + + if (index < 0) + { + if (siblings == null) + { + index = 0; + } + else + { + index = siblings.length; + } + } + + if (siblings == null) + { + siblings = new RSWidget[index + 1]; + setChildren(siblings); + } + else if (siblings.length <= index) + { + RSWidget[] newSiblings = new RSWidget[index + 1]; + System.arraycopy(siblings, 0, newSiblings, 0, siblings.length); + siblings = newSiblings; + setChildren(siblings); + } + + siblings[index] = w; + w.setIndex(index); + + return w; + } + + @Inject + @Override + public void revalidate() + { + client.revalidateWidget(this); + } + + @Inject + @Override + public void revalidateScroll() + { + client.revalidateWidget(this); + client.revalidateWidgetScroll(client.getWidgets()[TO_GROUP(this.getId())], this, false); + } } diff --git a/runelite-scripts/scripts/null.rs2asm b/runelite-scripts/scripts/null.rs2asm new file mode 100644 index 0000000000..81afec5354 --- /dev/null +++ b/runelite-scripts/scripts/null.rs2asm @@ -0,0 +1,37 @@ +; Copyright (c) 2018 Abex +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; 1. Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +; ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +;; +; Does nothing +; +; This is used to eat events when you want a menu action attached to it +; because you need an op listener attached to it for it to work +;; + +.id 10003 +.int_stack_count 0 +.string_stack_count 0 +.int_var_count 0 +.string_var_count 0 + +return 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 5c64dd89ec..9748ce4ec4 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 @@ -648,4 +648,13 @@ public interface RSClient extends RSGameEngine, Client RSItem getLastItemDespawn(); void setLastItemDespawn(RSItem lastItemDespawn); + + @Construct + RSWidget createWidget(); + + @Import("revalidateWidget") + void revalidateWidget(Widget w); + + @Import("revalidateWidgetScroll") + void revalidateWidgetScroll(Widget[] group, Widget w, boolean postEvent); } 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 53bfa46cfd..36d9ed69fe 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 @@ -36,6 +36,9 @@ public interface RSWidget extends Widget @Override RSWidget[] getChildren(); + @Import("children") + void setChildren(RSWidget[] children); + @Import("id") @Override int getId(); @@ -46,9 +49,15 @@ public interface RSWidget extends Widget void setRenderY(int y); + @Import("id") + void setId(int id); + @Import("parentId") int getRSParentId(); + @Import("parentId") + void setParentId(int id); + @Import("clickMask") int getClickMask(); @@ -141,6 +150,9 @@ public interface RSWidget extends Widget @Import("index") int getIndex(); + @Import("index") + void setIndex(int index); + @Import("rotationX") int getRotationX(); @@ -182,6 +194,22 @@ public interface RSWidget extends Widget @Override void setScrollY(int scrollY); + @Import("scrollWidth") + @Override + int getScrollWidth(); + + @Import("scrollWidth") + @Override + void setScrollWidth(int width); + + @Import("scrollHeight") + @Override + int getScrollHeight(); + + @Import("scrollHeight") + @Override + void setScrollHeight(int height); + @Import("spriteId") @Override int getSpriteId(); @@ -250,4 +278,27 @@ public interface RSWidget extends Widget void setPaddingY(int paddingY); void broadcastHidden(boolean hidden); + + @Import("onOpListener") + @Override + void setOnOpListener(Object ...args); + + @Import("setAction") + @Override + void setAction(int idx, String action); + + @Import("isIf3") + @Override + boolean isIf3(); + + @Import("isIf3") + void setIsIf3(boolean isIf3); + + @Import("hasListener") + @Override + boolean hasListener(); + + @Import("hasListener") + @Override + void setHasListener(boolean hasListener); } From af0fdb1d49daa1d80a67e9d9ea87af2323ae4d94 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 15 Jun 2018 12:30:31 -0600 Subject: [PATCH 4/5] runelite-client: Add fairy ring search to fairy ring plugin --- .../main/java/net/runelite/api/ScriptID.java | 10 + .../plugins/fairyring/FairyRingConfig.java | 43 +++ .../plugins/fairyring/FairyRingPlugin.java | 277 +++++++++++++++++- .../client/plugins/fairyring/FairyRings.java | 21 +- 4 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingConfig.java diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java index ce33b5f98e..761c8b2f08 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -26,6 +26,16 @@ package net.runelite.api; public final class ScriptID { + /** + * Updates the scrollbar handle and container to the new height of the content container + *

    + *
  • int (WidgetID) Scrollbar's widget ID
  • + *
  • int (WidgetID) Container widget ID
  • + *
  • int how far down to scroll
  • + *
+ */ + public static final int UPDATE_SCROLLBAR = 72; + /** * Sends a chat message *
    diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingConfig.java new file mode 100644 index 0000000000..131e36e6c1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.fairyring; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("fairyrings") +public interface FairyRingConfig extends Config +{ + @ConfigItem( + keyName = "autoOpen", + name = "Open search automatically", + description = "Open the search widget every time you enter a fairy ring" + ) + default boolean autoOpen() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java index f09938f751..5df2e08a23 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRingPlugin.java @@ -1,4 +1,6 @@ /* + * Copyright (c) 2018 Abex + * Copyright (c) 2017, Tyler * Copyright (c) 2018, Yoav Ram * Copyright (c) 2018, Infinitay * All rights reserved. @@ -26,20 +28,38 @@ package net.runelite.client.plugins.fairyring; +import com.google.common.base.Strings; import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import java.util.Collection; +import java.util.Map; +import java.util.TreeMap; +import javax.annotation.Nullable; import javax.inject.Inject; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import net.runelite.api.ScriptID; +import net.runelite.api.SoundEffectID; +import net.runelite.api.SpriteID; import net.runelite.api.Varbits; +import net.runelite.api.WidgetType; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.ChatboxInputManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.util.Text; +@Slf4j @PluginDescriptor( - name = "Fairy Ring Helper", + name = "Fairy Rings", description = "Show the location of the fairy ring teleport", tags = {"teleportation"} ) @@ -49,9 +69,44 @@ public class FairyRingPlugin extends Plugin private static final String[] middleDial = new String[]{"I", "L", "K", "J"}; private static final String[] rightDial = new String[]{"P", "S", "R", "Q"}; + private static final int ENTRY_PADDING = 3; + + private static final String MENU_OPEN = "Open"; + private static final String MENU_CLOSE = "Close"; + @Inject private Client client; + @Inject + private FairyRingConfig config; + + @Inject + private ChatboxInputManager chatboxInputManager; + + private Widget searchBtn; + private boolean chatboxOpenLastTick = false; + private boolean clearFilter = false; + private Collection codes = null; + + @Data + private static class CodeWidgets + { + // The fairy hideout has both of these null, because its not the same as the rest of them + @Nullable + private Widget favorite; + + @Nullable + private Widget code; + + private Widget description; + } + + @Provides + FairyRingConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(FairyRingConfig.class); + } + @Subscribe public void onVarbitChanged(VarbitChanged event) { @@ -64,6 +119,52 @@ public class FairyRingPlugin extends Plugin if (widgetLoaded.getGroupId() == WidgetID.FAIRY_RING_PANEL_GROUP_ID) { setWidgetTextToDestination(); + + Widget header = client.getWidget(WidgetInfo.FAIRY_RING_HEADER); + if (header != null) + { + searchBtn = header.createChild(-1, WidgetType.GRAPHIC); + searchBtn.setSpriteId(SpriteID.GE_SEARCH); + searchBtn.setOriginalWidth(17); + searchBtn.setOriginalHeight(17); + searchBtn.setOriginalX(11); + searchBtn.setOriginalY(11); + searchBtn.setOnOpListener(ScriptID.NULL); + searchBtn.setHasListener(true); + searchBtn.setAction(1, MENU_OPEN); + searchBtn.setName("Search"); + searchBtn.revalidate(); + + codes = null; + + if (config.autoOpen()) + { + openSearch(); + } + } + } + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked ev) + { + if (searchBtn != null && searchBtn.getId() == ev.getWidgetId()) + { + switch (ev.getMenuOption()) + { + case MENU_OPEN: + ev.consume(); + openSearch(); + client.playSoundEffect(SoundEffectID.UI_BOOP); + break; + case MENU_CLOSE: + ev.consume(); + updateFilter(""); + searchBtn.setAction(1, MENU_OPEN); + chatboxInputManager.closeInputWindow(); + client.playSoundEffect(SoundEffectID.UI_BOOP); + break; + } } } @@ -92,4 +193,178 @@ public class FairyRingPlugin extends Plugin { return FairyRings.valueOf(leftDial[varbitValueDialLeft] + middleDial[varbitValueDialMiddle] + rightDial[varbitValueDialRight]); } + + private void openSearch() + { + updateFilter(""); + searchBtn.setAction(1, MENU_CLOSE); + chatboxInputManager.openInputWindow("Filter fairy rings", "", ChatboxInputManager.NO_LIMIT, this::updateFilter, s -> + { + // We can't run it right now because scripts can't run other scripts in their callbacks + clearFilter = true; + searchBtn.setAction(1, MENU_OPEN); + }); + } + + @Subscribe + public void onGameTick(GameTick t) + { + if (clearFilter) + { + updateFilter(""); + clearFilter = false; + } + + // This has to happen because the only widget that gets hidden is the tli one + Widget fairyRingTeleportButton = client.getWidget(WidgetInfo.FAIRY_RING_TELEPORT_BUTTON); + boolean fairyRingWidgetOpen = fairyRingTeleportButton != null && !fairyRingTeleportButton.isHidden(); + boolean chatboxOpen = chatboxInputManager.isOpen(); + + if (!fairyRingWidgetOpen && chatboxOpen && chatboxOpenLastTick) + { + searchBtn.setAction(1, MENU_OPEN); + chatboxInputManager.closeInputWindow(); + } + + chatboxOpenLastTick = chatboxOpen && fairyRingWidgetOpen; + } + + private void updateFilter(String filter) + { + filter = filter.toLowerCase(); + final Widget list = client.getWidget(WidgetInfo.FAIRY_RING_LIST); + final Widget favorites = client.getWidget(WidgetInfo.FAIRY_RING_FAVORITES); + + if (list == null) + { + return; + } + + if (codes != null) + { + // Check to make sure the list hasn't been rebuild since we were last her + // Do this by making sure the list's dynamic children are the same as when we last saw them + if (codes.stream().noneMatch(w -> + { + Widget codeWidget = w.getCode(); + if (codeWidget == null) + { + return false; + } + return list.getChild(codeWidget.getIndex()) == codeWidget; + })) + { + codes = null; + } + } + + if (codes == null) + { + // Find all of the widgets that we care about, grouping by their Y value + Map codeMap = new TreeMap<>(); + + for (Widget w : list.getStaticChildren()) + { + if (w.isSelfHidden()) + { + continue; + } + + if (w.getSpriteId() != -1) + { + codeMap.computeIfAbsent(w.getRelativeY(), k -> new CodeWidgets()).setFavorite(w); + } + else if (!Strings.isNullOrEmpty(w.getText())) + { + codeMap.computeIfAbsent(w.getRelativeY(), k -> new CodeWidgets()).setDescription(w); + } + } + + for (Widget w : list.getDynamicChildren()) + { + if (w.isSelfHidden()) + { + continue; + } + + CodeWidgets c = codeMap.computeIfAbsent(w.getRelativeY(), k -> new CodeWidgets()); + c.setCode(w); + } + + codes = codeMap.values(); + } + + // Relayout the panel + int y = 0; + + if (favorites != null) + { + boolean hide = !filter.isEmpty(); + favorites.setHidden(hide); + if (!hide) + { + y += favorites.getOriginalHeight() + ENTRY_PADDING; + } + } + + for (CodeWidgets c : codes) + { + String code = Text.removeTags(c.getDescription().getName()).replaceAll(" ", ""); + String tags = null; + + if (code.length() > 0) + { + try + { + FairyRings ring = FairyRings.valueOf(code); + tags = ring.getTags(); + } + catch (IllegalArgumentException e) + { + log.warn("Unable to find ring with code '{}'", code, e); + } + } + + boolean hidden = !(filter.isEmpty() + || Text.removeTags(c.getDescription().getText()).toLowerCase().contains(filter) + || code.toLowerCase().contains(filter) + || tags != null && tags.contains(filter)); + + if (c.getCode() != null) + { + c.getCode().setHidden(hidden); + c.getCode().setOriginalY(y); + } + + if (c.getFavorite() != null) + { + c.getFavorite().setHidden(hidden); + c.getFavorite().setOriginalY(y); + } + + c.getDescription().setHidden(hidden); + c.getDescription().setOriginalY(y); + + if (!hidden) + { + y += c.getDescription().getHeight() + ENTRY_PADDING; + } + } + + y -= ENTRY_PADDING; + + if (y < 0) + { + y = 0; + } + + list.setScrollHeight(y); + list.revalidateScroll(); + client.runScript( + ScriptID.UPDATE_SCROLLBAR, + WidgetInfo.FAIRY_RING_LIST_SCROLLBAR.getId(), + WidgetInfo.FAIRY_RING_LIST.getId(), + 0 + ); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRings.java b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRings.java index 06b32ce08f..3d95de894f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRings.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fairyring/FairyRings.java @@ -26,10 +26,8 @@ package net.runelite.client.plugins.fairyring; -import lombok.AllArgsConstructor; import lombok.Getter; -@AllArgsConstructor public enum FairyRings { // A @@ -50,7 +48,7 @@ public enum FairyRings BIQ("Kalphite Hive"), BIS("Ardougne Zoo - Unicorns"), BJR("Realm of the Fisher King"), - BJS("(Island) Near Zul-Andra"), + BJS("(Island) Near Zul-Andra", "zulrah"), BKP("South of Castle Wars"), BKQ("Enchanted Valley"), BKR("Mort Myre Swamp, south of Canifis"), @@ -73,7 +71,7 @@ public enum FairyRings // D DIP("(Sire Boss) Abyssal Nexus"), DIR("Gorak's Plane"), - DIQ("Player-owned house"), + DIQ("Player-owned house", "poh home"), DIS("Wizards' Tower"), DJP("Tower of Life"), DJR("Chasm of Fire"), @@ -86,4 +84,19 @@ public enum FairyRings @Getter private final String destination; + + @Getter + private final String tags; + + FairyRings(String destination) + { + this(destination, ""); + } + + FairyRings(String destination, String tags) + { + this.destination = destination; + this.tags = tags.toLowerCase() + " " + destination.toLowerCase(); + } + } From 181c4a0abf644aecc57125dad4c59621b14d6864 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Wed, 27 Jun 2018 05:48:37 -0600 Subject: [PATCH 5/5] devtools: Show index on dynamic widgets in the tree --- .../client/plugins/devtools/WidgetTreeNode.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java index e4ccc2106c..6b7526637a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java @@ -49,8 +49,21 @@ class WidgetTreeNode extends DefaultMutableTreeNode public String toString() { Widget widget = getWidget(); + int id = widget.getId(); + String str = type + " " + TO_GROUP(id) + "." + TO_CHILD(id); + + if (widget.getIndex() != -1) + { + str += "[" + widget.getIndex() + "]"; + } + WidgetInfo info = WidgetInspector.getWidgetInfo(id); - return type + " " + TO_GROUP(id) + "." + TO_CHILD(id) + ((info != null) ? " " + info.name() : ""); + if (info != null) + { + str += " " + info.name(); + } + + return str; } }