properly set relative key(board) params

This commit is contained in:
Helium314 2023-10-27 13:19:57 +02:00
parent 1916eb233f
commit 309f121848
5 changed files with 158 additions and 113 deletions

View file

@ -938,18 +938,10 @@ public class Key implements Comparable<Key> {
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<Key> {
@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<Key> {
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<Key> {
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<Key> {
@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;

View file

@ -57,6 +57,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
public KeyboardBuilder<KP> 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) {

View file

@ -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();
}
}
}

View file

@ -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);

View file

@ -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<RowAttributes> 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();