@nativescript-community/ui-label 1.2.0 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,41 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.2.4](https://github.com/nativescript-community/ui-label/compare/v1.2.3...v1.2.4) (2022-01-14)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * uglify fix ([f04189f](https://github.com/nativescript-community/ui-label/commit/f04189f9838615d4eb037a6423fd245538860358))
12
+
13
+
14
+
15
+
16
+
17
+ ## [1.2.3](https://github.com/nativescript-community/ui-label/compare/v1.2.2...v1.2.3) (2022-01-13)
18
+
19
+ **Note:** Version bump only for package @nativescript-community/ui-label
20
+
21
+
22
+
23
+
24
+
25
+ ## [1.2.2](https://github.com/nativescript-community/ui-label/compare/v1.2.1...v1.2.2) (2022-01-08)
26
+
27
+ **Note:** Version bump only for package @nativescript-community/ui-label
28
+
29
+
30
+
31
+
32
+
33
+ ## [1.2.1](https://github.com/nativescript-community/ui-label/compare/v1.2.0...v1.2.1) (2022-01-02)
34
+
35
+ **Note:** Version bump only for package @nativescript-community/ui-label
36
+
37
+
38
+
39
+
40
+
6
41
  # [1.2.0](https://github.com/nativescript-community/ui-label/compare/v1.1.25...v1.2.0) (2021-10-20)
7
42
 
8
43
 
package/label-common.js CHANGED
@@ -9,15 +9,6 @@ import { layout } from '@nativescript/core/utils/utils';
9
9
  // }
10
10
  // init text to ensure font overrides are called
11
11
  init();
12
- // const CHILD_SPAN = 'Span';
13
- // const CHILD_FORMATTED_TEXT = 'formattedText';
14
- // const CHILD_FORMATTED_STRING = 'FormattedString';
15
- // FormattedString.prototype.addPropertyChangeHandler = function (span: Span) {
16
- // span.on(Observable.propertyChangeEvent, this.onPropertyChange, this);
17
- // };
18
- // FormattedString.prototype.removePropertyChangeHandler = function (span: Span) {
19
- // span.off(Observable.propertyChangeEvent, this.onPropertyChange, this);
20
- // };
21
12
  export const needFormattedStringComputation = function (target, propertyKey, descriptor) {
22
13
  const originalMethod = descriptor.value;
23
14
  descriptor.value = function (...args) {
@@ -42,7 +42,7 @@ declare abstract class LabelBase extends View implements LabelViewDefinition {
42
42
  linkUnderline: boolean;
43
43
  html: string;
44
44
  selectable: boolean;
45
- _isSingleLine: boolean;
45
+ mIsSingleLine: boolean;
46
46
  text: string;
47
47
  formattedText: FormattedString;
48
48
  get nativeTextViewProtected(): any;
@@ -76,10 +76,13 @@ declare abstract class LabelBase extends View implements LabelViewDefinition {
76
76
  }
77
77
  export declare class Label extends LabelBase {
78
78
  nativeViewProtected: com.nativescript.label.EllipsizingTextView;
79
- handleFontSize: boolean;
79
+ mHandleFontSize: boolean;
80
+ private mAutoFontSize;
80
81
  private _defaultMovementMethod;
81
82
  get nativeTextViewProtected(): com.nativescript.label.EllipsizingTextView;
82
83
  createNativeView(): com.nativescript.label.EllipsizingTextView;
84
+ private enableAutoSize;
85
+ private disableAutoSize;
83
86
  createFormattedTextNative(value: any): any;
84
87
  createHTMLString(): globalAndroid.text.SpannableStringBuilder;
85
88
  _tappable: boolean;
package/label.android.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { LightFormattedString, createNativeAttributedString, cssProperty, overrideSpanAndFormattedStringEnabled, verticalTextAlignmentProperty } from '@nativescript-community/text';
2
- import { CSSType, FormattedString, Observable, Property, Span, View, booleanConverter, profile } from '@nativescript/core';
2
+ import { CSSType, Device, FormattedString, Observable, Property, Span, View, booleanConverter, profile } from '@nativescript/core';
3
3
  import { Color } from '@nativescript/core/color';
4
4
  import { Font } from '@nativescript/core/ui/styling/font';
5
5
  import { Length, colorProperty, fontInternalProperty, fontSizeProperty, paddingBottomProperty, paddingLeftProperty, paddingRightProperty, paddingTopProperty } from '@nativescript/core/ui/styling/style-properties';
@@ -7,12 +7,12 @@ import { letterSpacingProperty, textAlignmentProperty, textDecorationProperty, t
7
7
  import { lineHeightProperty } from '@nativescript/core/ui/text-base/text-base-common';
8
8
  import { layout } from '@nativescript/core/utils/utils';
9
9
  import { autoFontSizeProperty, lineBreakProperty, maxLinesProperty, selectableProperty, textShadowProperty } from './label-common';
10
+ import lazy from '@nativescript/core/utils/lazy';
10
11
  export { createNativeAttributedString, enableIOSDTCoreText } from '@nativescript-community/text';
11
12
  export * from './label-common';
13
+ const sdkVersion = lazy(() => parseInt(Device.sdkVersion, 10));
12
14
  let TextView;
13
- const CHILD_SPAN = 'Span';
14
15
  const CHILD_FORMATTED_TEXT = 'formattedText';
15
- const CHILD_FORMATTED_STRING = 'FormattedString';
16
16
  const resetSymbol = Symbol('textPropertyDefault');
17
17
  var SuspendType;
18
18
  (function (SuspendType) {
@@ -157,7 +157,7 @@ let LabelBase = class LabelBase extends View {
157
157
  }
158
158
  }
159
159
  _addChildFromBuilder(name, value) {
160
- if (name === CHILD_SPAN) {
160
+ if (name === Span.name) {
161
161
  if (!this.formattedText) {
162
162
  let formattedText;
163
163
  if (overrideSpanAndFormattedStringEnabled) {
@@ -174,7 +174,7 @@ let LabelBase = class LabelBase extends View {
174
174
  this.formattedText.spans.push(value);
175
175
  }
176
176
  }
177
- else if (name === CHILD_FORMATTED_TEXT || name === CHILD_FORMATTED_STRING) {
177
+ else if (name === CHILD_FORMATTED_TEXT || name === FormattedString.name) {
178
178
  this.formattedText = value;
179
179
  value.parent = this;
180
180
  }
@@ -267,7 +267,8 @@ LabelBase = __decorate([
267
267
  export class Label extends LabelBase {
268
268
  constructor() {
269
269
  super(...arguments);
270
- this.handleFontSize = true;
270
+ this.mHandleFontSize = true;
271
+ this.mAutoFontSize = false;
271
272
  this._tappable = false;
272
273
  }
273
274
  get nativeTextViewProtected() {
@@ -357,7 +358,7 @@ export class Label extends LabelBase {
357
358
  }
358
359
  [textAlignmentProperty.setNative](value) {
359
360
  const view = this.nativeTextViewProtected;
360
- if (android.os.Build.VERSION.SDK_INT >= 25) {
361
+ if (android.os.Build.VERSION.SDK_INT >= 26) {
361
362
  if (value === 'justify') {
362
363
  view.setJustificationMode(android.text.Layout.JUSTIFICATION_MODE_INTER_WORD);
363
364
  }
@@ -380,18 +381,37 @@ export class Label extends LabelBase {
380
381
  }
381
382
  }
382
383
  [fontSizeProperty.setNative](value) {
384
+ // setTextSize is ignored if autoFontSize is enabled
385
+ // so we need to disable autoFontSize just to set textSize
386
+ if (this.mAutoFontSize) {
387
+ this.disableAutoSize();
388
+ }
383
389
  if (typeof value === 'number') {
384
390
  this.nativeTextViewProtected.setTextSize(value);
385
391
  }
386
392
  else {
387
393
  this.nativeTextViewProtected.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.nativeSize);
388
394
  }
395
+ if (this.mAutoFontSize) {
396
+ this.enableAutoSize();
397
+ }
389
398
  }
390
399
  [lineHeightProperty.setNative](value) {
391
- this.nativeTextViewProtected.setLineSpacing(value * layout.getDisplayDensity(), 1);
400
+ if (sdkVersion() >= 28) {
401
+ this.nativeTextViewProtected.setLineHeight(value * layout.getDisplayDensity());
402
+ }
403
+ else {
404
+ const fontHeight = this.nativeTextViewProtected.getPaint().getFontMetricsInt(null);
405
+ this.nativeTextViewProtected.setLineSpacing(value * layout.getDisplayDensity() - fontHeight, 1);
406
+ }
392
407
  }
393
408
  [fontInternalProperty.setNative](value) {
394
- this.nativeTextViewProtected.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value);
409
+ const androidFont = value instanceof Font ? value.getAndroidTypeface() : value;
410
+ this.nativeTextViewProtected.setTypeface(androidFont);
411
+ if (this.lineHeight && sdkVersion() < 28) {
412
+ const fontHeight = this.nativeTextViewProtected.getPaint().getFontMetricsInt(null);
413
+ this.nativeTextViewProtected.setLineSpacing(this.lineHeight * layout.getDisplayDensity() - fontHeight, 1);
414
+ }
395
415
  }
396
416
  [textDecorationProperty.setNative](value) {
397
417
  switch (value) {
@@ -439,12 +459,19 @@ export class Label extends LabelBase {
439
459
  [paddingLeftProperty.setNative](value) {
440
460
  org.nativescript.widgets.ViewHelper.setPaddingLeft(this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0));
441
461
  }
462
+ enableAutoSize() {
463
+ androidx.core.widget.TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this.nativeView, this.minFontSize || 10, this.maxFontSize || 200, this.autoFontSizeStep || 1, android.util.TypedValue.COMPLEX_UNIT_DIP);
464
+ }
465
+ disableAutoSize() {
466
+ androidx.core.widget.TextViewCompat.setAutoSizeTextTypeWithDefaults(this.nativeView, androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
467
+ }
442
468
  [autoFontSizeProperty.setNative](value) {
469
+ this.mAutoFontSize = value;
443
470
  if (value) {
444
- androidx.core.widget.TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this.nativeView, this.minFontSize || 10, this.maxFontSize || 200, this.autoFontSizeStep || 1, android.util.TypedValue.COMPLEX_UNIT_DIP);
471
+ this.enableAutoSize();
445
472
  }
446
473
  else {
447
- androidx.core.widget.TextViewCompat.setAutoSizeTextTypeWithDefaults(this.nativeView, androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
474
+ this.disableAutoSize();
448
475
  }
449
476
  }
450
477
  [selectableProperty.setNative](value) {
package/label.ios.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CoreTypes, FormattedString, Span } from '@nativescript/core';
1
+ import { CoreTypes, FormattedString } from '@nativescript/core';
2
2
  import { LabelBase } from './label-common';
3
3
  export { createNativeAttributedString, enableIOSDTCoreText } from '@nativescript-community/text';
4
4
  export * from './label-common';
@@ -6,8 +6,6 @@ declare module '@nativescript/core/ui/text-base' {
6
6
  interface TextBase {
7
7
  _requestLayoutOnTextChanged(): any;
8
8
  _setNativeText(): any;
9
- createMutableStringForSpan?(span: any, text: any): NSMutableAttributedString;
10
- createNSMutableAttributedString?(formattedString: FormattedString): NSMutableAttributedString;
11
9
  }
12
10
  }
13
11
  export declare function getTransformedText(text: string, textTransform: CoreTypes.TextTransformType): string;
@@ -18,7 +16,6 @@ export declare class Label extends LabelBase {
18
16
  attributedString: NSMutableAttributedString;
19
17
  private _delegate;
20
18
  static DTCORETEXT_INIT: boolean;
21
- constructor();
22
19
  createNativeView(): UITextView;
23
20
  initNativeView(): void;
24
21
  disposeNativeView(): void;
@@ -38,9 +35,8 @@ export declare class Label extends LabelBase {
38
35
  _setSpannablesFontSizeWithRatio(ratio: any): void;
39
36
  _setNativeText(): void;
40
37
  setTextDecorationAndTransform(): void;
41
- currentMaxFontSize: number;
42
- createNSMutableAttributedString(formattedString: FormattedString): NSMutableAttributedString;
43
- createMutableStringForSpan(span: Span, text: string): NSMutableAttributedString;
38
+ createFormattedTextNative(value: FormattedString): any;
39
+ setFormattedTextDecorationAndTransform(): void;
44
40
  fontSizeRatio: number;
45
41
  _lastAutoSizeKey: string;
46
42
  textViewDidChange(textView: UITextView, width?: any, height?: any, force?: boolean): void;
package/label.ios.js CHANGED
@@ -1,9 +1,9 @@
1
1
  var _a, _b, _c, _d;
2
- import { computeBaseLineOffset, createNativeAttributedString, usingIOSDTCoreText, verticalTextAlignmentProperty } from '@nativescript-community/text';
2
+ import { createNativeAttributedString, verticalTextAlignmentProperty } from '@nativescript-community/text';
3
3
  import { Color, Font, View } from '@nativescript/core';
4
4
  import { borderBottomWidthProperty, borderLeftWidthProperty, borderRightWidthProperty, borderTopWidthProperty, fontInternalProperty, paddingBottomProperty, paddingLeftProperty, paddingRightProperty, paddingTopProperty } from '@nativescript/core/ui/styling/style-properties';
5
- import { formattedTextProperty, letterSpacingProperty, textDecorationProperty, whiteSpaceProperty } from '@nativescript/core/ui/text-base';
6
- import { getClosestPropertyValue, lineHeightProperty } from '@nativescript/core/ui/text-base/text-base-common';
5
+ import { formattedTextProperty, letterSpacingProperty, whiteSpaceProperty } from '@nativescript/core/ui/text-base';
6
+ import { lineHeightProperty } from '@nativescript/core/ui/text-base/text-base-common';
7
7
  import { isNullOrUndefined, isString } from '@nativescript/core/utils/types';
8
8
  import { iOSNativeHelper, layout } from '@nativescript/core/utils/utils';
9
9
  import { LabelBase, autoFontSizeProperty, htmlProperty, lineBreakProperty, linkColorProperty, linkUnderlineProperty, maxLinesProperty, needFormattedStringComputation, selectableProperty, textShadowProperty } from './label-common';
@@ -103,14 +103,16 @@ var ObserverClass = /** @class */ (function (_super) {
103
103
  }(NSObject));
104
104
  export class Label extends LabelBase {
105
105
  constructor() {
106
- super();
107
- this.currentMaxFontSize = 0;
106
+ super(...arguments);
108
107
  this.fontSizeRatio = 1;
109
- // if (iOSUseDTCoreText && !Label.DTCORETEXT_INIT) {
110
- // Label.DTCORETEXT_INIT = true;
111
- // DTCoreTextFontDescriptor.asyncPreloadFontLookupTable();
112
- // }
113
108
  }
109
+ // constructor() {
110
+ // super();
111
+ // if (iOSUseDTCoreText && !Label.DTCORETEXT_INIT) {
112
+ // Label.DTCORETEXT_INIT = true;
113
+ // DTCoreTextFontDescriptor.asyncPreloadFontLookupTable();
114
+ // }
115
+ // }
114
116
  createNativeView() {
115
117
  const view = UITextView.new();
116
118
  if (!view.font) {
@@ -123,13 +125,10 @@ export class Label extends LabelBase {
123
125
  view.backgroundColor = UIColor.clearColor;
124
126
  view.userInteractionEnabled = true;
125
127
  view.dataDetectorTypes = -1 /* All */;
126
- view.textContainerInset = {
127
- top: 0,
128
- left: 0,
129
- bottom: 0,
130
- right: 0
131
- };
132
- view.textContainer.lineFragmentPadding = 0; // to remove left padding
128
+ view.textContainerInset = UIEdgeInsetsZero;
129
+ view.textContainer.lineFragmentPadding = 0;
130
+ // ignore font leading just like UILabel does
131
+ view.layoutManager.usesFontLeading = false;
133
132
  // view.textContainer.lineBreakMode = NSLineBreakMode.ByCharWrapping;
134
133
  return view;
135
134
  }
@@ -140,12 +139,6 @@ export class Label extends LabelBase {
140
139
  this._observer['_owner'] = new WeakRef(this);
141
140
  this.nativeViewProtected.addObserverForKeyPathOptionsContext(this._observer, 'contentSize', 1 /* New */, null);
142
141
  this.nativeViewProtected.attributedText = this.attributedString;
143
- // this.htmlText = null;
144
- // this.needsHTMLUpdate = false;
145
- // this.updatingHTML = false;
146
- // if (this.htmlText && this.needsHTMLUpdate) {
147
- // this.updateHTMLString();
148
- // }
149
142
  }
150
143
  disposeNativeView() {
151
144
  this._delegate = null;
@@ -169,20 +162,31 @@ export class Label extends LabelBase {
169
162
  }
170
163
  computeTextHeight(size) {
171
164
  const tv = this.nativeTextViewProtected;
165
+ const oldtextContainerInset = tv.textContainerInset;
166
+ tv.textContainerInset = UIEdgeInsetsZero;
167
+ // if (tv.attributedText) {
168
+ // const result = tv.attributedText.boundingRectWithSizeOptionsContext(
169
+ // size,
170
+ // NSStringDrawingOptions.UsesLineFragmentOrigin | NSStringDrawingOptions.UsesFontLeading,
171
+ // null
172
+ // );
173
+ // return Math.round(CGRectGetHeight(result));
174
+ // }
172
175
  const result = tv.sizeThatFits(size);
176
+ tv.textContainerInset = oldtextContainerInset;
173
177
  return result.height;
174
178
  }
175
179
  updateTextContainerInset(applyVerticalTextAlignment = true) {
176
- // if (!this.text) {
177
- // return;
178
- // }
180
+ var _e, _f;
179
181
  const tv = this.nativeTextViewProtected;
180
182
  const top = layout.toDeviceIndependentPixels(this.effectivePaddingTop + this.effectiveBorderTopWidth);
181
183
  const right = layout.toDeviceIndependentPixels(this.effectivePaddingRight + this.effectiveBorderRightWidth);
182
184
  const bottom = layout.toDeviceIndependentPixels(this.effectivePaddingBottom + this.effectiveBorderBottomWidth);
183
185
  const left = layout.toDeviceIndependentPixels(this.effectivePaddingLeft + this.effectiveBorderLeftWidth);
184
- if (!applyVerticalTextAlignment || !this.verticalTextAlignment) {
185
- this.nativeViewProtected.textContainerInset = {
186
+ if (!applyVerticalTextAlignment ||
187
+ !this.verticalTextAlignment ||
188
+ (((_e = tv.text) === null || _e === void 0 ? void 0 : _e.length) === 0 && ((_f = tv.attributedText) === null || _f === void 0 ? void 0 : _f.length) === 0)) {
189
+ tv.textContainerInset = {
186
190
  top,
187
191
  left,
188
192
  bottom,
@@ -193,7 +197,7 @@ export class Label extends LabelBase {
193
197
  switch (this.verticalTextAlignment) {
194
198
  case 'initial': // not supported
195
199
  case 'top':
196
- this.nativeViewProtected.textContainerInset = {
200
+ tv.textContainerInset = {
197
201
  top,
198
202
  left,
199
203
  bottom,
@@ -205,7 +209,7 @@ export class Label extends LabelBase {
205
209
  const height = this.computeTextHeight(CGSizeMake(tv.bounds.size.width, 10000));
206
210
  let topCorrect = (tv.bounds.size.height - top - bottom - height * tv.zoomScale) / 2.0;
207
211
  topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
208
- this.nativeViewProtected.textContainerInset = {
212
+ tv.textContainerInset = {
209
213
  top: top + topCorrect,
210
214
  left,
211
215
  bottom,
@@ -215,9 +219,9 @@ export class Label extends LabelBase {
215
219
  }
216
220
  case 'bottom': {
217
221
  const height = this.computeTextHeight(CGSizeMake(tv.bounds.size.width, 10000));
218
- let bottomCorrect = tv.bounds.size.height - bottom - height * tv.zoomScale;
222
+ let bottomCorrect = tv.bounds.size.height - top - bottom - height * tv.zoomScale;
219
223
  bottomCorrect = bottomCorrect < 0.0 ? 0.0 : bottomCorrect;
220
- this.nativeViewProtected.textContainerInset = {
224
+ tv.textContainerInset = {
221
225
  top: top + bottomCorrect,
222
226
  left,
223
227
  bottom,
@@ -231,78 +235,6 @@ export class Label extends LabelBase {
231
235
  get ios() {
232
236
  return this.nativeViewProtected;
233
237
  }
234
- // setTextDecorationAndTransform() {
235
- // const style = this.style;
236
- // const dict = new Map<string, any>();
237
- // switch (style.textDecoration) {
238
- // case 'none':
239
- // break;
240
- // case 'underline':
241
- // // TODO: Replace deprecated `StyleSingle` with `Single` after the next typings update
242
- // dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.Single);
243
- // break;
244
- // case 'line-through':
245
- // // TODO: Replace deprecated `StyleSingle` with `Single` after the next typings update
246
- // dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.Single);
247
- // break;
248
- // case 'underline line-through':
249
- // // TODO: Replace deprecated `StyleSingle` with `Single` after the next typings update
250
- // dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.Single);
251
- // dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.Single);
252
- // break;
253
- // default:
254
- // throw new Error(`Invalid text decoration value: ${style.textDecoration}. Valid values are: 'none', 'underline', 'line-through', 'underline line-through'.`);
255
- // }
256
- // if (style.letterSpacing !== 0) {
257
- // dict.set(NSKernAttributeName, style.letterSpacing * this.nativeTextViewProtected.font.pointSize);
258
- // }
259
- // if (style.lineHeight || style.whiteSpace === 'nowrap' || (style['lineBreak'] && style['lineBreak'] !== 'none')) {
260
- // const paragraphStyle = NSMutableParagraphStyle.alloc().init();
261
- // paragraphStyle.minimumLineHeight = style.lineHeight;
262
- // // make sure a possible previously set text alignment setting is not lost when line height is specified
263
- // paragraphStyle.alignment = (this.nativeTextViewProtected as UITextField | UITextView | UILabel).textAlignment;
264
- // // make sure a possible previously set line break mode is not lost when line height is specified
265
- // if (style['lineBreak']) {
266
- // paragraphStyle.lineBreakMode = lineBreakToLineBreakMode(style['lineBreak']);
267
- // } else if (style.whiteSpace) {
268
- // paragraphStyle.lineBreakMode = whiteSpaceToLineBreakMode(style.whiteSpace);
269
- // }
270
- // dict.set(NSParagraphStyleAttributeName, paragraphStyle);
271
- // } else if (isTextView && this.style.textAlignment !== 'initial') {
272
- // const paragraphStyle = NSMutableParagraphStyle.alloc().init();
273
- // paragraphStyle.alignment = this.nativeTextViewProtected.textAlignment;
274
- // dict.set(NSParagraphStyleAttributeName, paragraphStyle);
275
- // }
276
- // if (style.color && dict.size > 0) {
277
- // // dict.set(NSForegroundColorAttributeName, style.color.ios);
278
- // }
279
- // const text = this.text;
280
- // const str = text === undefined || text === null ? '' : text.toString();
281
- // const source = getTransformedText(str, this.textTransform);
282
- // if (dict.size > 0) {
283
- // if (isTextView) {
284
- // // UITextView's font seems to change inside.
285
- // dict.set(NSFontAttributeName, this.nativeTextViewProtected.font);
286
- // }
287
- // const result = NSMutableAttributedString.alloc().initWithString(source);
288
- // result.setAttributesRange(dict as any, { location: 0, length: source.length });
289
- // if (this.nativeTextViewProtected instanceof UIButton) {
290
- // this.nativeTextViewProtected.setAttributedTitleForState(result, UIControlState.Normal);
291
- // } else {
292
- // this.nativeTextViewProtected.attributedText = result;
293
- // }
294
- // } else {
295
- // if (this.nativeTextViewProtected instanceof UIButton) {
296
- // // Clear attributedText or title won't be affected.
297
- // this.nativeTextViewProtected.setAttributedTitleForState(null, UIControlState.Normal);
298
- // this.nativeTextViewProtected.setTitleForState(source, UIControlState.Normal);
299
- // } else {
300
- // // Clear attributedText or text won't be affected.
301
- // this.nativeTextViewProtected.attributedText = undefined;
302
- // this.nativeTextViewProtected.text = source;
303
- // }
304
- // }
305
- // }
306
238
  _requestLayoutOnTextChanged() {
307
239
  if (this._fixedSize === FixedSize.BOTH) {
308
240
  return;
@@ -390,14 +322,13 @@ export class Label extends LabelBase {
390
322
  // when in collectionView or pager
391
323
  // if this is done sync (without DTCoreText) while init the cell
392
324
  // it breaks the UICollectionView :s
393
- if (usingIOSDTCoreText()) {
394
- this._updateHTMLString();
395
- }
396
- else {
397
- // setTimeout(() => {
398
- this._updateHTMLString();
399
- // }, 0);
400
- }
325
+ // if (usingIOSDTCoreText()) {
326
+ // this._updateHTMLString();
327
+ // } else {
328
+ // setTimeout(() => {
329
+ this._updateHTMLString();
330
+ // }, 0);
331
+ // }
401
332
  }
402
333
  _setColor(color) {
403
334
  if (this.nativeTextViewProtected instanceof UIButton) {
@@ -514,6 +445,7 @@ export class Label extends LabelBase {
514
445
  else {
515
446
  super._setNativeText();
516
447
  }
448
+ this.updateTextContainerInset();
517
449
  this._requestLayoutOnTextChanged();
518
450
  }
519
451
  setTextDecorationAndTransform() {
@@ -538,25 +470,18 @@ export class Label extends LabelBase {
538
470
  if (style.letterSpacing !== 0 && this.nativeTextViewProtected.font) {
539
471
  const kern = style.letterSpacing * this.nativeTextViewProtected.font.pointSize;
540
472
  dict.set(NSKernAttributeName, kern);
541
- if (this.nativeTextViewProtected instanceof UITextField) {
542
- this.nativeTextViewProtected.defaultTextAttributes.setValueForKey(kern, NSKernAttributeName);
543
- }
544
473
  }
545
474
  const isTextView = false;
546
- if (style.lineHeight) {
475
+ if (style.lineHeight !== undefined) {
476
+ let lineHeight = style.lineHeight;
477
+ if (lineHeight === 0) {
478
+ lineHeight = 0.00001;
479
+ }
547
480
  const paragraphStyle = NSMutableParagraphStyle.alloc().init();
548
- paragraphStyle.lineSpacing = style.lineHeight;
481
+ paragraphStyle.minimumLineHeight = lineHeight;
482
+ paragraphStyle.maximumLineHeight = lineHeight;
549
483
  // make sure a possible previously set text alignment setting is not lost when line height is specified
550
- if (this.nativeTextViewProtected instanceof UIButton) {
551
- paragraphStyle.alignment = this.nativeTextViewProtected.titleLabel.textAlignment;
552
- }
553
- else {
554
- paragraphStyle.alignment = this.nativeTextViewProtected.textAlignment;
555
- }
556
- if (this.nativeTextViewProtected instanceof UILabel) {
557
- // make sure a possible previously set line break mode is not lost when line height is specified
558
- paragraphStyle.lineBreakMode = this.nativeTextViewProtected.lineBreakMode;
559
- }
484
+ paragraphStyle.alignment = this.nativeTextViewProtected.textAlignment;
560
485
  dict.set(NSParagraphStyleAttributeName, paragraphStyle);
561
486
  }
562
487
  else if (isTextView) {
@@ -580,81 +505,27 @@ export class Label extends LabelBase {
580
505
  location: 0,
581
506
  length: source.length
582
507
  });
583
- if (this.nativeTextViewProtected instanceof UIButton) {
584
- this.nativeTextViewProtected.setAttributedTitleForState(result, 0 /* Normal */);
585
- }
586
- else {
587
- this.nativeTextViewProtected.attributedText = result;
588
- }
508
+ this.nativeTextViewProtected.attributedText = result;
589
509
  }
590
510
  else {
591
- if (this.nativeTextViewProtected instanceof UIButton) {
592
- // Clear attributedText or title won't be affected.
593
- this.nativeTextViewProtected.setAttributedTitleForState(null, 0 /* Normal */);
594
- this.nativeTextViewProtected.setTitleForState(source, 0 /* Normal */);
595
- }
596
- else {
597
- // Clear attributedText or text won't be affected.
598
- this.nativeTextViewProtected.attributedText = undefined;
599
- this.nativeTextViewProtected.text = source;
600
- }
511
+ // Clear attributedText or text won't be affected.
512
+ this.nativeTextViewProtected.attributedText = undefined;
513
+ this.nativeTextViewProtected.text = source;
601
514
  }
602
515
  if (!style.color && majorVersion >= 13 && UIColor.labelColor) {
603
516
  this._setColor(UIColor.labelColor);
604
517
  }
605
518
  }
606
- createNSMutableAttributedString(formattedString) {
607
- var _e;
608
- // we need to store the max Font size to pass it to createMutableStringForSpan
609
- const length = formattedString.spans.length;
610
- let maxFontSize = ((_e = formattedString.style) === null || _e === void 0 ? void 0 : _e.fontSize) || (this === null || this === void 0 ? void 0 : this.style.fontSize) || 0;
611
- for (let i = 0; i < length; i++) {
612
- const s = formattedString.spans.getItem(i);
613
- if (s.style.fontSize) {
614
- maxFontSize = Math.max(maxFontSize, s.style.fontSize);
615
- }
616
- }
617
- this.currentMaxFontSize = maxFontSize;
618
- return super.createNSMutableAttributedString(formattedString);
619
- }
620
- createMutableStringForSpan(span, text) {
621
- const viewFont = this.nativeTextViewProtected.font;
622
- const attrDict = {};
623
- const style = span.style;
624
- let align = style.verticalAlignment || span.parent.style.verticalAlignment;
625
- if (!align || align === 'stretch') {
626
- align = this.verticalTextAlignment;
627
- }
628
- const font = new Font(style.fontFamily, style.fontSize, style.fontStyle, style.fontWeight);
629
- const iosFont = font.getUIFont(viewFont);
630
- attrDict[NSFontAttributeName] = iosFont;
631
- if (span.color) {
632
- const color = span.color instanceof Color ? span.color : new Color(span.color);
633
- attrDict[NSForegroundColorAttributeName] = color.ios;
634
- }
635
- // We don't use isSet function here because defaultValue for backgroundColor is null.
636
- const backgroundColor = style.backgroundColor || span.parent.backgroundColor;
637
- if (backgroundColor) {
638
- const color = backgroundColor instanceof Color ? backgroundColor : new Color(backgroundColor);
639
- attrDict[NSBackgroundColorAttributeName] = color.ios;
640
- }
641
- const textDecoration = getClosestPropertyValue(textDecorationProperty, span);
642
- if (textDecoration) {
643
- const underline = textDecoration.indexOf('underline') !== -1;
644
- if (underline) {
645
- attrDict[NSUnderlineStyleAttributeName] = underline;
646
- }
647
- const strikethrough = textDecoration.indexOf('line-through') !== -1;
648
- if (strikethrough) {
649
- attrDict[NSStrikethroughStyleAttributeName] = strikethrough;
650
- }
651
- }
652
- if (align && align !== 'stretch') {
653
- if (iosFont) {
654
- attrDict[NSBaselineOffsetAttributeName] = -computeBaseLineOffset(align, -iosFont.ascender, -iosFont.descender, -iosFont.ascender, -iosFont.descender, iosFont.pointSize, this.currentMaxFontSize);
655
- }
519
+ createFormattedTextNative(value) {
520
+ return createNativeAttributedString(value, this, this.autoFontSize, this.fontSizeRatio);
521
+ }
522
+ setFormattedTextDecorationAndTransform() {
523
+ const attrText = this.createFormattedTextNative(this.formattedText);
524
+ // we override parent class behavior because we apply letterSpacing and lineHeight on a per Span basis
525
+ if (majorVersion >= 13 && UIColor.labelColor) {
526
+ this.nativeTextViewProtected.textColor = UIColor.labelColor;
656
527
  }
657
- return NSMutableAttributedString.alloc().initWithStringAttributes(text, attrDict);
528
+ this.nativeTextViewProtected.attributedText = attrText;
658
529
  }
659
530
  [paddingTopProperty.getDefault]() {
660
531
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativescript-community/ui-label",
3
- "version": "1.2.0",
3
+ "version": "1.2.4",
4
4
  "description": "Alternative to the built-in NativeScript Label but with better performance and additional features such as HTML rendering and more.",
5
5
  "main": "./label",
6
6
  "sideEffects": false,
@@ -32,7 +32,7 @@
32
32
  "license": "Apache-2.0",
33
33
  "readmeFilename": "README.md",
34
34
  "dependencies": {
35
- "@nativescript-community/text": "^1.4.21"
35
+ "@nativescript-community/text": "^1.4.32"
36
36
  },
37
- "gitHead": "548a66a7342e4c8e1e33ce63a0b6dd9a6a57c5fb"
37
+ "gitHead": "27522e865296a86d576a68d8adc8973f384c572f"
38
38
  }
@@ -1,497 +0,0 @@
1
- package com.nativescript.label;
2
-
3
- import android.content.Context;
4
- import android.content.res.TypedArray;
5
- import android.graphics.Canvas;
6
- import android.graphics.Color;
7
- import android.text.Layout;
8
- import android.text.Layout.Alignment;
9
- import android.text.Spannable;
10
- import android.text.SpannableString;
11
- import android.text.SpannableStringBuilder;
12
- import android.text.Spanned;
13
- import android.text.StaticLayout;
14
- import android.text.TextUtils;
15
- import android.text.TextUtils.TruncateAt;
16
- import android.text.style.ForegroundColorSpan;
17
- import android.util.AttributeSet;
18
- import android.util.Log;
19
- import android.util.TypedValue;
20
- import android.view.View;
21
-
22
- import java.util.ArrayList;
23
- import java.util.List;
24
- import java.util.regex.Pattern;
25
-
26
- import androidx.annotation.NonNull;
27
- import androidx.appcompat.widget.AppCompatTextView;
28
-
29
- /**
30
- * A {@link android.widget.TextView} that ellipsizes more intelligently.
31
- * This class supports ellipsizing multiline text through setting {@code android:ellipsize}
32
- * and {@code android:maxLines}.
33
- * <p/>
34
- * Note: {@link android.text.TextUtils.TruncateAt#MARQUEE} ellipsizing type is not supported.
35
- * <p>
36
- * 学习戚继光,一边儿读别人的源码,一边儿做笔记
37
- */
38
- public class EllipsizingTextView extends AppCompatTextView {
39
- public static final int ELLIPSIZE_ALPHA = 0x88;
40
- private SpannableString ELLIPSIS = new SpannableString("\u2026");
41
-
42
- private static final Pattern DEFAULT_END_PUNCTUATION
43
- = Pattern.compile("[\\.!?,;:\u2026]*$", Pattern.DOTALL);
44
-
45
- private final List<EllipsizeListener> mEllipsizeListeners = new ArrayList<>();
46
-
47
- private EllipsizeStrategy mEllipsizeStrategy;
48
-
49
- private boolean isEllipsized;
50
-
51
- //In order to be the reset method only needs to be called once
52
- private boolean isStale;
53
-
54
- //The entry used to determine the call setText () is RESETTEXT or otherwhere call, this practice is cool.
55
- private boolean programmaticChange;
56
-
57
- //text
58
- private CharSequence mFullText;
59
- private int mMaxLines;
60
-
61
- //The line spacing, this point is considered, great.
62
- private float mLineSpacingMult = 1.0f;
63
- private float mLineAddVertPad = 0.0f;
64
-
65
-
66
- private boolean wordWrap = true;
67
- private float minTextSize = 20;
68
- private float maxTextSize = 30;
69
-
70
- /**
71
- * The end punctuation which will be removed when appending {@link #ELLIPSIS}.
72
- */
73
- private Pattern mEndPunctPattern;
74
-
75
- public EllipsizingTextView(Context context) {
76
- this(context, null);
77
- }
78
-
79
-
80
- public EllipsizingTextView(Context context, AttributeSet attrs) {
81
- this(context, attrs, android.R.attr.textViewStyle);
82
- }
83
-
84
-
85
- public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
86
- super(context, attrs, defStyle);
87
- TypedArray a = context.obtainStyledAttributes(attrs,
88
- new int[]{android.R.attr.maxLines, android.R.attr.ellipsize}, defStyle, 0);
89
- setMaxLines(a.getInt(0, Integer.MAX_VALUE));
90
- a.recycle();
91
- setEndPunctuationPattern(DEFAULT_END_PUNCTUATION);
92
- maxTextSize = this.getTextSize();
93
- if (maxTextSize < 35) {
94
- maxTextSize = 30;
95
- }
96
- super.setSingleLine(false);
97
- }
98
-
99
- public void setEndPunctuationPattern(Pattern pattern) {
100
- mEndPunctPattern = pattern;
101
- }
102
-
103
- @SuppressWarnings("unused")
104
- public void addEllipsizeListener(@NonNull EllipsizeListener listener) {
105
- mEllipsizeListeners.add(listener);
106
- }
107
-
108
- @SuppressWarnings("unused")
109
- public void removeEllipsizeListener(@NonNull EllipsizeListener listener) {
110
- mEllipsizeListeners.remove(listener);
111
- }
112
-
113
- @SuppressWarnings("unused")
114
- public boolean isEllipsized() {
115
- return isEllipsized;
116
- }
117
-
118
- /**
119
- * @return The maximum number of lines displayed in this {@link android.widget.TextView}.
120
- */
121
- public int getMaxLines() {
122
- return mMaxLines;
123
- }
124
-
125
- @Override
126
- public void setMaxLines(int maxLines) {
127
- super.setMaxLines(maxLines);
128
- // super.setMaxLines((maxLines == 0) ? Integer.MAX_VALUE : maxLines);
129
- // if (maxLines == Integer.MAX_VALUE) {
130
- // maxLines = 0;
131
- // }
132
- // // super.setMaxLines((maxLines == 0) ? Integer.MAX_VALUE : maxLines);
133
- mMaxLines = maxLines;
134
- isStale = true;
135
- }
136
-
137
- public float getMinTextSize() {
138
- return minTextSize;
139
- }
140
-
141
- public void setMinTextSize(float minTextSize) {
142
- this.minTextSize = minTextSize;
143
- }
144
-
145
- public float getMaxTextSize() {
146
- return maxTextSize;
147
- }
148
-
149
- public void setMaxTextSize(float minTextSize) {
150
- this.maxTextSize = minTextSize;
151
- }
152
-
153
- /**
154
- * Determines if the last fully visible line is being ellipsized.
155
- *
156
- * @return {@code true} if the last fully visible line is being ellipsized;
157
- * otherwise, returns {@code false}.
158
- */
159
- public boolean ellipsizingLastFullyVisibleLine() {
160
- return mMaxLines == Integer.MAX_VALUE;
161
- }
162
-
163
- @Override
164
- public void setLineSpacing(float add, float mult) {
165
- mLineAddVertPad = add;
166
- mLineSpacingMult = mult;
167
- super.setLineSpacing(add, mult);
168
- }
169
-
170
- @Override
171
- public void setText(CharSequence text, BufferType type) {
172
- if (!programmaticChange) {//Distribution source
173
- mFullText = text instanceof Spanned ? (Spanned) text : text;
174
- isStale = true;
175
- }
176
- super.setText(text, type);
177
- }
178
-
179
- @Override
180
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
181
- int w = View.MeasureSpec.getSize(widthMeasureSpec);
182
- // int wm = MeasureSpec.getMode(widthMeasureSpec);
183
- int h = View.MeasureSpec.getSize(heightMeasureSpec);
184
- int hm = View.MeasureSpec.getMode(heightMeasureSpec);
185
- if (hm == 0)
186
- h = 100000;
187
- if ((hm == View.MeasureSpec.AT_MOST || hm == View.MeasureSpec.UNSPECIFIED)
188
- && (mFullText == null || mFullText.length() == 0)) {
189
- heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.EXACTLY);
190
- }
191
-
192
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
193
- }
194
- @Override
195
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
196
- super.onSizeChanged(w, h, oldw, oldh);
197
- if (ellipsizingLastFullyVisibleLine()) {//Rigorous, learning
198
- isStale = true;
199
- }
200
- }
201
-
202
- @Override
203
- public void setPadding(int left, int top, int right, int bottom) {
204
- super.setPadding(left, top, right, bottom);
205
- if (ellipsizingLastFullyVisibleLine()) {//Rigorous, learning
206
- isStale = true;
207
- }
208
- }
209
-
210
- @Override
211
- protected void onDraw(@NonNull Canvas canvas) {
212
- if (isStale) {
213
- resetText();
214
- }
215
- super.onDraw(canvas);
216
- }
217
-
218
- /**
219
- * Sets the ellipsized text if appropriate.
220
- */
221
- private void resetText() {
222
- int maxLines = getMaxLines();
223
- CharSequence workingText = mFullText;
224
- boolean ellipsized = false;
225
-
226
- if (maxLines != -1) {
227
- if (mEllipsizeStrategy == null) setEllipsize(null);
228
- workingText = mEllipsizeStrategy.processText(mFullText);
229
- ellipsized = !mEllipsizeStrategy.isInLayout(mFullText);
230
- }
231
-
232
- if (!workingText.equals(getText())) {//Avoid useless operations
233
- programmaticChange = true;
234
- try {
235
- setText(workingText);
236
- } finally {
237
- programmaticChange = false;
238
- }
239
- }
240
-
241
- isStale = false;//Switch status
242
-
243
- //Notification listener
244
- if (ellipsized != isEllipsized) {
245
- isEllipsized = ellipsized;
246
- for (EllipsizeListener listener : mEllipsizeListeners) {
247
- listener.ellipsizeStateChanged(ellipsized);
248
- }
249
- }
250
- }
251
-
252
- /**
253
- * Causes words in the text that are longer than the view is wide to be ellipsized
254
- * instead of broken in the middle. Use {@code null} to turn off ellipsizing.
255
- * <p/>
256
- * Note: Method does nothing for {@link android.text.TextUtils.TruncateAt#MARQUEE}
257
- * ellipsizing type.
258
- *
259
- * @param where part of text to ellipsize
260
- */
261
- @Override
262
- public void setEllipsize(TruncateAt where) {
263
- if (where == null) {
264
- mEllipsizeStrategy = new EllipsizeNoneStrategy();
265
- return;
266
- }
267
-
268
- switch (where) {
269
- case END:
270
- mEllipsizeStrategy = new EllipsizeEndStrategy();
271
- break;
272
- case START:
273
- mEllipsizeStrategy = new EllipsizeStartStrategy();
274
- break;
275
- case MIDDLE:
276
- mEllipsizeStrategy = new EllipsizeMiddleStrategy();
277
- break;
278
- case MARQUEE:
279
- default:
280
- mEllipsizeStrategy = new EllipsizeNoneStrategy();
281
- break;
282
- }
283
- }
284
-
285
- /**
286
- * A listener that notifies when the ellipsize state has changed.
287
- */
288
- public interface EllipsizeListener {
289
- void ellipsizeStateChanged(boolean ellipsized);
290
- }
291
-
292
- /**
293
- * A base class for an ellipsize strategy.
294
- */
295
- private abstract class EllipsizeStrategy {
296
- /**
297
- * Returns ellipsized text if the text does not fit inside of the layout;
298
- * otherwise, returns the full text.
299
- *
300
- * @param text text to process
301
- * @return Ellipsized text if the text does not fit inside of the layout;
302
- * otherwise, returns the full text.
303
- */
304
- public CharSequence processText(CharSequence text) {
305
- return !isInLayout(text) ? createEllipsizedText(text) : text;
306
- }
307
-
308
- /**
309
- * Determines if the text fits inside of the layout.
310
- *
311
- * @param text text to fit
312
- * @return {@code true} if the text fits inside of the layout;
313
- * otherwise, returns {@code false}.
314
- */
315
- public boolean isInLayout(CharSequence text) {
316
- Layout layout = createWorkingLayout(text);
317
- return layout.getLineCount() <= getLinesCount();
318
- }
319
-
320
- /**
321
- * Creates a working layout with the given text.
322
- *
323
- * @param workingText text to create layout with
324
- * @return {@link android.text.Layout} with the given text.
325
- */
326
- protected Layout createWorkingLayout(CharSequence workingText) {
327
- return new StaticLayout(workingText, getPaint(),
328
- getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(),
329
- Alignment.ALIGN_NORMAL, mLineSpacingMult,
330
- mLineAddVertPad, false /* includepad */);
331
- }
332
-
333
- /**
334
- * Get how many lines of text we are allowed to display.
335
- */
336
- protected int getLinesCount() {
337
- if (ellipsizingLastFullyVisibleLine()) {
338
- int fullyVisibleLinesCount = getFullyVisibleLinesCount();
339
- return fullyVisibleLinesCount == -1 ? 1 : fullyVisibleLinesCount;
340
- } else {
341
- return mMaxLines;
342
- }
343
- }
344
-
345
- /**
346
- * Get how many lines of text we can display so their full height is visible.
347
- */
348
- protected int getFullyVisibleLinesCount() {
349
- Layout layout = createWorkingLayout("");
350
- int height = getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
351
- int lineHeight = layout.getLineBottom(0);
352
- return height / lineHeight;
353
- }
354
-
355
- /**
356
- * Creates ellipsized text from the given text.
357
- *
358
- * @param fullText text to ellipsize
359
- * @return Ellipsized text
360
- */
361
- protected abstract CharSequence createEllipsizedText(CharSequence fullText);
362
- }
363
-
364
- /**
365
- * An {@link EllipsizingTextView.EllipsizeStrategy} that
366
- * does not ellipsize text.
367
- */
368
- private class EllipsizeNoneStrategy extends EllipsizeStrategy {
369
- @Override
370
- protected CharSequence createEllipsizedText(CharSequence fullText) {
371
- return fullText;
372
- }
373
- }
374
-
375
- /**
376
- * An {@link EllipsizingTextView.EllipsizeStrategy} that
377
- * ellipsizes text at the end.
378
- */
379
- private class EllipsizeEndStrategy extends EllipsizeStrategy {
380
- @Override
381
- protected CharSequence createEllipsizedText(CharSequence fullText) {
382
- Layout layout = createWorkingLayout(fullText);
383
- int lineCount = layout.getLineCount();
384
- int lastLineIndex = Math.min(mMaxLines - 1, lineCount -1);
385
- int cutOffIndex = layout.getLineEnd(lastLineIndex);
386
- int textLength = fullText.length();
387
- int cutOffLength = textLength - cutOffIndex;
388
- if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
389
-
390
- //True to display text
391
- CharSequence workingText = TextUtils.substring(fullText, 0, textLength - cutOffLength).trim();
392
-
393
-
394
- final int lastLineStart = layout.getLineStart(lastLineIndex);
395
- final CharSequence remainder = TextUtils.ellipsize(fullText.subSequence(lastLineStart,
396
- fullText.length()), getPaint(), getWidth(), TextUtils.TruncateAt.END);
397
-
398
- //seems to be handling corner case when there is still too many chars
399
- while (!isInLayout(TextUtils.concat(stripEndPunctuation(workingText), ELLIPSIS))) {
400
- workingText = workingText.subSequence(0, workingText.length() - 1);
401
-
402
- }
403
-
404
- //first we copy the text
405
- workingText = TextUtils.concat(stripEndPunctuation(workingText), ELLIPSIS);
406
- SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
407
-
408
- //then we apply spans if necessary
409
- if (fullText instanceof Spanned) {
410
- TextUtils.copySpansFrom((Spanned) fullText, 0, workingText.length(), null, dest, 0);
411
- }
412
- return dest;
413
- }
414
-
415
- /**
416
- * Strips the end punctuation from a given text according to {@link #mEndPunctPattern}.
417
- *
418
- * @param workingText text to strip end punctuation from
419
- * @return Text without end punctuation.
420
- */
421
- public String stripEndPunctuation(CharSequence workingText) {
422
- return mEndPunctPattern.matcher(workingText).replaceFirst("");
423
- }
424
- }
425
-
426
- /**
427
- * An {@link EllipsizingTextView.EllipsizeStrategy} that
428
- * ellipsizes text at the start.
429
- */
430
- private class EllipsizeStartStrategy extends EllipsizeStrategy {
431
- @Override
432
- protected CharSequence createEllipsizedText(CharSequence fullText) {
433
- Layout layout = createWorkingLayout(fullText);
434
- int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
435
- int textLength = fullText.length();
436
- int cutOffLength = textLength - cutOffIndex;
437
- if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
438
- CharSequence workingText = TextUtils.substring(fullText, cutOffLength, textLength).trim();
439
-
440
- while (!isInLayout(TextUtils.concat(ELLIPSIS, workingText))) {
441
- int firstSpace = TextUtils.indexOf(workingText, ' ');
442
- if (firstSpace == -1) {
443
- break;
444
- }
445
- workingText = TextUtils.substring(workingText, firstSpace, workingText.length()).trim();
446
- }
447
-
448
- workingText = TextUtils.concat(ELLIPSIS, workingText);
449
- SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
450
-
451
- if (fullText instanceof Spanned) {
452
- TextUtils.copySpansFrom((Spanned) fullText, textLength - workingText.length(),
453
- textLength, null, dest, 0);
454
- }
455
- return dest;
456
- }
457
- }
458
-
459
- /**
460
- * An {@link EllipsizingTextView.EllipsizeStrategy} that
461
- * ellipsizes text in the middle.
462
- */
463
- private class EllipsizeMiddleStrategy extends EllipsizeStrategy {
464
- @Override
465
- protected CharSequence createEllipsizedText(CharSequence fullText) {
466
- Layout layout = createWorkingLayout(fullText);
467
- int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
468
- int textLength = fullText.length();
469
- int cutOffLength = textLength - cutOffIndex;
470
- if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
471
- cutOffLength += cutOffIndex % 2; // Make it even.
472
- String firstPart = TextUtils.substring(
473
- fullText, 0, textLength / 2 - cutOffLength / 2).trim();
474
- String secondPart = TextUtils.substring(
475
- fullText, textLength / 2 + cutOffLength / 2, textLength).trim();
476
-
477
- while (!isInLayout(TextUtils.concat(firstPart, ELLIPSIS, secondPart))) {
478
- int lastSpaceFirstPart = firstPart.lastIndexOf(' ');
479
- int firstSpaceSecondPart = secondPart.indexOf(' ');
480
- if (lastSpaceFirstPart == -1 || firstSpaceSecondPart == -1) break;
481
- firstPart = firstPart.substring(0, lastSpaceFirstPart).trim();
482
- secondPart = secondPart.substring(firstSpaceSecondPart, secondPart.length()).trim();
483
- }
484
-
485
- SpannableStringBuilder firstDest = new SpannableStringBuilder(firstPart);
486
- SpannableStringBuilder secondDest = new SpannableStringBuilder(secondPart);
487
-
488
- if (fullText instanceof Spanned) {
489
- TextUtils.copySpansFrom((Spanned) fullText, 0, firstPart.length(),
490
- null, firstDest, 0);
491
- TextUtils.copySpansFrom((Spanned) fullText, textLength - secondPart.length(),
492
- textLength, null, secondDest, 0);
493
- }
494
- return TextUtils.concat(firstDest, ELLIPSIS, secondDest);
495
- }
496
- }
497
- }