IDEA-242733 Updated NLS for intellij.emojipicker & reformatted code

GitOrigin-RevId: 11b8c76106b0dbd522ac7e033a57af3589cae830
This commit is contained in:
Nikita Gubarkov
2020-10-20 17:27:56 +00:00
committed by intellij-monorepo-bot
parent b57b618162
commit 99e0303b4a
17 changed files with 361 additions and 288 deletions
@@ -0,0 +1,13 @@
# Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
# These properties are accessed dynamically, so suppress "unused" warnings
# suppress inspection "UnusedProperty" for whole file
category.EmojiPicker.RecentlyUsed=Recently Used
category.EmojiPicker.SmileysAndPeople=Smileys \\& People
category.EmojiPicker.AnimalsAndNature=Animals \\& Nature
category.EmojiPicker.FoodAndDrink=Food \\& Drink
category.EmojiPicker.TravelAndPlaces=Travel \\& Places
category.EmojiPicker.Activities=Activities
category.EmojiPicker.Objects=Objects
category.EmojiPicker.Symbols=Symbols
category.EmojiPicker.Flags=Flags
@@ -1,16 +1,8 @@
# Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
action.EmojiPicker.Open.text=Emo_ji & Symbols
action.EmojiPicker.Open.text=Emo_ji \\& Symbols
message.EmojiPicker.Initializing=Initializing emoji picker
message.EmojiPicker.BuildingIndex=Building search index
message.EmojiPicker.SavingIndexData=Saving data
category.EmojiPicker.RecentlyUsed=Recently Used
category.EmojiPicker.SmileysAndPeople=Smileys & People
category.EmojiPicker.AnimalsAndNature=Animals & Nature
category.EmojiPicker.FoodAndDrink=Food & Drink
category.EmojiPicker.TravelAndPlaces=Travel & Places
category.EmojiPicker.Activities=Activities
category.EmojiPicker.Objects=Objects
category.EmojiPicker.Symbols=Symbols
category.EmojiPicker.Flags=Flags
message.EmojiPicker.NoEmojiFound=No emoji found
@@ -1,15 +1,17 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.emojipicker;
import com.intellij.openapi.util.NlsSafe;
import org.jetbrains.annotations.NonNls;
import java.io.Serializable;
public class Emoji implements Serializable {
private final int myId;
private final String myValue;
@NonNls private final String myValue;
private final boolean myToned;
public Emoji(int id, String value, boolean toned) {
public Emoji(int id, @NonNls String value, boolean toned) {
myId = id;
myValue = value;
myToned = toned;
@@ -19,8 +21,8 @@ public class Emoji implements Serializable {
return myId;
}
@NlsSafe
public String getTonedValue(EmojiSkinTone tone) {
return myToned ? myValue + tone.getStringValue() : myValue;
}
}
@@ -2,6 +2,7 @@
package org.jetbrains.plugins.emojipicker;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NonNls;
import javax.swing.*;
import java.io.Serializable;
@@ -9,9 +10,7 @@ import java.util.List;
public class EmojiCategory implements Serializable {
private final String myId;
@NonNls private final String myId;
private final List<Emoji> myEmoji;
private transient Icon myIcon;
@@ -20,6 +19,7 @@ public class EmojiCategory implements Serializable {
myEmoji = emojiList;
}
@NonNls
public String getId() {
return myId;
}
@@ -30,11 +30,9 @@ public class EmojiCategory implements Serializable {
public Icon getIcon() {
Icon icon = myIcon;
if(icon == null) {
if (icon == null) {
myIcon = icon = IconLoader.getIcon("/icons/categories/" + myId + ".svg", EmojiCategory.class);
}
return icon;
}
}
@@ -1,38 +1,38 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.emojipicker;
import org.jetbrains.annotations.NonNls;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class EmojiSearchIndex implements Serializable {
private final String myChars;
@NonNls private final String myChars;
private final int[] myData;
private final int myRootNodeOffset, myTotalEmojiIndices;
private EmojiSearchIndex(String chars, int[] data, int rootNodeOffset, int totalEmojiIndices) {
private EmojiSearchIndex(@NonNls String chars, int[] data, int rootNodeOffset, int totalEmojiIndices) {
myChars = chars;
myData = data;
myRootNodeOffset = rootNodeOffset;
myTotalEmojiIndices = totalEmojiIndices;
}
private int findNodeForPrefix(String key, int prefixOffset, int nodeOffset) {
if(prefixOffset == key.length()) return nodeOffset;
private int findNodeForPrefix(@NonNls String key, int prefixOffset, int nodeOffset) {
if (prefixOffset == key.length()) return nodeOffset;
int children = myData[nodeOffset + 2];
for (int c = 0; c < children; c++) {
int child = myData[nodeOffset + 3 + c];
int childCharsFrom = myData[child], childCharsTo = myData[child + 1];
int i, limit = Math.min(key.length() - prefixOffset, childCharsTo - childCharsFrom);
int i, limit = Math.min(key.length() - prefixOffset, childCharsTo - childCharsFrom);
for (i = 0; i < limit; i++) {
if(key.charAt(prefixOffset + i) != myChars.charAt(childCharsFrom + i)) break;
if (key.charAt(prefixOffset + i) != myChars.charAt(childCharsFrom + i)) break;
}
if(i == childCharsTo - childCharsFrom) return findNodeForPrefix(key, prefixOffset + i, child);
if(i == key.length() - prefixOffset) return child;
if(i > 0) return -1;
if (i == childCharsTo - childCharsFrom) return findNodeForPrefix(key, prefixOffset + i, child);
if (i == key.length() - prefixOffset) return child;
if (i > 0) return -1;
}
return -1;
}
@@ -50,13 +50,13 @@ public class EmojiSearchIndex implements Serializable {
}
}
public boolean lookupIds(boolean[] idMap, String prefix) {
if(idMap.length < myTotalEmojiIndices) {
public boolean lookupIds(boolean[] idMap, @NonNls String prefix) {
if (idMap.length < myTotalEmojiIndices) {
throw new IllegalArgumentException("Output array is too small: " + idMap.length +
", but there are " + myTotalEmojiIndices + "emoji indices");
}
int node = findNodeForPrefix(prefix, 0, myRootNodeOffset);
if(node == -1) return false;
if (node == -1) return false;
Arrays.fill(idMap, false);
collectIdsFromSubtree(idMap, node);
return true;
@@ -67,34 +67,32 @@ public class EmojiSearchIndex implements Serializable {
}
public static class PrefixTree {
private String myPrefix;
@NonNls private String myPrefix;
private final List<PrefixTree> myChildren = new ArrayList<>();
private final List<Integer> myValues = new ArrayList<>();
private PrefixTree(String prefix) { myPrefix = prefix; }
private PrefixTree(@NonNls String prefix) { myPrefix = prefix; }
public PrefixTree() { this(""); }
public void add(String key, int value) {
if(key.isEmpty()) {
public void add(@NonNls String key, int value) {
if (key.isEmpty()) {
myValues.add(value);
return;
}
for(int c = 0; c < myChildren.size(); c++) {
for (int c = 0; c < myChildren.size(); c++) {
PrefixTree child = myChildren.get(c);
int i, limit = Math.min(key.length(), child.myPrefix.length());
int i, limit = Math.min(key.length(), child.myPrefix.length());
for (i = 0; i < limit; i++) {
if(key.charAt(i) != child.myPrefix.charAt(i)) break;
if (key.charAt(i) != child.myPrefix.charAt(i)) break;
}
if(i == child.myPrefix.length()) {
if (i == child.myPrefix.length()) {
child.add(key.substring(limit), value);
return;
}
else if(i > 0) {
else if (i > 0) {
PrefixTree
firstPart = new PrefixTree(key.substring(0, i));
child.myPrefix = child.myPrefix.substring(i);
@@ -128,8 +126,8 @@ public class EmojiSearchIndex implements Serializable {
System.arraycopy(childrenLinks, 0, data.myData, data.myDataPointer, childrenLinks.length);
data.myDataPointer += childrenLinks.length;
data.write(myValues.size());
for(int i : myValues) {
if(i > data.myMaxEmojiId) data.myMaxEmojiId = i;
for (int i : myValues) {
if (i > data.myMaxEmojiId) data.myMaxEmojiId = i;
data.write(i);
}
return index;
@@ -139,15 +137,15 @@ public class EmojiSearchIndex implements Serializable {
StringBuffer myChars;
int[] myData;
int myDataPointer, myMaxEmojiId = -1;
private void ensureDataCapacity(int remaining) {
if(myDataPointer + remaining > myData.length) myData = Arrays.copyOf(myData, myData.length * 2);
if (myDataPointer + remaining > myData.length) myData = Arrays.copyOf(myData, myData.length * 2);
}
private BuildData write(int i) {
myData[myDataPointer++] = i;
return this;
}
}
}
}
@@ -1,6 +1,8 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.emojipicker;
import org.jetbrains.annotations.NonNls;
public enum EmojiSkinTone {
NO_TONE(""),
LIGHT("🏻"),
@@ -9,12 +11,12 @@ public enum EmojiSkinTone {
MEDIUM_DARK("🏾"),
DARK("🏿");
private final String myValue;
@NonNls private final String myValue;
EmojiSkinTone(String value) {this.myValue = value;}
EmojiSkinTone(@NonNls String value) {this.myValue = value;}
@NonNls
public String getStringValue() {
return myValue;
}
}
@@ -27,12 +27,16 @@ public class OpenEmojiPickerAction extends DumbAwareAction {
private static Context getContext(AnActionEvent e, boolean findOnly) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if(editor != null) {
if(editor.isViewer()) return null;
if(findOnly) return Context.FOUND;
if (editor != null) {
if (editor.isViewer()) return null;
if (findOnly) return Context.FOUND;
Consumer<String> input;
if(editor instanceof EditorImpl) input = ((EditorImpl) editor)::type;
else if(editor.getContentComponent() instanceof TypingTarget) input = ((TypingTarget) editor.getContentComponent())::type;
if (editor instanceof EditorImpl) {
input = ((EditorImpl)editor)::type;
}
else if (editor.getContentComponent() instanceof TypingTarget) {
input = ((TypingTarget)editor.getContentComponent())::type;
}
else {
input = s -> WriteCommandAction.writeCommandAction(editor.getProject()).run(() -> {
com.intellij.openapi.editor.Document doc = editor.getDocument();
@@ -46,28 +50,29 @@ public class OpenEmojiPickerAction extends DumbAwareAction {
}
Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT);
if(component instanceof EmojiSearchField) {
if (component instanceof EmojiSearchField) {
// Easy way to block recursive pickers
return null;
}
if(component instanceof TypingTarget) {
if(findOnly) return Context.FOUND;
if (component instanceof TypingTarget) {
if (findOnly) return Context.FOUND;
return new Context(
((TypingTarget) component)::type,
((TypingTarget)component)::type,
p -> p.showUnderneathOf(component)
);
}
if(component instanceof JTextComponent) {
if(findOnly) return Context.FOUND;
JTextComponent field = (JTextComponent) component;
if (component instanceof JTextComponent) {
if (findOnly) return Context.FOUND;
JTextComponent field = (JTextComponent)component;
Document doc = field.getDocument();
return new Context(
s -> {
try {
doc.insertString(field.getCaretPosition(), s, null);
} catch (BadLocationException exception) {
}
catch (BadLocationException exception) {
throw new RuntimeException(exception);
}
},
@@ -86,24 +91,21 @@ public class OpenEmojiPickerAction extends DumbAwareAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Context context = getContext(e, false);
if(context != null) {
if (context != null) {
JBPopup popup = EmojiPicker.createPopup(e.getProject(), context.myInputCallback);
context.myPopupShowCallback.accept(popup);
}
}
private static class Context {
private static final Context FOUND = new Context(null, null);
private final Consumer<String> myInputCallback;
private final Consumer<JBPopup> myPopupShowCallback;
private Context(Consumer<String> inputCallback, Consumer<JBPopup> popupShowCallback) {
myInputCallback = inputCallback;
myPopupShowCallback = popupShowCallback;
}
}
}
@@ -0,0 +1,35 @@
package org.jetbrains.plugins.emojipicker.messages;
import com.intellij.DynamicBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import org.jetbrains.plugins.emojipicker.EmojiCategory;
import java.util.function.Supplier;
public class EmojiCategoriesBundle extends DynamicBundle {
@NonNls private static final String BUNDLE = "messages.EmojiCategoriesBundle";
private static final EmojiCategoriesBundle INSTANCE = new EmojiCategoriesBundle();
private EmojiCategoriesBundle() {
super(BUNDLE);
}
@NotNull
public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getMessage(key, params);
}
@NotNull
public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key,
Object @NotNull ... params) {
return INSTANCE.getLazyMessage(key, params);
}
public static @Nls String findNameForCategory(EmojiCategory category) {
if (category == null) return null;
return message("category.EmojiPicker." + category.getId());
}
}
@@ -0,0 +1,29 @@
package org.jetbrains.plugins.emojipicker.messages;
import com.intellij.DynamicBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Supplier;
public class EmojipickerBundle extends DynamicBundle {
@NonNls private static final String BUNDLE = "messages.EmojipickerBundle";
private static final EmojipickerBundle INSTANCE = new EmojipickerBundle();
private EmojipickerBundle() {
super(BUNDLE);
}
@NotNull
public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getMessage(key, params);
}
@NotNull
public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key,
Object @NotNull ... params) {
return INSTANCE.getLazyMessage(key, params);
}
}
@@ -7,8 +7,9 @@ import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.emojipicker.Emoji;
@@ -32,18 +33,18 @@ import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import static org.jetbrains.plugins.emojipicker.messages.EmojipickerBundle.message;
@Service
@State(name = "EmojiPickerState", storages = @Storage("emoji.picker.xml"))
public final class EmojiService implements PersistentStateComponent<EmojiService.State> {
private final List<Emoji> myEmoji;
private final List<EmojiCategory> myCategories;
private final EmojiCategory myRecentlyUsedCategory;
private volatile SearchIndex myEmojiSearchIndex;
private final String[] myEmojiNames;
@Nls private final String[] myEmojiNames;
private volatile EmojiSkinTone mySkinTone;
private volatile boolean dirty;
@@ -61,62 +62,65 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
myCategories = new ArrayList<>();
List<Emoji> emoji;
SearchIndex searchIndex;
String[] emojiNames;
@Nls String[] emojiNames;
try {
try(ObjectInputStream in = new ObjectInputStream(Files.newInputStream(serializedEmojiPath))) {
emoji = (List<Emoji>) in.readObject();
try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(serializedEmojiPath))) {
emoji = (List<Emoji>)in.readObject();
myCategories.add(myRecentlyUsedCategory);
myCategories.addAll((List<EmojiCategory>) in.readObject());
myCategories.addAll((List<EmojiCategory>)in.readObject());
}
try(ObjectInputStream in = new ObjectInputStream(Files.newInputStream(serializedIndexPath))) {
searchIndex = new SearchIndex((EmojiSearchIndex) in.readObject());
try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(serializedIndexPath))) {
searchIndex = new SearchIndex((EmojiSearchIndex)in.readObject());
}
try(ObjectInputStream in = new ObjectInputStream(Files.newInputStream(serializedNamesPath))) {
emojiNames = (String[]) in.readObject();
try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(serializedNamesPath))) {
emojiNames = (String[])in.readObject();
}
} catch (IOException | ClassNotFoundException ignore) {
}
catch (IOException | ClassNotFoundException ignore) {
Files.createDirectories(configDir);
EmojiData emojiData = EmojiData.read();
emoji = emojiData.myEmoji;
myCategories.clear();
myCategories.add(myRecentlyUsedCategory);
myCategories.addAll(emojiData.myCategories);
String[] names = emojiNames = new String[emoji.size()];
@Nls String[] names = emojiNames = new String[emoji.size()];
CldrData cldr = new CldrData(emojiData);
final Locale baseLocale = Locale.ENGLISH;
cldr.read(baseLocale, emojiNames);
searchIndex = new SearchIndex(cldr.myIndexTree.buildIndex());
ProgressManager.getInstance().run(
new Task.Backgroundable(null, getString("message.EmojiPicker.Initializing"), false, PerformInBackgroundOption.ALWAYS_BACKGROUND) {
new Task.Backgroundable(null, message("message.EmojiPicker.Initializing"), false, PerformInBackgroundOption.ALWAYS_BACKGROUND) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
try(ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedEmojiPath, StandardOpenOption.CREATE))) {
try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedEmojiPath, StandardOpenOption.CREATE))) {
out.writeObject(emojiData.myEmoji);
out.writeObject(emojiData.myCategories);
}
try(ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedNamesPath, StandardOpenOption.CREATE))) {
try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedNamesPath, StandardOpenOption.CREATE))) {
out.writeObject(names);
}
indicator.setText(getString("message.EmojiPicker.BuildingIndex"));
indicator.setText(message("message.EmojiPicker.BuildingIndex"));
indicator.setIndeterminate(false);
Locale[] locales = Locale.getAvailableLocales();
double multiplier = 1.0 / (double) locales.length;
double multiplier = 1.0 / (double)locales.length;
for (int i = 0; i < locales.length; i++) {
indicator.setFraction((double) i * multiplier);
if(!locales[i].equals(baseLocale)) cldr.read(locales[i], null);
indicator.setFraction((double)i * multiplier);
if (!locales[i].equals(baseLocale)) cldr.read(locales[i], null);
}
indicator.setIndeterminate(true);
SearchIndex searchIndex = new SearchIndex(cldr.myIndexTree.buildIndex());
indicator.setText(getString("message.EmojiPicker.SavingIndexData"));
try(ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedIndexPath, StandardOpenOption.CREATE))) {
indicator.setText(message("message.EmojiPicker.SavingIndexData"));
try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedIndexPath, StandardOpenOption.CREATE))) {
out.writeObject(searchIndex.myIndex);
}
myEmojiSearchIndex = searchIndex;
} catch (IOException e) {
}
catch (IOException e) {
throw new UncheckedIOException(e);
} catch (SAXException e) {
}
catch (SAXException e) {
throw new RuntimeException(e);
}
}
@@ -132,30 +136,18 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
return myCategories;
}
public @NlsSafe String getString(String key) throws MissingResourceException {
return ResourceBundle.getBundle("/messages/EmojipickerBundle").getString(key);
}
public @NlsSafe String findNameForCategory(EmojiCategory category) {
if(category == null) return null;
try {
return getString("category.EmojiPicker." + category.getId());
} catch (MissingResourceException ignore) {
return category.getId();
}
}
@Nls
public String findNameForEmoji(Emoji emoji) {
return emoji == null ? null : myEmojiNames[emoji.getId()];
}
public List<Emoji> findEmojiByPrefix(String prefix) {
public List<Emoji> findEmojiByPrefix(@NonNls String prefix) {
return myEmojiSearchIndex.lookupEmoji(prefix);
}
public void saveRecentlyUsedEmoji(Emoji emoji) {
myRecentlyUsedCategory.getEmoji().remove(emoji);
while(myRecentlyUsedCategory.getEmoji().size() >= 32) {
while (myRecentlyUsedCategory.getEmoji().size() >= 32) {
myRecentlyUsedCategory.getEmoji().remove(myRecentlyUsedCategory.getEmoji().size() - 1);
}
myRecentlyUsedCategory.getEmoji().add(0, emoji);
@@ -173,15 +165,15 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
@Override
public @Nullable State getState() {
if(!dirty) return null;
if (!dirty) return null;
dirty = false;
return new State(mySkinTone, ContainerUtil.map(myRecentlyUsedCategory.getEmoji(), Emoji::getId));
}
@Override
public void loadState(@NotNull State state) {
if(state.mySkinTone != null) mySkinTone = state.mySkinTone;
if(state.myRecentlyUsedEmoji != null && !state.myRecentlyUsedEmoji.isEmpty()) {
if (state.mySkinTone != null) mySkinTone = state.mySkinTone;
if (state.myRecentlyUsedEmoji != null && !state.myRecentlyUsedEmoji.isEmpty()) {
myRecentlyUsedCategory.getEmoji().clear();
myRecentlyUsedCategory.getEmoji().addAll(ContainerUtil.map(state.myRecentlyUsedEmoji, myEmoji::get));
}
@@ -192,9 +184,6 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
}
static class State {
public EmojiSkinTone mySkinTone;
@@ -202,6 +191,7 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
@SuppressWarnings("unused")
private State() { this(EmojiSkinTone.NO_TONE, List.of()); }
private State(EmojiSkinTone tone, List<Integer> emoji) {
mySkinTone = tone;
myRecentlyUsedEmoji = emoji;
@@ -219,13 +209,9 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
public int hashCode() {
return Objects.hash(mySkinTone, myRecentlyUsedEmoji);
}
}
private class SearchIndex {
private final EmojiSearchIndex myIndex;
@@ -236,40 +222,40 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
myIdMap = new boolean[index.getTotalEmojiIndices()];
}
synchronized List<Emoji> lookupEmoji(String prefix) {
if(myIndex.lookupIds(myIdMap, prefix)) {
synchronized List<Emoji> lookupEmoji(@NonNls String prefix) {
if (myIndex.lookupIds(myIdMap, prefix)) {
List<Emoji> result = new ArrayList<>(100);
for (int i = 0; i < myIdMap.length; i++) {
if(myIdMap[i]) result.add(myEmoji.get(i));
if (myIdMap[i]) result.add(myEmoji.get(i));
}
return result;
}
else return List.of();
else {
return List.of();
}
}
}
private static class EmojiData {
private final List<Emoji> myEmoji = new ArrayList<>();
private final List<EmojiCategory> myCategories = new ArrayList<>();
private final Map<String, Integer> myIndicesMap = new HashMap<>();
private final Map<@NonNls String, Integer> myIndicesMap = new HashMap<>();
private static EmojiData read() throws IOException {
Set<String> tonedEmojiSet = readToned();
Set<@NonNls String> tonedEmojiSet = readToned();
EmojiData emojiData = new EmojiData();
try(BufferedReader reader = new BufferedReader(new InputStreamReader(
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
EmojiService.class.getResourceAsStream("/data/emoji.txt"), StandardCharsets.UTF_8))) {
EmojiCategory category = null;
String s;
while((s = reader.readLine()) != null) {
if(s.isBlank()) category = null;
@NonNls String s;
while ((s = reader.readLine()) != null) {
if (s.isBlank()) {
category = null;
}
else {
if(category == null) {
if (category == null) {
emojiData.myCategories.add(category = new EmojiCategory(s, new ArrayList<>()));
}
else {
@@ -285,19 +271,15 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
return emojiData;
}
private static Set<String> readToned() throws IOException {
try(BufferedReader reader = new BufferedReader(new InputStreamReader(
private static Set<@NonNls String> readToned() throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
EmojiService.class.getResourceAsStream("/data/toned.txt"), StandardCharsets.UTF_8))) {
return reader.lines().collect(Collectors.toSet());
}
}
}
private static class CldrData {
private final EmojiData myEmojiData;
@@ -316,38 +298,38 @@ public final class EmojiService implements PersistentStateComponent<EmojiService
myDocumentBuilder = factory.newDocumentBuilder();
}
private void read(Locale locale, String[] names) throws IOException, SAXException {
String l = locale.getLanguage();
if(!locale.getScript().isEmpty()) l += "_" + locale.getScript();
if(!locale.getCountry().isEmpty()) l += "_" + locale.getCountry();
private void read(Locale locale, @Nls String[] names) throws IOException, SAXException {
@NonNls String l = locale.getLanguage();
if (!locale.getScript().isEmpty()) l += "_" + locale.getScript();
if (!locale.getCountry().isEmpty()) l += "_" + locale.getCountry();
InputStream annotations = EmojiService.class.getResourceAsStream("/data/cldr/annotations/" + l + ".xml");
if(annotations != null) read(annotations, locale, names);
if (annotations != null) read(annotations, locale, names);
InputStream derived = EmojiService.class.getResourceAsStream("/data/cldr/annotationsDerived/" + l + ".xml");
if(derived != null) read(derived, locale, names);
if (derived != null) read(derived, locale, names);
}
private void read(InputStream in, Locale locale, String[] names) throws IOException, SAXException {
try(in) {
private void read(InputStream in, Locale locale, @Nls String[] names) throws IOException, SAXException {
try (in) {
Document document = myDocumentBuilder.parse(in);
NodeList annotations = document.getElementsByTagName("annotation");
for (int i = 0; i < annotations.getLength(); i++) {
Node node = annotations.item(i);
String emoji = node.getAttributes().getNamedItem("cp").getTextContent();
@NonNls String emoji = node.getAttributes().getNamedItem("cp").getTextContent();
Integer index = myEmojiData.myIndicesMap.get(emoji);
if(index != null) {
String value = node.getTextContent();
if(node.getAttributes().getNamedItem("type") == null) {
for(String keyword : value.split("\\|")) {
if (index != null) {
@NonNls String value = node.getTextContent();
if (node.getAttributes().getNamedItem("type") == null) {
for (String keyword : value.split("\\|")) {
myIndexTree.add(keyword.strip(), index);
}
}
else if(names != null) names[index] = value.substring(0, 1).toUpperCase(locale) + value.substring(1);
else if (names != null) {
@Nls String name = value.substring(0, 1).toUpperCase(locale) + value.substring(1);
names[index] = name;
}
}
}
}
}
}
}
@@ -4,15 +4,15 @@ package org.jetbrains.plugins.emojipicker.ui;
import com.intellij.ui.InplaceButton;
import com.intellij.ui.scale.JBUIScale;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.Nls;
import org.jetbrains.plugins.emojipicker.EmojiCategory;
import org.jetbrains.plugins.emojipicker.service.EmojiService;
import org.jetbrains.plugins.emojipicker.messages.EmojiCategoriesBundle;
import javax.swing.*;
import java.awt.*;
import java.util.List;
class EmojiCategoryPanel extends JPanel {
private final EmojiPickerStyle myStyle;
private EmojiCategory myActiveCategory;
@@ -27,18 +27,18 @@ class EmojiCategoryPanel extends JPanel {
gbc.gridy = 0;
gbc.insets = JBUI.insets(0, 4);
setLayout(new GridBagLayout());
for(EmojiCategory category : categories) {
String name = EmojiService.getInstance().findNameForCategory(category);
for (EmojiCategory category : categories) {
@Nls String name = EmojiCategoriesBundle.findNameForCategory(category);
InplaceButton button = new InplaceButton(name, category.getIcon(), e -> emojiPicker.selectCategory(category, true)) {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(emojiPicker.getCurrentFocusTarget() == category) {
if (emojiPicker.getCurrentFocusTarget() == category) {
int x = getWidth() / 2, y = getHeight() / 2;
g.setColor(myStyle.myFocusBorderColor);
g.drawRoundRect(x - 15, y - 15, 30, 30, 4, 4);
}
if(myActiveCategory == category) {
if (myActiveCategory == category) {
g.setColor(myStyle.mySelectedCategoryColor);
g.fillRect(0, getHeight() - 2, getWidth(), 2);
}
@@ -62,5 +62,4 @@ class EmojiCategoryPanel extends JPanel {
myActiveCategory = category;
repaint();
}
}
@@ -2,7 +2,9 @@
package org.jetbrains.plugins.emojipicker.ui;
import com.intellij.ide.ui.AntialiasingType;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.ui.scale.JBUIScale;
import org.jetbrains.annotations.Nls;
import org.jetbrains.plugins.emojipicker.Emoji;
import org.jetbrains.plugins.emojipicker.EmojiSkinTone;
import org.jetbrains.plugins.emojipicker.service.EmojiService;
@@ -12,10 +14,9 @@ import java.awt.*;
import java.awt.font.TextLayout;
public class EmojiInfoPanel extends JPanel {
private final EmojiPickerStyle myStyle;
private String myCurrentEmoji;
private String myCurrentEmojiName;
@NlsSafe private String myCurrentEmoji;
@Nls private String myCurrentEmojiName;
EmojiInfoPanel(EmojiPickerStyle style) {
myStyle = style;
@@ -26,16 +27,16 @@ public class EmojiInfoPanel extends JPanel {
@Override
public void paintComponent(Graphics g) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false));
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false));
g.setColor(myCurrentEmoji != null ? myStyle.myToolbarColor : myStyle.myBackgroundColor);
g.fillRoundRect(0, 0, getWidth(), getHeight(), 6, 6);
g.fillRect(0, 0, getWidth(), getHeight() - 6);
if(myCurrentEmoji != null) {
if (myCurrentEmoji != null) {
g.setColor(myStyle.myBorderColor);
g.drawLine(0, 0, getWidth(), 0);
new TextLayout(myCurrentEmoji, myStyle.myEmojiFont, ((Graphics2D) g).getFontRenderContext())
.draw((Graphics2D) g, 16, getHeight() - 17);
if(myCurrentEmojiName != null) {
new TextLayout(myCurrentEmoji, myStyle.myEmojiFont, ((Graphics2D)g).getFontRenderContext())
.draw((Graphics2D)g, 16, getHeight() - 17);
if (myCurrentEmojiName != null) {
g.setFont(myStyle.myFont);
g.setColor(myStyle.myTextColor);
g.drawString(myCurrentEmojiName, 49, getHeight() - 20);
@@ -44,7 +45,7 @@ public class EmojiInfoPanel extends JPanel {
}
void showEmojiInfo(Emoji emoji, EmojiSkinTone skinTone) {
if(emoji == null) {
if (emoji == null) {
myCurrentEmoji = myCurrentEmojiName = null;
}
else {
@@ -53,5 +54,4 @@ public class EmojiInfoPanel extends JPanel {
}
repaint();
}
}
@@ -8,9 +8,13 @@ import com.intellij.util.ImageLoader;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.StartupUiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.plugins.emojipicker.Emoji;
import org.jetbrains.plugins.emojipicker.EmojiCategory;
import org.jetbrains.plugins.emojipicker.EmojiSkinTone;
import org.jetbrains.plugins.emojipicker.messages.EmojiCategoriesBundle;
import org.jetbrains.plugins.emojipicker.messages.EmojipickerBundle;
import org.jetbrains.plugins.emojipicker.service.EmojiService;
import javax.swing.*;
@@ -24,8 +28,6 @@ import java.util.List;
import java.util.Objects;
class EmojiListPanel extends JBScrollPane {
private static final Image NO_EMOJI_FOUND_IMAGE = ImageLoader.scaleImage(Objects.requireNonNull(
ImageLoader.loadFromStream(EmojiListPanel.class.getResourceAsStream("/icons/NoEmojiFound.png"))), 40);
private final Dimension myCellSize = new Dimension(JBUIScale.scale(40), JBUIScale.scale(40));
@@ -58,30 +60,34 @@ class EmojiListPanel extends JBScrollPane {
}
});
getViewport().addChangeListener(e -> {
if(myCurrentPanel == myCategoriesPanel) {
if (myCurrentPanel == myCategoriesPanel) {
Component topComponent = myCategoriesPanel.getComponentAt(getViewport().getViewPosition());
if(topComponent instanceof Category) emojiPicker.selectCategory(((Category) topComponent).myCategory, false);
else emojiPicker.selectCategory(myCategoriesPanel.myCategories.get(0).myCategory, false);
if (topComponent instanceof Category) {
emojiPicker.selectCategory(((Category)topComponent).myCategory, false);
}
else {
emojiPicker.selectCategory(myCategoriesPanel.myCategories.get(0).myCategory, false);
}
}
});
setBorder(JBUI.Borders.customLine(myStyle.myBorderColor, 1, 0, 0, 0));
}
void selectCategory(EmojiCategory category) {
if(myCurrentPanel != myCategoriesPanel) {
if (myCurrentPanel != myCategoriesPanel) {
setViewportView(myCurrentPanel = myCategoriesPanel);
}
if(category == null || category == myCategoriesPanel.myCategories.get(0).myCategory) {
if (category == null || category == myCategoriesPanel.myCategories.get(0).myCategory) {
setCurrentItem(myCategoriesPanel.myCategories.get(0).myCategory.getEmoji().isEmpty() ?
myCategoriesPanel.myCategories.get(1) : myCategoriesPanel.myCategories.get(0), 0);
myCategoriesPanel.scrollRectToVisible(new Rectangle(0, 0, 1, getHeight()));
}
else {
Category cat = null;
for(Category c : myCategoriesPanel.myCategories) {
if(c.myCategory == category) cat = c;
for (Category c : myCategoriesPanel.myCategories) {
if (c.myCategory == category) cat = c;
}
if(cat == null) return;
if (cat == null) return;
setCurrentItem(cat, 0);
cat.scrollRectToVisible(new Rectangle(0, 0, 1, getHeight()));
}
@@ -90,52 +96,56 @@ class EmojiListPanel extends JBScrollPane {
void updateSkinTone(EmojiSkinTone skinTone) {
myCurrentSkinTone = skinTone;
myCurrentPanel.repaint();
if(myCurrentItemCategory != null && myCurrentItemIndex >= 0) {
if (myCurrentItemCategory != null && myCurrentItemIndex >= 0) {
myEmojiPicker.updateEmojiInfo(myCurrentItemCategory.myCategory.getEmoji().get(myCurrentItemIndex), myCurrentSkinTone);
}
}
void updateSearchFilter(String text) {
if(text.isEmpty()) {
void updateSearchFilter(@NonNls String text) {
if (text.isEmpty()) {
myEmojiPicker.selectCategory(myCategoriesPanel.myCategories.get(0).myCategory, true);
}
else {
myEmojiPicker.selectCategory(null, false);
mySearchCategoryPanel.myCategory = new EmojiCategory(null, EmojiService.getInstance().findEmojiByPrefix(text.strip()));
mySearchCategoryPanel.updateLayout(getWidth());
if(mySearchCategoryPanel.myCategory.getEmoji().isEmpty()) setCurrentItem(null, -1);
else setCurrentItem(mySearchCategoryPanel, 0);
if (mySearchCategoryPanel.myCategory.getEmoji().isEmpty()) {
setCurrentItem(null, -1);
}
else {
setCurrentItem(mySearchCategoryPanel, 0);
}
setViewportView(myCurrentPanel = mySearchCategoryPanel);
}
}
void selectCurrentEmoji() {
if(!hasCurrentItem()) return;
if (!hasCurrentItem()) return;
Emoji emoji = myCurrentItemCategory.myCategory.getEmoji().get(myCurrentItemIndex);
EmojiService.getInstance().saveRecentlyUsedEmoji(emoji);
myEmojiPicker.selectEmoji(emoji.getTonedValue(myCurrentSkinTone));
}
void navigate(int dx, int dy) {
if(myCurrentItemIndex < 0) return;
if (myCurrentItemIndex < 0) return;
int x = (myCurrentItemIndex % myItemsPerRow + dx + myItemsPerRow) % myItemsPerRow;
int y = myCurrentItemIndex / myItemsPerRow + dy;
int newIndex = myItemsPerRow * y + x;
if(newIndex >= 0 && newIndex < myCurrentItemCategory.myCategory.getEmoji().size()) {
if (newIndex >= 0 && newIndex < myCurrentItemCategory.myCategory.getEmoji().size()) {
setCurrentItem(myCurrentItemCategory, newIndex);
}
else {
int cat = myCategoriesPanel.findCategoryIndex(myCurrentItemCategory);
if(cat == -1) return;
if (cat == -1) return;
boolean up = newIndex < 0;
cat += up ? -1 : 1;
if(cat < 0 || cat >= myCategoriesPanel.myCategories.size()) return;
if (cat < 0 || cat >= myCategoriesPanel.myCategories.size()) return;
Category c = myCategoriesPanel.myCategories.get(cat);
int rows = (c.myCategory.getEmoji().size() + myItemsPerRow - 1) / myItemsPerRow;
y = up ? rows - 1 : 0;
if(y < 0 || myItemsPerRow * y + x >= c.myCategory.getEmoji().size()) {
if (y < 0 || myItemsPerRow * y + x >= c.myCategory.getEmoji().size()) {
y--;
if(y < 0) return;
if (y < 0) return;
}
setCurrentItem(c, myItemsPerRow * y + x);
}
@@ -162,9 +172,6 @@ class EmojiListPanel extends JBScrollPane {
}
private abstract class UpdatableLayoutPanel extends JPanel {
private UpdatableLayoutPanel() {
@@ -172,7 +179,6 @@ class EmojiListPanel extends JBScrollPane {
}
abstract void updateLayout(int width);
}
@@ -184,21 +190,20 @@ class EmojiListPanel extends JBScrollPane {
myCategories = categories;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(Box.createRigidArea(new Dimension(0, 15)));
for(Category c : categories) add(c);
for (Category c : categories) add(c);
}
@Override
void updateLayout(int width) {
for(Category c : myCategories) c.updateLayout(width);
for (Category c : myCategories) c.updateLayout(width);
}
private int findCategoryIndex(Category c) {
for (int i = 0; i < myCategories.size(); i++) {
if(myCategories.get(i) == c) return i;
if (myCategories.get(i) == c) return i;
}
return -1;
}
}
@@ -207,37 +212,42 @@ class EmojiListPanel extends JBScrollPane {
private static final int NO_LABEL_PADDING = 12;
private static final int LABEL_HEIGHT = 32;
private EmojiCategory myCategory;
private final String myName;
@Nls private final String myName;
private final Insets myPadding;
private Category(EmojiCategory category) {
myCategory = category;
myName = EmojiService.getInstance().findNameForCategory(category);
myName = EmojiCategoriesBundle.findNameForCategory(category);
myPadding = JBUI.insets(myName == null ? NO_LABEL_PADDING : LABEL_HEIGHT, myHorizontalPadding, 8, myHorizontalPadding);
MouseAdapter mouseAdapter = new MouseAdapter() {
private int getItemIndexUnderCursor(Point point) {
if(point.x < myPadding.left || point.y < myPadding.top) return -1;
if (point.x < myPadding.left || point.y < myPadding.top) return -1;
int x = xVisibleToGrid(point.x), y = yVisibleToGrid(point.y);
if(x >= myItemsPerRow || point.x > xGridToVisible(x) + myCellSize.width || point.y > yGridToVisible(y) + myCellSize.height) {
if (x >= myItemsPerRow || point.x > xGridToVisible(x) + myCellSize.width || point.y > yGridToVisible(y) + myCellSize.height) {
return -1;
}
int index = myItemsPerRow * y + x;
return index >= 0 && index < myCategory.getEmoji().size() ? index : -1;
}
private boolean setCurrentItemUnderCursor(Point point) {
int item = getItemIndexUnderCursor(point);
if(item != -1) {
if (item != -1) {
setCurrentItem(Category.this, item);
return true;
}
else return false;
else {
return false;
}
}
@Override
public void mouseClicked(MouseEvent e) {
if(setCurrentItemUnderCursor(e.getPoint())) {
if (setCurrentItemUnderCursor(e.getPoint())) {
selectCurrentEmoji();
}
}
@Override
public void mouseMoved(MouseEvent e) {
setCurrentItemUnderCursor(e.getPoint());
@@ -250,12 +260,15 @@ class EmojiListPanel extends JBScrollPane {
private int xVisibleToGrid(int x) {
return (x - myPadding.left) / (myCellSize.width + myCellGaps.width);
}
private int yVisibleToGrid(int y) {
return (y - myPadding.top) / (myCellSize.height + myCellGaps.height);
}
private int xGridToVisible(int x) {
return x * (myCellSize.width + myCellGaps.width) + myPadding.left;
}
private int yGridToVisible(int y) {
return y * (myCellSize.height + myCellGaps.height) + myPadding.top;
}
@@ -277,21 +290,23 @@ class EmojiListPanel extends JBScrollPane {
@Override
public void paint(Graphics g) {
super.paint(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false));
if(myName == null && myCategory.getEmoji().isEmpty()) paintNoEmojiFound(g);
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false));
if (myName == null && myCategory.getEmoji().isEmpty()) {
paintNoEmojiFound(g);
}
else {
if(myItemsPerRow <= 0) return;
if (myItemsPerRow <= 0) return;
paintSelection(g);
paintItems(g);
if(myName != null) paintCategoryLabel(g);
if (myName != null) paintCategoryLabel(g);
}
}
private void paintNoEmojiFound(Graphics g) {
if(g instanceof Graphics2D) {
if (g instanceof Graphics2D) {
int x = (getWidth() - NO_EMOJI_FOUND_IMAGE.getWidth(this)) / 2;
int y = (getHeight() - NO_EMOJI_FOUND_IMAGE.getHeight(this)) / 2;
Graphics2D g2 = (Graphics2D) g;
Graphics2D g2 = (Graphics2D)g;
final Composite saveComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5F));
StartupUiUtil.drawImage(g, NO_EMOJI_FOUND_IMAGE, x, y, null);
@@ -299,14 +314,14 @@ class EmojiListPanel extends JBScrollPane {
}
g.setColor(myStyle.myNoEmojiFoundTextColor);
g.setFont(myStyle.myLightFont);
String message = "No emoji found";
@Nls String message = EmojipickerBundle.message("message.EmojiPicker.NoEmojiFound");
int x = (getWidth() - g.getFontMetrics().stringWidth(message)) / 2;
int y = (getHeight() + NO_EMOJI_FOUND_IMAGE.getHeight(this)) / 2 + 25;
g.drawString(message, x, y);
}
private void paintSelection(Graphics g) {
if(myCurrentItemCategory == this && myCurrentItemIndex != -1) {
if (myCurrentItemCategory == this && myCurrentItemIndex != -1) {
int x = xGridToVisible(myCurrentItemIndex % myItemsPerRow);
int y = yGridToVisible(myCurrentItemIndex / myItemsPerRow);
g.setColor(myStyle.myHoverBackgroundColor);
@@ -317,18 +332,18 @@ class EmojiListPanel extends JBScrollPane {
private void paintItems(Graphics g) {
Rectangle bounds = g.getClipBounds();
int from = yVisibleToGrid(bounds.y) * myItemsPerRow, to = yVisibleToGrid(bounds.y + bounds.height) * myItemsPerRow + myItemsPerRow;
if(from < 0) from = 0;
if (from < 0) from = 0;
int itemCount = myCategory.getEmoji().size();
if(to > itemCount) to = itemCount;
if(from > to) from = to;
if (to > itemCount) to = itemCount;
if (from > to) from = to;
FontMetrics metrics = g.getFontMetrics(myStyle.myEmojiFont);
int verticalOffset = metrics.getHeight() / 2 - metrics.getDescent();
for (int i = from; i < to; i++) {
int x = xGridToVisible(i % myItemsPerRow), y = yGridToVisible(i / myItemsPerRow);
String item = myCategory.getEmoji().get(i).getTonedValue(myCurrentSkinTone);
@Nls String item = myCategory.getEmoji().get(i).getTonedValue(myCurrentSkinTone);
int width = metrics.stringWidth(item);
new TextLayout(item, myStyle.myEmojiFont, ((Graphics2D) g).getFontRenderContext())
.draw((Graphics2D) g,x + (myCellSize.width - width) / 2F, y + myCellSize.height / 2F + verticalOffset);
new TextLayout(item, myStyle.myEmojiFont, ((Graphics2D)g).getFontRenderContext())
.draw((Graphics2D)g, x + (myCellSize.width - width) / 2F, y + myCellSize.height / 2F + verticalOffset);
}
}
@@ -340,8 +355,5 @@ class EmojiListPanel extends JBScrollPane {
g.setColor(myStyle.myTextColor);
g.drawString(myName, 16, offset + 20);
}
}
}
@@ -5,6 +5,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.ui.WindowMoveListener;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.plugins.emojipicker.Emoji;
import org.jetbrains.plugins.emojipicker.EmojiCategory;
import org.jetbrains.plugins.emojipicker.EmojiSkinTone;
@@ -22,8 +23,6 @@ import java.util.function.Consumer;
public class EmojiPicker extends JLayeredPane {
private final List<EmojiCategory> myCategories;
private final EmojiSearchField mySearchField;
private final EmojiCategoryPanel myCategoryPanel;
@@ -32,7 +31,8 @@ public class EmojiPicker extends JLayeredPane {
private final JPanel myEmojiSkinTonesModal;
private final EmojiSkinTonesPanel myEmojiSkinTonesPanel;
private final KeyboardManager myKeyboardManager;
private Consumer<String> myInputCallback = emoji -> {};
private Consumer<String> myInputCallback = emoji -> {
};
private EmojiPicker() {
@@ -43,7 +43,7 @@ public class EmojiPicker extends JLayeredPane {
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.gridx = 0;
GridBagConstraints fillerGbc = (GridBagConstraints) gbc.clone();
GridBagConstraints fillerGbc = (GridBagConstraints)gbc.clone();
fillerGbc.weighty = 1;
EmojiPickerStyle style = new EmojiPickerStyle();
@@ -78,6 +78,7 @@ public class EmojiPicker extends JLayeredPane {
selectCategory(myCategories.get(0), true);
selectSkinToneFromPanel();
}
private static void applyWindowMoveListener(Component c) {
WindowMoveListener listener = new WindowMoveListener(c);
c.addMouseListener(listener);
@@ -86,14 +87,14 @@ public class EmojiPicker extends JLayeredPane {
void selectCategory(EmojiCategory category, boolean scrollToCategory) {
myCategoryPanel.selectCategory(category);
if(scrollToCategory) {
if(!mySearchField.getText().isEmpty()) mySearchField.setText("");
if (scrollToCategory) {
if (!mySearchField.getText().isEmpty()) mySearchField.setText("");
myEmojiListPanel.selectCategory(category);
myKeyboardManager.resetFocus();
}
}
void search(String text) {
void search(@NonNls String text) {
myEmojiListPanel.updateSearchFilter(text);
myKeyboardManager.resetFocus();
myKeyboardManager.emojiNavigationMode = false;
@@ -103,7 +104,7 @@ public class EmojiPicker extends JLayeredPane {
myEmojiInfoPanel.showEmojiInfo(emoji, skinTone);
}
void selectEmoji(String emoji) {
void selectEmoji(@NonNls String emoji) {
myInputCallback.accept(emoji);
}
@@ -149,19 +150,18 @@ public class EmojiPicker extends JLayeredPane {
.setShowBorder(false)
.createPopup();
picker.myInputCallback = emoji -> {
if(emoji != null) {
if (emoji != null) {
inputCallback.accept(emoji);
popup.closeOk(null);
}
else popup.cancel();
else {
popup.cancel();
}
};
return popup;
}
private class KeyboardManager {
private final List<Object> focusTargets = new ArrayList<>();
@@ -175,7 +175,7 @@ public class EmojiPicker extends JLayeredPane {
}
private void resetFocus() {
if(currentFocusTarget != 0) {
if (currentFocusTarget != 0) {
currentFocusTarget = 0;
emojiNavigationMode = false;
myCategoryPanel.repaint();
@@ -185,8 +185,8 @@ public class EmojiPicker extends JLayeredPane {
private boolean handleKey(int k, int modifiers) {
Object focus = focusTargets.get(currentFocusTarget);
if(focus == myEmojiSkinTonesPanel) {
if(myEmojiSkinTonesPanel.isVisible()) {
if (focus == myEmojiSkinTonesPanel) {
if (myEmojiSkinTonesPanel.isVisible()) {
switch (k) {
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
@@ -201,7 +201,7 @@ public class EmojiPicker extends JLayeredPane {
return true;
}
}
else if(k == KeyEvent.VK_ENTER || k == KeyEvent.VK_SPACE) {
else if (k == KeyEvent.VK_ENTER || k == KeyEvent.VK_SPACE) {
openSkinToneSelectionPanel();
myEmojiSkinTonesPanel.focusItem(1);
return true;
@@ -222,29 +222,31 @@ public class EmojiPicker extends JLayeredPane {
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
resetFocus();
if(!emojiNavigationMode) {
if (!emojiNavigationMode) {
emojiNavigationMode = k == KeyEvent.VK_UP || k == KeyEvent.VK_DOWN ||
(k == KeyEvent.VK_LEFT && mySearchField.getCaretPosition() == 0) ||
(k == KeyEvent.VK_RIGHT &&
mySearchField.getCaretPosition() == mySearchField.getText().length());
}
if(!myEmojiListPanel.hasCurrentItem()) emojiNavigationMode = false;
if(emojiNavigationMode) {
if (!myEmojiListPanel.hasCurrentItem()) emojiNavigationMode = false;
if (emojiNavigationMode) {
myEmojiListPanel.navigate(
keyToOffset(k, KeyEvent.VK_RIGHT, KeyEvent.VK_LEFT),
keyToOffset(k, KeyEvent.VK_DOWN, KeyEvent.VK_UP)
);
return true;
}
else return false;
else {
return false;
}
}
if(focus instanceof EmojiCategory) {
if(k == KeyEvent.VK_ENTER || k == KeyEvent.VK_SPACE) {
selectCategory((EmojiCategory) focus, true);
if (focus instanceof EmojiCategory) {
if (k == KeyEvent.VK_ENTER || k == KeyEvent.VK_SPACE) {
selectCategory((EmojiCategory)focus, true);
return true;
}
}
else if(focus == mySearchField && k == KeyEvent.VK_ENTER) {
else if (focus == mySearchField && k == KeyEvent.VK_ENTER) {
myEmojiListPanel.selectCurrentEmoji();
return true;
}
@@ -252,50 +254,53 @@ public class EmojiPicker extends JLayeredPane {
}
private int keyToOffset(int key, int positive, int negative) {
if(key == positive) return 1;
if(key == negative) return -1;
if (key == positive) return 1;
if (key == negative) return -1;
return 0;
}
}
private static class JPanelFillLayout implements LayoutManager2 {
@Override
public void addLayoutComponent(Component comp, Object constraints) {}
@Override
public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); }
@Override
public float getLayoutAlignmentX(Container target) { return 0.5f; }
@Override
public float getLayoutAlignmentY(Container target) { return 0.5f; }
@Override
public void invalidateLayout(Container target) {}
@Override
public void addLayoutComponent(String name, Component comp) {}
@Override
public void removeLayoutComponent(Component comp) {}
@Override
public Dimension preferredLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
return parent.getSize();
}
}
@Override
public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); }
@Override
public void layoutContainer(Container parent) {
synchronized (parent.getTreeLock()) {
Dimension size = parent.getSize();
for (int i = 0; i < parent.getComponentCount(); i++) {
if(parent.getComponent(i).getClass() == JPanel.class) parent.getComponent(i).setBounds(0, 0, size.width, size.height);
if (parent.getComponent(i).getClass() == JPanel.class) parent.getComponent(i).setBounds(0, 0, size.width, size.height);
}
}
}
}
}
@@ -11,7 +11,6 @@ import java.util.stream.Stream;
class EmojiPickerStyle {
final Font myFont = JBUI.Fonts.label().deriveFont(Font.BOLD, JBUIScale.scale(13F));
final Font myLightFont = myFont.deriveFont(Font.PLAIN);
final Font myEmojiFont = Stream.of(GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts())
@@ -28,5 +27,4 @@ class EmojiPickerStyle {
final Color myFocusBorderColor = new JBColor(0x97C3F3, 0x97C3F3);
final Color myTextColor = JBUI.CurrentTheme.Label.foreground();
final Color myNoEmojiFoundTextColor = new JBColor(0x808080, 0xBBBBBB);
}
@@ -23,10 +23,12 @@ public class EmojiSearchField extends ExtendableTextField {
public Icon getIcon(boolean hovered) {
return AllIcons.Actions.Search;
}
@Override
public boolean isIconBeforeText() {
return true;
}
@Override
public int getIconGap() {
return JBUIScale.scale(6);
@@ -45,14 +47,17 @@ public class EmojiSearchField extends ExtendableTextField {
public Icon getIcon(boolean hovered) {
return emojiPicker.getCurrentSkinToneIcon(hovered);
}
@Override
public boolean isIconBeforeText() {
return false;
}
@Override
public int getIconGap() {
return JBUIScale.scale(6);
}
@Override
public Runnable getActionOnClick() {
return emojiPicker::openSkinToneSelectionPanel;
@@ -76,14 +81,16 @@ public class EmojiSearchField extends ExtendableTextField {
void handle(KeyEvent e, boolean typed) {
int c = typed ? e.getKeyChar() : e.getKeyCode();
boolean mayIntercept = c == KeyEvent.VK_SPACE || c == KeyEvent.VK_ENTER;
if(mayIntercept == typed && emojiPicker.handleKey(c, e.getModifiersEx())) {
if (mayIntercept == typed && emojiPicker.handleKey(c, e.getModifiersEx())) {
e.consume();
}
}
@Override
public void keyTyped(KeyEvent e) {
handle(e, true);
}
@Override
public void keyPressed(KeyEvent e) {
handle(e, false);
@@ -98,12 +105,11 @@ public class EmojiSearchField extends ExtendableTextField {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(myEmojiPicker.getCurrentFocusTarget() instanceof EmojiSkinTonesPanel) {
if (myEmojiPicker.getCurrentFocusTarget() instanceof EmojiSkinTonesPanel) {
g.setColor(myStyle.myFocusBorderColor);
Insets i = getInsets();
final int SIZE = 30;
g.drawRoundRect(getWidth() - i.right - SIZE - 8, getHeight() / 2 - SIZE / 2, SIZE, SIZE, 6, 6);
}
}
}
@@ -3,6 +3,7 @@ package org.jetbrains.plugins.emojipicker.ui;
import com.intellij.ide.ui.AntialiasingType;
import com.intellij.openapi.ui.popup.IconButton;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.ui.InplaceButton;
import org.jetbrains.plugins.emojipicker.EmojiSkinTone;
import org.jetbrains.plugins.emojipicker.service.EmojiService;
@@ -14,10 +15,8 @@ import java.util.EnumMap;
import java.util.Map;
public class EmojiSkinTonesPanel extends JPanel {
private static final int ITEM_SIZE = 34;
private static final String ITEM_EMOJI = "🖐";
@NlsSafe private static final String ITEM_EMOJI = "🖐";
private final EmojiPickerStyle myStyle;
private final Map<EmojiSkinTone, Item> myItemMap = new EnumMap<>(EmojiSkinTone.class);
@@ -28,7 +27,7 @@ public class EmojiSkinTonesPanel extends JPanel {
myStyle = style;
setVisible(false);
setLayout(new GridBagLayout());
for(EmojiSkinTone tone : EmojiSkinTone.values()) {
for (EmojiSkinTone tone : EmojiSkinTone.values()) {
Item item = new Item(emojiPicker, tone);
myItemMap.put(tone, item);
}
@@ -45,7 +44,7 @@ public class EmojiSkinTonesPanel extends JPanel {
@Override
public void paint(Graphics g) {
super.paint(g);
if(myFocusedItem != -1) {
if (myFocusedItem != -1) {
g.setColor(myStyle.myFocusBorderColor);
g.drawRoundRect(3, myFocusedItem * ITEM_SIZE + 3, ITEM_SIZE - 4, ITEM_SIZE - 4, 6, 6);
}
@@ -66,8 +65,8 @@ public class EmojiSkinTonesPanel extends JPanel {
void setCurrentItemFromFocus() {
Component c = getComponent(myFocusedItem);
for(Item item : myItemMap.values()) {
if(item.myButton == c) {
for (Item item : myItemMap.values()) {
if (item.myButton == c) {
myCurrentSkinTone = item.myTone;
return;
}
@@ -84,8 +83,8 @@ public class EmojiSkinTonesPanel extends JPanel {
gbc.gridx = 0;
removeAll();
add(myItemMap.get(myCurrentSkinTone).myButton, gbc);
for(Map.Entry<EmojiSkinTone, Item> item : myItemMap.entrySet()) {
if(item.getKey() != myCurrentSkinTone) add(item.getValue().myButton, gbc);
for (Map.Entry<EmojiSkinTone, Item> item : myItemMap.entrySet()) {
if (item.getKey() != myCurrentSkinTone) add(item.getValue().myButton, gbc);
}
doLayout();
setVisible(true);
@@ -114,33 +113,34 @@ public class EmojiSkinTonesPanel extends JPanel {
private class Icon implements javax.swing.Icon {
private final boolean myHover;
private Icon(boolean hover) { myHover = hover; }
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false));
if(myHover) {
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false));
if (myHover) {
g.setColor(myStyle.myHoverBackgroundColor);
g.fillRoundRect(x, y, getIconWidth(), getIconHeight(), 6, 6);
}
String emoji = ITEM_EMOJI + myTone.getStringValue();
@NlsSafe String emoji = ITEM_EMOJI + myTone.getStringValue();
FontMetrics metrics = g.getFontMetrics(myStyle.myEmojiFont);
int verticalOffset = metrics.getHeight() / 2 - metrics.getDescent();
int width = metrics.stringWidth(emoji);
new TextLayout(emoji, myStyle.myEmojiFont, ((Graphics2D) g).getFontRenderContext())
.draw((Graphics2D) g, x + (getIconWidth() - width) / 2F, y + getIconHeight() / 2F + verticalOffset);
new TextLayout(emoji, myStyle.myEmojiFont, ((Graphics2D)g).getFontRenderContext())
.draw((Graphics2D)g, x + (getIconWidth() - width) / 2F, y + getIconHeight() / 2F + verticalOffset);
}
@Override
public int getIconWidth() {
return ITEM_SIZE;
}
@Override
public int getIconHeight() {
return ITEM_SIZE;
}
}
}
}