mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-17 07:22:45 +00:00
Allow splitting all layouts instead of having to define split layouts in xml (#258)
This commit is contained in:
parent
778f160c07
commit
1c0fb0c672
11 changed files with 442 additions and 290 deletions
|
@ -282,18 +282,24 @@ public class Key implements Comparable<Key> {
|
|||
mEnabled = keyParams.mEnabled;
|
||||
|
||||
// stuff to create
|
||||
mWidth = keyParams.mWidth;
|
||||
mHeight = keyParams.mHeight;
|
||||
// interestingly it looks a little better when rounding horizontalGap to int immediately instead of using horizontalGapFloat
|
||||
// but using float for determining mX and mWidth is more correct and keyboard ends up looking exactly like before introduction of KeyParams
|
||||
final float horizontalGapFloat = isSpacer() ? 0 : keyParams.mKeyboardParams.mHorizontalGap;
|
||||
mHorizontalGap = Math.round(horizontalGapFloat);
|
||||
mVerticalGap = Math.round(keyParams.mKeyboardParams.mVerticalGap);
|
||||
mWidth = Math.round(keyParams.mFullWidth - horizontalGapFloat);
|
||||
// todo (later): height better should be rounded, but this may end up shifting all keys up by one pixel,
|
||||
// increasing the keyboard height by one pixel, but not for emoji keyboard -> the 1 pixel shift feels very wrong
|
||||
// how to do it properly? check again when keyboard height is same for all views!
|
||||
mHeight = (int) (keyParams.mFullHeight - keyParams.mKeyboardParams.mVerticalGap);
|
||||
if (!isSpacer() && (mWidth == 0 || mHeight == 0)) {
|
||||
throw new IllegalStateException("key needs positive width and height");
|
||||
}
|
||||
mHorizontalGap = isSpacer() ? 0 : keyParams.mHorizontalGap;
|
||||
mVerticalGap = keyParams.mVerticalGap;
|
||||
// Horizontal gap is divided equally to both sides of the key.
|
||||
mX = Math.round(keyParams.xPos + ((float) keyParams.mHorizontalGap) / 2);
|
||||
mY = keyParams.yPos;
|
||||
mHitBox.set(Math.round(keyParams.xPos), keyParams.yPos, Math.round(keyParams.xPos + mWidth + mHorizontalGap) + 1,
|
||||
keyParams.yPos + mHeight + mHorizontalGap);
|
||||
mX = Math.round(keyParams.xPos + horizontalGapFloat / 2);
|
||||
mY = Math.round(keyParams.yPos);
|
||||
mHitBox.set(Math.round(keyParams.xPos), (int) keyParams.yPos, Math.round(keyParams.xPos + keyParams.mFullWidth) + 1,
|
||||
Math.round(keyParams.yPos + keyParams.mFullHeight));
|
||||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
|
@ -937,32 +943,30 @@ public class Key implements Comparable<Key> {
|
|||
// for creating keys that might get modified later
|
||||
public static class KeyParams {
|
||||
// params for building
|
||||
boolean isSpacer;
|
||||
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
|
||||
private boolean isSpacer;
|
||||
private final KeyboardParams mKeyboardParams; // for reading gaps and keyboard width / height
|
||||
public float mRelativeWidth;
|
||||
public float mRelativeHeight; // also should allow negative values, indicating absolute height is defined
|
||||
|
||||
// stuff that likely remains after constructor, maybe make final
|
||||
final int mCode;
|
||||
// params that may change
|
||||
public float mFullWidth;
|
||||
public float mFullHeight;
|
||||
public float xPos;
|
||||
public float yPos;
|
||||
|
||||
// params that remains constant
|
||||
public final int mCode;
|
||||
@Nullable final String mLabel;
|
||||
@Nullable final String mHintLabel;
|
||||
final int mLabelFlags;
|
||||
final int mIconId;
|
||||
public final MoreKeySpec[] mMoreKeys;
|
||||
final int mMoreKeysColumnAndFlags;
|
||||
final int mBackgroundType;
|
||||
public final int mBackgroundType;
|
||||
final int mActionFlags;
|
||||
@Nullable final KeyVisualAttributes mKeyVisualAttributes;
|
||||
@Nullable final OptionalAttributes mOptionalAttributes;
|
||||
public boolean mEnabled = true;
|
||||
|
||||
// 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 final boolean mEnabled;
|
||||
|
||||
public static KeyParams newSpacer(final TypedArray keyAttr, final KeyStyle keyStyle,
|
||||
final KeyboardParams params, final XmlKeyboardRow row) {
|
||||
|
@ -971,36 +975,25 @@ public class Key implements Comparable<Key> {
|
|||
return keyParams;
|
||||
}
|
||||
|
||||
public static KeyParams newSpacer(final KeyboardParams params) {
|
||||
return new KeyParams(params);
|
||||
}
|
||||
|
||||
public Key createKey() {
|
||||
if (isSpacer) return new Spacer(this);
|
||||
return new Key(this);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
public void setDimensionsFromRelativeSize(final float newX, final float newY) {
|
||||
if (mRelativeHeight == 0 || mRelativeWidth == 0)
|
||||
throw new IllegalStateException("can't use setUsingRelativeHeight, not all fields are set");
|
||||
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?
|
||||
// todo (later): deal with it properly when it needs to be adjusted, i.e. when changing moreKeys or moreSuggestions
|
||||
throw new IllegalStateException("can't (yet) deal with absolute height");
|
||||
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;
|
||||
mVerticalGap = (int) verticalGap;
|
||||
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 * mParams.mBaseHeight - verticalGap);
|
||||
mFullWidth = mRelativeWidth * mKeyboardParams.mBaseWidth;
|
||||
mFullHeight = mRelativeHeight * mKeyboardParams.mBaseHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1017,26 +1010,23 @@ 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;
|
||||
mKeyboardParams = params;
|
||||
mRelativeHeight = row.mRelativeRowHeight;
|
||||
mRelativeWidth = row.getRelativeKeyWidth(keyAttr);
|
||||
mHorizontalGap = params.mHorizontalGap;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
|
||||
final float horizontalGapFloat = mHorizontalGap;
|
||||
final int rowHeight = row.getRowHeight();
|
||||
mHeight = rowHeight - mVerticalGap;
|
||||
|
||||
mFullHeight = row.getRowHeight();
|
||||
xPos = row.getKeyX(keyAttr);
|
||||
final float keyWidth = row.getKeyWidth(keyAttr, xPos);
|
||||
mFullWidth = row.getKeyWidth(keyAttr, xPos);
|
||||
if (mRelativeWidth == -1f) {
|
||||
// determine from actual width if using fillRight
|
||||
mRelativeWidth = mFullWidth / mKeyboardParams.mBaseWidth;
|
||||
}
|
||||
yPos = row.getKeyY();
|
||||
|
||||
mWidth = Math.round(keyWidth - horizontalGapFloat);
|
||||
// Update row to have current x coordinate.
|
||||
row.setXPos(xPos + keyWidth);
|
||||
row.setXPos(xPos + mFullWidth);
|
||||
|
||||
mBackgroundType = style.getInt(keyAttr,
|
||||
R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
|
||||
mBackgroundType = style.getInt(keyAttr, R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
|
||||
|
||||
final int baseWidth = params.mBaseWidth;
|
||||
final int visualInsetsLeft = Math.round(keyAttr.getFraction(
|
||||
|
@ -1058,13 +1048,11 @@ public class Key implements Comparable<Key> {
|
|||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
|
@ -1081,8 +1069,7 @@ public class Key implements Comparable<Key> {
|
|||
if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
|
||||
additionalMoreKeys = null;
|
||||
} else {
|
||||
additionalMoreKeys = style.getStringArray(keyAttr,
|
||||
R.styleable.Keyboard_Key_additionalMoreKeys);
|
||||
additionalMoreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
|
||||
}
|
||||
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
|
||||
if (moreKeys != null) {
|
||||
|
@ -1117,8 +1104,7 @@ public class Key implements Comparable<Key> {
|
|||
if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
|
||||
mHintLabel = null;
|
||||
} else {
|
||||
final String hintLabel = style.getString(
|
||||
keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
|
||||
final String hintLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
|
||||
mHintLabel = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing)
|
||||
: hintLabel;
|
||||
|
@ -1128,12 +1114,12 @@ public class Key implements Comparable<Key> {
|
|||
outputText = StringUtils.toTitleCaseOfKeyLabel(outputText, localeForUpcasing);
|
||||
}
|
||||
// Choose the first letter of the label as primary code if not specified.
|
||||
if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
|
||||
&& !TextUtils.isEmpty(mLabel)) {
|
||||
if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) && !TextUtils.isEmpty(mLabel)) {
|
||||
if (StringUtils.codePointCount(mLabel) == 1) {
|
||||
// Use the first letter of the hint label if shiftedLetterActivated flag is
|
||||
// specified.
|
||||
if ((mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0 && !TextUtils.isEmpty(mHintLabel)) {
|
||||
if ((mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0
|
||||
&& !TextUtils.isEmpty(mHintLabel)) {
|
||||
mCode = mHintLabel.codePointAt(0);
|
||||
} else {
|
||||
mCode = mLabel.codePointAt(0);
|
||||
|
@ -1152,8 +1138,7 @@ public class Key implements Comparable<Key> {
|
|||
mCode = CODE_OUTPUT_TEXT;
|
||||
}
|
||||
} else {
|
||||
mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing)
|
||||
: code;
|
||||
mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing) : code;
|
||||
}
|
||||
final int altCodeInAttr = KeySpecParser.parseCode(
|
||||
style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
|
||||
|
@ -1163,6 +1148,7 @@ public class Key implements Comparable<Key> {
|
|||
mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
|
||||
disabledIconId, visualInsetsLeft, visualInsetsRight);
|
||||
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
|
||||
mEnabled = true;
|
||||
}
|
||||
|
||||
/** for <GridRows/> */
|
||||
|
@ -1170,11 +1156,9 @@ 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;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
mKeyboardParams = params;
|
||||
mFullWidth = width;
|
||||
mFullHeight = height;
|
||||
mHintLabel = hintLabel;
|
||||
mLabelFlags = labelFlags;
|
||||
mBackgroundType = backgroundType;
|
||||
|
@ -1184,8 +1168,7 @@ public class Key implements Comparable<Key> {
|
|||
if (moreKeySpecs != null) {
|
||||
String[] moreKeys = MoreKeySpec.splitKeySpecs(moreKeySpecs);
|
||||
// Get maximum column order number and set a relevant mode value.
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
|
||||
| params.mMaxMoreKeysKeyboardColumn;
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER | params.mMaxMoreKeysKeyboardColumn;
|
||||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
|
@ -1194,8 +1177,7 @@ public class Key implements Comparable<Key> {
|
|||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
|
@ -1235,5 +1217,48 @@ public class Key implements Comparable<Key> {
|
|||
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
|
||||
mKeyVisualAttributes = null;
|
||||
}
|
||||
|
||||
/** constructor for a spacer whose size MUST be determined using setDimensionsFromRelativeSize */
|
||||
private KeyParams(final KeyboardParams params) {
|
||||
isSpacer = true; // this is only for spacer!
|
||||
mKeyboardParams = params;
|
||||
|
||||
mCode = CODE_UNSPECIFIED;
|
||||
mLabel = null;
|
||||
mHintLabel = null;
|
||||
mKeyVisualAttributes = null;
|
||||
mOptionalAttributes = null;
|
||||
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
|
||||
mBackgroundType = BACKGROUND_TYPE_NORMAL;
|
||||
mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
|
||||
mMoreKeys = null;
|
||||
mMoreKeysColumnAndFlags = 0;
|
||||
mLabelFlags = LABEL_FLAGS_FONT_NORMAL;
|
||||
mEnabled = true;
|
||||
}
|
||||
|
||||
public KeyParams(final KeyParams keyParams) {
|
||||
xPos = keyParams.xPos;
|
||||
yPos = keyParams.yPos;
|
||||
mRelativeWidth = keyParams.mRelativeWidth;
|
||||
mRelativeHeight = keyParams.mRelativeHeight;
|
||||
isSpacer = keyParams.isSpacer;
|
||||
mKeyboardParams = keyParams.mKeyboardParams;
|
||||
mEnabled = keyParams.mEnabled;
|
||||
|
||||
mCode = keyParams.mCode;
|
||||
mLabel = keyParams.mLabel;
|
||||
mHintLabel = keyParams.mHintLabel;
|
||||
mLabelFlags = keyParams.mLabelFlags;
|
||||
mIconId = keyParams.mIconId;
|
||||
mFullWidth = keyParams.mFullWidth;
|
||||
mFullHeight = keyParams.mFullHeight;
|
||||
mMoreKeys = keyParams.mMoreKeys;
|
||||
mMoreKeysColumnAndFlags = keyParams.mMoreKeysColumnAndFlags;
|
||||
mBackgroundType = keyParams.mBackgroundType;
|
||||
mActionFlags = keyParams.mActionFlags;
|
||||
mKeyVisualAttributes = keyParams.mKeyVisualAttributes;
|
||||
mOptionalAttributes = keyParams.mOptionalAttributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -457,8 +457,7 @@ public final class KeyboardLayoutSet {
|
|||
elementParams.mProximityCharsCorrectionEnabled = a.getBoolean(
|
||||
R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection,
|
||||
false);
|
||||
elementParams.mSupportsSplitLayout = a.getBoolean(
|
||||
R.styleable.KeyboardLayoutSet_Element_supportsSplitLayout, false);
|
||||
elementParams.mSupportsSplitLayout = false; // this is to avoid xml parser reading split layouts, todo (later): remove mSupportsSplitLayout
|
||||
elementParams.mAllowRedundantMoreKeys = a.getBoolean(
|
||||
R.styleable.KeyboardLayoutSet_Element_allowRedundantMoreKeys, true);
|
||||
mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams);
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.keyboard.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
|
||||
import org.dslul.openboard.inputmethod.keyboard.Key;
|
||||
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardId;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.XmlKeyboardParser;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// TODO: Write unit tests for this class.
|
||||
public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||
private static final String BUILDER_TAG = "Keyboard.Builder";
|
||||
|
||||
@NonNull
|
||||
protected final KP mParams;
|
||||
protected final Context mContext;
|
||||
protected final Resources mResources;
|
||||
|
||||
private int mCurrentY = 0;
|
||||
// currently not used, but will be relevant when resizing a row or inserting a new key
|
||||
private float mCurrentX = 0f;
|
||||
private boolean mLeftEdge;
|
||||
private boolean mTopEdge;
|
||||
private Key mRightEdgeKey = null;
|
||||
private ArrayList<ArrayList<Key.KeyParams>> keysInRows;
|
||||
|
||||
public KeyboardBuilder(final Context context, @NonNull final KP params) {
|
||||
mContext = context;
|
||||
final Resources res = context.getResources();
|
||||
mResources = res;
|
||||
|
||||
mParams = params;
|
||||
|
||||
params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
|
||||
params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
|
||||
}
|
||||
|
||||
public void setAllowRedundantMoreKeys(final boolean enabled) {
|
||||
mParams.mAllowRedundantMoreKeys = enabled;
|
||||
}
|
||||
|
||||
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) {
|
||||
Log.w(BUILDER_TAG, "keyboard XML parse error", e);
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
Log.w(BUILDER_TAG, "keyboard XML parse error", e);
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@UsedForTesting
|
||||
public void disableTouchPositionCorrectionDataForTest() {
|
||||
mParams.mTouchPositionCorrection.setEnabled(false);
|
||||
}
|
||||
|
||||
public void setProximityCharsCorrectionEnabled(final boolean enabled) {
|
||||
mParams.mProximityCharsCorrectionEnabled = enabled;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Keyboard build() {
|
||||
addKeysToParams();
|
||||
return new Keyboard(mParams);
|
||||
}
|
||||
|
||||
private void startKeyboard() {
|
||||
mCurrentY += mParams.mTopPadding;
|
||||
mTopEdge = true;
|
||||
}
|
||||
|
||||
private void startRow() {
|
||||
addEdgeSpace(mParams.mLeftPadding);
|
||||
mLeftEdge = true;
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
|
||||
private void endRow() {
|
||||
int lastKeyHeight = 0;
|
||||
if (mRightEdgeKey != null) {
|
||||
mRightEdgeKey.markAsRightEdge(mParams);
|
||||
lastKeyHeight = mRightEdgeKey.getHeight() + mRightEdgeKey.getVerticalGap();
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
addEdgeSpace(mParams.mRightPadding);
|
||||
mCurrentY += lastKeyHeight;
|
||||
mTopEdge = false;
|
||||
}
|
||||
|
||||
private void endKey(@NonNull final Key key) {
|
||||
mParams.onAddKey(key);
|
||||
if (mLeftEdge) {
|
||||
key.markAsLeftEdge(mParams);
|
||||
mLeftEdge = false;
|
||||
}
|
||||
if (mTopEdge) {
|
||||
key.markAsTopEdge(mParams);
|
||||
}
|
||||
mRightEdgeKey = key;
|
||||
}
|
||||
|
||||
private void endKeyboard() {
|
||||
mParams.removeRedundantMoreKeys();
|
||||
// {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than
|
||||
// previously expected.
|
||||
final int actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding;
|
||||
mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight);
|
||||
}
|
||||
|
||||
private void addKeysToParams() {
|
||||
// need to reset it, we need to sum it up to get the height nicely
|
||||
// (though in the end we could just not touch it at all, final used value is the same as the one before resetting)
|
||||
mCurrentY = 0;
|
||||
startKeyboard();
|
||||
for (ArrayList<Key.KeyParams> row : keysInRows) {
|
||||
startRow();
|
||||
for (Key.KeyParams keyParams : row) {
|
||||
endKey(keyParams.createKey());
|
||||
}
|
||||
endRow();
|
||||
}
|
||||
endKeyboard();
|
||||
}
|
||||
|
||||
private void addEdgeSpace(final float width) {
|
||||
mCurrentX += width;
|
||||
mLeftEdge = false;
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
package org.dslul.openboard.inputmethod.keyboard.internal
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.util.Log
|
||||
import org.dslul.openboard.inputmethod.annotations.UsedForTesting
|
||||
import org.dslul.openboard.inputmethod.keyboard.Key
|
||||
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
|
||||
import org.dslul.openboard.inputmethod.keyboard.Keyboard
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.XmlKeyboardParser
|
||||
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.xmlpull.v1.XmlPullParserException
|
||||
import java.io.IOException
|
||||
|
||||
// TODO: Write unit tests for this class.
|
||||
open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context, @JvmField val mParams: KP) {
|
||||
@JvmField
|
||||
protected val mResources: Resources
|
||||
private var mCurrentY = 0
|
||||
|
||||
private var mLeftEdge = false
|
||||
private var mTopEdge = false
|
||||
private var mRightEdgeKey: Key? = null
|
||||
private lateinit var keysInRows: ArrayList<ArrayList<KeyParams>>
|
||||
|
||||
init {
|
||||
val res = mContext.resources
|
||||
mResources = res
|
||||
mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width)
|
||||
mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height)
|
||||
}
|
||||
|
||||
fun setAllowRedundantMoreKeys(enabled: Boolean) {
|
||||
mParams.mAllowRedundantMoreKeys = enabled
|
||||
}
|
||||
|
||||
fun loadFromXml(xmlId: Int, id: KeyboardId): KeyboardBuilder<KP> {
|
||||
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(xmlId, mParams, mContext).use { keyboardParser ->
|
||||
keysInRows = keyboardParser.parseKeyboard()
|
||||
}
|
||||
} catch (e: XmlPullParserException) {
|
||||
Log.w(BUILDER_TAG, "keyboard XML parse error", e)
|
||||
throw IllegalArgumentException(e.message, e)
|
||||
} catch (e: IOException) {
|
||||
Log.w(BUILDER_TAG, "keyboard XML parse error", e)
|
||||
throw RuntimeException(e.message, e)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
@UsedForTesting
|
||||
fun disableTouchPositionCorrectionDataForTest() {
|
||||
mParams.mTouchPositionCorrection.setEnabled(false)
|
||||
}
|
||||
|
||||
fun setProximityCharsCorrectionEnabled(enabled: Boolean) {
|
||||
mParams.mProximityCharsCorrectionEnabled = enabled
|
||||
}
|
||||
|
||||
open fun build(): Keyboard {
|
||||
if (Settings.getInstance().current.mIsSplitKeyboardEnabled
|
||||
&& mParams.mId.mElementId in KeyboardId.ELEMENT_ALPHABET..KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
|
||||
addSplit()
|
||||
}
|
||||
addKeysToParams()
|
||||
return Keyboard(mParams)
|
||||
}
|
||||
|
||||
// resize keyboard using relative params
|
||||
// ideally this should not change anything
|
||||
// but it does a little, depending on how float -> int is done (cast or round, and when to sum up gaps and width)
|
||||
// still should not be more than a pixel difference
|
||||
// keep it around for a while, for testing
|
||||
private fun useRelative() {
|
||||
for (row in keysInRows) {
|
||||
if (row.isEmpty()) continue
|
||||
fillGapsWithSpacers(row)
|
||||
val y = row.first().yPos
|
||||
assert(row.all { it.yPos == y })
|
||||
var currentX = 0f
|
||||
row.forEach {
|
||||
it.setDimensionsFromRelativeSize(currentX, y)
|
||||
currentX += it.mFullWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// necessary for adjusting widths and positions properly
|
||||
// without adding spacers whose width can then be adjusted, we would have to deal with keyXPos,
|
||||
// which is more complicated than expected
|
||||
private fun fillGapsWithSpacers(row: MutableList<KeyParams>) {
|
||||
if (mParams.mId.mElementId !in KeyboardId.ELEMENT_ALPHABET..KeyboardId.ELEMENT_SYMBOLS_SHIFTED) return
|
||||
if (row.isEmpty()) return
|
||||
var currentX = 0f + mParams.mLeftPadding
|
||||
var i = 0
|
||||
while (i < row.size) {
|
||||
val currentKeyXPos = row[i].xPos
|
||||
if (currentKeyXPos > currentX) {
|
||||
// insert spacer
|
||||
val spacer = KeyParams.newSpacer(mParams)
|
||||
spacer.mRelativeWidth = (currentKeyXPos - currentX) / mParams.mBaseWidth
|
||||
spacer.yPos = row[i].yPos
|
||||
spacer.mRelativeHeight = row[i].mRelativeHeight
|
||||
row.add(i, spacer)
|
||||
i++
|
||||
currentX += currentKeyXPos - currentX
|
||||
}
|
||||
currentX += row[i].mFullWidth
|
||||
i++
|
||||
}
|
||||
if (currentX < mParams.mOccupiedWidth) {
|
||||
// insert spacer
|
||||
val spacer = KeyParams.newSpacer(mParams)
|
||||
spacer.mRelativeWidth = (mParams.mOccupiedWidth - currentX) / mParams.mBaseWidth
|
||||
spacer.mRelativeHeight = row.last().mRelativeHeight
|
||||
spacer.yPos = row.last().yPos
|
||||
row.add(spacer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addSplit() {
|
||||
val spacerRelativeWidth = Settings.getInstance().current.mSpacerRelativeWidth
|
||||
// adjust gaps for the whole keyboard, so it's the same for all rows
|
||||
// todo: maybe remove? not sure if narrower gaps are desirable
|
||||
mParams.mRelativeHorizontalGap *= 1f / (1f + spacerRelativeWidth)
|
||||
mParams.mHorizontalGap = (mParams.mRelativeHorizontalGap * mParams.mId.mWidth).toInt()
|
||||
var maxWidthBeforeSpacer = 0f
|
||||
var maxWidthAfterSpacer = 0f
|
||||
for (row in keysInRows) {
|
||||
fillGapsWithSpacers(row)
|
||||
val y = row.first().yPos // all have the same y, so this is fine
|
||||
val relativeWidthSum = row.sumOf { it.mRelativeWidth } // sum up relative widths
|
||||
val spacer = KeyParams.newSpacer(mParams)
|
||||
spacer.mRelativeWidth = spacerRelativeWidth
|
||||
spacer.mRelativeHeight = row.first().mRelativeHeight
|
||||
// insert spacer before first key that starts right of the center (also consider gap)
|
||||
var insertIndex = row.indexOfFirst { it.xPos > mParams.mOccupiedWidth / 2 }
|
||||
.takeIf { it > -1 } ?: (row.size / 2) // fallback should never be needed, but better than having an error
|
||||
if (row.any { it.mCode == Constants.CODE_SPACE }) {
|
||||
val spaceLeft = row.single { it.mCode == Constants.CODE_SPACE }
|
||||
reduceSymbolAndActionKeyWidth(row)
|
||||
insertIndex = row.indexOf(spaceLeft) + 1
|
||||
val widthBeforeSpace = row.subList(0, insertIndex - 1).sumOf { it.mRelativeWidth }
|
||||
val widthAfterSpace = row.subList(insertIndex, row.size).sumOf { it.mRelativeWidth }
|
||||
val spaceLeftWidth = (maxWidthBeforeSpacer - widthBeforeSpace).coerceAtLeast(mParams.mDefaultRelativeKeyWidth)
|
||||
val spaceRightWidth = (maxWidthAfterSpacer - widthAfterSpace).coerceAtLeast(mParams.mDefaultRelativeKeyWidth)
|
||||
val spacerWidth = spaceLeft.mRelativeWidth + spacerRelativeWidth - spaceLeftWidth - spaceRightWidth
|
||||
if (spacerWidth > 0.05f) {
|
||||
// only insert if the spacer has a reasonable width
|
||||
val spaceRight = KeyParams(spaceLeft)
|
||||
spaceLeft.mRelativeWidth = spaceLeftWidth
|
||||
spaceRight.mRelativeWidth = spaceRightWidth
|
||||
spacer.mRelativeWidth = spacerWidth
|
||||
row.add(insertIndex, spaceRight)
|
||||
row.add(insertIndex, spacer)
|
||||
} else {
|
||||
// otherwise increase space width, so other keys are resized properly
|
||||
spaceLeft.mRelativeWidth += spacerWidth
|
||||
}
|
||||
} else {
|
||||
val widthBeforeSpacer = row.subList(0, insertIndex).sumOf { it.mRelativeWidth }
|
||||
val widthAfterSpacer = row.subList(insertIndex, row.size).sumOf { it.mRelativeWidth }
|
||||
maxWidthBeforeSpacer = maxWidthBeforeSpacer.coerceAtLeast(widthBeforeSpacer)
|
||||
maxWidthAfterSpacer = maxWidthAfterSpacer.coerceAtLeast(widthAfterSpacer)
|
||||
row.add(insertIndex, spacer)
|
||||
}
|
||||
// re-calculate relative widths
|
||||
val relativeWidthSumNew = row.sumOf { it.mRelativeWidth }
|
||||
val widthFactor = relativeWidthSum / relativeWidthSumNew
|
||||
// re-calculate absolute sizes and positions
|
||||
var currentX = 0f
|
||||
row.forEach {
|
||||
it.mRelativeWidth *= widthFactor
|
||||
it.setDimensionsFromRelativeSize(currentX, y)
|
||||
currentX += it.mFullWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reduce width of symbol and action key if in the row, and add this width to space to keep other key size constant
|
||||
private fun reduceSymbolAndActionKeyWidth(row: ArrayList<KeyParams>) {
|
||||
val spaceKey = row.first { it.mCode == Constants.CODE_SPACE }
|
||||
val symbolKey = row.firstOrNull { it.mCode == Constants.CODE_SWITCH_ALPHA_SYMBOL }
|
||||
val symbolKeyWidth = symbolKey?.mRelativeWidth ?: 0f
|
||||
if (symbolKeyWidth > mParams.mDefaultRelativeKeyWidth) {
|
||||
val widthToChange = symbolKey!!.mRelativeWidth - mParams.mDefaultRelativeKeyWidth
|
||||
symbolKey.mRelativeWidth -= widthToChange
|
||||
spaceKey.mRelativeWidth += widthToChange
|
||||
}
|
||||
val actionKey = row.firstOrNull { it.mBackgroundType == Key.BACKGROUND_TYPE_ACTION }
|
||||
val actionKeyWidth = actionKey?.mRelativeWidth ?: 0f
|
||||
if (actionKeyWidth > mParams.mDefaultRelativeKeyWidth * 1.1f) { // allow it to stay a little wider
|
||||
val widthToChange = actionKey!!.mRelativeWidth - mParams.mDefaultRelativeKeyWidth * 1.1f
|
||||
actionKey.mRelativeWidth -= widthToChange
|
||||
spaceKey.mRelativeWidth += widthToChange
|
||||
}
|
||||
}
|
||||
|
||||
private fun startKeyboard() {
|
||||
mCurrentY += mParams.mTopPadding
|
||||
mTopEdge = true
|
||||
}
|
||||
|
||||
private fun startRow() {
|
||||
mLeftEdge = true
|
||||
mRightEdgeKey = null
|
||||
}
|
||||
|
||||
private fun endRow() {
|
||||
val rightEdgeKey = mRightEdgeKey
|
||||
if (rightEdgeKey != null) {
|
||||
rightEdgeKey.markAsRightEdge(mParams)
|
||||
mCurrentY += rightEdgeKey.height + rightEdgeKey.verticalGap
|
||||
mRightEdgeKey = null
|
||||
}
|
||||
mLeftEdge = false
|
||||
mTopEdge = false
|
||||
}
|
||||
|
||||
private fun endKey(key: Key) {
|
||||
mParams.onAddKey(key)
|
||||
if (mLeftEdge) {
|
||||
key.markAsLeftEdge(mParams)
|
||||
mLeftEdge = false
|
||||
}
|
||||
if (mTopEdge) {
|
||||
key.markAsTopEdge(mParams)
|
||||
}
|
||||
mRightEdgeKey = key
|
||||
}
|
||||
|
||||
private fun endKeyboard() {
|
||||
mParams.removeRedundantMoreKeys()
|
||||
// {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than
|
||||
// previously expected.
|
||||
val actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding
|
||||
mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight)
|
||||
}
|
||||
|
||||
private fun addKeysToParams() {
|
||||
// need to reset it, we need to sum it up to get the height nicely
|
||||
// (though in the end we could just not touch it at all, final used value is the same as the one before resetting)
|
||||
mCurrentY = 0
|
||||
startKeyboard()
|
||||
for (row in keysInRows) {
|
||||
startRow()
|
||||
for (keyParams in row) {
|
||||
endKey(keyParams.createKey())
|
||||
}
|
||||
endRow()
|
||||
}
|
||||
endKeyboard()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BUILDER_TAG = "Keyboard.Builder"
|
||||
}
|
||||
}
|
||||
|
||||
// adapted from Kotlin source: https://github.com/JetBrains/kotlin/blob/7a7d392b3470b38d42f80c896b7270678d0f95c3/libraries/stdlib/common/src/generated/_Collections.kt#L3004
|
||||
private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float): Float {
|
||||
var sum = 0f
|
||||
for (element in this) {
|
||||
sum += selector(element)
|
||||
}
|
||||
return sum
|
||||
}
|
|
@ -217,11 +217,10 @@ public class KeyboardParams {
|
|||
mRightPadding = (int) keyboardAttr.getFraction(
|
||||
R.styleable.Keyboard_keyboardRightPadding, width, width, 0);
|
||||
|
||||
final int baseWidth = mOccupiedWidth - mLeftPadding - mRightPadding;
|
||||
mBaseWidth = baseWidth;
|
||||
mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding;
|
||||
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
1, 1, 1f / DEFAULT_KEYBOARD_COLUMNS);
|
||||
mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * baseWidth);
|
||||
mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * mBaseWidth);
|
||||
|
||||
// todo: maybe settings should not be accessed from here?
|
||||
if (Settings.getInstance().getCurrent().mNarrowKeyGaps) {
|
||||
|
@ -241,15 +240,14 @@ public class KeyboardParams {
|
|||
mHorizontalGap = (int) (mRelativeHorizontalGap * width);
|
||||
mVerticalGap = (int) (mRelativeVerticalGap * height);
|
||||
|
||||
final int baseHeight = mOccupiedHeight - mTopPadding - mBottomPadding + mVerticalGap;
|
||||
mBaseHeight = baseHeight;
|
||||
mBaseHeight = mOccupiedHeight - mTopPadding - mBottomPadding + mVerticalGap;
|
||||
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);
|
||||
mDefaultRowHeight = (int) (mDefaultRelativeRowHeight * mBaseHeight);
|
||||
}
|
||||
|
||||
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
|
||||
|
|
|
@ -38,8 +38,8 @@ public final class XmlKeyboardRow {
|
|||
|
||||
// TODO: Add keyActionFlags.
|
||||
private static class RowAttributes {
|
||||
/** Default width of a key in this row. */
|
||||
public final float mDefaultKeyWidth;
|
||||
/** Default width of a key in this row, relative to keyboard width */
|
||||
public final float mDefaultRelativeKeyWidth;
|
||||
/** Default keyLabelFlags in this row. */
|
||||
public final int mDefaultKeyLabelFlags;
|
||||
/** Default backgroundType for this row */
|
||||
|
@ -49,13 +49,11 @@ public final class XmlKeyboardRow {
|
|||
* Parse and create key attributes. This constructor is used to parse Row tag.
|
||||
*
|
||||
* @param keyAttr an attributes array of Row tag.
|
||||
* @param defaultKeyWidth a default key width.
|
||||
* @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
|
||||
* @param defaultRelativeKeyWidth a default key width relative to keyboardWidth.
|
||||
*/
|
||||
public RowAttributes(final TypedArray keyAttr, final float defaultKeyWidth,
|
||||
final int keyboardWidth) {
|
||||
mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
keyboardWidth, keyboardWidth, defaultKeyWidth);
|
||||
public RowAttributes(final TypedArray keyAttr, final float defaultRelativeKeyWidth) {
|
||||
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
1, 1, defaultRelativeKeyWidth);
|
||||
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
|
||||
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
|
||||
Key.BACKGROUND_TYPE_NORMAL);
|
||||
|
@ -67,12 +65,10 @@ public final class XmlKeyboardRow {
|
|||
*
|
||||
* @param keyAttr an attributes array of include tag.
|
||||
* @param defaultRowAttr default Row attributes.
|
||||
* @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
|
||||
*/
|
||||
public RowAttributes(final TypedArray keyAttr, final RowAttributes defaultRowAttr,
|
||||
final int keyboardWidth) {
|
||||
mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
keyboardWidth, keyboardWidth, defaultRowAttr.mDefaultKeyWidth);
|
||||
public RowAttributes(final TypedArray keyAttr, final RowAttributes defaultRowAttr) {
|
||||
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
1, 1, defaultRowAttr.mDefaultRelativeKeyWidth);
|
||||
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0)
|
||||
| defaultRowAttr.mDefaultKeyLabelFlags;
|
||||
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
|
||||
|
@ -101,8 +97,7 @@ public final class XmlKeyboardRow {
|
|||
}
|
||||
final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||
R.styleable.Keyboard_Key);
|
||||
mRowAttributesStack.push(new RowAttributes(
|
||||
keyAttr, params.mDefaultKeyWidth, params.mBaseWidth));
|
||||
mRowAttributesStack.push(new RowAttributes(keyAttr, params.mDefaultRelativeKeyWidth));
|
||||
keyAttr.recycle();
|
||||
|
||||
mCurrentY = y;
|
||||
|
@ -114,8 +109,7 @@ public final class XmlKeyboardRow {
|
|||
}
|
||||
|
||||
public void pushRowAttributes(final TypedArray keyAttr) {
|
||||
final RowAttributes newAttributes = new RowAttributes(
|
||||
keyAttr, mRowAttributesStack.peek(), mParams.mBaseWidth);
|
||||
final RowAttributes newAttributes = new RowAttributes(keyAttr, mRowAttributesStack.peek());
|
||||
mRowAttributesStack.push(newAttributes);
|
||||
}
|
||||
|
||||
|
@ -123,8 +117,8 @@ public final class XmlKeyboardRow {
|
|||
mRowAttributesStack.pop();
|
||||
}
|
||||
|
||||
public float getDefaultKeyWidth() {
|
||||
return mRowAttributesStack.peek().mDefaultKeyWidth;
|
||||
public float getDefaultRelativeKeyWidth() {
|
||||
return mRowAttributesStack.peek().mDefaultRelativeKeyWidth;
|
||||
}
|
||||
|
||||
public int getDefaultKeyLabelFlags() {
|
||||
|
@ -167,30 +161,24 @@ public final class XmlKeyboardRow {
|
|||
|
||||
public float getRelativeKeyWidth(final TypedArray keyAttr) {
|
||||
if (keyAttr == null)
|
||||
return mParams.mDefaultRelativeKeyWidth;
|
||||
return getDefaultRelativeKeyWidth();
|
||||
final int widthType = ResourceUtils.getEnumValue(keyAttr,
|
||||
R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
|
||||
if (widthType == KEYWIDTH_FILL_RIGHT)
|
||||
return -1;
|
||||
return -1f;
|
||||
// else KEYWIDTH_NOT_ENUM
|
||||
return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
1, 1, mParams.mDefaultRelativeKeyWidth);
|
||||
1, 1, getDefaultRelativeKeyWidth());
|
||||
}
|
||||
|
||||
public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) {
|
||||
if (keyAttr == null) {
|
||||
return getDefaultKeyWidth();
|
||||
}
|
||||
final int widthType = ResourceUtils.getEnumValue(keyAttr,
|
||||
R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
|
||||
switch (widthType) {
|
||||
case KEYWIDTH_FILL_RIGHT:
|
||||
final float relativeWidth = getRelativeKeyWidth(keyAttr);
|
||||
if (relativeWidth == -1f) {
|
||||
// If keyWidth is fillRight, the actual key width will be determined to fill
|
||||
// out the area up to the right edge of the keyboard.
|
||||
final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
|
||||
return keyboardRightEdge - keyXPos;
|
||||
default: // KEYWIDTH_NOT_ENUM
|
||||
return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
|
||||
mParams.mBaseWidth, mParams.mBaseWidth, getDefaultKeyWidth());
|
||||
}
|
||||
}
|
||||
return relativeWidth * mParams.mBaseWidth;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.*
|
|||
* "Appearance" settings sub screen.
|
||||
*/
|
||||
class AppearanceSettingsFragment : SubScreenFragment() {
|
||||
|
||||
private var needsReload = false
|
||||
|
||||
private val stylePref: ListPreference by lazy { preferenceScreen.findPreference(Settings.PREF_THEME_STYLE)!! }
|
||||
|
@ -30,17 +29,26 @@ class AppearanceSettingsFragment : SubScreenFragment() {
|
|||
private val dayNightPref: TwoStatePreference? by lazy { preferenceScreen.findPreference(Settings.PREF_THEME_DAY_NIGHT) }
|
||||
private val userColorsPref: Preference by lazy { preferenceScreen.findPreference("theme_select_colors")!! }
|
||||
private val userColorsPrefNight: Preference? by lazy { preferenceScreen.findPreference("theme_select_colors_night") }
|
||||
private val splitPref: TwoStatePreference? by lazy { preferenceScreen.findPreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD) }
|
||||
private val splitScalePref: Preference? by lazy { preferenceScreen.findPreference(Settings.PREF_SPLIT_SPACER_SCALE) }
|
||||
|
||||
|
||||
override fun onCreate(icicle: Bundle?) {
|
||||
super.onCreate(icicle)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addPreferencesFromResource(R.xml.prefs_screen_appearance)
|
||||
|
||||
removeUnsuitablePreferences()
|
||||
setupTheme()
|
||||
setThemeVariantPrefs(sharedPreferences.getString(Settings.PREF_THEME_STYLE, KeyboardTheme.STYLE_MATERIAL)!!)
|
||||
|
||||
setupKeyboardHeight(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
|
||||
setupScalePrefs(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
|
||||
if (splitScalePref != null) {
|
||||
setupScalePrefs(Settings.PREF_SPLIT_SPACER_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
|
||||
splitScalePref?.isVisible = splitPref?.isChecked == true
|
||||
splitPref?.setOnPreferenceChangeListener { _, value ->
|
||||
splitScalePref?.isVisible = value as Boolean
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
@ -52,7 +60,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
|
|||
|
||||
override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String?) {
|
||||
super.onSharedPreferenceChanged(prefs, key)
|
||||
needsReload = true // may not always be the necessary, but that's ok
|
||||
needsReload = true // may not always necessary, but that's ok
|
||||
}
|
||||
|
||||
private fun removeUnsuitablePreferences() {
|
||||
|
@ -79,6 +87,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
|
|||
val heightDp = metrics.heightPixels / metrics.density
|
||||
if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED || (min(widthDp, heightDp) < 600 && max(widthDp, heightDp) < 720)) {
|
||||
removePreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD)
|
||||
removePreference(Settings.PREF_SPLIT_SPACER_SCALE)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +149,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
|
|||
userColorsPrefNight?.isVisible = dayNightPref?.isChecked == true && colorsNightPref?.value == KeyboardTheme.THEME_USER_NIGHT
|
||||
}
|
||||
|
||||
private fun setupKeyboardHeight(prefKey: String, defaultValue: Float) {
|
||||
private fun setupScalePrefs(prefKey: String, defaultValue: Float) {
|
||||
val prefs = sharedPreferences
|
||||
val pref = findPreference(prefKey) as? SeekBarDialogPreference
|
||||
pref?.setInterface(object : SeekBarDialogPreference.ValueProxy {
|
||||
|
@ -149,13 +158,11 @@ class AppearanceSettingsFragment : SubScreenFragment() {
|
|||
|
||||
private fun getPercentageFromValue(floatValue: Float) = (floatValue * PERCENTAGE_FLOAT).toInt()
|
||||
|
||||
override fun writeValue(value: Int, key: String) = prefs.edit()
|
||||
.putFloat(key, getValueFromPercentage(value)).apply()
|
||||
override fun writeValue(value: Int, key: String) = prefs.edit().putFloat(key, getValueFromPercentage(value)).apply()
|
||||
|
||||
override fun writeDefaultValue(key: String) = prefs.edit().remove(key).apply()
|
||||
|
||||
override fun readValue(key: String) = getPercentageFromValue(
|
||||
Settings.readKeyboardHeight(prefs, defaultValue))
|
||||
override fun readValue(key: String) = getPercentageFromValue(prefs.getFloat(prefKey, defaultValue))
|
||||
|
||||
override fun readDefaultValue(key: String) = getPercentageFromValue(defaultValue)
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
public static final String PREF_SHOW_EMOJI_KEY = "pref_show_emoji_key";
|
||||
public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
|
||||
public static final String PREF_ENABLE_SPLIT_KEYBOARD = "pref_split_keyboard";
|
||||
public static final String PREF_SPLIT_SPACER_SCALE = "pref_split_spacer_scale";
|
||||
public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale";
|
||||
public static final String PREF_SPACE_TRACKPAD = "pref_space_trackpad";
|
||||
public static final String PREF_DELETE_SWIPE = "pref_delete_swipe";
|
||||
|
@ -341,13 +342,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
return res.getInteger(R.integer.config_clipboard_history_retention_time);
|
||||
}
|
||||
|
||||
public static float readKeyboardHeight(final SharedPreferences prefs,
|
||||
final float defaultValue) {
|
||||
final float percentage = prefs.getFloat(
|
||||
Settings.PREF_KEYBOARD_HEIGHT_SCALE, UNDEFINED_PREFERENCE_VALUE_FLOAT);
|
||||
return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue;
|
||||
}
|
||||
|
||||
public static boolean readSpaceTrackpadEnabled(final SharedPreferences prefs) {
|
||||
return prefs.getBoolean(PREF_SPACE_TRACKPAD, true);
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ public class SettingsValues {
|
|||
public final boolean mShouldShowLxxSuggestionUi;
|
||||
// Use split layout for keyboard.
|
||||
public final boolean mIsSplitKeyboardEnabled;
|
||||
public final float mSpacerRelativeWidth;
|
||||
public final int mScreenMetrics;
|
||||
public final boolean mAddToPersonalDictionary;
|
||||
public final boolean mUseContactsDictionary;
|
||||
|
@ -162,7 +163,12 @@ public class SettingsValues {
|
|||
mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
|
||||
mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
|
||||
mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
|
||||
mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false);
|
||||
final float displayWidthDp = res.getDisplayMetrics().widthPixels / res.getDisplayMetrics().density;
|
||||
mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false) && displayWidthDp > 600; // require display width of 600 dp for split
|
||||
// determine spacerWidth from display width and scale setting
|
||||
mSpacerRelativeWidth = mIsSplitKeyboardEnabled
|
||||
? Math.min(Math.max((displayWidthDp - 600) / 6000f + 0.15f, 0.15f), 0.25f) * prefs.getFloat(Settings.PREF_SPLIT_SPACER_SCALE, DEFAULT_SIZE_SCALE)
|
||||
: 0f;
|
||||
mScreenMetrics = Settings.readScreenMetrics(res);
|
||||
|
||||
mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI
|
||||
|
@ -188,7 +194,7 @@ public class SettingsValues {
|
|||
readSuggestionsEnabled(prefs);
|
||||
mIncognitoModeEnabled = Settings.readAlwaysIncognitoMode(prefs) || mInputAttributes.mNoLearning
|
||||
|| mInputAttributes.mIsPasswordField;
|
||||
mKeyboardHeightScale = Settings.readKeyboardHeight(prefs, DEFAULT_SIZE_SCALE);
|
||||
mKeyboardHeightScale = prefs.getFloat(Settings.PREF_KEYBOARD_HEIGHT_SCALE, DEFAULT_SIZE_SCALE);
|
||||
mDisplayOrientation = res.getConfiguration().orientation;
|
||||
mAppWorkarounds = new AsyncResultHolder<>("AppWorkarounds");
|
||||
final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
<string name="settings_category_miscellaneous">Miscellaneous</string>
|
||||
<!-- Option for enabling or disabling the split keyboard layout. [CHAR LIMIT=65]-->
|
||||
<string name="enable_split_keyboard">Enable split keyboard</string>
|
||||
<!-- Option for setting distance for split keyboard -->
|
||||
<string name="split_spacer_scale">Split distance</string>
|
||||
<!-- Option name for including other IMEs in the language key switch list [CHAR LIMIT=30] -->
|
||||
<string name="language_switch_key_switch_input_method">Switch to other input methods</string>
|
||||
<!-- Option name for switching language / subtype only [CHAR LIMIT=30] -->
|
||||
|
|
|
@ -63,6 +63,12 @@
|
|||
android:persistent="true"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<org.dslul.openboard.inputmethod.latin.settings.SeekBarDialogPreference
|
||||
android:key="pref_split_spacer_scale"
|
||||
android:title="@string/split_spacer_scale"
|
||||
latin:minValue="50"
|
||||
latin:maxValue="200" /> <!-- percentage -->
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="pref_narrow_key_gaps"
|
||||
android:title="@string/prefs_narrow_key_gaps"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue