Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.swmansion.enriched.markdown.utils.common.FeatureFlags
import com.swmansion.enriched.markdown.utils.common.MarkdownSegmentRenderer
import com.swmansion.enriched.markdown.utils.common.RenderedSegment
import com.swmansion.enriched.markdown.utils.common.splitASTIntoSegments
import com.swmansion.enriched.markdown.utils.text.view.applySelectionColors
import com.swmansion.enriched.markdown.utils.text.view.emitLinkLongPressEvent
import com.swmansion.enriched.markdown.utils.text.view.emitLinkPressEvent
import com.swmansion.enriched.markdown.views.BlockSegmentView
Expand Down Expand Up @@ -54,6 +55,8 @@ class EnrichedMarkdown
private var maxFontSizeMultiplier: Float = 0f
private var allowTrailingMargin: Boolean = false
private var selectable: Boolean = true
private var selectionColor: Int? = null
private var selectionHandleColor: Int? = null

Comment thread
xindixu marked this conversation as resolved.
private var onLinkPressCallback: ((String) -> Unit)? = null
private var onLinkLongPressCallback: ((String) -> Unit)? = null
Expand Down Expand Up @@ -128,6 +131,22 @@ class EnrichedMarkdown
}
}

fun setSelectionColor(color: Int?) {
selectionColor = color
applySelectionColorsToSegments()
}

fun setSelectionHandleColor(color: Int?) {
selectionHandleColor = color
applySelectionColorsToSegments()
}
Comment thread
xindixu marked this conversation as resolved.

private fun applySelectionColorsToSegments() {
segmentViews.filterIsInstance<EnrichedMarkdownInternalText>().forEach {
it.applySelectionColors(selectionColor, selectionHandleColor)
}
}

fun setOnLinkPressCallback(callback: (String) -> Unit) {
onLinkPressCallback = callback
}
Expand Down Expand Up @@ -234,6 +253,8 @@ class EnrichedMarkdown
if (contextMenuItemTexts.isNotEmpty()) {
setContextMenuItems(contextMenuItemTexts, ::forwardContextMenuItemPress)
}

applySelectionColors(selectionColor, selectionHandleColor)
}

private fun createTableView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ class EnrichedMarkdownManager :
view?.setIsSelectable(selectable)
}

override fun setSelectionColor(
view: EnrichedMarkdown?,
value: Int?,
) {
view?.setSelectionColor(value)
}

override fun setSelectionHandleColor(
view: EnrichedMarkdown?,
value: Int?,
) {
view?.setSelectionHandleColor(value)
}

@ReactProp(name = "md4cFlags")
override fun setMd4cFlags(
view: EnrichedMarkdown?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.swmansion.enriched.markdown.utils.text.TailFadeInAnimator
import com.swmansion.enriched.markdown.utils.text.interaction.CheckboxTouchHelper
import com.swmansion.enriched.markdown.utils.text.view.LinkLongPressMovementMethod
import com.swmansion.enriched.markdown.utils.text.view.applySelectableState
import com.swmansion.enriched.markdown.utils.text.view.applySelectionColors
import com.swmansion.enriched.markdown.utils.text.view.cancelJSTouchForCheckboxTap
import com.swmansion.enriched.markdown.utils.text.view.cancelJSTouchForLinkTap
import com.swmansion.enriched.markdown.utils.text.view.createSelectionActionModeCallback
Expand Down Expand Up @@ -81,6 +82,9 @@ class EnrichedMarkdownText
private set
var spoilerOverlay: SpoilerOverlay = SpoilerOverlay.PARTICLES

private var selectionColor: Int? = null
private var selectionHandleColor: Int? = null

init {
setupAsMarkdownTextView()
customSelectionActionModeCallback =
Expand Down Expand Up @@ -248,6 +252,8 @@ class EnrichedMarkdownText
fadeAnimator?.animate(tailStart, styledText.length)
previousTextLength = styledText.length
}

applySelectionColors(selectionColor, selectionHandleColor)
}

fun setContextMenuItems(items: List<String>) {
Expand All @@ -258,6 +264,16 @@ class EnrichedMarkdownText
applySelectableState(selectable)
}

fun setSelectionColor(color: Int?) {
selectionColor = color
applySelectionColors(selectionColor, selectionHandleColor)
}

fun setSelectionHandleColor(color: Int?) {
selectionHandleColor = color
applySelectionColors(selectionColor, selectionHandleColor)
}
Comment thread
xindixu marked this conversation as resolved.

fun emitOnLinkPress(url: String) {
emitLinkPressEvent(url)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ class EnrichedMarkdownTextManager :
view?.setIsSelectable(selectable)
}

override fun setSelectionColor(
view: EnrichedMarkdownText?,
value: Int?,
) {
view?.setSelectionColor(value)
}

override fun setSelectionHandleColor(
view: EnrichedMarkdownText?,
value: Int?,
) {
view?.setSelectionHandleColor(value)
}

