From 84164e2ca9b05b264e29312909f9ed70ab8d98a7 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sat, 2 Nov 2019 14:39:41 -0500 Subject: [PATCH] emoji icons in menu --- src/jexer/backend/GlyphMaker.java | 8 ++-- src/jexer/menu/TMenu.java | 37 +++++++++++++++--- src/jexer/menu/TMenuItem.java | 63 ++++++++++++++++++++++++++++--- 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/src/jexer/backend/GlyphMaker.java b/src/jexer/backend/GlyphMaker.java index 0da2918..84316b7 100644 --- a/src/jexer/backend/GlyphMaker.java +++ b/src/jexer/backend/GlyphMaker.java @@ -139,7 +139,7 @@ class GlyphMakerFont { if (filename.length() == 0) { // Fallback font - font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize); + font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); return; } @@ -148,16 +148,16 @@ class GlyphMakerFont { ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream in = loader.getResourceAsStream(filename); fontRoot = Font.createFont(Font.TRUETYPE_FONT, in); - font = fontRoot.deriveFont(Font.PLAIN, fontSize); + font = fontRoot.deriveFont(Font.PLAIN, fontSize - 2); } catch (java.awt.FontFormatException e) { // Ideally we would report an error here, either via System.err // or TExceptionDialog. However, I do not want GlyphMaker to // know about available backends, so we quietly fallback to // whatever is available as MONO. - font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize); + font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); } catch (java.io.IOException e) { // See comment above. - font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize); + font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); } } diff --git a/src/jexer/menu/TMenu.java b/src/jexer/menu/TMenu.java index 6d746df..a44de72 100644 --- a/src/jexer/menu/TMenu.java +++ b/src/jexer/menu/TMenu.java @@ -152,6 +152,11 @@ public class TMenu extends TWindow { */ private MnemonicString mnemonic; + /** + * If true, draw icons with menu items. Note package private access. + */ + boolean useIcons = false; + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -182,6 +187,11 @@ public class TMenu extends TWindow { setHeight(2); setActive(false); + + if (System.getProperty("jexer.menuIcons", "false").equals("true")) { + useIcons = true; + } + } // ------------------------------------------------------------------------ @@ -446,7 +456,7 @@ public class TMenu extends TWindow { final boolean enabled) { assert (id >= 1024); - return addItemInternal(id, label, null, enabled); + return addItemInternal(id, label, null, enabled, -1); } /** @@ -492,7 +502,7 @@ public class TMenu extends TWindow { private TMenuItem addItemInternal(final int id, final String label, final TKeypress key) { - return addItemInternal(id, label, key, true); + return addItemInternal(id, label, key, true, -1); } /** @@ -502,15 +512,16 @@ public class TMenu extends TWindow { * @param label menu item label * @param key global keyboard accelerator * @param enabled default state for enabled + * @param icon icon picture/emoji * @return the new menu item */ private TMenuItem addItemInternal(final int id, final String label, - final TKeypress key, final boolean enabled) { + final TKeypress key, final boolean enabled, final int icon) { int newY = getChildren().size() + 1; assert (newY < getHeight()); - TMenuItem menuItem = new TMenuItem(this, id, 1, newY, label); + TMenuItem menuItem = new TMenuItem(this, id, 1, newY, label, icon); menuItem.setKey(key); menuItem.setEnabled(enabled); setHeight(getHeight() + 1); @@ -551,6 +562,7 @@ public class TMenu extends TWindow { String label; TKeypress key = null; + int icon = -1; boolean checkable = false; boolean checked = false; @@ -558,6 +570,7 @@ public class TMenu extends TWindow { case MID_REPAINT: label = i18n.getString("menuRepaintDesktop"); + icon = 0x1F3A8; break; case MID_VIEW_IMAGE: @@ -570,41 +583,48 @@ public class TMenu extends TWindow { case MID_NEW: label = i18n.getString("menuNew"); + icon = 0x1F5CE; break; case MID_EXIT: label = i18n.getString("menuExit"); key = kbAltX; + icon = 0x1F5D9; break; case MID_SHELL: label = i18n.getString("menuShell"); + icon = 0x1F5AE; break; case MID_OPEN_FILE: label = i18n.getString("menuOpen"); key = kbF3; + icon = 0x1F5C1; break; case MID_CUT: label = i18n.getString("menuCut"); key = kbCtrlX; + icon = 0x1F5F6; break; case MID_COPY: label = i18n.getString("menuCopy"); key = kbCtrlC; + icon = 0x1F5D0; break; case MID_PASTE: label = i18n.getString("menuPaste"); key = kbCtrlV; + icon = 0x1F4CB; break; case MID_CLEAR: label = i18n.getString("menuClear"); - // key = kbDel; break; case MID_FIND: label = i18n.getString("menuFind"); + icon = 0x1F50D; break; case MID_REPLACE: label = i18n.getString("menuReplace"); @@ -622,6 +642,7 @@ public class TMenu extends TWindow { break; case MID_CASCADE: label = i18n.getString("menuWindowCascade"); + icon = 0x1F5D7; break; case MID_CLOSE_ALL: label = i18n.getString("menuWindowCloseAll"); @@ -629,18 +650,22 @@ public class TMenu extends TWindow { case MID_WINDOW_MOVE: label = i18n.getString("menuWindowMove"); key = kbCtrlF5; + icon = 0x263C; break; case MID_WINDOW_ZOOM: label = i18n.getString("menuWindowZoom"); key = kbF5; + icon = 0x2195; break; case MID_WINDOW_NEXT: label = i18n.getString("menuWindowNext"); key = kbF6; + icon = 0x2192; break; case MID_WINDOW_PREVIOUS: label = i18n.getString("menuWindowPrevious"); key = kbShiftF6; + icon = 0x2190; break; case MID_WINDOW_CLOSE: label = i18n.getString("menuWindowClose"); @@ -775,7 +800,7 @@ public class TMenu extends TWindow { throw new IllegalArgumentException("Invalid menu ID: " + id); } - TMenuItem item = addItemInternal(id, label, key, enabled); + TMenuItem item = addItemInternal(id, label, key, enabled, icon); item.setCheckable(checkable); return item; } diff --git a/src/jexer/menu/TMenuItem.java b/src/jexer/menu/TMenuItem.java index d9dfc2a..b478059 100644 --- a/src/jexer/menu/TMenuItem.java +++ b/src/jexer/menu/TMenuItem.java @@ -80,6 +80,11 @@ public class TMenuItem extends TWidget { */ private MnemonicString mnemonic; + /** + * An optional 2-cell-wide picture/icon for this item. + */ + private int icon = -1; + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -96,6 +101,22 @@ public class TMenuItem extends TWidget { TMenuItem(final TMenu parent, final int id, final int x, final int y, final String label) { + this(parent, id, x, y, label, -1); + } + + /** + * Package private constructor. + * + * @param parent parent widget + * @param id menu id + * @param x column relative to parent + * @param y row relative to parent + * @param label menu item title + * @param icon icon picture/emoji + */ + TMenuItem(final TMenu parent, final int id, final int x, final int y, + final String label, final int icon) { + // Set parent and window super(parent); @@ -105,8 +126,13 @@ public class TMenuItem extends TWidget { setY(y); setHeight(1); this.label = mnemonic.getRawLabel(); - setWidth(StringUtils.width(label) + 4); + if (parent.useIcons) { + setWidth(StringUtils.width(label) + 6); + } else { + setWidth(StringUtils.width(label) + 4); + } this.id = id; + this.icon = icon; // Default state for some known menu items switch (id) { @@ -220,26 +246,31 @@ public class TMenuItem extends TWidget { } } + boolean useIcons = ((TMenu) getParent()).useIcons; + char cVSide = GraphicsChars.WINDOW_SIDE; vLineXY(0, 0, 1, cVSide, background); vLineXY(getWidth() - 1, 0, 1, cVSide, background); hLineXY(1, 0, getWidth() - 2, ' ', menuColor); - putStringXY(2, 0, mnemonic.getRawLabel(), menuColor); + putStringXY(2 + (useIcons ? 2 : 0), 0, mnemonic.getRawLabel(), + menuColor); if (key != null) { String keyLabel = key.toString(); putStringXY((getWidth() - StringUtils.width(keyLabel) - 2), 0, keyLabel, menuColor); } if (mnemonic.getScreenShortcutIdx() >= 0) { - putCharXY(2 + mnemonic.getScreenShortcutIdx(), 0, - mnemonic.getShortcut(), menuMnemonicColor); + putCharXY(2 + (useIcons ? 2 : 0) + mnemonic.getScreenShortcutIdx(), + 0, mnemonic.getShortcut(), menuMnemonicColor); } if (checked) { assert (checkable); putCharXY(1, 0, GraphicsChars.CHECK, menuColor); } - + if ((useIcons == true) && (icon != -1)) { + putCharXY(2, 0, icon, menuColor); + } } // ------------------------------------------------------------------------ @@ -318,12 +349,34 @@ public class TMenuItem extends TWidget { if (key != null) { int newWidth = (StringUtils.width(label) + 4 + StringUtils.width(key.toString()) + 2); + if (((TMenu) getParent()).useIcons) { + newWidth += 2; + } if (newWidth > getWidth()) { setWidth(newWidth); } } } + /** + * Get a picture/emoji icon for this menu item. + * + * @return the codepoint, or -1 if no icon is specified for this menu + * item + */ + public final int getIcon() { + return icon; + } + + /** + * Set a picture/emoji icon for this menu item. + * + * @param icon a codepoint, or -1 to unset the icon + */ + public final void setIcon(final int icon) { + this.icon = icon; + } + /** * Dispatch event(s) due to selection or click. */ -- 2.27.0