OptionsMenu XML support

This commit is contained in:
Erjan Gavalji
2015-03-03 10:34:44 +02:00
committed by hshristov
parent 71a01a53e0
commit 70756fb115
12 changed files with 260 additions and 5 deletions

View File

@ -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" />

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

View 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!!!");
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -37,6 +37,7 @@ var MODULES = {
"TimePicker": "ui/time-picker",
"DatePicker": "ui/date-picker",
"ListPicker": "ui/list-picker",
"MenuItem": "ui/page",
};
var ROW = "row";

View File

@ -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 {

View File

@ -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
View File

@ -84,6 +84,7 @@ declare module "ui/frame" {
//@private
_processNavigationQueue(page: pages.Page);
_invalidateOptionsMenu();
//@endprivate
}

View File

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

View File

@ -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
View File

@ -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
}
}