feat: add font icons to image actionbar and tab navigation (#7498)

This commit is contained in:
Martin Yankov
2019-07-10 21:05:45 +03:00
committed by Manol Donev
parent aa1c160465
commit d8262a624e
29 changed files with 416 additions and 36 deletions

View File

@ -0,0 +1,11 @@
.font-awesome {
font-family: "FontAwesome";
}
.font-size {
font-size: 48;
}
.color {
color: blue;
}

View File

@ -0,0 +1,17 @@
import * as frame from "tns-core-modules/ui/frame";
import { EventData } from "tns-core-modules/ui/frame";
import { Button } from "tns-core-modules/ui/button";
import { ActionBar } from "tns-core-modules/ui/action-bar";
const iconModes = ["automatic", "alwaysOriginal", "alwaysTemplate", undefined];
export function navigate(args) {
frame.topmost().navigate("ui-tests-app/action-bar/clean");
}
export function onChangeRenderingMode(args: EventData) {
const button = <Button>args.object;
const actionBar = <ActionBar>button.page.actionBar;
actionBar.iosIconRenderingMode = <"automatic" | "alwaysOriginal" | "alwaysTemplate">iconModes[(iconModes.indexOf(actionBar.iosIconRenderingMode) + 1) % iconModes.length];
button.text = "" + actionBar.iosIconRenderingMode;
}

View File

@ -0,0 +1,19 @@
<Page>
<Page.actionBar>
<ActionBar>
<ActionBar.actionItems>
<!-- font family + font size + color -->
<ActionItem icon="font://&#xF10B;" class="font-awesome font-size color" tap="navigate"/>
<!-- default font + valid char code -->
<ActionItem icon="font://&#xF10B;" tap="navigate"/>
<!-- font family + invalid char code -->
<ActionItem icon="font://&#xF556;" class="font-awesome font-size" tap="navigate"/>
</ActionBar.actionItems>
</ActionBar>
</Page.actionBar>
<StackLayout>
<Button text="go to cleared page" tap="navigate"/>
<Button text="undefined" tap="onChangeRenderingMode"/>
</StackLayout>
</Page>

View File

@ -23,6 +23,7 @@ export function loadExamples() {
examples.set("actTransparentBgCss", "action-bar/transparent-bg-css-page"); examples.set("actTransparentBgCss", "action-bar/transparent-bg-css-page");
examples.set("modalHiddenActBar", "action-bar/modal-test-hidden-action-bar-page"); examples.set("modalHiddenActBar", "action-bar/modal-test-hidden-action-bar-page");
examples.set("modalShownActBar", "action-bar/modal-test-with-action-bar-page"); examples.set("modalShownActBar", "action-bar/modal-test-with-action-bar-page");
examples.set("font-icons", "action-bar/font-icons-page");
examples.set("flat", "action-bar/flat-page"); examples.set("flat", "action-bar/flat-page");
examples.set("flat-tab", "action-bar/flat-tab-page"); examples.set("flat-tab", "action-bar/flat-tab-page");
examples.set("flat-tab-opaque-bar", "action-bar/flat-tab-opaque-bar-page"); examples.set("flat-tab-opaque-bar", "action-bar/flat-tab-opaque-bar-page");

View File

@ -0,0 +1,11 @@
.font-awesome {
font-family: "FontAwesome";
}
.font-size {
font-size: 36;
}
.color {
color: blue;
}

View File

@ -0,0 +1,34 @@
<Page class="page">
<ActionBar title="BottomNavigation font icons" icon="" class="action-bar">
</ActionBar>
<BottomNavigation class="font-awesome"> <!-- TODO: The font-awesome class here should be removed -->
<TabStrip>
<!-- font family + font size + color -->
<TabStripItem title="First" iconSource="font://&#xF10B;" class="special font-awesome font-size color"></TabStripItem>
<!-- default font + valid char code -->
<TabStripItem title="Second" iconSource="font://&#xF10B;"></TabStripItem>
<!-- font family + invalid char code -->
<TabStripItem title="Third" iconSource="font://&#xF556;" class="font-awesome font-size"></TabStripItem>
</TabStrip>
<TabContentItem class="special">
<GridLayout>
<Label text="First View" />
</GridLayout>
</TabContentItem>
<TabContentItem>
<GridLayout>
<Label text="Second View" />
</GridLayout>
</TabContentItem>
<TabContentItem>
<GridLayout>
<Label text="Third View" />
</GridLayout>
</TabContentItem>
</BottomNavigation>
</Page>

View File

@ -20,6 +20,7 @@ export function loadExamples() {
examples.set("icon-title-placement", "bottom-navigation/icon-title-placement-page"); examples.set("icon-title-placement", "bottom-navigation/icon-title-placement-page");
examples.set("icon-change", "bottom-navigation/icon-change-page"); examples.set("icon-change", "bottom-navigation/icon-change-page");
examples.set("binding", "bottom-navigation/binding-page"); examples.set("binding", "bottom-navigation/binding-page");
examples.set("font-icons", "bottom-navigation/font-icons-page");
return examples; return examples;
} }

View File

@ -0,0 +1,11 @@
.font-awesome {
font-family: "FontAwesome";
}
.font-size {
font-size: 96;
}
.color {
color: blue;
}

View File

@ -0,0 +1,16 @@
<Page navigatingTo="navigatingTo">
<GridLayout rows="*, *, *" columns="*, *" height="200">
<!-- font family only -->
<Image row="0" col="0" src="font://&#xF10B;" class="font-awesome"/>
<!-- font family + font size -->
<Image row="0" col="1" src="font://&#xF10B;" class="font-awesome font-size"/>
<!-- font family + color -->
<Image row="1" col="0" src="font://&#xF10B;" class="font-awesome color"/>
<!-- font family + font size + color -->
<Image row="1" col="1" src="font://&#xF10B;" class="font-awesome font-size color"/>
<!-- default font + valid char code -->
<Image row="2" col="0" src="font://&#xF10B;" />
<!-- font family + invalid char code -->
<Image row="2" col="1" src="font://&#xF556;" class="font-awesome"/>
</GridLayout>
</Page>

View File

@ -17,6 +17,7 @@ export function loadExamples() {
examples.set("stretch-modes", "image-view/stretch-modes-page"); examples.set("stretch-modes", "image-view/stretch-modes-page");
examples.set("missing-image", "image-view/missing-image-page"); examples.set("missing-image", "image-view/missing-image-page");
examples.set("image-asset", "image-view/image-asset/image-asset-page"); examples.set("image-asset", "image-view/image-asset/image-asset-page");
examples.set("font-icons", "image-view/font-icons-page");
return examples; return examples;
} }

