Skip to content

android, desktop: fix several RTL layout issues#6908

Draft
Narasimha-sc wants to merge 2 commits into
masterfrom
nd/fix-RTL-issues
Draft

android, desktop: fix several RTL layout issues#6908
Narasimha-sc wants to merge 2 commits into
masterfrom
nd/fix-RTL-issues

Conversation

@Narasimha-sc
Copy link
Copy Markdown
Collaborator

Fixes #5448.

The issue documents four user-visible regressions under an RTL locale
(Arabic, Persian, Hebrew). This PR addresses all of them and a handful
of related directional-icon mirroring sites discovered while auditing.

Changes

  • Chat bubble tail direction (chatItemShape in ChatItemView.kt). The
    shape's GenericShape block now uses the layoutDirection parameter that
    Compose already passes in. The matrix-mirror predicate flips from sent
    to sent != isRtl, so sent-bubble tails point toward the user's profile
    and received-bubble tails toward the contact's profile under both LTR and
    RTL. The bubble-tail padding(start, end) in FramedItemView.kt and
    ChatView.kt is already direction-aware, so it falls into place against
    the corrected shape automatically.

  • App-bar slot mirroring (CenteredRowLayout in FramedItemView.kt).
    Switched the three placements from place(...) to placeRelative(...).
    Both the chat header (call + 3-dot-menu buttons, back arrow) and the chat
    list header (profile avatar, signal icon, "Chats" title) route through
    this layout, so one edit covers both panes.

  • Asymmetric directional drawables. Added Modifier.mirrorIfRtl()
    (Modifier.scale(-1f, 1f) when LocalLayoutDirection.current is Rtl)
    in views/helpers/Modifiers.kt and applied it to:

    • the back arrow in NavigationButtonBack
    • all five variants of the SubscriptionStatusIcon waves
    • the pagination chevrons in WhatsNewView
    • the back arrow + submenu chevron in CommandsMenuView
    • the back arrow in OnboardingCards
    • the back arrow in ImageFullScreenView.desktop
    • the share-profile chevron in NewChatView
    • the sent-via-proxy arrow in CIMetaView and ChatItemInfoView

Notes

  • DropdownMenu DpOffset(-width.value, ...) calls in ChatView.kt are
    intentionally left as-is. DropdownMenuPositionProvider already negates
    the content offset under RTL, so once CenteredRowLayout is fixed the
    chat-header menus anchor correctly and open in the right direction
    without any code change at the call sites.
  • Two settings rows route their icon through helpers
    (SimpleButtonIconEnded, SettingsPreferenceItem) that don't accept a
    per-icon Modifier. They are not addressed in this PR — fixing them
    would require extending those helper signatures and is out of scope for
    the user-visible bugs reported in the issue.
  • No iOS-side changes — the iOS message composer architecture handles all
    these cases via UIKit's RTL support and SwiftUI's semantic edges.

Test plan

Switch the in-app language to Arabic / Persian / Hebrew (or system locale
to RTL) and walk through the screens called out in the issue:

  • Open any chat — sent-message tails point to your profile (visual
    side closest to your avatar in 2-pane mode); received-message tails
    point to the contact's avatar.
  • Chat header — call + 3-dot menu on the visual left, back chevron on
    the visual right and pointing right (>). Tapping the 3-dot menu
    opens a dropdown that anchors on the left and stays on-screen.
  • Settings → Your settings — back chevron points > and is on the
    right edge of the navigation header.
  • Chat list header — profile avatar on the right; "Chats" title and
    signal/wifi icon on the left, with the signal icon's "waves"
    mirrored.
  • Settings → About → What's new — pagination chevrons mirror.
  • Sent-via-proxy badge in chat-item meta and message info — arrow
    mirrors.
  • Switch back to English — every above location matches pre-PR
    behavior; no LTR regression.

Fixes #5448

The original report documents four user-visible regressions under an RTL
locale (Arabic, Persian, Hebrew): chat bubble tails on the wrong side,
chat header call/menu buttons not mirrored, settings back arrow pointing
the wrong way, and the chat list profile avatar / signal icon laid out
in their LTR positions. This commit addresses all of them and a handful
of related directional-icon mirroring sites discovered during the audit.

- Chat bubble tail direction: chatItemShape now reads layoutDirection
  from the GenericShape lambda and conditions the matrix mirror on
  sent != isRtl, so sent-bubble tails point toward the user's profile
  and received-bubble tails toward the contact's profile under both
  LTR and RTL. The bubble-tail paddings in FramedItemView and ChatView
  already use direction-aware start/end and now align with the
  mirrored shape automatically.

- App-bar slot mirroring: CenteredRowLayout in FramedItemView switches
  from place(...) to placeRelative(...) so the navigation icon and the
  action buttons swap visual sides under RTL. Both the chat header
  (call/menu buttons) and the chat list header (profile avatar, signal
  icon) flow through this layout, so one fix covers both.

- Asymmetric directional drawables: add Modifier.mirrorIfRtl()
  (Modifier.scale(-1f, 1f) when LocalLayoutDirection.current is Rtl)
  and apply it to the back arrow in NavigationButtonBack, every variant
  of the SubscriptionStatusIcon waves, the WhatsNewView pagination
  arrows, the back arrow and submenu chevron in CommandsMenuView, the
  back arrow in OnboardingCards and ImageFullScreenView.desktop, the
  share-profile chevron in NewChatView, and the sent-via-proxy arrow
  in CIMetaView and ChatItemInfoView.

DropdownMenu offsets in ChatView (DpOffset(-width, ...)) are left
unchanged: DropdownMenuPositionProvider already negates the content
offset under RTL, so menus anchor and open correctly once
CenteredRowLayout is fixed.
@Narasimha-sc Narasimha-sc marked this pull request as draft April 28, 2026 15:57
Surgical refinements on top of the initial RTL fix, plus a design doc
in plans/.

- chatItemShape (ChatItemView.kt): extract `val tailOnRight = ...` so
  the predicate names what the conditional does. Comment trimmed to
  the single load-bearing invariant ("default path draws tail at
  bottom-left") since the rest is implied by the variable name.

- SubscriptionStatusIcon.kt: hoist `val iconModifier =
  modifier.mirrorIfRtl()` once at the top of the composable and reuse
  it at all five Icon call sites. Calling mirrorIfRtl five times or
  once produces the same Modifier within one composition.

- GoToItemInnerButton (ChatItemView.kt): add `mirror: Boolean = false`
  parameter and pass `mirror = true` at the directional call site
  (ic_arrow_forward, "go to forwarded source"). The other call site
  (ic_search) keeps the default and is unchanged.

- Modifiers.kt, FramedItemView.kt: drop two explanatory comments that
  restated framework-primitive behavior (mirrorIfRtl's own name, and
  the documented behavior of placeRelative).

plans/2026-05-13-rtl-layout-issues.md documents the three independent
mechanisms behind the fix (bubble shape, app-bar slot placement,
asymmetric drawables), the bubble-tail XOR predicate, and four
considered-and-rejected alternatives.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant