রিচ টেক্সট ইনপুট ব্যবহারকারীদের টাইপ করা টেক্সট ফর্ম্যাট করতে দেয়, যার মধ্যে রয়েছে কোনও শব্দ বোল্ড করা,
শিরোনাম যোগ করা, অনুচ্ছেদ অ্যালাইন করা বা হাইলাইট করা @mention যোগ করা।
Compose-এ, আপনি এটি স্টেট-ভিত্তিক টেক্সট ফিল্ডের
(BasicTextField এবং TextField) উপরে তৈরি করেন। স্টাইল টেক্সটের সাথে
TextFieldState-এ থাকে, তাই এডিট, বেছে নেওয়া
পরিবর্তন এবং রিকম্পোজিশন করার পরেও তা থেকে যায় এবং ব্যবহারকারী টাইপ, মুছে ফেলা এবং
পেস্ট করার সময় সিঙ্ক করা থাকে।
এই পৃষ্ঠায় দেখানো হয়েছে যে কীভাবে TextFieldBuffer ব্যবহার করে TextFieldState-এ স্টাইল প্রয়োগ, আপডেট, কোয়েরি, এক্সট্র্যাক্ট ও সরানো যায় এবং কীভাবে TextFieldState.textStyles থেকে ফর্ম্যাটিং টুলবার চালানো যায়।
ইনলাইন ও প্যারাগ্রাফ স্টাইল প্রয়োগ করা
আপনি যেকোনও TextFieldBuffer রিসিভার স্কোপের মধ্যে অক্ষর-লেভেল এবং অনুচ্ছেদ-লেভেল ফর্ম্যাটিং প্রয়োগ করতে পারবেন, যার মধ্যে TextFieldState.edit,
InputTransformation.transformInput এবং
OutputTransformation.transformOutput অন্তর্ভুক্ত।
ExpandPolicy-এর মাধ্যমে বাউন্ডারি এক্সপ্যানশন নিয়ন্ত্রণ করুন
আপনি addStyle কল করার সময়, ExpandPolicy পাস করতে পারেন যাতে ব্যবহারকারী সরাসরি তার start বা end সীমানায় নতুন অক্ষর যোগ করলে,
স্টাইল করা রেঞ্জ কীভাবে কাজ করবে তা নির্ধারণ করা যায়:
(start, end)-এর মধ্যে ইনসার্ট করা: স্টাইল করা রেঞ্জ সবসময় বড় করে।start-এর আগে যোগ করা: সবসময়startওendঅফসেট দুটিকেই ডানদিকে সরিয়ে দেয়।- সরাসরি
startবাend-এ ইনসার্ট করা: আপনার উল্লেখ করাExpandPolicy-এর দ্বারা নিয়ন্ত্রিত হয়।
নিম্নলিখিত সারণীতে উপলভ্য ExpandPolicy বিকল্প এবং সেগুলির
সাধারণ ব্যবহারের উদাহরণ বর্ণনা করা হয়েছে:
| নীতি | সীমা সংক্রান্ত আচরণ | সাজেস্ট করা ব্যবহারিক ক্ষেত্র | উদাহরণ |
|---|---|---|---|
ExpandPolicy.InsideOnly |
start বা end-এ টাইপ করা টেক্সট স্টাইল করা রেঞ্জের বাইরে থাকে। |
@mentions, #hashtags-এর মতো ডিসক্রিট টোকেন বা ব্যাকগ্রাউন্ড পিল যেখানে ট্যাগের পাশে টাইপ করলে ট্যাগের ব্যাকগ্রাউন্ড কালার ইনহেরিট করা উচিত নয়। |
![]() |
ExpandPolicy.AtStart |
start-এ টাইপ করা টেক্সট স্টাইল ইনহেরিট করে এবং end-এ টাইপ করা টেক্সট স্টাইল করা থাকে না। |
স্টাইল করা টেক্সটের ঠিক সামনে টাইপ করা টেক্সটেও একই ফর্ম্যাটিং প্রয়োগ করা উচিত। | ![]() |
ExpandPolicy.AtEnd |
end-এ টাইপ করা টেক্সট স্টাইল ইনহেরিট করে এবং start-এ টাইপ করা টেক্সট স্টাইল করা থাকে না। |
স্ট্যান্ডার্ড রিচ টেক্সট এডিটর টাইপিং, যেখানে বোল্ড বা ইটালিক শব্দের পরে টাইপ করা চালিয়ে গেলে সেই ফর্ম্যাটিং বজায় থাকে। | ![]() |
ExpandPolicy.AtBoth |
start বা end-এ টাইপ করা টেক্সট রেঞ্জকে প্রসারিত করে এবং স্টাইলটি উত্তরাধিকার সূত্রে পায়। |
অ্যাক্টিভ হাইলাইট বা একটানা ফর্ম্যাট করা ব্লক যা যেকোনও প্রান্ত থেকে বাড়তে পারে। | ![]() |
SpanStyle-এর সাহায্যে অক্ষর ফর্ম্যাটিং প্রয়োগ করুন
ক্যারেক্টার-লেভেল ফর্ম্যাটিং প্রয়োগ করতে SpanStyle-এর সাথে addStyle ব্যবহার করুন,
যেমন টেক্সটের রঙ, ব্যাকগ্রাউন্ড হাইলাইট, FontWeight, FontStyle,
অথবা বাফারে নির্দিষ্ট TextRange-এর জন্য আন্ডারলাইন।
SpanStyle রেঞ্জের জন্য মূল আচরণ
- লেয়ারিং অর্ডার: আপনি যে ক্রমে বাফারে স্টাইল যোগ করেন সেই ক্রমেই স্টাইল প্রয়োগ করা হয়। একাধিক
ওভারল্যাপ করা
SpanStyleরেঞ্জ যদি একই প্রপার্টিতে আলাদা আলাদা ভ্যালু সেট করে, তাহলে পরে যোগ করা স্টাইলটি উপরে দেখায়। - খালি নয় এমন রেঞ্জের প্রয়োজনীয়তা: আপনাকে অবশ্যই এমন একটি নন-কোল্যাপ্সড রেঞ্জ পাস করতে হবে যেখানে
start<end.0দৈর্ঘ্যের একটি সঙ্কুচিত নির্বাচনেaddStyleকল করার কোনও প্রভাব নেই এবং বিপরীত বা সীমার বাইরের সূচক পাস করলে একটিIllegalArgumentExceptionথ্রো করে।
TextField( state = rememberTextFieldState(), inputTransformation = InputTransformation { for (i in 0 until changes.changeCount) { val range = changes.getRange(i) if (!range.collapsed) { addStyle( spanStyle = SpanStyle( fontWeight = FontWeight.ExtraBold, fontStyle = FontStyle.Italic, color = Color.Red, ), range = range, expandPolicy = ExpandPolicy.AtEnd ) } } } )
ParagraphStyle-এর মাধ্যমে ব্লক ফর্ম্যাটিং প্রয়োগ করুন
প্যারাগ্রাফ রেঞ্জে textAlign, lineHeight,
textDirection বা textIndent-এর মতো ব্লক-লেভেল
স্ট্রাকচারাল ফর্ম্যাটিং প্রয়োগ করতে ParagraphStyle-এর সাথে addStyle ব্যবহার করুন।
fun setParagraphAlignment(state: TextFieldState, alignment: TextAlign) { val targetRange = if (state.selection.collapsed) { TextRange(0, state.text.length) } else { state.selection } if (targetRange.collapsed) return state.edit { // 1. Remove existing paragraph styles in this range getParagraphStyles(targetRange).forEach { removeStyle(it) } // 2. Apply the new paragraph alignment addStyle( paragraphStyle = ParagraphStyle(textAlign = alignment), range = targetRange, expandPolicy = ExpandPolicy.InsideOnly ) } }
ParagraphStyle ব্যবহার করে শুরু, মধ্য ও শেষের মধ্যে প্যারাগ্রাফ অ্যালাইনমেন্ট টগল করা।ParagraphStyle-এর সাথে যেসব সমস্যা এড়িয়ে চলবেন
যেহেতু ParagraphStyle টেক্সটের সম্পূর্ণ ব্লকে প্রযোজ্য, তাই নিম্নলিখিত পরিস্থিতিতে এটি ব্যবহার করা
এড়িয়ে চলুন:
- সংলগ্ন অনুচ্ছেদে প্রসারিত করা:
ExpandPolicyসাবধানে সেট করুন যাতে ব্যবহারকারীর এডিট করার পরে সংলগ্নParagraphStyleরেঞ্জ ওভারল্যাপ না করে। যেমন, যদি[0, 5),ExpandPolicy.AtEndব্যবহার করে এবং[5, 10),ExpandPolicy.AtStartব্যবহার করে, তাহলে5ইন্ডেক্সে টেক্সট যোগ করলে দুটি স্টাইলই ওভারল্যাপে প্রসারিত হয় এবংIllegalArgumentExceptionদেখায়। ParagraphStyleরেঞ্জ নেস্ট করা: সম্পূর্ণভাবে নেস্ট করা রেঞ্জ (যেমন[0, 10)এবং[2, 8))AnnotatedString-এ বৈধ হলেও, টেক্সট ফিল্ডেParagraphStyleরেঞ্জ নেস্ট করা এড়িয়ে চলুন। লেআউট নেস্ট করা রেঞ্জকে আলাদা আলাদা অনুচ্ছেদে ভাগ করে এবং পরবর্তী এডিটগুলি অসাবধানতাবশত নেস্ট করা রেঞ্জকে আংশিক ওভারল্যাপে পরিণত করতে পারে।
TrackedRange-এর মাধ্যমে কোনও জায়গায় স্টাইল আপডেট করুন
TextRange এবং ExpandPolicy সহ addStyle কল করলে একটি
TrackedRange হ্যান্ডেল রিটার্ন করে, যা TrackedRange<SpanStyle> বা
TrackedRange<ParagraphStyle> হতে পারে। TrackedRange হল TextFieldBuffer-এ স্টাইল ইন্টারভ্যালের একটি ডায়নামিক রেফারেন্স যা টেক্সট যোগ, মুছে ফেলা বা পরিবর্তন করা হলে, অটোমেটিক এর start এবং end ক্যারেক্টার অফসেট আপডেট করে।
ইন-প্লেস পরিবর্তনযোগ্যতা
আগে থেকে থাকা স্টাইল আপডেট করতে বা এর সীমানা অ্যাডজাস্ট করতে আপনাকে removeStyle এবং addStyle-এ কল করতে হবে না। TextFieldBuffer স্কোপের মধ্যে, TrackedRange
এক্সটেনশন প্রপার্টি প্রদান করে যা অন্যান্য স্টাইলের সাপেক্ষে
এর আসল লেয়ারিং অর্ডার বজায় রেখে ইন্টারভ্যাল আপডেট করে:
TrackedRange<SpanStyle>.spanStyle:TrackedRange<SpanStyle>-এSpanStyleপে-লোড পড়ে বা পরিবর্তন করে।TrackedRange<ParagraphStyle>.paragraphStyle:TrackedRange<ParagraphStyle>-এParagraphStyleপেলোড পড়ে বা পরিবর্তন করে।TrackedRange<*>.textRange: বর্তমানTextRangeবাউন্ডারি পড়ে বা আপডেট করে। সংকুচিত, বিপরীত বা সীমার বাইরের রেঞ্জ সেট করলেIllegalArgumentExceptionদেখায়।TrackedRange<*>.expandPolicy:ExpandPolicy-এ পড়ে বা আপডেট করে।TrackedRange<*>.isValid: স্টাইল ইন্টারভাল এখনও বাফারে অ্যাক্টিভ থাকলেtrueরিটার্ন করে, অথবা এডিট করার কারণে এর দৈর্ঘ্য কমে0হয়ে গেলে বা এটি সরিয়ে দেওয়া হলেfalseরিটার্ন করে।
// Trim an existing bold span in place when the selection overlaps one edge val range = style.textRange if (range.start < selection.min && range.end <= selection.max) { // Selection covers the end of the span - shrink end boundary in place style.textRange = TextRange(range.start, selection.min) } else if (range.start >= selection.min && range.end > selection.max) { // Selection covers the start of the span - shrink start boundary in place style.textRange = TextRange(selection.max, range.end) }
স্কোপ কনফাইনমেন্ট ও লাইফসাইকেল সংক্রান্ত নিয়ম
TrackedRange ইনস্ট্যান্স নিয়ে কাজ করার সময় নিম্নলিখিত স্কোপ ও লাইফসাইকেল সংক্রান্ত নিয়ম মনে রাখবেন:
TextFieldBuffer-এ সীমাবদ্ধ:TrackedRangeএক্সটেনশন প্রপার্টি শুধুমাত্রTextFieldBufferস্কোপের মধ্যে থাকে যেখানে হ্যান্ডেল তৈরি বা কোয়েরি করা হয়েছে।- কলাপ্স করার সময় অটোমেটিক প্রুনিং: ব্যবহারকারী যদি
TrackedRange-এর মাধ্যমে কভার করা সব অক্ষর মুছে দেওয়ার জন্য এডিট করেন, তাহলে এর দৈর্ঘ্য কমে0হয়ে যায়,isValidfalseরিটার্ন করে এবং বাফার থেকে স্টাইল সরিয়ে দেওয়া হয়।
কোয়েরি ও স্টাইল সরানো
আপনি বাফার মিউটেট করছেন নাকি আপনার UI-এর জন্য স্টেট রিড করছেন তার উপর নির্ভর করে, অ্যাক্টিভ স্টাইল পরীক্ষা করার জন্য Compose দুটি উপায় প্রদান করে:
TextFieldBufferব্লকের মধ্যে: মিউটেবলTrackedRangeহ্যান্ডেল পেতে সরাসরিTextFieldBufferরিসিভারেgetSpanStylesএবংgetParagraphStylesকল করুন।- এডিট ব্লকের বাইরে:
TextFieldState.textStylesপড়ুন, যা কমিট করা স্ট্যাটাস দেখায় এমন অপরিবর্তনীয়AnnotatedString.Rangeতালিকা ফেরত পাঠায়।
TextFieldBuffer-এ ইন্টারসেক্টিং স্টাইল কোয়েরি করুন
TextFieldBuffer.getSpanStyles এবং
TextFieldBuffer.getParagraphStyles প্রদত্ত TextRange-এর সাথে ইন্টারসেক্ট করে এমন সব TrackedRange ইনস্ট্যান্স
ফেরত দেয়, যেগুলি বাফারে যোগ করার সময় অনুযায়ী
সাজানো থাকে।
যেমন, ব্যবহারকারীর বর্তমান সিলেকশন কভার করে এমন স্টাইল পরীক্ষা বা আপডেট করতে, আপনি state.edit-এর মধ্যে getSpanStyles(selection) কোয়েরি করতে পারেন:
state.edit { if (selection.collapsed) return@edit // Query all bold spans that overlap with the current selection val boldStyles: List<TrackedRange<SpanStyle>> = getSpanStyles(selection).filter { it.spanStyle.fontWeight == FontWeight.Bold } // ... }
স্টাইল ইন্টারভ্যাল সেমি-ওপেন গাণিতিক রেঞ্জ [start, end) ব্যবহার করে। টাচিং স্টাইল
ফেরত দেওয়া হবে কিনা তা নির্ভর করে আপনি কোনও সিলেকশন বা একটি কার্সর পয়েন্ট কোয়েরি
করেছেন কিনা তার উপর:
- কোনও বিকল্প বেছে নিলে
[start, end):- ফেরত দেওয়া হয়েছে: বেছে নেওয়ার রেঞ্জের মধ্যে ওভারল্যাপ করে, থাকে বা বসে এমন যেকোনও স্টাইল। যেমন, কোয়েরি
[5, 15),[0, 20),[8, 12)বা[0, 10)-এর সাথে ম্যাচ করে। - রিটার্ন করা হয়নি: এমন স্টাইল যা ওভারল্যাপ করা অক্ষর ছাড়াই শুধু বাইরের সীমানার প্রান্তকে স্পর্শ করে। যেমন, কোয়েরি
[5, 15)-এ[0, 5)এবং[15, 25)বাদ দেওয়া হয়েছে।
- ফেরত দেওয়া হয়েছে: বেছে নেওয়ার রেঞ্জের মধ্যে ওভারল্যাপ করে, থাকে বা বসে এমন যেকোনও স্টাইল। যেমন, কোয়েরি
- গুটিয়ে নেওয়া কার্সরের জন্য
[index, index]:- ফেরত এসেছে: কার্সর আছে বা কার্সরের পজিশন থেকে শুরু হয় এমন যেকোনও
স্টাইল। যেমন,
10-এ থাকা কার্সর[0, 20)ও[10, 20)-এর সাথে ম্যাচ করে। - ফেরত দেওয়া হয়নি (শেষের নিয়ম স্পর্শ করা): কার্সারের পজিশনে শেষ হওয়া
যেকোনও স্টাইল। যেমন,
10-এ কার্সর থাকলে[0, 10)বাদ যায়।
- ফেরত এসেছে: কার্সর আছে বা কার্সরের পজিশন থেকে শুরু হয় এমন যেকোনও
স্টাইল। যেমন,
removeStyle-এর সাহায্যে স্টাইল সরান বা টগল করুন
TextFieldBuffer থেকে নির্দিষ্ট স্টাইল ইন্টারভাল মুছে দিতে removeStyle-এ TrackedRange হ্যান্ডেল পাস করুন।
TrackedRange খুঁজে পাওয়া ও সরানো গেলে removeStyle, true রিটার্ন করে, অথবা
এটি আগে থেকেই সরানো হয়ে থাকলে বা অন্য বাফারের অন্তর্গত হলে false রিটার্ন করে। এটি শুধুমাত্র
টার্গেট করা TrackedRange হ্যান্ডেল সরিয়ে দেয়, অন্যান্য ওভারল্যাপ করা স্টাইল
অপরিবর্তিত রাখে।
fun toggleBold(state: TextFieldState) { state.edit { if (selection.collapsed) return@edit // Query all bold spans that overlap with the current selection val boldStyles: List<TrackedRange<SpanStyle>> = getSpanStyles(selection).filter { it.spanStyle.fontWeight == FontWeight.Bold } if (boldStyles.isNotEmpty()) { // Remove or trim existing bold spans covering the selection for (style in boldStyles) { val range = style.textRange if (range.start >= selection.min && range.end <= selection.max) { removeStyle(style) } else if (range.start < selection.min && range.end > selection.max) { val oldEnd = range.end style.textRange = TextRange(range.start, selection.min) addStyle( spanStyle = SpanStyle(fontWeight = FontWeight.Bold), range = TextRange(selection.max, oldEnd), expandPolicy = ExpandPolicy.AtEnd ) } else if (range.start < selection.min) { style.textRange = TextRange(range.start, selection.min) } else { style.textRange = TextRange(selection.max, range.end) } } } else { // No bold spans intersect the selection - apply bold addStyle( spanStyle = SpanStyle(fontWeight = FontWeight.Bold), range = selection, expandPolicy = ExpandPolicy.AtEnd ) } } }
রিচ টেক্সট এক্সট্র্যাক্ট বা সিরিয়ালাইজ করুন
শুধু-পড়া-যায় এমন Text কম্পোজেবলে ফর্ম্যাট করা ইনপুট দেখাতে অথবা ব্যাকএন্ড পরিষেবায় এটি সিরিয়ালাইজ করতে,
এডিট ব্লকের বাইরে TextFieldState.textStyles পড়ুন।
state.textStyles.getSpanStyles এবং
state.textStyles.getParagraphStyles,
List<AnnotatedString.Range<SpanStyle>> এবং
List<AnnotatedString.Range<ParagraphStyle>> রিটার্ন করে, যা আপনি সরাসরি
AnnotatedString কনস্ট্রাক্টরে পাস করতে পারবেন:
fun TextFieldState.toAnnotatedString(): AnnotatedString { val fullRange = TextRange(0, text.length) return AnnotatedString( text = text.toString(), spanStyles = textStyles.getSpanStyles(fullRange), paragraphStyles = textStyles.getParagraphStyles(fullRange) ) }
Jetchat-এ সম্পূর্ণ উদাহরণ
ইনলাইন ক্যারেক্টার স্টাইলিং (বোল্ড, ইটালিক, আন্ডারলাইন, স্ট্রাইকথ্রু ও রঙ) একত্রিত করে এমন একটি সম্পূর্ণ প্রয়োগ দেখতে, GitHub-এ Jetchat স্যাম্পেল দেখুন:
RichTextState.kt:TextFieldStateস্টাইল কোয়েরি, স্প্যান টগল করা, ট্রিম করা বা বেছে নেওয়া সীমার মধ্যে স্প্লিট করা এবং আরও অনেক কিছু এনক্যাপসুলেট করে।UserInput.kt:RichTextState-কে রিচ টেক্সট ফর্ম্যাটিং টুলবার ও মেসেজ ইনপুট ফিল্ডের সাথে কানেক্ট করে।