View File

@ -0,0 +1,11 @@
.font-awesome {
font-family: "FontAwesome";
}
.font-size {
font-size: 36;
}
.color {
color: blue;
}

View File

@ -0,0 +1,34 @@
<Page class="page">
<ActionBar title="Tabs font icons" icon="" class="action-bar">
</ActionBar>
<Tabs class="font-awesome"> <!-- TODO: The font-awesome class here should be removed -->
<TabStrip>
<!-- font family + font size + color -->
<TabStripItem title="First" iconSource="font://&#xF10B;" class="special font-awesome font-size color"></TabStripItem>
<!-- default font + valid char code -->
<TabStripItem title="Second" iconSource="font://&#xF10B;"></TabStripItem>
<!-- font family + invalid char code -->
<TabStripItem title="Third" iconSource="font://&#xF556;" class="font-awesome font-size"></TabStripItem>
</TabStrip>
<TabContentItem class="special">
<GridLayout>
<Label text="First View" />
</GridLayout>
</TabContentItem>
<TabContentItem>
<GridLayout>
<Label text="Second View" />
</GridLayout>
</TabContentItem>
<TabContentItem>
<GridLayout>
<Label text="Third View" />
</GridLayout>
</TabContentItem>
</Tabs>
</Page>

View File

@ -26,6 +26,7 @@ export function loadExamples() {
examples.set("strip-items", "tabs/tab-strip-items-page"); examples.set("strip-items", "tabs/tab-strip-items-page");
examples.set("tabs-position", "tabs/tabs-position-page"); examples.set("tabs-position", "tabs/tabs-position-page");
examples.set("tabs-binding", "tabs/tabs-binding-page"); examples.set("tabs-binding", "tabs/tabs-binding-page");
examples.set("font-icons", "tabs/font-icons-page");
return examples; return examples;
} }

