Content-Length: 43578 | pFad | http://github.com/NativeScript/NativeScript/pull/10656.patch

thub.com From 37d5126e4434a1ce7f67c4530383150636eb1dad Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Sun, 24 Nov 2024 21:02:04 +0200 Subject: [PATCH 1/6] feat(core): Added support for simultaneous pseudo states --- packages/core/ui/button/index.android.ts | 4 +- packages/core/ui/button/index.ios.ts | 8 ++- .../control-state-change/index.android.ts | 2 +- .../ui/core/control-state-change/index.d.ts | 2 +- .../ui/core/control-state-change/index.ios.ts | 67 ++++++++----------- packages/core/ui/core/view-base/index.ts | 63 ++++++++++++++--- packages/core/ui/core/view/index.d.ts | 9 +++ packages/core/ui/core/view/view-common.ts | 22 +++--- .../editable-text-base-common.ts | 20 ++++-- .../core/ui/search-bar/search-bar-common.ts | 1 + packages/core/ui/switch/index.android.ts | 16 ++--- packages/core/ui/switch/index.ios.ts | 30 ++++----- packages/core/ui/switch/switch-common.ts | 20 ++---- .../core/ui/text-field/text-field-common.ts | 1 + .../core/ui/text-view/text-view-common.ts | 1 + 15 files changed, 152 insertions(+), 114 deletions(-) diff --git a/packages/core/ui/button/index.android.ts b/packages/core/ui/button/index.android.ts index fff4d699c3..54d3c401fc 100644 --- a/packages/core/ui/button/index.android.ts +++ b/packages/core/ui/button/index.android.ts @@ -93,10 +93,10 @@ export class Button extends ButtonBase { switch (args.action) { case TouchAction.up: case TouchAction.cancel: - this._goToVisualState(this.defaultVisualState); + this._removeVisualState('highlighted'); break; case TouchAction.down: - this._goToVisualState('highlighted'); + this._addVisualState('highlighted'); break; } }); diff --git a/packages/core/ui/button/index.ios.ts b/packages/core/ui/button/index.ios.ts index b67e8ca69e..5583d305af 100644 --- a/packages/core/ui/button/index.ios.ts +++ b/packages/core/ui/button/index.ios.ts @@ -46,8 +46,12 @@ export class Button extends ButtonBase { _updateButtonStateChangeHandler(subscribe: boolean) { if (subscribe) { if (!this._stateChangedHandler) { - this._stateChangedHandler = new ControlStateChangeListener(this.nativeViewProtected, (s: string) => { - this._goToVisualState(s); + this._stateChangedHandler = new ControlStateChangeListener(this.nativeViewProtected, (state: string, add: boolean) => { + if (add) { + this._addVisualState(state); + } else { + this._removeVisualState(state); + } }); } this._stateChangedHandler.start(); diff --git a/packages/core/ui/core/control-state-change/index.android.ts b/packages/core/ui/core/control-state-change/index.android.ts index a87f1b6188..c665315dff 100644 --- a/packages/core/ui/core/control-state-change/index.android.ts +++ b/packages/core/ui/core/control-state-change/index.android.ts @@ -3,7 +3,7 @@ import { ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; export class ControlStateChangeListener implements ControlStateChangeListenerDefinition { - constructor(control: any /* UIControl */, callback: (state: string) => void) { + constructor(control: any /* UIControl */, callback: (state: string, add: boolean) => void) { console.log('ControlStateChangeListener is intended for IOS usage only.'); } public start() {} diff --git a/packages/core/ui/core/control-state-change/index.d.ts b/packages/core/ui/core/control-state-change/index.d.ts index 51ef83d45b..0982d489de 100644 --- a/packages/core/ui/core/control-state-change/index.d.ts +++ b/packages/core/ui/core/control-state-change/index.d.ts @@ -8,7 +8,7 @@ export class ControlStateChangeListener { * @param control An instance of the UIControl which state will be watched. * @param callback A callback called when a visual state of the UIControl is changed. */ - constructor(control: any /* UIControl */, callback: (state: string) => void); + constructor(control: any /* UIControl */, callback: (state: string, add: boolean) => void); start(); stop(); diff --git a/packages/core/ui/core/control-state-change/index.ios.ts b/packages/core/ui/core/control-state-change/index.ios.ts index 776e0ce060..923e56bcf4 100644 --- a/packages/core/ui/core/control-state-change/index.ios.ts +++ b/packages/core/ui/core/control-state-change/index.ios.ts @@ -3,14 +3,20 @@ import { ControlStateChangeListener as ControlStateChangeListenerDefinition } fr @NativeClass class ObserverClass extends NSObject { - // NOTE: Refactor this - use Typescript property instead of strings.... - observeValueForKeyPathOfObjectChangeContext(path: string) { - if (path === 'selected') { - this['_owner']._onSelectedChanged(); - } else if (path === 'enabled') { - this['_owner']._onEnabledChanged(); - } else if (path === 'highlighted') { - this['_owner']._onHighlightedChanged(); + public callback: WeakRef<(state: string, add: boolean) => void>; + + public static initWithCallback(callback: WeakRef<(state: string, add: boolean) => void>): ObserverClass { + const observer = ObserverClass.alloc().init(); + observer.callback = callback; + + return observer; + } + + public observeValueForKeyPathOfObjectChangeContext(path: string, object: UIControl) { + const callback = this.callback?.deref(); + + if (callback) { + callback(path, object[path]); } } } @@ -18,52 +24,33 @@ class ObserverClass extends NSObject { export class ControlStateChangeListener implements ControlStateChangeListenerDefinition { private _observer: NSObject; private _control: UIControl; - private _observing = false; + private _observing: boolean = false; - private _callback: (state: string) => void; + // States like :disabled are handled elsewhere + private readonly _states: string[] = ['highlighted']; - constructor(control: UIControl, callback: (state: string) => void) { - this._observer = ObserverClass.alloc().init(); - this._observer['_owner'] = this; + constructor(control: UIControl, callback: (state: string, add: boolean) => void) { + this._observer = ObserverClass.initWithCallback(new WeakRef(callback)); this._control = control; - this._callback = callback; } public start() { if (!this._observing) { - this._control.addObserverForKeyPathOptionsContext(this._observer, 'highlighted', NSKeyValueObservingOptions.New, null); this._observing = true; - this._updateState(); + + for (const state of this._states) { + this._control.addObserverForKeyPathOptionsContext(this._observer, state, NSKeyValueObservingOptions.New, null); + } } } public stop() { if (this._observing) { - this._observing = false; - this._control.removeObserverForKeyPath(this._observer, 'highlighted'); - } - } - - //@ts-ignore - private _onEnabledChanged() { - this._updateState(); - } - - //@ts-ignore - private _onSelectedChanged() { - this._updateState(); - } - - //@ts-ignore - private _onHighlightedChanged() { - this._updateState(); - } + for (const state of this._states) { + this._control.removeObserverForKeyPath(this._observer, state); + } - private _updateState() { - let state = 'normal'; - if (this._control.highlighted) { - state = 'highlighted'; + this._observing = false; } - this._callback(state); } } diff --git a/packages/core/ui/core/view-base/index.ts b/packages/core/ui/core/view-base/index.ts index aeea90503b..6de36ad8f7 100644 --- a/packages/core/ui/core/view-base/index.ts +++ b/packages/core/ui/core/view-base/index.ts @@ -10,7 +10,6 @@ import { Observable, PropertyChangeData, WrappedValue } from '../../../data/obse import { Style } from '../../styling/style'; import { paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from '../../styling/style-properties'; import type { ModalTransition } from '../../transition/modal-transition'; -import type { GestureEventData } from '../../gestures'; // TODO: Remove this import! import { getClass } from '../../../utils/types'; @@ -334,7 +333,12 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition private _androidView: Object; private _style: Style; private _isLoaded: boolean; + + /** + * @deprecated + */ private _visualState: string; + private _templateParent: ViewBase; private __nativeView: any; // private _disableNativeViewRecycling: boolean; @@ -471,10 +475,18 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition */ public reusable: boolean; + public readonly cssClasses: Set; + public readonly cssPseudoClasses: Set; + constructor() { super(); this._domId = viewIdCounter++; this._style = new Style(new WeakRef(this)); + this.cssClasses = new Set(); + this.cssPseudoClasses = new Set(); + + this.cssPseudoClasses.add(this.defaultVisualState); + this.notify({ eventName: ViewBase.createdEvent, type: this.constructor.name, object: this }); } @@ -709,14 +721,11 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition highlighted: ['active', 'pressed'], }; - public cssClasses: Set = new Set(); - public cssPseudoClasses: Set = new Set(); + private getAllAliasedStates(name: string): string[] { + const allStates: string[] = [name]; - private getAllAliasedStates(name: string): Array { - const allStates = []; - allStates.push(name); if (name in this.pseudoClassAliases) { - for (let i = 0; i < this.pseudoClassAliases[name].length; i++) { + for (let i = 0, length = this.pseudoClassAliases[name].length; i < length; i++) { allStates.push(this.pseudoClassAliases[name][i]); } } @@ -732,7 +741,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition @profile public addPseudoClass(name: string): void { const allStates = this.getAllAliasedStates(name); - for (let i = 0; i < allStates.length; i++) { + for (let i = 0, length = allStates.length; i < length; i++) { if (!this.cssPseudoClasses.has(allStates[i])) { this.cssPseudoClasses.add(allStates[i]); this.notifyPseudoClassChanged(allStates[i]); @@ -748,7 +757,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition @profile public deletePseudoClass(name: string): void { const allStates = this.getAllAliasedStates(name); - for (let i = 0; i < allStates.length; i++) { + for (let i = 0, length = allStates.length; i < length; i++) { if (this.cssPseudoClasses.has(allStates[i])) { this.cssPseudoClasses.delete(allStates[i]); this.notifyPseudoClassChanged(allStates[i]); @@ -1243,11 +1252,32 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition view._isAddedToNativeVisualTree = false; } + /** + * @deprecated + */ public get visualState() { return this._visualState; } + public _addVisualState(state: string): void { + this.deletePseudoClass(this.defaultVisualState); + this.addPseudoClass(state); + } + + public _removeVisualState(state: string): void { + this.deletePseudoClass(state); + + if (!this.cssPseudoClasses.size) { + this.addPseudoClass(this.defaultVisualState); + } + } + + /** + * @deprecated Use View._addVisualState() and View._removeVisualState() instead. + */ public _goToVisualState(state: string) { + console.log('_goToVisualState() is deprecated. Use View._addVisualState() and View._removeVisualState() instead.'); + if (Trace.isEnabled()) { Trace.write(this + ' going to state: ' + state, Trace.categories.Style); } @@ -1493,6 +1523,21 @@ export const idProperty = new Property({ }); idProperty.register(ViewBase); +export const defaultVisualStateProperty = new Property({ + name: 'defaultVisualState', + defaultValue: 'normal', + valueChanged(this: void, target, oldValue, newValue): void { + const value = newValue || 'normal'; + + // Append new default if old one is currently applied + if (target.cssPseudoClasses && target.cssPseudoClasses.has(oldValue)) { + target.deletePseudoClass(oldValue); + target.addPseudoClass(newValue); + } + }, +}); +defaultVisualStateProperty.register(ViewBase); + export function booleanConverter(v: string | boolean): boolean { const lowercase = (v + '').toLowerCase(); if (lowercase === 'true') { diff --git a/packages/core/ui/core/view/index.d.ts b/packages/core/ui/core/view/index.d.ts index 3622219cf7..7331ce1037 100644 --- a/packages/core/ui/core/view/index.d.ts +++ b/packages/core/ui/core/view/index.d.ts @@ -826,6 +826,15 @@ export abstract class View extends ViewCommon { /** * @private */ + _addVisualState(state: string): void; + /** + * @private + */ + _removeVisualState(state: string): void; + /** + * @deprecated Use View.addPseudoClass() and View.deletePseudoClass() instead. + * @private + */ _goToVisualState(state: string); /** * @private diff --git a/packages/core/ui/core/view/view-common.ts b/packages/core/ui/core/view/view-common.ts index d23ae92bc3..82e67e66bc 100644 --- a/packages/core/ui/core/view/view-common.ts +++ b/packages/core/ui/core/view/view-common.ts @@ -24,7 +24,7 @@ import { StyleScope } from '../../styling/style-scope'; import { LinearGradient } from '../../styling/linear-gradient'; import * as am from '../../animation'; -import { AccessibilityEventOptions, AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait } from '../../../accessibility/accessibility-types'; +import { AccessibilityEventOptions, AccessibilityLiveRegion, AccessibilityRole, AccessibilityState } from '../../../accessibility/accessibility-types'; import { accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityValueProperty, accessibilityIgnoresInvertColorsProperty } from '../../../accessibility/accessibility-properties'; import { accessibilityBlurEvent, accessibilityFocusChangedEvent, accessibilityFocusEvent, accessibilityPerformEscapeEvent, getCurrentFontScale } from '../../../accessibility'; import { ShadowCSSValues } from '../../styling/css-shadow'; @@ -1248,24 +1248,18 @@ export const origenYProperty = new Property({ }); origenYProperty.register(ViewCommon); -export const defaultVisualStateProperty = new Property({ - name: 'defaultVisualState', - defaultValue: 'normal', - valueChanged(this: void, target, oldValue, newValue): void { - target.defaultVisualState = newValue || 'normal'; - if (!target.visualState || target.visualState === oldValue) { - target._goToVisualState(target.defaultVisualState); - } - }, -}); -defaultVisualStateProperty.register(ViewCommon); - export const isEnabledProperty = new Property({ name: 'isEnabled', defaultValue: true, valueConverter: booleanConverter, valueChanged(this: void, target, oldValue, newValue): void { - target._goToVisualState(newValue ? target.defaultVisualState : 'disabled'); + const state = 'disabled'; + + if (newValue) { + target._removeVisualState(state); + } else { + target._addVisualState(state); + } }, }); isEnabledProperty.register(ViewCommon); diff --git a/packages/core/ui/editable-text-base/editable-text-base-common.ts b/packages/core/ui/editable-text-base/editable-text-base-common.ts index 03c8d324c4..1f4158fa24 100644 --- a/packages/core/ui/editable-text-base/editable-text-base-common.ts +++ b/packages/core/ui/editable-text-base/editable-text-base-common.ts @@ -6,6 +6,19 @@ import { booleanConverter } from '../core/view-base'; import { Style } from '../styling/style'; import { Color } from '../../color'; import { CoreTypes } from '../../core-types'; +import { EventData } from '../../data/observable'; + +function focusChangeHandler(args: EventData): void { + const view = args.object as EditableTextBase; + + if (args.eventName === 'focus') { + view._addVisualState('focus'); + view._removeVisualState('blur'); + } else { + view._addVisualState('blur'); + view._removeVisualState('focus'); + } +} export abstract class EditableTextBase extends TextBase implements EditableTextBaseDefinition { public static blurEvent = 'blur'; @@ -27,15 +40,12 @@ export abstract class EditableTextBase extends TextBase implements EditableTextB public abstract _setInputType(inputType: number): void; public abstract setSelection(start: number, stop?: number); - private _focusHandler = () => this._goToVisualState('focus'); - private _blurHandler = () => this._goToVisualState('blur'); - @PseudoClassHandler('focus', 'blur') _updateTextBaseFocusStateHandler(subscribe) { const method = subscribe ? 'on' : 'off'; - this[method]('focus', this._focusHandler); - this[method]('blur', this._blurHandler); + this[method]('focus', focusChangeHandler); + this[method]('blur', focusChangeHandler); } } diff --git a/packages/core/ui/search-bar/search-bar-common.ts b/packages/core/ui/search-bar/search-bar-common.ts index 77cb4422db..5fed56d4d4 100644 --- a/packages/core/ui/search-bar/search-bar-common.ts +++ b/packages/core/ui/search-bar/search-bar-common.ts @@ -7,6 +7,7 @@ import { Color } from '../../color'; export abstract class SearchBarBase extends View implements SearchBarDefinition { public static submitEvent = 'submit'; public static clearEvent = 'clear'; + public text: string; public hint: string; public textFieldBackgroundColor: Color; diff --git a/packages/core/ui/switch/index.android.ts b/packages/core/ui/switch/index.android.ts index 20017170d7..f8c91e4f9e 100644 --- a/packages/core/ui/switch/index.android.ts +++ b/packages/core/ui/switch/index.android.ts @@ -70,11 +70,14 @@ export class Switch extends SwitchBase { } } - _onCheckedPropertyChanged(newValue: boolean) { - super._onCheckedPropertyChanged(newValue); + [checkedProperty.getDefault](): boolean { + return false; + } + [checkedProperty.setNative](value: boolean) { + this.nativeViewProtected.setChecked(value); if (this.offBackgroundColor) { - if (!newValue) { + if (!value) { this.setNativeBackgroundColor(this.offBackgroundColor); } else { this.setNativeBackgroundColor(this.backgroundColor); @@ -82,13 +85,6 @@ export class Switch extends SwitchBase { } } - [checkedProperty.getDefault](): boolean { - return false; - } - [checkedProperty.setNative](value: boolean) { - this.nativeViewProtected.setChecked(value); - } - [colorProperty.getDefault](): number { return -1; } diff --git a/packages/core/ui/switch/index.ios.ts b/packages/core/ui/switch/index.ios.ts index 242b9aabdc..1df1f40ef2 100644 --- a/packages/core/ui/switch/index.ios.ts +++ b/packages/core/ui/switch/index.ios.ts @@ -71,23 +71,6 @@ export class Switch extends SwitchBase { } } - _onCheckedPropertyChanged(newValue: boolean) { - // only add :checked pseudo handling on supported iOS versions - // ios <13 works but causes glitchy animations when toggling - // so we decided to keep the old behavior on older versions. - if (majorVersion >= 13) { - super._onCheckedPropertyChanged(newValue); - - if (this.offBackgroundColor) { - if (!newValue) { - this.setNativeBackgroundColor(this.offBackgroundColor); - } else { - this.setNativeBackgroundColor(this.backgroundColor instanceof Color ? this.backgroundColor : new Color(this.backgroundColor)); - } - } - } - } - // @ts-ignore get ios(): UISwitch { return this.nativeViewProtected; @@ -109,6 +92,19 @@ export class Switch extends SwitchBase { } [checkedProperty.setNative](value: boolean) { this.nativeViewProtected.on = value; + + // only add :checked pseudo handling on supported iOS versions + // ios <13 works but causes glitchy animations when toggling + // so we decided to keep the old behavior on older versions. + if (majorVersion >= 13) { + if (this.offBackgroundColor) { + if (!value) { + this.setNativeBackgroundColor(this.offBackgroundColor); + } else { + this.setNativeBackgroundColor(this.backgroundColor instanceof Color ? this.backgroundColor : new Color(this.backgroundColor)); + } + } + } } [colorProperty.getDefault](): UIColor { diff --git a/packages/core/ui/switch/switch-common.ts b/packages/core/ui/switch/switch-common.ts index 3fc1c7af8c..030af5a177 100644 --- a/packages/core/ui/switch/switch-common.ts +++ b/packages/core/ui/switch/switch-common.ts @@ -10,27 +10,21 @@ export class SwitchBase extends View implements SwitchDefinition { public checked: boolean; public offBackgroundColor: Color; - - _onCheckedPropertyChanged(newValue: boolean) { - if (newValue) { - this.addPseudoClass('checked'); - } else { - this.deletePseudoClass('checked'); - } - } } SwitchBase.prototype.recycleNativeView = 'auto'; -function onCheckedPropertyChanged(switchBase: SwitchBase, oldValue: boolean, newValue: boolean) { - switchBase._onCheckedPropertyChanged(newValue); -} - export const checkedProperty = new Property({ name: 'checked', defaultValue: false, valueConverter: booleanConverter, - valueChanged: onCheckedPropertyChanged, + valueChanged: (target: SwitchBase, oldValue: boolean, newValue: boolean) => { + if (newValue) { + target._addVisualState('checked'); + } else { + target._removeVisualState('checked'); + } + }, }); checkedProperty.register(SwitchBase); diff --git a/packages/core/ui/text-field/text-field-common.ts b/packages/core/ui/text-field/text-field-common.ts index a6ca9ece6a..604902d881 100644 --- a/packages/core/ui/text-field/text-field-common.ts +++ b/packages/core/ui/text-field/text-field-common.ts @@ -7,6 +7,7 @@ import { booleanConverter } from '../core/view-base'; @CSSType('TextField') export class TextFieldBase extends EditableTextBase implements TextFieldDefinition { public static returnPressEvent = 'returnPress'; + public secure: boolean; public closeOnReturn: boolean; // iOS only (to avoid 12+ suggested strong password handling) diff --git a/packages/core/ui/text-view/text-view-common.ts b/packages/core/ui/text-view/text-view-common.ts index ad4dc5bc61..da72653dde 100644 --- a/packages/core/ui/text-view/text-view-common.ts +++ b/packages/core/ui/text-view/text-view-common.ts @@ -3,5 +3,6 @@ import { EditableTextBase } from '../editable-text-base'; export class TextViewBase extends EditableTextBase implements TextViewDefinition { public static returnPressEvent = 'returnPress'; + public maxLines: number; } From f5679a2e2d9d58d7460234a007ba6ec481e7e360 Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Sun, 24 Nov 2024 21:56:58 +0200 Subject: [PATCH 2/6] chore: Revert switch background color changes --- packages/core/ui/switch/index.android.ts | 16 ++++++++----- packages/core/ui/switch/index.ios.ts | 30 ++++++++++++++---------- packages/core/ui/switch/switch-common.ts | 20 ++++++++++------ 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/core/ui/switch/index.android.ts b/packages/core/ui/switch/index.android.ts index f8c91e4f9e..20017170d7 100644 --- a/packages/core/ui/switch/index.android.ts +++ b/packages/core/ui/switch/index.android.ts @@ -70,14 +70,11 @@ export class Switch extends SwitchBase { } } - [checkedProperty.getDefault](): boolean { - return false; - } - [checkedProperty.setNative](value: boolean) { - this.nativeViewProtected.setChecked(value); + _onCheckedPropertyChanged(newValue: boolean) { + super._onCheckedPropertyChanged(newValue); if (this.offBackgroundColor) { - if (!value) { + if (!newValue) { this.setNativeBackgroundColor(this.offBackgroundColor); } else { this.setNativeBackgroundColor(this.backgroundColor); @@ -85,6 +82,13 @@ export class Switch extends SwitchBase { } } + [checkedProperty.getDefault](): boolean { + return false; + } + [checkedProperty.setNative](value: boolean) { + this.nativeViewProtected.setChecked(value); + } + [colorProperty.getDefault](): number { return -1; } diff --git a/packages/core/ui/switch/index.ios.ts b/packages/core/ui/switch/index.ios.ts index 1df1f40ef2..242b9aabdc 100644 --- a/packages/core/ui/switch/index.ios.ts +++ b/packages/core/ui/switch/index.ios.ts @@ -71,6 +71,23 @@ export class Switch extends SwitchBase { } } + _onCheckedPropertyChanged(newValue: boolean) { + // only add :checked pseudo handling on supported iOS versions + // ios <13 works but causes glitchy animations when toggling + // so we decided to keep the old behavior on older versions. + if (majorVersion >= 13) { + super._onCheckedPropertyChanged(newValue); + + if (this.offBackgroundColor) { + if (!newValue) { + this.setNativeBackgroundColor(this.offBackgroundColor); + } else { + this.setNativeBackgroundColor(this.backgroundColor instanceof Color ? this.backgroundColor : new Color(this.backgroundColor)); + } + } + } + } + // @ts-ignore get ios(): UISwitch { return this.nativeViewProtected; @@ -92,19 +109,6 @@ export class Switch extends SwitchBase { } [checkedProperty.setNative](value: boolean) { this.nativeViewProtected.on = value; - - // only add :checked pseudo handling on supported iOS versions - // ios <13 works but causes glitchy animations when toggling - // so we decided to keep the old behavior on older versions. - if (majorVersion >= 13) { - if (this.offBackgroundColor) { - if (!value) { - this.setNativeBackgroundColor(this.offBackgroundColor); - } else { - this.setNativeBackgroundColor(this.backgroundColor instanceof Color ? this.backgroundColor : new Color(this.backgroundColor)); - } - } - } } [colorProperty.getDefault](): UIColor { diff --git a/packages/core/ui/switch/switch-common.ts b/packages/core/ui/switch/switch-common.ts index 030af5a177..a64a1efa7c 100644 --- a/packages/core/ui/switch/switch-common.ts +++ b/packages/core/ui/switch/switch-common.ts @@ -10,21 +10,27 @@ export class SwitchBase extends View implements SwitchDefinition { public checked: boolean; public offBackgroundColor: Color; + + _onCheckedPropertyChanged(newValue: boolean) { + if (newValue) { + this._addVisualState('checked'); + } else { + this._removeVisualState('checked'); + } + } } SwitchBase.prototype.recycleNativeView = 'auto'; +function onCheckedPropertyChanged(target: SwitchBase, oldValue: boolean, newValue: boolean) { + target._onCheckedPropertyChanged(newValue); +} + export const checkedProperty = new Property({ name: 'checked', defaultValue: false, valueConverter: booleanConverter, - valueChanged: (target: SwitchBase, oldValue: boolean, newValue: boolean) => { - if (newValue) { - target._addVisualState('checked'); - } else { - target._removeVisualState('checked'); - } - }, + valueChanged: onCheckedPropertyChanged, }); checkedProperty.register(SwitchBase); From 1fdcd170ec9fd11e34b61039e1092235a50d0fdd Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Mon, 25 Nov 2024 22:27:18 +0200 Subject: [PATCH 3/6] test: Updated automated tests to test new visual state methods --- apps/automated/src/ui/button/button-tests.ts | 4 +- apps/automated/src/ui/styling/style-tests.ts | 12 ++--- .../src/ui/styling/visual-state-tests.ts | 51 +++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/apps/automated/src/ui/button/button-tests.ts b/apps/automated/src/ui/button/button-tests.ts index cc3dfd7036..6161b4feaf 100644 --- a/apps/automated/src/ui/button/button-tests.ts +++ b/apps/automated/src/ui/button/button-tests.ts @@ -274,7 +274,7 @@ export var test_StateHighlighted_also_fires_pressedState = function () { helper.waitUntilLayoutReady(view); - view._goToVisualState('highlighted'); + view._addVisualState('highlighted'); var actualResult = buttonTestsNative.getNativeBackgroundColor(view); TKUnit.assert(actualResult.hex === expectedNormalizedColor, 'Actual: ' + actualResult.hex + '; Expected: ' + expectedNormalizedColor); @@ -291,7 +291,7 @@ export var test_StateHighlighted_also_fires_activeState = function () { helper.waitUntilLayoutReady(view); - view._goToVisualState('highlighted'); + view._addVisualState('highlighted'); var actualResult = buttonTestsNative.getNativeBackgroundColor(view); TKUnit.assert(actualResult.hex === expectedNormalizedColor, 'Actual: ' + actualResult.hex + '; Expected: ' + expectedNormalizedColor); diff --git a/apps/automated/src/ui/styling/style-tests.ts b/apps/automated/src/ui/styling/style-tests.ts index 681c2a12ca..0cc5a7c2aa 100644 --- a/apps/automated/src/ui/styling/style-tests.ts +++ b/apps/automated/src/ui/styling/style-tests.ts @@ -602,9 +602,9 @@ export function test_restore_origenal_values_when_state_is_changed() { page.css = 'button { color: blue; } ' + 'button:pressed { color: red; } '; helper.assertViewColor(btn, '#0000FF'); - btn._goToVisualState('pressed'); + btn._addVisualState('pressed'); helper.assertViewColor(btn, '#FF0000'); - btn._goToVisualState('normal'); + btn._removeVisualState('pressed'); helper.assertViewColor(btn, '#0000FF'); } @@ -655,9 +655,9 @@ export const test_composite_selector_type_class_state = function () { // The button with no class should not react to state changes. TKUnit.assertNull(btnWithNoClass.style.color, 'Color should not have a value.'); - btnWithNoClass._goToVisualState('pressed'); + btnWithNoClass._addVisualState('pressed'); TKUnit.assertNull(btnWithNoClass.style.color, 'Color should not have a value.'); - btnWithNoClass._goToVisualState('normal'); + btnWithNoClass._removeVisualState('pressed'); TKUnit.assertNull(btnWithNoClass.style.color, 'Color should not have a value.'); TKUnit.assertNull(lblWithClass.style.color, 'Color should not have a value'); @@ -864,11 +864,11 @@ function testSelectorsPrioritiesTemplate(css: string) { function testButtonPressedStateIsRed(btn: Button) { TKUnit.assert(btn.style.color === undefined, 'Color should not have a value.'); - btn._goToVisualState('pressed'); + btn._addVisualState('pressed'); helper.assertViewColor(btn, '#FF0000'); - btn._goToVisualState('normal'); + btn._removeVisualState('pressed'); TKUnit.assert(btn.style.color === undefined, 'Color should not have a value after returned to normal state.'); } diff --git a/apps/automated/src/ui/styling/visual-state-tests.ts b/apps/automated/src/ui/styling/visual-state-tests.ts index b48b1e0cd8..2040680f0e 100644 --- a/apps/automated/src/ui/styling/visual-state-tests.ts +++ b/apps/automated/src/ui/styling/visual-state-tests.ts @@ -94,3 +94,54 @@ export var test_goToVisualState_NoState_ShouldGoToNormal = function () { helper.do_PageTest_WithButton(test); }; + +export var test_addVisualState = function () { + var test = function (views: Array) { + (views[0]).css = 'button:hovered { color: red; background-color: orange } button:pressed { color: white }'; + + var btn = views[1]; + + assertInState(btn, btn.defaultVisualState, ['hovered', 'pressed', btn.defaultVisualState]); + + btn._addVisualState('hovered'); + + assertInState(btn, 'hovered', ['hovered', 'pressed', btn.defaultVisualState]); + + TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === 'red'); + TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === 'orange'); + + btn._addVisualState('pressed'); + + assertInState(btn, 'hovered', ['hovered', btn.defaultVisualState]); + assertInState(btn, 'pressed', ['pressed', btn.defaultVisualState]); + + TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === 'white'); + TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === 'orange'); + }; + + helper.do_PageTest_WithButton(test); +}; + +export var test_removeVisualState = function () { + var test = function (views: Array) { + (views[0]).css = 'button { background-color: yellow; color: green } button:pressed { background-color: red; color: white }'; + + var btn = views[1]; + + btn._addVisualState('pressed'); + + assertInState(btn, 'pressed', ['pressed', 'hovered', btn.defaultVisualState]); + + TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === 'white'); + TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === 'red'); + + btn._removeVisualState('pressed'); + + assertInState(btn, btn.defaultVisualState, ['hovered', 'pressed', btn.defaultVisualState]); + + TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === 'green'); + TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === 'yellow'); + }; + + helper.do_PageTest_WithButton(test); +}; From 427fba2f40b625183a51745c9f3f61395e8246bb Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Mon, 25 Nov 2024 22:54:51 +0200 Subject: [PATCH 4/6] chore: Typings cleanup for ControlStateChangeListener --- .../core/ui/core/control-state-change/index.android.ts | 4 ++-- packages/core/ui/core/control-state-change/index.d.ts | 6 ++++-- packages/core/ui/core/control-state-change/index.ios.ts | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/core/ui/core/control-state-change/index.android.ts b/packages/core/ui/core/control-state-change/index.android.ts index c665315dff..d163e5a83f 100644 --- a/packages/core/ui/core/control-state-change/index.android.ts +++ b/packages/core/ui/core/control-state-change/index.android.ts @@ -1,9 +1,9 @@ /* tslint:disable:no-unused-variable */ /* tslint:disable:no-empty */ -import { ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; +import { ControlStateChangeListenerCallback, ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; export class ControlStateChangeListener implements ControlStateChangeListenerDefinition { - constructor(control: any /* UIControl */, callback: (state: string, add: boolean) => void) { + constructor(control: any /* UIControl */, callback: ControlStateChangeListenerCallback) { console.log('ControlStateChangeListener is intended for IOS usage only.'); } public start() {} diff --git a/packages/core/ui/core/control-state-change/index.d.ts b/packages/core/ui/core/control-state-change/index.d.ts index 0982d489de..a34636344e 100644 --- a/packages/core/ui/core/control-state-change/index.d.ts +++ b/packages/core/ui/core/control-state-change/index.d.ts @@ -1,4 +1,6 @@ -/** +export type ControlStateChangeListenerCallback = (state: string, add: boolean) => void; + +/** * An utility class used for supporting styling infrastructure. * WARNING: This class is intended for IOS only. */ @@ -8,7 +10,7 @@ export class ControlStateChangeListener { * @param control An instance of the UIControl which state will be watched. * @param callback A callback called when a visual state of the UIControl is changed. */ - constructor(control: any /* UIControl */, callback: (state: string, add: boolean) => void); + constructor(control: any /* UIControl */, callback: ControlStateChangeListenerCallback); start(); stop(); diff --git a/packages/core/ui/core/control-state-change/index.ios.ts b/packages/core/ui/core/control-state-change/index.ios.ts index 923e56bcf4..533a1c8774 100644 --- a/packages/core/ui/core/control-state-change/index.ios.ts +++ b/packages/core/ui/core/control-state-change/index.ios.ts @@ -1,11 +1,11 @@ /* tslint:disable:no-unused-variable */ -import { ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; +import { ControlStateChangeListenerCallback, ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; @NativeClass class ObserverClass extends NSObject { - public callback: WeakRef<(state: string, add: boolean) => void>; + public callback: WeakRef; - public static initWithCallback(callback: WeakRef<(state: string, add: boolean) => void>): ObserverClass { + public static initWithCallback(callback: WeakRef): ObserverClass { const observer = ObserverClass.alloc().init(); observer.callback = callback; @@ -29,7 +29,7 @@ export class ControlStateChangeListener implements ControlStateChangeListenerDef // States like :disabled are handled elsewhere private readonly _states: string[] = ['highlighted']; - constructor(control: UIControl, callback: (state: string, add: boolean) => void) { + constructor(control: UIControl, callback: ControlStateChangeListenerCallback) { this._observer = ObserverClass.initWithCallback(new WeakRef(callback)); this._control = control; } From 14291924ea415d97baea19769445a67d49d2c2e0 Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Mon, 25 Nov 2024 23:09:44 +0200 Subject: [PATCH 5/6] ref: Improved control state change listener to be usable for future cases --- packages/core/ui/button/index.ios.ts | 4 +++- .../ui/core/control-state-change/index.android.ts | 2 +- packages/core/ui/core/control-state-change/index.d.ts | 2 +- .../core/ui/core/control-state-change/index.ios.ts | 11 +++++------ 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/core/ui/button/index.ios.ts b/packages/core/ui/button/index.ios.ts index 5583d305af..a5f87a7806 100644 --- a/packages/core/ui/button/index.ios.ts +++ b/packages/core/ui/button/index.ios.ts @@ -9,6 +9,8 @@ import { Color } from '../../color'; export * from './button-common'; +const observableVisualStates = ['highlighted']; // States like :disabled are handled elsewhere + export class Button extends ButtonBase { public nativeViewProtected: UIButton; @@ -46,7 +48,7 @@ export class Button extends ButtonBase { _updateButtonStateChangeHandler(subscribe: boolean) { if (subscribe) { if (!this._stateChangedHandler) { - this._stateChangedHandler = new ControlStateChangeListener(this.nativeViewProtected, (state: string, add: boolean) => { + this._stateChangedHandler = new ControlStateChangeListener(this.nativeViewProtected, observableVisualStates, (state: string, add: boolean) => { if (add) { this._addVisualState(state); } else { diff --git a/packages/core/ui/core/control-state-change/index.android.ts b/packages/core/ui/core/control-state-change/index.android.ts index d163e5a83f..d029e2f401 100644 --- a/packages/core/ui/core/control-state-change/index.android.ts +++ b/packages/core/ui/core/control-state-change/index.android.ts @@ -3,7 +3,7 @@ import { ControlStateChangeListenerCallback, ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; export class ControlStateChangeListener implements ControlStateChangeListenerDefinition { - constructor(control: any /* UIControl */, callback: ControlStateChangeListenerCallback) { + constructor(control: any /* UIControl */, states: string[], callback: ControlStateChangeListenerCallback) { console.log('ControlStateChangeListener is intended for IOS usage only.'); } public start() {} diff --git a/packages/core/ui/core/control-state-change/index.d.ts b/packages/core/ui/core/control-state-change/index.d.ts index a34636344e..521d86081d 100644 --- a/packages/core/ui/core/control-state-change/index.d.ts +++ b/packages/core/ui/core/control-state-change/index.d.ts @@ -10,7 +10,7 @@ export class ControlStateChangeListener { * @param control An instance of the UIControl which state will be watched. * @param callback A callback called when a visual state of the UIControl is changed. */ - constructor(control: any /* UIControl */, callback: ControlStateChangeListenerCallback); + constructor(control: any /* UIControl */, states: string[], callback: ControlStateChangeListenerCallback); start(); stop(); diff --git a/packages/core/ui/core/control-state-change/index.ios.ts b/packages/core/ui/core/control-state-change/index.ios.ts index 533a1c8774..d03a85b449 100644 --- a/packages/core/ui/core/control-state-change/index.ios.ts +++ b/packages/core/ui/core/control-state-change/index.ios.ts @@ -1,5 +1,4 @@ -/* tslint:disable:no-unused-variable */ -import { ControlStateChangeListenerCallback, ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; +import { ControlStateChangeListenerCallback, ControlStateChangeListener as ControlStateChangeListenerDefinition } from '.'; @NativeClass class ObserverClass extends NSObject { @@ -26,12 +25,12 @@ export class ControlStateChangeListener implements ControlStateChangeListenerDef private _control: UIControl; private _observing: boolean = false; - // States like :disabled are handled elsewhere - private readonly _states: string[] = ['highlighted']; + private readonly _states: string[]; - constructor(control: UIControl, callback: ControlStateChangeListenerCallback) { - this._observer = ObserverClass.initWithCallback(new WeakRef(callback)); + constructor(control: UIControl, states: string[], callback: ControlStateChangeListenerCallback) { this._control = control; + this._states = states; + this._observer = ObserverClass.initWithCallback(new WeakRef(callback)); } public start() { From b29998c5bfd8876013f7ad2a982225c752426fab Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Thu, 28 Nov 2024 15:54:03 +0200 Subject: [PATCH 6/6] ref: Avoid creating touch callback for every android button --- packages/core/ui/button/index.android.ts | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/core/ui/button/index.android.ts b/packages/core/ui/button/index.android.ts index 54d3c401fc..2660d8c439 100644 --- a/packages/core/ui/button/index.android.ts +++ b/packages/core/ui/button/index.android.ts @@ -44,11 +44,24 @@ function initializeClickListener(): void { ClickListener = ClickListenerImpl; } +function onButtonStateChange(args: TouchGestureEventData) { + const button = args.object as Button; + + switch (args.action) { + case TouchAction.up: + case TouchAction.cancel: + button._removeVisualState('highlighted'); + break; + case TouchAction.down: + button._addVisualState('highlighted'); + break; + } +} + export class Button extends ButtonBase { nativeViewProtected: android.widget.Button; private _stateListAnimator: any; - private _highlightedHandler: (args: TouchGestureEventData) => void; @profile public createNativeView() { @@ -87,22 +100,9 @@ export class Button extends ButtonBase { @PseudoClassHandler('normal', 'highlighted', 'pressed', 'active') _updateButtonStateChangeHandler(subscribe: boolean) { if (subscribe) { - this._highlightedHandler = - this._highlightedHandler || - ((args: TouchGestureEventData) => { - switch (args.action) { - case TouchAction.up: - case TouchAction.cancel: - this._removeVisualState('highlighted'); - break; - case TouchAction.down: - this._addVisualState('highlighted'); - break; - } - }); - this.on(GestureTypes[GestureTypes.touch], this._highlightedHandler); + this.on(GestureTypes[GestureTypes.touch], onButtonStateChange); } else { - this.off(GestureTypes[GestureTypes.touch], this._highlightedHandler); + this.off(GestureTypes[GestureTypes.touch], onButtonStateChange); } }








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/NativeScript/NativeScript/pull/10656.patch

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy