Skip to content

Commit e763467

Browse files
authored
feat(android): Add fontName prop (#146)
1 parent 0960db2 commit e763467

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ Optional. When set to `true`, the context menu is triggered with a single tap in
9696

9797
Optional. Disable menu interaction.
9898

99+
### `fontName`
100+
Optional, Android only. Custom font name to use for menu items. The font must be available in your app's font resources.
101+
99102
### `borderRadius`
100103
Optional, iOS only. Sets the border radius for all corners of the preview. Can be overridden by individual corner settings.
101104

android/src/main/java/com/mpiannucci/reactnativecontextmenu/ContextMenuManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public void setDisabled(ContextMenuView view, @Nullable boolean disabled) {
5050
view.setDisabled(disabled);
5151
}
5252

53+
@ReactProp(name = "fontName")
54+
public void setFontName(ContextMenuView view, @Nullable String fontName) {
55+
view.setFontName(fontName);
56+
}
57+
5358
@androidx.annotation.Nullable
5459
@Override
5560
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {

android/src/main/java/com/mpiannucci/reactnativecontextmenu/ContextMenuView.java

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import android.content.res.ColorStateList;
55
import android.content.res.Resources;
66
import android.graphics.Color;
7+
import android.graphics.Paint;
8+
import android.graphics.Typeface;
79
import android.graphics.drawable.Drawable;
810
import android.os.Build;
911
import android.text.SpannableString;
1012
import android.text.Spanned;
13+
import android.text.TextPaint;
1114
import android.text.style.ForegroundColorSpan;
15+
import android.text.style.TypefaceSpan;
1216
import android.view.GestureDetector;
1317
import android.view.Menu;
1418
import android.view.MenuItem;
@@ -34,6 +38,7 @@
3438

3539
public class ContextMenuView extends ReactViewGroup implements View.OnCreateContextMenuListener {
3640
@Nullable ReadableArray actions;
41+
@Nullable private String fontName; // Default font name
3742

3843
boolean cancelled = true;
3944

@@ -45,6 +50,46 @@ public class ContextMenuView extends ReactViewGroup implements View.OnCreateCont
4550

4651
private GestureDetector gestureDetector;
4752

53+
private static class CustomTypefaceSpan extends TypefaceSpan {
54+
private final Typeface newType;
55+
56+
public CustomTypefaceSpan(String family, Typeface type) {
57+
super(family);
58+
newType = type;
59+
}
60+
61+
@Override
62+
public void updateDrawState(TextPaint ds) {
63+
applyCustomTypeFace(ds, newType);
64+
}
65+
66+
@Override
67+
public void updateMeasureState(TextPaint paint) {
68+
applyCustomTypeFace(paint, newType);
69+
}
70+
71+
private static void applyCustomTypeFace(Paint paint, Typeface tf) {
72+
int oldStyle;
73+
Typeface old = paint.getTypeface();
74+
if (old == null) {
75+
oldStyle = 0;
76+
} else {
77+
oldStyle = old.getStyle();
78+
}
79+
80+
int fake = oldStyle & ~tf.getStyle();
81+
if ((fake & Typeface.BOLD) != 0) {
82+
paint.setFakeBoldText(true);
83+
}
84+
85+
if ((fake & Typeface.ITALIC) != 0) {
86+
paint.setTextSkewX(-0.25f);
87+
}
88+
89+
paint.setTypeface(tf);
90+
}
91+
}
92+
4893
public ContextMenuView(final Context context) {
4994
super(context);
5095

@@ -142,9 +187,36 @@ public void setDisabled(@Nullable boolean disabled) {
142187
this.disabled = disabled;
143188
}
144189

190+
public void setFontName(@Nullable String fontName) {
191+
this.fontName = fontName;
192+
}
193+
194+
private Typeface getCustomFont() {
195+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && fontName != null) {
196+
try {
197+
Resources resources = getContext().getResources();
198+
int fontId = resources.getIdentifier(fontName, "font", getContext().getPackageName());
199+
if (fontId != 0) {
200+
return ResourcesCompat.getFont(getContext(), fontId);
201+
}
202+
} catch (Exception e) {
203+
// Fallback to default font if custom font is not available
204+
}
205+
}
206+
return null;
207+
}
208+
145209
private void createContextMenuSubMenu(Menu menu, ReadableMap action, ReadableArray childActions, int i) {
146210
String title = action.getString("title");
147-
Menu parentMenu = menu.addSubMenu(title);
211+
212+
// Apply custom font to submenu title
213+
SpannableString spannableTitle = new SpannableString(title);
214+
Typeface customFont = getCustomFont();
215+
if (customFont != null) {
216+
spannableTitle.setSpan(new CustomTypefaceSpan("", customFont), 0, title.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
217+
}
218+
219+
Menu parentMenu = menu.addSubMenu(spannableTitle);
148220

149221
@Nullable Drawable icon = getResourceWithName(getContext(), action.getString("icon"));
150222
menu.getItem(i).setIcon(icon); // set icon to current item.
@@ -160,7 +232,14 @@ private void createContextMenuAction(Menu menu, ReadableMap action, int i, int p
160232
String title = action.getString("title");
161233
@Nullable Drawable icon = getResourceWithName(getContext(), action.getString("icon"));
162234

163-
MenuItem item = menu.add(Menu.NONE, Menu.NONE, i, title);
235+
// Create spannable string with custom font
236+
SpannableString spannableTitle = new SpannableString(title);
237+
Typeface customFont = getCustomFont();
238+
if (customFont != null) {
239+
spannableTitle.setSpan(new CustomTypefaceSpan("", customFont), 0, title.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
240+
}
241+
242+
MenuItem item = menu.add(Menu.NONE, Menu.NONE, i, spannableTitle);
164243
item.setEnabled(!action.hasKey("disabled") || !action.getBoolean("disabled"));
165244

166245
if (action.hasKey("iconColor") && icon != null) {
@@ -172,7 +251,7 @@ private void createContextMenuAction(Menu menu, ReadableMap action, int i, int p
172251
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
173252
item.setIconTintList(ColorStateList.valueOf(Color.RED));
174253
}
175-
SpannableString redTitle = new SpannableString(title);
254+
SpannableString redTitle = new SpannableString(spannableTitle);
176255
redTitle.setSpan(new ForegroundColorSpan(Color.RED), 0, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
177256
item.setTitle(redTitle);
178257
}

index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ export interface ContextMenuProps extends ViewProps {
120120
* Disable shadow in preview
121121
*/
122122
disableShadow?: boolean;
123+
/**
124+
* Custom font name to use for menu items (Android only). The font must be available in your app's font resources.
125+
*/
126+
fontName?: string;
123127
}
124128

125129
export default class ContextMenu extends Component<ContextMenuProps> { }

0 commit comments

Comments
 (0)