diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/OptionBarProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/OptionBarProxy.java new file mode 100644 index 00000000000..f90ab509bf3 --- /dev/null +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/OptionBarProxy.java @@ -0,0 +1,38 @@ +/** + * Appcelerator Titanium Mobile + * Copyright (c) 2021 by Axway, Inc. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +package ti.modules.titanium.ui; + +import android.app.Activity; +import org.appcelerator.kroll.annotations.Kroll; +import org.appcelerator.titanium.proxy.TiViewProxy; +import org.appcelerator.titanium.TiC; +import org.appcelerator.titanium.view.TiUIView; +import ti.modules.titanium.ui.widget.TiUIOptionBar; + +@Kroll.proxy(creatableInModule = UIModule.class, propertyAccessors = { + TiC.PROPERTY_INDEX, + TiC.PROPERTY_LABELS, +}) +public class OptionBarProxy extends TiViewProxy +{ + public OptionBarProxy() + { + defaultValues.put(TiC.PROPERTY_INDEX, 0); + } + + @Override + public TiUIView createView(Activity activity) + { + return new TiUIOptionBar(this); + } + + @Override + public String getApiName() + { + return "Ti.UI.OptionBar"; + } +} diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIOptionBar.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIOptionBar.java new file mode 100644 index 00000000000..7f81d36896b --- /dev/null +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIOptionBar.java @@ -0,0 +1,255 @@ +/** + * Appcelerator Titanium Mobile + * Copyright (c) 2021 by Axway, Inc. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +package ti.modules.titanium.ui.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Checkable; +import androidx.appcompat.view.ContextThemeWrapper; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.button.MaterialButtonToggleGroup; +import java.util.HashMap; +import org.appcelerator.kroll.KrollDict; +import org.appcelerator.kroll.KrollProxy; +import org.appcelerator.titanium.TiC; +import org.appcelerator.titanium.proxy.TiViewProxy; +import org.appcelerator.titanium.R; +import org.appcelerator.titanium.util.TiConvert; +import org.appcelerator.titanium.util.TiUIHelper; +import org.appcelerator.titanium.view.TiUIView; + +public class TiUIOptionBar extends TiUIView +{ + private static final String TAG = "TiUIOptionBar"; + + /** Set true to prevent "click" events from firing. Set false to allow these events. */ + private boolean isIgnoringCheckEvent; + + public TiUIOptionBar(TiViewProxy proxy) + { + super(proxy); + + // Determine if the options should be shown vertically or horizontally. + String layout = TiConvert.toString(proxy.getProperty(TiC.PROPERTY_LAYOUT), TiC.LAYOUT_HORIZONTAL); + boolean isHorizontal = !TiC.LAYOUT_VERTICAL.equals(layout); + + // Create an outlined toggle button group. (Looks similar to iOS' old segmented control.) + MaterialButtonToggleGroup buttonGroup = new MaterialButtonToggleGroup(proxy.getActivity()); + buttonGroup.setSelectionRequired(true); + buttonGroup.setSingleSelection(true); + buttonGroup.setOrientation( + isHorizontal ? MaterialButtonToggleGroup.HORIZONTAL : MaterialButtonToggleGroup.VERTICAL); + + // Listen for selection changes. + buttonGroup.addOnButtonCheckedListener(this::onButtonChecked); + + // Set up view to fire a "postlayout" event every time its layout has changed. + buttonGroup.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange( + View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) + { + TiUIHelper.firePostLayoutEvent(getProxy()); + } + }); + + // Keep a reference to the above created view. + setNativeView(buttonGroup); + } + + @Override + public void processProperties(KrollDict properties) + { + // Validate. + if (properties == null) { + return; + } + + // Apply given properties to view. + if (properties.containsKey(TiC.PROPERTY_LABELS)) { + processLabels(properties.get(TiC.PROPERTY_LABELS)); + } + if (properties.containsKey(TiC.PROPERTY_INDEX)) { + checkOption(TiConvert.toInt(properties.get(TiC.PROPERTY_INDEX))); + } + + // Let base class handle all other view property settings. + super.processProperties(properties); + } + + @Override + public void propertyChanged(String key, Object oldValue, Object newValue, KrollProxy proxy) + { + // Validate. + if (key == null) { + return; + } + + // Handle property change. + if (key.equals(TiC.PROPERTY_INDEX)) { + checkOption(TiConvert.toInt(newValue)); + } else if (key.equals(TiC.PROPERTY_LABELS)) { + processLabels(newValue); + } else { + super.propertyChanged(key, oldValue, newValue, proxy); + } + } + + private void checkOption(int index) + { + View view = getNativeView(); + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + if ((index >= 0) && (index < viewGroup.getChildCount())) { + view = viewGroup.getChildAt(index); + if (view instanceof Checkable) { + boolean oldValue = this.isIgnoringCheckEvent; + this.isIgnoringCheckEvent = true; + ((Checkable) view).setChecked(true); + this.isIgnoringCheckEvent = oldValue; + } + } + } + } + + private void processLabels(Object labels) + { + // Do not continue if proxy has been released. + if (this.proxy == null) { + return; + } + + // Fetch the button group view. + MaterialButtonToggleGroup buttonGroup = getButtonGroup(); + if (buttonGroup == null) { + return; + } + + // Clear the previously assigned buttons. + buttonGroup.removeAllViews(); + + // Fetch "labels" property and validate it. + if ((labels == null) || !labels.getClass().isArray()) { + return; + } + Object[] objectArray = (Object[]) labels; + if (objectArray.length <= 0) { + return; + } + + // Process the labels object. + if (objectArray[0] instanceof String) { + // We were given an array of option titles. + for (Object title : objectArray) { + addButton(TiConvert.toString(title, ""), null, null, true); + } + } else if (objectArray[0] instanceof HashMap) { + // We were given an array of Titanium "BarItemType" dictionaries. + for (Object nextObject : objectArray) { + // Make sure next element is a dictionary. + if ((nextObject instanceof HashMap) == false) { + continue; + } + HashMap hashMap = (HashMap) nextObject; + + // Fetch the optional "title" property. + String title = TiConvert.toString(hashMap.get(TiC.PROPERTY_TITLE), ""); + + // Fetch the optional "accessibilityLabel" property. + String accessibilityLabel = TiConvert.toString(hashMap.get(TiC.PROPERTY_ACCESSIBILITY_LABEL), null); + + // Fetch the optional "image" property and load it as a drawable. + Drawable imageDrawable = null; + Object imageObject = hashMap.get(TiC.PROPERTY_IMAGE); + if (imageObject != null) { + imageDrawable = TiUIHelper.getResourceDrawable(imageObject); + } + + // Fetch the optional "enabled" flag. + boolean isEnabled = TiConvert.toBoolean(hashMap.get(TiC.PROPERTY_ENABLED), true); + + // Add the button. + addButton(title, accessibilityLabel, imageDrawable, isEnabled); + } + } + } + + private void addButton(String title, String accessibilityLabel, Drawable imageDrawable, boolean isEnabled) + { + // Fetch the button group view. + MaterialButtonToggleGroup buttonGroup = getButtonGroup(); + if (buttonGroup == null) { + return; + } + + // Title must be non-null. + if (title == null) { + title = ""; + } + + // Create a button with given settings and add it to view group. + Context context = buttonGroup.getContext(); + int attributeId = R.attr.materialButtonOutlinedStyle; + if (title.isEmpty() && (imageDrawable != null)) { + context = new ContextThemeWrapper(context, R.style.Widget_Titanium_OutlinedButton_IconOnly); + attributeId = R.attr.materialButtonToggleGroupStyle; + } + MaterialButton button = new MaterialButton(context, null, attributeId); + button.setEnabled(isEnabled); + button.setText(title); + if ((accessibilityLabel != null) && !accessibilityLabel.isEmpty()) { + button.setContentDescription(accessibilityLabel); + } + if (imageDrawable != null) { + button.setIcon(imageDrawable); + } + if (buttonGroup.getOrientation() != MaterialButtonToggleGroup.HORIZONTAL) { + button.setInsetTop(0); + button.setInsetBottom(0); + } + buttonGroup.addView(button); + } + + private void onButtonChecked(ViewGroup viewGroup, int viewId, boolean isChecked) + { + // Validate. + if ((this.proxy == null) || (viewGroup == null)) { + return; + } + + // Find the checked/selected view matching the given ID. + for (int index = 0; index < viewGroup.getChildCount(); index++) { + View childView = viewGroup.getChildAt(index); + if ((childView instanceof Checkable) && (childView.getId() == viewId)) { + if (((Checkable) childView).isChecked()) { + // Update the proxy's "index" property. + this.proxy.setProperty(TiC.PROPERTY_INDEX, index); + + // Fire a "click" event for selected option. + if (!this.isIgnoringCheckEvent) { + KrollDict data = new KrollDict(); + data.put(TiC.PROPERTY_INDEX, index); + this.proxy.fireEvent(TiC.EVENT_CLICK, data); + } + return; + } + } + } + } + + private MaterialButtonToggleGroup getButtonGroup() + { + View view = getNativeView(); + if (view instanceof MaterialButtonToggleGroup) { + return (MaterialButtonToggleGroup) view; + } + return null; + } +} diff --git a/apidoc/Titanium/UI/OptionBar.yml b/apidoc/Titanium/UI/OptionBar.yml new file mode 100644 index 00000000000..02431449a9d --- /dev/null +++ b/apidoc/Titanium/UI/OptionBar.yml @@ -0,0 +1,114 @@ +--- +name: Titanium.UI.OptionBar + +summary: A bar of selectable buttons where only 1 can be selected at a time. +description: | + This is a view which shows a list of options where only 1 option is selectable by the user. + + On iOS, this displays a + [segmented control](https://developer.apple.com/design/human-interface-guidelines/ios/controls/segmented-controls/). + + On Android, this displays a + [toggle button group](https://material.io/components/buttons/android#toggle-button). + + Use the method to create a Option Bar. +extends: Titanium.UI.View +platforms: [iphone, ipad, android, macos] +since: "10.0.0" +excludes: + events: [ 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'dblclick', + 'doubletap', 'longclick', 'singletap', 'swipe', 'twofingertap' ] + properties: [children] + methods: [add, remove, removeAllChildren, replaceAt] + +events: + - name: click + summary: Fired when an option has been selected. + properties: + - name: index + summary: Index of the option clicked on. + type: Number +properties: + - name: index + summary: Index of the currently selected option. + type: Number + + - name: labels + summary: Array of labels for the option bar. + description: | + Setting this to an array of strings specifies the title of each option in the bar. + + Alternatively, this can be set to an array of dictionary objects of type + which allows you to specify a title and/or image for each option in the bar. + Only Android supports setting both `title` and `image` properties on the same item. + On iOS, if you set the `image` property, then the `title` property is ignored. + type: [Array, Array] + availability: creation + + - name: layout + summary: Specifies the layout direction such as 'horizontal' or 'vertical'. + description: | + This property is only supported on Android and allows you to display its toggle buttons + in ther `'vertical'` direction. + type: String + default: horizontal + availability: creation + +examples: + - title: Text-Only Buttons + example: | + Creates an option bar of text buttons. + + ``` js + const win = Ti.UI.createWindow(); + const optionBar = Ti.UI.createOptionBar({ + labels: [ 'Option 1', 'Option 2', Option 3 ] + }); + optionBar.addEventListener('click', (e) => { + Ti.API.info(`Option ${e.index} was selected.`); + }); + win.add(optionBar); + win.open(); + ``` + + - title: Image-Only Buttons + example: | + Creates an option bar of image-only buttons. + + ``` js + const win = Ti.UI.createWindow(); + const optionBar = Ti.UI.createOptionBar({ + labels: [ + { image: '/Option1.png' }, + { image: '/Option2.png' }, + { image: '/Option3.png' }, + ] + }); + optionBar.addEventListener('click', (e) => { + Ti.API.info(`Option ${e.index} was selected.`); + }); + win.add(optionBar); + win.open(); + ``` + + - title: Buttons with Text and Images + example: | + Creates an option bar where each button shows an image to the left of its text. + This is only supported on Android. On iOS, if you set the `image` property, + then the `title` property is ignored. + + ``` js + const win = Ti.UI.createWindow(); + const optionBar = Ti.UI.createOptionBar({ + labels: [ + { image: '/Option1.png', title: 'Option 1' }, + { image: '/Option2.png', title: 'Option 2' }, + { image: '/Option3.png', title: 'Option 3' }, + ] + }); + optionBar.addEventListener('click', (e) => { + Ti.API.info(`Option ${e.index} was selected.`); + }); + win.add(optionBar); + win.open(); + ``` diff --git a/iphone/Classes/TiUIButtonBar.m b/iphone/Classes/TiUIButtonBar.m index 747222e900d..83cd824fd6f 100644 --- a/iphone/Classes/TiUIButtonBar.m +++ b/iphone/Classes/TiUIButtonBar.m @@ -231,6 +231,9 @@ - (void)setLabels_:(id)value } if (![segmentedControl isMomentary]) { + if ((selectedIndex < 0) && (segmentedControl.numberOfSegments > 0)) { + selectedIndex = 0; + } [segmentedControl setSelectedSegmentIndex:selectedIndex]; } } diff --git a/iphone/Classes/TiUIOptionBarProxy.h b/iphone/Classes/TiUIOptionBarProxy.h new file mode 100644 index 00000000000..eadafb31de3 --- /dev/null +++ b/iphone/Classes/TiUIOptionBarProxy.h @@ -0,0 +1,15 @@ +/** + * Appcelerator Titanium Mobile + * Copyright (c) 2021-Present by Axway, Inc. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +#if defined(USE_TI_UIOPTIONBAR) +#import + +@interface TiUIOptionBarProxy : TiViewProxy { +} + +@end + +#endif diff --git a/iphone/Classes/TiUIOptionBarProxy.m b/iphone/Classes/TiUIOptionBarProxy.m new file mode 100644 index 00000000000..4d0e241f02f --- /dev/null +++ b/iphone/Classes/TiUIOptionBarProxy.m @@ -0,0 +1,49 @@ +/** + * Appcelerator Titanium Mobile + * Copyright (c) 2021-Present by Axway, Inc. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +#if defined(USE_TI_UIOPTIONBAR) +#import "TiUIOptionBarProxy.h" +#import "TiUIButtonBar.h" + +@implementation TiUIOptionBarProxy + +- (NSArray *)keySequence +{ + static NSArray *optionKeySequence = nil; + if (optionKeySequence == nil) { + optionKeySequence = [[NSArray alloc] initWithObjects:@"labels", nil]; + } + return optionKeySequence; +} + +- (NSString *)apiName +{ + return @"Ti.UI.OptionBar"; +} + +- (TiUIView *)newView +{ + TiUIButtonBar *result = [[TiUIButtonBar alloc] init]; + [result setTabbedBar:YES]; + return result; +} + +USE_VIEW_FOR_CONTENT_WIDTH +USE_VIEW_FOR_CONTENT_HEIGHT + +#ifndef TI_USE_AUTOLAYOUT +- (TiDimension)defaultAutoWidthBehavior:(id)unused +{ + return TiDimensionAutoSize; +} +- (TiDimension)defaultAutoHeightBehavior:(id)unused +{ + return TiDimensionAutoSize; +} +#endif + +@end +#endif diff --git a/iphone/Classes/UIModule.m b/iphone/Classes/UIModule.m index d23eab80953..106c13b8f15 100644 --- a/iphone/Classes/UIModule.m +++ b/iphone/Classes/UIModule.m @@ -30,6 +30,9 @@ #ifdef USE_TI_UIATTRIBUTEDSTRING #import "TiUIAttributedStringProxy.h" #endif +#ifdef USE_TI_UIOPTIONBAR +#import "TiUIOptionBarProxy.h" +#endif #ifdef USE_TI_UITOOLBAR #import "TiUIToolbarProxy.h" #endif @@ -306,6 +309,13 @@ - (id)createAnimation:(id)args } #endif +#ifdef USE_TI_UIOPTIONBAR +- (id)createOptionBar:(id)args +{ + return [[[TiUIOptionBarProxy alloc] _initWithPageContext:[self executionContext] args:args] autorelease]; +} +#endif + #ifdef USE_TI_UITOOLBAR - (id)createToolbar:(id)args { diff --git a/iphone/cli/commands/_build.js b/iphone/cli/commands/_build.js index 22cc9c1324f..e54d5d4434d 100644 --- a/iphone/cli/commands/_build.js +++ b/iphone/cli/commands/_build.js @@ -6736,8 +6736,10 @@ iOSBuilder.prototype.processTiSymbols = function processTiSymbols() { 'USE_TI_UIMASKEDIMAGE', 'USE_TI_UIPROGRESSBAR', 'USE_TI_UIACTIVITYINDICATOR', + 'USE_TI_UIOPTIONBAR', 'USE_TI_UISWITCH', 'USE_TI_UISLIDER', + 'USE_TI_UITABBEDBAR', 'USE_TI_UITEXTFIELD', 'USE_TI_UITEXTAREA', 'USE_TI_UISCROLLABLEVIEW', diff --git a/iphone/iphone/Titanium.xcodeproj/project.pbxproj b/iphone/iphone/Titanium.xcodeproj/project.pbxproj index 88dfb6d711b..12be39c3c2b 100644 --- a/iphone/iphone/Titanium.xcodeproj/project.pbxproj +++ b/iphone/iphone/Titanium.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 030FB71825EDEEB200B68467 /* TiUIOptionBarProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 030FB71625EDEEB200B68467 /* TiUIOptionBarProxy.m */; }; 0BF9338A1CA0948D0091C7EC /* TiUIiOSStepper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BF933891CA0948D0091C7EC /* TiUIiOSStepper.m */; }; 0BFAD90F1CA1B86B00A04BF7 /* TiUIiOSStepperProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFAD90E1CA1B86B00A04BF7 /* TiUIiOSStepperProxy.m */; }; 1592CC2E1C47148D00C3DB83 /* TiUIiOSTableViewScrollPositionProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1592CC2D1C47148D00C3DB83 /* TiUIiOSTableViewScrollPositionProxy.m */; }; @@ -296,6 +297,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 030FB71625EDEEB200B68467 /* TiUIOptionBarProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIOptionBarProxy.m; sourceTree = ""; }; + 030FB71725EDEEB200B68467 /* TiUIOptionBarProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIOptionBarProxy.h; sourceTree = ""; }; 0BF933881CA0948D0091C7EC /* TiUIiOSStepper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIiOSStepper.h; sourceTree = ""; }; 0BF933891CA0948D0091C7EC /* TiUIiOSStepper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIiOSStepper.m; sourceTree = ""; }; 0BFAD90D1CA1B86B00A04BF7 /* TiUIiOSStepperProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIiOSStepperProxy.h; sourceTree = ""; }; @@ -816,6 +819,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 030FB71525EDEE7200B68467 /* OptionBar */ = { + isa = PBXGroup; + children = ( + 030FB71725EDEEB200B68467 /* TiUIOptionBarProxy.h */, + 030FB71625EDEEB200B68467 /* TiUIOptionBarProxy.m */, + ); + name = OptionBar; + sourceTree = ""; + }; 0BF933871CA0929A0091C7EC /* Stepper */ = { isa = PBXGroup; children = ( @@ -1191,6 +1203,7 @@ 24CA8C72111167590084E2DE /* Label */, 24CA8C701111672E0084E2DE /* Button */, 24CA8C6F111167140084E2DE /* ButtonBar */, + 030FB71525EDEE7200B68467 /* OptionBar */, 24CA8C711111673C0084E2DE /* ProgressBar */, 24CA8C6D111166C20084E2DE /* Searchbar */, 24CA8C6C111166BA0084E2DE /* ActivityIndicator */, @@ -2196,6 +2209,7 @@ 84A00FFF17FB833000D4BF94 /* TiPushBehavior.m in Sources */, 1592CC2E1C47148D00C3DB83 /* TiUIiOSTableViewScrollPositionProxy.m in Sources */, 24CA8BDC111161FE0084E2DE /* NetworkModule.m in Sources */, + 030FB71825EDEEB200B68467 /* TiUIOptionBarProxy.m in Sources */, 24CA8BDE111161FE0084E2DE /* MediaModule.m in Sources */, 24CA8BE3111161FE0084E2DE /* GestureModule.m in Sources */, 24CA8BE5111161FE0084E2DE /* FilesystemModule.m in Sources */, diff --git a/iphone/iphone/project.xcconfig b/iphone/iphone/project.xcconfig index 0f3b338bd76..64aa4ac9c0c 100644 --- a/iphone/iphone/project.xcconfig +++ b/iphone/iphone/project.xcconfig @@ -1,5 +1,5 @@ TI_VERSION=0.0.0 JSCORE_LD_FLAGS=-weak_framework JavaScriptCore GCC_DEFINITIONS= -TI_SYMBOL_MACROS=USE_JSCORE_FRAMEWORK USE_TI_STREAM USE_TI_CODEC USE_TI_UTILS USE_TI_XML USE_TI_ACCELEROMETER USE_TI_API USE_TI_APP USE_TI_APPTRACKUSERINTERACTION USE_TI_CALENDAR USE_TI_CONTACTS USE_TI_DATABASE USE_TI_FILESYSTEM USE_TI_GEOLOCATION USE_TI_GESTURE USE_TI_MEDIA USE_TI_NETWORK USE_TI_NETWORKSOCKET USE_TI_PLATFORM USE_TI_PLATFORMIDENTIFIERFORADVERTISING USE_TI_PLATFORMGETIDENTIFIERFORADVERTISING USE_TI_WATCHSESSION USE_TI_UI USE_TI_UITAB USE_TI_UILABEL USE_TI_UIBUTTON USE_TI_UIPROGRESSBAR USE_TI_UISEARCHBAR USE_TI_UIACTIVITYINDICATOR USE_TI_UISLIDER USE_TI_UISWITCH USE_TI_UIPICKER USE_TI_UITEXTAREA USE_TI_UITEXTFIELD USE_TI_UIIMAGEVIEW USE_TI_UIMASKEDIMAGE USE_TI_UIWEBVIEW USE_TI_UIWINDOW USE_TI_UIVIEW USE_TI_UIOPTIONDIALOG USE_TI_UIEMAILDIALOG USE_TI_UIDASHBOARDVIEW USE_TI_UISCROLLVIEW USE_TI_UISCROLLABLEVIEW USE_TI_UITABLEVIEW USE_TI_UILISTVIEW USE_TI_UIANIMATION USE_TI_UIATTRIBUTEDSTRING USE_TI_UIACTIVITYINDICATORSTYLE USE_TI_UITOOLBAR USE_TI_UITABBEDBAR USE_TI_UIAPPLICATIONSHORTCUTS USE_TI_UINAVIGATIONWINDOW USE_TI_UICLIPBOARD USE_TI_UIIPAD USE_TI_UIIPADPOPOVER USE_TI_UIIPADSPLITWINDOW USE_TI_UIIPADSPLITWINDOWBUTTON USE_TI_UIIOS USE_TI_UIIOSADVIEW USE_TI_UIIOSCOVERFLOWVIEW USE_TI_UIIOSTOOLBAR USE_TI_UIIOSTABBEDBAR USE_TI_UIIOSDOCUMENTVIEWER USE_TI_UIIOSNAVIGATIONWINDOW USE_TI_UIIOSSPLITWINDOW USE_TI_UIIOSPREVIEWCONTEXT USE_TI_UIIOSMENUPOPUP USE_TI_UIIOSLIVEPHOTOVIEW USE_TI_UIIOSLIVEPHOTOBADGE USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_OVER_CONTENT USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_LIVE_OFF USE_TI_UIIOSALERTDIALOGSTYLE USE_TI_UIIOSANIMATIONSTYLE USE_TI_UIIOSLISTVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWSCROLLPOSITION USE_TI_UIIOSLISTVIEWSCROLLPOSITION USE_TI_UIIOSTABLEVIEWSTYLE USE_TI_UIIOSLISTVIEWSTYLE USE_TI_UIIOSPROGRESSBARSTYLE USE_TI_UIIOSROWANIMATIONSTYLE USE_TI_UIIOSSCROLLINDICATORSTYLE USE_TI_UIIOSSTATUSBAR USE_TI_UIIOSSYSTEMBUTTONSTYLE USE_TI_UIIOSSYSTEMBUTTON USE_TI_UIIOSSYSTEMICON USE_TI_UIIOSFEEDBACKGENERATOR USE_TI_UIIOSSTEPPER USE_TI_APPIOS USE_TI_APPIOSSEARCHABLEINDEX USE_TI_APPIOSSEARCHABLEITEM USE_TI_APPIOSSEARCHABLEITEMATTRIBUTESET USE_TI_APPIOSSEARCHQUERY USE_TI_APPIOSUSERACTIVITY USE_TI_APPIOSUSERNOTIFICATIONCENTER USE_TI_UIIOSANIMATOR USE_TI_UIIOSSNAPBEHAVIOR USE_TI_UIIOSPUSHBEHAVIOR USE_TI_UIIOSGRAVITYBEHAVIOR USE_TI_UIIOSANCHORATTACHMENTBEHAVIOR USE_TI_UIIOSVIEWATTACHMENTBEHAVIOR USE_TI_UIIOSCOLLISIONBEHAVIOR USE_TI_UIIOSDYNAMICITEMBEHAVIOR USE_TI_UIIOSTRANSITIONANIMATION USE_TI_UIREFRESHCONTROL USE_TI_UIIOSAPPLICATIONSHORTCUTS USE_TI_UISHORTCUT USE_TI_UISHORTCUTITEM USE_TI_UIIOSBLURVIEW USE_TI_NETWORKREGISTERFORPUSHNOTIFICATIONS USE_TI_SILENTPUSH USE_TI_FETCH USE_TI_MEDIASHOWCAMERA USE_TI_MEDIAHIDECAMERA USE_TI_MEDIAOPENPHOTOGALLERY USE_TI_MEDIATAKEPICTURE USE_TI_MEDIASTARTVIDEOCAPTURE USE_TI_MEDIASTOPVIDEOCAPTURE USE_TI_MEDIASWITCHCAMERA USE_TI_MEDIAREQUESTCAMERAPERMISSIONS USE_TI_MEDIAHASCAMERAPERMISSIONS USE_TI_MEDIAHASPHOTOGALLERYPERMISSIONS USE_TI_MEDIAREQUESTPHOTOGALLERYPERMISSIONS USE_TI_MEDIAOPENMUSICLIBRARY USE_TI_MEDIAHIDEMUSICLIBRARY USE_TI_MEDIAQUERYMUSICLIBRARY USE_TI_MEDIAREQUESTAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIOPERMISSIONS USE_TI_MEDIAHASMUSICLIBRARYPERMISSIONS USE_TI_MEDIAREQUESTMUSICLIBRARYPERMISSIONS USE_TI_MEDIACANRECORD USE_TI_MEDIAISCAMERASUPPORTED USE_TI_MEDIAISMEDIATYPESUPPORTED USE_TI_MEDIASAVETOPHOTOGALLERY USE_TI_MEDIASTARTVIDEOEDITING USE_TI_MEDIASTOPVIDEOEDITING USE_TI_MEDIAAUDIOPLAYER USE_TI_MEDIAAUDIORECORDER USE_TI_MEDIAMUSICPLAYER USE_TI_MEDIASYSTEMMUSICPLAYER USE_TI_MEDIASYSTEMALERT USE_TI_MEDIAGETSYSTEMMUSICPLAYER USE_TI_MEDIAAPPMUSICPLAYER USE_TI_MEDIAGETAPPMUSICPLAYER USE_TI_MEDIAVIDEOPLAYER USE_TI_MEDIASOUND USE_TI_MEDIACAMERA_AUTHORIZATION_AUTHORIZED USE_TI_MEDIACAMERA_AUTHORIZATION_DENIED USE_TI_MEDIACAMERA_AUTHORIZATION_RESTRICTED USE_TI_MEDIACAMERA_AUTHORIZATION_UNKNOWN USE_TI_MEDIACAMERA_FRONT USE_TI_MEDIACAMERA_REAR USE_TI_MEDIACAMERA_FLASH_OFF USE_TI_MEDIACAMERA_FLASH_AUTO USE_TI_MEDIACAMERA_FLASH_ON USE_TI_MEDIACAMERAFLASHMODE USE_TI_MEDIAAVAILABLECAMERAMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOGALLERYMEDIATYPES USE_TI_MEDIAAVAILABLECAMERAS USE_TI_MEDIACAMERAAUTHORIZATION USE_TI_MEDIAVOLUME USE_TI_MEDIAAUDIOPLAYING USE_TI_MEDIACURRENTROUTE USE_TI_MEDIAVIBRATE USE_TI_MEDIABEEP USE_TI_MEDIASTARTMICROPHONEMONITOR USE_TI_MEDIASTOPMICROPHONEMONITOR USE_TI_MEDIAPEAKMICROPHONEPOWER USE_TI_MEDIAGETPEAKMICROPHONEPOWER USE_TI_MEDIAAVERAGEMICROPHONEPOWER USE_TI_MEDIAGETAVERAGEMICROPHONEPOWER +TI_SYMBOL_MACROS=USE_JSCORE_FRAMEWORK USE_TI_STREAM USE_TI_CODEC USE_TI_UTILS USE_TI_XML USE_TI_ACCELEROMETER USE_TI_API USE_TI_APP USE_TI_APPTRACKUSERINTERACTION USE_TI_CALENDAR USE_TI_CONTACTS USE_TI_DATABASE USE_TI_FILESYSTEM USE_TI_GEOLOCATION USE_TI_GESTURE USE_TI_MEDIA USE_TI_NETWORK USE_TI_NETWORKSOCKET USE_TI_PLATFORM USE_TI_PLATFORMIDENTIFIERFORADVERTISING USE_TI_PLATFORMGETIDENTIFIERFORADVERTISING USE_TI_WATCHSESSION USE_TI_UI USE_TI_UITAB USE_TI_UILABEL USE_TI_UIBUTTON USE_TI_UIPROGRESSBAR USE_TI_UISEARCHBAR USE_TI_UIACTIVITYINDICATOR USE_TI_UIOPTIONBAR USE_TI_UISLIDER USE_TI_UISWITCH USE_TI_UIPICKER USE_TI_UITEXTAREA USE_TI_UITEXTFIELD USE_TI_UIIMAGEVIEW USE_TI_UIMASKEDIMAGE USE_TI_UIWEBVIEW USE_TI_UIWINDOW USE_TI_UIVIEW USE_TI_UIOPTIONDIALOG USE_TI_UIEMAILDIALOG USE_TI_UIDASHBOARDVIEW USE_TI_UISCROLLVIEW USE_TI_UISCROLLABLEVIEW USE_TI_UITABLEVIEW USE_TI_UILISTVIEW USE_TI_UIANIMATION USE_TI_UIATTRIBUTEDSTRING USE_TI_UIACTIVITYINDICATORSTYLE USE_TI_UITOOLBAR USE_TI_UITABBEDBAR USE_TI_UIAPPLICATIONSHORTCUTS USE_TI_UINAVIGATIONWINDOW USE_TI_UICLIPBOARD USE_TI_UIIPAD USE_TI_UIIPADPOPOVER USE_TI_UIIPADSPLITWINDOW USE_TI_UIIPADSPLITWINDOWBUTTON USE_TI_UIIOS USE_TI_UIIOSADVIEW USE_TI_UIIOSCOVERFLOWVIEW USE_TI_UIIOSTOOLBAR USE_TI_UIIOSTABBEDBAR USE_TI_UIIOSDOCUMENTVIEWER USE_TI_UIIOSNAVIGATIONWINDOW USE_TI_UIIOSSPLITWINDOW USE_TI_UIIOSPREVIEWCONTEXT USE_TI_UIIOSMENUPOPUP USE_TI_UIIOSLIVEPHOTOVIEW USE_TI_UIIOSLIVEPHOTOBADGE USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_OVER_CONTENT USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_LIVE_OFF USE_TI_UIIOSALERTDIALOGSTYLE USE_TI_UIIOSANIMATIONSTYLE USE_TI_UIIOSLISTVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWSCROLLPOSITION USE_TI_UIIOSLISTVIEWSCROLLPOSITION USE_TI_UIIOSTABLEVIEWSTYLE USE_TI_UIIOSLISTVIEWSTYLE USE_TI_UIIOSPROGRESSBARSTYLE USE_TI_UIIOSROWANIMATIONSTYLE USE_TI_UIIOSSCROLLINDICATORSTYLE USE_TI_UIIOSSTATUSBAR USE_TI_UIIOSSYSTEMBUTTONSTYLE USE_TI_UIIOSSYSTEMBUTTON USE_TI_UIIOSSYSTEMICON USE_TI_UIIOSFEEDBACKGENERATOR USE_TI_UIIOSSTEPPER USE_TI_APPIOS USE_TI_APPIOSSEARCHABLEINDEX USE_TI_APPIOSSEARCHABLEITEM USE_TI_APPIOSSEARCHABLEITEMATTRIBUTESET USE_TI_APPIOSSEARCHQUERY USE_TI_APPIOSUSERACTIVITY USE_TI_APPIOSUSERNOTIFICATIONCENTER USE_TI_UIIOSANIMATOR USE_TI_UIIOSSNAPBEHAVIOR USE_TI_UIIOSPUSHBEHAVIOR USE_TI_UIIOSGRAVITYBEHAVIOR USE_TI_UIIOSANCHORATTACHMENTBEHAVIOR USE_TI_UIIOSVIEWATTACHMENTBEHAVIOR USE_TI_UIIOSCOLLISIONBEHAVIOR USE_TI_UIIOSDYNAMICITEMBEHAVIOR USE_TI_UIIOSTRANSITIONANIMATION USE_TI_UIREFRESHCONTROL USE_TI_UIIOSAPPLICATIONSHORTCUTS USE_TI_UISHORTCUT USE_TI_UISHORTCUTITEM USE_TI_UIIOSBLURVIEW USE_TI_NETWORKREGISTERFORPUSHNOTIFICATIONS USE_TI_SILENTPUSH USE_TI_FETCH USE_TI_MEDIASHOWCAMERA USE_TI_MEDIAHIDECAMERA USE_TI_MEDIAOPENPHOTOGALLERY USE_TI_MEDIATAKEPICTURE USE_TI_MEDIASTARTVIDEOCAPTURE USE_TI_MEDIASTOPVIDEOCAPTURE USE_TI_MEDIASWITCHCAMERA USE_TI_MEDIAREQUESTCAMERAPERMISSIONS USE_TI_MEDIAHASCAMERAPERMISSIONS USE_TI_MEDIAHASPHOTOGALLERYPERMISSIONS USE_TI_MEDIAREQUESTPHOTOGALLERYPERMISSIONS USE_TI_MEDIAOPENMUSICLIBRARY USE_TI_MEDIAHIDEMUSICLIBRARY USE_TI_MEDIAQUERYMUSICLIBRARY USE_TI_MEDIAREQUESTAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIOPERMISSIONS USE_TI_MEDIAHASMUSICLIBRARYPERMISSIONS USE_TI_MEDIAREQUESTMUSICLIBRARYPERMISSIONS USE_TI_MEDIACANRECORD USE_TI_MEDIAISCAMERASUPPORTED USE_TI_MEDIAISMEDIATYPESUPPORTED USE_TI_MEDIASAVETOPHOTOGALLERY USE_TI_MEDIASTARTVIDEOEDITING USE_TI_MEDIASTOPVIDEOEDITING USE_TI_MEDIAAUDIOPLAYER USE_TI_MEDIAAUDIORECORDER USE_TI_MEDIAMUSICPLAYER USE_TI_MEDIASYSTEMMUSICPLAYER USE_TI_MEDIASYSTEMALERT USE_TI_MEDIAGETSYSTEMMUSICPLAYER USE_TI_MEDIAAPPMUSICPLAYER USE_TI_MEDIAGETAPPMUSICPLAYER USE_TI_MEDIAVIDEOPLAYER USE_TI_MEDIASOUND USE_TI_MEDIACAMERA_AUTHORIZATION_AUTHORIZED USE_TI_MEDIACAMERA_AUTHORIZATION_DENIED USE_TI_MEDIACAMERA_AUTHORIZATION_RESTRICTED USE_TI_MEDIACAMERA_AUTHORIZATION_UNKNOWN USE_TI_MEDIACAMERA_FRONT USE_TI_MEDIACAMERA_REAR USE_TI_MEDIACAMERA_FLASH_OFF USE_TI_MEDIACAMERA_FLASH_AUTO USE_TI_MEDIACAMERA_FLASH_ON USE_TI_MEDIACAMERAFLASHMODE USE_TI_MEDIAAVAILABLECAMERAMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOGALLERYMEDIATYPES USE_TI_MEDIAAVAILABLECAMERAS USE_TI_MEDIACAMERAAUTHORIZATION USE_TI_MEDIAVOLUME USE_TI_MEDIAAUDIOPLAYING USE_TI_MEDIACURRENTROUTE USE_TI_MEDIAVIBRATE USE_TI_MEDIABEEP USE_TI_MEDIASTARTMICROPHONEMONITOR USE_TI_MEDIASTOPMICROPHONEMONITOR USE_TI_MEDIAPEAKMICROPHONEPOWER USE_TI_MEDIAGETPEAKMICROPHONEPOWER USE_TI_MEDIAAVERAGEMICROPHONEPOWER USE_TI_MEDIAGETAVERAGEMICROPHONEPOWER diff --git a/tests/Resources/app.js b/tests/Resources/app.js index 99cf77b98ed..575a6f6d117 100644 --- a/tests/Resources/app.js +++ b/tests/Resources/app.js @@ -173,6 +173,7 @@ function loadTests() { require('./ti.ui.maskedimage.test'); require('./ti.ui.matrix2d.test'); require('./ti.ui.navigationwindow.test'); + require('./ti.ui.optionbar.test'); require('./ti.ui.optiondialog.test'); require('./ti.ui.picker.test'); require('./ti.ui.progressbar.test'); diff --git a/tests/Resources/ti.ui.optionbar.test.js b/tests/Resources/ti.ui.optionbar.test.js new file mode 100644 index 00000000000..7bb91ace565 --- /dev/null +++ b/tests/Resources/ti.ui.optionbar.test.js @@ -0,0 +1,152 @@ +/* + * Appcelerator Titanium Mobile + * Copyright (c) 2021-Present by Axway, Inc. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +/* eslint-env mocha */ +/* eslint no-unused-expressions: "off" */ +'use strict'; +const should = require('./utilities/assertions'); + +describe.windowsMissing('Titanium.UI.OptionBar', function () { + let win; + this.timeout(5000); + + beforeEach(() => { + win = Ti.UI.createWindow(); + }); + + afterEach(done => { // fires after every test in sub-suites too... + if (win && !win.closed) { + win.addEventListener('close', function listener () { + win.removeEventListener('close', listener); + win = null; + done(); + }); + win.close(); + } else { + win = null; + done(); + } + }); + + describe('properties', () => { + describe('.apiName', () => { + it('is a read-only String', () => { + const optionBar = Ti.UI.createOptionBar(); + should(optionBar).have.readOnlyProperty('apiName').which.is.a.String(); + }); + + it('equals Ti.UI.OptionBar', () => { + const optionBar = Ti.UI.createOptionBar(); + should(optionBar.apiName).be.eql('Ti.UI.OptionBar'); + }); + }); + + describe('.labels', () => { + it('from string[]', finish => { + const optionBar = Ti.UI.createOptionBar({ + labels: [ 'A', 'B', 'C' ] + }); + function postlayout() { + optionBar.removeEventListener('postlayout', postlayout); + finish(); + } + optionBar.addEventListener('postlayout', postlayout); + win.add(optionBar); + win.open(); + }); + + it('from BarItemType[]', finish => { + const optionBar = Ti.UI.createOptionBar({ + labels: [ + { title: 'A' }, + { title: 'B' }, + { title: 'C' } + ] + }); + function postlayout() { + optionBar.removeEventListener('postlayout', postlayout); + finish(); + } + optionBar.addEventListener('postlayout', postlayout); + win.add(optionBar); + win.open(); + }); + + it('update', finish => { + const optionBar = Ti.UI.createOptionBar({ + labels: [ 'A', 'B', 'C' ] + }); + function postlayout() { + optionBar.removeEventListener('postlayout', postlayout); + try { + optionBar.labels = [ 'D', 'E', 'F' ]; + should(optionBar.labels[1]).be.eql('E'); + } catch (err) { + return finish(err); + } + finish(); + } + optionBar.addEventListener('postlayout', postlayout); + win.add(optionBar); + win.open(); + }); + + it('update - before window.open()', finish => { + const optionBar = Ti.UI.createOptionBar(); + optionBar.labels = [ 'A', 'B', 'C' ]; + win.add(optionBar); + win.addEventListener('open', () => { + try { + should(optionBar.labels[1]).be.eql('B'); + } catch (err) { + return finish(err); + } + finish(); + }); + win.open(); + }); + }); + + describe('.index', () => { + it('direct change', finish => { + const optionBar = Ti.UI.createOptionBar({ + labels: [ 'A', 'B', 'C' ], + index: 1 + }); + win.add(optionBar); + function postlayout() { + optionBar.removeEventListener('postlayout', postlayout); + try { + optionBar.index = 2; + should(optionBar.index).eql(2); + } catch (err) { + return finish(err); + } + finish(); + } + optionBar.addEventListener('postlayout', postlayout); + win.open(); + }); + + it('update - before window.open()', finish => { + var optionBar = Ti.UI.createOptionBar({ + labels: [ 'A', 'B', 'C' ] + }); + optionBar.index = 2; + win.add(optionBar); + win.addEventListener('open', () => { + try { + should(optionBar.index).be.eql(2); + } catch (err) { + return finish(err); + } + finish(); + }); + win.open(); + }); + }); + }); +});