diff --git a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java index f1ff26dfa..c642688db 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java @@ -619,12 +619,13 @@ protected void onDetachedFromWindow() { @Nullable public PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, @NonNull final PointerTracker tracker) { - return showPopupKeysKeyboard(key, tracker, false); + return showPopupKeysKeyboard(key, tracker, false, PopupKeysKeyboardView.NO_ROW_ALIGN); } @Nullable private PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, - @NonNull final PointerTracker tracker, final boolean belowSourceKey) { + @NonNull final PointerTracker tracker, final boolean belowSourceKey, + final int rowAlignedLeftX) { final PopupKeySpec[] popupKeys = key.getPopupKeys(); if (popupKeys == null) { return null; @@ -675,8 +676,10 @@ private PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, // {@code mPreviewVisibleOffset} has been set appropriately in // {@link KeyboardView#showKeyPreview(PointerTracker)}. final int pointY = belowSourceKey - ? key.getY() + key.getHeight() + container.getMeasuredHeight() + ? key.getY() + key.getHeight() : key.getY() + mKeyPreviewDrawParams.getVisibleOffset(); + popupKeysKeyboardView.setShowBelowAnchor(belowSourceKey); + popupKeysKeyboardView.setRowAlignedLeftX(rowAlignedLeftX); popupKeysKeyboardView.showPopupKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); return popupKeysKeyboardView; } @@ -695,7 +698,18 @@ public PopupKeysPanel showShortcutRowKeyboard(@NonNull final Key key, if (popupParentKey == null) { return null; } - return showPopupKeysKeyboard(popupParentKey, tracker, belowSourceKey); + // Align the shortcut popup's icons to the source letter row so the swiped key maps to the + // icon directly above it, instead of re-anchoring on the swiped key (which the irregular + // shift/backspace keys at the row edges throw off). Row left = leftmost normal key in the row. + int rowLeftX = Integer.MAX_VALUE; + for (final Key rowKey : keyboard.getSortedKeys()) { + if (rowKey.getY() == key.getY() && rowKey.getBackgroundType() == Key.BACKGROUND_TYPE_NORMAL + && !rowKey.isModifier() && !rowKey.isSpacer()) { + rowLeftX = Math.min(rowLeftX, rowKey.getX()); + } + } + if (rowLeftX == Integer.MAX_VALUE) rowLeftX = key.getX(); + return showPopupKeysKeyboard(popupParentKey, tracker, belowSourceKey, rowLeftX); } public boolean isInDraggingFinger() { diff --git a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java index ef5b80352..20e1bf47c 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java @@ -44,6 +44,14 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane private int mOriginX; private int mOriginY; private Key mCurrentKey; + // When true, the panel is anchored below the source key (its top aligned to pointY) + // instead of the default above-key placement (its bottom aligned to pointY). + private boolean mShowBelowAnchor; + // Shortcut-row popup: when set (not NO_ROW_ALIGN), the panel's visible content left edge is + // pinned to this x (the source letter row's left edge) so the icons tile across the row and the + // swiped key maps to the icon directly above it — instead of re-anchoring on the swiped key. + public static final int NO_ROW_ALIGN = Integer.MIN_VALUE; + private int mRowAlignedLeftX = NO_ROW_ALIGN; private int mActivePointerId; @@ -137,6 +145,15 @@ public void showPopupKeysPanel(final View parentView, final Controller controlle mEmojiViewCallback = emojiViewCallback; showPopupKeysPanelInternal(parentView, controller, pointX, pointY); } + // Must be called before showPopupKeysPanel(...) for a given show; resets per show. + public void setShowBelowAnchor(final boolean below) { + mShowBelowAnchor = below; + } + + // Must be called before showPopupKeysPanel(...) for a given show; NO_ROW_ALIGN resets it. + public void setRowAlignedLeftX(final int rowLeftX) { + mRowAlignedLeftX = rowLeftX; + } @SuppressLint("RtlHardcoded") // a key on the left is on the left, independent of layout direction private void showPopupKeysPanelInternal(final View parentView, final Controller controller, @@ -145,9 +162,19 @@ private void showPopupKeysPanelInternal(final View parentView, final Controller final View container = getContainerView(); // The coordinates of panel's left-top corner in parentView's coordinate system. // We need to consider background drawable paddings. - final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft(); - final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom() - + getPaddingBottom(); + // Default: anchor the panel so its default column sits under pointX (centered on the key). + // Row-aligned (shortcut-row popup): pin the visible content left edge to the source row's + // left edge so the icons tile across the row and the swiped key maps to the icon above it. + final int x = (mRowAlignedLeftX != NO_ROW_ALIGN) + ? mRowAlignedLeftX - container.getPaddingLeft() - getPaddingLeft() + : pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft(); + // Above-anchor (default): panel bottom aligned to pointY (popup sits above the key). + // Below-anchor: panel top aligned to pointY (popup sits below the key), mirroring the + // padding compensation so the visible content edge lands on pointY either way. + final int y = mShowBelowAnchor + ? pointY - container.getPaddingTop() - getPaddingTop() + : pointY - container.getMeasuredHeight() + container.getPaddingBottom() + + getPaddingBottom(); parentView.getLocationInWindow(mCoordinates); final int containerY = y + CoordinateUtils.y(mCoordinates);