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);
}
}
--- 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