Binary file not shown.

View File

@ -3,6 +3,8 @@ import * as imageAssetModule from "tns-core-modules/image-asset";
import * as fs from "tns-core-modules/file-system"; import * as fs from "tns-core-modules/file-system";
import * as app from "tns-core-modules/application"; import * as app from "tns-core-modules/application";
import * as TKUnit from "../tk-unit"; import * as TKUnit from "../tk-unit";
import { Font } from "tns-core-modules/ui/styling/font";
import { Color } from "tns-core-modules/color";
const imagePath = "~/assets/logo.png"; const imagePath = "~/assets/logo.png";
const splashscreenPath = "~/assets/splashscreen.png"; const splashscreenPath = "~/assets/splashscreen.png";
@ -285,3 +287,12 @@ export function testLoadFromBase64Encode_PNG() {
TKUnit.assertEqual(img.width, 4, "img.width"); TKUnit.assertEqual(img.width, 4, "img.width");
TKUnit.assertEqual(img.height, 4, "img.height"); TKUnit.assertEqual(img.height, 4, "img.height");
} }
export function testLoadFromFontIconCode() {
let img: imageSource.ImageSource;
img = imageSource.fromFontIconCode("F10B", Font.default.withFontFamily("FontAwesome"), new Color("red"));
TKUnit.assert(img !== null, "Actual: " + img);
TKUnit.assert(img.width !== null, "img.width");
TKUnit.assert(img.height !== null, "img.width");
}

View File

