chore: cleanup

This commit is contained in:
Nathan Walker
2020-07-06 14:25:11 -07:00
parent 94cf5c4706
commit 6336eac4b5
477 changed files with 59087 additions and 74961 deletions

View File

@@ -1,187 +1,174 @@
import { Image as ImageDefinition, Stretch } from ".";
import { View, CSSType } from "../core/view";
import { booleanConverter } from "../core/view-base";
import { ImageAsset } from "../../image-asset";
import { ImageSource } from "../../image-source";
import {
isDataURI,
isFontIconURI,
isFileOrResourcePath,
RESOURCE_PREFIX,
} from "../../utils/utils";
import { Color } from "../../color";
import { isIOS } from "../../platform";
import { Style } from "../styling/style";
import { Length } from "../styling/style-properties";
import { Property, InheritedCssProperty } from "../core/properties";
import { Trace } from "../../trace";
import { Image as ImageDefinition, Stretch } from '.';
import { View, CSSType } from '../core/view';
import { booleanConverter } from '../core/view-base';
import { ImageAsset } from '../../image-asset';
import { ImageSource } from '../../image-source';
import { isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX } from '../../utils';
import { Color } from '../../color';
import { isIOS } from '../../platform';
import { Style } from '../styling/style';
import { Length } from '../styling/style-properties';
import { Property, InheritedCssProperty } from '../core/properties';
import { Trace } from '../../trace';
@CSSType("Image")
@CSSType('Image')
export abstract class ImageBase extends View implements ImageDefinition {
public imageSource: ImageSource;
public src: string | ImageSource;
public isLoading: boolean;
public stretch: Stretch;
public loadMode: "sync" | "async";
public decodeWidth: Length;
public decodeHeight: Length;
public imageSource: ImageSource;
public src: string | ImageSource;
public isLoading: boolean;
public stretch: Stretch;
public loadMode: 'sync' | 'async';
public decodeWidth: Length;
public decodeHeight: Length;
get tintColor(): Color {
return this.style.tintColor;
}
set tintColor(value: Color) {
this.style.tintColor = value;
}
get tintColor(): Color {
return this.style.tintColor;
}
set tintColor(value: Color) {
this.style.tintColor = value;
}
/**
* @internal
*/
public _createImageSourceFromSrc(
value: string | ImageSource | ImageAsset
): void {
const originalValue = value;
const sync = this.loadMode === "sync";
if (typeof value === "string" || value instanceof String) {
value = value.trim();
this.imageSource = null;
this["_url"] = value;
/**
* @internal
*/
public _createImageSourceFromSrc(value: string | ImageSource | ImageAsset): void {
const originalValue = value;
const sync = this.loadMode === 'sync';
if (typeof value === 'string' || value instanceof String) {
value = value.trim();
this.imageSource = null;
this['_url'] = value;
this.isLoading = true;
this.isLoading = true;
const imageLoaded = (source: ImageSource) => {
let currentValue = this.src;
if (currentValue !== originalValue) {
return;
}
this.imageSource = source;
this.isLoading = false;
};
const imageLoaded = (source: ImageSource) => {
let currentValue = this.src;
if (currentValue !== originalValue) {
return;
}
this.imageSource = source;
this.isLoading = false;
};
if (isFontIconURI(value)) {
const fontIconCode = value.split("//")[1];
if (fontIconCode !== undefined) {
// support sync mode only
const font = this.style.fontInternal;
const color = this.style.color;
imageLoaded(
ImageSource.fromFontIconCodeSync(
fontIconCode,
font,
color
)
);
}
} else if (isDataURI(value)) {
const base64Data = value.split(",")[1];
if (base64Data !== undefined) {
if (sync) {
imageLoaded(ImageSource.fromBase64Sync(base64Data));
} else {
ImageSource.fromBase64(base64Data).then(imageLoaded);
}
}
} else if (isFileOrResourcePath(value)) {
if (value.indexOf(RESOURCE_PREFIX) === 0) {
const resPath = value.substr(RESOURCE_PREFIX.length);
if (sync) {
imageLoaded(ImageSource.fromResourceSync(resPath));
} else {
this.imageSource = null;
ImageSource.fromResource(resPath).then(imageLoaded);
}
} else {
if (sync) {
imageLoaded(ImageSource.fromFileSync(value));
} else {
this.imageSource = null;
ImageSource.fromFile(value).then(imageLoaded);
}
}
} else {
this.imageSource = null;
ImageSource.fromUrl(value).then(
(r) => {
if (this["_url"] === value) {
this.imageSource = r;
this.isLoading = false;
}
},
(err) => {
// catch: Response content may not be converted to an Image
this.isLoading = false;
if (Trace.isEnabled()) {
if (typeof err === "object" && err.message) {
err = err.message;
}
Trace.write(err, Trace.categories.Debug);
}
}
);
}
} else if (value instanceof ImageSource) {
// Support binding the imageSource trough the src property
this.imageSource = value;
this.isLoading = false;
} else if (value instanceof ImageAsset) {
ImageSource.fromAsset(value).then((result) => {
this.imageSource = result;
this.isLoading = false;
});
} else {
this.imageSource = new ImageSource(value);
this.isLoading = false;
}
}
if (isFontIconURI(value)) {
const fontIconCode = value.split('//')[1];
if (fontIconCode !== undefined) {
// support sync mode only
const font = this.style.fontInternal;
const color = this.style.color;
imageLoaded(ImageSource.fromFontIconCodeSync(fontIconCode, font, color));
}
} else if (isDataURI(value)) {
const base64Data = value.split(',')[1];
if (base64Data !== undefined) {
if (sync) {
imageLoaded(ImageSource.fromBase64Sync(base64Data));
} else {
ImageSource.fromBase64(base64Data).then(imageLoaded);
}
}
} else if (isFileOrResourcePath(value)) {
if (value.indexOf(RESOURCE_PREFIX) === 0) {
const resPath = value.substr(RESOURCE_PREFIX.length);
if (sync) {
imageLoaded(ImageSource.fromResourceSync(resPath));
} else {
this.imageSource = null;
ImageSource.fromResource(resPath).then(imageLoaded);
}
} else {
if (sync) {
imageLoaded(ImageSource.fromFileSync(value));
} else {
this.imageSource = null;
ImageSource.fromFile(value).then(imageLoaded);
}
}
} else {
this.imageSource = null;
ImageSource.fromUrl(value).then(
(r) => {
if (this['_url'] === value) {
this.imageSource = r;
this.isLoading = false;
}
},
(err) => {
// catch: Response content may not be converted to an Image
this.isLoading = false;
if (Trace.isEnabled()) {
if (typeof err === 'object' && err.message) {
err = err.message;
}
Trace.write(err, Trace.categories.Debug);
}
}
);
}
} else if (value instanceof ImageSource) {
// Support binding the imageSource trough the src property
this.imageSource = value;
this.isLoading = false;
} else if (value instanceof ImageAsset) {
ImageSource.fromAsset(value).then((result) => {
this.imageSource = result;
this.isLoading = false;
});
} else {
this.imageSource = new ImageSource(value);
this.isLoading = false;
}
}
}
ImageBase.prototype.recycleNativeView = "auto";
ImageBase.prototype.recycleNativeView = 'auto';
export const imageSourceProperty = new Property<ImageBase, ImageSource>({
name: "imageSource",
name: 'imageSource',
});
imageSourceProperty.register(ImageBase);
export const srcProperty = new Property<ImageBase, any>({ name: "src" });
export const srcProperty = new Property<ImageBase, any>({ name: 'src' });
srcProperty.register(ImageBase);
export const loadModeProperty = new Property<ImageBase, "sync" | "async">({
name: "loadMode",
defaultValue: "sync",
export const loadModeProperty = new Property<ImageBase, 'sync' | 'async'>({
name: 'loadMode',
defaultValue: 'sync',
});
loadModeProperty.register(ImageBase);
export const isLoadingProperty = new Property<ImageBase, boolean>({
name: "isLoading",
defaultValue: false,
valueConverter: booleanConverter,
name: 'isLoading',
defaultValue: false,
valueConverter: booleanConverter,
});
isLoadingProperty.register(ImageBase);
export const stretchProperty = new Property<ImageBase, Stretch>({
name: "stretch",
defaultValue: "aspectFit",
affectsLayout: isIOS,
name: 'stretch',
defaultValue: 'aspectFit',
affectsLayout: isIOS,
});
stretchProperty.register(ImageBase);
export const tintColorProperty = new InheritedCssProperty<Style, Color>({
name: "tintColor",
cssName: "tint-color",
equalityComparer: Color.equals,
valueConverter: (value) => new Color(value),
name: 'tintColor',
cssName: 'tint-color',
equalityComparer: Color.equals,
valueConverter: (value) => new Color(value),
});
tintColorProperty.register(Style);
export const decodeHeightProperty = new Property<ImageBase, Length>({
name: "decodeHeight",
defaultValue: { value: 0, unit: "dip" },
valueConverter: Length.parse,
name: 'decodeHeight',
defaultValue: { value: 0, unit: 'dip' },
valueConverter: Length.parse,
});
decodeHeightProperty.register(ImageBase);
export const decodeWidthProperty = new Property<ImageBase, Length>({
name: "decodeWidth",
defaultValue: { value: 0, unit: "dip" },
valueConverter: Length.parse,
name: 'decodeWidth',
defaultValue: { value: 0, unit: 'dip' },
valueConverter: Length.parse,
});
decodeWidthProperty.register(ImageBase);

View File

@@ -1,245 +1,191 @@
import {
ImageBase,
stretchProperty,
imageSourceProperty,
srcProperty,
tintColorProperty,
} from "./image-common";
import {
isDataURI,
isFontIconURI,
isFileOrResourcePath,
RESOURCE_PREFIX,
} from "../../utils/utils";
import { Color } from "../../color";
import { ImageSource } from "../../image-source";
import { ImageAsset } from "../../image-asset";
import { Length } from "../styling/style-properties";
import { knownFolders } from "../../file-system";
import { ImageBase, stretchProperty, imageSourceProperty, srcProperty, tintColorProperty } from './image-common';
import { isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX } from '../../utils';
import { Color } from '../../color';
import { ImageSource } from '../../image-source';
import { ImageAsset } from '../../image-asset';
import { Length } from '../styling/style-properties';
import { knownFolders } from '../../file-system';
import * as platform from "../../platform";
export * from "./image-common";
import * as platform from '../../platform';
export * from './image-common';
const FILE_PREFIX = "file:///";
const ASYNC = "async";
const FILE_PREFIX = 'file:///';
const ASYNC = 'async';
let AndroidImageView: typeof org.nativescript.widgets.ImageView;
interface ImageLoadedListener {
new (
owner: Image
): org.nativescript.widgets.image.Worker.OnImageLoadedListener;
new (owner: Image): org.nativescript.widgets.image.Worker.OnImageLoadedListener;
}
let ImageLoadedListener: ImageLoadedListener;
function initializeImageLoadedListener() {
if (ImageLoadedListener) {
return;
}
if (ImageLoadedListener) {
return;
}
@Interfaces([org.nativescript.widgets.image.Worker.OnImageLoadedListener])
class ImageLoadedListenerImpl extends java.lang.Object
implements org.nativescript.widgets.image.Worker.OnImageLoadedListener {
constructor(public owner: Image) {
super();
@Interfaces([org.nativescript.widgets.image.Worker.OnImageLoadedListener])
class ImageLoadedListenerImpl extends java.lang.Object implements org.nativescript.widgets.image.Worker.OnImageLoadedListener {
constructor(public owner: Image) {
super();
return global.__native(this);
}
return global.__native(this);
}
onImageLoaded(success: boolean): void {
const owner = this.owner;
if (owner) {
owner.isLoading = false;
}
}
}
onImageLoaded(success: boolean): void {
const owner = this.owner;
if (owner) {
owner.isLoading = false;
}
}
}
ImageLoadedListener = ImageLoadedListenerImpl;
ImageLoadedListener = ImageLoadedListenerImpl;
}
export class Image extends ImageBase {
nativeViewProtected: org.nativescript.widgets.ImageView;
nativeViewProtected: org.nativescript.widgets.ImageView;
public useCache = true;
public useCache = true;
public createNativeView() {
if (!AndroidImageView) {
AndroidImageView = org.nativescript.widgets.ImageView;
}
public createNativeView() {
if (!AndroidImageView) {
AndroidImageView = org.nativescript.widgets.ImageView;
}
return new AndroidImageView(this._context);
}
return new AndroidImageView(this._context);
}
public initNativeView(): void {
super.initNativeView();
initializeImageLoadedListener();
const nativeView = this.nativeViewProtected;
const listener = new ImageLoadedListener(this);
nativeView.setImageLoadedListener(listener);
(<any>nativeView).listener = listener;
}
public initNativeView(): void {
super.initNativeView();
initializeImageLoadedListener();
const nativeView = this.nativeViewProtected;
const listener = new ImageLoadedListener(this);
nativeView.setImageLoadedListener(listener);
(<any>nativeView).listener = listener;
}
public disposeNativeView() {
(<any>this.nativeViewProtected).listener.owner = null;
super.disposeNativeView();
}
public disposeNativeView() {
(<any>this.nativeViewProtected).listener.owner = null;
super.disposeNativeView();
}
public resetNativeView(): void {
super.resetNativeView();
this.nativeViewProtected.setImageMatrix(new android.graphics.Matrix());
}
public resetNativeView(): void {
super.resetNativeView();
this.nativeViewProtected.setImageMatrix(new android.graphics.Matrix());
}
public _createImageSourceFromSrc(value: string | ImageSource | ImageAsset) {
const imageView = this.nativeViewProtected;
if (!imageView) {
return;
}
public _createImageSourceFromSrc(value: string | ImageSource | ImageAsset) {
const imageView = this.nativeViewProtected;
if (!imageView) {
return;
}
if (!value) {
imageView.setUri(null, 0, 0, false, false, true);
if (!value) {
imageView.setUri(null, 0, 0, false, false, true);
return;
}
return;
}
let screen = platform.screen.mainScreen;
let screen = platform.screen.mainScreen;
let decodeWidth = Math.min(
Length.toDevicePixels(this.decodeWidth, 0),
screen.widthPixels
);
let decodeHeight = Math.min(
Length.toDevicePixels(this.decodeHeight, 0),
screen.heightPixels
);
let keepAspectRatio = this._calculateKeepAspectRatio();
if (value instanceof ImageAsset) {
if (value.options) {
decodeWidth = value.options.width || decodeWidth;
decodeHeight = value.options.height || decodeHeight;
keepAspectRatio = !!value.options.keepAspectRatio;
}
let decodeWidth = Math.min(Length.toDevicePixels(this.decodeWidth, 0), screen.widthPixels);
let decodeHeight = Math.min(Length.toDevicePixels(this.decodeHeight, 0), screen.heightPixels);
let keepAspectRatio = this._calculateKeepAspectRatio();
if (value instanceof ImageAsset) {
if (value.options) {
decodeWidth = value.options.width || decodeWidth;
decodeHeight = value.options.height || decodeHeight;
keepAspectRatio = !!value.options.keepAspectRatio;
}
// handle assets as file paths natively
value = value.android;
}
// handle assets as file paths natively
value = value.android;
}
const async = this.loadMode === ASYNC;
if (typeof value === "string" || value instanceof String) {
value = value.trim();
this.isLoading = true;
const async = this.loadMode === ASYNC;
if (typeof value === 'string' || value instanceof String) {
value = value.trim();
this.isLoading = true;
if (isFontIconURI(value) || isDataURI(value)) {
// TODO: Check with runtime what should we do in case of base64 string.
super._createImageSourceFromSrc(value);
} else if (isFileOrResourcePath(value)) {
if (value.indexOf(RESOURCE_PREFIX) === 0) {
imageView.setUri(
value,
decodeWidth,
decodeHeight,
keepAspectRatio,
this.useCache,
async
);
} else {
let fileName = value;
if (fileName.indexOf("~/") === 0) {
fileName =
knownFolders.currentApp().path +
"/" +
fileName.replace("~/", "");
}
if (isFontIconURI(value) || isDataURI(value)) {
// TODO: Check with runtime what should we do in case of base64 string.
super._createImageSourceFromSrc(value);
} else if (isFileOrResourcePath(value)) {
if (value.indexOf(RESOURCE_PREFIX) === 0) {
imageView.setUri(value, decodeWidth, decodeHeight, keepAspectRatio, this.useCache, async);
} else {
let fileName = value;
if (fileName.indexOf('~/') === 0) {
fileName = knownFolders.currentApp().path + '/' + fileName.replace('~/', '');
}
imageView.setUri(
FILE_PREFIX + fileName,
decodeWidth,
decodeHeight,
keepAspectRatio,
this.useCache,
async
);
}
} else {
// For backwards compatibility http always use async loading.
imageView.setUri(
value,
decodeWidth,
decodeHeight,
keepAspectRatio,
this.useCache,
true
);
}
} else {
super._createImageSourceFromSrc(value);
}
}
imageView.setUri(FILE_PREFIX + fileName, decodeWidth, decodeHeight, keepAspectRatio, this.useCache, async);
}
} else {
// For backwards compatibility http always use async loading.
imageView.setUri(value, decodeWidth, decodeHeight, keepAspectRatio, this.useCache, true);
}
} else {
super._createImageSourceFromSrc(value);
}
}
private _calculateKeepAspectRatio(): boolean {
return this.stretch === "fill" ? false : true;
}
private _calculateKeepAspectRatio(): boolean {
return this.stretch === 'fill' ? false : true;
}
[stretchProperty.getDefault](): "aspectFit" {
return "aspectFit";
}
[stretchProperty.setNative](
value: "none" | "aspectFill" | "aspectFit" | "fill"
) {
switch (value) {
case "aspectFit":
this.nativeViewProtected.setScaleType(
android.widget.ImageView.ScaleType.FIT_CENTER
);
break;
case "aspectFill":
this.nativeViewProtected.setScaleType(
android.widget.ImageView.ScaleType.CENTER_CROP
);
break;
case "fill":
this.nativeViewProtected.setScaleType(
android.widget.ImageView.ScaleType.FIT_XY
);
break;
case "none":
default:
this.nativeViewProtected.setScaleType(
android.widget.ImageView.ScaleType.MATRIX
);
break;
}
}
[stretchProperty.getDefault](): 'aspectFit' {
return 'aspectFit';
}
[stretchProperty.setNative](value: 'none' | 'aspectFill' | 'aspectFit' | 'fill') {
switch (value) {
case 'aspectFit':
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.FIT_CENTER);
break;
case 'aspectFill':
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.CENTER_CROP);
break;
case 'fill':
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.FIT_XY);
break;
case 'none':
default:
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.MATRIX);
break;
}
}
[tintColorProperty.getDefault](): Color {
return undefined;
}
[tintColorProperty.setNative](value: Color) {
if (value === undefined) {
this.nativeViewProtected.clearColorFilter();
} else {
this.nativeViewProtected.setColorFilter(value.android);
}
}
[tintColorProperty.getDefault](): Color {
return undefined;
}
[tintColorProperty.setNative](value: Color) {
if (value === undefined) {
this.nativeViewProtected.clearColorFilter();
} else {
this.nativeViewProtected.setColorFilter(value.android);
}
}
[imageSourceProperty.getDefault](): ImageSource {
return undefined;
}
[imageSourceProperty.setNative](value: ImageSource) {
const nativeView = this.nativeViewProtected;
if (value && value.android) {
const rotation = value.rotationAngle ? value.rotationAngle : 0;
nativeView.setRotationAngle(rotation);
nativeView.setImageBitmap(value.android);
} else {
nativeView.setRotationAngle(0);
nativeView.setImageBitmap(null);
}
}
[imageSourceProperty.getDefault](): ImageSource {
return undefined;
}
[imageSourceProperty.setNative](value: ImageSource) {
const nativeView = this.nativeViewProtected;
if (value && value.android) {
const rotation = value.rotationAngle ? value.rotationAngle : 0;
nativeView.setRotationAngle(rotation);
nativeView.setImageBitmap(value.android);
} else {
nativeView.setRotationAngle(0);
nativeView.setImageBitmap(null);
}
}
[srcProperty.getDefault](): any {
return undefined;
}
[srcProperty.setNative](value: any) {
this._createImageSourceFromSrc(value);
}
[srcProperty.getDefault](): any {
return undefined;
}
[srcProperty.setNative](value: any) {
this._createImageSourceFromSrc(value);
}
}

