diff --git a/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.android.ts b/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.android.ts index af7eb0450..b22fb4eb7 100644 --- a/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.android.ts +++ b/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.android.ts @@ -1,4 +1,9 @@ import * as segmentedBarModule from '@nativescript/core/ui/segmented-bar'; +import { Color } from '@nativescript/core'; + +export function getNativeTabWidget(bar: segmentedBarModule.SegmentedBar): android.widget.TabWidget { + return (bar.android).getTabWidget(); +} export function getNativeItemsCount(bar: segmentedBarModule.SegmentedBar): number { return (bar.android).getTabWidget().getTabCount(); @@ -25,3 +30,54 @@ export function checkNativeItemsTextColor(bar: segmentedBarModule.SegmentedBar): export function setNativeSelectedIndex(bar: segmentedBarModule.SegmentedBar, index: number): void { (bar.android).setCurrentTab(index); } + +export var checkBackgroundColorUpdatedAfterItemSelected = function (bar: segmentedBarModule.SegmentedBar): boolean { + let isValid = 0; + bar.selectedIndex = 0; + bar.selectedTextColor = new Color('green'); + bar.selectedBackgroundColor = new Color('red'); + + const tabWidget = getNativeTabWidget(bar); + if (tabWidget) { + for (let i = 0; i < tabWidget.getTabCount(); i++) { + const view = tabWidget.getChildTabViewAt(i); + const item = bar.items[i]; + const textView = item?.nativeViewProtected; + + const newDrawable = tryCloneDrawable(view.getBackground(), view.getResources()); + newDrawable.setColorFilter(new android.graphics.Paint(bar.selectedBackgroundColor.android).getColorFilter()); + + if (bar.selectedIndex == i) { + if (view.getBackground() !== newDrawable) { + console.log('>>>>>>>>>>>>>>>>>>>>>> newDrawable', view.getBackground()); + console.log('>>>>>>>>>>>>>>>>>>>>>> bar.selectedBackgroundColor.android', newDrawable); + console.log('>>>>>>>>>>>>>>>>>>>>>> selectedBackgroundColor', newDrawable.getColorFilter(), view.getBackground().getColorFilter()); + console.log('>>>>>>>>>>>>>>>>>>>>>> selectedBackgroundColor', newDrawable.hashCode(), view.hashCode()); + + isValid++; + break; + } else if (textView.getCurrentTextColor() !== bar.selectedTextColor) { + console.log('>>>>>>>>>>>>>>>>>>>>>>'); + console.log('>>>>>>>>>>>>>>>>>>>>>>'); + console.log('>>>>>>>>>>>>>>>>>>>>>> selectedTextColor'); + + isValid++; + break; + } + } + } + } + + function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable { + if (value) { + const constantState = value.getConstantState(); + if (constantState) { + return constantState.newDrawable(resources); + } + } + + return value; + } + + return isValid === 0; +}; diff --git a/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.d.ts b/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.d.ts index 9741f90e1..4249f2db5 100644 --- a/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.d.ts +++ b/apps/automated/src/ui/segmented-bar/segmented-bar-tests-native.d.ts @@ -5,3 +5,5 @@ export declare function getNativeItemsCount(bar: segmentedBarModule.SegmentedBar export declare function setNativeSelectedIndex(bar: segmentedBarModule.SegmentedBar, index: number): void; export declare function checkNativeItemsTextColor(bar: segmentedBarModule.SegmentedBar): boolean; + +export declare function checkBackgroundColorUpdatedAfterItemSelected(bar: segmentedBarModule.SegmentedBar): boolean; diff --git a/apps/automated/src/ui/segmented-bar/segmented-bar-tests.ts b/apps/automated/src/ui/segmented-bar/segmented-bar-tests.ts index 7bbfa61fa..14f452990 100644 --- a/apps/automated/src/ui/segmented-bar/segmented-bar-tests.ts +++ b/apps/automated/src/ui/segmented-bar/segmented-bar-tests.ts @@ -276,3 +276,21 @@ export function test_SettingNumberAsTitleFromXML_DoesNotThrow() { TKUnit.assertEqual(item.title, '1'); }); } + +/*export function testBackgroundColorUpdatedAfterItemSelected() { + let segmentedBar = new segmentedBarModule.SegmentedBar(); + let item1 = new segmentedBarModule.SegmentedBarItem(); + (item1).title = 1; + let item2 = new segmentedBarModule.SegmentedBarItem(); + (item2).title = 2; + let item3 = new segmentedBarModule.SegmentedBarItem(); + (item3).title = 3; + let item4 = new segmentedBarModule.SegmentedBarItem(); + (item4).title = 4; + + segmentedBar.items = [item1, item2, item3, item4]; + + buildUIAndRunTest(segmentedBar, function (views: Array) { + TKUnit.assertTrue(segmentedBarTestsNative.checkBackgroundColorUpdatedAfterItemSelected(segmentedBar)); + }); +}*/ diff --git a/apps/ui/src/segmented-bar/all-page.xml b/apps/ui/src/segmented-bar/all-page.xml index 6b08fdc0e..021304a4e 100644 --- a/apps/ui/src/segmented-bar/all-page.xml +++ b/apps/ui/src/segmented-bar/all-page.xml @@ -7,7 +7,20 @@ - + + + + + + + + diff --git a/packages/core/ui/segmented-bar/index.android.ts b/packages/core/ui/segmented-bar/index.android.ts index fda7afcf0..400334cb7 100644 --- a/packages/core/ui/segmented-bar/index.android.ts +++ b/packages/core/ui/segmented-bar/index.android.ts @@ -1,10 +1,11 @@ import { Font } from '../styling/font'; -import { SegmentedBarItemBase, SegmentedBarBase, selectedIndexProperty, itemsProperty, selectedBackgroundColorProperty } from './segmented-bar-common'; +import { SegmentedBarItemBase, SegmentedBarBase, selectedIndexProperty, itemsProperty, selectedBackgroundColorProperty, selectedTextColorProperty } from './segmented-bar-common'; import { isEnabledProperty } from '../core/view'; import { colorProperty, fontInternalProperty, fontSizeProperty } from '../styling/style-properties'; import { Color } from '../../color'; import { layout } from '../../utils'; import { SDK_VERSION } from '../../utils/constants'; +import { Trace } from '../../trace'; export * from './segmented-bar-common'; @@ -51,8 +52,13 @@ function initializeNativeClasses(): void { onTabChanged(id: string): void { const owner = this.owner; - if (owner.shouldChangeSelectedIndex()) { - owner.selectedIndex = parseInt(id); + if (owner) { + setTimeout(() => { + owner.setTabColor(id); + }); + if (owner.shouldChangeSelectedIndex()) { + owner.selectedIndex = parseInt(id); + } } } } @@ -165,7 +171,7 @@ export class SegmentedBarItem extends SegmentedBarItemBase { const backgroundDrawable = viewGroup.getBackground(); if (SDK_VERSION > 21 && backgroundDrawable) { const newDrawable = tryCloneDrawable(backgroundDrawable, nativeView.getResources()); - newDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN); + newDrawable.setColorFilter(new android.graphics.Paint(color).getColorFilter()); viewGroup.setBackground(newDrawable); } else { const stateDrawable = new android.graphics.drawable.StateListDrawable(); @@ -292,4 +298,42 @@ export class SegmentedBar extends SegmentedBarBase { tabWidget.setEnabled(value); } } + public setTabColor(index) { + try { + const tabWidget = this.nativeViewProtected?.getTabWidget(); + if (tabWidget) { + const unselectedTextColor = this.getColorForAndroid(this.color ?? '#6e6e6e'); + const selectedTextColor = this.getColorForAndroid(this?.selectedTextColor ?? '#000000'); + const unselectedBackgroundColor = this.getColorForAndroid(this?.backgroundColor ?? '#dbdbdb'); + const selectedBackgroundColor = this.getColorForAndroid(this?.selectedBackgroundColor ?? this?.backgroundColor ?? 'blue'); + if (tabWidget) { + for (let i = 0; i < tabWidget.getTabCount(); i++) { + const view = tabWidget.getChildTabViewAt(i); + const item = this.items[i]; + const textView = item?.nativeViewProtected; + view.setBackgroundColor(unselectedBackgroundColor); + if (textView) { + textView.setTextColor(unselectedTextColor); + } + if (index == i) { + view.setBackgroundColor(selectedBackgroundColor); + if (textView) { + textView.setTextColor(selectedTextColor); + } + continue; + } + } + } + } + } catch (e) { + Trace.error(e); + } + } + private getColorForAndroid(color: string | Color): number { + if (typeof color === 'string') { + return new Color(color).android; + } else if (color instanceof Color) { + return color.android; + } + } } diff --git a/packages/core/ui/segmented-bar/index.d.ts b/packages/core/ui/segmented-bar/index.d.ts index 4a972513a..9da03da9a 100644 --- a/packages/core/ui/segmented-bar/index.d.ts +++ b/packages/core/ui/segmented-bar/index.d.ts @@ -44,6 +44,11 @@ export class SegmentedBar extends View implements AddChildFromBuilder, AddArrayF */ selectedBackgroundColor: Color; + /** + * Gets or sets the selected text color of the SegmentedBar component. + */ + selectedTextColor: Color; + /** * Gets or sets the items of the SegmentedBar. */ @@ -90,3 +95,8 @@ export const selectedBackgroundColorProperty: CssProperty; * Gets or sets the items dependency property of the SegmentedBar. */ export const itemsProperty: Property; + +/** + * Gets or sets the selected text color property of the SegmentedBar. + */ +export const selectedTextColorProperty: CssProperty; diff --git a/packages/core/ui/segmented-bar/index.ios.ts b/packages/core/ui/segmented-bar/index.ios.ts index 653d82611..27e27ea12 100644 --- a/packages/core/ui/segmented-bar/index.ios.ts +++ b/packages/core/ui/segmented-bar/index.ios.ts @@ -2,7 +2,8 @@ import { Font } from '../styling/font'; import { SegmentedBarItemBase, SegmentedBarBase, selectedIndexProperty, itemsProperty, selectedBackgroundColorProperty } from './segmented-bar-common'; import { colorProperty, fontInternalProperty } from '../styling/style-properties'; import { Color } from '../../color'; -import { iOSNativeHelper } from '../../utils'; +import { Trace } from '../../trace'; +import { SDK_VERSION } from '../../utils'; export * from './segmented-bar-common'; export class SegmentedBarItem extends SegmentedBarItemBase { @@ -68,14 +69,11 @@ export class SegmentedBar extends SegmentedBarBase { } [selectedBackgroundColorProperty.getDefault](): UIColor { - const currentOsVersion = iOSNativeHelper.MajorVersion; - - return currentOsVersion < 13 ? this.ios.tintColor : this.ios.selectedSegmentTintColor; + return SDK_VERSION < 13 ? this.ios.tintColor : this.ios.selectedSegmentTintColor; } [selectedBackgroundColorProperty.setNative](value: UIColor | Color) { - const currentOsVersion = iOSNativeHelper.MajorVersion; const color = value instanceof Color ? value.ios : value; - if (currentOsVersion < 13) { + if (SDK_VERSION < 13) { this.ios.tintColor = color; } else { this.ios.selectedSegmentTintColor = color; @@ -92,6 +90,8 @@ export class SegmentedBar extends SegmentedBarBase { const attrs = currentAttrs ? currentAttrs.mutableCopy() : NSMutableDictionary.new(); attrs.setValueForKey(color, NSForegroundColorAttributeName); bar.setTitleTextAttributesForState(attrs, UIControlState.Normal); + // Set the selected text color + this.setSelectedTextColor(bar); } [fontInternalProperty.getDefault](): Font { @@ -105,6 +105,27 @@ export class SegmentedBar extends SegmentedBarBase { attrs.setValueForKey(font, NSFontAttributeName); bar.setTitleTextAttributesForState(attrs, UIControlState.Normal); } + setSelectedTextColor(bar: UISegmentedControl) { + try { + const selectedTextColor = this.getColorForIOS(this?.selectedTextColor ?? this?.color ?? '#000000'); + if (!selectedTextColor) { + Trace.write(`unable te set selectedTextColor`, Trace.categories.Error); + } + const selectedText = bar.titleTextAttributesForState(UIControlState.Selected); + const attrsSelected = selectedText ? selectedText.mutableCopy() : NSMutableDictionary.new(); + attrsSelected.setValueForKey(selectedTextColor, NSForegroundColorAttributeName); + bar.setTitleTextAttributesForState(attrsSelected, UIControlState.Selected); + } catch (e) { + console.error(`SegmentedBar:`, e); + } + } + private getColorForIOS(color: string | Color): UIColor { + if (typeof color === 'string') { + return new Color(color).ios; + } else if (color instanceof Color) { + return color.ios; + } + } } @NativeClass @@ -122,6 +143,7 @@ class SelectionHandlerImpl extends NSObject { const owner = this._owner?.deref(); if (owner) { owner.selectedIndex = sender.selectedSegmentIndex; + owner.setSelectedTextColor(sender); } } diff --git a/packages/core/ui/segmented-bar/segmented-bar-common.ts b/packages/core/ui/segmented-bar/segmented-bar-common.ts index d486dae0c..03247835f 100644 --- a/packages/core/ui/segmented-bar/segmented-bar-common.ts +++ b/packages/core/ui/segmented-bar/segmented-bar-common.ts @@ -38,6 +38,13 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin this.style.selectedBackgroundColor = value; } + public get selectedTextColor(): Color { + return this.style.selectedTabTextColor; + } + public set selectedTextColor(value: Color) { + this.style.selectedTabTextColor = value; + } + public _addArrayFromBuilder(name: string, value: Array): void { if (name === 'items') { this.items = value; @@ -141,6 +148,16 @@ export const selectedBackgroundColorProperty = new InheritedCssProperty new Color(v), }); selectedBackgroundColorProperty.register(Style); + +export const selectedTextColorProperty = new InheritedCssProperty({ + name: 'selectedTextColor', + cssName: 'selected-text-color', + equalityComparer: Color.equals, + defaultValue: new Color('black'), + valueConverter: (v) => new Color(v), +}); +selectedTextColorProperty.register(Style); diff --git a/packages/core/ui/styling/style/index.ts b/packages/core/ui/styling/style/index.ts index e083f7243..47254bd65 100644 --- a/packages/core/ui/styling/style/index.ts +++ b/packages/core/ui/styling/style/index.ts @@ -206,6 +206,7 @@ export class Style extends Observable implements StyleDefinition { //SegmentedBar-specific props public selectedBackgroundColor: Color; + public selectedTextColor: Color; // Page-specific props public statusBarStyle: 'light' | 'dark'; diff --git a/packages/core/utils/ios/index.ts b/packages/core/utils/ios/index.ts index c8a79beb7..261208a0d 100644 --- a/packages/core/utils/ios/index.ts +++ b/packages/core/utils/ios/index.ts @@ -94,6 +94,9 @@ export function isLandscape(): boolean { return isDeviceOrientationLandscape || isStatusBarOrientationLandscape; } +/** + * @deprecated use Utils.SDK_VERSION instead which is a float of the {major}.{minor} verison + */ export const MajorVersion = NSString.stringWithString(UIDevice.currentDevice.systemVersion).intValue; export function openFile(filePath: string): boolean {