Skip to content

Commit c2a26e9

Browse files
Nitin-100Nitin Chaudhary
andauthored
More robust handling for Caret color related to Issue 14378 (#15121)
* More robust handling for Caret color * Removing Platform brush instead using proper caret brush * Change files * Magic numbers to proper constants and utility function added * Missing header fix. * Resolving PAPER failure with this FABRIC fix. * Putting fix under Macro. * Removing the fabric macro in Fabric code itself. --------- Co-authored-by: Nitin Chaudhary <[email protected]>
1 parent 0be4188 commit c2a26e9

File tree

4 files changed

+97
-9
lines changed

4 files changed

+97
-9
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Removing Platform brush instead using proper caret brush",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
#include <AutoDraw.h>
99
#include <Fabric/Composition/CompositionDynamicAutomationProvider.h>
1010
#include <Fabric/Composition/UiaHelpers.h>
11+
#include <Utils/ThemeUtils.h>
1112
#include <Utils/ValueUtils.h>
1213
#include <react/renderer/components/textinput/TextInputState.h>
14+
#include <react/renderer/graphics/HostPlatformColor.h>
1315
#include <react/renderer/textlayoutmanager/WindowsTextLayoutManager.h>
1416
#include <tom.h>
1517
#include <unicode.h>
@@ -340,8 +342,9 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
340342
int r = GetRValue(selectionColor);
341343
int g = GetGValue(selectionColor);
342344
int b = GetBValue(selectionColor);
343-
int brightness = (r * 299 + g * 587 + b * 114) / 1000;
344-
return brightness > 125 ? RGB(0, 0, 0) : RGB(255, 255, 255);
345+
int brightness = ::Microsoft::ReactNative::CalculateColorBrightness(r, g, b);
346+
return brightness > ::Microsoft::ReactNative::kCaretSelectionBrightnessThreshold ? RGB(0, 0, 0)
347+
: RGB(255, 255, 255);
345348
}
346349
break;
347350

