From 9f613a0c54cb97e9305fd87ce8eb2f76ac82804e Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Thu, 8 Aug 2019 18:01:52 -0500 Subject: [PATCH] cjk support wip --- src/jexer/TCheckBox.java | 3 ++- src/jexer/TLabel.java | 3 ++- src/jexer/TList.java | 6 ++++-- src/jexer/TRadioButton.java | 7 ++++--- src/jexer/TRadioGroup.java | 9 +++++---- src/jexer/TStatusBar.java | 14 ++++++++------ src/jexer/backend/ECMA48Terminal.java | 13 ++++++++----- src/jexer/backend/SwingTerminal.java | 19 ++++++++++++++++--- src/jexer/bits/MnemonicString.java | 20 ++++++++++++++++++++ src/jexer/menu/TMenu.java | 3 ++- src/jexer/menu/TMenuItem.java | 10 ++++++---- src/jexer/ttree/TTreeItem.java | 9 +++++---- 12 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/jexer/TCheckBox.java b/src/jexer/TCheckBox.java index 9cdb303..1e86470 100644 --- a/src/jexer/TCheckBox.java +++ b/src/jexer/TCheckBox.java @@ -34,6 +34,7 @@ import static jexer.TKeypress.kbSpace; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; import jexer.bits.MnemonicString; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; @@ -78,7 +79,7 @@ public class TCheckBox extends TWidget { final String label, final boolean checked) { // Set parent and window - super(parent, x, y, label.length() + 4, 1); + super(parent, x, y, StringUtils.width(label) + 4, 1); mnemonic = new MnemonicString(label); this.checked = checked; diff --git a/src/jexer/TLabel.java b/src/jexer/TLabel.java index c34aee1..1d65976 100644 --- a/src/jexer/TLabel.java +++ b/src/jexer/TLabel.java @@ -30,6 +30,7 @@ package jexer; import jexer.bits.CellAttributes; import jexer.bits.MnemonicString; +import jexer.bits.StringUtils; /** * TLabel implements a simple label, with an optional mnemonic hotkey action @@ -157,7 +158,7 @@ public class TLabel extends TWidget { final TAction action) { // Set parent and window - super(parent, false, x, y, text.length(), 1); + super(parent, false, x, y, StringUtils.width(text), 1); mnemonic = new MnemonicString(text); this.colorKey = colorKey; diff --git a/src/jexer/TList.java b/src/jexer/TList.java index f9e7216..a962f7c 100644 --- a/src/jexer/TList.java +++ b/src/jexer/TList.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import jexer.bits.CellAttributes; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import static jexer.TKeypress.*; @@ -338,8 +339,9 @@ public class TList extends TScrollableWidget { for (int i = 0; i < strings.size(); i++) { String line = strings.get(i); - if (line.length() > maxLineWidth) { - maxLineWidth = line.length(); + int lineLength = StringUtils.width(line); + if (lineLength > maxLineWidth) { + maxLineWidth = lineLength; } } diff --git a/src/jexer/TRadioButton.java b/src/jexer/TRadioButton.java index e3d98a9..2d602fa 100644 --- a/src/jexer/TRadioButton.java +++ b/src/jexer/TRadioButton.java @@ -31,6 +31,7 @@ package jexer; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; import jexer.bits.MnemonicString; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import static jexer.TKeypress.*; @@ -81,7 +82,7 @@ public class TRadioButton extends TWidget { final String label, final int id) { // Set parent and window - super(parent, x, y, label.length() + 4, 1); + super(parent, x, y, StringUtils.width(label) + 4, 1); mnemonic = new MnemonicString(label); this.id = id; @@ -179,8 +180,8 @@ public class TRadioButton extends TWidget { } putCharXY(2, 0, ')', radioButtonColor); putStringXY(4, 0, mnemonic.getRawLabel(), radioButtonColor); - if (mnemonic.getShortcutIdx() >= 0) { - putCharXY(4 + mnemonic.getShortcutIdx(), 0, + if (mnemonic.getScreenShortcutIdx() >= 0) { + putCharXY(4 + mnemonic.getScreenShortcutIdx(), 0, mnemonic.getShortcut(), mnemonicColor); } } diff --git a/src/jexer/TRadioGroup.java b/src/jexer/TRadioGroup.java index 0f84e71..7460de4 100644 --- a/src/jexer/TRadioGroup.java +++ b/src/jexer/TRadioGroup.java @@ -29,6 +29,7 @@ package jexer; import jexer.bits.CellAttributes; +import jexer.bits.StringUtils; /** * TRadioGroup is a collection of TRadioButtons with a box and label. @@ -71,7 +72,7 @@ public class TRadioGroup extends TWidget { final String label) { // Set parent and window - super(parent, x, y, label.length() + 4, 2); + super(parent, x, y, StringUtils.width(label) + 4, 2); this.label = label; } @@ -96,7 +97,7 @@ public class TRadioGroup extends TWidget { drawBox(0, 0, getWidth(), getHeight(), radioGroupColor, radioGroupColor, 3, false); - hLineXY(1, 0, label.length() + 2, ' ', radioGroupColor); + hLineXY(1, 0, StringUtils.width(label) + 2, ' ', radioGroupColor); putStringXY(2, 0, label, radioGroupColor); } @@ -161,8 +162,8 @@ public class TRadioGroup extends TWidget { public TRadioButton addRadioButton(final String label) { int buttonX = 1; int buttonY = getChildren().size() + 1; - if (label.length() + 4 > getWidth()) { - setWidth(label.length() + 7); + if (StringUtils.width(label) + 4 > getWidth()) { + setWidth(StringUtils.width(label) + 7); } setHeight(getChildren().size() + 3); TRadioButton button = new TRadioButton(this, buttonX, buttonY, label, diff --git a/src/jexer/TStatusBar.java b/src/jexer/TStatusBar.java index 98565d0..fbd79da 100644 --- a/src/jexer/TStatusBar.java +++ b/src/jexer/TStatusBar.java @@ -33,6 +33,7 @@ import java.util.List; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; +import jexer.bits.StringUtils; import jexer.event.TCommandEvent; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; @@ -97,7 +98,8 @@ public class TStatusBar extends TWidget { * @return the number of columns this takes when drawn */ public int width() { - return this.label.length() + this.key.toString().length() + 3; + return StringUtils.width(this.label) + + StringUtils.width(this.key.toString()) + 3; } /** @@ -132,7 +134,7 @@ public class TStatusBar extends TWidget { // TStatusBar is a parentless widget, because TApplication handles // its drawing and event routing directly. - super(null, false, 0, 0, text.length(), 1); + super(null, false, 0, 0, StringUtils.width(text), 1); this.text = text; setWindow(window); @@ -269,17 +271,17 @@ public class TStatusBar extends TWidget { if (key.selected) { putCharXY(col++, row, ' ', selectedColor); putStringXY(col, row, keyStr, selectedColor); - col += keyStr.length(); + col += StringUtils.width(keyStr); putCharXY(col++, row, ' ', selectedColor); putStringXY(col, row, key.label, selectedColor); - col += key.label.length(); + col += StringUtils.width(key.label); putCharXY(col++, row, ' ', selectedColor); } else { putCharXY(col++, row, ' ', barColor); putStringXY(col, row, keyStr, keyColor); - col += keyStr.length() + 1; + col += StringUtils.width(keyStr) + 1; putStringXY(col, row, key.label, barColor); - col += key.label.length(); + col += StringUtils.width(key.label); putCharXY(col++, row, ' ', barColor); } } diff --git a/src/jexer/backend/ECMA48Terminal.java b/src/jexer/backend/ECMA48Terminal.java index 252e80c..ca1d499 100644 --- a/src/jexer/backend/ECMA48Terminal.java +++ b/src/jexer/backend/ECMA48Terminal.java @@ -1002,7 +1002,8 @@ public class ECMA48Terminal extends LogicalScreen // ------------------------------------------------------------------------ /** - * Constructor sets up state for getEvent(). + * Constructor sets up state for getEvent(). If either windowWidth or + * windowHeight are less than 1, the terminal is not resized. * * @param listener the object this backend needs to wake up when new * input comes in @@ -1026,10 +1027,12 @@ public class ECMA48Terminal extends LogicalScreen // Send dtterm/xterm sequences, which will probably not work because // allowWindowOps is defaulted to false. - String resizeString = String.format("\033[8;%d;%dt", windowHeight, - windowWidth); - this.output.write(resizeString); - this.output.flush(); + if ((windowWidth > 0) && (windowHeight > 0)) { + String resizeString = String.format("\033[8;%d;%dt", windowHeight, + windowWidth); + this.output.write(resizeString); + this.output.flush(); + } } /** diff --git a/src/jexer/backend/SwingTerminal.java b/src/jexer/backend/SwingTerminal.java index 58b8f79..bd1ba87 100644 --- a/src/jexer/backend/SwingTerminal.java +++ b/src/jexer/backend/SwingTerminal.java @@ -1265,18 +1265,31 @@ public class SwingTerminal extends LogicalScreen int xPixel = cursorX * textWidth + left; int yPixel = cursorY * textHeight + top; Cell lCell = logical[cursorX][cursorY]; + int cursorWidth = textWidth; + switch (lCell.getWidth()) { + case SINGLE: + // NOP + break; + case LEFT: + cursorWidth *= 2; + break; + case RIGHT: + cursorWidth *= 2; + xPixel -= textWidth; + break; + } gr.setColor(attrToForegroundColor(lCell)); switch (cursorStyle) { default: // Fall through... case UNDERLINE: - gr.fillRect(xPixel, yPixel + textHeight - 2, textWidth, 2); + gr.fillRect(xPixel, yPixel + textHeight - 2, cursorWidth, 2); break; case BLOCK: - gr.fillRect(xPixel, yPixel, textWidth, textHeight); + gr.fillRect(xPixel, yPixel, cursorWidth, textHeight); break; case OUTLINE: - gr.drawRect(xPixel, yPixel, textWidth - 1, textHeight - 1); + gr.drawRect(xPixel, yPixel, cursorWidth - 1, textHeight - 1); break; } } diff --git a/src/jexer/bits/MnemonicString.java b/src/jexer/bits/MnemonicString.java index 5977ed5..2d5dbc8 100644 --- a/src/jexer/bits/MnemonicString.java +++ b/src/jexer/bits/MnemonicString.java @@ -50,6 +50,12 @@ public class MnemonicString { */ private int shortcutIdx = -1; + /** + * Screen location of the highlighted character (number of text cells + * required to display from the beginning to shortcutIdx). + */ + private int screenShortcutIdx = -1; + /** * The raw (uncolored) string. */ @@ -72,12 +78,14 @@ public class MnemonicString { boolean foundAmp = false; boolean foundShortcut = false; int scanShortcutIdx = 0; + int scanScreenShortcutIdx = 0; for (int i = 0; i < label.length(); i++) { char c = label.charAt(i); if (c == '&') { if (foundAmp) { newLabel += '&'; scanShortcutIdx++; + scanScreenShortcutIdx++; } else { foundAmp = true; } @@ -89,9 +97,11 @@ public class MnemonicString { foundAmp = false; foundShortcut = true; shortcutIdx = scanShortcutIdx; + screenShortcutIdx = scanScreenShortcutIdx; } } else { scanShortcutIdx++; + scanScreenShortcutIdx += StringUtils.width(c); } } } @@ -120,6 +130,16 @@ public class MnemonicString { return shortcutIdx; } + /** + * Get the screen location of the highlighted character. + * + * @return the number of text cells required to display from the + * beginning of the label to shortcutIdx + */ + public int getScreenShortcutIdx() { + return screenShortcutIdx; + } + /** * Get the raw (uncolored) string. * diff --git a/src/jexer/menu/TMenu.java b/src/jexer/menu/TMenu.java index 1d6a6ad..58228f9 100644 --- a/src/jexer/menu/TMenu.java +++ b/src/jexer/menu/TMenu.java @@ -37,6 +37,7 @@ import jexer.TWindow; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; import jexer.bits.MnemonicString; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import static jexer.TKeypress.*; @@ -177,7 +178,7 @@ public class TMenu extends TWindow { assert (mnemonic.getShortcutIdx() >= 0); // Recompute width and height to reflect an empty menu - setWidth(getTitle().length() + 4); + setWidth(StringUtils.width(getTitle()) + 4); setHeight(2); setActive(false); diff --git a/src/jexer/menu/TMenuItem.java b/src/jexer/menu/TMenuItem.java index bd149ab..eafa9e5 100644 --- a/src/jexer/menu/TMenuItem.java +++ b/src/jexer/menu/TMenuItem.java @@ -33,6 +33,7 @@ import jexer.TWidget; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; import jexer.bits.MnemonicString; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import jexer.event.TMenuEvent; @@ -104,7 +105,7 @@ public class TMenuItem extends TWidget { setY(y); setHeight(1); this.label = mnemonic.getRawLabel(); - setWidth(label.length() + 4); + setWidth(StringUtils.width(label) + 4); this.id = id; // Default state for some known menu items @@ -227,8 +228,8 @@ public class TMenuItem extends TWidget { putStringXY(2, 0, mnemonic.getRawLabel(), menuColor); if (key != null) { String keyLabel = key.toString(); - putStringXY((getWidth() - keyLabel.length() - 2), 0, keyLabel, - menuColor); + putStringXY((getWidth() - StringUtils.width(keyLabel) - 2), 0, + keyLabel, menuColor); } if (mnemonic.getShortcutIdx() >= 0) { putCharXY(2 + mnemonic.getShortcutIdx(), 0, mnemonic.getShortcut(), @@ -315,7 +316,8 @@ public class TMenuItem extends TWidget { this.key = key; if (key != null) { - int newWidth = (label.length() + 4 + key.toString().length() + 2); + int newWidth = (StringUtils.width(label) + 4 + + StringUtils.width(key.toString()) + 2); if (newWidth > getWidth()) { setWidth(newWidth); } diff --git a/src/jexer/ttree/TTreeItem.java b/src/jexer/ttree/TTreeItem.java index 759bfb7..44c408b 100644 --- a/src/jexer/ttree/TTreeItem.java +++ b/src/jexer/ttree/TTreeItem.java @@ -34,6 +34,7 @@ import java.util.List; import jexer.TWidget; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import static jexer.TKeypress.*; @@ -266,9 +267,9 @@ public class TTreeItem extends TWidget { } putStringXY(offset, 0, line, color); if (selected) { - putStringXY(offset + line.length(), 0, text, selectedColor); + putStringXY(offset + StringUtils.width(line), 0, text, selectedColor); } else { - putStringXY(offset + line.length(), 0, text, textColor); + putStringXY(offset + StringUtils.width(line), 0, text, textColor); } if ((level > 0) && (expandable)) { if (expanded) { @@ -402,10 +403,10 @@ public class TTreeItem extends TWidget { * @return the maximum number of columns for this item or its children */ public int getMaximumColumn() { - int max = prefix.length() + 4 + text.length(); + int max = prefix.length() + 4 + StringUtils.width(text); for (TWidget widget: getChildren()) { TTreeItem item = (TTreeItem) widget; - int n = item.prefix.length() + 4 + item.text.length(); + int n = item.prefix.length() + 4 + StringUtils.width(item.text); if (n > max) { max = n; } -- 2.27.0