View File

@@ -1,76 +1,76 @@
import { View } from "../core/view";
import { Style } from "../styling/style";
import { ImageSource } from "../../image-source";
import { Color } from "../../color";
import { Property, InheritedCssProperty } from "../core/properties";
import { Length } from "../styling/style-properties";
import { View } from '../core/view';
import { Style } from '../styling/style';
import { ImageSource } from '../../image-source';
import { Color } from '../../color';
import { Property, InheritedCssProperty } from '../core/properties';
import { Length } from '../styling/style-properties';
/**
* Represents a class that provides functionality for loading and streching image(s).
*/
export class Image extends View {
/**
* Gets the native [android widget](http://developer.android.com/reference/android/widget/ImageView.html) that represents the user interface for this component. Valid only when running on Android OS.
*/
android: any /* android.widget.ImageView */;
/**
* Gets the native [android widget](http://developer.android.com/reference/android/widget/ImageView.html) that represents the user interface for this component. Valid only when running on Android OS.
*/
android: any /* android.widget.ImageView */;
/**
* Gets the native iOS [UIImageView](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImageView_Class/) that represents the user interface for this component. Valid only when running on iOS.
*/
ios: any /* UIImageView */;
/**
* Gets the native iOS [UIImageView](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImageView_Class/) that represents the user interface for this component. Valid only when running on iOS.
*/
ios: any /* UIImageView */;
/**
* Gets or sets the image source of the image.
*/
imageSource: ImageSource;
/**
* Gets or sets the image source of the image.
*/
imageSource: ImageSource;
/**
* Gets or sets the source of the Image. This can be either an URL string or a native image instance.
*/
src: any;
/**
* Gets or sets the source of the Image. This can be either an URL string or a native image instance.
*/
src: any;
/**
* Gets a value indicating if the image is currently loading.
*/
readonly isLoading: boolean;
/**
* Gets a value indicating if the image is currently loading.
*/
readonly isLoading: boolean;
/**
* Gets or sets the image stretch mode.
*/
stretch: Stretch;
/**
* Gets or sets the image stretch mode.
*/
stretch: Stretch;
/**
* Gets or sets the loading strategy for images on the local file system:
* - **sync** - blocks the UI if necessary to display immediately, good for small icons.
* - **async** *(default)* - will load in the background, may appear with short delay, good for large images.
* When loading images from web they are always loaded **async** no matter of loadMode value.
*/
loadMode: "sync" | "async";
/**
* Gets or sets the loading strategy for images on the local file system:
* - **sync** - blocks the UI if necessary to display immediately, good for small icons.
* - **async** *(default)* - will load in the background, may appear with short delay, good for large images.
* When loading images from web they are always loaded **async** no matter of loadMode value.
*/
loadMode: 'sync' | 'async';
/**
* A color used to tint template images.
*/
tintColor: Color;
/**
* A color used to tint template images.
*/
tintColor: Color;
/**
* Gets or sets the desired decode height of the image.
* This property is Android specific.
*/
decodeHeight: Length;
/**
* Gets or sets the desired decode height of the image.
* This property is Android specific.
*/
decodeHeight: Length;
/**
* Gets or sets the desired decode width of the image.
* This property is Android specific.
*/
decodeWidth: Length;
/**
* Gets or sets the desired decode width of the image.
* This property is Android specific.
*/
decodeWidth: Length;
}
export type Stretch = "none" | "aspectFill" | "aspectFit" | "fill";
export type Stretch = 'none' | 'aspectFill' | 'aspectFit' | 'fill';
export const imageSourceProperty: Property<Image, ImageSource>;
export const srcProperty: Property<Image, any>;
export const isLoadingProperty: Property<Image, string>;
export const loadMode: Property<Image, "sync" | "async">;
export const loadMode: Property<Image, 'sync' | 'async'>;
export const stretchProperty: Property<Image, Stretch>;
export const tintColorProperty: InheritedCssProperty<Style, Color>;
export const decodeHeightProperty: Property<Image, Length>;

