diff --git a/tns-core-modules/ui/action-bar/action-bar-common.ts b/tns-core-modules/ui/action-bar/action-bar-common.ts index c3fc2b884..1e5c7edb4 100644 --- a/tns-core-modules/ui/action-bar/action-bar-common.ts +++ b/tns-core-modules/ui/action-bar/action-bar-common.ts @@ -10,12 +10,20 @@ import { profile } from "../../profiling"; export * from "../core/view"; import { - View, ViewBase, Property, - unsetValue, booleanConverter, + View, + ViewBase, + Property, + unsetValue, + booleanConverter, horizontalAlignmentProperty, - verticalAlignmentProperty, CSSType, - traceWrite, traceCategories, traceMessageType + verticalAlignmentProperty, + CSSType, + traceWrite, + traceCategories, + traceMessageType, Color } from "../core/view"; +import { ShorthandProperty, CssProperty, Style } from "../core/properties/properties"; +import { Length } from "../core/view"; export module knownCollections { export const actionItems = "actionItems"; @@ -31,6 +39,9 @@ export class ActionBarBase extends View implements ActionBarDefinition { public flat: boolean; public iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate"; + public effectiveContentInsetLeft: number; + public effectiveContentInsetRight: number; + get navigationButton(): NavigationButton { return this._navigationButton; } @@ -90,6 +101,27 @@ export class ActionBarBase extends View implements ActionBarDefinition { } } + public get androidContentInset(): string | Length { + return this.style.androidContentInset; + } + public set androidContentInset(value: string | Length) { + this.style.androidContentInset = value; + } + + public get androidContentInsetLeft(): Length { + return this.style.androidContentInsetLeft; + } + public set androidContentInsetLeft(value: Length) { + this.style.androidContentInsetLeft = value; + } + + public get androidContentInsetRight(): Length { + return this.style.androidContentInsetRight; + } + public set androidContentInsetRight(value: Length) { + this.style.androidContentInsetRight = value; + } + get ios(): any { return undefined; } @@ -351,6 +383,23 @@ export function traceMissingIcon(icon: string) { traceMessageType.error); } +function convertToContentInset(this: void, value: string | Length): [CssProperty, any][] { + if (typeof value === "string" && value !== "auto") { + let insets = value.split(/[ ,]+/); + + return [ + [androidContentInsetLeftProperty, Length.parse(insets[0])], + [androidContentInsetRightProperty, Length.parse(insets[1] || insets[0])] + ]; + } + else { + return [ + [androidContentInsetLeftProperty, value], + [androidContentInsetRightProperty, value] + ]; + } +} + export const iosIconRenderingModeProperty = new Property({ name: "iosIconRenderingMode", defaultValue: "alwaysOriginal" }); iosIconRenderingModeProperty.register(ActionBarBase); @@ -365,3 +414,44 @@ visibilityProperty.register(ActionItemBase); export const flatProperty = new Property({ name: "flat", defaultValue: false, valueConverter: booleanConverter }); flatProperty.register(ActionBarBase); + +const androidContentInsetProperty = new ShorthandProperty({ + name: "androidContentInset", cssName: "android-content-inset", + getter: function (this: Style) { + if (Length.equals(this.androidContentInsetLeft, this.androidContentInsetRight)) { + return this.androidContentInsetLeft; + } + + return `${Length.convertToString(this.androidContentInsetLeft)} ${Length.convertToString(this.androidContentInsetRight)}`; + }, + converter: convertToContentInset +}); +androidContentInsetProperty.register(Style); + +export const androidContentInsetLeftProperty = new CssProperty({ + name: "androidContentInsetLeft", cssName: "android-content-inset-left", + defaultValue: "auto", equalityComparer: Length.equals, + valueChanged: (target, oldValue, newValue) => { + const view = target.viewRef.get(); + if (view) { + view.effectiveContentInsetLeft = Length.toDevicePixels(newValue); + } else { + traceWrite(`${newValue} not set to view's property because ".viewRef" is cleared`, traceCategories.Style, traceMessageType.warn); + } + }, valueConverter: Length.parse +}); +androidContentInsetLeftProperty.register(Style); + +export const androidContentInsetRightProperty = new CssProperty({ + name: "androidContentInsetRight", cssName: "android-content-inset-right", + defaultValue: "auto", equalityComparer: Length.equals, + valueChanged: (target, oldValue, newValue) => { + const view = target.viewRef.get(); + if (view) { + view.effectiveContentInsetRight = Length.toDevicePixels(newValue); + } else { + traceWrite(`${newValue} not set to view's property because ".viewRef" is cleared`, traceCategories.Style, traceMessageType.warn); + } + }, valueConverter: Length.parse +}); +androidContentInsetRightProperty.register(Style); diff --git a/tns-core-modules/ui/action-bar/action-bar.android.ts b/tns-core-modules/ui/action-bar/action-bar.android.ts index fc6a3fc50..a86d59336 100644 --- a/tns-core-modules/ui/action-bar/action-bar.android.ts +++ b/tns-core-modules/ui/action-bar/action-bar.android.ts @@ -1,8 +1,8 @@ import { AndroidActionBarSettings as AndroidActionBarSettingsDefinition, AndroidActionItemSettings } from "."; import { ActionItemBase, ActionBarBase, isVisible, - View, layout, colorProperty, flatProperty, - Color, traceMissingIcon + View, layout, colorProperty, flatProperty, Color, + traceMissingIcon, androidContentInsetLeftProperty, androidContentInsetRightProperty, Length } from "./action-bar-common"; import { RESOURCE_PREFIX, isFontIconURI } from "../../utils/utils"; import { fromFileOrResource, fromFontIconCode } from "../../image-source"; @@ -34,6 +34,8 @@ function initializeMenuItemClickListener(): void { return; } + apiLevel = android.os.Build.VERSION.SDK_INT; + AppCompatTextView = androidx.appcompat.widget.AppCompatTextView; @Interfaces([androidx.appcompat.widget.Toolbar.OnMenuItemClickListener]) @@ -55,6 +57,8 @@ function initializeMenuItemClickListener(): void { appResources = application.android.context.getResources(); } +let apiLevel: number; + export class ActionItem extends ActionItemBase { private _androidPosition: AndroidActionItemSettings = { position: "actionBar", @@ -433,6 +437,18 @@ export class ActionBar extends ActionBarBase { } } } + + [androidContentInsetLeftProperty.setNative]() { + if (apiLevel >= 21) { + this.nativeViewProtected.setContentInsetsAbsolute(this.effectiveContentInsetLeft, this.effectiveContentInsetRight); + } + } + + [androidContentInsetRightProperty.setNative]() { + if (apiLevel >= 21) { + this.nativeViewProtected.setContentInsetsAbsolute(this.effectiveContentInsetLeft, this.effectiveContentInsetRight); + } + } } function getAppCompatTextView(toolbar: androidx.appcompat.widget.Toolbar): typeof AppCompatTextView { diff --git a/tns-core-modules/ui/action-bar/action-bar.d.ts b/tns-core-modules/ui/action-bar/action-bar.d.ts index bf040fbe3..3f96f822f 100644 --- a/tns-core-modules/ui/action-bar/action-bar.d.ts +++ b/tns-core-modules/ui/action-bar/action-bar.d.ts @@ -55,6 +55,9 @@ export class ActionBar extends View { */ iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate"; + public effectiveContentInsetLeft: number; + public effectiveContentInsetRight: number; + /** * Updates the action bar. */ diff --git a/tns-core-modules/ui/styling/style/style.d.ts b/tns-core-modules/ui/styling/style/style.d.ts index 6e166562e..12d147b68 100644 --- a/tns-core-modules/ui/styling/style/style.d.ts +++ b/tns-core-modules/ui/styling/style/style.d.ts @@ -132,13 +132,39 @@ export class Style extends Observable { // ListView-specific props public separatorColor: Color; - //SegmentedBar-specific props + // SegmentedBar-specific props public selectedBackgroundColor: Color; // Page-specific props public statusBarStyle: "light" | "dark"; public androidStatusBarBackground: Color; + // Android ActionBar specific props + + /** + * Gets or sets the content inset for the android actionbar. + * The content inset affects the valid area for ActionBar content; insets can be used to effectively align ActionBar content along well-known gridlines. + * + * This property is effective on Android API level 21 or later. + */ + public androidContentInset: string | Length; + + /** + * Gets or sets the left content inset for the android actionbar. + * The content inset affects the valid area for ActionBar content; insets can be used to effectively align ActionBar content along well-known gridlines. + * + * This property is effective on Android API level 21 or later. + */ + public androidContentInsetLeft: Length; + + /** + * Gets or sets the right content inset for the android actionbar. + * The content inset affects the valid area for ActionBar content; insets can be used to effectively align ActionBar content along well-known gridlines. + * + * This property is effective on Android API level 21 or later. + */ + public androidContentInsetRight: Length; + constructor(ownerView: ViewBase | WeakRef); public viewRef: WeakRef; diff --git a/tns-core-modules/ui/styling/style/style.ts b/tns-core-modules/ui/styling/style/style.ts index ea5892ca7..fd24dc3c6 100644 --- a/tns-core-modules/ui/styling/style/style.ts +++ b/tns-core-modules/ui/styling/style/style.ts @@ -172,6 +172,11 @@ export class Style extends Observable implements StyleDefinition { public statusBarStyle: "light" | "dark"; public androidStatusBarBackground: Color; + // ActionBar-specific props + public androidContentInset: string | Length; + public androidContentInsetLeft: Length; + public androidContentInsetRight: Length; + //flexbox layout properties public flexDirection: FlexDirection; public flexWrap: FlexWrap;