Skip to content

feat: context menu in MintedEditorView (right-click and long-press) - #33

Merged
michaelstonis merged 7 commits into
mainfrom
housekeeping/context-menu
Oct 21, 2026
Merged

michaelstonis merged 7 commits into
mainfrom
housekeeping/context-menu

Conversation

@michaelstonis

Copy link
Copy Markdown
Contributor

Summary

Core already had ContextMenuModel/ContextMenuRenderer and the theme defined context-menu colours, but the control never showed a menu. Now it does.

Opening it:

  • Right-click: opens the menu on the canvas. Windows and Mac Catalyst also get a secondary-button TapGestureRecognizer, because Mac Catalyst doesn't report right-clicks as canvas touches; a guard stops Windows opening the menu twice. If you right-click outside the selection, the caret moves to that spot first.
  • Long-press rule:
    • Long-press on the selection: opens the menu and keeps the selection.
    • Long-press and release without moving: places the caret and opens the menu (for Paste).
    • Long-press and drag: caret drag with the magnifier, as before.

Using it:

  • Choosing an item: tap, click or hover; Up/Down/Enter also work. Escape closes the menu, and any other key closes it and is then handled as normal.
  • Closing: a press outside the menu, the mouse wheel, ScrollTo, a document change, focus loss or turning on read-only all close it.
  • Item actions: Cut, Copy and Paste use the same clipboard path as the keyboard shortcuts, so Cut can be undone. Insert Hyperlink, Image and Table prompt the same way the toolbar does. Edit Hyperlink, which is new, prompts with the current URL.
  • New API: IsContextMenuEnabled (default true) and a ContextMenuItemsRequested event for customising the items. The event's args type already existed but was never raised.
  • Colours: taken from the theme's context-menu colours.

Core changes:

  • ContextMenuRenderer gets colour properties, ApplyStyle and Bounds. Keyboard and hit-testing skip disabled items.
  • ContextMenuFactory now offers the hyperlink items when the caret is on a link. Before, they needed a non-empty selection, so a long press on a link never showed them.

Testing

  • 18 new tests in ContextMenuTests cover the menu contents for different selection states, hit testing, bounds and clamping, keyboard navigation and theme colours.
  • Core.Tests pass (812) and SkiaSharp.Tests pass (29). The SkiaSharp Release build has 0 warnings, and the MAUI shared code compiles cleanly.
  • Not tested on a device: right-click and long-press have only been compiled. They need checking on Android, iOS, Mac Catalyst and Windows, especially that the long-press rule doesn't get in the way of caret drag.

Not included

  • Menu icons are not drawn.
  • No Shift+F10 / Menu-key shortcut, because EditorKey has no such keys.
  • Paste is always enabled, without checking the clipboard.
  • Menu labels are localised in the separate EditorStrings PR.

Checklist

  • CHANGELOG Added/Changed entries
  • docs (commands-events, getting-started, theming, architecture) and README updated

Generated by Claude Code

Core already had ContextMenuFactory/ContextMenuRenderer and the themes
define context-menu colours, but the MAUI control never showed a menu.

MintedEditorView now opens the menu on a right-click (canvas right-button
press, plus a secondary-button tap recognizer on Windows and Mac Catalyst,
where secondary clicks are not canvas touches) and on touch long presses
that are not used to drag the caret: a long press on the selection keeps
the selection and opens the menu, and a long press released without
moving opens it at the caret. Long press then drag still moves the caret
with the magnifier. The menu is drawn on the canvas with the theme
colours, highlights on hover, supports Up/Down/Enter/Escape, and closes on
a press outside, wheel scroll, ScrollTo, document change and focus loss.
Items run through the same paths as the toolbar (undoable clipboard
cut/copy/paste, prompts for hyperlink/image/table, HyperlinkHandler for
Open Hyperlink). New IsContextMenuEnabled bindable property and
ContextMenuItemsRequested event.