@ReactProp(name = "md4cFlags")
override fun setMd4cFlags(
view: EnrichedMarkdownText?,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.swmansion.enriched.markdown.utils.text.view

import android.graphics.drawable.Drawable
import android.os.Build
import android.util.Log
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.graphics.drawable.DrawableCompat

private const val TAG = "TextSelectionColors"

private typealias HandleGetter = (TextView) -> Drawable?
private typealias HandleSetter = (TextView, Drawable) -> Unit

/**
* Applies selection highlight and (where supported) handle tinting to a [TextView].
*
* Handle drawables are only tinted on API 29+ where the framework exposes getters;
* on older versions the handle theme defaults remain unchanged.
*/
fun TextView.applySelectionColors(
selectionColor: Int?,
Comment thread
xindixu marked this conversation as resolved.
selectionHandleColor: Int?,
) {
selectionColor?.let { highlightColor = it }
selectionHandleColor?.let { applySelectionHandleTint(it) }
}

private fun TextView.applySelectionHandleTint(
@ColorInt color: Int,
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return

val handles: List<Pair<HandleGetter, HandleSetter>> =
listOf(
TextView::getTextSelectHandleLeft to { tv, d -> tv.setTextSelectHandleLeft(d) },
TextView::getTextSelectHandle to { tv, d -> tv.setTextSelectHandle(d) },
TextView::getTextSelectHandleRight to { tv, d -> tv.setTextSelectHandleRight(d) },
)

handles.forEach { (getter, setter) ->
try {
getter(this)?.mutate()?.also { DrawableCompat.setTint(it, color) }?.let { setter(this, it) }
} catch (e: LinkageError) {
Log.w(TAG, "Selection handle tint skipped: ${e.message}")
}
}
}
2 changes: 2 additions & 0 deletions apps/example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export default function App() {
onLinkPress={handleLinkPress}
markdownStyle={markdownStyle}
contextMenuItems={contextMenuItems}
selectionColor={Platform.OS === 'ios' ? '#5A52FA' : '#DCDDFE'}
selectionHandleColor="#5A52FA"
/>
</ScrollView>
)}
Expand Down
2 changes: 2 additions & 0 deletions apps/web-example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ export default function App() {
onLinkPress={onLinkPress}
onLinkLongPress={onLinkLongPress}
onTaskListItemPress={onTaskListItemPress}
selectionColor="#DCDDFE"
selectionHandleColor="#5A52FA"
/>

<View style={styles.divider} />
Expand Down
27 changes: 27 additions & 0 deletions ios/EnrichedMarkdown.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import "EditMenuUtils.h"

#import "ENRMFeatureFlags.h"
#import "ENRMUIKit.h"

#if ENRICHED_MARKDOWN_MATH
#import "ENRMMathContainerView.h"
Expand Down Expand Up @@ -90,6 +91,7 @@ + (instancetype)segmentWithLatex:(NSString *)latex
#endif

@interface EnrichedMarkdown () <RCTEnrichedMarkdownViewProtocol, UITextViewDelegate>
- (void)applySelectionColor:(const EnrichedMarkdownProps &)props toTextView:(ENRMPlatformTextView *)textView;
@end

@implementation EnrichedMarkdown {
Expand Down Expand Up @@ -493,6 +495,9 @@ - (EnrichedMarkdownInternalText *)createTextViewForRenderedSegment:(ENRMRenderRe
view.textView.selectable = _selectable;
[view applyAttributedText:segment.attributedText context:segment.context];

const auto &selectionProps = *std::static_pointer_cast<EnrichedMarkdownProps const>(self->_props);
[self applySelectionColor:selectionProps toTextView:view.textView];

ENRMTapRecognizer *tapRecognizer = [[ENRMTapRecognizer alloc] initWithTarget:self action:@selector(textTapped:)];
[view.textView addGestureRecognizer:tapRecognizer];

Expand Down Expand Up @@ -657,6 +662,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
}
}

if (newViewProps.selectionColor != oldViewProps.selectionColor) {
#if !TARGET_OS_OSX
for (RCTUIView *segment in _segmentViews) {
if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
ENRMPlatformTextView *tv = ((EnrichedMarkdownInternalText *)segment).textView;
[self applySelectionColor:newViewProps toTextView:tv];
}
}
#endif
}

if (markdownChanged || stylePropChanged || md4cFlagsChanged || allowTrailingMarginChanged) {
NSString *markdownString = [[NSString alloc] initWithUTF8String:newViewProps.markdown.c_str()];
[self renderMarkdownContent:markdownString];
Expand Down Expand Up @@ -880,4 +896,15 @@ - (NSInteger)indexOfAccessibilityElement:(id)element
}
#endif

- (void)applySelectionColor:(const EnrichedMarkdownProps &)props toTextView:(ENRMPlatformTextView *)textView
{
#if !TARGET_OS_OSX
if (isColorMeaningful(props.selectionColor)) {
ENRMSetSelectionColor(textView, RCTUIColorFromSharedColor(props.selectionColor));
} else {
ENRMSetSelectionColor(textView, nil);
}
#endif
}
Comment thread
xindixu marked this conversation as resolved.

