diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 5a7abf86e2..2b7b4183b5 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -25,9 +25,9 @@ object ProjectVersions { const val launcherVersion = "2.2.0" - const val rlVersion = "1.6.10" + const val rlVersion = "1.6.11" - const val openosrsVersion = "3.2.1" + const val openosrsVersion = "3.2.2" const val openosrsInjectorVersion = "1.0.3.1" const val rsversion = 189 diff --git a/http-api/src/main/java/net/runelite/http/api/config/ConfigClient.java b/http-api/src/main/java/net/runelite/http/api/config/ConfigClient.java index fbdb2610e6..63fc7c444e 100644 --- a/http-api/src/main/java/net/runelite/http/api/config/ConfigClient.java +++ b/http-api/src/main/java/net/runelite/http/api/config/ConfigClient.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import net.runelite.http.api.RuneLiteAPI; import okhttp3.Call; import okhttp3.Callback; @@ -77,8 +78,10 @@ public class ConfigClient } } - public void set(String key, String value) + public CompletableFuture set(String key, String value) { + CompletableFuture future = new CompletableFuture<>(); + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() .addPathSegment("config") .addPathSegment(key) @@ -98,6 +101,7 @@ public class ConfigClient public void onFailure(Call call, IOException e) { logger.warn("Unable to synchronize configuration item", e); + future.completeExceptionally(e); } @Override @@ -105,12 +109,17 @@ public class ConfigClient { response.close(); logger.debug("Synchronized configuration value '{}' to '{}'", key, value); + future.complete(null); } }); + + return future; } - public void unset(String key) + public CompletableFuture unset(String key) { + CompletableFuture future = new CompletableFuture<>(); + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() .addPathSegment("config") .addPathSegment(key) @@ -130,6 +139,7 @@ public class ConfigClient public void onFailure(Call call, IOException e) { logger.warn("Unable to unset configuration item", e); + future.completeExceptionally(e); } @Override @@ -137,7 +147,10 @@ public class ConfigClient { response.close(); logger.debug("Unset configuration value '{}'", key); + future.complete(null); } }); + + return future; } } \ No newline at end of file diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java index 716798c3b1..eb6f2274f1 100644 --- a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java +++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java @@ -33,6 +33,7 @@ import java.io.InputStreamReader; import java.util.Collection; import java.util.List; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.runelite.http.api.RuneLiteAPI; @@ -52,8 +53,10 @@ public class LootTrackerClient private final UUID uuid; - public void submit(Collection lootRecords) + public CompletableFuture submit(Collection lootRecords) { + CompletableFuture future = new CompletableFuture<>(); + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() .addPathSegment("loottracker") .build(); @@ -71,6 +74,7 @@ public class LootTrackerClient public void onFailure(Call call, IOException e) { log.warn("unable to submit loot", e); + future.completeExceptionally(e); } @Override @@ -78,8 +82,11 @@ public class LootTrackerClient { log.debug("Submitted loot"); response.close(); + future.complete(null); } }); + + return future; } public Collection get() throws IOException diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index 6179f6ef69..943c2ae8a5 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -121,6 +121,10 @@ public final class AnimationID public static final int FISHING_BAREHAND_CAUGHT_SWORDFISH_2 = 6708; public static final int FISHING_BAREHAND_CAUGHT_TUNA_1 = 6710; public static final int FISHING_BAREHAND_CAUGHT_TUNA_2 = 6711; + public static final int FISHING_PEARL_ROD = 8188; + public static final int FISHING_PEARL_FLY_ROD = 8189; + public static final int FISHING_PEARL_BARBARIAN_ROD = 8190; + public static final int FISHING_PEARL_OILY_ROD = 6932; public static final int MINING_BRONZE_PICKAXE = 625; public static final int MINING_IRON_PICKAXE = 626; public static final int MINING_STEEL_PICKAXE = 627; 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 6049140268..7a0cedc82b 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -138,6 +138,13 @@ public interface Client extends GameShell */ void setGameState(GameState gameState); + /** + * Causes the client to shutdown. It is faster than + * {@link java.applet.Applet#stop()} because it doesn't wait for 4000ms. + * This will call {@link System#exit} when it is done + */ + void stopNow(); + /** * Gets the current logged in username. * diff --git a/runelite-api/src/main/java/net/runelite/api/ItemID.java b/runelite-api/src/main/java/net/runelite/api/ItemID.java index b467f8f689..4640a30447 100644 --- a/runelite-api/src/main/java/net/runelite/api/ItemID.java +++ b/runelite-api/src/main/java/net/runelite/api/ItemID.java @@ -11426,8 +11426,13 @@ public final class ItemID public static final int CARROT = 24546; public static final int BROKEN_GOAT_HORN = 24547; public static final int CAKE_24549 = 24549; - public static final int BLADE_OF_SAELDOR_I = 24551; - public static final int BLADE_OF_SAELDOR_I_24553 = 24553; + public static final int BLADE_OF_SAELDOR_C = 24551; + public static final int BLADE_OF_SAELDOR_C_24553 = 24553; public static final int PYROMANCER_SET = 24554; + public static final int TANGLEROOT_24555 = 24555; + public static final int TANGLEROOT_24557 = 24557; + public static final int TANGLEROOT_24559 = 24559; + public static final int TANGLEROOT_24561 = 24561; + public static final int TANGLEROOT_24563 = 24563; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/NpcID.java b/runelite-api/src/main/java/net/runelite/api/NpcID.java index fc3dfa8882..ffc9db2501 100644 --- a/runelite-api/src/main/java/net/runelite/api/NpcID.java +++ b/runelite-api/src/main/java/net/runelite/api/NpcID.java @@ -5993,7 +5993,6 @@ public final class NpcID public static final int SISTER_SCAROPHIA = 6590; public static final int LAVA_DRAGON = 6593; public static final int ENT = 6594; - public static final int THE_DISAPPOINTING_GELT = 6595; public static final int ZOMBIE_6596 = 6596; public static final int ZOMBIE_6597 = 6597; public static final int ZOMBIE_6598 = 6598; @@ -8483,21 +8482,17 @@ public final class NpcID public static final int SISTER_SENGA = 9471; public static final int SISTER_SENGA_9472 = 9472; public static final int ENT_TRUNK = 9474; - public static final int DUKE_RABBACIO = 9475; - public static final int PAWS = 9476; - public static final int RABBIT_CHEF = 9477; - public static final int GUARD_9478 = 9478; - public static final int MAGIC_EGG = 9479; - public static final int RABBIT_9480 = 9480; - public static final int RABBIT_9481 = 9481; - public static final int RABBIT_9482 = 9482; - public static final int RID = 9483; - public static final int FARMER_MAGGOT = 9484; - public static final int FATHER_BUCKEREK = 9485; - public static final int RESTLESS_GOAT = 9486; - public static final int SHOP_KEEPER_9487 = 9487; - public static final int BARTENDER_9488 = 9488; - public static final int COW_CALF_9489 = 9489; - public static final int CHICKEN_9490 = 9490; + public static final int TANGLEROOT_9492 = 9492; + public static final int TANGLEROOT_9493 = 9493; + public static final int TANGLEROOT_9494 = 9494; + public static final int TANGLEROOT_9495 = 9495; + public static final int TANGLEROOT_9496 = 9496; + public static final int TANGLEROOT_9497 = 9497; + public static final int TANGLEROOT_9498 = 9498; + public static final int TANGLEROOT_9499 = 9499; + public static final int TANGLEROOT_9500 = 9500; + public static final int TANGLEROOT_9501 = 9501; + public static final int IORWERTH_WARRIOR_9502 = 9502; + public static final int IORWERTH_WARRIOR_9503 = 9503; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/NullItemID.java b/runelite-api/src/main/java/net/runelite/api/NullItemID.java index d766b2c53e..2f5a108d49 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullItemID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullItemID.java @@ -12917,5 +12917,10 @@ public final class NullItemID public static final int NULL_24548 = 24548; public static final int NULL_24550 = 24550; public static final int NULL_24552 = 24552; + public static final int NULL_24556 = 24556; + public static final int NULL_24558 = 24558; + public static final int NULL_24560 = 24560; + public static final int NULL_24562 = 24562; + public static final int NULL_24564 = 24564; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java index 1eaf83e2d5..83135cc4af 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java @@ -14123,10 +14123,6 @@ public final class NullObjectID public static final int NULL_30144 = 30144; public static final int NULL_30156 = 30156; public static final int NULL_30159 = 30159; - public static final int NULL_30165 = 30165; - public static final int NULL_30166 = 30166; - public static final int NULL_30167 = 30167; - public static final int NULL_30168 = 30168; public static final int NULL_30171 = 30171; public static final int NULL_30181 = 30181; public static final int NULL_30182 = 30182; @@ -14981,7 +14977,6 @@ public final class NullObjectID public static final int NULL_31895 = 31895; public static final int NULL_31896 = 31896; public static final int NULL_31897 = 31897; - public static final int NULL_31899 = 31899; public static final int NULL_31937 = 31937; public static final int NULL_31938 = 31938; public static final int NULL_31945 = 31945; @@ -16166,7 +16161,6 @@ public final class NullObjectID public static final int NULL_34678 = 34678; public static final int NULL_34679 = 34679; public static final int NULL_34680 = 34680; - public static final int NULL_34688 = 34688; public static final int NULL_34689 = 34689; public static final int NULL_34690 = 34690; public static final int NULL_34691 = 34691; @@ -18168,7 +18162,6 @@ public final class NullObjectID public static final int NULL_37722 = 37722; public static final int NULL_37723 = 37723; public static final int NULL_37724 = 37724; - public static final int NULL_37725 = 37725; public static final int NULL_37732 = 37732; public static final int NULL_37733 = 37733; public static final int NULL_37734 = 37734; @@ -18336,11 +18329,7 @@ public final class NullObjectID public static final int NULL_37933 = 37933; public static final int NULL_37934 = 37934; public static final int NULL_37935 = 37935; - public static final int NULL_37940 = 37940; - public static final int NULL_37945 = 37945; - public static final int NULL_37947 = 37947; public static final int NULL_37950 = 37950; - public static final int NULL_37956 = 37956; public static final int NULL_37958 = 37958; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectID.java b/runelite-api/src/main/java/net/runelite/api/ObjectID.java index af1d77c7a5..d6b44f591e 100644 --- a/runelite-api/src/main/java/net/runelite/api/ObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/ObjectID.java @@ -15803,8 +15803,6 @@ public final class ObjectID public static final int POOL_OF_NIGHTMARES = 29706; public static final int POOL_OF_NIGHTMARES_29707 = 29707; public static final int SCOREBOARD_29708 = 29708; - public static final int MAGIC_PORTAL_29711 = 29711; - public static final int PAINT_BUCKETS = 29712; public static final int NOTICEBOARD_29718 = 29718; public static final int BALLISTA_29719 = 29719; public static final int STAIRCASE_29720 = 29720; @@ -16025,11 +16023,6 @@ public final class ObjectID public static final int PAINTING_30155 = 30155; public static final int FURNACE_30157 = 30157; public static final int FURNACE_30158 = 30158; - public static final int BROKEN_EGG = 30160; - public static final int CONCH_SHELL = 30161; - public static final int THE_MAGIC_EGG = 30162; - public static final int CAKE = 30163; - public static final int CARROT = 30164; public static final int CRACK_30169 = 30169; public static final int ROOT_30170 = 30170; public static final int PORTAL_30172 = 30172; @@ -19545,9 +19538,6 @@ public final class ObjectID public static final int NIGHTMARE_TOPIARY = 37630; public static final int SUPPLIES_37631 = 37631; public static final int BANNER_37726 = 37726; - public static final int RABBIT = 37727; - public static final int RABBIT_37728 = 37728; - public static final int GRAVE_37729 = 37729; public static final int ENERGY_BARRIER_37730 = 37730; public static final int ENERGY_BARRIER_37731 = 37731; public static final int SPORE = 37738; @@ -19590,19 +19580,8 @@ public final class ObjectID public static final int CRATE_37936 = 37936; public static final int DOOR_37937 = 37937; public static final int DOOR_37938 = 37938; - public static final int BROKEN_GOAT_HORN = 37939; - public static final int GATE_37941 = 37941; - public static final int GATE_37942 = 37942; - public static final int GATE_37943 = 37943; - public static final int GATE_37944 = 37944; - public static final int TREE_37946 = 37946; - public static final int DEAD_TREE_37948 = 37948; public static final int SCOREBOARD_37949 = 37949; public static final int COFFIN_37951 = 37951; - public static final int JUNGLE_TREE_37952 = 37952; - public static final int YEW_37953 = 37953; - public static final int TREE_STUMP_37954 = 37954; - public static final int FURNACE_37955 = 37955; public static final int HANDY_PORTAL = 37957; /* This file is automatically generated. Do not edit. */ } 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 fb02976579..05d1dd5432 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -308,4 +308,13 @@ public final class ScriptID */ @ScriptArguments(integer = 7) public static final int IGNORE_UPDATE = 630; + + /** + * Called in an onTimer, determines whether to layout the bank during a search + *
    + *
  • int (WidgetID) * 16, various widgets making up the bank interface
  • + *
+ */ + @ScriptArguments(integer = 16) + public static final int BANKMAIN_SEARCH_REFRESH = 283; } diff --git a/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java b/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java index 7ec8e5ecfa..5da3ca7e97 100644 --- a/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java @@ -27,10 +27,12 @@ package net.runelite.client; import io.reactivex.rxjava3.schedulers.Schedulers; import java.time.temporal.ChronoUnit; import java.util.UUID; +import java.util.concurrent.Future; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.events.ClientShutdown; import net.runelite.client.task.Schedule; @Singleton @@ -38,15 +40,23 @@ import net.runelite.client.task.Schedule; public class ClientSessionManager { private final SessionClient sessionClient; - private final ClientThread clientThread; private UUID sessionId; @Inject - ClientSessionManager(ClientThread clientThread) + ClientSessionManager(EventBus eventBus) { this.sessionClient = new SessionClient(); - this.clientThread = clientThread; + + eventBus.subscribe(ClientShutdown.class, this, (e) -> + { + Future f = shutdown(); + if (f != null) + { + e.waitFor(f); + } + sessionId = null; + }); } void start() @@ -54,7 +64,8 @@ public class ClientSessionManager sessionClient.openSession() .subscribeOn(Schedulers.io()) .observeOn(Schedulers.single()) - .subscribe(this::setUuid, this::error); + .doOnError(this::error) + .subscribe(this::setUuid); } @Schedule(period = 10, unit = ChronoUnit.MINUTES, asynchronous = true) @@ -73,18 +84,15 @@ public class ClientSessionManager .subscribe(); } - public void shutdown() + private Future shutdown() { if (sessionId != null) { - sessionClient.delete(sessionId) - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.single()) - .doOnError(this::error) - .subscribe(); + return sessionClient.delete(sessionId) + .toFuture(); - sessionId = null; } + return null; } private void setUuid(UUID uuid) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index ff14bcd04d..48598c0667 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -469,7 +469,7 @@ public class RuneLite // Initialize UI RuneLiteSplashScreen.stage(.80, "Initialize UI"); - clientUI.init(this); + clientUI.init(); // Initialize Discord service discordService.init(); @@ -599,13 +599,6 @@ public class RuneLite } } - public void shutdown() - { - clientSessionManager.shutdown(); - discordService.close(); - groups.close(); - } - private static class ConfigFileConverter implements ValueConverter { @Override diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index fd88f334b5..2da5c9e0e8 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -371,18 +371,6 @@ public interface RuneLiteConfig extends Config return false; } - @ConfigItem( - keyName = "infoBoxWrap", - name = "Infobox wrap count", - description = "Configures the amount of infoboxes shown before wrapping", - position = 29, - titleSection = "infoboxTitle" - ) - default int infoBoxWrap() - { - return 4; - } - @ConfigItem( keyName = "infoBoxSize", name = "Infobox size", diff --git a/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java b/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java new file mode 100644 index 0000000000..57b3fac470 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 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.events; + +import java.time.Duration; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.Event; + +@Value +@Slf4j +public class ClientShutdown implements Event +{ + private Queue> tasks = new ConcurrentLinkedQueue<>(); + + public void waitFor(Future future) + { + tasks.add(future); + } + + public void waitForAllConsumers(Duration totalTimeout) + { + long deadline = System.nanoTime() + totalTimeout.toNanos(); + for (Future task; (task = tasks.poll()) != null; ) + { + long timeout = deadline - System.nanoTime(); + if (timeout < 0) + { + log.warn("Timed out waiting for task completion"); + return; + } + + try + { + task.get(timeout, TimeUnit.NANOSECONDS); + } + catch (ThreadDeath d) + { + throw d; + } + catch (Throwable t) + { + log.warn("Error during shutdown: ", t); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java b/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java index d8e5c81a11..f9e2c4f07d 100644 --- a/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java +++ b/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java @@ -49,25 +49,52 @@ public class KeyManager public void processKeyPressed(KeyEvent keyEvent) { + if (keyEvent.isConsumed()) + { + return; + } + for (KeyListener keyListener : keyListeners) { keyListener.keyPressed(keyEvent); + if (keyEvent.isConsumed()) + { + break; + } } } public void processKeyReleased(KeyEvent keyEvent) { + if (keyEvent.isConsumed()) + { + return; + } + for (KeyListener keyListener : keyListeners) { keyListener.keyReleased(keyEvent); + if (keyEvent.isConsumed()) + { + break; + } } } public void processKeyTyped(KeyEvent keyEvent) { + if (keyEvent.isConsumed()) + { + return; + } + for (KeyListener keyListener : keyListeners) { keyListener.keyTyped(keyEvent); + if (keyEvent.isConsumed()) + { + break; + } } } } diff --git a/runelite-client/src/main/java/net/runelite/client/input/MouseManager.java b/runelite-client/src/main/java/net/runelite/client/input/MouseManager.java index 0357f4195d..052415e04f 100644 --- a/runelite-client/src/main/java/net/runelite/client/input/MouseManager.java +++ b/runelite-client/src/main/java/net/runelite/client/input/MouseManager.java @@ -82,30 +82,57 @@ public class MouseManager public MouseEvent processMousePressed(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + checkExtraMouseButtons(mouseEvent); for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mousePressed(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } public MouseEvent processMouseReleased(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + checkExtraMouseButtons(mouseEvent); for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mouseReleased(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } public MouseEvent processMouseClicked(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + checkExtraMouseButtons(mouseEvent); for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mouseClicked(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } @@ -123,45 +150,90 @@ public class MouseManager public MouseEvent processMouseEntered(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mouseEntered(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } public MouseEvent processMouseExited(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mouseExited(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } public MouseEvent processMouseDragged(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mouseDragged(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } public MouseEvent processMouseMoved(MouseEvent mouseEvent) { + if (mouseEvent.isConsumed()) + { + return mouseEvent; + } + for (MouseListener mouseListener : mouseListeners) { mouseEvent = mouseListener.mouseMoved(mouseEvent); + if (mouseEvent.isConsumed()) + { + break; + } } return mouseEvent; } public MouseWheelEvent processMouseWheelMoved(MouseWheelEvent mouseWheelEvent) { + if (mouseWheelEvent.isConsumed()) + { + return mouseWheelEvent; + } + for (MouseWheelListener mouseWheelListener : mouseWheelListeners) { mouseWheelEvent = mouseWheelListener.mouseWheelMoved(mouseWheelEvent); + if (mouseWheelEvent.isConsumed()) + { + break; + } } return mouseWheelEvent; } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index 31b0e65fdd..965c3675be 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -44,10 +44,13 @@ import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.Window; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.time.Duration; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Provider; @@ -72,7 +75,6 @@ import net.runelite.api.Point; import net.runelite.api.events.GameStateChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; -import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteProperties; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; @@ -80,6 +82,7 @@ import net.runelite.client.config.ExpandResizeType; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.config.WarningOnExit; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.events.ClientShutdown; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.NavigationButtonAdded; import net.runelite.client.events.NavigationButtonRemoved; @@ -131,6 +134,7 @@ public class ClientUI private final Applet client; private final ConfigManager configManager; private final Provider clientThreadProvider; + private final EventBus eventBus; private final CardLayout cardLayout = new CardLayout(); private final Rectangle sidebarButtonPosition = new Rectangle(); private boolean withTitleBar; @@ -144,6 +148,7 @@ public class ClientUI private NavigationButton sidebarNavigationButton; private JButton sidebarNavigationJButton; private Dimension lastClientSize; + private Cursor defaultCursor; private Field opacityField; private Field peerField; private Method setOpacityMethod; @@ -164,6 +169,7 @@ public class ClientUI this.client = client; this.configManager = configManager; this.clientThreadProvider = clientThreadProvider; + this.eventBus = eventbus; eventbus.subscribe(ConfigChanged.class, this, this::onConfigChanged); eventbus.subscribe(NavigationButtonAdded.class, this, this::onNavigationButtonAdded); @@ -313,11 +319,8 @@ public class ClientUI /** * Initialize UI. - * - * @param runelite runelite instance that will be shut down on exit - * @throws Exception exception that can occur during creation of the UI */ - public void init(final RuneLite runelite) throws Exception + public void init() throws Exception { SwingUtilities.invokeAndWait(() -> { @@ -335,14 +338,36 @@ public class ClientUI frame.setLocationRelativeTo(frame.getOwner()); frame.setResizable(true); - SwingUtil.addGracefulExitCallback(frame, - () -> + frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent event) { - saveClientBoundsConfig(); - runelite.shutdown(); - }, - this::showWarningOnExit - ); + int result = JOptionPane.OK_OPTION; + + if (showWarningOnExit()) + { + try + { + result = JOptionPane.showConfirmDialog( + frame, + "Are you sure you want to exit?", "Exit", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + } + catch (Exception e) + { + log.warn("Unexpected exception occurred while check for confirm required", e); + } + } + + if (result == JOptionPane.OK_OPTION) + { + shutdownClient(); + } + } + }); container = new JPanel(); container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); @@ -550,7 +575,7 @@ public class ClientUI SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame, "OpenOSRS has not yet been updated to work with the latest\n" + "game update, it will work with reduced functionality until then.", - "RuneLite is outdated", INFORMATION_MESSAGE)); + "OpenOSRS is outdated", INFORMATION_MESSAGE)); } } @@ -569,6 +594,45 @@ public class ClientUI return false; } + private void shutdownClient() + { + saveClientBoundsConfig(); + ClientShutdown csev = new ClientShutdown(); + eventBus.post(ClientShutdown.class, csev); + new Thread(() -> + { + csev.waitForAllConsumers(Duration.ofSeconds(10)); + + if (client != null) + { + // The client can call System.exit when it's done shutting down + // if it doesn't though, we want to exit anyway, so race it + int clientShutdownWaitMS; + if (client instanceof Client) + { + ((Client) client).stopNow(); + clientShutdownWaitMS = 1000; + } + else + { + // it will continue rendering for about 4 seconds before attempting shutdown if its vanilla + client.stop(); + frame.setVisible(false); + clientShutdownWaitMS = 6000; + } + + try + { + Thread.sleep(clientShutdownWaitMS); + } + catch (InterruptedException ignored) + { + } + } + System.exit(0); + }, "OpenOSRS Shutdown").start(); + } + /** * Paint this component to target graphics * @@ -654,7 +718,27 @@ public class ClientUI } /** - * Changes cursor for client window. Requires ${@link ClientUI#init(RuneLite)} to be called first. + * Returns current cursor set on game container + * + * @return awt cursor + */ + public Cursor getCurrentCursor() + { + return container.getCursor(); + } + + /** + * Returns current custom cursor or default system cursor if cursor is not set + * + * @return awt cursor + */ + public Cursor getDefaultCursor() + { + return defaultCursor != null ? defaultCursor : Cursor.getDefaultCursor(); + } + + /** + * Changes cursor for client window. Requires ${@link ClientUI#init()} to be called first. * FIXME: This is working properly only on Windows, Linux and Mac are displaying cursor incorrectly * * @param image cursor image @@ -669,7 +753,18 @@ public class ClientUI final java.awt.Point hotspot = new java.awt.Point(0, 0); final Cursor cursorAwt = Toolkit.getDefaultToolkit().createCustomCursor(image, hotspot, name); - container.setCursor(cursorAwt); + defaultCursor = cursorAwt; + setCursor(cursorAwt); + } + + /** + * Changes cursor for client window. Requires ${@link ClientUI#init()} to be called first. + * + * @param cursor awt cursor + */ + public void setCursor(final Cursor cursor) + { + container.setCursor(cursor); } /** @@ -684,6 +779,7 @@ public class ClientUI return; } + defaultCursor = null; container.setCursor(Cursor.getDefaultCursor()); } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java b/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java index 265c8d8c7a..e70a31b06d 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java @@ -128,6 +128,12 @@ public class FlatTextField extends JPanel setBackground(color, true); } + @Override + public boolean requestFocusInWindow() + { + return textField.requestFocusInWindow(); + } + public void setBackground(Color color, boolean saveColor) { if (color == null) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java index 18d995beb0..f5b372d4d8 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java @@ -49,6 +49,7 @@ public abstract class Overlay implements LayoutableRenderableEntity private OverlayPriority priority = OverlayPriority.NONE; private OverlayLayer layer = OverlayLayer.UNDER_WIDGETS; private final List menuEntries = new ArrayList<>(); + private boolean resizable; protected Overlay() { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java index beaf43f052..638c2a7d49 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java @@ -261,7 +261,7 @@ public class OverlayManager saveOverlay(overlay); } - private synchronized void rebuildOverlayLayers() + synchronized void rebuildOverlayLayers() { for (OverlayLayer l : OverlayLayer.values()) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java new file mode 100644 index 0000000000..4ccbdebc70 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.ui.overlay; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.components.ComponentConstants; +import net.runelite.client.ui.overlay.components.PanelComponent; + +@Getter +@Setter +public abstract class OverlayPanel extends Overlay +{ + protected final PanelComponent panelComponent = new PanelComponent(); + + /** + * Enables/disables automatic clearing of {@link this#getPanelComponent()} children after rendering (enabled by default) + */ + private boolean clearChildren = true; + + /** + * Enables/disables automatic font size changes based on panel component size relative to default panel component size. + */ + private boolean dynamicFont = false; + + protected OverlayPanel() + { + super(); + setResizable(true); + } + + protected OverlayPanel(Plugin plugin) + { + super(plugin); + setResizable(true); + } + + @Override + public Dimension render(final Graphics2D graphics) + { + final Dimension oldSize = panelComponent.getPreferredSize(); + + if (getPreferredSize() != null) + { + panelComponent.setPreferredSize(getPreferredSize()); + + if (dynamicFont) + { + if (getPreferredSize().width >= ComponentConstants.STANDARD_WIDTH * 1.3) + { + graphics.setFont(FontManager.getRunescapeBoldFont()); + } + else if (getPreferredSize().width <= ComponentConstants.STANDARD_WIDTH * 0.8) + { + graphics.setFont(FontManager.getRunescapeSmallFont()); + } + } + } + + final Dimension dimension = panelComponent.render(graphics); + + if (clearChildren) + { + panelComponent.getChildren().clear(); + } + + panelComponent.setPreferredSize(oldSize); + return dimension; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 17228f6b50..3ce28fe600 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -25,8 +25,10 @@ package net.runelite.client.ui.overlay; import com.google.common.base.MoreObjects; +import com.google.common.primitives.Ints; import java.awt.Color; import java.awt.Composite; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Paint; @@ -60,6 +62,7 @@ import net.runelite.client.input.KeyListener; import net.runelite.client.input.KeyManager; import net.runelite.client.input.MouseAdapter; import net.runelite.client.input.MouseManager; +import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.JagexColors; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.MiscUtils; @@ -71,21 +74,29 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private static final int BORDER = 5; private static final int BORDER_TOP = BORDER + 15; private static final int PADDING = 2; + private static final int MIN_OVERLAY_SIZE = 32; + private static final int OVERLAY_RESIZE_TOLERANCE = 5; private static final Dimension SNAP_CORNER_SIZE = new Dimension(80, 80); private static final Color SNAP_CORNER_COLOR = new Color(0, 255, 255, 50); private static final Color SNAP_CORNER_ACTIVE_COLOR = new Color(0, 255, 0, 100); private static final Color MOVING_OVERLAY_COLOR = new Color(255, 255, 0, 100); private static final Color MOVING_OVERLAY_ACTIVE_COLOR = new Color(255, 255, 0, 200); + private static final Color MOVING_OVERLAY_RESIZING_COLOR = new Color(255, 0, 255, 200); private final Client client; private final OverlayManager overlayManager; private final RuneLiteConfig runeLiteConfig; + private final ClientUI clientUI; // Overlay movement variables private final Point overlayOffset = new Point(); private final Point mousePosition = new Point(); - private Overlay movedOverlay; + private Overlay currentManagedOverlay; + private Rectangle currentManagedBounds; + private boolean inOverlayManagingMode; + private boolean inOverlayResizingMode; private boolean inOverlayDraggingMode; private boolean inMenuEntryMode; + private boolean startedMovingOverlay; private MenuEntry[] menuEntries; // Overlay state validation @@ -106,11 +117,13 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener final RuneLiteConfig runeLiteConfig, final MouseManager mouseManager, final KeyManager keyManager, - final EventBus eventbus) + final EventBus eventbus, + final ClientUI clientUI) { this.client = client; this.overlayManager = overlayManager; this.runeLiteConfig = runeLiteConfig; + this.clientUI = clientUI; keyManager.registerKeyListener(this); mouseManager.registerMouseListener(this); @@ -123,7 +136,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { if (!event.isFocused()) { - inOverlayDraggingMode = false; + inOverlayManagingMode = false; + resetOverlayManagementMode(); inMenuEntryMode = false; menuEntries = null; } @@ -202,7 +216,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener OverlayUtil.setGraphicProperties(graphics); // Draw snap corners - if (layer == OverlayLayer.UNDER_WIDGETS && movedOverlay != null && movedOverlay.getPosition() != OverlayPosition.DETACHED) + if (inOverlayDraggingMode && layer == OverlayLayer.UNDER_WIDGETS && currentManagedOverlay != null && currentManagedOverlay.getPosition() != OverlayPosition.DETACHED) { final OverlayBounds translatedSnapCorners = snapCorners.translated( -SNAP_CORNER_SIZE.width, @@ -294,12 +308,19 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener if (!bounds.isEmpty()) { - if (inOverlayDraggingMode) + if (inOverlayManagingMode) { - final Color previous = graphics.getColor(); - graphics.setColor(movedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR); + if (inOverlayResizingMode && currentManagedOverlay == overlay) + { + graphics.setColor(MOVING_OVERLAY_RESIZING_COLOR); + } + else + { + graphics.setColor(inOverlayDraggingMode && currentManagedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR); + } + graphics.draw(bounds); - graphics.setColor(previous); + graphics.setPaint(paint); } if (!client.isMenuOpen() && !client.isSpellSelected() && bounds.contains(mouse)) @@ -319,7 +340,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener @Override public MouseEvent mousePressed(MouseEvent mouseEvent) { - if (!inOverlayDraggingMode) + if (!inOverlayManagingMode) { return mouseEvent; } @@ -327,71 +348,205 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener final Point mousePoint = mouseEvent.getPoint(); mousePosition.setLocation(mousePoint); - synchronized (overlayManager) + if (currentManagedOverlay == null) { - for (Overlay overlay : overlayManager.getOverlays()) + return mouseEvent; + } + + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + overlayManager.resetOverlay(currentManagedOverlay); + } + else if (SwingUtilities.isLeftMouseButton(mouseEvent)) + { + final Point offset = new Point(mousePoint.x, mousePoint.y); + offset.translate(-currentManagedOverlay.getBounds().x, -currentManagedOverlay.getBounds().y); + overlayOffset.setLocation(offset); + + inOverlayResizingMode = currentManagedOverlay != null && currentManagedOverlay.isResizable() && clientUI.getCurrentCursor() != clientUI.getDefaultCursor(); + inOverlayDraggingMode = !inOverlayResizingMode; + startedMovingOverlay = true; + currentManagedBounds = new Rectangle(currentManagedOverlay.getBounds()); + } + else + { + return mouseEvent; + } + + mouseEvent.consume(); + return mouseEvent; + } + + @Override + public MouseEvent mouseMoved(MouseEvent mouseEvent) + { + if (!inOverlayManagingMode) + { + return mouseEvent; + } + + final Point mousePoint = mouseEvent.getPoint(); + mousePosition.setLocation(mousePoint); + + if (!inOverlayResizingMode && !inOverlayDraggingMode) + { + currentManagedOverlay = null; + + synchronized (overlayManager) { - final OverlayPosition overlayPosition = getCorrectedOverlayPosition(overlay); - - if (overlayPosition == OverlayPosition.DYNAMIC || overlayPosition == OverlayPosition.TOOLTIP) + for (Overlay overlay : overlayManager.getOverlays()) { - continue; - } - - if (overlay.getBounds().contains(mousePoint)) - { - if (SwingUtilities.isRightMouseButton(mouseEvent)) + final Rectangle bounds = overlay.getBounds(); + if (bounds.contains(mousePoint)) { - overlayManager.resetOverlay(overlay); + currentManagedOverlay = overlay; + break; } - else - { - final Point offset = new Point(mousePoint.x, mousePoint.y); - offset.translate(-overlay.getBounds().x, -overlay.getBounds().y); - overlayOffset.setLocation(offset); - - mousePoint.translate(-offset.x, -offset.y); - movedOverlay = overlay; - movedOverlay.setPreferredPosition(null); - movedOverlay.setPreferredLocation(mousePoint); - overlayManager.saveOverlay(movedOverlay); - } - - mouseEvent.consume(); - break; } } } + if (currentManagedOverlay == null || !currentManagedOverlay.isResizable()) + { + clientUI.setCursor(clientUI.getDefaultCursor()); + return mouseEvent; + } + + final Rectangle toleranceRect = new Rectangle(currentManagedOverlay.getBounds()); + toleranceRect.grow(-OVERLAY_RESIZE_TOLERANCE, -OVERLAY_RESIZE_TOLERANCE); + final int outcode = toleranceRect.outcode(mouseEvent.getPoint()); + + switch (outcode) + { + case Rectangle.OUT_TOP: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); + break; + case Rectangle.OUT_TOP | Rectangle.OUT_LEFT: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); + break; + case Rectangle.OUT_LEFT: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); + break; + case Rectangle.OUT_LEFT | Rectangle.OUT_BOTTOM: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)); + break; + case Rectangle.OUT_BOTTOM: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); + break; + case Rectangle.OUT_BOTTOM | Rectangle.OUT_RIGHT: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); + break; + case Rectangle.OUT_RIGHT: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); + break; + case Rectangle.OUT_RIGHT | Rectangle.OUT_TOP: + clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); + break; + default: + // center + clientUI.setCursor(clientUI.getDefaultCursor()); + } + return mouseEvent; } @Override public MouseEvent mouseDragged(MouseEvent mouseEvent) { - if (!inOverlayDraggingMode) + if (!inOverlayManagingMode) + { + return mouseEvent; + } + + final Point p = mouseEvent.getPoint(); + mousePosition.setLocation(p); + + if (currentManagedOverlay == null) { return mouseEvent; } - final Point mousePoint = mouseEvent.getPoint(); - mousePosition.setLocation(mousePoint); final Rectangle canvasRect = new Rectangle(client.getRealDimensions()); - if (!canvasRect.contains(mousePoint)) + if (!canvasRect.contains(p)) { return mouseEvent; } - if (movedOverlay != null) + if (inOverlayResizingMode) + { + final Rectangle r = currentManagedBounds; + + final int dx = p.x - r.x; + final int dy = p.y - r.y; + switch (clientUI.getCurrentCursor().getType()) + { + case Cursor.N_RESIZE_CURSOR: + int height = r.height - dy; + r.setRect(r.x, r.y + dy, r.width, height); + break; + case Cursor.NW_RESIZE_CURSOR: + int width = r.width - dx; + height = r.height - dy; + r.setRect(r.x + dx, r.y + dy, width, height); + break; + case Cursor.W_RESIZE_CURSOR: + width = r.width - dx; + r.setRect(r.x + dx, r.y, width, r.height); + break; + case Cursor.SW_RESIZE_CURSOR: + width = r.width - dx; + height = dy; + r.setRect(r.x + dx, r.y, width, height); + break; + case Cursor.S_RESIZE_CURSOR: + height = dy; + r.setRect(r.x, r.y, r.width, height); + break; + case Cursor.SE_RESIZE_CURSOR: + width = dx; + height = dy; + r.setRect(r.x, r.y, width, height); + break; + case Cursor.E_RESIZE_CURSOR: + width = dx; + r.setRect(r.x, r.y, width, r.height); + break; + case Cursor.NE_RESIZE_CURSOR: + width = dx; + height = r.height - dy; + r.setRect(r.x, r.y + dy, width, height); + break; + default: + // center + } + + currentManagedOverlay.setPreferredSize(new Dimension(Math.max(MIN_OVERLAY_SIZE, currentManagedBounds.width), Math.max(MIN_OVERLAY_SIZE, currentManagedBounds.height))); + + if (currentManagedOverlay.getPreferredLocation() != null) + { + currentManagedOverlay.setPreferredLocation(currentManagedBounds.getLocation()); + } + } + else if (inOverlayDraggingMode) { final Dimension realDimension = client.getRealDimensions(); - mousePoint.translate(-overlayOffset.x, -overlayOffset.y); - mousePoint.x = MiscUtils.clamp(mousePoint.x, 0, Math.max(0, realDimension.width - movedOverlay.getBounds().width)); - mousePoint.y = MiscUtils.clamp(mousePoint.y, 0, Math.max(0, realDimension.height - movedOverlay.getBounds().height)); - movedOverlay.setPreferredPosition(null); - movedOverlay.setPreferredLocation(mousePoint); - mouseEvent.consume(); + p.translate(-overlayOffset.x, -overlayOffset.y); + p.x = Ints.constrainToRange(p.x, 0, Math.max(0, realDimension.width - currentManagedOverlay.getBounds().width)); + p.y = Ints.constrainToRange(p.y, 0, Math.max(0, realDimension.height - currentManagedOverlay.getBounds().height)); + currentManagedOverlay.setPreferredPosition(null); + currentManagedOverlay.setPreferredLocation(p); + } + else + { + return mouseEvent; + } + + if (startedMovingOverlay) + { + // Move currently moved overlay to correct layer + overlayManager.rebuildOverlayLayers(); + startedMovingOverlay = false; } return mouseEvent; @@ -400,39 +555,40 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener @Override public MouseEvent mouseReleased(MouseEvent mouseEvent) { - if (movedOverlay != null) + if (!inOverlayManagingMode || currentManagedOverlay == null || (!inOverlayDraggingMode && !inOverlayResizingMode)) { - mousePosition.setLocation(-1, -1); - - // do not snapcorner detached overlays - if (movedOverlay.getPosition() != OverlayPosition.DETACHED) - { - final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height); - - for (Rectangle snapCorner : snapCorners.getBounds()) - { - if (snapCorner.contains(mouseEvent.getPoint())) - { - OverlayPosition position = snapCorners.fromBounds(snapCorner); - - if (position == movedOverlay.getPosition()) - { - // overlay moves back to default position - position = null; - } - - movedOverlay.setPreferredPosition(position); - movedOverlay.setPreferredLocation(null); // from dragging - break; - } - } - } - - overlayManager.saveOverlay(movedOverlay); - movedOverlay = null; - mouseEvent.consume(); + return mouseEvent; } + mousePosition.setLocation(-1, -1); + + // do not snapcorner detached overlays + if (currentManagedOverlay.getPosition() != OverlayPosition.DETACHED && inOverlayDraggingMode) + { + final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height); + + for (Rectangle snapCorner : snapCorners.getBounds()) + { + if (snapCorner.contains(mouseEvent.getPoint())) + { + OverlayPosition position = snapCorners.fromBounds(snapCorner); + + if (position == currentManagedOverlay.getPosition()) + { + // overlay moves back to default position + position = null; + } + + currentManagedOverlay.setPreferredPosition(position); + currentManagedOverlay.setPreferredLocation(null); // from dragging + break; + } + } + } + + overlayManager.saveOverlay(currentManagedOverlay); + resetOverlayManagementMode(); + mouseEvent.consume(); return mouseEvent; } @@ -446,7 +602,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { if (e.isAltDown()) { - inOverlayDraggingMode = true; + inOverlayManagingMode = true; } if (e.isShiftDown() && runeLiteConfig.menuEntryShift()) @@ -460,7 +616,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { if (!e.isAltDown()) { - inOverlayDraggingMode = false; + inOverlayManagingMode = false; + resetOverlayManagementMode(); } if (!e.isShiftDown()) @@ -546,6 +703,15 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return overlayPosition; } + private void resetOverlayManagementMode() + { + inOverlayResizingMode = false; + inOverlayDraggingMode = false; + currentManagedOverlay = null; + currentManagedBounds = null; + clientUI.setCursor(clientUI.getDefaultCursor()); + } + private boolean shouldInvalidateBounds() { final Widget chatbox = client.getWidget(WidgetInfo.CHATBOX); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java index bbdac78011..908db8eef7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java @@ -45,6 +45,7 @@ public class PanelComponent implements LayoutableRenderableEntity private Point preferredLocation = new Point(); @Setter + @Getter private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); @Setter @@ -58,7 +59,7 @@ public class PanelComponent implements LayoutableRenderableEntity private ComponentOrientation orientation = ComponentOrientation.VERTICAL; @Setter - private int wrapping = -1; + private boolean wrap = false; @Setter private Rectangle border = new Rectangle( @@ -114,12 +115,23 @@ public class PanelComponent implements LayoutableRenderableEntity int totalWidth = 0; // Render all children - for (int i = 0; i < children.size(); i++) + for (final LayoutableRenderableEntity child : children) { - final LayoutableRenderableEntity child = children.get(i); + // Correctly propagate child dimensions based on orientation and wrapping + if (!wrap) + { + switch (orientation) + { + case VERTICAL: + child.setPreferredSize(new Dimension(childPreferredSize.width, 0)); + break; + case HORIZONTAL: + child.setPreferredSize(new Dimension(0, childPreferredSize.height)); + break; + } + } child.setPreferredLocation(new Point(x, y)); - child.setPreferredSize(childPreferredSize); final Dimension childDimension = child.render(graphics); switch (orientation) @@ -140,28 +152,38 @@ public class PanelComponent implements LayoutableRenderableEntity totalWidth = Math.max(totalWidth, width); totalHeight = Math.max(totalHeight, height); - if (wrapping > 0 && i < children.size() - 1 && (i + 1) % wrapping == 0) + if (!wrap) { - switch (orientation) + continue; + } + + switch (orientation) + { + case VERTICAL: { - case VERTICAL: + if (childPreferredSize.height > 0 && height >= childPreferredSize.height) { height = 0; y = baseY; int diff = childDimension.width + gap.x; x += diff; width += diff; - break; } - case HORIZONTAL: + + break; + } + case HORIZONTAL: + { + if (childPreferredSize.width > 0 && width >= childPreferredSize.width) { width = 0; x = baseX; int diff = childDimension.height + gap.y; y += diff; height += diff; - break; } + + break; } } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java index 08412783f1..b917a38e6b 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java @@ -36,19 +36,20 @@ import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; import net.runelite.client.config.RuneLiteConfig; -import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.components.ComponentOrientation; import net.runelite.client.ui.overlay.components.InfoBoxComponent; import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; -import net.runelite.client.ui.overlay.components.PanelComponent; import net.runelite.client.ui.overlay.tooltip.Tooltip; import net.runelite.client.ui.overlay.tooltip.TooltipManager; @Singleton -public class InfoBoxOverlay extends Overlay +public class InfoBoxOverlay extends OverlayPanel { - private final PanelComponent panelComponent = new PanelComponent(); + private static final int GAP = 1; + private static final int DEFAULT_WRAP_COUNT = 4; + private final InfoBoxManager infoboxManager; private final TooltipManager tooltipManager; private final Client client; @@ -66,10 +67,12 @@ public class InfoBoxOverlay extends Overlay this.client = client; this.config = config; setPosition(OverlayPosition.TOP_LEFT); + setClearChildren(false); + panelComponent.setWrap(true); panelComponent.setBackgroundColor(null); panelComponent.setBorder(new Rectangle()); - panelComponent.setGap(new Point(1, 1)); + panelComponent.setGap(new Point(GAP, GAP)); } @Override @@ -82,12 +85,12 @@ public class InfoBoxOverlay extends Overlay return null; } - panelComponent.getChildren().clear(); - panelComponent.setWrapping(config.infoBoxWrap()); + // Set preferred size to the size of DEFAULT_WRAP_COUNT infoboxes, including the padding - which is applied + // to the last infobox prior to wrapping too. + panelComponent.setPreferredSize(new Dimension(DEFAULT_WRAP_COUNT * (config.infoBoxSize() + GAP), DEFAULT_WRAP_COUNT * (config.infoBoxSize() + GAP))); panelComponent.setOrientation(config.infoBoxVertical() ? ComponentOrientation.VERTICAL : ComponentOrientation.HORIZONTAL); - panelComponent.setPreferredSize(new Dimension(config.infoBoxSize(), config.infoBoxSize())); for (InfoBox box : infoBoxes) { @@ -107,10 +110,11 @@ public class InfoBoxOverlay extends Overlay } infoBoxComponent.setImage(box.getScaledImage()); infoBoxComponent.setTooltip(box.getTooltip()); + infoBoxComponent.setPreferredSize(new Dimension(config.infoBoxSize(), config.infoBoxSize())); panelComponent.getChildren().add(infoBoxComponent); } - final Dimension dimension = panelComponent.render(graphics); + final Dimension dimension = super.render(graphics); // Handle tooltips final Point mouse = new Point(client.getMouseCanvasPosition().getX(), client.getMouseCanvasPosition().getY()); @@ -135,6 +139,7 @@ public class InfoBoxOverlay extends Overlay } } + panelComponent.getChildren().clear(); return dimension; } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/tooltip/TooltipManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/tooltip/TooltipManager.java index 9ed020b72b..6472381ec3 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/tooltip/TooltipManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/tooltip/TooltipManager.java @@ -24,27 +24,14 @@ */ package net.runelite.client.ui.overlay.tooltip; -import com.google.common.base.Strings; import java.util.ArrayList; import java.util.List; -import javax.inject.Inject; import javax.inject.Singleton; import lombok.Getter; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; @Singleton public class TooltipManager { - private final ConfigManager configManager; - - @Inject - private TooltipManager(final ConfigManager configManager) - { - this.configManager = configManager; - } - @Getter private final List tooltips = new ArrayList<>(); @@ -62,17 +49,4 @@ public class TooltipManager { tooltips.clear(); } - - @Subscribe - public void onConfigChanged(final ConfigChanged event) - { - // Temporary fix for resetting repositioned tooltip overlay - // TODO: Remove this eventually - if (event.getGroup().equals("runelite") && - event.getKey().equals("TooltipOverlay_preferredLocation") && - !Strings.isNullOrEmpty(event.getNewValue())) - { - configManager.unsetConfiguration(event.getGroup(), event.getKey()); - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/Groups.java b/runelite-client/src/main/java/net/runelite/client/util/Groups.java index 2984bcae46..53e243a928 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/Groups.java +++ b/runelite-client/src/main/java/net/runelite/client/util/Groups.java @@ -5,6 +5,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import javax.inject.Inject; import javax.inject.Singleton; import lombok.AccessLevel; @@ -12,6 +14,8 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLite; import net.runelite.client.config.OpenOSRSConfig; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.events.ClientShutdown; import net.runelite.client.events.ConfigChanged; import net.runelite.client.ui.RuneLiteSplashScreen; import org.jgroups.Address; @@ -38,7 +42,7 @@ public class Groups extends ReceiverAdapter private final PublishSubject messageObjectSubject = PublishSubject.create(); @Inject - public Groups(OpenOSRSConfig openOSRSConfig) throws Exception + public Groups(OpenOSRSConfig openOSRSConfig, EventBus eventBus) throws Exception { this.openOSRSConfig = openOSRSConfig; @@ -47,6 +51,11 @@ public class Groups extends ReceiverAdapter .setReceiver(this) .setDiscardOwnMessages(true) .connect("openosrs"); + + eventBus.subscribe(ClientShutdown.class, this, (e) -> { + Future f = close(); + e.waitFor(f); + }); } public void broadcastSring(String command) @@ -134,10 +143,22 @@ public class Groups extends ReceiverAdapter { messageObjectSubject.onNext(message); } + } - public void close() + private CompletableFuture close() { - channel.close(); + CompletableFuture future = new CompletableFuture<>(); + try + { + channel.close(); + future.complete(null); + } + catch (Exception ex) + { + future.completeExceptionally(ex); + } + + return future; } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java index f551628639..e690c4065c 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -41,22 +41,18 @@ import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.lang.reflect.InvocationTargetException; import java.util.Enumeration; -import java.util.concurrent.Callable; import java.util.function.BiConsumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.swing.ButtonModel; import javax.swing.AbstractButton; +import javax.swing.ButtonModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; -import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; @@ -67,7 +63,6 @@ import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; -import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; @@ -214,48 +209,6 @@ public class SwingUtil return trayIcon; } - /** - * Add graceful exit callback. - * - * @param frame the frame - * @param callback the callback - * @param confirmRequired the confirm required - */ - public static void addGracefulExitCallback(@Nonnull final JFrame frame, @Nonnull final Runnable callback, @Nonnull final Callable confirmRequired) - { - frame.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - frame.addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent event) - { - int result = JOptionPane.OK_OPTION; - - try - { - if (confirmRequired.call()) - { - result = JOptionPane.showConfirmDialog( - frame, - "Are you sure you want to exit?", "Exit", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE); - } - } - catch (Exception e) - { - log.warn("Unexpected exception occurred while check for confirm required", e); - } - - if (result == JOptionPane.OK_OPTION) - { - callback.run(); - System.exit(0); - } - } - }); - } - /** * Create swing button from navigation button. * diff --git a/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java b/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java index 9fedf84a6d..98ac463ec2 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java +++ b/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java @@ -83,7 +83,7 @@ public class WeaponMap StyleMap.put(ItemID.BLACK_SWORD, WeaponStyle.MELEE); StyleMap.put(ItemID.BLACK_WARHAMMER, WeaponStyle.MELEE); StyleMap.put(ItemID.BLADE_OF_SAELDOR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLADE_OF_SAELDOR_I, WeaponStyle.MELEE); + StyleMap.put(ItemID.BLADE_OF_SAELDOR_C, WeaponStyle.MELEE); StyleMap.put(ItemID.BLADE_OF_SAELDOR_INACTIVE, WeaponStyle.MELEE); StyleMap.put(ItemID.BLESSED_AXE, WeaponStyle.MELEE); StyleMap.put(ItemID.BLUE_FLOWERS, WeaponStyle.MELEE); diff --git a/runelite-client/src/main/resources/item_variations.json b/runelite-client/src/main/resources/item_variations.json index f11cb25e32..a6af42fc1e 100644 --- a/runelite-client/src/main/resources/item_variations.json +++ b/runelite-client/src/main/resources/item_variations.json @@ -8706,6 +8706,14 @@ 20621, 20622 ], + "tangleroot": [ + 20661, + 24555, + 24557, + 24559, + 24561, + 24563 + ], "rift guardian": [ 20665, 20667, 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 c23ed58d5c..9ff781927b 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -1930,5 +1930,12 @@ public abstract class RSClientMixin implements RSClient { invertYaw = state; } + + @Inject + @Override + public void stopNow() + { + setStopTimeMs(1); + } } 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 281c5cee59..113fd53b38 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 @@ -1245,4 +1245,7 @@ public interface RSClient extends RSGameShell, Client @Import("showLoadingMessages") void setShowLoadingMessages(boolean showLoadingMessages); + + @Import("stopTimeMs") + void setStopTimeMs(long time); }