diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java index e3e63f94d..1bd8e6f43 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java @@ -938,18 +938,10 @@ public class Key implements Comparable { public static class KeyParams { // params for building boolean isSpacer; - // relative values and absolute keyboard dimensions for re-determining key dimensions (if necessary) -/* - // todo: currently commented because not used, but planned (see todo below) - public int keyboardWidth; - public int keyboardHeight; - // relative widths and heights may not add up to 100% when including gaps - // this is ok with fill right, but otherwise? - public float mRelativeWidth; // also allow -1f as value, this means "fill right" - public float mRelativeHeight; - public float mRelativeHorizontalGap; - public float mRelativeVerticalGap; -*/ + private final KeyboardParams mParams; // for reading gaps and keyboard width / height + public float mRelativeWidth; // also allows -1f as value, this means "fill right" + public float mRelativeHeight; // also allows negative values, indicating absolute height is defined + // stuff that likely remains after constructor, maybe make final final int mCode; @Nullable final String mLabel; @@ -964,13 +956,13 @@ public class Key implements Comparable { @Nullable final OptionalAttributes mOptionalAttributes; public boolean mEnabled = true; - // stuff that may very well change, or only be set just before it's needed - int mWidth; - int mHeight; - int mHorizontalGap; - int mVerticalGap; - float xPos; - int yPos; + // stuff that may very well change + private int mWidth; + private int mHeight; + private int mHorizontalGap; + private int mVerticalGap; + private float xPos; + private int yPos; public static KeyParams newSpacer(final TypedArray keyAttr, final KeyStyle keyStyle, final KeyboardParams params, final XmlKeyboardRow row) { @@ -984,26 +976,33 @@ public class Key implements Comparable { return new Key(this); } - // todo: - // get relativeWidth and others when creating the params - // check how relative width could be adjusted - // there is the fillRight thing - // and not sure if there is a spacer on the left side if the key starts not directly at the edge - // then it should be possible to re-create the entire keyboard using the new dimensions - // can add keys (spacer) in a row, for split keyboard -/* - public void setDimensionsFromRelativeSize() { - if (keyboardHeight == 0 || keyboardWidth == 0 || mRelativeHeight == 0 || mRelativeWidth == 0) + // todo: use it + // first for inserting spacers to get a split keyboard + // any use for adjusting width or height? + // width is already more or less done with one-handed mode, but this could be more flexible + // height is already implemented via the setting + // any use in combination with number row? + // when completely replacing number row stuff, also moreKeys stuff would need to be adjusted + public void setDimensionsFromRelativeSize(final int newX, final int newY) { + if (mRelativeHeight == 0 || mRelativeWidth == 0) throw new IllegalStateException("can't use setUsingRelativeHeight, not all fields are set"); - float horizontalGap = isSpacer ? 0f : mRelativeHorizontalGap * keyboardWidth; + if (mRelativeHeight < 0) + throw new IllegalStateException("can't (yet) deal with absolute height"); // todo: decide... maybe just use it and deal with it properly when it needs to be adjusted? + xPos = newX; + yPos = newY; + float horizontalGap = isSpacer ? 0f : mParams.mRelativeHorizontalGap * mParams.mId.mWidth; // gap width / height is based on params.mId.height / width + float verticalGap = mParams.mRelativeVerticalGap * mParams.mId.mHeight; mHorizontalGap = (int) horizontalGap; - float verticalGap = mRelativeVerticalGap * mRelativeHeight; mVerticalGap = (int) verticalGap; - float keyWidth = mRelativeWidth * keyboardWidth; + float keyWidth; + if (mRelativeWidth > 0) + keyWidth = mRelativeWidth * mParams.mBaseWidth; // key width / height is based on params.mBaseHeight / width + else // fillRight + keyWidth = (mParams.mOccupiedWidth - mParams.mRightPadding) - xPos; // right keyboard edge - x mWidth = Math.round(keyWidth - horizontalGap); - mHeight = (int) (mRelativeHeight * keyboardHeight - verticalGap); + mHeight = (int) (mRelativeHeight * mParams.mBaseHeight - verticalGap); } -*/ + /** * Create keyParams with the given top-left coordinate and extract its attributes from a key * specification string, Key attribute array, key style, and etc. @@ -1018,6 +1017,9 @@ public class Key implements Comparable { public KeyParams(@Nullable final String keySpec, @NonNull final TypedArray keyAttr, @NonNull final KeyStyle style, @NonNull final KeyboardParams params, @NonNull final XmlKeyboardRow row) { + mParams = params; + mRelativeHeight = row.mRelativeRowHeight; + mRelativeWidth = row.getRelativeKeyWidth(keyAttr); mHorizontalGap = params.mHorizontalGap; mVerticalGap = params.mVerticalGap; @@ -1168,6 +1170,7 @@ public class Key implements Comparable { @Nullable final String hintLabel, @Nullable final String moreKeySpecs, final int labelFlags, final int backgroundType, final int x, final int y, final int width, final int height, final KeyboardParams params) { + mParams = params; mWidth = width - params.mHorizontalGap; mHeight = height - params.mVerticalGap; mHorizontalGap = params.mHorizontalGap; diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java index 2b04e58c1..a2f54e08f 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -57,6 +57,8 @@ public class KeyboardBuilder { public KeyboardBuilder loadFromXml(final int xmlId, final KeyboardId id) { mParams.mId = id; + // loading a keyboard should set default params like mParams.readAttributes(mContext, attrs); + // attrs may be null, then default values are used (looks good for "normal" keyboards) try (XmlKeyboardParser keyboardParser = new XmlKeyboardParser(xmlId, mParams, mContext)) { keysInRows = keyboardParser.parseKeyboard(); } catch (XmlPullParserException e) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java index 2c450b1f4..1a82779c8 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java @@ -6,6 +6,9 @@ package org.dslul.openboard.inputmethod.keyboard.internal; +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; import android.util.SparseIntArray; import androidx.annotation.NonNull; @@ -13,7 +16,10 @@ import androidx.annotation.Nullable; import org.dslul.openboard.inputmethod.keyboard.Key; import org.dslul.openboard.inputmethod.keyboard.KeyboardId; +import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.common.Constants; +import org.dslul.openboard.inputmethod.latin.settings.Settings; +import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils; import java.util.ArrayList; import java.util.Comparator; @@ -21,6 +27,9 @@ import java.util.SortedSet; import java.util.TreeSet; public class KeyboardParams { + private static final int DEFAULT_KEYBOARD_COLUMNS = 10; + private static final int DEFAULT_KEYBOARD_ROWS = 4; + public KeyboardId mId; public int mThemeId; @@ -42,6 +51,11 @@ public class KeyboardParams { @Nullable public KeyVisualAttributes mKeyVisualAttributes; + public float mDefaultRelativeRowHeight; + public float mDefaultRelativeKeyWidth; + public float mRelativeHorizontalGap; + public float mRelativeVerticalGap; + // relative values multiplied with baseHeight / baseWidth public int mDefaultRowHeight; public int mDefaultKeyWidth; public int mHorizontalGap; @@ -177,4 +191,84 @@ public class KeyboardParams { mMostCommonKeyWidth = width; } } + + // when attr is null, default attributes will be loaded + // these are good for basic keyboards already, but have wrong/unsuitable sizes e.g. for emojis, + // moreKeys and moreSuggestions + public void readAttributes(final Context context, @Nullable final AttributeSet attr) { + final TypedArray keyboardAttr = context.obtainStyledAttributes( + attr, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard); + final TypedArray keyAttr; + if (attr == null) + keyAttr = context.obtainStyledAttributes(attr, R.styleable.Keyboard_Key); + else + keyAttr = context.getResources().obtainAttributes(attr, R.styleable.Keyboard_Key); + try { + final int height = mId.mHeight; + final int width = mId.mWidth; + mOccupiedHeight = height; + mOccupiedWidth = width; + mTopPadding = (int) keyboardAttr.getFraction( + R.styleable.Keyboard_keyboardTopPadding, height, height, 0); + mBottomPadding = (int) keyboardAttr.getFraction( + R.styleable.Keyboard_keyboardBottomPadding, height, height, 0); + mLeftPadding = (int) keyboardAttr.getFraction( + R.styleable.Keyboard_keyboardLeftPadding, width, width, 0); + mRightPadding = (int) keyboardAttr.getFraction( + R.styleable.Keyboard_keyboardRightPadding, width, width, 0); + + final int baseWidth = mOccupiedWidth - mLeftPadding - mRightPadding; + mBaseWidth = baseWidth; + mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, + 1, 1, 1f / DEFAULT_KEYBOARD_COLUMNS); + mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * baseWidth); + + // todo: maybe settings should not be accessed from here? + if (Settings.getInstance().getCurrent().mNarrowKeyGaps) { + mRelativeHorizontalGap = keyboardAttr.getFraction( + R.styleable.Keyboard_horizontalGapNarrow, 1, 1, 0); + mRelativeVerticalGap = keyboardAttr.getFraction( + R.styleable.Keyboard_verticalGapNarrow, 1, 1, 0); + } else { + mRelativeHorizontalGap = keyboardAttr.getFraction( + R.styleable.Keyboard_horizontalGap, 1, 1, 0); + mRelativeVerticalGap = keyboardAttr.getFraction( + R.styleable.Keyboard_verticalGap, 1, 1, 0); + // TODO: Fix keyboard geometry calculation clearer. Historically vertical gap between + // rows are determined based on the entire keyboard height including top and bottom + // paddings. + } + mHorizontalGap = (int) (mRelativeHorizontalGap * width); + mVerticalGap = (int) (mRelativeVerticalGap * height); + + final int baseHeight = mOccupiedHeight - mTopPadding - mBottomPadding + mVerticalGap; + mBaseHeight = baseHeight; + mDefaultRelativeRowHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_rowHeight, 1, 1f / DEFAULT_KEYBOARD_ROWS); + if (mDefaultRelativeRowHeight > 1) { // can be absolute size, in that case will be > 1 + mDefaultRowHeight = (int) mDefaultRelativeRowHeight; + mDefaultRelativeRowHeight *= -1; // make it negative when it's absolute + } else { + mDefaultRowHeight = (int) (mDefaultRelativeRowHeight * baseHeight); + } + + mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); + + mMoreKeysTemplate = keyboardAttr.getResourceId(R.styleable.Keyboard_moreKeysTemplate, 0); + mMaxMoreKeysKeyboardColumn = keyAttr.getInt(R.styleable.Keyboard_Key_maxMoreKeysColumn, 5); + + mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0); + mIconsSet.loadIcons(keyboardAttr); + mTextsSet.setLocale(mId.getLocale(), context); + + final int resourceId = keyboardAttr.getResourceId(R.styleable.Keyboard_touchPositionCorrectionData, 0); + if (resourceId != 0) { + final String[] data = context.getResources().getStringArray(resourceId); + mTouchPositionCorrection.load(data); + } + } finally { + keyAttr.recycle(); + keyboardAttr.recycle(); + } + } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardParser.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardParser.java index 73c56558e..9f86de8fd 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardParser.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardParser.java @@ -16,14 +16,12 @@ import org.dslul.openboard.inputmethod.keyboard.KeyboardId; import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme; import org.dslul.openboard.inputmethod.keyboard.internal.CodesArrayParser; import org.dslul.openboard.inputmethod.keyboard.internal.KeyStyle; -import org.dslul.openboard.inputmethod.keyboard.internal.KeyVisualAttributes; import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardIconsSet; import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams; import org.dslul.openboard.inputmethod.keyboard.internal.MoreCodesArrayParser; import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.common.Constants; import org.dslul.openboard.inputmethod.latin.common.StringUtils; -import org.dslul.openboard.inputmethod.latin.settings.Settings; import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils; import org.dslul.openboard.inputmethod.latin.utils.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; @@ -121,9 +119,6 @@ public class XmlKeyboardParser implements AutoCloseable { private static final String TAG_DEFAULT = "default"; public static final String TAG_KEY_STYLE = "key-style"; - private static final int DEFAULT_KEYBOARD_COLUMNS = 10; - private static final int DEFAULT_KEYBOARD_ROWS = 4; - protected final Context mContext; protected final Resources mResources; private final XmlResourceParser mParser; @@ -189,74 +184,7 @@ public class XmlKeyboardParser implements AutoCloseable { /** this and parseKeyStyle are the only place where anything is written to params */ private void parseKeyboardAttributes(final XmlPullParser parser) { final AttributeSet attr = Xml.asAttributeSet(parser); - final TypedArray keyboardAttr = mContext.obtainStyledAttributes( - attr, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard); - final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key); - final KeyboardParams params = mParams; - try { - final int height = params.mId.mHeight; - final int width = params.mId.mWidth; - params.mOccupiedHeight = height; - params.mOccupiedWidth = width; - params.mTopPadding = (int)keyboardAttr.getFraction( - R.styleable.Keyboard_keyboardTopPadding, height, height, 0); - params.mBottomPadding = (int)keyboardAttr.getFraction( - R.styleable.Keyboard_keyboardBottomPadding, height, height, 0); - params.mLeftPadding = (int)keyboardAttr.getFraction( - R.styleable.Keyboard_keyboardLeftPadding, width, width, 0); - params.mRightPadding = (int)keyboardAttr.getFraction( - R.styleable.Keyboard_keyboardRightPadding, width, width, 0); - - final int baseWidth = - params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding; - params.mBaseWidth = baseWidth; - params.mDefaultKeyWidth = (int)keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, - baseWidth, baseWidth, baseWidth / DEFAULT_KEYBOARD_COLUMNS); - - // todo: actually settings should not be accessed from here - // maybe parse normal key gaps and adjust them later (using the relative params!) - if (Settings.getInstance().getCurrent().mNarrowKeyGaps) { - params.mHorizontalGap = (int) keyboardAttr.getFraction( - R.styleable.Keyboard_horizontalGapNarrow, baseWidth, baseWidth, 0); - params.mVerticalGap = (int) keyboardAttr.getFraction( - R.styleable.Keyboard_verticalGapNarrow, height, height, 0); - } else { - params.mHorizontalGap = (int) keyboardAttr.getFraction( - R.styleable.Keyboard_horizontalGap, baseWidth, baseWidth, 0); - // TODO: Fix keyboard geometry calculation clearer. Historically vertical gap between - // rows are determined based on the entire keyboard height including top and bottom - // paddings. - params.mVerticalGap = (int) keyboardAttr.getFraction( - R.styleable.Keyboard_verticalGap, height, height, 0); - } - - final int baseHeight = params.mOccupiedHeight - params.mTopPadding - - params.mBottomPadding + params.mVerticalGap; - params.mBaseHeight = baseHeight; - params.mDefaultRowHeight = (int) ResourceUtils.getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_rowHeight, baseHeight, baseHeight / DEFAULT_KEYBOARD_ROWS); - - params.mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); - - params.mMoreKeysTemplate = keyboardAttr.getResourceId( - R.styleable.Keyboard_moreKeysTemplate, 0); - params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt( - R.styleable.Keyboard_Key_maxMoreKeysColumn, 5); - - params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0); - params.mIconsSet.loadIcons(keyboardAttr); - params.mTextsSet.setLocale(params.mId.getLocale(), mContext); - - final int resourceId = keyboardAttr.getResourceId( - R.styleable.Keyboard_touchPositionCorrectionData, 0); - if (resourceId != 0) { - final String[] data = mResources.getStringArray(resourceId); - params.mTouchPositionCorrection.load(data); - } - } finally { - keyAttr.recycle(); - keyboardAttr.recycle(); - } + mParams.readAttributes(mContext, attr); } private void parseKeyboardContent(final XmlPullParser parser, final boolean skip) @@ -366,7 +294,6 @@ public class XmlKeyboardParser implements AutoCloseable { R.styleable.Keyboard_GridRows_textsArray, 0); final int moreCodesArrayId = gridRowAttr.getResourceId( R.styleable.Keyboard_GridRows_moreCodesArray, 0); - // todo: read relative key width, key / row height and gaps (but they might also be absolute, see getDimensionOrFraction) gridRowAttr.recycle(); if (codesArrayId == 0 && textsArrayId == 0) { throw new XmlParseUtils.ParseException( @@ -436,7 +363,9 @@ public class XmlKeyboardParser implements AutoCloseable { final String hintLabel = moreKeySpecs != null ? "\u25E5" : null; final Key.KeyParams key = new Key.KeyParams(label, code, outputText, hintLabel, moreKeySpecs, labelFlags, backgroundType, x, y, width, height, mParams); - // todo: add relative width and others + // (relative) width is always default when using gridRows.getKeyWidth(null, 0.0f) + key.mRelativeWidth = mParams.mDefaultRelativeKeyWidth; + key.mRelativeHeight = gridRows.mRelativeRowHeight; keyParamsRow.add(key); row.advanceXPos(keyWidth); } @@ -462,7 +391,6 @@ public class XmlKeyboardParser implements AutoCloseable { } final Key.KeyParams key = new Key.KeyParams(keySpec, keyAttr, keyStyle, mParams, row); keyAttr.recycle(); - // todo: add relative width and others if (DEBUG) { startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, (key.mEnabled ? "" : " disabled"), key, Arrays.toString(key.mMoreKeys)); @@ -483,7 +411,6 @@ public class XmlKeyboardParser implements AutoCloseable { final KeyStyle keyStyle = mParams.mKeyStyles.getKeyStyle(keyAttr, parser); final Key.KeyParams spacer = Key.KeyParams.newSpacer(keyAttr, keyStyle, mParams, row); keyAttr.recycle(); - // todo: add relative width and others keysInRows.get(keysInRows.size() - 1).add(spacer); if (DEBUG) startEndTag("<%s />", TAG_SPACER); XmlParseUtils.checkEndTag(TAG_SPACER, parser); diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java index bd9b5a741..b8c90031c 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java @@ -32,6 +32,7 @@ public final class XmlKeyboardRow { private final KeyboardParams mParams; /** The height of this row. */ private final int mRowHeight; + final public float mRelativeRowHeight; private final ArrayDeque mRowAttributesStack = new ArrayDeque<>(); @@ -88,9 +89,16 @@ public final class XmlKeyboardRow { mParams = params; final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); - mRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight); + float relativeRowHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_rowHeight, 1, params.mDefaultRelativeRowHeight); keyboardAttr.recycle(); + if (relativeRowHeight > 1) { + mRelativeRowHeight = -relativeRowHeight; + mRowHeight = (int) relativeRowHeight; + } else { + mRelativeRowHeight = relativeRowHeight; + mRowHeight = (int) (relativeRowHeight * params.mBaseHeight); + } final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); mRowAttributesStack.push(new RowAttributes( @@ -157,6 +165,17 @@ public final class XmlKeyboardRow { return Math.max(keyXPos + keyboardRightEdge, mCurrentX); } + public float getRelativeKeyWidth(final TypedArray keyAttr) { + if (keyAttr == null) + return mParams.mDefaultRelativeKeyWidth; + final int widthType = ResourceUtils.getEnumValue(keyAttr, + R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM); + if (widthType == KEYWIDTH_FILL_RIGHT) + return -1; + return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, + 1, 1, mParams.mDefaultRelativeKeyWidth); + } + public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) { if (keyAttr == null) { return getDefaultKeyWidth();