@end
11 changes: 11 additions & 0 deletions ios/EnrichedMarkdownText.mm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "ENRMTailFadeInAnimator.h"
#import "ENRMTextRenderer.h"
#import "ENRMTextViewSetup.h"
#import "ENRMUIKit.h"
#import "EditMenuUtils.h"
#import "FontScaleObserver.h"
#import "FontUtils.h"
Expand Down Expand Up @@ -411,6 +412,16 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
_textView.selectable = newViewProps.selectable;
}

if (newViewProps.selectionColor != oldViewProps.selectionColor) {
#if !TARGET_OS_OSX
if (isColorMeaningful(newViewProps.selectionColor)) {
ENRMSetSelectionColor(_textView, RCTUIColorFromSharedColor(newViewProps.selectionColor));
} else {
ENRMSetSelectionColor(_textView, nil);
}
#endif
}
Comment thread
xindixu marked this conversation as resolved.

if (newViewProps.allowFontScaling != oldViewProps.allowFontScaling) {
_fontScaleObserver.allowFontScaling = newViewProps.allowFontScaling;

Expand Down
2 changes: 2 additions & 0 deletions ios/input/EnrichedMarkdownTextInput.mm
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
if (newViewProps.selectionColor != oldViewProps.selectionColor) {
if (isColorMeaningful(newViewProps.selectionColor)) {
ENRMSetSelectionColor(_textView, RCTUIColorFromSharedColor(newViewProps.selectionColor));
} else {
ENRMSetSelectionColor(_textView, nil);
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/EnrichedMarkdownNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ export interface NativeProps extends ViewProps {
* @default true
*/
selectable?: boolean;
/**
* Color of the text selection highlight.
*
* On iOS, this also affects the caret and selection handle colors
* (they share a single tint).
*
* @platform ios, android, web
*/
selectionColor?: ColorValue;
/**
* Color of the selection handles (drag anchors).
* No-op on API levels below 29.
*
* @platform android
*/
selectionHandleColor?: ColorValue;
/**
Comment thread
xindixu marked this conversation as resolved.
* MD4C parser flags configuration.
* Controls how the markdown parser interprets certain syntax.
Expand Down
16 changes: 16 additions & 0 deletions src/EnrichedMarkdownTextNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ export interface NativeProps extends ViewProps {
* @default true
*/
selectable?: boolean;
/**
* Color of the text selection highlight.
*
* On iOS, this also affects the caret and selection handle colors
* (they share a single tint).
*
* @platform ios, android, web
*/
selectionColor?: ColorValue;
/**
* Color of the selection handles (drag anchors).
* No-op on API levels below 29.
*
* @platform android
*/
selectionHandleColor?: ColorValue;
Comment thread
xindixu marked this conversation as resolved.
/**
* MD4C parser flags configuration.
* Controls how the markdown parser interprets certain syntax.
Expand Down
4 changes: 4 additions & 0 deletions src/native/EnrichedMarkdownText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const EnrichedMarkdownText = ({
streamingAnimation = false,
spoilerOverlay = 'particles',
contextMenuItems,
selectionColor,
selectionHandleColor,
...rest
}: EnrichedMarkdownTextProps) => {
const normalizedStyleRef = useRef<MarkdownStyleInternal | null>(null);
Expand Down Expand Up @@ -137,6 +139,8 @@ export const EnrichedMarkdownText = ({
style: containerStyle,
contextMenuItems: nativeContextMenuItems,
onContextMenuItemPress: handleContextMenuItemPress,
selectionColor,
selectionHandleColor,
...rest,
};

Expand Down
18 changes: 17 additions & 1 deletion src/types/MarkdownTextProps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ViewProps, ViewStyle, TextStyle } from 'react-native';
import type { ColorValue, ViewProps, ViewStyle, TextStyle } from 'react-native';
import type { MarkdownStyle, Md4cFlags } from './MarkdownStyle';
import type {
LinkPressEvent,
Expand Down Expand Up @@ -91,6 +91,22 @@ export interface EnrichedMarkdownTextProps extends Omit<ViewProps, 'style'> {
* @platform ios, android, web
*/
selectable?: boolean;
/**
* Color of the text selection highlight.
*
* On iOS, this also affects the caret and selection handle colors
* (they share a single tint).
*
* @platform ios, android, web
*/
selectionColor?: ColorValue;
/**
* Color of the selection handles (drag anchors).
* No-op on API levels below 29.
*
* @platform android
*/
selectionHandleColor?: ColorValue;
Comment thread
xindixu marked this conversation as resolved.
/**
* Specifies whether fonts should scale to respect Text Size accessibility settings.
* When false, text will not scale with the user's accessibility settings.
Expand Down
Loading
Loading