# PandaText API on every platform

PandaText is a rich-text editor SDK: a C++ text engine with thin native views for iOS and macOS (Swift), Android (Kotlin), the web (WebAssembly), Flutter and React Native. Each row is one editor job and the call that does it on each target. `—` means the target has no call for that job.

## Setup

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Create | `PandaTextView(frame:fontCollection:pixelSize:)` | `PandaTextView(context)` | `new PandaTextView({ textEngine, fontCollection })` | `PandaTextView(fonts:, text:)`, `PandaTextView(fonts:, document:)` | `<PandaTextView fonts={...} />` |
| Show and release | `addSubview(_:)` | `addView(view)` | `mount(container)`, `destroy()` | The widget tree | The component tree |
| Fonts | `fontCollection` (set at init, then read-only) | `fontCollection` | `fontCollection` | `fonts` | `fonts` |
| Base font size | `pixelSize` | `pixelSize` | `fontSize` (option `fontSizeCss`) | `fontSize` | `fontSize` |

## Text

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Set the whole text | `string` | `text` | `text` | `text` | `text` |
| Read the whole text | `string` | `text` | `text` | `state.text` | `ref.getText()` |
| Replace a range | `replaceText(in:with:actionName:)` | `replaceText(start, end, text)` | `replaceText(start, end, replacement)` | `state.replaceText(start, end, text)` | `ref.replaceText(start, end, text)` |
| Reach the document | `document` (read-only) | `document` (read-only), `attachDocument(document)` | `document` (read-only) | `state.document`, `document` | — |
| Read-only mode | `isEditable` | `isEditable` | `editable` (property only) | `editable` | `editable` |
| Hear a user's change | macOS `NSText.didChangeNotification`, iOS `UITextView.textDidChangeNotification` | `onTextDidChange` | `input` event on `element` | `onTextDidChange` | `onTextDidChange` |
| Read what changed | `makeChangeCursor()`, `changes(since:capacity:advance:)` | `createChangeCursor()`, `changesSince(cursor, capacity, advance)` | `createChangeCursor()`, `changesSince(cursor, { capacity, advance })` | `state.createChangeCursor()`, `state.changesSince(cursor, capacity:, advance:)` | `places` in the `onTextDidChange` payload |

## Selection and focus

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Set the selection | `selectedUTF16Range` | `setSelectionRange(start, end)` | `setSelection(start, end, { anchor })` | `state.setSelection(start, end)` | `ref.setSelection(start, end)` |
| Read the selection | `selectedUTF16Range` | `selectionStartOffset`, `selectionEndOffset` | `orderedSelection()` | `state.orderedSelection()`, `state.selection`, `state.selectedText` | `ref.getSelection()` |
| Hear a selection change | macOS `NSTextView.didChangeSelectionNotification`, iOS `PandaTextView.selectionDidChangeNotification` | `onSelectionDidChange` | `selectiondidchange` event on `element` | `onSelectionDidChange` | `onSelectionChange` |
| Take focus | macOS `window.makeFirstResponder(view)`, iOS `becomeFirstResponder()` | `requestFocus()` | `focusEditor()` | `state.focus()`, `autofocus`, `focusNode` | `ref.focus()` |
| Give up focus | macOS `window.makeFirstResponder(nil)`, iOS `resignFirstResponder()` | `clearFocus()` | — | `state.blur()`, `focusNode` | `ref.blur()` |
| Hear a focus change | macOS —, iOS `UITextView.textDidBeginEditingNotification`, `UITextView.textDidEndEditingNotification` | `setOnFocusChangeListener(listener)` | The DOM's focus events | `focusNode` | `onFocus`, `onBlur` |

## Undo

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Undo and redo | macOS `undo(_:)`, `redo(_:)`, iOS `undoManager?.undo()`, `undoManager?.redo()` | `undo()`, `redo()` | `undo()`, `redo()` | `state.undo()`, `state.redo()` | `ref.undo()`, `ref.redo()` |
| Ask what can be undone | `undoManager?.canUndo`, `undoManager?.canRedo` | `canUndo()`, `canRedo()` | `canUndo()`, `canRedo()` | `state.canUndo`, `state.canRedo` | `ref.getUndoState()` |
| Hear the undo state change | — | — | — | — | `onUndoStateChange` |
| Set the undo limit | `undoLevels` | `undoLevels` | `undoLevels` | `undoLevels` | `undoLevels` |
| End the merging of edits | `breakUndoCoalescing()` | `document.breakHistoryBurst()` | `document.breakHistoryBurst()` | `document.breakHistoryBurst()` | `ref.breakUndoCoalescing()` |
| Snapshot and restore | `snapshotDocument()`, `restoreDocument(_:selection:actionName:)` | `snapshotDocument()`, `restoreDocument(snapshot)` | `snapshotDocument()`, `restoreDocument(snapshot, selection)` | `state.snapshotDocument()`, `state.restoreDocument(snapshot, selection:)` | — |

## Clipboard and kill ring

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Cut, copy, paste | `cut(_:)`, `copy(_:)`, `paste(_:)` | — | `performEditAction("cut")`, `performEditAction("copy")`, `performEditAction("paste")` | `state.cutSelection()`, `state.copySelection()`, `state.paste()` | — |
| Select all | `selectAll(_:)` | — | `performEditAction("selectAll")` | `state.selectAll()` | — |
| Yank | macOS `yank(_:)`, iOS — | — | `yank()` | `state.yank()` | — |

