runelite-client: Add Keybind class for configs to save and set hotkeys

This commit is contained in:
Max Weber
2018-06-16 18:58:52 -06:00
parent 2146ccaa5e
commit bd99ada685
5 changed files with 330 additions and 2 deletions

View File

@@ -359,6 +359,7 @@ public class ConfigManager
/**
* Initialize the configuration from the default settings
*
* @param proxy
*/
public void setDefaultConfiguration(Object proxy, boolean override)
@@ -459,7 +460,7 @@ public class ConfigManager
if (type == Rectangle.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int width = Integer.parseInt(splitStr[2]);
int height = Integer.parseInt(splitStr[3]);
@@ -473,6 +474,13 @@ public class ConfigManager
{
return Instant.parse(str);
}
if (type == Keybind.class)
{
String[] splitStr = str.split(":");
int code = Integer.parseInt(splitStr[0]);
int mods = Integer.parseInt(splitStr[1]);
return new Keybind(code, mods);
}
return str;
}
@@ -498,13 +506,18 @@ public class ConfigManager
}
if (object instanceof Rectangle)
{
Rectangle r = (Rectangle)object;
Rectangle r = (Rectangle) object;
return r.x + ":" + r.y + ":" + r.width + ":" + r.height;
}
if (object instanceof Instant)
{
return ((Instant) object).toString();
}
if (object instanceof Keybind)
{
Keybind k = (Keybind) object;
return k.getKeyCode() + ":" + k.getModifiers();
}
return object.toString();
}
}

View File

@@ -0,0 +1,151 @@
/*
* 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.config;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import lombok.Getter;
/**
* A combination of zero or more modifier keys (Ctrl, alt, shift, meta)
* and an optional non-modifier key
*/
@Getter
public class Keybind
{
public static final Keybind NOT_SET = new Keybind(KeyEvent.VK_UNDEFINED, 0);
public static final Keybind CTRL = new Keybind(KeyEvent.VK_UNDEFINED, InputEvent.CTRL_DOWN_MASK);
public static final Keybind ALT = new Keybind(KeyEvent.VK_UNDEFINED, InputEvent.ALT_DOWN_MASK);
public static final Keybind SHIFT = new Keybind(KeyEvent.VK_UNDEFINED, InputEvent.SHIFT_DOWN_MASK);
private static final BiMap<Integer, Integer> modifierToKeyCode = new ImmutableBiMap.Builder<Integer, Integer>()
.put(InputEvent.CTRL_DOWN_MASK, KeyEvent.VK_CONTROL)
.put(InputEvent.ALT_DOWN_MASK, KeyEvent.VK_ALT)
.put(InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_SHIFT)
.put(InputEvent.META_DOWN_MASK, KeyEvent.VK_META)
.build();
// Bitmask of all supported modifers
private static final int KEYBOARD_MODIFIER_MASK = modifierToKeyCode.keySet().stream()
.reduce((a, b) -> a | b).get();
private final int keyCode;
private final int modifiers;
public Keybind(int keyCode, int modifiers)
{
modifiers &= KEYBOARD_MODIFIER_MASK;
// If the keybind is just modifiers we don't want the keyCode to contain the modifier too,
// becasue this breaks if you do the keycode backwards
Integer mf = modifierToKeyCode.inverse().get(keyCode);
if (mf != null)
{
assert (modifiers & mf) != 0;
keyCode = KeyEvent.VK_UNDEFINED;
}
this.keyCode = keyCode;
this.modifiers = modifiers;
}
/**
* Constructs a keybind with that matches the passed KeyEvent
*/
public Keybind(KeyEvent e)
{
this(e.getExtendedKeyCode(), e.getModifiersEx());
assert matches(e);
}
/**
* If the KeyEvent is from a KeyPressed event this returns if the
* Event is this hotkey being pressed. If the KeyEvent is a
* KeyReleased event this returns if the event is this hotkey being
* released
*/
public boolean matches(KeyEvent e)
{
if (NOT_SET.equals(this))
{
return false;
}
int keyCode = e.getExtendedKeyCode();
int modifiers = e.getModifiersEx() & KEYBOARD_MODIFIER_MASK;
Integer mf = modifierToKeyCode.inverse().get(keyCode);
if (mf != null)
{
modifiers |= mf;
keyCode = KeyEvent.VK_UNDEFINED;
}
return this.keyCode == keyCode && this.modifiers == modifiers;
}
@Override
public String toString()
{
if (keyCode == KeyEvent.VK_UNDEFINED && modifiers == 0)
{
return "Not set";
}
String key;
if (keyCode == KeyEvent.VK_UNDEFINED)
{
key = "";
}
else
{
key = KeyEvent.getKeyText(keyCode);
}
String mod = "";
if (modifiers != 0)
{
mod = InputEvent.getModifiersExText(modifiers);
}
if (mod.isEmpty() && key.isEmpty())
{
return "Not set";
}
if (!mod.isEmpty() && !key.isEmpty())
{
return mod + "+" + key;
}
if (mod.isEmpty())
{
return key;
}
return mod;
}
}

View File

@@ -73,6 +73,7 @@ import net.runelite.client.config.ConfigDescriptor;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.ConfigItemDescriptor;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.Keybind;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@@ -429,6 +430,12 @@ public class ConfigPanel extends PluginPanel
JComboBox jComboBox = (JComboBox) component;
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), ((Enum) jComboBox.getSelectedItem()).name());
}
if (component instanceof HotkeyButton)
{
HotkeyButton hotkeyButton = (HotkeyButton) component;
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), hotkeyButton.getValue());
}
}
private void openGroupConfigPanel(Config config, ConfigDescriptor cd, ConfigManager configManager)
@@ -613,6 +620,24 @@ public class ConfigPanel extends PluginPanel
item.add(box, BorderLayout.EAST);
}
if (cid.getType() == Keybind.class)
{
Keybind startingValue = configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), Keybind.class);
HotkeyButton button = new HotkeyButton(startingValue);
button.addFocusListener(new FocusAdapter()
{
@Override
public void focusLost(FocusEvent e)
{
changeConfiguration(config, button, cd, cid);
}
});
item.add(button, BorderLayout.EAST);
}
add(item);
}

View File

@@ -0,0 +1,65 @@
/*
* 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.config;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import lombok.Getter;
import net.runelite.client.config.Keybind;
public class HotkeyButton extends JButton
{
@Getter
private Keybind value;
public HotkeyButton(Keybind value)
{
setValue(value);
addActionListener(e ->
{
setValue(Keybind.NOT_SET);
});
addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent e)
{
setValue(new Keybind(e));
}
});
}
public void setValue(Keybind value)
{
if (value == null)
{
value = Keybind.NOT_SET;
}
this.value = value;
setText(value.toString());
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.util;
import java.awt.event.KeyEvent;
import java.util.function.Supplier;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.config.Keybind;
import net.runelite.client.input.KeyListener;
@RequiredArgsConstructor
public abstract class HotkeyListener implements KeyListener
{
private final Supplier<Keybind> keybind;
@Getter
private boolean isPressed = false;
@Override
public void keyTyped(KeyEvent e)
{
}
@Override
public void keyPressed(KeyEvent e)
{
if (keybind.get().matches(e))
{
isPressed = true;
hotkeyPressed();
}
}
@Override
public void keyReleased(KeyEvent e)
{
if (keybind.get().matches(e))
{
isPressed = false;
hotkeyReleased();
}
}
public void hotkeyPressed()
{
}
public void hotkeyReleased()
{
}
}