diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/InventoryPane.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/InventoryPane.java
index a41a72754..1010f749e 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/InventoryPane.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/InventoryPane.java
@@ -49,6 +49,7 @@ import com.watabou.noosa.NinePatch;
import com.watabou.noosa.PointerArea;
import com.watabou.noosa.ui.Component;
import com.watabou.utils.Point;
+import com.watabou.utils.PointF;
import java.util.ArrayList;
@@ -130,44 +131,7 @@ public class InventoryPane extends Component {
equipped = new ArrayList<>();
for (int i = 0; i < 5; i++){
- InventorySlot btn = new InventorySlot(null){
- @Override
- protected void onClick() {
- if (lastBag != item && !lastBag.contains(item) && !item.isEquipped(Dungeon.hero)){
- updateInventory();
- return;
- }
-
- if (targeting){
- if (targetingSlot == this){
- int cell = QuickSlotButton.autoAim(lastTarget, item());
-
- if (cell != -1){
- GameScene.handleCell(cell);
- } else {
- //couldn't auto-aim, just target the position and hope for the best.
- GameScene.handleCell( lastTarget.pos );
- }
- return;
- } else {
- cancelTargeting();
- }
- }
-
- //any windows opened as a consequence of this button should be centered on the inventory
- GameScene.lastOffset = new Point((int)InventoryPane.this.centerX() - camera.width/2,
- (int)InventoryPane.this.centerY() - camera.height/2);
- if (selector != null) {
- WndBag.ItemSelector activating = selector;
- selector = null;
- activating.onSelect( item );
- updateInventory();
- } else {
- targetingSlot = this;
- GameScene.show(new WndUseItem( null, item ));
- }
- }
- };
+ InventorySlot btn = new InventoryPaneSlot(null);
equipped.add(btn);
add(btn);
}
@@ -190,44 +154,7 @@ public class InventoryPane extends Component {
bagItems = new ArrayList<>();
for (int i = 0; i < 20; i++){
- InventorySlot btn = new InventorySlot(null){
- @Override
- protected void onClick() {
- if (lastBag != item && !lastBag.contains(item) && !item.isEquipped(Dungeon.hero)){
- updateInventory();
- return;
- }
-
- if (targeting){
- if (targetingSlot == this){
- int cell = QuickSlotButton.autoAim(lastTarget, item());
-
- if (cell != -1){
- GameScene.handleCell(cell);
- } else {
- //couldn't auto-aim, just target the position and hope for the best.
- GameScene.handleCell( lastTarget.pos );
- }
- return;
- } else {
- cancelTargeting();
- }
- }
-
- //any windows opened as a consequence of this button should be centered on the inventory
- GameScene.lastOffset = new Point((int)InventoryPane.this.centerX() - camera.width/2,
- (int)InventoryPane.this.centerY() - camera.height/2);
- if (selector != null) {
- WndBag.ItemSelector activating = selector;
- selector = null;
- activating.onSelect( item );
- updateInventory();
- } else {
- targetingSlot = this;
- GameScene.show(new WndUseItem( null, item ));
- }
- }
- };
+ InventorySlot btn = new InventoryPaneSlot(null);
bagItems.add(btn);
add(btn);
}
@@ -491,6 +418,103 @@ public class InventoryPane extends Component {
}
}
+ private class InventoryPaneSlot extends InventorySlot {
+
+ private InventoryPaneSlot( Item item ){
+ super(item);
+ }
+
+ @Override
+ protected void onClick() {
+ if (lastBag != item && !lastBag.contains(item) && !item.isEquipped(Dungeon.hero)){
+ updateInventory();
+ return;
+ }
+
+ if (targeting){
+ if (targetingSlot == this){
+ int cell = QuickSlotButton.autoAim(lastTarget, item());
+
+ if (cell != -1){
+ GameScene.handleCell(cell);
+ } else {
+ //couldn't auto-aim, just target the position and hope for the best.
+ GameScene.handleCell( lastTarget.pos );
+ }
+ return;
+ } else {
+ cancelTargeting();
+ }
+ }
+
+ //any windows opened as a consequence of this button should be centered on the inventory
+ GameScene.lastOffset = new Point((int)InventoryPane.this.centerX() - camera.width/2,
+ (int)InventoryPane.this.centerY() - camera.height/2);
+ if (selector != null) {
+ WndBag.ItemSelector activating = selector;
+ selector = null;
+ activating.onSelect( item );
+ updateInventory();
+ } else {
+ targetingSlot = this;
+ GameScene.show(new WndUseItem( null, item ));
+ }
+ }
+
+ @Override
+ protected void onMiddleClick() {
+ if (lastBag != item && !lastBag.contains(item) && !item.isEquipped(Dungeon.hero)){
+ updateInventory();
+ return;
+ }
+
+ if (targeting){
+ if (targetingSlot == this){
+ onClick();
+ } else{
+ cancelTargeting();
+ }
+ }
+
+ if (selector == null && item.defaultAction != null){
+ item.execute(Dungeon.hero);
+ if (item.usesTargeting) {
+ targetingSlot = this;
+ InventoryPane.useTargeting();
+ }
+ } else {
+ super.onMiddleClick();
+ }
+ }
+
+ @Override
+ protected void onRightClick() {
+ if (lastBag != item && !lastBag.contains(item) && !item.isEquipped(Dungeon.hero)){
+ updateInventory();
+ return;
+ }
+
+ if (targeting){
+ if (targetingSlot == this){
+ onClick();
+ } else{
+ cancelTargeting();
+ }
+ }
+
+ if (selector == null){
+ RightClickMenu r = new RightClickMenu(item);
+ parent.addToFront(r);
+ r.camera = camera();
+ PointF mousePos = PointerEvent.currentHoverPos();
+ mousePos = camera.screenToCamera((int)mousePos.x, (int)mousePos.y);
+ r.setPos(mousePos.x-3, mousePos.y-3);
+ } else {
+ super.onRightClick();
+ }
+ }
+ }
+
private class BagButton extends IconButton {
private static final int ACTIVE = 0x9953564D;
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/RightClickMenu.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/RightClickMenu.java
new file mode 100644
index 000000000..5311ea878
--- /dev/null
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/RightClickMenu.java
@@ -0,0 +1,159 @@
+/*
+ * Pixel Dungeon
+ * Copyright (C) 2012-2015 Oleg Dolya
+ *
+ * Shattered Pixel Dungeon
+ * Copyright (C) 2014-2022 Evan Debenham
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ */
+
+package com.shatteredpixel.shatteredpixeldungeon.ui;
+
+import com.shatteredpixel.shatteredpixeldungeon.Chrome;
+import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
+import com.shatteredpixel.shatteredpixeldungeon.items.Item;
+import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
+import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
+import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
+import com.shatteredpixel.shatteredpixeldungeon.windows.IconTitle;
+import com.watabou.input.PointerEvent;
+import com.watabou.noosa.ColorBlock;
+import com.watabou.noosa.Image;
+import com.watabou.noosa.NinePatch;
+import com.watabou.noosa.PointerArea;
+import com.watabou.noosa.ui.Component;
+
+import java.util.ArrayList;
+
+public class RightClickMenu extends Component {
+
+ private NinePatch bg;
+ private PointerArea blocker;
+
+ private Image icon;
+ private RenderedTextBlock titleText;
+ private ColorBlock separator;
+
+ private RedButton[] buttons;
+
+ private Item item;
+
+ public RightClickMenu(Item item){
+ ArrayList actions = item.actions(Dungeon.hero);
+ if (actions.remove(item.defaultAction)) {
+ actions.add(0, item.defaultAction);
+ }
+ String[] options = actions.toArray(new String[0]);
+ this.item = item;
+ setup(new ItemSprite(item), Messages.titleCase(item.name()), options);
+ }
+
+ public RightClickMenu(Image icon, String title, String... options){
+ setup(icon, title, options);
+ }
+
+ private void setup(Image icon, String title, String... options){
+ bg = Chrome.get(Chrome.Type.TOAST_TR);
+ add(bg);
+
+ this.icon = icon;
+ add(icon);
+
+ titleText = PixelScene.renderTextBlock(title, 6);
+ titleText.maxWidth(50);
+ titleText.hardlight(Window.TITLE_COLOR);
+ add(titleText);
+
+ separator = new ColorBlock(1, 1, 0xFF000000);
+ add(separator);
+
+ buttons = new RedButton[options.length];
+ for (int i = 0; i < options.length; i++){
+ int finalI = i;
+ buttons[i] = new RedButton(options[finalI], 6){
+ @Override
+ protected void onClick() {
+ super.onClick();
+ if (item != null){
+ item.execute(Dungeon.hero, options[finalI]);
+ } else {
+ onSelect(finalI);
+ }
+ RightClickMenu.this.destroy();
+ RightClickMenu.this.killAndErase();
+ }
+ };
+ if (item != null && options[i].equals(item.defaultAction)){
+ buttons[i].textColor(Window.TITLE_COLOR);
+ }
+ add(buttons[i]);
+ }
+
+ blocker = new PointerArea(0, 0, 0, 0){
+ @Override
+ protected void onHoverEnd(PointerEvent event) {
+ RightClickMenu.this.destroy();
+ RightClickMenu.this.killAndErase();
+ }
+ };
+ blocker.blockLevel = PointerArea.NEVER_BLOCK;
+ blocker.target = bg;
+ add(blocker);
+
+ }
+
+ public void onSelect(int index){}
+
+ @Override
+ protected void layout() {
+ super.layout();
+
+ height = 0;
+ height += bg.marginVer();
+ height += Math.max(icon.height(), titleText.height());
+ height += 2;
+ height += 13*buttons.length;
+
+ width = icon.width + 2 + titleText.width()+bg.marginVer();
+
+ if (x + width > camera.width){
+ x -= (x + width - camera.width);
+ }
+ if (y + height > camera.height){
+ y -= (y + height - camera.height);
+ }
+
+ bg.x = x;
+ bg.y = y;
+
+ icon.x = x+bg.marginLeft();
+ icon.y = y+bg.marginTop();
+
+ titleText.setPos(icon.x+icon.width()+2, icon.y + (icon.height()- titleText.height())/2);
+
+ separator.x = x+bg.marginLeft();
+ separator.y = Math.max(icon.y + icon.height(), titleText.bottom()) + 1;
+ separator.size(width - bg.marginHor(), 1);
+
+ float top = separator.y + 2;
+ for (RedButton button : buttons){
+ button.setRect(x + bg.marginLeft(), top, width-bg.marginHor(), 12);
+ top = button.bottom()+1;
+ }
+
+ bg.size(width, height);
+
+ }
+}