chatbox: add ChatboxItemSearch

This commit is contained in:
Ron Young
2019-03-29 23:29:37 -05:00
committed by Adam
parent 4aa0cb14b7
commit bcbbb3182d
2 changed files with 315 additions and 1 deletions

View File

@@ -0,0 +1,314 @@
/*
* Copyright (c) 2019, Ron Young <https://github.com/raiyni>
* 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.game.chatbox;
import com.google.common.primitives.Ints;
import com.google.inject.Inject;
import java.awt.event.KeyEvent;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import javax.inject.Singleton;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.ItemComposition;
import net.runelite.api.widgets.ItemQuantityMode;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetSizeMode;
import net.runelite.api.widgets.WidgetTextAlignment;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.game.ItemManager;
@Singleton
public class ChatboxItemSearch extends ChatboxTextInput
{
private static final int ICON_HEIGHT = 32;
private static final int ICON_WIDTH = 36;
private static final int PADDING = 6;
private static final int MAX_RESULTS = 24;
private static final int FONT_SIZE = 16;
private static final int HOVERED_OPACITY = 128;
private final ChatboxPanelManager chatboxPanelManager;
private final ItemManager itemManager;
private final Client client;
private Map<Integer, ItemComposition> results = new LinkedHashMap<>();
private String tooltipText;
private int index = -1;
@Getter
private Consumer<Integer> onItemSelected;
@Inject
private ChatboxItemSearch(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread,
ItemManager itemManager, Client client)
{
super(chatboxPanelManager, clientThread);
this.chatboxPanelManager = chatboxPanelManager;
this.itemManager = itemManager;
this.client = client;
lines(1);
prompt("Item Search");
onChanged(searchString ->
clientThread.invokeLater(() ->
{
filterResults();
update();
}));
}
@Override
protected void update()
{
Widget container = chatboxPanelManager.getContainerWidget();
container.deleteAllChildren();
Widget promptWidget = container.createChild(-1, WidgetType.TEXT);
promptWidget.setText(getPrompt());
promptWidget.setTextColor(0x800000);
promptWidget.setFontId(getFontID());
promptWidget.setOriginalX(0);
promptWidget.setOriginalY(5);
promptWidget.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
promptWidget.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
promptWidget.setOriginalHeight(FONT_SIZE);
promptWidget.setXTextAlignment(WidgetTextAlignment.CENTER);
promptWidget.setYTextAlignment(WidgetTextAlignment.CENTER);
promptWidget.setWidthMode(WidgetSizeMode.MINUS);
promptWidget.revalidate();
buildEdit(0, 5 + FONT_SIZE, container.getWidth(), FONT_SIZE);
Widget separator = container.createChild(-1, WidgetType.LINE);
separator.setOriginalX(0);
separator.setOriginalY(8 + (FONT_SIZE * 2));
separator.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
separator.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
separator.setOriginalHeight(0);
separator.setOriginalWidth(16);
separator.setWidthMode(WidgetSizeMode.MINUS);
separator.setTextColor(0x666666);
separator.revalidate();
int x = PADDING;
int y = PADDING * 3;
int idx = 0;
for (ItemComposition itemComposition : results.values())
{
Widget item = container.createChild(-1, WidgetType.GRAPHIC);
item.setXPositionMode(WidgetPositionMode.ABSOLUTE_LEFT);
item.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
item.setOriginalX(x);
item.setOriginalY(y + FONT_SIZE * 2);
item.setOriginalHeight(ICON_HEIGHT);
item.setOriginalWidth(ICON_WIDTH);
item.setName("<col=ff9040>" + itemComposition.getName());
item.setItemId(itemComposition.getId());
item.setItemQuantity(10000);
item.setItemQuantityMode(ItemQuantityMode.NEVER);
item.setBorderType(1);
item.setAction(0, tooltipText);
item.setHasListener(true);
if (index == idx)
{
item.setOpacity(HOVERED_OPACITY);
}
else
{
item.setOnMouseOverListener((JavaScriptCallback) ev -> item.setOpacity(HOVERED_OPACITY));
item.setOnMouseLeaveListener((JavaScriptCallback) ev -> item.setOpacity(0));
}
item.setOnOpListener((JavaScriptCallback) ev ->
{
if (onItemSelected != null)
{
onItemSelected.accept(itemComposition.getId());
}
chatboxPanelManager.close();
});
x += ICON_WIDTH + PADDING;
if (x + ICON_WIDTH >= container.getWidth())
{
y += ICON_HEIGHT + PADDING;
x = PADDING;
}
item.revalidate();
++idx;
}
}
@Override
public void keyPressed(KeyEvent ev)
{
switch (ev.getKeyCode())
{
case KeyEvent.VK_ENTER:
ev.consume();
if (index > -1)
{
if (onItemSelected != null)
{
onItemSelected.accept(results.keySet().toArray(new Integer[results.size()])[index]);
}
chatboxPanelManager.close();
}
break;
case KeyEvent.VK_TAB:
case KeyEvent.VK_RIGHT:
ev.consume();
if (!results.isEmpty())
{
index++;
if (index >= results.size())
{
index = 0;
}
clientThread.invokeLater(this::update);
}
break;
case KeyEvent.VK_LEFT:
ev.consume();
if (!results.isEmpty())
{
index--;
if (index < 0)
{
index = results.size() - 1;
}
clientThread.invokeLater(this::update);
}
break;
case KeyEvent.VK_UP:
ev.consume();
if (results.size() >= (MAX_RESULTS / 2))
{
index -= MAX_RESULTS / 2;
if (index < 0)
{
if (results.size() == MAX_RESULTS)
{
index += results.size();
}
else
{
index += MAX_RESULTS;
}
index = Ints.constrainToRange(index, 0, results.size() - 1);
}
clientThread.invokeLater(this::update);
}
break;
case KeyEvent.VK_DOWN:
ev.consume();
if (results.size() >= (MAX_RESULTS / 2))
{
index += MAX_RESULTS / 2;
if (index >= MAX_RESULTS)
{
if (results.size() == MAX_RESULTS)
{
index -= results.size();
}
else
{
index -= MAX_RESULTS;
}
index = Ints.constrainToRange(index, 0, results.size() - 1);
}
clientThread.invokeLater(this::update);
}
break;
default:
super.keyPressed(ev);
}
}
@Override
protected void close()
{
// Clear search string when closed
value("");
results.clear();
index = -1;
super.close();
}
@Override
@Deprecated
public ChatboxTextInput onDone(Consumer<String> onDone)
{
throw new UnsupportedOperationException();
}
private void filterResults()
{
results.clear();
index = -1;
String search = getValue().toLowerCase();
if (search.isEmpty())
{
return;
}
for (int i = 0; i < client.getItemCount() && results.size() < MAX_RESULTS; i++)
{
ItemComposition itemComposition = itemManager.getItemComposition(itemManager.canonicalize(i));
String name = itemComposition.getName();
// The client assigns "null" to item names of items it doesn't know about
if (!name.equals("null") && name.toLowerCase().contains(search))
{
// This may already be in the map due to canonicalize mapping the item to something we've already seen
results.putIfAbsent(itemComposition.getId(), itemComposition);
}
}
}
public ChatboxItemSearch onItemSelected(Consumer<Integer> onItemSelected)
{
this.onItemSelected = onItemSelected;
return this;
}
public ChatboxItemSearch tooltipText(final String text)
{
tooltipText = text;
return this;
}
}

View File

@@ -67,7 +67,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
private static final Pattern BREAK_MATCHER = Pattern.compile("[^a-zA-Z0-9']");
private final ChatboxPanelManager chatboxPanelManager;
private final ClientThread clientThread;
protected final ClientThread clientThread;
private static IntPredicate getDefaultCharValidator()
{