Core: ContextMenuRenderer gains colour properties, ApplyStyle(EditorStyle)
and Bounds; arrow keys skip disabled items and disabled items are never
focused or returned by Enter. ContextMenuFactory shows the hyperlink items
when the caret is on a link, where the hyperlink commands act.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
Copilot AI balanced review requested due to automatic review settings October 21, 2026 16:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Input routing, menu overflow, hyperlink undo and custom-command handling have unresolved correctness issues.

5 open issues
What changed in this PR

Connects the existing Core context-menu system to MintedEditorView for right-click and long-press interaction.

Changes:

  • Adds menu gestures, keyboard handling, clipboard actions and customization.
  • Adds theme support and disabled-item navigation.
  • Expands tests and documents the public API.
File Description
tests/​MintedTextEditor.Core.Tests/​MockDrawingContext.cs Records additional drawing calls.
tests/​MintedTextEditor.Core.Tests/​ContextMenuTests.cs Tests menu contents, navigation, layout and colors.
src/​MintedTextEditor.Maui/​MintedEditorView.cs Integrates menu input, rendering and actions.
src/​MintedTextEditor.Core/​ContextMenu/​ContextMenuRenderer.cs Adds theming, bounds and enabled-item navigation.
src/​MintedTextEditor.Core/​ContextMenu/​ContextMenuModel.cs Detects hyperlinks at the caret.
README.md Introduces context-menu support.
docs/​theming.md Documents menu colors.
docs/​getting-started.md Documents configuration and gestures.
docs/​commands-events.md Explains actions and customization.
docs/​architecture.md Describes menu integration.
CHANGELOG.md Records the new functionality.

Review effort: Balanced

# Conflicts:
#	CHANGELOG.md
…ink undo, IME Enter

- A press outside the open menu now ignores the rest of that contact, so its
  moves and release no longer drag a selection, scroll or raise HyperlinkClicked.
- ContextMenuRenderer keeps the popup inside the viewport in both directions:
  a menu taller than the view is shrunk to fit and its items scroll
  (ScrollOffset/MaxScrollOffset/ScrollBy, scroll indicator, clipped drawing,
  HitTest and arrow keys follow the scroll). A new Open overload sizes the
  popup to its longest label (at least MinWidth, at most the viewport) so
  translated labels are not clipped. MintedEditorView scrolls the menu with a
  finger drag or the mouse wheel over it.
- Only the default menu's Insert Table command prompts for a size; an
  InsertTableCommand added through ContextMenuItemsRequested runs as given.
- Edit Hyperlink goes through the undo manager with a new EditHyperlinkAction
  (restores the old URL and title on undo).
- A standalone newline committed by an Android soft keyboard while the menu
  is open activates the highlighted item instead of splitting the paragraph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
Brings in localized UI strings, theme hover/focus-ring colours, character
and hyperlink undo, editor events/FormattingState, find/replace,
accessibility semantics, paragraph undo and caret formatting.

Conflict resolution:
- ContextMenuModel: keep main's EditorStrings-based labels and the
  CreateDefault(ctx, strings) overload together with this branch's
  caret-on-link detection.
- MintedEditorView: keep both sides (context menu + pointer hover, focus
  ring, semantics, editor events, strings subscription). The context menu
  draws above the focus ring, receives every pointer event while open, and
  clears toolbar/link hover when it opens; IsReadOnly and handler detach
  close the menu and clear hover / unsubscribe from strings. The edit-link
  prompt now uses EditorStrings (new EditHyperlinkDialogTitle and
  EditHyperlinkDialogSave), and an open menu closes when
  EditorStrings.Current changes. Remove Hyperlink goes through main's
  undoable RemoveHyperlinkCommand, and items raise selection/formatting
  events through the same paths as the toolbar.
- Tests: keep both sides' HyperlinkTests; one MockDrawingContext with the
  record-based DrawTextInRectCalls/FillRoundRectCalls (main's tests read
  .Text/.Rect from them); branch context-menu tests use the localized
  sentence-case labels and drop a duplicate of main's insert-link test.
- Docs/CHANGELOG: union of both sides; theming no longer lists the
  context-menu colours as unused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
# Conflicts:
#	CHANGELOG.md
@michaelstonis
michaelstonis merged commit 150193b into main Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants