From 243dc98005d43617872da5cfc010e76178aa7f97 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Mon, 16 Mar 2020 12:54:30 +0200 Subject: [PATCH] Added selectedItemColor and unSelectedItemColor to the TabStrip (#8431) * chore: add guard for ios * feat(bottom-nav): adding new properties * feat(tabs): new property implementation * feat: new feature implementation in android Implemented selectedItemColor and unSelectedItemColor properties on TabStrip * chore: added some comments * chore: change method return type * fix: setting icon color * fix: rendering mode setting * chore: rename variable * chore: fixed a typo * chore: updated log in build gradle * fix: item color setting in android * fix: tab styling when no css aplied * chore: private methods renamed * tests: added selected-item test pages * chore: renamed test pages * chore: move css-tree package to the right place * tests: added new ui tests * fix: use renamed function * fix: set item color * tests: aded automationText attribute * tests: trying to fix the tests Co-authored-by: Dimitar Topuzov --- api-reports/NativeScript.api.md | 12 ++ .../action-bar/flat-tab-opaque-bar-page.ts | 5 +- .../app/bottom-navigation/item-color-page.css | 7 + .../app/bottom-navigation/item-color-page.xml | 40 +++++ .../app/bottom-navigation/main-page.ts | 1 + e2e/ui-tests-app/app/tabs/frame-in-tabs.xml | 2 +- e2e/ui-tests-app/app/tabs/item-color-page.css | 20 +++ e2e/ui-tests-app/app/tabs/item-color-page.xml | 40 +++++ e2e/ui-tests-app/app/tabs/main-page.ts | 1 + .../bottom-navigation.e2e-spec.ts | 16 ++ .../tabs/tabs-tests.e2e-spec.ts | 43 +++-- nativescript-core/package.json | 118 +++++++------ .../bottom-navigation.android.ts | 95 ++++++++--- .../bottom-navigation.ios.ts | 112 +++++++++---- .../tab-navigation-base.d.ts | 26 ++- .../tab-navigation-base.ts | 20 ++- .../tab-strip/tab-strip.d.ts | 12 ++ .../tab-strip/tab-strip.ts | 28 ++++ nativescript-core/ui/tabs/tabs.android.ts | 88 ++++++++-- nativescript-core/ui/tabs/tabs.ios.ts | 157 ++++++++++++++---- .../android/widgets/build.gradle | 4 +- .../org/nativescript/widgets/TabsBar.java | 24 +-- 22 files changed, 675 insertions(+), 196 deletions(-) create mode 100644 e2e/ui-tests-app/app/bottom-navigation/item-color-page.css create mode 100644 e2e/ui-tests-app/app/bottom-navigation/item-color-page.xml create mode 100644 e2e/ui-tests-app/app/tabs/item-color-page.css create mode 100644 e2e/ui-tests-app/app/tabs/item-color-page.xml diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index 834f4fa39..82d7abcdc 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -2225,8 +2225,12 @@ export class TabNavigationBase extends View { getTabBarItemTextTransform(tabStripItem: TabStripItem): any + getTabBarSelectedItemColor(): Color + getTabBarTextTransform(): any + getTabBarUnSelectedItemColor(): Color + ios: any /* UITabBarController */; items: Array; @@ -2266,8 +2270,12 @@ export class TabNavigationBase extends View { setTabBarItemTitle(tabStripItem: TabStripItem, value: any): any + setTabBarSelectedItemColor(value: Color) + setTabBarTextTransform(value: any): void + setTabBarUnSelectedItemColor(value: Color) + tabStrip: TabStrip; } @@ -2320,6 +2328,10 @@ export class TabStrip extends View { on(eventNames: string, callback: (data: EventData) => void, thisArg?: any); on(event: "itemTap", callback: (args: TabStripItemEventData) => void, thisArg?: any); + + selectedItemColor: Color; + + unSelectedItemColor: Color; } // @public diff --git a/e2e/ui-tests-app/app/action-bar/flat-tab-opaque-bar-page.ts b/e2e/ui-tests-app/app/action-bar/flat-tab-opaque-bar-page.ts index 2c5e3504a..a77573d20 100644 --- a/e2e/ui-tests-app/app/action-bar/flat-tab-opaque-bar-page.ts +++ b/e2e/ui-tests-app/app/action-bar/flat-tab-opaque-bar-page.ts @@ -1,8 +1,11 @@ import { EventData } from "tns-core-modules/data/observable"; import { TabView } from "tns-core-modules/ui/tab-view"; +import { isIOS } from "tns-core-modules/platform"; export function onLoaded(args: EventData) { console.log("TEST", args.object); const tabView = args.object; - tabView.ios.tabBar.translucent = false; + if (isIOS) { + tabView.ios.tabBar.translucent = false; + } } diff --git a/e2e/ui-tests-app/app/bottom-navigation/item-color-page.css b/e2e/ui-tests-app/app/bottom-navigation/item-color-page.css new file mode 100644 index 000000000..f43139ce8 --- /dev/null +++ b/e2e/ui-tests-app/app/bottom-navigation/item-color-page.css @@ -0,0 +1,7 @@ +.font-awesome { + font-family: "FontAwesome"; +} + +.font-size { + font-size: 36; +} \ No newline at end of file diff --git a/e2e/ui-tests-app/app/bottom-navigation/item-color-page.xml b/e2e/ui-tests-app/app/bottom-navigation/item-color-page.xml new file mode 100644 index 000000000..8c736f67c --- /dev/null +++ b/e2e/ui-tests-app/app/bottom-navigation/item-color-page.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/e2e/ui-tests-app/app/bottom-navigation/main-page.ts b/e2e/ui-tests-app/app/bottom-navigation/main-page.ts index 344d41ef8..77fa47e6f 100644 --- a/e2e/ui-tests-app/app/bottom-navigation/main-page.ts +++ b/e2e/ui-tests-app/app/bottom-navigation/main-page.ts @@ -26,6 +26,7 @@ export function loadExamples() { examples.set("css-text-transform", "bottom-navigation/bottom-navigation-css-page"); examples.set("custom-tabstrip", "bottom-navigation/custom-tabstrip-page"); examples.set("reselect", "bottom-navigation/reselect-page"); + examples.set("item-color", "bottom-navigation/item-color-page"); return examples; } diff --git a/e2e/ui-tests-app/app/tabs/frame-in-tabs.xml b/e2e/ui-tests-app/app/tabs/frame-in-tabs.xml index 02ca704c6..6c0fcd264 100644 --- a/e2e/ui-tests-app/app/tabs/frame-in-tabs.xml +++ b/e2e/ui-tests-app/app/tabs/frame-in-tabs.xml @@ -1,6 +1,6 @@ - + diff --git a/e2e/ui-tests-app/app/tabs/item-color-page.css b/e2e/ui-tests-app/app/tabs/item-color-page.css new file mode 100644 index 000000000..27a16bf77 --- /dev/null +++ b/e2e/ui-tests-app/app/tabs/item-color-page.css @@ -0,0 +1,20 @@ +.font-awesome { + font-family: "FontAwesome"; +} + +.font-size { + font-size: 36; +} + + +/* TabStrip { + color: mediumvioletred; +} + +TabStripItem { + color: skyblue; +} + +TabStripItem:active { + color: darkblue; +} */ \ No newline at end of file diff --git a/e2e/ui-tests-app/app/tabs/item-color-page.xml b/e2e/ui-tests-app/app/tabs/item-color-page.xml new file mode 100644 index 000000000..b0a5ebae1 --- /dev/null +++ b/e2e/ui-tests-app/app/tabs/item-color-page.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/e2e/ui-tests-app/app/tabs/main-page.ts b/e2e/ui-tests-app/app/tabs/main-page.ts index de21578a6..2262190fe 100644 --- a/e2e/ui-tests-app/app/tabs/main-page.ts +++ b/e2e/ui-tests-app/app/tabs/main-page.ts @@ -33,6 +33,7 @@ export function loadExamples() { examples.set("nested-bottom-navigation", "tabs/nested-bottom-navigation-page"); examples.set("custom-tabstrip", "tabs/custom-tabstrip-page"); examples.set("frame-in-tabs", "tabs/frame-in-tabs"); + examples.set("item-color", "tabs/item-color-page"); return examples; } diff --git a/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts b/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts index e4167eb1d..d6be6846e 100644 --- a/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts +++ b/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts @@ -303,4 +303,20 @@ describe(`${suite}-${spec}-suite`, async function () { assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); await bottomNavigationBasePage.navigateBackToSuitMainPage(); }); + + it(`${spec}-item-color`, async function () { + await bottomNavigationBasePage.navigateToSample("item-color"); + await bottomNavigationBasePage.refreshTabItems(); + await driver.imageHelper.compareScreen(); + + // go through the tabs and check that they are loaded + await bottomNavigationBasePage.tabOnItem(1); + await driver.imageHelper.compareScreen(); + + await bottomNavigationBasePage.tabOnItem(2); + await driver.imageHelper.compareScreen(); + + assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); + await bottomNavigationBasePage.navigateBackToSuitMainPage(); + }); }); diff --git a/e2e/ui-tests-app/e2e/suites/tab-navigation/tabs/tabs-tests.e2e-spec.ts b/e2e/ui-tests-app/e2e/suites/tab-navigation/tabs/tabs-tests.e2e-spec.ts index 7a98d6b2a..c690d1ba7 100644 --- a/e2e/ui-tests-app/e2e/suites/tab-navigation/tabs/tabs-tests.e2e-spec.ts +++ b/e2e/ui-tests-app/e2e/suites/tab-navigation/tabs/tabs-tests.e2e-spec.ts @@ -284,21 +284,38 @@ describe(`${imagePrefix}-suite`, async function () { await tabsViewBasePage.navigateBackToSuitMainPage(); }); - // it(`${imagePrefix}-frame-in-tabs`, async function () { - // await tabsViewBasePage.navigateToSample("frame-in-tabs"); - // await driver.imageHelper.compareScreen(); + it(`${imagePrefix}-frame-in-tabs`, async function () { + await tabsViewBasePage.navigateToSample("frame-in-tabs"); + await tabsViewBasePage.refreshTabItems(); + await driver.imageHelper.compareScreen(); - // // go through the tabs and check that they are loaded - // await tabsViewBasePage.tabOnItem(1); - // await driver.imageHelper.compareScreen(); + // go through the tabs and check that they are loaded + await tabsViewBasePage.tabOnItem(1); + await driver.imageHelper.compareScreen(); - // await tabsViewBasePage.tabOnItem(2); - // await driver.imageHelper.compareScreen(); + await tabsViewBasePage.tabOnItem(2); + await driver.imageHelper.compareScreen(); - // await tabsViewBasePage.tabOnItem(3); - // await driver.imageHelper.compareScreen(); + await tabsViewBasePage.tabOnItem(3); + await driver.imageHelper.compareScreen(); - // assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); - // await tabsViewBasePage.navigateBackToSuitMainPage(); - // }); + assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); + await tabsViewBasePage.navigateBackToSuitMainPage(); + }); + + it(`${spec}-item-color`, async function () { + await tabsViewBasePage.navigateToSample("item-color"); + await tabsViewBasePage.refreshTabItems(); + await driver.imageHelper.compareScreen(); + + // go through the tabs and check that they are loaded + await tabsViewBasePage.tabOnItem(1); + await driver.imageHelper.compareScreen(); + + await tabsViewBasePage.tabOnItem(2); + await driver.imageHelper.compareScreen(); + + assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); + await tabsViewBasePage.navigateBackToSuitMainPage(); + }); }); diff --git a/nativescript-core/package.json b/nativescript-core/package.json index 94ef4766a..0dbf2e07a 100644 --- a/nativescript-core/package.json +++ b/nativescript-core/package.json @@ -1,63 +1,61 @@ { - "name": "@nativescript/core", - "main": "index", - "types": "index.d.ts", - "description": "Telerik NativeScript Core Modules", - "version": "6.5.0", - "homepage": "https://www.nativescript.org", - "repository": { - "type": "git", - "url": "https://github.com/NativeScript/NativeScript" - }, - "files": [ - "**/*.d.ts", - "**/*.js", - "**/platforms/ios/**", - "**/platforms/android/**", - "**/package.json", - "!org.nativescript.widgets.d.ts" - ], - "license": "Apache-2.0", - "dependencies": { - "nativescript-hook": "0.2.5", - "reduce-css-calc": "^2.1.6", - "css-tree": "^1.0.0-alpha.37", - "semver": "6.3.0", - "tns-core-modules-widgets": "next", - "tslib": "1.10.0" - }, - "devDependencies": { - "@types/chai": "~4.2.5", - "@types/node": "~10.12.18", - "tns-platform-declarations": "next" - }, - "scripts": { - "version": "conventional-changelog -p angular -i ../CHANGELOG.md -s && git add ../CHANGELOG.md", - "postinstall": "node cli-hooks/postinstall.js", - "preuninstall": "node cli-hooks/preuninstall.js" - }, - "nativescript": { - "platforms": { - "ios": "6.0.0", - "android": "6.0.0" + "name": "@nativescript/core", + "main": "index", + "types": "index.d.ts", + "description": "Telerik NativeScript Core Modules", + "version": "6.5.0", + "homepage": "https://www.nativescript.org", + "repository": { + "type": "git", + "url": "https://github.com/NativeScript/NativeScript" }, - "hooks": [ - { - "name": "nativescript-core", - "type": "before-checkForChanges", - "script": "cli-hooks/before-checkForChanges.js", - "inject": true - } - ] - }, - "snapshot": { - "android": { - "tns-java-classes": { - "modules": [ - "ui/frame/activity", - "ui/frame/fragment" - ] - } + "files": [ + "**/*.d.ts", + "**/*.js", + "**/platforms/ios/**", + "**/platforms/android/**", + "**/package.json", + "!org.nativescript.widgets.d.ts" + ], + "license": "Apache-2.0", + "dependencies": { + "css-tree": "^1.0.0-alpha.37", + "nativescript-hook": "0.2.5", + "reduce-css-calc": "^2.1.6", + "semver": "6.3.0", + "tns-core-modules-widgets": "next", + "tslib": "1.10.0" + }, + "devDependencies": { + "@types/chai": "~4.2.5", + "@types/node": "~10.12.18", + "tns-platform-declarations": "next" + }, + "scripts": { + "version": "conventional-changelog -p angular -i ../CHANGELOG.md -s && git add ../CHANGELOG.md", + "postinstall": "node cli-hooks/postinstall.js", + "preuninstall": "node cli-hooks/preuninstall.js" + }, + "nativescript": { + "platforms": { + "ios": "6.0.0", + "android": "6.0.0" + }, + "hooks": [{ + "name": "nativescript-core", + "type": "before-checkForChanges", + "script": "cli-hooks/before-checkForChanges.js", + "inject": true + }] + }, + "snapshot": { + "android": { + "tns-java-classes": { + "modules": [ + "ui/frame/activity", + "ui/frame/fragment" + ] + } + } } - } -} +} \ No newline at end of file diff --git a/nativescript-core/ui/bottom-navigation/bottom-navigation.android.ts b/nativescript-core/ui/bottom-navigation/bottom-navigation.android.ts index ad3671aa5..edbf93294 100644 --- a/nativescript-core/ui/bottom-navigation/bottom-navigation.android.ts +++ b/nativescript-core/ui/bottom-navigation/bottom-navigation.android.ts @@ -169,13 +169,13 @@ function initializeNativeClasses() { if (position >= 0 && tabStripItems && tabStripItems[position]) { tabStripItems[position]._emit(TabStripItem.selectEvent); + owner._setItemColor(tabStripItems[position]); } if (prevPosition >= 0 && tabStripItems && tabStripItems[prevPosition]) { tabStripItems[prevPosition]._emit(TabStripItem.unselectEvent); + owner._setItemColor(tabStripItems[prevPosition]); } - - owner.selectedIndex = position; } public onTap(position: number): boolean { @@ -257,6 +257,8 @@ export class BottomNavigation extends TabNavigationBase { private _attachedToWindow = false; public _originalBackground: any; private _textTransform: TextTransform = "none"; + private _selectedItemColor: Color; + private _unSelectedItemColor: Color; constructor() { super(); @@ -529,6 +531,7 @@ export class BottomNavigation extends TabNavigationBase { } this._currentFragment = fragment; + this.selectedIndex = position; const tabItems = this.items; const tabItem = tabItems ? tabItems[position] : null; @@ -573,6 +576,7 @@ export class BottomNavigation extends TabNavigationBase { items.forEach((item, i, arr) => { const textView = this._bottomNavigationBar.getTextViewForItemAt(i); item.setNativeView(textView); + this._setItemColor(item); }); } @@ -605,10 +609,9 @@ export class BottomNavigation extends TabNavigationBase { tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor(); // COLOR - const color = titleLabel.style.color; - if (color) { - tabItemSpec.color = color.android; - } + let itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor; + const color = itemColor || titleLabel.style.color; + tabItemSpec.color = color && color.android; // FONT const fontInternal = titleLabel.style.fontInternal; @@ -620,7 +623,7 @@ export class BottomNavigation extends TabNavigationBase { // ICON const iconSource = tabStripItem.image && tabStripItem.image.src; if (iconSource) { - const iconInfo = this.getIconInfo(tabStripItem); + const iconInfo = this.getIconInfo(tabStripItem, itemColor); if (iconInfo) { // TODO: Make this native call that accepts string so that we don't load Bitmap in JS. @@ -637,7 +640,7 @@ export class BottomNavigation extends TabNavigationBase { return tabItemSpec; } - private getOriginalIcon(tabStripItem: TabStripItem): android.graphics.Bitmap { + private getOriginalIcon(tabStripItem: TabStripItem, color?: Color): android.graphics.Bitmap { const iconSource = tabStripItem.image && tabStripItem.image.src; if (!iconSource) { return null; @@ -648,7 +651,9 @@ export class BottomNavigation extends TabNavigationBase { const fontIconCode = iconSource.split("//")[1]; const target = tabStripItem.image ? tabStripItem.image : tabStripItem; const font = target.style.fontInternal; - const color = target.style.color; + if (!color) { + color = target.style.color; + } is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color); } else { is = ImageSource.fromFileOrResourceSync(iconSource); @@ -674,8 +679,8 @@ export class BottomNavigation extends TabNavigationBase { return new IconInfo(); } - private getIconInfo(tabStripItem: TabStripItem): IconInfo { - let originalIcon = this.getOriginalIcon(tabStripItem); + private getIconInfo(tabStripItem: TabStripItem, color?: Color): IconInfo { + let originalIcon = this.getOriginalIcon(tabStripItem, color); return this.getDrawableInfo(originalIcon); } @@ -710,6 +715,22 @@ export class BottomNavigation extends TabNavigationBase { } } + public getTabBarSelectedItemColor(): Color { + return this._selectedItemColor; + } + + public setTabBarSelectedItemColor(value: Color) { + this._selectedItemColor = value; + } + + public getTabBarUnSelectedItemColor(): Color { + return this._unSelectedItemColor; + } + + public setTabBarUnSelectedItemColor(value: Color) { + this._unSelectedItemColor = value; + } + public setTabBarItemTitle(tabStripItem: TabStripItem, value: string): void { // TODO: Should figure out a way to do it directly with the the nativeView const tabStripItemIndex = this.tabStrip.items.indexOf(tabStripItem); @@ -724,21 +745,51 @@ export class BottomNavigation extends TabNavigationBase { this.updateAndroidItemAt(tabStripItemIndex, tabItemSpec); } - public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void { - if (typeof value === "number") { - tabStripItem.nativeViewProtected.setTextColor(value); - } else { - tabStripItem.nativeViewProtected.setTextColor(value.android); + public _setItemColor(tabStripItem: TabStripItem) { + const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor; + if (!itemColor) { + return; + } + + // set label color + tabStripItem.nativeViewProtected.setTextColor(itemColor.android); + + // set icon color + this.setIconColor(tabStripItem, itemColor); + } + + private setIconColor(tabStripItem: TabStripItem, color?: Color) { + const tabBarItem = this._bottomNavigationBar.getViewForItemAt(tabStripItem._index); + + const drawableInfo = this.getIconInfo(tabStripItem, color); + const imgView = tabBarItem.getChildAt(0); + imgView.setImageDrawable(drawableInfo.drawable); + if (color) { + imgView.setColorFilter(color.android); } } - public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void { - const index = tabStripItem._index; - const tabBarItem = this._bottomNavigationBar.getViewForItemAt(index); - const imgView = tabBarItem.getChildAt(0); - const drawableInfo = this.getIconInfo(tabStripItem); + public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void { + const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor; + if (itemColor) { + // the itemColor is set through the selectedItemColor and unSelectedItemColor properties + // so it does not respect the css color + return; + } - imgView.setImageDrawable(drawableInfo.drawable); + const androidColor = value instanceof Color ? value.android : value; + tabStripItem.nativeViewProtected.setTextColor(androidColor); + } + + public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void { + const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor; + if (itemColor) { + // the itemColor is set through the selectedItemColor and unSelectedItemColor properties + // so it does not respect the css color + return; + } + + this.setIconColor(tabStripItem); } public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void { diff --git a/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts b/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts index 9c0f4b703..666b4e0ae 100644 --- a/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts +++ b/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts @@ -257,6 +257,8 @@ export class BottomNavigation extends TabNavigationBase { private _delegate: UITabBarControllerDelegateImpl; private _moreNavigationControllerDelegate: UINavigationControllerDelegateImpl; private _iconsCache = {}; + private _selectedItemColor: Color; + private _unSelectedItemColor: Color; constructor() { super(); @@ -372,18 +374,20 @@ export class BottomNavigation extends TabNavigationBase { } public setTabBarItemColor(tabStripItem: TabStripItem, value: UIColor | Color): void { - setViewTextAttributes(tabStripItem.nativeView, tabStripItem.label, this.viewController.tabBar); + this.setViewAttributes(tabStripItem.nativeView, tabStripItem.label); } public setTabBarIconColor(tabStripItem: TabStripItem, value: UIColor | Color): void { - const image = this.getIcon(tabStripItem); + if (!this._unSelectedItemColor && !this._selectedItemColor) { + const image = this.getIcon(tabStripItem); - tabStripItem.nativeView.image = image; - tabStripItem.nativeView.selectedImage = image; + tabStripItem.nativeView.image = image; + tabStripItem.nativeView.selectedImage = image; + } } public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void { - setViewTextAttributes(tabStripItem.nativeView, tabStripItem.label, this.viewController.tabBar); + this.setViewAttributes(tabStripItem.nativeView, tabStripItem.label); } public setTabBarItemTextTransform(tabStripItem: TabStripItem, value: TextTransform): void { @@ -400,6 +404,22 @@ export class BottomNavigation extends TabNavigationBase { this._ios.tabBar.tintColor = nativeColor; } + public getTabBarSelectedItemColor(): Color { + return this._selectedItemColor; + } + + public setTabBarSelectedItemColor(value: Color) { + this._selectedItemColor = value; + } + + public getTabBarUnSelectedItemColor(): Color { + return this._unSelectedItemColor; + } + + public setTabBarUnSelectedItemColor(value: Color) { + this._unSelectedItemColor = value; + } + public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { const width = layout.getMeasureSpecSize(widthMeasureSpec); const widthMode = layout.getMeasureSpecMode(widthMeasureSpec); @@ -521,7 +541,7 @@ export class BottomNavigation extends TabNavigationBase { const tabBarItem = this.createTabBarItem(tabStripItem, i); updateTitleAndIconPositions(tabStripItem, tabBarItem, controller); - setViewTextAttributes(tabBarItem, tabStripItem.label, this.viewController.tabBar); + this.setViewAttributes(tabBarItem, tabStripItem.label); controller.tabBarItem = tabBarItem; tabStripItem._index = i; @@ -531,6 +551,8 @@ export class BottomNavigation extends TabNavigationBase { controllers.addObject(controller); }); + this.setItemImages(); + this._ios.viewControllers = controllers; this._ios.customizableViewControllers = null; @@ -538,6 +560,23 @@ export class BottomNavigation extends TabNavigationBase { this._ios.moreNavigationController.delegate = this._moreNavigationControllerDelegate; } + private setItemImages() { + if (this._selectedItemColor || this._unSelectedItemColor) { + if (this.tabStrip && this.tabStrip.items) { + this.tabStrip.items.forEach(item => { + if (this._unSelectedItemColor && item.nativeView) { + item.nativeView.image = this.getIcon(item, this._unSelectedItemColor); + item.nativeView.tintColor = this._unSelectedItemColor; + } + if (this._selectedItemColor && item.nativeView) { + item.nativeView.selectedImage = this.getIcon(item, this._selectedItemColor); + item.nativeView.tintColor = this._selectedItemColor; + } + }); + } + } + } + private createTabBarItem(item: TabStripItem, index: number): UITabBarItem { let image: UIImage; let title: string; @@ -569,7 +608,7 @@ export class BottomNavigation extends TabNavigationBase { } } - private getIcon(tabStripItem: TabStripItem): UIImage { + private getIcon(tabStripItem: TabStripItem, color?: Color): UIImage { // Image and Label children of TabStripItem // take priority over its `iconSource` and `title` properties const iconSource = tabStripItem.image && tabStripItem.image.src; @@ -579,7 +618,9 @@ export class BottomNavigation extends TabNavigationBase { const target = tabStripItem.image; const font = target.style.fontInternal; - const color = target.style.color; + if (!color) { + color = target.style.color; + } const iconTag = [iconSource, font.fontStyle, font.fontWeight, font.fontSize, font.fontFamily, color].join(";"); let isFontIcon = false; @@ -688,33 +729,40 @@ export class BottomNavigation extends TabNavigationBase { this.setViewControllers(this.items); selectedIndexProperty.coerce(this); } -} -function setViewTextAttributes(item: UITabBarItem, view: View, tabBar: UITabBar): any { - if (!view) { - return null; - } + private setViewAttributes(item: UITabBarItem, view: View): any { + if (!view) { + return null; + } - const defaultTabItemFontSize = 10; - const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize; - const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); - const tabItemTextColor = view.style.color; - const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null; - let attributes: any = { [NSFontAttributeName]: font }; - if (textColor) { - attributes[UITextAttributeTextColor] = textColor; - attributes[NSForegroundColorAttributeName] = textColor; - } + const defaultTabItemFontSize = 10; + const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize; + const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); + const tabItemTextColor = view.style.color; + const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null; + let attributes: any = { [NSFontAttributeName]: font }; - item.setTitleTextAttributesForState(attributes, UIControlState.Selected); - item.setTitleTextAttributesForState(attributes, UIControlState.Normal); + // if selectedItemColor or unSelectedItemColor is set we don't respect the color from the style + if (!this._selectedItemColor && !this._unSelectedItemColor) { + if (textColor) { + attributes[UITextAttributeTextColor] = textColor; + attributes[NSForegroundColorAttributeName] = textColor; + } + } else { + this.viewController.tabBar.unselectedItemTintColor = this._unSelectedItemColor && this._unSelectedItemColor.ios; + this.viewController.tabBar.selectedImageTintColor = this._selectedItemColor && this._selectedItemColor.ios; + } - // there's a bug when setting the item color on ios 13 if there's no background set to the tabstrip - // https://books.google.bg/books?id=99_BDwAAQBAJ&q=tabBar.unselectedItemTintColor - // to fix the above issue we are applying the selected fix only for the case, when there is no background set - // in that case we have the following known issue: - // we will set the color to all unselected items, so you won't be able to set different colors for the different not selected items - if (!tabBar.barTintColor && attributes[UITextAttributeTextColor] && (majorVersion > 9)) { - tabBar.unselectedItemTintColor = attributes[UITextAttributeTextColor]; + item.setTitleTextAttributesForState(attributes, UIControlState.Selected); + item.setTitleTextAttributesForState(attributes, UIControlState.Normal); + + // there's a bug when setting the item color on ios 13 if there's no background set to the tabstrip + // https://books.google.bg/books?id=99_BDwAAQBAJ&q=tabBar.unselectedItemTintColor + // to fix the above issue we are applying the selected fix only for the case, when there is no background set + // in that case we have the following known issue: + // // we will set the color to all unselected items, so you won't be able to set different colors for the different not selected items + if (!this.viewController.tabBar.barTintColor && attributes[UITextAttributeTextColor] && (majorVersion > 9)) { + this.viewController.tabBar.unselectedItemTintColor = attributes[UITextAttributeTextColor]; + } } } \ No newline at end of file diff --git a/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.d.ts b/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.d.ts index 7440e23e6..8d623e7d0 100644 --- a/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.d.ts +++ b/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.d.ts @@ -4,7 +4,7 @@ */ /** */ import { - View, ViewBase, Property, CoercibleProperty, isIOS, AddArrayFromBuilder, AddChildFromBuilder, EventData + View, ViewBase, Property, CoercibleProperty, isIOS, AddArrayFromBuilder, AddChildFromBuilder, EventData, Color } from "../../core/view"; import { TabStrip } from "../tab-strip"; import { TabStripItem } from "../tab-strip-item"; @@ -150,6 +150,30 @@ export class TabNavigationBase extends View { */ setTabBarHighlightColor(value: any) + /** + * @private + * Method is intended to be overridden by inheritors and used as "protected" + */ + getTabBarSelectedItemColor(): Color + + /** + * @private + * Method is intended to be overridden by inheritors and used as "protected" + */ + setTabBarSelectedItemColor(value: Color) + + /** + * @private + * Method is intended to be overridden by inheritors and used as "protected" + */ + getTabBarUnSelectedItemColor(): Color + + /** + * @private + * Method is intended to be overridden by inheritors and used as "protected" + */ + setTabBarUnSelectedItemColor(value: Color) + /** * @private * Method is intended to be overridden by inheritors and used as "protected" diff --git a/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts b/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts index 011d283ef..09d8b2dd2 100644 --- a/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts +++ b/nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts @@ -1,7 +1,7 @@ // Types import { TabNavigationBase as TabNavigationBaseDefinition, SelectedIndexChangedEventData } from "."; import { TabStripItem } from "../tab-strip-item"; -import { ViewBase, AddArrayFromBuilder, AddChildFromBuilder, EventData } from "../../core/view"; +import { ViewBase, AddArrayFromBuilder, AddChildFromBuilder, EventData, Color } from "../../core/view"; // Requires import { View, Property, CoercibleProperty, isIOS } from "../../core/view"; @@ -150,6 +150,24 @@ export class TabNavigationBase extends View implements TabNavigationBaseDefiniti // overridden by inheritors } + public getTabBarSelectedItemColor(): Color { + // overridden by inheritors + return null; + } + + public setTabBarSelectedItemColor(value: Color) { + // overridden by inheritors + } + + public getTabBarUnSelectedItemColor(): Color { + // overridden by inheritors + return null; + } + + public setTabBarUnSelectedItemColor(value: Color) { + // overridden by inheritors + } + public getTabBarColor(): any { // overridden by inheritors return null; diff --git a/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.d.ts b/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.d.ts index 6df5c81cd..b21d31f97 100644 --- a/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.d.ts +++ b/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.d.ts @@ -32,6 +32,16 @@ export class TabStrip extends View { */ highlightColor: Color; + /** + * Gets or sets the color of the selected item in the tab strip. + */ + selectedItemColor: Color; + + /** + * Gets or sets the color of the non-selected items in the tab strip. + */ + unSelectedItemColor: Color; + /** * @private */ @@ -73,3 +83,5 @@ export interface TabStripItemEventData extends EventData { export const iosIconRenderingModeProperty: Property; export const isIconSizeFixedProperty: Property; +export const selectedItemColorProperty: Property; +export const unSelectedItemColorProperty: Property; \ No newline at end of file diff --git a/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.ts b/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.ts index 2b70e5439..5505a71de 100644 --- a/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.ts +++ b/nativescript-core/ui/tab-navigation-base/tab-strip/tab-strip.ts @@ -17,6 +17,8 @@ export const traceCategory = "TabView"; // Place this on top because the webpack ts-loader doesn't work when export // is after reference export const highlightColorProperty = new Property({ name: "highlightColor", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) }); +export const selectedItemColorProperty = new Property({ name: "selectedItemColor", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) }); +export const unSelectedItemColorProperty = new Property({ name: "unSelectedItemColor", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) }); @CSSType("TabStrip") export class TabStrip extends View implements TabStripDefinition, AddChildFromBuilder, AddArrayFromBuilder { @@ -25,6 +27,8 @@ export class TabStrip extends View implements TabStripDefinition, AddChildFromBu public isIconSizeFixed: boolean; public iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate"; public highlightColor: Color; + public selectedItemColor: Color; + public unSelectedItemColor: Color; public _hasImage: boolean; public _hasTitle: boolean; @@ -127,6 +131,28 @@ export class TabStrip extends View implements TabStripDefinition, AddChildFromBu return parent && parent.setTabBarHighlightColor(value); } + + [selectedItemColorProperty.getDefault](): Color { + const parent = this.parent; + + return parent && parent.getTabBarSelectedItemColor(); + } + [selectedItemColorProperty.setNative](value: Color) { + const parent = this.parent; + + return parent && parent.setTabBarSelectedItemColor(value); + } + + [unSelectedItemColorProperty.getDefault](): Color { + const parent = this.parent; + + return parent && parent.getTabBarUnSelectedItemColor(); + } + [unSelectedItemColorProperty.setNative](value: Color) { + const parent = this.parent; + + return parent && parent.setTabBarUnSelectedItemColor(value); + } } export interface TabStrip { @@ -150,3 +176,5 @@ export const isIconSizeFixedProperty = new Property({ isIconSizeFixedProperty.register(TabStrip); highlightColorProperty.register(TabStrip); +selectedItemColorProperty.register(TabStrip); +unSelectedItemColorProperty.register(TabStrip); diff --git a/nativescript-core/ui/tabs/tabs.android.ts b/nativescript-core/ui/tabs/tabs.android.ts index afdbab527..67df258f1 100644 --- a/nativescript-core/ui/tabs/tabs.android.ts +++ b/nativescript-core/ui/tabs/tabs.android.ts @@ -300,10 +300,12 @@ function initializeNativeClasses() { if (position >= 0 && tabStripItems && tabStripItems[position]) { tabStripItems[position]._emit(TabStripItem.selectEvent); + owner._setItemColor(tabStripItems[position]); } if (prevPosition >= 0 && tabStripItems && tabStripItems[prevPosition]) { tabStripItems[prevPosition]._emit(TabStripItem.unselectEvent); + owner._setItemColor(tabStripItems[prevPosition]); } } @@ -374,6 +376,8 @@ export class Tabs extends TabsBase { private _androidViewId: number = -1; public _originalBackground: any; private _textTransform: TextTransform = "uppercase"; + private _selectedItemColor: Color; + private _unSelectedItemColor: Color; constructor() { super(); @@ -635,6 +639,7 @@ export class Tabs extends TabsBase { items.forEach((item, i, arr) => { const tv = tabsBar.getTextViewForItemAt(i); item.setNativeView(tv); + this._setItemColor(item); }); } @@ -667,10 +672,9 @@ export class Tabs extends TabsBase { tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor(); // COLOR - const color = nestedLabel.style.color; - if (color) { - tabItemSpec.color = color.android; - } + let itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor; + const color = itemColor || nestedLabel.style.color; + tabItemSpec.color = color && color.android; // FONT const fontInternal = nestedLabel.style.fontInternal; @@ -682,7 +686,7 @@ export class Tabs extends TabsBase { // ICON const iconSource = tabStripItem.image && tabStripItem.image.src; if (iconSource) { - const icon = this.getIcon(tabStripItem); + const icon = this.getIcon(tabStripItem, itemColor); if (icon) { // TODO: Make this native call that accepts string so that we don't load Bitmap in JS. @@ -698,7 +702,7 @@ export class Tabs extends TabsBase { return tabItemSpec; } - private getIcon(tabStripItem: TabStripItem): android.graphics.drawable.BitmapDrawable { + private getIcon(tabStripItem: TabStripItem, color?: Color): android.graphics.drawable.BitmapDrawable { const iconSource = tabStripItem.image && tabStripItem.image.src; if (!iconSource) { return null; @@ -709,7 +713,9 @@ export class Tabs extends TabsBase { const fontIconCode = iconSource.split("//")[1]; const target = tabStripItem.image ? tabStripItem.image : tabStripItem; const font = target.style.fontInternal; - const color = target.style.color; + if (!color) { + color = target.style.color; + } is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color); } else { is = ImageSource.fromFileOrResourceSync(iconSource); @@ -801,6 +807,22 @@ export class Tabs extends TabsBase { this._tabsBar.setSelectedIndicatorColors([color]); } + public getTabBarSelectedItemColor(): Color { + return this._selectedItemColor; + } + + public setTabBarSelectedItemColor(value: Color) { + this._selectedItemColor = value; + } + + public getTabBarUnSelectedItemColor(): Color { + return this._unSelectedItemColor; + } + + public setTabBarUnSelectedItemColor(value: Color) { + this._unSelectedItemColor = value; + } + public setTabBarItemTitle(tabStripItem: TabStripItem, value: string): void { // TODO: Should figure out a way to do it directly with the the nativeView const tabStripItemIndex = this.tabStrip.items.indexOf(tabStripItem); @@ -815,21 +837,51 @@ export class Tabs extends TabsBase { this.updateAndroidItemAt(tabStripItemIndex, tabItemSpec); } - public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void { - if (typeof value === "number") { - tabStripItem.nativeViewProtected.setTextColor(value); - } else { - tabStripItem.nativeViewProtected.setTextColor(value.android); + public _setItemColor(tabStripItem: TabStripItem) { + const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor; + if (!itemColor) { + return; + } + + // set label color + tabStripItem.nativeViewProtected.setTextColor(itemColor.android); + + // set icon color + this.setIconColor(tabStripItem, itemColor); + } + + private setIconColor(tabStripItem: TabStripItem, color?: Color) { + const tabBarItem = this._tabsBar.getViewForItemAt(tabStripItem._index); + + const drawable = this.getIcon(tabStripItem, color); + const imgView = tabBarItem.getChildAt(0); + imgView.setImageDrawable(drawable); + if (color) { + imgView.setColorFilter(color.android); } } - public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void { - const index = tabStripItem._index; - const tabBarItem = this._tabsBar.getViewForItemAt(index); - const imgView = tabBarItem.getChildAt(0); - const drawable = this.getIcon(tabStripItem); + public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void { + const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor; + if (itemColor) { + // the itemColor is set through the selectedItemColor and unSelectedItemColor properties + // so it does not respect the css color + return; + } - imgView.setImageDrawable(drawable); + const androidColor = value instanceof Color ? value.android : value; + tabStripItem.nativeViewProtected.setTextColor(androidColor); + } + + public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void { + const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor; + if (itemColor) { + // the itemColor is set through the selectedItemColor and unSelectedItemColor properties + // so it does not respect the css color + return; + } + + this.setIconColor(tabStripItem); } public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void { diff --git a/nativescript-core/ui/tabs/tabs.ios.ts b/nativescript-core/ui/tabs/tabs.ios.ts index c8ce759d4..b5af27d00 100644 --- a/nativescript-core/ui/tabs/tabs.ios.ts +++ b/nativescript-core/ui/tabs/tabs.ios.ts @@ -461,6 +461,8 @@ export class Tabs extends TabsBase { private _iconsCache = {}; private _backgroundIndicatorColor: UIColor; public _defaultItemBackgroundColor: UIColor; + private _selectedItemColor: Color; + private _unSelectedItemColor: Color; constructor() { super(); @@ -548,10 +550,12 @@ export class Tabs extends TabsBase { if (tabStripItems) { if (tabStripItems[newIndex]) { tabStripItems[newIndex]._emit(TabStripItem.selectEvent); + this.setIconColor(tabStripItems[newIndex]); } if (tabStripItems[oldIndex]) { tabStripItems[oldIndex]._emit(TabStripItem.unselectEvent); + this.setIconColor(tabStripItems[oldIndex]); } } @@ -702,7 +706,7 @@ export class Tabs extends TabsBase { const tabBarItem = this.createTabBarItem(tabStripItem, i); updateTitleAndIconPositions(tabStripItem, tabBarItem, controller); - setViewTextAttributes(this._ios.tabBar, tabStripItem.label, i === this.selectedIndex); + this.setViewTextAttributes(tabStripItem.label, i === this.selectedIndex); controller.tabBarItem = tabBarItem; tabStripItem._index = i; @@ -714,6 +718,8 @@ export class Tabs extends TabsBase { viewControllers.push(controller); }); + this.setItemImages(); + this.viewControllers = viewControllers; this.tabBarItems = tabBarItems; @@ -728,6 +734,23 @@ export class Tabs extends TabsBase { } } + private setItemImages() { + if (this._selectedItemColor || this._unSelectedItemColor) { + if (this.tabStrip && this.tabStrip.items) { + this.tabStrip.items.forEach(item => { + if (this._unSelectedItemColor && item.nativeView) { + item.nativeView.image = this.getIcon(item, this._unSelectedItemColor); + } + if (this._selectedItemColor && item.nativeView) { + if (this.selectedIndex === item._index) { + item.nativeView.image = this.getIcon(item, this._selectedItemColor); + } + } + }); + } + } + } + private createTabBarItem(item: TabStripItem, index: number): UITabBarItem { let image: UIImage; let title: string; @@ -764,11 +787,20 @@ export class Tabs extends TabsBase { } private getIconRenderingMode(): UIImageRenderingMode { - // MDCTabBar doesn't work with rendering mode AlwaysTemplate - return UIImageRenderingMode.AlwaysOriginal; + switch (this.tabStrip && this.tabStrip.iosIconRenderingMode) { + case "alwaysOriginal": + return UIImageRenderingMode.AlwaysOriginal; + case "alwaysTemplate": + return UIImageRenderingMode.AlwaysTemplate; + case "automatic": + default: + const hasItemColor = this._selectedItemColor || this._unSelectedItemColor; + + return hasItemColor ? UIImageRenderingMode.AlwaysTemplate : UIImageRenderingMode.AlwaysOriginal; + } } - private getIcon(tabStripItem: TabStripItem): UIImage { + private getIcon(tabStripItem: TabStripItem, color?: Color): UIImage { // Image and Label children of TabStripItem // take priority over its `iconSource` and `title` properties const iconSource = tabStripItem.image && tabStripItem.image.src; @@ -778,7 +810,9 @@ export class Tabs extends TabsBase { const target = tabStripItem.image; const font = target.style.fontInternal; - const color = target.style.color; + if (!color) { + color = target.style.color; + } const iconTag = [iconSource, font.fontStyle, font.fontWeight, font.fontSize, font.fontFamily, color].join(";"); let isFontIcon = false; @@ -800,16 +834,13 @@ export class Tabs extends TabsBase { image = this.getFixedSizeIcon(image); } - let renderingMode: UIImageRenderingMode = UIImageRenderingMode.AlwaysOriginal; + let renderingMode: UIImageRenderingMode = UIImageRenderingMode.Automatic; if (!isFontIcon) { renderingMode = this.getIconRenderingMode(); } const originalRenderedImage = image.imageWithRenderingMode(renderingMode); this._iconsCache[iconTag] = originalRenderedImage; image = originalRenderedImage; - } else { - // TODO - // traceMissingIcon(iconSource); } } @@ -862,6 +893,11 @@ export class Tabs extends TabsBase { } private isSelectedAndHightlightedItem(tabStripItem: TabStripItem): boolean { + // to find out whether the current tab strip item is active (has style with :active selector applied) + // we need to check whether its _visualState is equal to "highlighted" as when changing tabs + // we first go through setTabBarItemBackgroundColor thice, once before setting the "highlighted" state + // and once after that, but if the "highlighted" state is not set we cannot get the backgroundColor + // set using :active selector return (tabStripItem._index === this.selectedIndex && tabStripItem["_visualState"] === "highlighted"); } @@ -873,6 +909,14 @@ export class Tabs extends TabsBase { let newColor = value instanceof Color ? value.ios : value; const itemSelectedAndHighlighted = this.isSelectedAndHightlightedItem(tabStripItem); + // As we cannot implement selected item background color in Tabs we are using the Indicator for this + // To be able to detect that there are two different background colors (one for selected and one for not selected item) + // we are checking whether the current item is not selected and higlighted and we store the value of its + // background color to _defaultItemBackgroundColor and later if we need to process a selected and highlighted item + // we are comparing it's backgroun color to the default one and if there's a difference + // we are changing the selectionIndicatorTemplate from underline to the whole item + // in that mode we are not able to show the indicator as it is used for the background of the selected item + if (!this._defaultItemBackgroundColor && !itemSelectedAndHighlighted) { this._defaultItemBackgroundColor = newColor; } @@ -890,18 +934,40 @@ export class Tabs extends TabsBase { } public setTabBarItemColor(tabStripItem: TabStripItem, value: UIColor | Color): void { - setViewTextAttributes(this._ios.tabBar, tabStripItem.label); + this.setViewTextAttributes(tabStripItem.label); + } + + private setItemColors(): void { + if (this._selectedItemColor) { + this.viewController.tabBar.selectedItemTintColor = this._selectedItemColor.ios; + } + if (this._unSelectedItemColor) { + this.viewController.tabBar.unselectedItemTintColor = this._unSelectedItemColor.ios; + } + } + + private setIconColor(tabStripItem: TabStripItem, forceReload: boolean = false): void { + // if there is no change in the css color and there is no item color set + // we don't need to reload the icon + if (!forceReload && !this._selectedItemColor && !this._unSelectedItemColor) { + return; + } + + let image: UIImage; + + // if selectedItemColor or unSelectedItemColor is set we don't respect the color from the style + const tabStripColor = (this.selectedIndex === tabStripItem._index) ? this._selectedItemColor : this._unSelectedItemColor; + image = this.getIcon(tabStripItem, tabStripColor); + + tabStripItem.nativeView.image = image; } public setTabBarIconColor(tabStripItem: TabStripItem, value: UIColor | Color): void { - const image = this.getIcon(tabStripItem); - - tabStripItem.nativeView.image = image; - tabStripItem.nativeView.selectedImage = image; + this.setIconColor(tabStripItem, true); } public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void { - setViewTextAttributes(this._ios.tabBar, tabStripItem.label); + this.setViewTextAttributes(tabStripItem.label); } public getTabBarFontInternal(): UIFont { @@ -958,6 +1024,22 @@ export class Tabs extends TabsBase { this._ios.tabBar.tintColor = nativeColor; } + public getTabBarSelectedItemColor(): Color { + return this._selectedItemColor; + } + + public setTabBarSelectedItemColor(value: Color) { + this._selectedItemColor = value; + } + + public getTabBarUnSelectedItemColor(): Color { + return this._unSelectedItemColor; + } + + public setTabBarUnSelectedItemColor(value: Color) { + this._unSelectedItemColor = value; + } + private visitFrames(view: ViewBase, operation: (frame: Frame) => {}) { if (view instanceof Frame) { operation(view); @@ -1082,28 +1164,37 @@ export class Tabs extends TabsBase { this.viewController.tabBar.alignment = alignment; } -} -function setViewTextAttributes(tabBar: MDCTabBar, view: View, setSelected: boolean = false): any { - if (!view) { - return null; - } + private setViewTextAttributes(view: View, setSelected: boolean = false): any { + if (!view) { + return null; + } - const defaultTabItemFontSize = 10; - const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize; - const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); + const defaultTabItemFontSize = 10; + const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize; + const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); - tabBar.unselectedItemTitleFont = font; - tabBar.selectedItemTitleFont = font; + this.viewController.tabBar.unselectedItemTitleFont = font; + this.viewController.tabBar.selectedItemTitleFont = font; - const tabItemTextColor = view.style.color; - const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null; - if (textColor) { - tabBar.setTitleColorForState(textColor, MDCTabBarItemState.Normal); - if (setSelected) { - tabBar.setTitleColorForState(textColor, MDCTabBarItemState.Selected); + const tabItemTextColor = view.style.color; + const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null; + + if (textColor) { + this.viewController.tabBar.setTitleColorForState(textColor, MDCTabBarItemState.Normal); + this.viewController.tabBar.setImageTintColorForState(textColor, MDCTabBarItemState.Normal); + + if (setSelected) { + this.viewController.tabBar.setTitleColorForState(textColor, MDCTabBarItemState.Selected); + this.viewController.tabBar.setImageTintColorForState(textColor, MDCTabBarItemState.Selected); + } + } + + if (this._selectedItemColor) { + this.viewController.tabBar.selectedItemTintColor = this._selectedItemColor.ios; + } + if (this._unSelectedItemColor) { + this.viewController.tabBar.unselectedItemTintColor = this._unSelectedItemColor.ios; } } - - tabBar.inkColor = UIColor.clearColor; } \ No newline at end of file diff --git a/tns-core-modules-widgets/android/widgets/build.gradle b/tns-core-modules-widgets/android/widgets/build.gradle index ae8c43767..f7fcfcde0 100644 --- a/tns-core-modules-widgets/android/widgets/build.gradle +++ b/tns-core-modules-widgets/android/widgets/build.gradle @@ -71,9 +71,9 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - + if(project.hasProperty("useAndroidX")) { - println 'Using androix' + println 'Using android X' def androidxVersion = computeAndroidXVersion() implementation 'androidx.viewpager:viewpager:' + androidxVersion implementation 'androidx.fragment:fragment:' + androidxVersion diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java index 20dfdfd0c..b34137ba4 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java @@ -160,7 +160,7 @@ public class TabsBar extends HorizontalScrollView { * {@link TabsBar} you are required to set any * {@link ViewPager.OnPageChangeListener} through this method. This is so * that the layout can update it's scroll position correctly. - * + * * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) */ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { @@ -196,25 +196,25 @@ public class TabsBar extends HorizontalScrollView { TextView textView = (TextView)ll.getChildAt(1); this.setupItem(ll, textView, imgView, tabItem); } - + /** * Gets the TextView for tab item at index */ public TextView getTextViewForItemAt(int index){ LinearLayout ll = this.getViewForItemAt(index); - return (ll != null) ? (TextView)ll.getChildAt(1) : null; + return (ll != null) ? (TextView)ll.getChildAt(1) : null; } - + /** * Gets the LinearLayout container for tab item at index */ public LinearLayout getViewForItemAt(int index){ LinearLayout result = null; - + if(this.mTabStrip.getChildCount() > index){ result = (LinearLayout)this.mTabStrip.getChildAt(index); } - + return result; } @@ -262,10 +262,10 @@ public class TabsBar extends HorizontalScrollView { ll.addView(textView); return ll; } - + private void setupItem(LinearLayout ll, TextView textView,ImageView imgView, TabItemSpec tabItem){ float density = getResources().getDisplayMetrics().density; - + if (tabItem.iconId != 0) { imgView.setImageResource(tabItem.iconId); imgView.setVisibility(VISIBLE); @@ -283,14 +283,14 @@ public class TabsBar extends HorizontalScrollView { if (tabItem.typeFace != null) { textView.setTypeface(tabItem.typeFace); } - + if (tabItem.fontSize != 0) { textView.setTextSize(tabItem.fontSize); } - + if (tabItem.color != 0) { textView.setTextColor(tabItem.color); - mTabStrip.setShouldUpdateTabsTextColor(false); + mTabStrip.setShouldUpdateTabsTextColor(false); } } else { textView.setVisibility(GONE); @@ -305,7 +305,7 @@ public class TabsBar extends HorizontalScrollView { } else { ll.setMinimumHeight((int) (SMALL_MIN_HEIGHT * density)); } - + if (mDistributeEvenly) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); lp.width = 0;