mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Merge remote-tracking branch 'origin/main' into feat/v9-prerelease
This commit is contained in:
@@ -97,11 +97,6 @@ interface ApplicationEvents {
|
||||
*/
|
||||
on(event: 'livesync', callback: (args: ApplicationEventData) => void, thisArg?: any): void;
|
||||
|
||||
/**
|
||||
* This event is raised when application css is changed.
|
||||
*/
|
||||
on(event: 'cssChanged', callback: (args: CssChangedEventData) => void, thisArg?: any): void;
|
||||
|
||||
/**
|
||||
* This event is raised on application launchEvent.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ImageAssetBase extends Observable implements ImageAssetDefinition {
|
||||
}
|
||||
|
||||
set options(value: ImageAssetOptions) {
|
||||
this._options = value;
|
||||
this._options = normalizeImageAssetOptions(value);
|
||||
}
|
||||
|
||||
get nativeImage(): any {
|
||||
@@ -35,6 +35,35 @@ export class ImageAssetBase extends Observable implements ImageAssetDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
function toPositiveInt(value: any): number {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return value > 0 ? Math.floor(value) : 0;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const parsed = parseInt(value, 10);
|
||||
return isNaN(parsed) || parsed <= 0 ? 0 : parsed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function normalizeImageAssetOptions(options: ImageAssetOptions): ImageAssetOptions {
|
||||
const normalized = options ? { ...options } : ({} as ImageAssetOptions);
|
||||
// Coerce potential string values to positive integers; fallback to 0
|
||||
// to trigger default sizing downstream
|
||||
(normalized as any).width = toPositiveInt((options as any)?.width);
|
||||
(normalized as any).height = toPositiveInt((options as any)?.height);
|
||||
if (typeof normalized.keepAspectRatio !== 'boolean') {
|
||||
normalized.keepAspectRatio = true;
|
||||
}
|
||||
if (typeof normalized.autoScaleFactor !== 'boolean') {
|
||||
normalized.autoScaleFactor = true;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function getAspectSafeDimensions(sourceWidth, sourceHeight, reqWidth, reqHeight) {
|
||||
const widthCoef = sourceWidth / reqWidth;
|
||||
const heightCoef = sourceHeight / reqHeight;
|
||||
@@ -47,8 +76,9 @@ export function getAspectSafeDimensions(sourceWidth, sourceHeight, reqWidth, req
|
||||
}
|
||||
|
||||
export function getRequestedImageSize(src: { width: number; height: number }, options: ImageAssetOptions): { width: number; height: number } {
|
||||
let reqWidth = options.width || Math.min(src.width, Screen.mainScreen.widthPixels);
|
||||
let reqHeight = options.height || Math.min(src.height, Screen.mainScreen.heightPixels);
|
||||
const normalized = normalizeImageAssetOptions(options);
|
||||
let reqWidth = normalized.width || Math.min(src.width, Screen.mainScreen.widthPixels);
|
||||
let reqHeight = normalized.height || Math.min(src.height, Screen.mainScreen.heightPixels);
|
||||
|
||||
if (options && options.keepAspectRatio) {
|
||||
const safeAspectSize = getAspectSafeDimensions(src.width, src.height, reqWidth, reqHeight);
|
||||
|
||||
27
packages/core/image-asset/image-asset-options.spec.ts
Normal file
27
packages/core/image-asset/image-asset-options.spec.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { getRequestedImageSize } from './image-asset-common';
|
||||
|
||||
describe('ImageAssetOptions normalization', () => {
|
||||
it('coerces string width/height to numbers', () => {
|
||||
const src = { width: 2000, height: 1500 };
|
||||
const result = getRequestedImageSize(src as any, { width: '300' as any, height: '200' as any, keepAspectRatio: false, autoScaleFactor: true } as any);
|
||||
expect(result.width).toBe(300);
|
||||
expect(result.height).toBe(200);
|
||||
});
|
||||
|
||||
it('falls back to defaults when invalid strings provided', () => {
|
||||
const src = { width: 800, height: 600 };
|
||||
const result = getRequestedImageSize(src as any, { width: 'abc' as any, height: '' as any, keepAspectRatio: false } as any);
|
||||
// should fall back to screen pixel defaults via getRequestedImageSize, but since
|
||||
// we cannot easily control Screen.mainScreen here, we at least assert they are > 0
|
||||
expect(result.width).toBeGreaterThan(0);
|
||||
expect(result.height).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('respects keepAspectRatio by adjusting to safe dimensions', () => {
|
||||
const src = { width: 2000, height: 1000 };
|
||||
const result = getRequestedImageSize(src as any, { width: '500' as any, height: '500' as any, keepAspectRatio: true } as any);
|
||||
// current implementation scales using the smaller coefficient (min), so expect 1000x500
|
||||
expect(result.width).toBe(1000);
|
||||
expect(result.height).toBe(500);
|
||||
});
|
||||
});
|
||||
@@ -181,13 +181,14 @@ export class ImageSource implements ImageSourceDefinition {
|
||||
const textBounds = new android.graphics.Rect();
|
||||
paint.getTextBounds(source, 0, source.length, textBounds);
|
||||
|
||||
const textWidth = textBounds.width();
|
||||
const textHeight = textBounds.height();
|
||||
const padding = 1;
|
||||
const textWidth = textBounds.width() + padding * 2;
|
||||
const textHeight = textBounds.height() + padding * 2;
|
||||
if (textWidth > 0 && textHeight > 0) {
|
||||
const bitmap = android.graphics.Bitmap.createBitmap(textWidth, textHeight, android.graphics.Bitmap.Config.ARGB_8888);
|
||||
|
||||
const canvas = new android.graphics.Canvas(bitmap);
|
||||
canvas.drawText(source, -textBounds.left, -textBounds.top, paint);
|
||||
canvas.drawText(source, -textBounds.left + padding, -textBounds.top + padding, paint);
|
||||
|
||||
return new ImageSource(bitmap);
|
||||
}
|
||||
|
||||
Binary file not shown.
2
packages/core/references.d.ts
vendored
2
packages/core/references.d.ts
vendored
@@ -4,7 +4,7 @@
|
||||
/// <reference path="../types-ios/src/lib/ios/objc-x86_64/objc!Darwin.d.ts" />
|
||||
/// <reference path="../types-ios/src/lib/ios/objc-x86_64/objc!DarwinFoundation.d.ts" />
|
||||
/// <reference path="../types-ios/src/lib/ios/objc-x86_64/objc!Symbols.d.ts" />
|
||||
/// <reference path="../types-android/src/lib/android-29.d.ts" />
|
||||
/// <reference path="../types-android/src/lib/android-30.d.ts" />
|
||||
/// <reference path="./platforms/ios/typings/objc!MaterialComponents.d.ts" />
|
||||
/// <reference path="./platforms/ios/typings/objc!NativeScriptUtils.d.ts" />
|
||||
/// <reference path="./global-types.d.ts" />
|
||||
|
||||
15
packages/core/ui/animation/index.d.ts
vendored
15
packages/core/ui/animation/index.d.ts
vendored
@@ -1,3 +1,16 @@
|
||||
export type { Pair, Transformation, TransformationType, TransformationValue, TransformFunctionsInfo, Point3D, AnimationPromise, Cancelable } from './animation-types';
|
||||
export { Animation, _resolveAnimationCurve } from './animation';
|
||||
export { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo } from './keyframe-animation';
|
||||
import type { AnimationDefinition, AnimationPromise } from './animation-types';
|
||||
|
||||
/**
|
||||
* Defines a animation set.
|
||||
*/
|
||||
export class Animation {
|
||||
constructor(animationDefinitions: Array<AnimationDefinition>, playSequentially?: boolean);
|
||||
public play: (resetOnFinish?: boolean) => AnimationPromise;
|
||||
public cancel: () => void;
|
||||
public isPlaying: boolean;
|
||||
public _resolveAnimationCurve(curve: any): any;
|
||||
}
|
||||
|
||||
export function _resolveAnimationCurve(curve: any): any;
|
||||
|
||||
2
packages/core/ui/button/index.d.ts
vendored
2
packages/core/ui/button/index.d.ts
vendored
@@ -10,7 +10,7 @@ export class Button extends TextBase {
|
||||
/**
|
||||
* String value used when hooking to tap event.
|
||||
*
|
||||
* @nsEvent {EventData} string;
|
||||
* @nsEvent {EventData} tap
|
||||
*/
|
||||
public static tapEvent: string;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Point, Position } from './view-interfaces';
|
||||
import type { GestureTypes, GestureEventData } from '../../gestures';
|
||||
import { getNativeScriptGlobals } from '../../../globals/global-utils';
|
||||
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, AndroidHelper } from './view-common';
|
||||
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, AndroidHelper, statusBarStyleProperty } from './view-common';
|
||||
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../../styling/style-properties';
|
||||
import { Length } from '../../styling/length-shared';
|
||||
import { layout } from '../../../utils';
|
||||
@@ -52,6 +52,10 @@ const GRAVITY_FILL_HORIZONTAL = 7; // android.view.Gravity.FILL_HORIZONTAL
|
||||
const GRAVITY_CENTER_VERTICAL = 16; // android.view.Gravity.CENTER_VERTICAL
|
||||
const GRAVITY_FILL_VERTICAL = 112; // android.view.Gravity.FILL_VERTICAL
|
||||
|
||||
const SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
|
||||
const STATUS_BAR_LIGHT_BCKG = -657931;
|
||||
const STATUS_BAR_DARK_BCKG = 1711276032;
|
||||
|
||||
const modalMap = new Map<number, DialogOptions>();
|
||||
|
||||
let TouchListener: TouchListener;
|
||||
@@ -1218,6 +1222,89 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
[statusBarStyleProperty.getDefault]() {
|
||||
return this.style.statusBarStyle;
|
||||
}
|
||||
|
||||
[statusBarStyleProperty.setNative](value: 'dark' | 'light' | { color: number; systemUiVisibility: number }) {
|
||||
this.updateStatusBarStyle(value);
|
||||
}
|
||||
|
||||
updateStatusBarStyle(value: 'dark' | 'light' | { color: number; systemUiVisibility: number }) {
|
||||
if (SDK_VERSION < 21) return; // nothing we can do
|
||||
|
||||
const window = this.getClosestWindow();
|
||||
// Ensure the window draws system bar backgrounds (required to color status bar)
|
||||
window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
|
||||
const decorView = window.getDecorView();
|
||||
|
||||
// API 30+ path (preferred)
|
||||
const controller = window.getInsetsController?.();
|
||||
if (controller && SDK_VERSION >= 30) {
|
||||
const APPEARANCE_LIGHT_STATUS_BARS = android.view.WindowInsetsController?.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
this.style.statusBarStyle = value;
|
||||
if (value === 'light') {
|
||||
// light icons/text
|
||||
controller.setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS);
|
||||
} else {
|
||||
// dark icons/text
|
||||
controller.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS);
|
||||
}
|
||||
} else {
|
||||
if (value.color != null) window.setStatusBarColor(value.color);
|
||||
// No direct passthrough for systemUiVisibility on API 30+, use appearances instead
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// API 23–29 path (systemUiVisibility)
|
||||
if (SDK_VERSION >= 23) {
|
||||
if (typeof value === 'string') {
|
||||
this.style.statusBarStyle = value;
|
||||
let flags = decorView.getSystemUiVisibility();
|
||||
if (value === 'light') {
|
||||
// Add the LIGHT_STATUS_BAR bit without clobbering other flags
|
||||
flags |= android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
||||
decorView.setSystemUiVisibility(flags);
|
||||
} else {
|
||||
// Remove only the LIGHT_STATUS_BAR bit
|
||||
flags &= ~android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
||||
decorView.setSystemUiVisibility(flags);
|
||||
}
|
||||
} else {
|
||||
if (value.color != null) window.setStatusBarColor(value.color);
|
||||
if (value.systemUiVisibility != null) {
|
||||
// Preserve existing flags, don’t blindly overwrite to 0
|
||||
const merged = (decorView.getSystemUiVisibility() & ~android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) | (value.systemUiVisibility & android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
decorView.setSystemUiVisibility(merged);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// API 21–22: you can only change the background color; icon color is fixed (light)
|
||||
if (typeof value === 'object' && value.color != null) {
|
||||
window.setStatusBarColor(value.color);
|
||||
}
|
||||
}
|
||||
|
||||
getClosestWindow(): android.view.Window {
|
||||
// When it comes to modals, check parent as it may not be the modal root view itself
|
||||
const view = this.parent ?? this;
|
||||
const dialogFragment = (view as this)._dialogFragment;
|
||||
if (dialogFragment) {
|
||||
const dialog = dialogFragment.getDialog();
|
||||
if (dialog) {
|
||||
return dialog.getWindow();
|
||||
}
|
||||
}
|
||||
return this._context.getWindow();
|
||||
}
|
||||
|
||||
[testIDProperty.setNative](value: string) {
|
||||
this.setAccessibilityIdentifier(this.nativeViewProtected, value);
|
||||
}
|
||||
|
||||
6
packages/core/ui/core/view/index.d.ts
vendored
6
packages/core/ui/core/view/index.d.ts
vendored
@@ -649,8 +649,10 @@ export abstract class View extends ViewCommon {
|
||||
cssType: string;
|
||||
|
||||
/**
|
||||
* (iOS only) Gets or sets the status bar style for this view.
|
||||
* Note: You must remove Info.plist key `UIViewControllerBasedStatusBarAppearance`
|
||||
* Gets or sets the status bar style for this view.
|
||||
* Platform Notes:
|
||||
* - Android: When using this property throughout navigations, ensure starting views have it set as well. Ensures it will reset on back navigation.
|
||||
* - iOS: You must remove Info.plist key `UIViewControllerBasedStatusBarAppearance`
|
||||
* It defaults to true when not present: https://developer.apple.com/documentation/bundleresources/information-property-list/uiviewcontrollerbasedstatusbarappearance
|
||||
* Or you can explicitly set it to true:
|
||||
* <key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Point, Position } from './view-interfaces';
|
||||
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, iosGlassEffectProperty, GlassEffectType, GlassEffectVariant } from './view-common';
|
||||
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, iosGlassEffectProperty, GlassEffectType, GlassEffectVariant, statusBarStyleProperty } from './view-common';
|
||||
import { isAccessibilityServiceEnabled } from '../../../application';
|
||||
import { updateA11yPropertiesCallback } from '../../../application/helpers-common';
|
||||
import { ShowModalOptions, hiddenProperty } from '../view-base';
|
||||
@@ -904,6 +904,28 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
[statusBarStyleProperty.getDefault]() {
|
||||
return this.style.statusBarStyle;
|
||||
}
|
||||
[statusBarStyleProperty.setNative](value: 'light' | 'dark') {
|
||||
this.style.statusBarStyle = value;
|
||||
const parent = this.parent;
|
||||
if (parent) {
|
||||
const ctrl = parent.ios?.controller;
|
||||
if (ctrl && ctrl instanceof UINavigationController) {
|
||||
const navigationBar = ctrl.navigationBar;
|
||||
if (!navigationBar) return;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
console.log('here:', value);
|
||||
navigationBar.barStyle = value === 'dark' ? UIBarStyle.Black : UIBarStyle.Default;
|
||||
} else {
|
||||
navigationBar.barStyle = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[iosGlassEffectProperty.setNative](value: GlassEffectType) {
|
||||
if (!this.nativeViewProtected || !supportsGlass()) {
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,8 @@ import { layout } from '../../../utils';
|
||||
import { isObject } from '../../../utils/types';
|
||||
import { sanitizeModuleName } from '../../../utils/common';
|
||||
import { Color } from '../../../color';
|
||||
import { Property, InheritedProperty } from '../properties';
|
||||
import { Property, InheritedProperty, CssProperty } from '../properties';
|
||||
import { Style } from '../../styling/style';
|
||||
import { EventData } from '../../../data/observable';
|
||||
import { ViewHelper } from './view-helper';
|
||||
import { setupAccessibleView } from '../../../application/helpers';
|
||||
@@ -195,6 +196,12 @@ export abstract class ViewCommon extends ViewBase {
|
||||
super.onLoaded();
|
||||
|
||||
setupAccessibleView(this);
|
||||
|
||||
if (this.statusBarStyle) {
|
||||
// reapply status bar style on load
|
||||
// helps back navigation cases to restore if overridden
|
||||
this.updateStatusBarStyle(this.statusBarStyle);
|
||||
}
|
||||
}
|
||||
|
||||
public _closeAllModalViewsInternal(): boolean {
|
||||
@@ -972,6 +979,14 @@ export abstract class ViewCommon extends ViewBase {
|
||||
this.style.androidDynamicElevationOffset = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Android only) Gets closest window parent considering modals.
|
||||
*/
|
||||
getClosestWindow(): android.view.Window {
|
||||
// platform impl
|
||||
return null;
|
||||
}
|
||||
|
||||
//END Style property shortcuts
|
||||
|
||||
public originX: number;
|
||||
@@ -1005,6 +1020,10 @@ export abstract class ViewCommon extends ViewBase {
|
||||
this.style.statusBarStyle = value;
|
||||
}
|
||||
|
||||
updateStatusBarStyle(value: 'dark' | 'light') {
|
||||
// platform specific impl
|
||||
}
|
||||
|
||||
get isLayoutRequired(): boolean {
|
||||
return true;
|
||||
}
|
||||
@@ -1298,6 +1317,15 @@ export const isUserInteractionEnabledProperty = new Property<ViewCommon, boolean
|
||||
});
|
||||
isUserInteractionEnabledProperty.register(ViewCommon);
|
||||
|
||||
/**
|
||||
* Property backing statusBarStyle.
|
||||
*/
|
||||
export const statusBarStyleProperty = new CssProperty<Style, 'light' | 'dark'>({
|
||||
name: 'statusBarStyle',
|
||||
cssName: 'status-bar-style',
|
||||
});
|
||||
statusBarStyleProperty.register(Style);
|
||||
|
||||
// Apple only
|
||||
export const iosOverflowSafeAreaProperty = new Property<ViewCommon, boolean>({
|
||||
name: 'iosOverflowSafeArea',
|
||||
|
||||
@@ -634,6 +634,11 @@ class UINavigationControllerImpl extends UINavigationController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
public get childViewControllerForStatusBarStyle() {
|
||||
return this.topViewController;
|
||||
}
|
||||
}
|
||||
|
||||
function _getTransitionId(nativeTransition: UIViewAnimationTransition, transitionType: string): string {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { isAccessibilityServiceEnabled } from '../../application';
|
||||
import { PageBase, actionBarHiddenProperty, statusBarStyleProperty, androidStatusBarBackgroundProperty } from './page-common';
|
||||
import { PageBase, actionBarHiddenProperty, androidStatusBarBackgroundProperty } from './page-common';
|
||||
import { View } from '../core/view';
|
||||
import { Color } from '../../color';
|
||||
import { ActionBar } from '../action-bar';
|
||||
@@ -10,10 +10,6 @@ import { AndroidAccessibilityEvent, getLastFocusedViewOnPage } from '../../acces
|
||||
|
||||
export * from './page-common';
|
||||
|
||||
const SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
|
||||
const STATUS_BAR_LIGHT_BCKG = -657931;
|
||||
const STATUS_BAR_DARK_BCKG = 1711276032;
|
||||
|
||||
export class Page extends PageBase {
|
||||
nativeViewProtected: org.nativescript.widgets.GridLayout;
|
||||
|
||||
@@ -69,19 +65,6 @@ export class Page extends PageBase {
|
||||
}
|
||||
}
|
||||
|
||||
private getClosestWindow() {
|
||||
// When it comes to modals, check if page has a parent as it may not be the modal root view itself
|
||||
const view = this.parent ?? this;
|
||||
const dialogFragment = (<any>view)._dialogFragment;
|
||||
if (dialogFragment) {
|
||||
const dialog = dialogFragment.getDialog();
|
||||
if (dialog) {
|
||||
return dialog.getWindow();
|
||||
}
|
||||
}
|
||||
return this._context.getWindow();
|
||||
}
|
||||
|
||||
[actionBarHiddenProperty.setNative](value: boolean) {
|
||||
// in case the actionBar is not created and actionBarHidden is changed to true
|
||||
// the actionBar will be created by updateActionBar
|
||||
@@ -90,44 +73,10 @@ export class Page extends PageBase {
|
||||
}
|
||||
}
|
||||
|
||||
[statusBarStyleProperty.getDefault](): {
|
||||
color: number;
|
||||
systemUiVisibility: number;
|
||||
} {
|
||||
if (SDK_VERSION >= 21) {
|
||||
const window = this.getClosestWindow();
|
||||
const decorView = window.getDecorView();
|
||||
|
||||
return {
|
||||
color: (<any>window).getStatusBarColor(),
|
||||
systemUiVisibility: decorView.getSystemUiVisibility(),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
[statusBarStyleProperty.setNative](value: 'dark' | 'light' | { color: number; systemUiVisibility: number }) {
|
||||
if (SDK_VERSION >= 21) {
|
||||
const window = this.getClosestWindow();
|
||||
const decorView = window.getDecorView();
|
||||
|
||||
if (value === 'light') {
|
||||
(<any>window).setStatusBarColor(STATUS_BAR_LIGHT_BCKG);
|
||||
decorView.setSystemUiVisibility(SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
} else if (value === 'dark') {
|
||||
(<any>window).setStatusBarColor(STATUS_BAR_DARK_BCKG);
|
||||
decorView.setSystemUiVisibility(0);
|
||||
} else {
|
||||
(<any>window).setStatusBarColor(value.color);
|
||||
decorView.setSystemUiVisibility(value.systemUiVisibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[androidStatusBarBackgroundProperty.getDefault](): number {
|
||||
if (SDK_VERSION >= 21) {
|
||||
const window = this.getClosestWindow();
|
||||
return (<any>window).getStatusBarColor();
|
||||
return window.getStatusBarColor();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -136,7 +85,7 @@ export class Page extends PageBase {
|
||||
if (SDK_VERSION >= 21) {
|
||||
const window = this.getClosestWindow();
|
||||
const color = value instanceof Color ? value.android : value;
|
||||
(<any>window).setStatusBarColor(color);
|
||||
window.setStatusBarColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { isAccessibilityServiceEnabled } from '../../application';
|
||||
import type { Frame } from '../frame';
|
||||
import { BackstackEntry, NavigationType } from '../frame/frame-interfaces';
|
||||
import { View, IOSHelper } from '../core/view';
|
||||
import { PageBase, actionBarHiddenProperty, statusBarStyleProperty } from './page-common';
|
||||
import { PageBase, actionBarHiddenProperty } from './page-common';
|
||||
|
||||
import { profile } from '../../profiling';
|
||||
import { layout } from '../../utils/layout-helper';
|
||||
@@ -350,7 +350,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
const owner = this._owner?.deref();
|
||||
if (owner) {
|
||||
if (SDK_VERSION >= 13) {
|
||||
return owner.statusBarStyle === 'dark' ? UIStatusBarStyle.DarkContent : UIStatusBarStyle.LightContent;
|
||||
return owner.statusBarStyle === 'light' ? UIStatusBarStyle.LightContent : UIStatusBarStyle.DarkContent;
|
||||
} else {
|
||||
return owner.statusBarStyle === 'dark' ? UIStatusBarStyle.LightContent : UIStatusBarStyle.Default;
|
||||
}
|
||||
@@ -561,21 +561,6 @@ export class Page extends PageBase {
|
||||
}
|
||||
}
|
||||
|
||||
[statusBarStyleProperty.getDefault](): UIBarStyle {
|
||||
return UIBarStyle.Default;
|
||||
}
|
||||
[statusBarStyleProperty.setNative](value: string | UIBarStyle) {
|
||||
const frame = this.frame;
|
||||
if (frame) {
|
||||
const navigationBar = (<UINavigationController>frame.ios.controller).navigationBar;
|
||||
if (typeof value === 'string') {
|
||||
navigationBar.barStyle = value === 'dark' ? UIBarStyle.Black : UIBarStyle.Default;
|
||||
} else {
|
||||
navigationBar.barStyle = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public accessibilityScreenChanged(refocus = false): void {
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
return;
|
||||
|
||||
@@ -207,15 +207,6 @@ export const enableSwipeBackNavigationProperty = new Property<PageBase, boolean>
|
||||
});
|
||||
enableSwipeBackNavigationProperty.register(PageBase);
|
||||
|
||||
/**
|
||||
* Property backing statusBarStyle.
|
||||
*/
|
||||
export const statusBarStyleProperty = new CssProperty<Style, 'light' | 'dark'>({
|
||||
name: 'statusBarStyle',
|
||||
cssName: 'status-bar-style',
|
||||
});
|
||||
statusBarStyleProperty.register(Style);
|
||||
|
||||
/**
|
||||
* Property backing androidStatusBarBackground.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Font } from '../styling/font';
|
||||
import { SearchBarBase, textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty } from './search-bar-common';
|
||||
import { SearchBarBase, textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty, clearButtonColorProperty } from './search-bar-common';
|
||||
import { isUserInteractionEnabledProperty, isEnabledProperty } from '../core/view';
|
||||
import { ad } from '../../utils';
|
||||
import { Color } from '../../color';
|
||||
@@ -33,6 +33,7 @@ function initializeNativeClasses(): void {
|
||||
constructor(private owner: SearchBar) {
|
||||
super();
|
||||
|
||||
// @ts-ignore
|
||||
return global.__native(this);
|
||||
}
|
||||
|
||||
@@ -70,6 +71,7 @@ function initializeNativeClasses(): void {
|
||||
constructor(private owner: SearchBar) {
|
||||
super();
|
||||
|
||||
// @ts-ignore
|
||||
return global.__native(this);
|
||||
}
|
||||
|
||||
@@ -272,6 +274,25 @@ export class SearchBar extends SearchBarBase {
|
||||
const color = value instanceof Color ? value.android : value;
|
||||
textView.setHintTextColor(color);
|
||||
}
|
||||
[clearButtonColorProperty.setNative](value: Color) {
|
||||
if (!this.nativeViewProtected || !value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// The close (clear) button inside the SearchView can be found by its resource ID
|
||||
const closeButtonId = this.nativeViewProtected.getContext().getResources().getIdentifier('android:id/search_close_btn', null, null);
|
||||
const closeButton = this.nativeViewProtected.findViewById(closeButtonId) as android.widget.ImageView;
|
||||
|
||||
const color = value instanceof Color ? value.android : new Color(value).android;
|
||||
|
||||
if (closeButton) {
|
||||
closeButton.setColorFilter(color);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error setting clear button color:', err);
|
||||
}
|
||||
}
|
||||
|
||||
private _getTextView(): android.widget.TextView {
|
||||
if (!this._searchTextView) {
|
||||
|
||||
7
packages/core/ui/search-bar/index.d.ts
vendored
7
packages/core/ui/search-bar/index.d.ts
vendored
@@ -61,6 +61,13 @@ export class SearchBar extends View {
|
||||
*/
|
||||
textFieldHintColor: Color;
|
||||
|
||||
/**
|
||||
* Gets or sets the Clear Button color of the SearchBar component.
|
||||
*
|
||||
* @nsProperty
|
||||
*/
|
||||
clearButtonColor: Color | string;
|
||||
|
||||
/**
|
||||
* Adds a listener for the specified event name.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Font } from '../styling/font';
|
||||
import { SearchBarBase, textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty } from './search-bar-common';
|
||||
import { SearchBarBase, textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty, clearButtonColorProperty } from './search-bar-common';
|
||||
import { isEnabledProperty } from '../core/view';
|
||||
import { Color } from '../../color';
|
||||
import { colorProperty, backgroundColorProperty, backgroundInternalProperty, fontInternalProperty } from '../styling/style-properties';
|
||||
@@ -220,4 +220,13 @@ export class SearchBar extends SearchBarBase {
|
||||
const attributedPlaceholder = NSAttributedString.alloc().initWithStringAttributes(stringValue, attributes);
|
||||
this._getTextField().attributedPlaceholder = attributedPlaceholder;
|
||||
}
|
||||
[clearButtonColorProperty.setNative](value: Color | UIColor) {
|
||||
const textField = this._getTextField();
|
||||
if (!textField) return;
|
||||
// Check if clear button is available in the text field
|
||||
const clearButton = textField.valueForKey('clearButton');
|
||||
if (!clearButton) return;
|
||||
|
||||
clearButton.tintColor = value instanceof Color ? value.ios : value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export abstract class SearchBarBase extends View implements SearchBarDefinition
|
||||
public hint: string;
|
||||
public textFieldBackgroundColor: Color;
|
||||
public textFieldHintColor: Color;
|
||||
public clearButtonColor: Color | string;
|
||||
|
||||
public abstract dismissSoftInput();
|
||||
}
|
||||
@@ -44,3 +45,11 @@ export const textFieldBackgroundColorProperty = new Property<SearchBarBase, Colo
|
||||
valueConverter: (v) => new Color(v),
|
||||
});
|
||||
textFieldBackgroundColorProperty.register(SearchBarBase);
|
||||
|
||||
// --- Added property for clear button color ---
|
||||
export const clearButtonColorProperty = new Property<SearchBarBase, Color>({
|
||||
name: 'clearButtonColor',
|
||||
equalityComparer: Color.equals,
|
||||
valueConverter: (v) => new Color(v),
|
||||
});
|
||||
clearButtonColorProperty.register(SearchBarBase);
|
||||
|
||||
Reference in New Issue
Block a user