@@ -1077,13 +1080,9 @@ std::string WindowsTextInputComponentView::DefaultHelpText() const noexcept {
10771080
void WindowsTextInputComponentView::updateCursorColor(
10781081
const facebook::react::SharedColor &cursorColor,
10791082
const facebook::react::SharedColor &foregroundColor) noexcept {
1080-
if (cursorColor) {
1081-
m_caretVisual.Brush(theme()->Brush(*cursorColor));
1082-
} else if (foregroundColor) {
1083-
m_caretVisual.Brush(theme()->Brush(*foregroundColor));
1084-
} else {
1085-
m_caretVisual.Brush(theme()->PlatformBrush("TextControlForeground"));
1086-
}
1083+
const auto &props = windowsTextInputProps();
1084+
auto caretColor = ::Microsoft::ReactNative::GetCaretColor(cursorColor, foregroundColor, props.backgroundColor);
1085+
m_caretVisual.Brush(theme()->Brush(*caretColor));
10871086
}
10881087

10891088
void WindowsTextInputComponentView::updateProps(
@@ -1595,6 +1594,8 @@ void WindowsTextInputComponentView::ensureDrawingSurface() noexcept {
15951594

15961595
void WindowsTextInputComponentView::ShowCaret(bool show) noexcept {
15971596
ensureVisual();
1597+
const auto &props = windowsTextInputProps();
1598+
updateCursorColor(props.cursorColor, props.textAttributes.foregroundColor);
15981599
m_caretVisual.IsVisible(show);
15991600
}
16001601

vnext/Microsoft.ReactNative/Utils/ThemeUtils.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
#include "pch.h"
55
#include <Utils/ThemeUtils.h>
66

7+
#ifdef USE_FABRIC
8+
#include <react/renderer/graphics/Color.h>
9+
#include <react/renderer/graphics/HostPlatformColor.h>
10+
#endif
711
#include <winuser.h>
812

913
namespace Microsoft::ReactNative {
@@ -30,4 +34,49 @@ bool IsInHighContrastWin32() noexcept {
3034
return false;
3135
}
3236

37+
int CalculateColorBrightness(const winrt::Windows::UI::Color &color) noexcept {
38+
return CalculateColorBrightness(color.R, color.G, color.B);
39+
}
40+
41+
int CalculateColorBrightness(int r, int g, int b) noexcept {
42+
return (r * kColorBrightnessRedWeight + g * kColorBrightnessGreenWeight + b * kColorBrightnessBlueWeight) /
43+
kColorBrightnessDivisor;
44+
}
45+
46+
#ifdef USE_FABRIC
47+
bool isColorMeaningful(const facebook::react::SharedColor &color) noexcept {
48+
return facebook::react::isColorMeaningful(color);
49+
}
50+
51+
facebook::react::SharedColor GetCaretColor(
52+
const facebook::react::SharedColor &cursorColor,
53+
const facebook::react::SharedColor &foregroundColor,
54+
const facebook::react::SharedColor &backgroundColor) noexcept {
55+
const auto defaultCaretColor =
56+
facebook::react::hostPlatformColorFromRGBA(0, 0, 0, 0xFF); // Default caret color is black
57+
58+
if (cursorColor) {
59+
return cursorColor;
60+
} else if (foregroundColor) {
61+
// Extra Caution if Background color is present
62+
auto fgWindows = (*foregroundColor).AsWindowsColor();
63+
int fgBrightness = CalculateColorBrightness(fgWindows);
64+
65+
// If foreground is very light and background is also very light, force black caret.
66+
if (fgBrightness > kCaretLightForegroundThreshold && Microsoft::ReactNative::isColorMeaningful(backgroundColor)) {
67+
auto bgWindows = (*backgroundColor).AsWindowsColor();
68+
int bgBrightness = CalculateColorBrightness(bgWindows);
69+
if (bgBrightness > kCaretLightBackgroundThreshold) {
70+
// Use opaque black caret
71+
return defaultCaretColor;
72+
}
73+
}
74+
75+
return foregroundColor;
76+
} else {
77+
return defaultCaretColor;
78+
}
79+
}
80+
#endif
81+
3382
} // namespace Microsoft::ReactNative

vnext/Microsoft.ReactNative/Utils/ThemeUtils.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,41 @@
44
#pragma once
55

66
#include <winrt/Windows.UI.h>
7+
#ifdef USE_FABRIC
8+
#include <react/renderer/graphics/Color.h>
9+
#endif
710

811
namespace Microsoft::ReactNative {
912

13+
// Color brightness calculation constants (standard luminance formula)
14+
constexpr int kColorBrightnessRedWeight = 299;
15+
constexpr int kColorBrightnessGreenWeight = 587;
16+
constexpr int kColorBrightnessBlueWeight = 114;
17+
constexpr int kColorBrightnessDivisor = 1000;
18+
19+
// Caret color threshold constants
20+
constexpr int kCaretLightForegroundThreshold = 240;
21+
constexpr int kCaretLightBackgroundThreshold = 186;
22+
constexpr int kCaretSelectionBrightnessThreshold = 125;
23+
1024
bool IsColorLight(const winrt::Windows::UI::Color &clr) noexcept;
1125
bool IsInHighContrastWin32() noexcept;
1226

27+
// Calculate brightness using standard luminance formula
28+
int CalculateColorBrightness(const winrt::Windows::UI::Color &color) noexcept;
29+
30+
// Calculate brightness from RGB values
31+
int CalculateColorBrightness(int r, int g, int b) noexcept;
32+
33+
#ifdef USE_FABRIC
34+
// Check if a color is meaningful (handles both PAPER and Fabric architectures)
35+
bool isColorMeaningful(const facebook::react::SharedColor &color) noexcept;
36+
37+
// Determine appropriate caret color based on foreground and background colors
38+
facebook::react::SharedColor GetCaretColor(
39+
const facebook::react::SharedColor &cursorColor,
40+
const facebook::react::SharedColor &foregroundColor,
41+
const facebook::react::SharedColor &backgroundColor) noexcept;
42+
#endif
43+
1344
} // namespace Microsoft::ReactNative

0 commit comments

Comments
 (0)