View File

@@ -1,228 +1,153 @@
import {
ImageBase,
stretchProperty,
imageSourceProperty,
tintColorProperty,
srcProperty,
} from "./image-common";
import { ImageSource } from "../../image-source";
import { Color } from "../../color";
import { Trace } from "../../trace";
import { layout } from "../../utils/utils";
import { ImageBase, stretchProperty, imageSourceProperty, tintColorProperty, srcProperty } from './image-common';
import { ImageSource } from '../../image-source';
import { Color } from '../../color';
import { Trace } from '../../trace';
import { layout } from '../../utils';
export * from "./image-common";
export * from './image-common';
export class Image extends ImageBase {
nativeViewProtected: UIImageView;
private _imageSourceAffectsLayout: boolean = true;
private _templateImageWasCreated: boolean;
nativeViewProtected: UIImageView;
private _imageSourceAffectsLayout: boolean = true;
private _templateImageWasCreated: boolean;
public createNativeView() {
const imageView = UIImageView.new();
imageView.contentMode = UIViewContentMode.ScaleAspectFit;
imageView.userInteractionEnabled = true;
public createNativeView() {
const imageView = UIImageView.new();
imageView.contentMode = UIViewContentMode.ScaleAspectFit;
imageView.userInteractionEnabled = true;
return imageView;
}
return imageView;
}
public initNativeView(): void {
super.initNativeView();
this._setNativeClipToBounds();
}
public initNativeView(): void {
super.initNativeView();
this._setNativeClipToBounds();
}
private setTintColor(value: Color) {
if (
value &&
this.nativeViewProtected.image &&
!this._templateImageWasCreated
) {
this.nativeViewProtected.image = this.nativeViewProtected.image.imageWithRenderingMode(
UIImageRenderingMode.AlwaysTemplate
);
this._templateImageWasCreated = true;
} else if (
!value &&
this.nativeViewProtected.image &&
this._templateImageWasCreated
) {
this._templateImageWasCreated = false;
this.nativeViewProtected.image = this.nativeViewProtected.image.imageWithRenderingMode(
UIImageRenderingMode.Automatic
);
}
this.nativeViewProtected.tintColor = value ? value.ios : null;
}
private setTintColor(value: Color) {
if (value && this.nativeViewProtected.image && !this._templateImageWasCreated) {
this.nativeViewProtected.image = this.nativeViewProtected.image.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
this._templateImageWasCreated = true;
} else if (!value && this.nativeViewProtected.image && this._templateImageWasCreated) {
this._templateImageWasCreated = false;
this.nativeViewProtected.image = this.nativeViewProtected.image.imageWithRenderingMode(UIImageRenderingMode.Automatic);
}
this.nativeViewProtected.tintColor = value ? value.ios : null;
}
public _setNativeImage(nativeImage: UIImage) {
this.nativeViewProtected.image = nativeImage;
this._templateImageWasCreated = false;
this.setTintColor(this.style.tintColor);
public _setNativeImage(nativeImage: UIImage) {
this.nativeViewProtected.image = nativeImage;
this._templateImageWasCreated = false;
this.setTintColor(this.style.tintColor);
if (this._imageSourceAffectsLayout) {
this.requestLayout();
}
}
if (this._imageSourceAffectsLayout) {
this.requestLayout();
}
}
_setNativeClipToBounds() {
// Always set clipsToBounds for images
this.nativeViewProtected.clipsToBounds = true;
}
_setNativeClipToBounds() {
// Always set clipsToBounds for images
this.nativeViewProtected.clipsToBounds = true;
}
public onMeasure(
widthMeasureSpec: number,
heightMeasureSpec: number
): void {
// We don't call super because we measure native view with specific size.
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
// We don't call super because we measure native view with specific size.
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
const nativeWidth = this.imageSource
? layout.toDevicePixels(this.imageSource.width)
: 0;
const nativeHeight = this.imageSource
? layout.toDevicePixels(this.imageSource.height)
: 0;
const nativeWidth = this.imageSource ? layout.toDevicePixels(this.imageSource.width) : 0;
const nativeHeight = this.imageSource ? layout.toDevicePixels(this.imageSource.height) : 0;
let measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
let measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
let measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
let measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
const finiteWidth: boolean = widthMode !== layout.UNSPECIFIED;
const finiteHeight: boolean = heightMode !== layout.UNSPECIFIED;
const finiteWidth: boolean = widthMode !== layout.UNSPECIFIED;
const finiteHeight: boolean = heightMode !== layout.UNSPECIFIED;
this._imageSourceAffectsLayout =
widthMode !== layout.EXACTLY || heightMode !== layout.EXACTLY;
this._imageSourceAffectsLayout = widthMode !== layout.EXACTLY || heightMode !== layout.EXACTLY;
if (
nativeWidth !== 0 &&
nativeHeight !== 0 &&
(finiteWidth || finiteHeight)
) {
const scale = Image.computeScaleFactor(
width,
height,
finiteWidth,
finiteHeight,
nativeWidth,
nativeHeight,
this.stretch
);
const resultW = Math.round(nativeWidth * scale.width);
const resultH = Math.round(nativeHeight * scale.height);
if (nativeWidth !== 0 && nativeHeight !== 0 && (finiteWidth || finiteHeight)) {
const scale = Image.computeScaleFactor(width, height, finiteWidth, finiteHeight, nativeWidth, nativeHeight, this.stretch);
const resultW = Math.round(nativeWidth * scale.width);
const resultH = Math.round(nativeHeight * scale.height);
measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
if (Trace.isEnabled()) {
Trace.write(
"Image stretch: " +
this.stretch +
", nativeWidth: " +
nativeWidth +
", nativeHeight: " +
nativeHeight,
Trace.categories.Layout
);
}
}
if (Trace.isEnabled()) {
Trace.write('Image stretch: ' + this.stretch + ', nativeWidth: ' + nativeWidth + ', nativeHeight: ' + nativeHeight, Trace.categories.Layout);
}
}
const widthAndState = Image.resolveSizeAndState(
measureWidth,
width,
widthMode,
0
);
const heightAndState = Image.resolveSizeAndState(
measureHeight,
height,
heightMode,
0
);
const widthAndState = Image.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightAndState = Image.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
this.setMeasuredDimension(widthAndState, heightAndState);
}
private static computeScaleFactor(
measureWidth: number,
measureHeight: number,
widthIsFinite: boolean,
heightIsFinite: boolean,
nativeWidth: number,
nativeHeight: number,
imageStretch: string
): { width: number; height: number } {
let scaleW = 1;
let scaleH = 1;
private static computeScaleFactor(measureWidth: number, measureHeight: number, widthIsFinite: boolean, heightIsFinite: boolean, nativeWidth: number, nativeHeight: number, imageStretch: string): { width: number; height: number } {
let scaleW = 1;
let scaleH = 1;
if (
(imageStretch === "aspectFill" ||
imageStretch === "aspectFit" ||
imageStretch === "fill") &&
(widthIsFinite || heightIsFinite)
) {
scaleW = nativeWidth > 0 ? measureWidth / nativeWidth : 0;
scaleH = nativeHeight > 0 ? measureHeight / nativeHeight : 0;
if ((imageStretch === 'aspectFill' || imageStretch === 'aspectFit' || imageStretch === 'fill') && (widthIsFinite || heightIsFinite)) {
scaleW = nativeWidth > 0 ? measureWidth / nativeWidth : 0;
scaleH = nativeHeight > 0 ? measureHeight / nativeHeight : 0;
if (!widthIsFinite) {
scaleW = scaleH;
} else if (!heightIsFinite) {
scaleH = scaleW;
} else {
// No infinite dimensions.
switch (imageStretch) {
case "aspectFit":
scaleH = scaleW < scaleH ? scaleW : scaleH;
scaleW = scaleH;
break;
case "aspectFill":
scaleH = scaleW > scaleH ? scaleW : scaleH;
scaleW = scaleH;
break;
}
}
}
if (!widthIsFinite) {
scaleW = scaleH;
} else if (!heightIsFinite) {
scaleH = scaleW;
} else {
// No infinite dimensions.
switch (imageStretch) {
case 'aspectFit':
scaleH = scaleW < scaleH ? scaleW : scaleH;
scaleW = scaleH;
break;
case 'aspectFill':
scaleH = scaleW > scaleH ? scaleW : scaleH;
scaleW = scaleH;
break;
}
}
}
return { width: scaleW, height: scaleH };
}
return { width: scaleW, height: scaleH };
}
[stretchProperty.setNative](
value: "none" | "aspectFill" | "aspectFit" | "fill"
) {
switch (value) {
case "aspectFit":
this.nativeViewProtected.contentMode =
UIViewContentMode.ScaleAspectFit;
break;
[stretchProperty.setNative](value: 'none' | 'aspectFill' | 'aspectFit' | 'fill') {
switch (value) {
case 'aspectFit':
this.nativeViewProtected.contentMode = UIViewContentMode.ScaleAspectFit;
break;
case "aspectFill":
this.nativeViewProtected.contentMode =
UIViewContentMode.ScaleAspectFill;
break;
case 'aspectFill':
this.nativeViewProtected.contentMode = UIViewContentMode.ScaleAspectFill;
break;
case "fill":
this.nativeViewProtected.contentMode =
UIViewContentMode.ScaleToFill;
break;
case 'fill':
this.nativeViewProtected.contentMode = UIViewContentMode.ScaleToFill;
break;
case "none":
default:
this.nativeViewProtected.contentMode =
UIViewContentMode.TopLeft;
break;
}
}
case 'none':
default:
this.nativeViewProtected.contentMode = UIViewContentMode.TopLeft;
break;
}
}
[tintColorProperty.setNative](value: Color) {
this.setTintColor(value);
}
[tintColorProperty.setNative](value: Color) {
this.setTintColor(value);
}
[imageSourceProperty.setNative](value: ImageSource) {
this._setNativeImage(value ? value.ios : null);
}
[imageSourceProperty.setNative](value: ImageSource) {
this._setNativeImage(value ? value.ios : null);
}
[srcProperty.setNative](value: any) {
this._createImageSourceFromSrc(value);
}
[srcProperty.setNative](value: any) {
this._createImageSourceFromSrc(value);
}
}