@ -136,6 +136,14 @@ export const test_SettingImageSrcToDataURI_async = function (done) {
runImageTestAsync(image, image.src, done); runImageTestAsync(image, image.src, done);
}; };
export const test_SettingImageSrcToFontIconCode_sync = function () {
const image = new ImageModule.Image();
image.style.fontFamily = "FontAwesome";
image.src = "font://&#xF10B";
runImageTestSync(image, image.src);
};
export function test_imageSourceNotResetAfterCreateUI() { export function test_imageSourceNotResetAfterCreateUI() {
let image = new ImageModule.Image(); let image = new ImageModule.Image();
let imageSource = ImageSourceModule.fromResource("splashscreen"); let imageSource = ImageSourceModule.fromResource("splashscreen");

View File

@ -5,8 +5,10 @@ import * as httpModule from "../http";
// Types. // Types.
import { path as fsPath, knownFolders } from "../file-system"; import { path as fsPath, knownFolders } from "../file-system";
import { isFileOrResourcePath, RESOURCE_PREFIX } from "../utils/utils"; import { isFileOrResourcePath, RESOURCE_PREFIX, layout } from "../utils/utils";
import { getNativeApplication } from "../application"; import { getNativeApplication } from "../application";
import { Font } from "../ui/styling/font";
import { Color } from "../color";
export { isFileOrResourcePath }; export { isFileOrResourcePath };
@ -140,6 +142,43 @@ export class ImageSource implements ImageSourceDefinition {
}); });
} }
public loadFromFontIconCode(source: string, font: Font, color: Color): boolean {
const paint = new android.graphics.Paint();
paint.setTypeface(font.getAndroidTypeface());
paint.setAntiAlias(true);
if (color) {
paint.setColor(color.android);
}
let fontSize = layout.toDevicePixels(font.fontSize);
if (!fontSize) {
// TODO: Consider making 36 font size as default for optimal look on TabView and ActionBar
fontSize = paint.getTextSize();
}
const density = layout.getDisplayDensity();
const scaledFontSize = fontSize * density;
paint.setTextSize(scaledFontSize);
const textBounds = new android.graphics.Rect();
paint.getTextBounds(source, 0, source.length, textBounds);
const bitmap = android.graphics.Bitmap
.createBitmap(
textBounds.width(),
textBounds.height(),
android.graphics.Bitmap.Config.ARGB_8888
);
const canvas = new android.graphics.Canvas(bitmap);
canvas.drawText(source, -textBounds.left, -textBounds.top, paint);
this.android = bitmap;
return this.android != null;
}
public setNativeSource(source: any): void { public setNativeSource(source: any): void {
if (source && !(source instanceof android.graphics.Bitmap)) { if (source && !(source instanceof android.graphics.Bitmap)) {
throw new Error("The method setNativeSource() expects android.graphics.Bitmap instance."); throw new Error("The method setNativeSource() expects android.graphics.Bitmap instance.");
@ -241,6 +280,12 @@ export function fromData(data: any): ImageSource {
return image.loadFromData(data) ? image : null; return image.loadFromData(data) ? image : null;
} }
export function fromFontIconCode(source: string, font: Font, color: Color): ImageSource {
const image = new ImageSource();
return image.loadFromFontIconCode(source, font, color) ? image : null;
}
export function fromBase64(source: string): ImageSource { export function fromBase64(source: string): ImageSource {
const image = new ImageSource(); const image = new ImageSource();

View File

@ -4,6 +4,8 @@
*/ /** */ */ /** */
import * as imageAssetModule from "../image-asset"; import * as imageAssetModule from "../image-asset";
import { Font } from "../ui/styling/font";
import { Color } from "../color";
/** /**
* Encapsulates the common abstraction behind a platform specific object (typically a Bitmap) that is used as a source for images. * Encapsulates the common abstraction behind a platform specific object (typically a Bitmap) that is used as a source for images.
*/ */
@ -87,6 +89,14 @@ export class ImageSource {
*/ */
fromBase64(source: string): Promise<boolean>; fromBase64(source: string): Promise<boolean>;
/**
* Loads this instance from the specified font icon code.
* @param source The hex font icon code string
* @param font The font for the corresponding font icon code
* @param color The color of the generated icon image
*/
loadFromFontIconCode(source: string, font: Font, color: Color): boolean;
/** /**
* Sets the provided native source object (typically a Bitmap or a UIImage). * Sets the provided native source object (typically a Bitmap or a UIImage).
* This will update either the android or ios properties, depending on the target os. * This will update either the android or ios properties, depending on the target os.
@ -147,6 +157,14 @@ export function fromBase64(source: string): ImageSource;
*/ */
export function fromNativeSource(source: any): ImageSource; export function fromNativeSource(source: any): ImageSource;
/**
* Creates a new ImageSource instance and loads it from the specified font icon code.
* @param source The hex font icon code string
* @param font The font for the corresponding font icon code
* @param color The color of the generated icon image
*/
export function fromFontIconCode(source: string, font: Font, color: Color): ImageSource;
/** /**
* Downloads the image from the provided Url and creates a new ImageSource instance from it. * Downloads the image from the provided Url and creates a new ImageSource instance from it.
* @param url The link to the remote image object. This operation will download and decode the image. * @param url The link to the remote image object. This operation will download and decode the image.

View File

@ -2,10 +2,12 @@
import { ImageSource as ImageSourceDefinition } from "."; import { ImageSource as ImageSourceDefinition } from ".";
import { ImageAsset } from "../image-asset"; import { ImageAsset } from "../image-asset";
import * as httpModule from "../http"; import * as httpModule from "../http";
import { Font } from "../ui/styling/font";
import { Color } from "../color";
// Types. // Types.
import { path as fsPath, knownFolders } from "../file-system"; import { path as fsPath, knownFolders } from "../file-system";
import { isFileOrResourcePath, RESOURCE_PREFIX } from "../utils/utils"; import { isFileOrResourcePath, RESOURCE_PREFIX, layout } from "../utils/utils";
export { isFileOrResourcePath }; export { isFileOrResourcePath };
@ -121,6 +123,37 @@ export class ImageSource implements ImageSourceDefinition {
}); });
} }
public loadFromFontIconCode(source: string, font: Font, color: Color): boolean {
let fontSize = layout.toDevicePixels(font.fontSize);
if (!fontSize) {
// TODO: Consider making 36 font size as default for optimal look on TabView and ActionBar
fontSize = UIFont.labelFontSize;
}
const density = layout.getDisplayDensity();
const scaledFontSize = fontSize * density;
const attributes = {
[NSFontAttributeName]: font.getUIFont(UIFont.systemFontOfSize(scaledFontSize))
};
if (color) {
attributes[NSForegroundColorAttributeName] = color.ios;
}
const attributedString = NSAttributedString.alloc().initWithStringAttributes(source, <NSDictionary<string, any>>attributes);
UIGraphicsBeginImageContextWithOptions(attributedString.size(), false, 0.0);
attributedString.drawAtPoint(CGPointMake(0, 0));
const iconImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
this.ios = iconImage;
return this.ios != null;
}
public setNativeSource(source: any): void { public setNativeSource(source: any): void {
if (source && !(source instanceof UIImage)) { if (source && !(source instanceof UIImage)) {
throw new Error("The method setNativeSource() expects UIImage instance."); throw new Error("The method setNativeSource() expects UIImage instance.");
@ -231,6 +264,12 @@ export function fromFile(path: string): ImageSource {
return image.loadFromFile(path) ? image : null; return image.loadFromFile(path) ? image : null;
} }
export function fromFontIconCode(source: string, font: Font, color: Color): ImageSource {
const image = new ImageSource();
return image.loadFromFontIconCode(source, font, color) ? image : null;
}
export function fromData(data: any): ImageSource { export function fromData(data: any): ImageSource {
const image = new ImageSource(); const image = new ImageSource();

View File

@ -4,8 +4,8 @@ import {
View, layout, colorProperty, flatProperty, View, layout, colorProperty, flatProperty,
Color, traceMissingIcon Color, traceMissingIcon
} from "./action-bar-common"; } from "./action-bar-common";
import { RESOURCE_PREFIX } from "../../utils/utils"; import { RESOURCE_PREFIX, isFontIconURI } from "../../utils/utils";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource, fromFontIconCode } from "../../image-source";
import * as application from "../../application"; import * as application from "../../application";
export * from "./action-bar-common"; export * from "./action-bar-common";
@ -311,11 +311,23 @@ export class ActionBar extends ActionBarBase {
} }
} }
else if (item.icon) { else if (item.icon) {
if (isFontIconURI(item.icon)) {
const fontIconCode = item.icon.split("//")[1];
const font = item.style.fontInternal;
const color = item.style.color;
const is = fromFontIconCode(fontIconCode, font, color);
if (is && is.android) {
const drawable = new android.graphics.drawable.BitmapDrawable(appResources, is.android);
menuItem.setIcon(drawable);
}
} else {
let drawableOrId = getDrawableOrResourceId(item.icon, appResources); let drawableOrId = getDrawableOrResourceId(item.icon, appResources);
if (drawableOrId) { if (drawableOrId) {
menuItem.setIcon(drawableOrId); menuItem.setIcon(drawableOrId);
} }
} }
}
let showAsAction = getShowAsAction(item); let showAsAction = getShowAsAction(item);
menuItem.setShowAsAction(showAsAction); menuItem.setShowAsAction(showAsAction);

View File

@ -4,8 +4,8 @@ import {
colorProperty, backgroundColorProperty, colorProperty, backgroundColorProperty,
backgroundInternalProperty, flatProperty, iosIconRenderingModeProperty, backgroundInternalProperty, flatProperty, iosIconRenderingModeProperty,
layout, Color, traceMissingIcon } from "./action-bar-common"; layout, Color, traceMissingIcon } from "./action-bar-common";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource, fromFontIconCode } from "../../image-source";
import { ios as iosUtils } from "../../utils/utils"; import { ios as iosUtils, isFontIconURI } from "../../utils/utils";
export * from "./action-bar-common"; export * from "./action-bar-common";
@ -255,7 +255,23 @@ export class ActionBar extends ActionBarBase {
barButtonItem = UIBarButtonItem.alloc().initWithBarButtonSystemItemTargetAction(id, tapHandler, "tap"); barButtonItem = UIBarButtonItem.alloc().initWithBarButtonSystemItemTargetAction(id, tapHandler, "tap");
} else if (item.icon) { } else if (item.icon) {
const img = loadActionIconFromFileOrResource(item.icon); let img = null;
if (isFontIconURI(item.icon)) {
const fontIconCode = item.icon.split("//")[1];
const font = item.style.fontInternal;
const color = item.style.color;
const is = fromFontIconCode(fontIconCode, font, color);
if (is && is.ios) {
img = is.ios;
} else {
traceMissingIcon(item.icon);
}
} else {
img = loadActionIconFromFileOrResource(item.icon);
}
const image = img.imageWithRenderingMode(this._getIconRenderingMode()); const image = img.imageWithRenderingMode(this._getIconRenderingMode());
barButtonItem = UIBarButtonItem.alloc().initWithImageStyleTargetAction(image, UIBarButtonItemStyle.Plain, tapHandler, "tap"); barButtonItem = UIBarButtonItem.alloc().initWithImageStyleTargetAction(image, UIBarButtonItemStyle.Plain, tapHandler, "tap");
} else { } else {

View File

@ -10,8 +10,8 @@ import { Font } from "../styling/font";
import { getTransformedText } from "../text-base"; import { getTransformedText } from "../text-base";
import { CSSType, Color } from "../core/view"; import { CSSType, Color } from "../core/view";
import { Frame, View } from "../frame"; import { Frame, View } from "../frame";
import { RESOURCE_PREFIX, ad, layout } from "../../utils/utils"; import { RESOURCE_PREFIX, ad, layout, isFontIconURI } from "../../utils/utils";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource, fromFontIconCode, ImageSource } from "../../image-source";
import * as application from "../../application"; import * as application from "../../application";
// TODO: Impl trace // TODO: Impl trace
@ -177,7 +177,16 @@ function createTabItemSpec(tabStripItem: TabStripItem): org.nativescript.widgets
// traceMissingIcon(iconSource); // traceMissingIcon(iconSource);
} }
} else { } else {
const is = fromFileOrResource(tabStripItem.iconSource); let is = new ImageSource();
if (isFontIconURI(tabStripItem.iconSource)) {
const fontIconCode = tabStripItem.iconSource.split("//")[1];
const font = tabStripItem.style.fontInternal;
const color = tabStripItem.style.color;
is = fromFontIconCode(fontIconCode, font, color);
} else {
is = fromFileOrResource(tabStripItem.iconSource);
}
if (is) { if (is) {
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS. // TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
// tslint:disable-next-line:deprecation // tslint:disable-next-line:deprecation

View File

@ -1,4 +1,4 @@
// Types // Types
import { TabContentItem } from "../tab-navigation-base/tab-content-item"; import { TabContentItem } from "../tab-navigation-base/tab-content-item";
import { TabStripItem } from "../tab-navigation-base/tab-strip-item"; import { TabStripItem } from "../tab-navigation-base/tab-strip-item";
import { TextTransform } from "../text-base"; import { TextTransform } from "../text-base";
@ -9,10 +9,10 @@ import { Font } from "../styling/font";
import { getTransformedText } from "../text-base"; import { getTransformedText } from "../text-base";
import { Frame } from "../frame"; import { Frame } from "../frame";
import { ios as iosView, View, CSSType } from "../core/view"; import { ios as iosView, View, CSSType } from "../core/view";
import { ios as iosUtils, layout } from "../../utils/utils"; import { ios as iosUtils, layout, isFontIconURI } from "../../utils/utils";
import { device } from "../../platform"; import { device } from "../../platform";
import { Color } from "../../color"; import { Color } from "../../color";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource, fromFontIconCode, ImageSource } from "../../image-source";
// TODO: // TODO:
// import { profile } from "../../profiling"; // import { profile } from "../../profiling";
@ -526,9 +526,7 @@ export class BottomNavigation extends TabNavigationBase {
let image: UIImage; let image: UIImage;
let title: string; let title: string;
// Image and Label children of TabStripItem image = this._getIcon(item);
// take priority over its `iconSource` and `title` properties
image = item.image ? this._getIcon(item.image.src) : this._getIcon(item.iconSource);
title = item.label ? item.label.text : item.title; title = item.label ? item.label.text : item.title;
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(title, image, index); const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(title, image, index);
@ -540,14 +538,26 @@ export class BottomNavigation extends TabNavigationBase {
return UIImageRenderingMode.AlwaysOriginal; return UIImageRenderingMode.AlwaysOriginal;
} }
public _getIcon(iconSource: string): UIImage { public _getIcon(tabStripItem: TabStripItem): UIImage {
// Image and Label children of TabStripItem
// take priority over its `iconSource` and `title` properties
const iconSource = tabStripItem.image ? tabStripItem.image.src : tabStripItem.iconSource;
if (!iconSource) { if (!iconSource) {
return null; return null;
} }
let image: UIImage = this._iconsCache[iconSource]; let image: UIImage = this._iconsCache[iconSource];
if (!image) { if (!image) {
const is = fromFileOrResource(iconSource); let is = new ImageSource;
if (isFontIconURI(iconSource)) {
const fontIconCode = iconSource.split("//")[1];
const font = tabStripItem.style.fontInternal;
const color = tabStripItem.style.color;
is = fromFontIconCode(fontIconCode, font, color);
} else {
is = fromFileOrResource(iconSource);
}
if (is && is.ios) { if (is && is.ios) {
const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode()); const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode());
this._iconsCache[iconSource] = originalRenderedImage; this._iconsCache[iconSource] = originalRenderedImage;

View File

@ -2,9 +2,9 @@ import { Image as ImageDefinition, Stretch } from ".";
import { View, Property, InheritedCssProperty, Length, Style, Color, isIOS, booleanConverter, CSSType, traceEnabled, traceWrite, traceCategories } from "../core/view"; import { View, Property, InheritedCssProperty, Length, Style, Color, isIOS, booleanConverter, CSSType, traceEnabled, traceWrite, traceCategories } from "../core/view";
import { ImageAsset } from "../../image-asset"; import { ImageAsset } from "../../image-asset";
import { ImageSource, fromAsset, fromNativeSource, fromUrl } from "../../image-source"; import { ImageSource, fromAsset, fromNativeSource, fromUrl } from "../../image-source";
import { isDataURI, isFileOrResourcePath, RESOURCE_PREFIX } from "../../utils/utils"; import { isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX } from "../../utils/utils";
export * from "../core/view"; export * from "../core/view";
export { ImageSource, ImageAsset, fromAsset, fromNativeSource, fromUrl, isDataURI, isFileOrResourcePath, RESOURCE_PREFIX }; export { ImageSource, ImageAsset, fromAsset, fromNativeSource, fromUrl, isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX };
@CSSType("Image") @CSSType("Image")
export abstract class ImageBase extends View implements ImageDefinition { export abstract class ImageBase extends View implements ImageDefinition {
@ -46,7 +46,16 @@ export abstract class ImageBase extends View implements ImageDefinition {
this.isLoading = false; this.isLoading = false;
}; };
if (isDataURI(value)) { 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;
source.loadFromFontIconCode(fontIconCode, font, color);
imageLoaded();
}
} else if (isDataURI(value)) {
const base64Data = value.split(",")[1]; const base64Data = value.split(",")[1];
if (base64Data !== undefined) { if (base64Data !== undefined) {
if (sync) { if (sync) {

View File

@ -1,6 +1,6 @@
import { import {
ImageSource, ImageAsset, ImageBase, stretchProperty, imageSourceProperty, srcProperty, tintColorProperty, Color, ImageSource, ImageAsset, ImageBase, stretchProperty, imageSourceProperty, srcProperty, tintColorProperty, Color,
isDataURI, isFileOrResourcePath, RESOURCE_PREFIX, Length isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX, Length
} from "./image-common"; } from "./image-common";
import { knownFolders } from "../../file-system"; import { knownFolders } from "../../file-system";
@ -106,7 +106,7 @@ export class Image extends ImageBase {
value = value.trim(); value = value.trim();
this.isLoading = true; this.isLoading = true;
if (isDataURI(value)) { if (isFontIconURI(value) || isDataURI(value)) {
// TODO: Check with runtime what should we do in case of base64 string. // TODO: Check with runtime what should we do in case of base64 string.
super._createImageSourceFromSrc(value); super._createImageSourceFromSrc(value);
} else if (isFileOrResourcePath(value)) { } else if (isFileOrResourcePath(value)) {

View File

@ -11,8 +11,8 @@ import { Font } from "../styling/font";
import { getTransformedText } from "../text-base"; import { getTransformedText } from "../text-base";
import { Frame } from "../frame"; import { Frame } from "../frame";
import { Color } from "../core/view"; import { Color } from "../core/view";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource, fromFontIconCode, ImageSource } from "../../image-source";
import { RESOURCE_PREFIX, ad, layout } from "../../utils/utils"; import { RESOURCE_PREFIX, ad, layout, isFontIconURI } from "../../utils/utils";
import * as application from "../../application"; import * as application from "../../application";
export * from "./tabs-common"; export * from "./tabs-common";
@ -304,7 +304,16 @@ function createTabItemSpec(item: TabStripItem): org.nativescript.widgets.TabItem
// traceMissingIcon(iconSource); // traceMissingIcon(iconSource);
} }
} else { } else {
const is = fromFileOrResource(iconSource); let is = new ImageSource();
if (isFontIconURI(item.iconSource)) {
const fontIconCode = item.iconSource.split("//")[1];
const font = item.style.fontInternal;
const color = item.style.color;
is = fromFontIconCode(fontIconCode, font, color);
} else {
is = fromFileOrResource(item.iconSource);
}
if (is) { if (is) {
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS. // TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
tabItemSpec.iconDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), is.android); tabItemSpec.iconDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), is.android);

View File

@ -11,9 +11,9 @@ import { Font } from "../styling/font";
import { Frame } from "../frame"; import { Frame } from "../frame";
import { ios as iosView, View } from "../core/view"; import { ios as iosView, View } from "../core/view";
import { Color } from "../../color"; import { Color } from "../../color";
import { /*ios as iosUtils,*/ layout } from "../../utils/utils"; import { /*ios as iosUtils,*/ layout, isFontIconURI } from "../../utils/utils";
// import { device } from "../../platform"; // import { device } from "../../platform";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource, fromFontIconCode, ImageSource } from "../../image-source";
// TODO // TODO
// import { profile } from "../../profiling"; // import { profile } from "../../profiling";
@ -883,9 +883,7 @@ export class Tabs extends TabsBase {
let image: UIImage; let image: UIImage;
let title: string; let title: string;
// Image and Label children of TabStripItem image = this._getIcon(item);
// take priority over its `iconSource` and `title` properties
image = item.image ? this._getIcon(item.image.src) : this._getIcon(item.iconSource);
title = item.label ? item.label.text : item.title; title = item.label ? item.label.text : item.title;
if (!this.tabStrip._hasImage) { if (!this.tabStrip._hasImage) {
@ -918,14 +916,26 @@ export class Tabs extends TabsBase {
return UIImageRenderingMode.AlwaysOriginal; return UIImageRenderingMode.AlwaysOriginal;
} }
public _getIcon(iconSource: string): UIImage { public _getIcon(tabStripItem: TabStripItem): UIImage {
// Image and Label children of TabStripItem
// take priority over its `iconSource` and `title` properties
const iconSource = tabStripItem.image ? tabStripItem.image.src : tabStripItem.iconSource;
if (!iconSource) { if (!iconSource) {
return null; return null;
} }
let image: UIImage = this._iconsCache[iconSource]; let image: UIImage = this._iconsCache[iconSource];
if (!image) { if (!image) {
const is = fromFileOrResource(iconSource); let is = new ImageSource;
if (isFontIconURI(iconSource)) {
const fontIconCode = iconSource.split("//")[1];
const font = tabStripItem.style.fontInternal;
const color = tabStripItem.style.color;
is = fromFontIconCode(fontIconCode, font, color);
} else {
is = fromFileOrResource(iconSource);
}
if (is && is.ios) { if (is && is.ios) {
const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode()); const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode());
this._iconsCache[iconSource] = originalRenderedImage; this._iconsCache[iconSource] = originalRenderedImage;

View File

@ -113,6 +113,16 @@ export function isFileOrResourcePath(path: string): boolean {
path.indexOf(RESOURCE_PREFIX) === 0; // resource path.indexOf(RESOURCE_PREFIX) === 0; // resource
} }
export function isFontIconURI(uri: string): boolean {
if (!types.isString(uri)) {
return false;
}
const firstSegment = uri.trim().split("//")[0];
return firstSegment && firstSegment.indexOf("font:") === 0;
}
export function isDataURI(uri: string): boolean { export function isDataURI(uri: string): boolean {
if (!types.isString(uri)) { if (!types.isString(uri)) {
return false; return false;

View File

@ -278,6 +278,12 @@ export function executeOnMainThread(func: Function);
*/ */
export function mainThreadify(func: Function): (...args: any[]) => void export function mainThreadify(func: Function): (...args: any[]) => void
/**
* Returns true if the specified URI is a font icon URI like "fontIcon://&#xf1e0".
* @param uri The URI.
*/
export function isFontIconURI(uri: string): boolean
/** /**
* Returns true if the specified path points to a resource or local file. * Returns true if the specified path points to a resource or local file.
* @param path The path. * @param path The path.