mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 04:41:36 +08:00
OptionsMenu XML support
This commit is contained in:
@ -118,8 +118,12 @@
|
||||
<TypeScriptCompile Include="apps\tests\layouts\layout-helper.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\layouts\wrap-layout-tests.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\pages\page12.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\pages\page16.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\pages\page15.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\pages\page13.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\pages\page17.ts">
|
||||
<DependentUpon>page17.xml</DependentUpon>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="apps\tests\pages\property-bindings.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\platform-tests.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\fps-meter-tests.ts" />
|
||||
@ -578,6 +582,10 @@
|
||||
<Content Include="apps\tests\file-name-resolver-tests\files\test.ios.land.xml" />
|
||||
<Content Include="apps\tests\file-name-resolver-tests\files\test.android.minWH600.xml" />
|
||||
<Content Include="apps\tests\file-name-resolver-tests\files\test.xml" />
|
||||
<Content Include="apps\tests\pages\page17.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="apps\tests\test-icon.png" />
|
||||
<Content Include="apps\ui-tests-app\pages\i86.xml" />
|
||||
<Content Include="apps\template-blank\app.css" />
|
||||
<Content Include="apps\template-hello-world\app.css" />
|
||||
|
55
apps/tests/pages/page16.ts
Normal file
55
apps/tests/pages/page16.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import pageModule = require("ui/page");
|
||||
import buttonModule = require("ui/button");
|
||||
import stackModule = require("ui/layouts/stack-layout");
|
||||
import frame = require("ui/frame");
|
||||
|
||||
export function createPage() {
|
||||
var page = new pageModule.Page();
|
||||
|
||||
var iconItem = new pageModule.MenuItem();
|
||||
iconItem.text = "TEST";
|
||||
|
||||
iconItem.icon = "~/app" + "/tests" + "/test-icon.png"; // use + to stop regex replace during build
|
||||
iconItem.on("tap", () => {
|
||||
console.log("Icon item tapped");
|
||||
});
|
||||
page.optionsMenu.addItem(iconItem);
|
||||
|
||||
var textItem = new pageModule.MenuItem();
|
||||
textItem.text = "SAVE";
|
||||
textItem.on("tap", () => {
|
||||
console.log("Save item tapped");
|
||||
});
|
||||
page.optionsMenu.addItem(textItem);
|
||||
|
||||
var stackLayout = new stackModule.StackLayout();
|
||||
var count = 0;
|
||||
var btn1 = new buttonModule.Button();
|
||||
btn1.text = "add item";
|
||||
btn1.on("tap", () => {
|
||||
console.log("adding menu item");
|
||||
|
||||
var newItem = new pageModule.MenuItem();
|
||||
var text = "item " + count;
|
||||
newItem.text = text
|
||||
newItem.on("tap", () => {
|
||||
console.log("ITEM [" + text + "] tapped");
|
||||
});
|
||||
page.optionsMenu.addItem(newItem);
|
||||
count++;
|
||||
});
|
||||
|
||||
stackLayout.addChild(btn1);
|
||||
|
||||
var btn2 = new buttonModule.Button();
|
||||
btn2.text = "navigate";
|
||||
btn2.on("tap", () => {
|
||||
var nextPage = "app/tests/pages/page16";
|
||||
frame.topmost().navigate(nextPage);
|
||||
});
|
||||
|
||||
stackLayout.addChild(btn2);
|
||||
|
||||
page.content = stackLayout;
|
||||
return page;
|
||||
}
|
20
apps/tests/pages/page17.ts
Normal file
20
apps/tests/pages/page17.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import observable = require("data/observable");
|
||||
import pages = require("ui/page");
|
||||
|
||||
// Event handler for Page "loaded" event attached in main-page.xml
|
||||
export function pageLoaded(args: observable.EventData) {
|
||||
// Get the event sender
|
||||
var page = <pages.Page>args.object;
|
||||
|
||||
var textItem = new pages.MenuItem();
|
||||
textItem.text = "from loaded";
|
||||
textItem.on("tap", () => {
|
||||
console.log("item added in page.loaded tapped!!!");
|
||||
});
|
||||
page.optionsMenu.addItem(textItem);
|
||||
}
|
||||
|
||||
export function optionTap(args) {
|
||||
console.log("item added form XML tapped!!!");
|
||||
}
|
||||
|
9
apps/tests/pages/page17.xml
Normal file
9
apps/tests/pages/page17.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<Page loaded="pageLoaded">
|
||||
<Page.optionsMenu>
|
||||
<MenuItem text="test" tap="optionTap"/>
|
||||
<MenuItem text="with icon" tap="optionTap" icon="~/app/test-icon.png"/>
|
||||
</Page.optionsMenu>
|
||||
<StackLayout>
|
||||
<Button text="button" />
|
||||
</StackLayout>
|
||||
</Page>
|
BIN
apps/tests/test-icon.png
Normal file
BIN
apps/tests/test-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
@ -37,6 +37,7 @@ var MODULES = {
|
||||
"TimePicker": "ui/time-picker",
|
||||
"DatePicker": "ui/date-picker",
|
||||
"ListPicker": "ui/list-picker",
|
||||
"MenuItem": "ui/page",
|
||||
};
|
||||
|
||||
var ROW = "row";
|
||||
|
@ -370,6 +370,10 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
public _removeViewFromNativeVisualTree(child: view.View): void {
|
||||
child._isAddedToNativeVisualTree = false;
|
||||
}
|
||||
|
||||
public _invalidateOptionsMenu() {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
var _topmost = function (): Frame {
|
||||
|
@ -6,6 +6,7 @@ import observable = require("data/observable");
|
||||
import utils = require("utils/utils");
|
||||
import view = require("ui/core/view");
|
||||
import application = require("application");
|
||||
import imageSource = require("image-source");
|
||||
|
||||
declare var exports;
|
||||
require("utils/module-merge").merge(frameCommon, exports);
|
||||
@ -37,6 +38,7 @@ class PageFragmentBody extends android.app.Fragment {
|
||||
onCreate(savedInstanceState: android.os.Bundle) {
|
||||
super.onCreate(savedInstanceState);
|
||||
trace.write(this.getTag() + ".onCreate(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle);
|
||||
super.setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
onCreateView(inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View {
|
||||
@ -132,6 +134,38 @@ class PageFragmentBody extends android.app.Fragment {
|
||||
super.onDetach();
|
||||
trace.write(this.getTag() + ".onDetach();", trace.categories.NativeLifecycle);
|
||||
}
|
||||
|
||||
onCreateOptionsMenu(menu: android.view.IMenu, inflater: android.view.MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
var page: pages.Page = this.entry.resolvedPage;
|
||||
var items = page.optionsMenu.getItems();
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
var menuItem = menu.add(android.view.Menu.NONE, i, item.priority, item.text);
|
||||
if (item.icon) {
|
||||
var img = imageSource.fromFile(item.icon);
|
||||
var drawable = new android.graphics.drawable.BitmapDrawable(img.android);
|
||||
menuItem.setIcon(drawable);
|
||||
}
|
||||
|
||||
menuItem.setShowAsAction(android.view.MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
onOptionsItemSelected(item: android.view.IMenuItem) {
|
||||
var page: pages.Page = this.entry.resolvedPage;
|
||||
var itemId = item.getItemId();
|
||||
|
||||
var menuItem = page.optionsMenu.getItemAt(itemId);
|
||||
if (menuItem) {
|
||||
menuItem._raiseTap();
|
||||
return true;
|
||||
}
|
||||
|
||||
super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
function onFragmentShown(fragment: PageFragmentBody) {
|
||||
@ -314,6 +348,12 @@ export class Frame extends frameCommon.Frame {
|
||||
public _clearAndroidReference() {
|
||||
// we should keep the reference to underlying native object, since frame can contain many pages.
|
||||
}
|
||||
|
||||
public _invalidateOptionsMenu() {
|
||||
if (this.android && this.android.activity) {
|
||||
this.android.activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module com {
|
||||
|
1
ui/frame/frame.d.ts
vendored
1
ui/frame/frame.d.ts
vendored
@ -84,6 +84,7 @@ declare module "ui/frame" {
|
||||
|
||||
//@private
|
||||
_processNavigationQueue(page: pages.Page);
|
||||
_invalidateOptionsMenu();
|
||||
//@endprivate
|
||||
}
|
||||
|
||||
|
@ -5,19 +5,30 @@ import frame = require("ui/frame");
|
||||
import styleScope = require("ui/styling/style-scope");
|
||||
import fs = require("file-system");
|
||||
import fileSystemAccess = require("file-system/file-system-access");
|
||||
import trace = require("trace");
|
||||
import observable = require("data/observable");
|
||||
|
||||
var OPTIONS_MENU = "optionsMenu";
|
||||
|
||||
export module knownEvents {
|
||||
export var navigatedTo = "navigatedTo";
|
||||
export var tap = "tap";
|
||||
}
|
||||
|
||||
export class Page extends contentView.ContentView implements dts.Page {
|
||||
export module knownCollections {
|
||||
export var optionsMenu = "optionsMenu";
|
||||
}
|
||||
|
||||
export class Page extends contentView.ContentView implements dts.Page, view.AddArrayFromBuilder {
|
||||
private _navigationContext: any;
|
||||
|
||||
private _cssApplied: boolean;
|
||||
private _styleScope: styleScope.StyleScope = new styleScope.StyleScope();
|
||||
private _optionsMenu: OptionsMenu;
|
||||
|
||||
constructor(options?: dts.Options) {
|
||||
super(options);
|
||||
this._optionsMenu = new OptionsMenu(this);
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
@ -40,6 +51,13 @@ export class Page extends contentView.ContentView implements dts.Page {
|
||||
this._refreshCss();
|
||||
}
|
||||
|
||||
get optionsMenu(): OptionsMenu {
|
||||
return this._optionsMenu;
|
||||
}
|
||||
set optionsMenu(value: OptionsMenu) {
|
||||
throw new Error("optionsMenu property is read-only");
|
||||
}
|
||||
|
||||
private _refreshCss(): void {
|
||||
if (this._cssApplied) {
|
||||
this._resetCssValues();
|
||||
@ -131,4 +149,71 @@ export class Page extends contentView.ContentView implements dts.Page {
|
||||
view.eachDescendant(this, resetCssValuesFunc);
|
||||
|
||||
}
|
||||
|
||||
public _addArrayFromBuilder(name: string, value: Array<any>) {
|
||||
if (name === OPTIONS_MENU) {
|
||||
this.optionsMenu.setItems(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OptionsMenu implements dts.OptionsMenu {
|
||||
private _items: Array<MenuItem> = new Array<MenuItem>();
|
||||
private _page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this._page = page;
|
||||
}
|
||||
|
||||
public addItem(item: MenuItem): void {
|
||||
if (!item) {
|
||||
throw new Error("Cannot add empty item");
|
||||
}
|
||||
|
||||
this._items.push(item);
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
public removeItem(item: MenuItem): void {
|
||||
if (!item) {
|
||||
throw new Error("Cannot remove empty item");
|
||||
}
|
||||
|
||||
var itemIndex = this._items.indexOf(item);
|
||||
if (itemIndex < 0) {
|
||||
throw new Error("Cannot find item to remove");
|
||||
}
|
||||
|
||||
this._items.splice(itemIndex, 1);
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
public getItems(): Array<MenuItem> {
|
||||
return this._items.slice();
|
||||
}
|
||||
|
||||
public getItemAt(index: number): MenuItem {
|
||||
return this._items[index];
|
||||
}
|
||||
|
||||
public setItems(items: Array<MenuItem>) {
|
||||
this._items = items;
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
private invalidate() {
|
||||
if (this._page.frame) {
|
||||
this._page.frame._invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MenuItem extends observable.Observable implements dts.MenuItem {
|
||||
public text: string = "";
|
||||
public icon: string;
|
||||
public priority: number = 0;
|
||||
|
||||
public _raiseTap() {
|
||||
this._emit(knownEvents.tap);
|
||||
}
|
||||
}
|
@ -2,7 +2,8 @@
|
||||
import definition = require("ui/page");
|
||||
import trace = require("trace");
|
||||
|
||||
module.exports.knownEvents = pageCommon.knownEvents;
|
||||
declare var exports;
|
||||
require("utils/module-merge").merge(pageCommon, exports);
|
||||
|
||||
export class Page extends pageCommon.Page {
|
||||
private _isBackNavigation = false;
|
||||
|
37
ui/page/page.d.ts
vendored
37
ui/page/page.d.ts
vendored
@ -30,10 +30,14 @@ declare module "ui/page" {
|
||||
export var navigatedTo: string;
|
||||
}
|
||||
|
||||
export module knownCollections {
|
||||
export var optionsMenu: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a logical unit for navigation (inside Frame).
|
||||
*/
|
||||
export class Page extends contentView.ContentView {
|
||||
export class Page extends contentView.ContentView implements view.AddArrayFromBuilder {
|
||||
|
||||
constructor(options?: Options)
|
||||
|
||||
@ -64,6 +68,11 @@ declare module "ui/page" {
|
||||
*/
|
||||
frame: frame.Frame;
|
||||
|
||||
/**
|
||||
* Gets the OptionsMenu for this page.
|
||||
*/
|
||||
optionsMenu: OptionsMenu;
|
||||
|
||||
/**
|
||||
* A method called before navigating to the page.
|
||||
* @param context - The data passed to the page through the NavigationEntry.context property.
|
||||
@ -88,7 +97,8 @@ declare module "ui/page" {
|
||||
onNavigatedFrom(isBackNavigation: boolean): void;
|
||||
|
||||
//@private
|
||||
_getStyleScope(): styleScope.StyleScope
|
||||
_getStyleScope(): styleScope.StyleScope;
|
||||
_addArrayFromBuilder(name: string, value: Array<any>): void;
|
||||
//@endprivate
|
||||
|
||||
on(event: string, callback: (data: observable.EventData) => void);
|
||||
@ -118,4 +128,25 @@ declare module "ui/page" {
|
||||
*/
|
||||
exports?: any;
|
||||
}
|
||||
}
|
||||
|
||||
export class OptionsMenu {
|
||||
addItem(item: MenuItem): void;
|
||||
removeItem(item: MenuItem): void;
|
||||
getItems(): Array<MenuItem>;
|
||||
getItemAt(index: number): MenuItem;
|
||||
}
|
||||
|
||||
export class MenuItem extends observable.Observable {
|
||||
text: string;
|
||||
icon: string;
|
||||
priority: number;
|
||||
|
||||
on(event: string, callback: (data: observable.EventData) => void);
|
||||
on(event: "tap", callback: (args: observable.EventData) => void);
|
||||
|
||||
//@private
|
||||
_raiseTap();
|
||||
//@endprivate
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user