The most common way today to yyyyyyyyyb to get the job is by taking the time off to do it and doing the work for You and the other person you want it done any time of of your day to get the the best of your life to get the most important things you need .
The only way I could be sure is to have the same amount as you in a couple weeks and see how much it is and how long you will need
using Microsoft.Maui.Handlers;
using UIKit;
using Foundation;
using CoreGraphics;
using Epyst.Mobile.Controls;
using NMath = System.Math;
namespace Epyst.Mobile.Platforms.iOS
{
// Custom UITextView subclass that handles paste operations
public class RichTextView : UITextView
{
public NSDictionary DefaultAttributes { get; set; }
public override void Paste(NSObject sender)
{
var pasteboard = UIPasteboard.General;
if (pasteboard.HasStrings)
{
var text = pasteboard.String;
if (!string.IsNullOrEmpty(text))
{
// Create attributed string with default formatting
var attributedString = new NSAttributedString(text, DefaultAttributes ?? TypingAttributes);
// Get current selection
var selectedRange = SelectedRange;
// Create mutable copy of current attributed text
var mutableAttributedText = new NSMutableAttributedString(AttributedText);
// Replace selection with formatted text
mutableAttributedText.Replace(selectedRange, attributedString);
// Apply dark mode text color to ensure visibility
ApplyDarkModeTextColor(mutableAttributedText, selectedRange.Location, attributedString.Length);
// Update the text view
AttributedText = mutableAttributedText;
// Set cursor position after pasted text
SelectedRange = new NSRange(selectedRange.Location + attributedString.Length, 0);
// The AttributedText setter will trigger the Changed event automatically
return;
}
}
// Fall back to default paste for non-text content
base.Paste(sender);
}
private void ApplyDarkModeTextColor(NSMutableAttributedString attributedString, nint startLocation, nint length)
{
var range = new NSRange(startLocation, length);
// Ensure text color adapts to dark/light mode
attributedString.EnumerateAttribute(
UIStringAttributeKey.ForegroundColor,
range,
NSAttributedStringEnumeration.None,
(NSObject value, NSRange effectiveRange, ref bool stop) =>
{
// If no color is set or if it's black in dark mode, apply label color
if (value == null || (value is UIColor color && IsBlackInDarkMode(color)))
{
attributedString.AddAttribute(UIStringAttributeKey.ForegroundColor, UIColor.Label, effectiveRange);
}
});
}
public bool IsBlackInDarkMode(UIColor color)
{
// Check if we're in dark mode
if (TraitCollection.UserInterfaceStyle == UIUserInterfaceStyle.Dark)
{
// Get color components
nfloat red, green, blue, alpha;
color.GetRGBA(out red, out green, out blue, out alpha);
// Check if color is black or very dark
return red < 0.1 && green < 0.1 && blue < 0.1;
}
return false;
}
}
public class RichTextEditorHandler : ViewHandler<IRichTextEditor, UITextView>
{
public static IPropertyMapper<IRichTextEditor, RichTextEditorHandler> Mapper = new PropertyMapper<IRichTextEditor, RichTextEditorHandler>(ViewMapper)
{
[nameof(IRichTextEditor.HtmlContent)] = MapHtmlContent,
[nameof(IRichTextEditor.IsReadOnly)] = MapIsReadOnly,
[nameof(IRichTextEditor.MaxLength)] = MapMaxLength,
[nameof(IView.Background)] = MapBackground,
};
public static CommandMapper<IRichTextEditor, RichTextEditorHandler> CommandMapper = new(ViewCommandMapper)
{
[nameof(IRichTextEditor.ToggleBold)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.ToggleBold)),
[nameof(IRichTextEditor.ToggleItalic)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.ToggleItalic)),
[nameof(IRichTextEditor.ToggleUnderline)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.ToggleUnderline)),
[nameof(IRichTextEditor.ToggleStrikethrough)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.ToggleStrikethrough)),
[nameof(IRichTextEditor.AlignLeft)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.AlignLeft)),
[nameof(IRichTextEditor.AlignCenter)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.AlignCenter)),
[nameof(IRichTextEditor.AlignRight)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.AlignRight)),
[nameof(IRichTextEditor.AlignJustify)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.AlignJustify)),
[nameof(IRichTextEditor.ToggleBulletList)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.ToggleBulletList)),
[nameof(IRichTextEditor.ToggleNumberedList)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.ToggleNumberedList)),
[nameof(IRichTextEditor.InsertLink)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.InsertLink), args),
[nameof(IRichTextEditor.InsertImage)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.InsertImage), args),
[nameof(IRichTextEditor.ClearFormatting)] = (h, v, a) => h.ExecuteCommand(nameof(IRichTextEditor.ClearFormatting)),
[nameof(IRichTextEditor.InsertTable)] = (handler, view, args) => handler.ExecuteCommand(nameof(IRichTextEditor.InsertTable), args),
};
public RichTextEditorHandler() : base(Mapper, CommandMapper)
{
}
public RichTextEditorHandler(IPropertyMapper mapper, CommandMapper commandMapper) : base(mapper, commandMapper ?? CommandMapper)
{
}
private RichTextToolbar _toolbar;
private NSAttributedString _lastAttributedString;
private bool _isUpdatingContent;
private bool _isUserEditing = false; // Changed to false by default
private System.Timers.Timer _htmlGenerationTimer;
private readonly object _timerLock = new object();
private string _pendingHtmlContent = null;
protected override UITextView CreatePlatformView()
{
var textView = new RichTextView
{
BackgroundColor = UIColor.Clear,
Font = UIFont.SystemFontOfSize(16),
TextColor = UIColor.Label, // Automatically adapts to dark/light mode
TextContainerInset = new UIEdgeInsets(10, 10, 10, 10),
ScrollEnabled = false, // Disable scrolling since we expand to content
AllowsEditingTextAttributes = true,
DataDetectorTypes = UIDataDetectorType.None,
AutocorrectionType = UITextAutocorrectionType.Yes,
AutocapitalizationType = UITextAutocapitalizationType.Sentences,
SpellCheckingType = UITextSpellCheckingType.Yes
};
// Configure text container to prevent unwanted line breaks
textView.TextContainer.LineFragmentPadding = 0;
textView.TextContainer.WidthTracksTextView = true;
// Set default typing attributes
var paragraphStyle = new NSMutableParagraphStyle();
paragraphStyle.LineSpacing = 2;
paragraphStyle.ParagraphSpacing = 8;
var typingAttributes = new NSMutableDictionary();
typingAttributes[UIStringAttributeKey.Font] = UIFont.SystemFontOfSize(16);
typingAttributes[UIStringAttributeKey.ParagraphStyle] = paragraphStyle;
typingAttributes[UIStringAttributeKey.ForegroundColor] = UIColor.Label; // Adapts to dark/light mode
textView.TypingAttributes = typingAttributes;
textView.DefaultAttributes = typingAttributes;
textView.Started += OnEditingStarted;
textView.Changed += OnTextChanged;
textView.SelectionChanged += OnSelectionChanged;
textView.Ended += OnEditingEnded;
return textView;
}
protected override void ConnectHandler(UITextView platformView)
{
base.ConnectHandler(platformView);
// Only create toolbar for editable editors
if (VirtualView != null && !VirtualView.IsReadOnly)
{
// Create and setup the toolbar
_toolbar = new RichTextToolbar(this);
platformView.InputAccessoryView = _toolbar;
// Initialize the HTML generation timer
_htmlGenerationTimer = new System.Timers.Timer(500); // 500ms debounce
_htmlGenerationTimer.Elapsed += OnHtmlGenerationTimerElapsed;
_htmlGenerationTimer.AutoReset = false;
}
UpdateContent();
UpdateIsReadOnly();
UpdateBackground();
// Process any pending content
if (!string.IsNullOrEmpty(_pendingHtmlContent) && VirtualView != null)
{