## Styles

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Set all style runs | `styleRuns` | `styleRuns` | `styleRuns` | `styleRuns` | `styleRuns` |
| Style a range | `applyStyle(in:fields:value:actionName:)` | `applyStyle(start, end, mask, value)` | `applyStyle(startOffset, endOffset, style)` | `state.applyStyle(start, end, fieldMask, value)` | `ref.applyStyle(start, end, style)` |
| Toggle a style | `toggleBold()`, `toggleItalic()`, `toggleUnderline()`, `toggleStrikethrough()` | `toggleBold()`, `toggleItalic()`, `toggleUnderline()`, `toggleStrikethrough()` | `toggleBold()`, `toggleItalic()`, `toggleUnderline()`, `toggleStrikethrough()` | `state.toggleBold()`, `state.toggleItalic()`, `state.toggleUnderline()`, `state.toggleStrikethrough()` | `ref.toggleBold()`, `ref.toggleItalic()`, `ref.toggleUnderline()`, `ref.toggleStrikethrough()` |
| Set the typing style | `setTypingStyle(fields:value:)` | `setTypingStyle(mask, value)` | `setTypingStyle(style)` | `state.setTypingStyle(fieldMask, value)` | `ref.setTypingStyle(style)` |
| Read a style | `styleAt(_:)` | `styleAt(offset)` | `styleAt(offset)` | `state.styleAt(offset)` | `ref.styleAt(offset)` |

## Lists and line breaks

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Set list markers | `listMarkers`, `setListMarkers(_:actionName:)` | `listMarkers`, `setListMarkers(markers)` | `listMarkers`, `setListMarkers(markers)` | `listMarkers`, `state.setListMarkers(markers)`, `document.setBlockMarkers(blocks, markers)` | `listMarkers`, `ref.setListMarkers(markers)` |
| Renumber markers | `listMarkerTextProvider` | `listMarkerTextProvider` | `setListMarkerTextProvider(provider)` (option `listMarkerTextProvider`) | `listMarkerTextProvider` | — |
| Marker gap | `markerGap` | `markerGap` | `markerGap` | `markerGap` | `markerGap` |
| Keep a range on one line | `setNoBreakRanges(_:actionName:)` | `setNoBreakRanges(ranges)` | `setNoBreakRanges(ranges)` | `state.setNoBreakRanges(ranges)` | `ref.setNoBreakRanges(ranges)` |

## Layout and appearance

These options have the same name on every target: `alignment`, `baseDirection`, `justified`, `lineHeight`, `paragraphSpacing`, `paragraphSpacingBefore`, `breakStrategy`, `hyphenationFrequency`, `localeList`, `lineBreakRules`, `underlineInkSkip`, `underlineSkipSpaces`.

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Insets | `textContainerInset` | `textContainerInset` | `insets` | `insets` | `insets` |
| Selection color | macOS `selectionBackgroundColor`, iOS `tintColor` | `selectionColor` | `selectionColor` | `selectionColor` | `selectionColor` |
| Lazy layout threshold | `lazyLayoutThresholdUnits` | `lazyLayoutThresholdUnits` | `lazyLayoutThresholdUnits` | `lazyLayoutThresholdUnits` | `lazyLayoutThresholdUnits` |

## Scrolling

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Page up and down | macOS `pageUp(_:)`, `pageDown(_:)`, `pageUpAndModifySelection(_:)`, `pageDownAndModifySelection(_:)`, iOS `pageUp(extendingSelection:)`, `pageDown(extendingSelection:)` | `pageUp(extendingSelection)`, `pageDown(extendingSelection)` | `pageUp({ extendingSelection })`, `pageDown({ extendingSelection })` | `state.pageUp(extendingSelection:)`, `state.pageDown(extendingSelection:)` | `ref.pageUp(extendingSelection)`, `ref.pageDown(extendingSelection)` |
| Center the selection | macOS `centerSelectionInVisibleArea(_:)`, iOS `centerSelectionInVisibleArea()` | `centerSelectionInVisibleArea()` | `centerSelectionInVisibleArea()` | `state.centerSelectionInVisibleArea()` | `ref.centerSelectionInVisibleArea()` |
| Scroll position | macOS `enclosingScrollView`, iOS `contentOffset` | `scrollTo(x, y)`, `scrollY` | `contentScrollTop`, `maxContentScrollTop` | `state.contentScrollTop`, `state.maxContentScrollTop`, `state.scrollTo(offset)`, `controller` | `ref.scrollTo(offset)` |
| Turn scrolling off | macOS —, iOS `isScrollEnabled` | `isScrollEnabled` | — | `scrollEnabled` | `scrollEnabled`, `autoGrow` |
| Content size | macOS `intrinsicContentSize`, iOS `contentSize` | `contentHeight`, `onContentSizeDidChange` | — | `state.contentHeight`, `onContentSizeDidChange` | `ref.getContentSize()`, `onContentSizeChange` |

## Editing rules

| Job | Apple | Android | Web | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| Follow a native view's editing | `editingRule` | `editingRule` | `editingRule` | `editingRule` | `editingRule` |
| Touch long press | — | `longPress` | `longPress` | `longPress` | `longPress` (Android only) |
| Finger tap | — | `touchTap` | `touchTap` | `touchTap` | `touchTap` (Android only) |
| Left and Right in mixed text | `arrowMovement` | `arrowMovement` | `arrowMovement` | `arrowMovement` | `arrowMovement` |

## Keyboard shortcuts

| Command | macOS and iOS | Windows, Linux, Android |
| --- | --- | --- |
| Undo | ⌘Z | Ctrl+Z |
| Redo | ⌘⇧Z | Ctrl+Shift+Z |
| Cut | ⌘X | Ctrl+X |
| Copy | ⌘C | Ctrl+C |
| Paste | ⌘V | Ctrl+V |
| Select all | ⌘A |  |
| Bold | ⌘B | Ctrl+B |
| Italic | ⌘I | Ctrl+I |
| Underline | ⌘U | Ctrl+U |
| Strikethrough | ⌘⇧X | Ctrl+Shift+X |
