Skip to content

[Feature Request]: RTL support for **all** Krypton.Navigator.Utilities controls #4356

Description

@PWagner1

Feature Title

RTL support for all Krypton.Navigator.Utilities controls

Feature Description

Krypton.Navigator.Utilities RTL Compatibility Evaluation Report

Executive Summary

Krypton.Navigator.Utilities has no explicit RTL implementation. A repository-wide search of Source/Krypton Components/Krypton.Navigator.Utilities/ finds zero references to RightToLeft, RightToLeftLayout, IsRightToLeftLayout, RtlLayout, or RTL. There is no helper comparable to RibbonRtlLayout / WorkspaceRtlLayout, no RightToLeftChanged listeners, no TestForm RTL demo, and no unit-test script.

Some caption packing can incidentally flip Left/Right because injected views live on a KryptonForm whose ViewDrawDocker / ViewLayoutDocker.CalculateDock already honour both flags (#2103). That is not a designed contract: spare caption geometry, maximized insets, tab reorder, “close tabs to the right”, tear-out windows, and context menus remain LTR-hardcoded.

Overall RTL implementation status: ~15% complete (incidental docker flip only; 0% explicit).

Target contract (do not invent a third model):

  • Both Control.RightToLeft == Yes and a bool RightToLeftLayout must be set (same as KryptonForm / KryptonRibbon / KryptonNavigator / KryptonWorkspace).
  • Do not enable WS_EX_LAYOUTRTL or GDI-mirror glyphs/text.
  • Collection order, XML persistence, and Pages / TabGroups order stay logical (first item remains first in the collection even when it paints on the visual right).
  • Vertical stacks stay top-to-bottom.
  • Host forms already own the flags; Utilities should read them from the integrated KryptonForm (and from the navigator, which copies the layout flag from the parent form per [Feature Request]: RTL support for **all** Krypton.Navigator controls #2381).

Scope

Location: Source/Krypton Components/Krypton.Navigator.Utilities/

Package: shipped inside Krypton.Standard.Toolkit (the assembly is not a standalone NuGet id).

Public / Toolbox surface evaluated:

Type Role RTL status
KryptonNavigatorFormIntegrator Browser-style caption tabs, client chrome, document groups Critical gaps
KryptonTabbedMdiManager MDI child → navigator tab bar Mostly inherits Navigator; untested
KryptonNavigatorTaskbarThumbnails Per-page / per-group taskbar flyout Low (OS-owned order)
NavigatorTabGroup / NavigatorTabGroupCollection / NavigatorTabGroupAppearance Catalog + wash/underline Data only; consumers need RTL
NavigatorTabGroupLayoutSerializer XML page order + groups Keep logical order (same as Workspace #2383)
NavigatorTabGroupBarAccent Tab border tint No geometry
Designers / event args Design-time and payloads No layout

Internal layout / interaction (the real work):

  1. ViewLayoutNavigatorCaptionTabs
  2. ViewLayoutCaptionDocumentGroups
  3. ViewDrawTabGroupHeader / ViewDrawTabGroupChrome
  4. NavigatorCaptionDragPageNotify
  5. TearOutFeedbackWindow
  6. NavigatorTabGroupDragHelper

What already works by inheritance (do not re-implement)

These are out of scope except to consume them:

Critical findings

1. Project-wide: no RTL API or gating

Krypton.Navigator.Utilities.csproj has nothing RTL-related (NeutralLanguage=en is unrelated). No type in the assembly:

  • exposes or syncs RightToLeftLayout
  • calls CommonHelper.IsRightToLeftLayout / GetRightToLeftLayout
  • subscribes to RightToLeftChanged / RightToLeftLayoutChanged
  • packs from client.Right the way RibbonRtlLayout / WorkspaceRtlLayout do

Impact: consumers who already set both flags on a KryptonForm (the documented contract) get undefined Utilities behaviour. Caption-integrated chrome is the worst case because it replaces the navigator bar with a custom view tree.

2. Caption tab strip is LTR-hardcoded (ViewLayoutNavigatorCaptionTabs)

File: View Layout/ViewLayoutNavigatorCaptionTabs.cs

Rebuild always docks Left, then reverse-adds so the first page is leftmost:

  • Comment at RebuildTabs: “ViewLayoutDocker lays out Left-docked children in reverse collection order (last child becomes leftmost).”
  • Every tab, group header, separator, and new-tab button is Add(..., ViewDockStyle.Left).
  • ShowNewTabButton XML: “shown to the right of the last caption tab.”

Custom Layout re-implements Left/Right packing (it does not call base.Layout). It still calls CalculateDock, so when context.Control is an RTL KryptonForm, Left children become Right and the reverse-add trick can accidentally put page 0 on the visual right.

That incidental flip is not sufficient:

Area LTR assumption RTL failure
Spare caption _spareCaptionRect = fillerRect after left packing; comment “spare caption space to the right of the tab strip” After a Right-pack, leftover space is on the left. CustomCaptionArea can point at the wrong band (or empty), so caption drag / double-click / icon hit-testing regress.
Maximized inset SyncChromePadding applies maximizedEdge = 8 to Left only (padding.Right unchanged) Under RTL the start edge is the physical right (icon side). The strip can sit flush on the right monitor edge while over-padding the control-box side.
Padding on Top orientation fillerRect.X += Padding.Left Left/Right metric padding is not swapped for reading order.
Drag reorder movingBefore = captionPoint.X < targetMid then insertIndex = movingBefore ? targetIndex : targetIndex + 1 Visual-left of a tab is collection-after once tabs pack from the right. Drop-to-reorder inserts on the wrong side of the target.
Group header Chip is always before members in visualItems (LTR “header then tabs”) After a docker flip this may become “tabs then header” relative to reading order, or stay header-at-start depending on packing — needs an explicit rule: header stays on the start edge of its members.
New-tab + Last visual item (LTR far/end) Must stay on the end edge (visual left under RTL), not follow collection reverse blindly.
Tooltips / context menus Show(screenPoint) without a RightToLeft source control ContextMenuStrip / KryptonContextMenu may stay LTR; item text should still read correctly if strings are localized.

ControllerPointToScreen / ScreenToCaptionPoint offset by RealWindowBorders.Left/Top. That mapping is physical (no WS_EX_LAYOUTRTL) and is probably fine; do not “fix” it unless a repro shows a hit-test miss.

3. Form integrator injects the strip as ViewDockStyle.Left

File: Controls Toolkit/KryptonNavigatorFormIntegrator.cs

_form.InjectViewElement(_captionTabs, ViewDockStyle.Left);
_form.InjectViewElement(_captionDocumentGroups, ViewDockStyle.Left);

Revoke uses the same Left dock.

On an RTL form, _drawHeading (ViewDrawDocker) will flip that to physical Right. Combined with #2103:

  • Control box is on the physical left.
  • Icon is on the physical right.
  • The whole tab strip then docks on the icon (start) side — which is the desired reading-order start if inner packing also starts there.

Required work: keep injecting Left and let CalculateDock flip or inject start-edge explicitly via a helper. Either way, document it, re-layout on RTL flag changes, and fix spare-area math so leftover caption is the end side (toward the control box), not “always ClientRectangle.Right”.

Tear-out windows ignore RTL. TryTearOutPages constructs a new KryptonForm and KryptonNavigator without copying RightToLeft or RightToLeftLayout from the source form. Dropping a tab out of an Arabic/Hebrew window opens a LTR host. Copy both flags (and palette / size) from _form.

Window placement uses dropScreenPoint.X - 40 (always to the left of the cursor). Minor; optional to use a start-edge offset.

ClientChrome / CaptionAdjacent: no custom caption view. They should inherit Navigator ButtonSpec RTL (#2381) and form chrome (#2103). Still add a TestForm toggle and confirm min/max/close sit on the start edge and do not fight caption-integrated mode.

No RightToLeftChanged hook on the integrator: toggling flags at runtime relies on the form’s existing layout pass. Caption tabs rebuild on page changes only — RTL toggle must PerformNeedPaint(true) / rebuild spare areas.

4. Multi-strip document groups (ViewLayoutCaptionDocumentGroups)

File: View Layout/ViewLayoutCaptionDocumentGroups.cs

// Residual caption to the right of the entire multi-strip composite.
ClientRectangle.Right - borders.Left
residual.Right - ClientRectangle.Right

If the composite docks Right under RTL, unused caption is to the left of ClientRectangle. Residual width becomes 0 / negative and caption dragging in the empty header dies.

Workspace visual cell order (A on the right of C under RTL) vs caption strip order must be specified: caption strips should match visual cell order (start-edge first), not a second independent LTR row.

New-tab + is shown only on ActiveCell; under RTL it must remain the end-edge of that cell’s strip.

5. “Close tabs to the right” is collection-after, labelled as visual-right

Files:

  • KryptonNavigatorFormIntegrator.ClosePagesToRight — removes Pages with index > startIndex.
  • NavigatorFormIntegrationStrings.CloseTabsToTheRight default: "Close tabs to the right".

Under RTL packing, collection-after is the visual left. Browser (Chrome/Edge) RTL builds typically keep the command as “Close tabs to the right” meaning visual right, or switch the copy to “Close tabs to the end”.

Recommendation: keep closing collection-after (stable, matches XML order, matches Workspace’s “after stays collection-after” rule from #2383). Change the default English string (and description) to something direction-neutral, e.g. “Close tabs to the end”, and/or swap the command to visual-right only if product wants Chrome parity. Do not silently close visual-right while the menu still says “right” after tabs have mirrored.

ContextMenuStrip is created without RightToLeft = Yes. Show(screenPoint) does not inherit from the form. Set RightToLeft from the host form when building/showing the built-in menu.

6. Tear-out feedback overlay (TearOutFeedbackWindow)

  • Inherits KryptonForm but never sets RTL flags.
  • Paints ↗ {NewWindow} with Graphics.DrawString and no StringFormat.DirectionRightToLeft / alignment.
  • The ↗ glyph is LTR-biased; under RTL use a start-edge arrow or drop the glyph (text is already localizable via NavigatorFormIntegrationStrings.NewWindow).

Low severity; cheap to fix while touching drag.

7. Tabbed MDI (KryptonTabbedMdiManager)

Treat as verify + demo, not a second layout engine.

8. Taskbar thumbnails (KryptonNavigatorTaskbarThumbnails)

Windows ITaskbarList3 tab order follows Pages collection (and group composites inserted ahead of members). The OS flyout is LTR. Do not reverse thumbnail registration to match visual tab order — that would desync DWM tab ids from Pages and fight Explorer. Optional: document that flyout order is logical, not visual.

Live preview bitmaps are page captures; no mirroring (correct — no WS_EX_LAYOUTRTL).

9. Persistence (NavigatorTabGroupLayoutSerializer)

Saves Pages order, TabGroupId, selected unique name, and the group catalog. Same rule as Workspace #2383: do not reverse XML. RTL is a view concern only. Call this out in the issue so a future PR does not “fix” save/load by writing visual order.

10. Group chrome drawing

ViewDrawTabGroupHeader / ViewDrawCaptionGroupTab / ViewDrawTabGroupChrome draw a full-width wash and a bottom underline. Underlines stay bottom (not a Left/Right issue). Text comes from ViewDrawButton / palette content, which already has some RTL via Toolkit. No extra mirroring of accents.

Incidental CalculateDock behaviour (verify, then replace with an explicit helper)

When the host KryptonForm has both flags:

  1. InjectViewElement(..., Left) → heading docker paints the strip on the physical right.
  2. Caption Layout CalculateDock(Left) → children pack from fillerRect.Right.
  3. Reverse-add of visualItems → page 0 lands on the visual right, + on the visual left.

That can look “almost RTL” in a screenshot and still be wrong for drag, spare caption, maximized padding, and “close to the right”.

Do not ship that as the feature. Add an internal NavigatorUtilitiesRtlLayout (mirror RibbonRtlLayout / WorkspaceRtlLayout) with IsRtl(KryptonForm?), StartX, NextItem, and use it in caption layout, spare-area publish, and reorder. Keep CalculateDock for the form heading docker (it already matches #2103) unless tests show a double-flip.

Required implementation (suggested phases)

Phase 1 — Contract and caption strip (high)

  1. Internal NavigatorUtilitiesRtlLayout gated on CommonHelper.IsRightToLeftLayout(form).
  2. Pack caption tabs / headers / + / separators from the start edge; spare caption is the leftover end side.
  3. Symmetric maximized insets (both physical edges, or start+end explicitly).
  4. Reorder: movingBefore means start-side of the target (visual right when RTL), mapped back to collection insert.
  5. Integrator: copy RTL flags onto tear-out forms; relayout on form RTL changes; keep InjectViewElement Left + docker flip or document start-edge inject.
  6. Built-in context menu: RightToLeft from the form; direction-neutral “close to the end” (or documented visual-right behaviour).

Phase 2 — Document groups and drag (high)

  1. ViewLayoutCaptionDocumentGroups spare/residual geometry from the end edge.
  2. Strip order matches workspace visual cell order under [Feature Request]: RTL support for **all** Krypton.Workspace controls #2383 without changing Children / XML.
  3. Tear-out feedback string/glyph; optional cursor offset.

Phase 3 — Tabbed MDI + ClientChrome (medium)

  1. Confirm Navigator/form inheritance; add RTL toggles to TabbedMdiDemo and NavigatorFormIntegrationDemo (CaptionAdjacent / ClientChrome / CaptionIntegrated).
  2. DocumentGroupsDemo toggle aligned with WorkspaceRtlDemo.

Phase 4 — Taskbar / persistence / strings (low)

  1. Document logical thumbnail order.
  2. Serializer remains collection order.
  3. Localizable strings: CloseTabsToTheRight default/description.

Testing requirements

No TestForm RTL demo exists for this assembly today (NavigatorFormIntegrationDemo, DocumentGroupsDemo, TabbedMdiDemo, Feature882NavigatorTaskbarThumbnailsDemo are LTR-only). WorkspaceRtlDemo covers cell headers via Navigator #2381, not caption-injected tabs.

Proposed TestForm: append RTL checkboxes (RightToLeft / RightToLeftLayout / both) on NavigatorFormIntegrationDemo and DocumentGroupsDemo — do not overwrite those demos. Register a short note on StartScreen if a dedicated NavigatorUtilitiesRtlDemo is easier to screenshot.

Manual matrix (both flags on vs LTR default):

  1. CaptionIntegrated — first page on the start (right) edge; + on the end (left); form min/max/close still on the left; icon on the right; leftover caption still drags the window.
  2. Maximized — tabs not flush on either monitor edge.
  3. Reorder — drop on the start half of a tab inserts before in visual reading order; Pages order updates logically; save/load XML order unchanged.
  4. Groups — header chip stays on the start side of its members; collapse/expand; drop on header still joins the group.
  5. Close tabs to the end/right — matches the chosen spec (collection-after vs visual-right); menu layout RTL.
  6. Tear-out — new window copies both flags; remerge onto another integrated caption still works; empty source still closes when enabled.
  7. Document groups — multi-strip order matches workspace visual cells; spare regions between/after strips remain draggable.
  8. ClientChrome / CaptionAdjacent — ButtonSpecs on the start edge ([Feature Request]: RTL support for **all** Krypton.Navigator controls #2381); no caption-strip regressions.
  9. Tabbed MDI — bar tabs pack RTL via Navigator; MDI children unchanged.
  10. Taskbar — flyout still one thumbnail per page in Pages order.

Suggested script: Scripts/UnitTests/UnitTest-NavigatorUtilitiesRtlLayout.ps1 (STA, host NavigatorFormIntegrationDemo, assert first-tab X > last-tab X when both flags set; spare CustomCaptionArea non-empty on the end side). Pattern: UnitTest-WorkspaceRtlLayout.ps1.

Standard-Toolkit-Demos: append the same two-flag toggle on the existing Navigator Form Integration / Document Groups examples (do not clone the demos repo if ..\Standard-Toolkit-Demos already exists; branch alpha-… from alpha).

Priority

Priority Item
High Caption tab packing, spare caption, maximized insets, reorder insert side
High Document-group residual spare areas + visual strip order
High Tear-out form copies RightToLeft + RightToLeftLayout
Medium Context menu RTL + “close to the right/end” semantics
Medium ClientChrome / CaptionAdjacent / Tabbed MDI verification + TestForm toggles
Low Tear-out overlay glyph; taskbar documentation

Conclusion

Unlike Krypton.Navigator (strong bar RTL) and the completed Ribbon/Workspace logical packing, Navigator Utilities caption chrome is an LTR view tree sitting on an RTL-aware form. Docker CalculateDock can hide that in a still screenshot. Drag, spare caption, maximized padding, tear-out, and “close to the right” will still fail Arabic/Hebrew / RightToLeftLayout hosts.

Estimated implementation effort: 25–40 hours (caption layout + document groups + tear-out + demos/tests), not a greenfield ribbon-scale rewrite.

Dependencies: #2103 (form), #2381 (navigator layout flag), #2383 (workspace cell order). Do not enable WS_EX_LAYOUTRTL.

Key recommendations:

  1. Reuse the two-flag contract and a small NavigatorUtilitiesRtlLayout helper (copy the Ribbon/Workspace pattern).
  2. Treat caption spare geometry and reorder as first-class RTL, not as fallout from ViewDockStyle.Left.
  3. Keep Pages / XML / taskbar tab order logical.
  4. Copy RTL flags onto tear-out windows.
  5. Extend existing TestForm demos; add a STA unit-test script.

Related issues

Implementation Ideas (Optional)

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:navigator-utilitiesAll things related to the 'Krypton.Navigator.Utilities'.enhancementNew feature or request✨ new featureA new feature has been requested.💡 suggestionA suggestion has been requested.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions